xref: /openbmc/linux/arch/x86/hyperv/hv_proc.c (revision 753ed9c9)
186b5ec35SWei Liu // SPDX-License-Identifier: GPL-2.0
286b5ec35SWei Liu #include <linux/types.h>
386b5ec35SWei Liu #include <linux/vmalloc.h>
486b5ec35SWei Liu #include <linux/mm.h>
586b5ec35SWei Liu #include <linux/clockchips.h>
686b5ec35SWei Liu #include <linux/acpi.h>
786b5ec35SWei Liu #include <linux/hyperv.h>
886b5ec35SWei Liu #include <linux/slab.h>
986b5ec35SWei Liu #include <linux/cpuhotplug.h>
1086b5ec35SWei Liu #include <linux/minmax.h>
1186b5ec35SWei Liu #include <asm/hypervisor.h>
1286b5ec35SWei Liu #include <asm/mshyperv.h>
1386b5ec35SWei Liu #include <asm/apic.h>
1486b5ec35SWei Liu 
1586b5ec35SWei Liu #include <asm/trace/hyperv.h>
1686b5ec35SWei Liu 
1786b5ec35SWei Liu /*
1886b5ec35SWei Liu  * See struct hv_deposit_memory. The first u64 is partition ID, the rest
1986b5ec35SWei Liu  * are GPAs.
2086b5ec35SWei Liu  */
2186b5ec35SWei Liu #define HV_DEPOSIT_MAX (HV_HYP_PAGE_SIZE / sizeof(u64) - 1)
2286b5ec35SWei Liu 
2386b5ec35SWei Liu /* Deposits exact number of pages. Must be called with interrupts enabled.  */
hv_call_deposit_pages(int node,u64 partition_id,u32 num_pages)2486b5ec35SWei Liu int hv_call_deposit_pages(int node, u64 partition_id, u32 num_pages)
2586b5ec35SWei Liu {
2686b5ec35SWei Liu 	struct page **pages, *page;
2786b5ec35SWei Liu 	int *counts;
2886b5ec35SWei Liu 	int num_allocations;
2986b5ec35SWei Liu 	int i, j, page_count;
3086b5ec35SWei Liu 	int order;
3186b5ec35SWei Liu 	u64 status;
3286b5ec35SWei Liu 	int ret;
3386b5ec35SWei Liu 	u64 base_pfn;
3486b5ec35SWei Liu 	struct hv_deposit_memory *input_page;
3586b5ec35SWei Liu 	unsigned long flags;
3686b5ec35SWei Liu 
3786b5ec35SWei Liu 	if (num_pages > HV_DEPOSIT_MAX)
3886b5ec35SWei Liu 		return -E2BIG;
3986b5ec35SWei Liu 	if (!num_pages)
4086b5ec35SWei Liu 		return 0;
4186b5ec35SWei Liu 
4286b5ec35SWei Liu 	/* One buffer for page pointers and counts */
4386b5ec35SWei Liu 	page = alloc_page(GFP_KERNEL);
4486b5ec35SWei Liu 	if (!page)
4586b5ec35SWei Liu 		return -ENOMEM;
4686b5ec35SWei Liu 	pages = page_address(page);
4786b5ec35SWei Liu 
4886b5ec35SWei Liu 	counts = kcalloc(HV_DEPOSIT_MAX, sizeof(int), GFP_KERNEL);
4986b5ec35SWei Liu 	if (!counts) {
5086b5ec35SWei Liu 		free_page((unsigned long)pages);
5186b5ec35SWei Liu 		return -ENOMEM;
5286b5ec35SWei Liu 	}
5386b5ec35SWei Liu 
5486b5ec35SWei Liu 	/* Allocate all the pages before disabling interrupts */
5586b5ec35SWei Liu 	i = 0;
5686b5ec35SWei Liu 
5786b5ec35SWei Liu 	while (num_pages) {
5886b5ec35SWei Liu 		/* Find highest order we can actually allocate */
5986b5ec35SWei Liu 		order = 31 - __builtin_clz(num_pages);
6086b5ec35SWei Liu 
6186b5ec35SWei Liu 		while (1) {
6286b5ec35SWei Liu 			pages[i] = alloc_pages_node(node, GFP_KERNEL, order);
6386b5ec35SWei Liu 			if (pages[i])
6486b5ec35SWei Liu 				break;
6586b5ec35SWei Liu 			if (!order) {
6686b5ec35SWei Liu 				ret = -ENOMEM;
6786b5ec35SWei Liu 				num_allocations = i;
6886b5ec35SWei Liu 				goto err_free_allocations;
6986b5ec35SWei Liu 			}
7086b5ec35SWei Liu 			--order;
7186b5ec35SWei Liu 		}
7286b5ec35SWei Liu 
7386b5ec35SWei Liu 		split_page(pages[i], order);
7486b5ec35SWei Liu 		counts[i] = 1 << order;
7586b5ec35SWei Liu 		num_pages -= counts[i];
7686b5ec35SWei Liu 		i++;
7786b5ec35SWei Liu 	}
7886b5ec35SWei Liu 	num_allocations = i;
7986b5ec35SWei Liu 
8086b5ec35SWei Liu 	local_irq_save(flags);
8186b5ec35SWei Liu 
8286b5ec35SWei Liu 	input_page = *this_cpu_ptr(hyperv_pcpu_input_arg);
8386b5ec35SWei Liu 
8486b5ec35SWei Liu 	input_page->partition_id = partition_id;
8586b5ec35SWei Liu 
8686b5ec35SWei Liu 	/* Populate gpa_page_list - these will fit on the input page */
8786b5ec35SWei Liu 	for (i = 0, page_count = 0; i < num_allocations; ++i) {
8886b5ec35SWei Liu 		base_pfn = page_to_pfn(pages[i]);
8986b5ec35SWei Liu 		for (j = 0; j < counts[i]; ++j, ++page_count)
9086b5ec35SWei Liu 			input_page->gpa_page_list[page_count] = base_pfn + j;
9186b5ec35SWei Liu 	}
9286b5ec35SWei Liu 	status = hv_do_rep_hypercall(HVCALL_DEPOSIT_MEMORY,
9386b5ec35SWei Liu 				     page_count, 0, input_page, NULL);
9486b5ec35SWei Liu 	local_irq_restore(flags);
95*753ed9c9SJoseph Salisbury 	if (!hv_result_success(status)) {
9686b5ec35SWei Liu 		pr_err("Failed to deposit pages: %lld\n", status);
97*753ed9c9SJoseph Salisbury 		ret = hv_result(status);
9886b5ec35SWei Liu 		goto err_free_allocations;
9986b5ec35SWei Liu 	}
10086b5ec35SWei Liu 
10186b5ec35SWei Liu 	ret = 0;
10286b5ec35SWei Liu 	goto free_buf;
10386b5ec35SWei Liu 
10486b5ec35SWei Liu err_free_allocations:
10586b5ec35SWei Liu 	for (i = 0; i < num_allocations; ++i) {
10686b5ec35SWei Liu 		base_pfn = page_to_pfn(pages[i]);
10786b5ec35SWei Liu 		for (j = 0; j < counts[i]; ++j)
10886b5ec35SWei Liu 			__free_page(pfn_to_page(base_pfn + j));
10986b5ec35SWei Liu 	}
11086b5ec35SWei Liu 
11186b5ec35SWei Liu free_buf:
11286b5ec35SWei Liu 	free_page((unsigned long)pages);
11386b5ec35SWei Liu 	kfree(counts);
11486b5ec35SWei Liu 	return ret;
11586b5ec35SWei Liu }
11686b5ec35SWei Liu 
hv_call_add_logical_proc(int node,u32 lp_index,u32 apic_id)11786b5ec35SWei Liu int hv_call_add_logical_proc(int node, u32 lp_index, u32 apic_id)
11886b5ec35SWei Liu {
11986b5ec35SWei Liu 	struct hv_add_logical_processor_in *input;
12086b5ec35SWei Liu 	struct hv_add_logical_processor_out *output;
12186b5ec35SWei Liu 	u64 status;
12286b5ec35SWei Liu 	unsigned long flags;
123*753ed9c9SJoseph Salisbury 	int ret = HV_STATUS_SUCCESS;
12486b5ec35SWei Liu 	int pxm = node_to_pxm(node);
12586b5ec35SWei Liu 
12686b5ec35SWei Liu 	/*
12786b5ec35SWei Liu 	 * When adding a logical processor, the hypervisor may return
12886b5ec35SWei Liu 	 * HV_STATUS_INSUFFICIENT_MEMORY. When that happens, we deposit more
12986b5ec35SWei Liu 	 * pages and retry.
13086b5ec35SWei Liu 	 */
13186b5ec35SWei Liu 	do {
13286b5ec35SWei Liu 		local_irq_save(flags);
13386b5ec35SWei Liu 
13486b5ec35SWei Liu 		input = *this_cpu_ptr(hyperv_pcpu_input_arg);
13586b5ec35SWei Liu 		/* We don't do anything with the output right now */
13686b5ec35SWei Liu 		output = *this_cpu_ptr(hyperv_pcpu_output_arg);
13786b5ec35SWei Liu 
13886b5ec35SWei Liu 		input->lp_index = lp_index;
13986b5ec35SWei Liu 		input->apic_id = apic_id;
14086b5ec35SWei Liu 		input->flags = 0;
14186b5ec35SWei Liu 		input->proximity_domain_info.domain_id = pxm;
14286b5ec35SWei Liu 		input->proximity_domain_info.flags.reserved = 0;
14386b5ec35SWei Liu 		input->proximity_domain_info.flags.proximity_info_valid = 1;
14486b5ec35SWei Liu 		input->proximity_domain_info.flags.proximity_preferred = 1;
14586b5ec35SWei Liu 		status = hv_do_hypercall(HVCALL_ADD_LOGICAL_PROCESSOR,
14686b5ec35SWei Liu 					 input, output);
14786b5ec35SWei Liu 		local_irq_restore(flags);
14886b5ec35SWei Liu 
149*753ed9c9SJoseph Salisbury 		if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) {
150*753ed9c9SJoseph Salisbury 			if (!hv_result_success(status)) {
15186b5ec35SWei Liu 				pr_err("%s: cpu %u apic ID %u, %lld\n", __func__,
15286b5ec35SWei Liu 				       lp_index, apic_id, status);
153*753ed9c9SJoseph Salisbury 				ret = hv_result(status);
15486b5ec35SWei Liu 			}
15586b5ec35SWei Liu 			break;
15686b5ec35SWei Liu 		}
15786b5ec35SWei Liu 		ret = hv_call_deposit_pages(node, hv_current_partition_id, 1);
15886b5ec35SWei Liu 	} while (!ret);
15986b5ec35SWei Liu 
16086b5ec35SWei Liu 	return ret;
16186b5ec35SWei Liu }
16286b5ec35SWei Liu 
hv_call_create_vp(int node,u64 partition_id,u32 vp_index,u32 flags)16386b5ec35SWei Liu int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags)
16486b5ec35SWei Liu {
16586b5ec35SWei Liu 	struct hv_create_vp *input;
16686b5ec35SWei Liu 	u64 status;
16786b5ec35SWei Liu 	unsigned long irq_flags;
168*753ed9c9SJoseph Salisbury 	int ret = HV_STATUS_SUCCESS;
16986b5ec35SWei Liu 	int pxm = node_to_pxm(node);
17086b5ec35SWei Liu 
17186b5ec35SWei Liu 	/* Root VPs don't seem to need pages deposited */
17286b5ec35SWei Liu 	if (partition_id != hv_current_partition_id) {
17386b5ec35SWei Liu 		/* The value 90 is empirically determined. It may change. */
17486b5ec35SWei Liu 		ret = hv_call_deposit_pages(node, partition_id, 90);
17586b5ec35SWei Liu 		if (ret)
17686b5ec35SWei Liu 			return ret;
17786b5ec35SWei Liu 	}
17886b5ec35SWei Liu 
17986b5ec35SWei Liu 	do {
18086b5ec35SWei Liu 		local_irq_save(irq_flags);
18186b5ec35SWei Liu 
18286b5ec35SWei Liu 		input = *this_cpu_ptr(hyperv_pcpu_input_arg);
18386b5ec35SWei Liu 
18486b5ec35SWei Liu 		input->partition_id = partition_id;
18586b5ec35SWei Liu 		input->vp_index = vp_index;
18686b5ec35SWei Liu 		input->flags = flags;
18786b5ec35SWei Liu 		input->subnode_type = HvSubnodeAny;
18886b5ec35SWei Liu 		if (node != NUMA_NO_NODE) {
18986b5ec35SWei Liu 			input->proximity_domain_info.domain_id = pxm;
19086b5ec35SWei Liu 			input->proximity_domain_info.flags.reserved = 0;
19186b5ec35SWei Liu 			input->proximity_domain_info.flags.proximity_info_valid = 1;
19286b5ec35SWei Liu 			input->proximity_domain_info.flags.proximity_preferred = 1;
19386b5ec35SWei Liu 		} else {
19486b5ec35SWei Liu 			input->proximity_domain_info.as_uint64 = 0;
19586b5ec35SWei Liu 		}
19686b5ec35SWei Liu 		status = hv_do_hypercall(HVCALL_CREATE_VP, input, NULL);
19786b5ec35SWei Liu 		local_irq_restore(irq_flags);
19886b5ec35SWei Liu 
199*753ed9c9SJoseph Salisbury 		if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) {
200*753ed9c9SJoseph Salisbury 			if (!hv_result_success(status)) {
20186b5ec35SWei Liu 				pr_err("%s: vcpu %u, lp %u, %lld\n", __func__,
20286b5ec35SWei Liu 				       vp_index, flags, status);
203*753ed9c9SJoseph Salisbury 				ret = hv_result(status);
20486b5ec35SWei Liu 			}
20586b5ec35SWei Liu 			break;
20686b5ec35SWei Liu 		}
20786b5ec35SWei Liu 		ret = hv_call_deposit_pages(node, partition_id, 1);
20886b5ec35SWei Liu 
20986b5ec35SWei Liu 	} while (!ret);
21086b5ec35SWei Liu 
21186b5ec35SWei Liu 	return ret;
21286b5ec35SWei Liu }
21386b5ec35SWei Liu 
214