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