162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/types.h> 362306a36Sopenharmony_ci#include <linux/vmalloc.h> 462306a36Sopenharmony_ci#include <linux/mm.h> 562306a36Sopenharmony_ci#include <linux/clockchips.h> 662306a36Sopenharmony_ci#include <linux/acpi.h> 762306a36Sopenharmony_ci#include <linux/hyperv.h> 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/cpuhotplug.h> 1062306a36Sopenharmony_ci#include <linux/minmax.h> 1162306a36Sopenharmony_ci#include <asm/hypervisor.h> 1262306a36Sopenharmony_ci#include <asm/mshyperv.h> 1362306a36Sopenharmony_ci#include <asm/apic.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <asm/trace/hyperv.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* 1862306a36Sopenharmony_ci * See struct hv_deposit_memory. The first u64 is partition ID, the rest 1962306a36Sopenharmony_ci * are GPAs. 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci#define HV_DEPOSIT_MAX (HV_HYP_PAGE_SIZE / sizeof(u64) - 1) 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* Deposits exact number of pages. Must be called with interrupts enabled. */ 2462306a36Sopenharmony_ciint hv_call_deposit_pages(int node, u64 partition_id, u32 num_pages) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci struct page **pages, *page; 2762306a36Sopenharmony_ci int *counts; 2862306a36Sopenharmony_ci int num_allocations; 2962306a36Sopenharmony_ci int i, j, page_count; 3062306a36Sopenharmony_ci int order; 3162306a36Sopenharmony_ci u64 status; 3262306a36Sopenharmony_ci int ret; 3362306a36Sopenharmony_ci u64 base_pfn; 3462306a36Sopenharmony_ci struct hv_deposit_memory *input_page; 3562306a36Sopenharmony_ci unsigned long flags; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (num_pages > HV_DEPOSIT_MAX) 3862306a36Sopenharmony_ci return -E2BIG; 3962306a36Sopenharmony_ci if (!num_pages) 4062306a36Sopenharmony_ci return 0; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci /* One buffer for page pointers and counts */ 4362306a36Sopenharmony_ci page = alloc_page(GFP_KERNEL); 4462306a36Sopenharmony_ci if (!page) 4562306a36Sopenharmony_ci return -ENOMEM; 4662306a36Sopenharmony_ci pages = page_address(page); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci counts = kcalloc(HV_DEPOSIT_MAX, sizeof(int), GFP_KERNEL); 4962306a36Sopenharmony_ci if (!counts) { 5062306a36Sopenharmony_ci free_page((unsigned long)pages); 5162306a36Sopenharmony_ci return -ENOMEM; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci /* Allocate all the pages before disabling interrupts */ 5562306a36Sopenharmony_ci i = 0; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci while (num_pages) { 5862306a36Sopenharmony_ci /* Find highest order we can actually allocate */ 5962306a36Sopenharmony_ci order = 31 - __builtin_clz(num_pages); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci while (1) { 6262306a36Sopenharmony_ci pages[i] = alloc_pages_node(node, GFP_KERNEL, order); 6362306a36Sopenharmony_ci if (pages[i]) 6462306a36Sopenharmony_ci break; 6562306a36Sopenharmony_ci if (!order) { 6662306a36Sopenharmony_ci ret = -ENOMEM; 6762306a36Sopenharmony_ci num_allocations = i; 6862306a36Sopenharmony_ci goto err_free_allocations; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci --order; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci split_page(pages[i], order); 7462306a36Sopenharmony_ci counts[i] = 1 << order; 7562306a36Sopenharmony_ci num_pages -= counts[i]; 7662306a36Sopenharmony_ci i++; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci num_allocations = i; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci local_irq_save(flags); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci input_page = *this_cpu_ptr(hyperv_pcpu_input_arg); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci input_page->partition_id = partition_id; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* Populate gpa_page_list - these will fit on the input page */ 8762306a36Sopenharmony_ci for (i = 0, page_count = 0; i < num_allocations; ++i) { 8862306a36Sopenharmony_ci base_pfn = page_to_pfn(pages[i]); 8962306a36Sopenharmony_ci for (j = 0; j < counts[i]; ++j, ++page_count) 9062306a36Sopenharmony_ci input_page->gpa_page_list[page_count] = base_pfn + j; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci status = hv_do_rep_hypercall(HVCALL_DEPOSIT_MEMORY, 9362306a36Sopenharmony_ci page_count, 0, input_page, NULL); 9462306a36Sopenharmony_ci local_irq_restore(flags); 9562306a36Sopenharmony_ci if (!hv_result_success(status)) { 9662306a36Sopenharmony_ci pr_err("Failed to deposit pages: %lld\n", status); 9762306a36Sopenharmony_ci ret = hv_result(status); 9862306a36Sopenharmony_ci goto err_free_allocations; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci ret = 0; 10262306a36Sopenharmony_ci goto free_buf; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cierr_free_allocations: 10562306a36Sopenharmony_ci for (i = 0; i < num_allocations; ++i) { 10662306a36Sopenharmony_ci base_pfn = page_to_pfn(pages[i]); 10762306a36Sopenharmony_ci for (j = 0; j < counts[i]; ++j) 10862306a36Sopenharmony_ci __free_page(pfn_to_page(base_pfn + j)); 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cifree_buf: 11262306a36Sopenharmony_ci free_page((unsigned long)pages); 11362306a36Sopenharmony_ci kfree(counts); 11462306a36Sopenharmony_ci return ret; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ciint hv_call_add_logical_proc(int node, u32 lp_index, u32 apic_id) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct hv_add_logical_processor_in *input; 12062306a36Sopenharmony_ci struct hv_add_logical_processor_out *output; 12162306a36Sopenharmony_ci u64 status; 12262306a36Sopenharmony_ci unsigned long flags; 12362306a36Sopenharmony_ci int ret = HV_STATUS_SUCCESS; 12462306a36Sopenharmony_ci int pxm = node_to_pxm(node); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* 12762306a36Sopenharmony_ci * When adding a logical processor, the hypervisor may return 12862306a36Sopenharmony_ci * HV_STATUS_INSUFFICIENT_MEMORY. When that happens, we deposit more 12962306a36Sopenharmony_ci * pages and retry. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_ci do { 13262306a36Sopenharmony_ci local_irq_save(flags); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci input = *this_cpu_ptr(hyperv_pcpu_input_arg); 13562306a36Sopenharmony_ci /* We don't do anything with the output right now */ 13662306a36Sopenharmony_ci output = *this_cpu_ptr(hyperv_pcpu_output_arg); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci input->lp_index = lp_index; 13962306a36Sopenharmony_ci input->apic_id = apic_id; 14062306a36Sopenharmony_ci input->flags = 0; 14162306a36Sopenharmony_ci input->proximity_domain_info.domain_id = pxm; 14262306a36Sopenharmony_ci input->proximity_domain_info.flags.reserved = 0; 14362306a36Sopenharmony_ci input->proximity_domain_info.flags.proximity_info_valid = 1; 14462306a36Sopenharmony_ci input->proximity_domain_info.flags.proximity_preferred = 1; 14562306a36Sopenharmony_ci status = hv_do_hypercall(HVCALL_ADD_LOGICAL_PROCESSOR, 14662306a36Sopenharmony_ci input, output); 14762306a36Sopenharmony_ci local_irq_restore(flags); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) { 15062306a36Sopenharmony_ci if (!hv_result_success(status)) { 15162306a36Sopenharmony_ci pr_err("%s: cpu %u apic ID %u, %lld\n", __func__, 15262306a36Sopenharmony_ci lp_index, apic_id, status); 15362306a36Sopenharmony_ci ret = hv_result(status); 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci break; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci ret = hv_call_deposit_pages(node, hv_current_partition_id, 1); 15862306a36Sopenharmony_ci } while (!ret); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci return ret; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ciint hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci struct hv_create_vp *input; 16662306a36Sopenharmony_ci u64 status; 16762306a36Sopenharmony_ci unsigned long irq_flags; 16862306a36Sopenharmony_ci int ret = HV_STATUS_SUCCESS; 16962306a36Sopenharmony_ci int pxm = node_to_pxm(node); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* Root VPs don't seem to need pages deposited */ 17262306a36Sopenharmony_ci if (partition_id != hv_current_partition_id) { 17362306a36Sopenharmony_ci /* The value 90 is empirically determined. It may change. */ 17462306a36Sopenharmony_ci ret = hv_call_deposit_pages(node, partition_id, 90); 17562306a36Sopenharmony_ci if (ret) 17662306a36Sopenharmony_ci return ret; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci do { 18062306a36Sopenharmony_ci local_irq_save(irq_flags); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci input = *this_cpu_ptr(hyperv_pcpu_input_arg); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci input->partition_id = partition_id; 18562306a36Sopenharmony_ci input->vp_index = vp_index; 18662306a36Sopenharmony_ci input->flags = flags; 18762306a36Sopenharmony_ci input->subnode_type = HvSubnodeAny; 18862306a36Sopenharmony_ci if (node != NUMA_NO_NODE) { 18962306a36Sopenharmony_ci input->proximity_domain_info.domain_id = pxm; 19062306a36Sopenharmony_ci input->proximity_domain_info.flags.reserved = 0; 19162306a36Sopenharmony_ci input->proximity_domain_info.flags.proximity_info_valid = 1; 19262306a36Sopenharmony_ci input->proximity_domain_info.flags.proximity_preferred = 1; 19362306a36Sopenharmony_ci } else { 19462306a36Sopenharmony_ci input->proximity_domain_info.as_uint64 = 0; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci status = hv_do_hypercall(HVCALL_CREATE_VP, input, NULL); 19762306a36Sopenharmony_ci local_irq_restore(irq_flags); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) { 20062306a36Sopenharmony_ci if (!hv_result_success(status)) { 20162306a36Sopenharmony_ci pr_err("%s: vcpu %u, lp %u, %lld\n", __func__, 20262306a36Sopenharmony_ci vp_index, flags, status); 20362306a36Sopenharmony_ci ret = hv_result(status); 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci break; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci ret = hv_call_deposit_pages(node, partition_id, 1); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci } while (!ret); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci return ret; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 214