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