18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2009, Microsoft Corporation. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: 68c2ecf20Sopenharmony_ci * Haiyang Zhang <haiyangz@microsoft.com> 78c2ecf20Sopenharmony_ci * Hank Janssen <hjanssen@microsoft.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/mm.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 158c2ecf20Sopenharmony_ci#include <linux/hyperv.h> 168c2ecf20Sopenharmony_ci#include <linux/version.h> 178c2ecf20Sopenharmony_ci#include <linux/random.h> 188c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 198c2ecf20Sopenharmony_ci#include <clocksource/hyperv_timer.h> 208c2ecf20Sopenharmony_ci#include <asm/mshyperv.h> 218c2ecf20Sopenharmony_ci#include "hyperv_vmbus.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* The one and only */ 248c2ecf20Sopenharmony_cistruct hv_context hv_context; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* 278c2ecf20Sopenharmony_ci * hv_init - Main initialization routine. 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * This routine must be called before any other routines in here are called 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_ciint hv_init(void) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci hv_context.cpu_context = alloc_percpu(struct hv_per_cpu_context); 348c2ecf20Sopenharmony_ci if (!hv_context.cpu_context) 358c2ecf20Sopenharmony_ci return -ENOMEM; 368c2ecf20Sopenharmony_ci return 0; 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* 408c2ecf20Sopenharmony_ci * hv_post_message - Post a message using the hypervisor message IPC. 418c2ecf20Sopenharmony_ci * 428c2ecf20Sopenharmony_ci * This involves a hypercall. 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ciint hv_post_message(union hv_connection_id connection_id, 458c2ecf20Sopenharmony_ci enum hv_message_type message_type, 468c2ecf20Sopenharmony_ci void *payload, size_t payload_size) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct hv_input_post_message *aligned_msg; 498c2ecf20Sopenharmony_ci struct hv_per_cpu_context *hv_cpu; 508c2ecf20Sopenharmony_ci u64 status; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT) 538c2ecf20Sopenharmony_ci return -EMSGSIZE; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci hv_cpu = get_cpu_ptr(hv_context.cpu_context); 568c2ecf20Sopenharmony_ci aligned_msg = hv_cpu->post_msg_page; 578c2ecf20Sopenharmony_ci aligned_msg->connectionid = connection_id; 588c2ecf20Sopenharmony_ci aligned_msg->reserved = 0; 598c2ecf20Sopenharmony_ci aligned_msg->message_type = message_type; 608c2ecf20Sopenharmony_ci aligned_msg->payload_size = payload_size; 618c2ecf20Sopenharmony_ci memcpy((void *)aligned_msg->payload, payload, payload_size); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci status = hv_do_hypercall(HVCALL_POST_MESSAGE, aligned_msg, NULL); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci /* Preemption must remain disabled until after the hypercall 668c2ecf20Sopenharmony_ci * so some other thread can't get scheduled onto this cpu and 678c2ecf20Sopenharmony_ci * corrupt the per-cpu post_msg_page 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_ci put_cpu_ptr(hv_cpu); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci return status & 0xFFFF; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ciint hv_synic_alloc(void) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci int cpu; 778c2ecf20Sopenharmony_ci struct hv_per_cpu_context *hv_cpu; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci /* 808c2ecf20Sopenharmony_ci * First, zero all per-cpu memory areas so hv_synic_free() can 818c2ecf20Sopenharmony_ci * detect what memory has been allocated and cleanup properly 828c2ecf20Sopenharmony_ci * after any failures. 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_ci for_each_present_cpu(cpu) { 858c2ecf20Sopenharmony_ci hv_cpu = per_cpu_ptr(hv_context.cpu_context, cpu); 868c2ecf20Sopenharmony_ci memset(hv_cpu, 0, sizeof(*hv_cpu)); 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci hv_context.hv_numa_map = kcalloc(nr_node_ids, sizeof(struct cpumask), 908c2ecf20Sopenharmony_ci GFP_KERNEL); 918c2ecf20Sopenharmony_ci if (hv_context.hv_numa_map == NULL) { 928c2ecf20Sopenharmony_ci pr_err("Unable to allocate NUMA map\n"); 938c2ecf20Sopenharmony_ci goto err; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci for_each_present_cpu(cpu) { 978c2ecf20Sopenharmony_ci hv_cpu = per_cpu_ptr(hv_context.cpu_context, cpu); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci tasklet_init(&hv_cpu->msg_dpc, 1008c2ecf20Sopenharmony_ci vmbus_on_msg_dpc, (unsigned long) hv_cpu); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci hv_cpu->synic_message_page = 1038c2ecf20Sopenharmony_ci (void *)get_zeroed_page(GFP_ATOMIC); 1048c2ecf20Sopenharmony_ci if (hv_cpu->synic_message_page == NULL) { 1058c2ecf20Sopenharmony_ci pr_err("Unable to allocate SYNIC message page\n"); 1068c2ecf20Sopenharmony_ci goto err; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci hv_cpu->synic_event_page = (void *)get_zeroed_page(GFP_ATOMIC); 1108c2ecf20Sopenharmony_ci if (hv_cpu->synic_event_page == NULL) { 1118c2ecf20Sopenharmony_ci pr_err("Unable to allocate SYNIC event page\n"); 1128c2ecf20Sopenharmony_ci goto err; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci hv_cpu->post_msg_page = (void *)get_zeroed_page(GFP_ATOMIC); 1168c2ecf20Sopenharmony_ci if (hv_cpu->post_msg_page == NULL) { 1178c2ecf20Sopenharmony_ci pr_err("Unable to allocate post msg page\n"); 1188c2ecf20Sopenharmony_ci goto err; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return 0; 1238c2ecf20Sopenharmony_cierr: 1248c2ecf20Sopenharmony_ci /* 1258c2ecf20Sopenharmony_ci * Any memory allocations that succeeded will be freed when 1268c2ecf20Sopenharmony_ci * the caller cleans up by calling hv_synic_free() 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_ci return -ENOMEM; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_civoid hv_synic_free(void) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci int cpu; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci for_each_present_cpu(cpu) { 1378c2ecf20Sopenharmony_ci struct hv_per_cpu_context *hv_cpu 1388c2ecf20Sopenharmony_ci = per_cpu_ptr(hv_context.cpu_context, cpu); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci free_page((unsigned long)hv_cpu->synic_event_page); 1418c2ecf20Sopenharmony_ci free_page((unsigned long)hv_cpu->synic_message_page); 1428c2ecf20Sopenharmony_ci free_page((unsigned long)hv_cpu->post_msg_page); 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci kfree(hv_context.hv_numa_map); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci/* 1498c2ecf20Sopenharmony_ci * hv_synic_init - Initialize the Synthetic Interrupt Controller. 1508c2ecf20Sopenharmony_ci * 1518c2ecf20Sopenharmony_ci * If it is already initialized by another entity (ie x2v shim), we need to 1528c2ecf20Sopenharmony_ci * retrieve the initialized message and event pages. Otherwise, we create and 1538c2ecf20Sopenharmony_ci * initialize the message and event pages. 1548c2ecf20Sopenharmony_ci */ 1558c2ecf20Sopenharmony_civoid hv_synic_enable_regs(unsigned int cpu) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct hv_per_cpu_context *hv_cpu 1588c2ecf20Sopenharmony_ci = per_cpu_ptr(hv_context.cpu_context, cpu); 1598c2ecf20Sopenharmony_ci union hv_synic_simp simp; 1608c2ecf20Sopenharmony_ci union hv_synic_siefp siefp; 1618c2ecf20Sopenharmony_ci union hv_synic_sint shared_sint; 1628c2ecf20Sopenharmony_ci union hv_synic_scontrol sctrl; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* Setup the Synic's message page */ 1658c2ecf20Sopenharmony_ci hv_get_simp(simp.as_uint64); 1668c2ecf20Sopenharmony_ci simp.simp_enabled = 1; 1678c2ecf20Sopenharmony_ci simp.base_simp_gpa = virt_to_phys(hv_cpu->synic_message_page) 1688c2ecf20Sopenharmony_ci >> HV_HYP_PAGE_SHIFT; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci hv_set_simp(simp.as_uint64); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* Setup the Synic's event page */ 1738c2ecf20Sopenharmony_ci hv_get_siefp(siefp.as_uint64); 1748c2ecf20Sopenharmony_ci siefp.siefp_enabled = 1; 1758c2ecf20Sopenharmony_ci siefp.base_siefp_gpa = virt_to_phys(hv_cpu->synic_event_page) 1768c2ecf20Sopenharmony_ci >> HV_HYP_PAGE_SHIFT; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci hv_set_siefp(siefp.as_uint64); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* Setup the shared SINT. */ 1818c2ecf20Sopenharmony_ci hv_get_synint_state(VMBUS_MESSAGE_SINT, shared_sint.as_uint64); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci shared_sint.vector = hv_get_vector(); 1848c2ecf20Sopenharmony_ci shared_sint.masked = false; 1858c2ecf20Sopenharmony_ci shared_sint.auto_eoi = hv_recommend_using_aeoi(); 1868c2ecf20Sopenharmony_ci hv_set_synint_state(VMBUS_MESSAGE_SINT, shared_sint.as_uint64); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* Enable the global synic bit */ 1898c2ecf20Sopenharmony_ci hv_get_synic_state(sctrl.as_uint64); 1908c2ecf20Sopenharmony_ci sctrl.enable = 1; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci hv_set_synic_state(sctrl.as_uint64); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ciint hv_synic_init(unsigned int cpu) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci hv_synic_enable_regs(cpu); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci hv_stimer_legacy_init(cpu, VMBUS_MESSAGE_SINT); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci return 0; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci/* 2058c2ecf20Sopenharmony_ci * hv_synic_cleanup - Cleanup routine for hv_synic_init(). 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_civoid hv_synic_disable_regs(unsigned int cpu) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci union hv_synic_sint shared_sint; 2108c2ecf20Sopenharmony_ci union hv_synic_simp simp; 2118c2ecf20Sopenharmony_ci union hv_synic_siefp siefp; 2128c2ecf20Sopenharmony_ci union hv_synic_scontrol sctrl; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci hv_get_synint_state(VMBUS_MESSAGE_SINT, shared_sint.as_uint64); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci shared_sint.masked = 1; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* Need to correctly cleanup in the case of SMP!!! */ 2198c2ecf20Sopenharmony_ci /* Disable the interrupt */ 2208c2ecf20Sopenharmony_ci hv_set_synint_state(VMBUS_MESSAGE_SINT, shared_sint.as_uint64); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci hv_get_simp(simp.as_uint64); 2238c2ecf20Sopenharmony_ci simp.simp_enabled = 0; 2248c2ecf20Sopenharmony_ci simp.base_simp_gpa = 0; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci hv_set_simp(simp.as_uint64); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci hv_get_siefp(siefp.as_uint64); 2298c2ecf20Sopenharmony_ci siefp.siefp_enabled = 0; 2308c2ecf20Sopenharmony_ci siefp.base_siefp_gpa = 0; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci hv_set_siefp(siefp.as_uint64); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* Disable the global synic bit */ 2358c2ecf20Sopenharmony_ci hv_get_synic_state(sctrl.as_uint64); 2368c2ecf20Sopenharmony_ci sctrl.enable = 0; 2378c2ecf20Sopenharmony_ci hv_set_synic_state(sctrl.as_uint64); 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ciint hv_synic_cleanup(unsigned int cpu) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci struct vmbus_channel *channel, *sc; 2438c2ecf20Sopenharmony_ci bool channel_found = false; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci /* 2468c2ecf20Sopenharmony_ci * Hyper-V does not provide a way to change the connect CPU once 2478c2ecf20Sopenharmony_ci * it is set; we must prevent the connect CPU from going offline 2488c2ecf20Sopenharmony_ci * while the VM is running normally. But in the panic or kexec() 2498c2ecf20Sopenharmony_ci * path where the vmbus is already disconnected, the CPU must be 2508c2ecf20Sopenharmony_ci * allowed to shut down. 2518c2ecf20Sopenharmony_ci */ 2528c2ecf20Sopenharmony_ci if (cpu == VMBUS_CONNECT_CPU && 2538c2ecf20Sopenharmony_ci vmbus_connection.conn_state == CONNECTED) 2548c2ecf20Sopenharmony_ci return -EBUSY; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* 2578c2ecf20Sopenharmony_ci * Search for channels which are bound to the CPU we're about to 2588c2ecf20Sopenharmony_ci * cleanup. In case we find one and vmbus is still connected, we 2598c2ecf20Sopenharmony_ci * fail; this will effectively prevent CPU offlining. 2608c2ecf20Sopenharmony_ci * 2618c2ecf20Sopenharmony_ci * TODO: Re-bind the channels to different CPUs. 2628c2ecf20Sopenharmony_ci */ 2638c2ecf20Sopenharmony_ci mutex_lock(&vmbus_connection.channel_mutex); 2648c2ecf20Sopenharmony_ci list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { 2658c2ecf20Sopenharmony_ci if (channel->target_cpu == cpu) { 2668c2ecf20Sopenharmony_ci channel_found = true; 2678c2ecf20Sopenharmony_ci break; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci list_for_each_entry(sc, &channel->sc_list, sc_list) { 2708c2ecf20Sopenharmony_ci if (sc->target_cpu == cpu) { 2718c2ecf20Sopenharmony_ci channel_found = true; 2728c2ecf20Sopenharmony_ci break; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci if (channel_found) 2768c2ecf20Sopenharmony_ci break; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci mutex_unlock(&vmbus_connection.channel_mutex); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (channel_found && vmbus_connection.conn_state == CONNECTED) 2818c2ecf20Sopenharmony_ci return -EBUSY; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci hv_stimer_legacy_cleanup(cpu); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci hv_synic_disable_regs(cpu); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return 0; 2888c2ecf20Sopenharmony_ci} 289