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