18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci/*
48c2ecf20Sopenharmony_ci * Hyper-V nested virtualization code.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2018, Microsoft, Inc.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Author : Lan Tianyu <Tianyu.Lan@microsoft.com>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci#define pr_fmt(fmt)  "Hyper-V: " fmt
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/types.h>
148c2ecf20Sopenharmony_ci#include <asm/hyperv-tlfs.h>
158c2ecf20Sopenharmony_ci#include <asm/mshyperv.h>
168c2ecf20Sopenharmony_ci#include <asm/tlbflush.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <asm/trace/hyperv.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ciint hyperv_flush_guest_mapping(u64 as)
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	struct hv_guest_mapping_flush **flush_pcpu;
238c2ecf20Sopenharmony_ci	struct hv_guest_mapping_flush *flush;
248c2ecf20Sopenharmony_ci	u64 status;
258c2ecf20Sopenharmony_ci	unsigned long flags;
268c2ecf20Sopenharmony_ci	int ret = -ENOTSUPP;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	if (!hv_hypercall_pg)
298c2ecf20Sopenharmony_ci		goto fault;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	local_irq_save(flags);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	flush_pcpu = (struct hv_guest_mapping_flush **)
348c2ecf20Sopenharmony_ci		this_cpu_ptr(hyperv_pcpu_input_arg);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	flush = *flush_pcpu;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	if (unlikely(!flush)) {
398c2ecf20Sopenharmony_ci		local_irq_restore(flags);
408c2ecf20Sopenharmony_ci		goto fault;
418c2ecf20Sopenharmony_ci	}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	flush->address_space = as;
448c2ecf20Sopenharmony_ci	flush->flags = 0;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	status = hv_do_hypercall(HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_SPACE,
478c2ecf20Sopenharmony_ci				 flush, NULL);
488c2ecf20Sopenharmony_ci	local_irq_restore(flags);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	if (!(status & HV_HYPERCALL_RESULT_MASK))
518c2ecf20Sopenharmony_ci		ret = 0;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cifault:
548c2ecf20Sopenharmony_ci	trace_hyperv_nested_flush_guest_mapping(as, ret);
558c2ecf20Sopenharmony_ci	return ret;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(hyperv_flush_guest_mapping);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ciint hyperv_fill_flush_guest_mapping_list(
608c2ecf20Sopenharmony_ci		struct hv_guest_mapping_flush_list *flush,
618c2ecf20Sopenharmony_ci		u64 start_gfn, u64 pages)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	u64 cur = start_gfn;
648c2ecf20Sopenharmony_ci	u64 additional_pages;
658c2ecf20Sopenharmony_ci	int gpa_n = 0;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	do {
688c2ecf20Sopenharmony_ci		/*
698c2ecf20Sopenharmony_ci		 * If flush requests exceed max flush count, go back to
708c2ecf20Sopenharmony_ci		 * flush tlbs without range.
718c2ecf20Sopenharmony_ci		 */
728c2ecf20Sopenharmony_ci		if (gpa_n >= HV_MAX_FLUSH_REP_COUNT)
738c2ecf20Sopenharmony_ci			return -ENOSPC;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci		additional_pages = min_t(u64, pages, HV_MAX_FLUSH_PAGES) - 1;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci		flush->gpa_list[gpa_n].page.additional_pages = additional_pages;
788c2ecf20Sopenharmony_ci		flush->gpa_list[gpa_n].page.largepage = false;
798c2ecf20Sopenharmony_ci		flush->gpa_list[gpa_n].page.basepfn = cur;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci		pages -= additional_pages + 1;
828c2ecf20Sopenharmony_ci		cur += additional_pages + 1;
838c2ecf20Sopenharmony_ci		gpa_n++;
848c2ecf20Sopenharmony_ci	} while (pages > 0);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	return gpa_n;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(hyperv_fill_flush_guest_mapping_list);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ciint hyperv_flush_guest_mapping_range(u64 as,
918c2ecf20Sopenharmony_ci		hyperv_fill_flush_list_func fill_flush_list_func, void *data)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	struct hv_guest_mapping_flush_list **flush_pcpu;
948c2ecf20Sopenharmony_ci	struct hv_guest_mapping_flush_list *flush;
958c2ecf20Sopenharmony_ci	u64 status = 0;
968c2ecf20Sopenharmony_ci	unsigned long flags;
978c2ecf20Sopenharmony_ci	int ret = -ENOTSUPP;
988c2ecf20Sopenharmony_ci	int gpa_n = 0;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	if (!hv_hypercall_pg || !fill_flush_list_func)
1018c2ecf20Sopenharmony_ci		goto fault;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	local_irq_save(flags);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	flush_pcpu = (struct hv_guest_mapping_flush_list **)
1068c2ecf20Sopenharmony_ci		this_cpu_ptr(hyperv_pcpu_input_arg);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	flush = *flush_pcpu;
1098c2ecf20Sopenharmony_ci	if (unlikely(!flush)) {
1108c2ecf20Sopenharmony_ci		local_irq_restore(flags);
1118c2ecf20Sopenharmony_ci		goto fault;
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	flush->address_space = as;
1158c2ecf20Sopenharmony_ci	flush->flags = 0;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	gpa_n = fill_flush_list_func(flush, data);
1188c2ecf20Sopenharmony_ci	if (gpa_n < 0) {
1198c2ecf20Sopenharmony_ci		local_irq_restore(flags);
1208c2ecf20Sopenharmony_ci		goto fault;
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	status = hv_do_rep_hypercall(HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_LIST,
1248c2ecf20Sopenharmony_ci				     gpa_n, 0, flush, NULL);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	local_irq_restore(flags);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (!(status & HV_HYPERCALL_RESULT_MASK))
1298c2ecf20Sopenharmony_ci		ret = 0;
1308c2ecf20Sopenharmony_ci	else
1318c2ecf20Sopenharmony_ci		ret = status;
1328c2ecf20Sopenharmony_cifault:
1338c2ecf20Sopenharmony_ci	trace_hyperv_nested_flush_guest_mapping_range(as, ret);
1348c2ecf20Sopenharmony_ci	return ret;
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(hyperv_flush_guest_mapping_range);
137