162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/*
462306a36Sopenharmony_ci * Hyper-V nested virtualization code.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2018, Microsoft, Inc.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Author : Lan Tianyu <Tianyu.Lan@microsoft.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci#define pr_fmt(fmt)  "Hyper-V: " fmt
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/types.h>
1462306a36Sopenharmony_ci#include <asm/hyperv-tlfs.h>
1562306a36Sopenharmony_ci#include <asm/mshyperv.h>
1662306a36Sopenharmony_ci#include <asm/tlbflush.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <asm/trace/hyperv.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ciint hyperv_flush_guest_mapping(u64 as)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	struct hv_guest_mapping_flush *flush;
2362306a36Sopenharmony_ci	u64 status;
2462306a36Sopenharmony_ci	unsigned long flags;
2562306a36Sopenharmony_ci	int ret = -ENOTSUPP;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	if (!hv_hypercall_pg)
2862306a36Sopenharmony_ci		goto fault;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	local_irq_save(flags);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	flush = *this_cpu_ptr(hyperv_pcpu_input_arg);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	if (unlikely(!flush)) {
3562306a36Sopenharmony_ci		local_irq_restore(flags);
3662306a36Sopenharmony_ci		goto fault;
3762306a36Sopenharmony_ci	}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	flush->address_space = as;
4062306a36Sopenharmony_ci	flush->flags = 0;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	status = hv_do_hypercall(HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_SPACE,
4362306a36Sopenharmony_ci				 flush, NULL);
4462306a36Sopenharmony_ci	local_irq_restore(flags);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	if (hv_result_success(status))
4762306a36Sopenharmony_ci		ret = 0;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cifault:
5062306a36Sopenharmony_ci	trace_hyperv_nested_flush_guest_mapping(as, ret);
5162306a36Sopenharmony_ci	return ret;
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hyperv_flush_guest_mapping);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ciint hyperv_fill_flush_guest_mapping_list(
5662306a36Sopenharmony_ci		struct hv_guest_mapping_flush_list *flush,
5762306a36Sopenharmony_ci		u64 start_gfn, u64 pages)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	u64 cur = start_gfn;
6062306a36Sopenharmony_ci	u64 additional_pages;
6162306a36Sopenharmony_ci	int gpa_n = 0;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	do {
6462306a36Sopenharmony_ci		/*
6562306a36Sopenharmony_ci		 * If flush requests exceed max flush count, go back to
6662306a36Sopenharmony_ci		 * flush tlbs without range.
6762306a36Sopenharmony_ci		 */
6862306a36Sopenharmony_ci		if (gpa_n >= HV_MAX_FLUSH_REP_COUNT)
6962306a36Sopenharmony_ci			return -ENOSPC;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci		additional_pages = min_t(u64, pages, HV_MAX_FLUSH_PAGES) - 1;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci		flush->gpa_list[gpa_n].page.additional_pages = additional_pages;
7462306a36Sopenharmony_ci		flush->gpa_list[gpa_n].page.largepage = false;
7562306a36Sopenharmony_ci		flush->gpa_list[gpa_n].page.basepfn = cur;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci		pages -= additional_pages + 1;
7862306a36Sopenharmony_ci		cur += additional_pages + 1;
7962306a36Sopenharmony_ci		gpa_n++;
8062306a36Sopenharmony_ci	} while (pages > 0);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	return gpa_n;
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hyperv_fill_flush_guest_mapping_list);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ciint hyperv_flush_guest_mapping_range(u64 as,
8762306a36Sopenharmony_ci		hyperv_fill_flush_list_func fill_flush_list_func, void *data)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct hv_guest_mapping_flush_list *flush;
9062306a36Sopenharmony_ci	u64 status;
9162306a36Sopenharmony_ci	unsigned long flags;
9262306a36Sopenharmony_ci	int ret = -ENOTSUPP;
9362306a36Sopenharmony_ci	int gpa_n = 0;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (!hv_hypercall_pg || !fill_flush_list_func)
9662306a36Sopenharmony_ci		goto fault;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	local_irq_save(flags);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	flush = *this_cpu_ptr(hyperv_pcpu_input_arg);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if (unlikely(!flush)) {
10362306a36Sopenharmony_ci		local_irq_restore(flags);
10462306a36Sopenharmony_ci		goto fault;
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	flush->address_space = as;
10862306a36Sopenharmony_ci	flush->flags = 0;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	gpa_n = fill_flush_list_func(flush, data);
11162306a36Sopenharmony_ci	if (gpa_n < 0) {
11262306a36Sopenharmony_ci		local_irq_restore(flags);
11362306a36Sopenharmony_ci		goto fault;
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	status = hv_do_rep_hypercall(HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_LIST,
11762306a36Sopenharmony_ci				     gpa_n, 0, flush, NULL);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	local_irq_restore(flags);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (hv_result_success(status))
12262306a36Sopenharmony_ci		ret = 0;
12362306a36Sopenharmony_ci	else
12462306a36Sopenharmony_ci		ret = hv_result(status);
12562306a36Sopenharmony_cifault:
12662306a36Sopenharmony_ci	trace_hyperv_nested_flush_guest_mapping_range(as, ret);
12762306a36Sopenharmony_ci	return ret;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hyperv_flush_guest_mapping_range);
130