18c2ecf20Sopenharmony_ci/****************************************************************************** 28c2ecf20Sopenharmony_ci * pcpu.c 38c2ecf20Sopenharmony_ci * Management physical cpu in dom0, get pcpu info and provide sys interface 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2012 Intel Corporation 68c2ecf20Sopenharmony_ci * Author: Liu, Jinsong <jinsong.liu@intel.com> 78c2ecf20Sopenharmony_ci * Author: Jiang, Yunhong <yunhong.jiang@intel.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 108c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License version 2 118c2ecf20Sopenharmony_ci * as published by the Free Software Foundation; or, when distributed 128c2ecf20Sopenharmony_ci * separately from the Linux kernel or incorporated into other 138c2ecf20Sopenharmony_ci * software packages, subject to the following license: 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy 168c2ecf20Sopenharmony_ci * of this source file (the "Software"), to deal in the Software without 178c2ecf20Sopenharmony_ci * restriction, including without limitation the rights to use, copy, modify, 188c2ecf20Sopenharmony_ci * merge, publish, distribute, sublicense, and/or sell copies of the Software, 198c2ecf20Sopenharmony_ci * and to permit persons to whom the Software is furnished to do so, subject to 208c2ecf20Sopenharmony_ci * the following conditions: 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 238c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software. 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 268c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 278c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 288c2ecf20Sopenharmony_ci * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 298c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 308c2ecf20Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 318c2ecf20Sopenharmony_ci * IN THE SOFTWARE. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "xen_cpu: " fmt 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 378c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 388c2ecf20Sopenharmony_ci#include <linux/cpu.h> 398c2ecf20Sopenharmony_ci#include <linux/stat.h> 408c2ecf20Sopenharmony_ci#include <linux/capability.h> 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#include <xen/xen.h> 438c2ecf20Sopenharmony_ci#include <xen/acpi.h> 448c2ecf20Sopenharmony_ci#include <xen/xenbus.h> 458c2ecf20Sopenharmony_ci#include <xen/events.h> 468c2ecf20Sopenharmony_ci#include <xen/interface/platform.h> 478c2ecf20Sopenharmony_ci#include <asm/xen/hypervisor.h> 488c2ecf20Sopenharmony_ci#include <asm/xen/hypercall.h> 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* 528c2ecf20Sopenharmony_ci * @cpu_id: Xen physical cpu logic number 538c2ecf20Sopenharmony_ci * @flags: Xen physical cpu status flag 548c2ecf20Sopenharmony_ci * - XEN_PCPU_FLAGS_ONLINE: cpu is online 558c2ecf20Sopenharmony_ci * - XEN_PCPU_FLAGS_INVALID: cpu is not present 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_cistruct pcpu { 588c2ecf20Sopenharmony_ci struct list_head list; 598c2ecf20Sopenharmony_ci struct device dev; 608c2ecf20Sopenharmony_ci uint32_t cpu_id; 618c2ecf20Sopenharmony_ci uint32_t acpi_id; 628c2ecf20Sopenharmony_ci uint32_t flags; 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic struct bus_type xen_pcpu_subsys = { 668c2ecf20Sopenharmony_ci .name = "xen_cpu", 678c2ecf20Sopenharmony_ci .dev_name = "xen_cpu", 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(xen_pcpu_lock); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic LIST_HEAD(xen_pcpus); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic int xen_pcpu_down(uint32_t cpu_id) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct xen_platform_op op = { 778c2ecf20Sopenharmony_ci .cmd = XENPF_cpu_offline, 788c2ecf20Sopenharmony_ci .interface_version = XENPF_INTERFACE_VERSION, 798c2ecf20Sopenharmony_ci .u.cpu_ol.cpuid = cpu_id, 808c2ecf20Sopenharmony_ci }; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci return HYPERVISOR_platform_op(&op); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic int xen_pcpu_up(uint32_t cpu_id) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct xen_platform_op op = { 888c2ecf20Sopenharmony_ci .cmd = XENPF_cpu_online, 898c2ecf20Sopenharmony_ci .interface_version = XENPF_INTERFACE_VERSION, 908c2ecf20Sopenharmony_ci .u.cpu_ol.cpuid = cpu_id, 918c2ecf20Sopenharmony_ci }; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci return HYPERVISOR_platform_op(&op); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic ssize_t show_online(struct device *dev, 978c2ecf20Sopenharmony_ci struct device_attribute *attr, 988c2ecf20Sopenharmony_ci char *buf) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct pcpu *cpu = container_of(dev, struct pcpu, dev); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", !!(cpu->flags & XEN_PCPU_FLAGS_ONLINE)); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic ssize_t __ref store_online(struct device *dev, 1068c2ecf20Sopenharmony_ci struct device_attribute *attr, 1078c2ecf20Sopenharmony_ci const char *buf, size_t count) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct pcpu *pcpu = container_of(dev, struct pcpu, dev); 1108c2ecf20Sopenharmony_ci unsigned long long val; 1118c2ecf20Sopenharmony_ci ssize_t ret; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 1148c2ecf20Sopenharmony_ci return -EPERM; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (kstrtoull(buf, 0, &val) < 0) 1178c2ecf20Sopenharmony_ci return -EINVAL; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci switch (val) { 1208c2ecf20Sopenharmony_ci case 0: 1218c2ecf20Sopenharmony_ci ret = xen_pcpu_down(pcpu->cpu_id); 1228c2ecf20Sopenharmony_ci break; 1238c2ecf20Sopenharmony_ci case 1: 1248c2ecf20Sopenharmony_ci ret = xen_pcpu_up(pcpu->cpu_id); 1258c2ecf20Sopenharmony_ci break; 1268c2ecf20Sopenharmony_ci default: 1278c2ecf20Sopenharmony_ci ret = -EINVAL; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (ret >= 0) 1318c2ecf20Sopenharmony_ci ret = count; 1328c2ecf20Sopenharmony_ci return ret; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_cistatic DEVICE_ATTR(online, S_IRUGO | S_IWUSR, show_online, store_online); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic struct attribute *pcpu_dev_attrs[] = { 1378c2ecf20Sopenharmony_ci &dev_attr_online.attr, 1388c2ecf20Sopenharmony_ci NULL 1398c2ecf20Sopenharmony_ci}; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic umode_t pcpu_dev_is_visible(struct kobject *kobj, 1428c2ecf20Sopenharmony_ci struct attribute *attr, int idx) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 1458c2ecf20Sopenharmony_ci /* 1468c2ecf20Sopenharmony_ci * Xen never offline cpu0 due to several restrictions 1478c2ecf20Sopenharmony_ci * and assumptions. This basically doesn't add a sys control 1488c2ecf20Sopenharmony_ci * to user, one cannot attempt to offline BSP. 1498c2ecf20Sopenharmony_ci */ 1508c2ecf20Sopenharmony_ci return dev->id ? attr->mode : 0; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic const struct attribute_group pcpu_dev_group = { 1548c2ecf20Sopenharmony_ci .attrs = pcpu_dev_attrs, 1558c2ecf20Sopenharmony_ci .is_visible = pcpu_dev_is_visible, 1568c2ecf20Sopenharmony_ci}; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic const struct attribute_group *pcpu_dev_groups[] = { 1598c2ecf20Sopenharmony_ci &pcpu_dev_group, 1608c2ecf20Sopenharmony_ci NULL 1618c2ecf20Sopenharmony_ci}; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic bool xen_pcpu_online(uint32_t flags) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci return !!(flags & XEN_PCPU_FLAGS_ONLINE); 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic void pcpu_online_status(struct xenpf_pcpuinfo *info, 1698c2ecf20Sopenharmony_ci struct pcpu *pcpu) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci if (xen_pcpu_online(info->flags) && 1728c2ecf20Sopenharmony_ci !xen_pcpu_online(pcpu->flags)) { 1738c2ecf20Sopenharmony_ci /* the pcpu is onlined */ 1748c2ecf20Sopenharmony_ci pcpu->flags |= XEN_PCPU_FLAGS_ONLINE; 1758c2ecf20Sopenharmony_ci kobject_uevent(&pcpu->dev.kobj, KOBJ_ONLINE); 1768c2ecf20Sopenharmony_ci } else if (!xen_pcpu_online(info->flags) && 1778c2ecf20Sopenharmony_ci xen_pcpu_online(pcpu->flags)) { 1788c2ecf20Sopenharmony_ci /* The pcpu is offlined */ 1798c2ecf20Sopenharmony_ci pcpu->flags &= ~XEN_PCPU_FLAGS_ONLINE; 1808c2ecf20Sopenharmony_ci kobject_uevent(&pcpu->dev.kobj, KOBJ_OFFLINE); 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic struct pcpu *get_pcpu(uint32_t cpu_id) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct pcpu *pcpu; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci list_for_each_entry(pcpu, &xen_pcpus, list) { 1898c2ecf20Sopenharmony_ci if (pcpu->cpu_id == cpu_id) 1908c2ecf20Sopenharmony_ci return pcpu; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return NULL; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic void pcpu_release(struct device *dev) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct pcpu *pcpu = container_of(dev, struct pcpu, dev); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci list_del(&pcpu->list); 2018c2ecf20Sopenharmony_ci kfree(pcpu); 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic void unregister_and_remove_pcpu(struct pcpu *pcpu) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci struct device *dev; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (!pcpu) 2098c2ecf20Sopenharmony_ci return; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci dev = &pcpu->dev; 2128c2ecf20Sopenharmony_ci /* pcpu remove would be implicitly done */ 2138c2ecf20Sopenharmony_ci device_unregister(dev); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic int register_pcpu(struct pcpu *pcpu) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct device *dev; 2198c2ecf20Sopenharmony_ci int err = -EINVAL; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (!pcpu) 2228c2ecf20Sopenharmony_ci return err; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci dev = &pcpu->dev; 2258c2ecf20Sopenharmony_ci dev->bus = &xen_pcpu_subsys; 2268c2ecf20Sopenharmony_ci dev->id = pcpu->cpu_id; 2278c2ecf20Sopenharmony_ci dev->release = pcpu_release; 2288c2ecf20Sopenharmony_ci dev->groups = pcpu_dev_groups; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci err = device_register(dev); 2318c2ecf20Sopenharmony_ci if (err) { 2328c2ecf20Sopenharmony_ci put_device(dev); 2338c2ecf20Sopenharmony_ci return err; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return 0; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic struct pcpu *create_and_register_pcpu(struct xenpf_pcpuinfo *info) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct pcpu *pcpu; 2428c2ecf20Sopenharmony_ci int err; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (info->flags & XEN_PCPU_FLAGS_INVALID) 2458c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci pcpu = kzalloc(sizeof(struct pcpu), GFP_KERNEL); 2488c2ecf20Sopenharmony_ci if (!pcpu) 2498c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&pcpu->list); 2528c2ecf20Sopenharmony_ci pcpu->cpu_id = info->xen_cpuid; 2538c2ecf20Sopenharmony_ci pcpu->acpi_id = info->acpi_id; 2548c2ecf20Sopenharmony_ci pcpu->flags = info->flags; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* Need hold on xen_pcpu_lock before pcpu list manipulations */ 2578c2ecf20Sopenharmony_ci list_add_tail(&pcpu->list, &xen_pcpus); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci err = register_pcpu(pcpu); 2608c2ecf20Sopenharmony_ci if (err) { 2618c2ecf20Sopenharmony_ci pr_warn("Failed to register pcpu%u\n", info->xen_cpuid); 2628c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci return pcpu; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci/* 2698c2ecf20Sopenharmony_ci * Caller should hold the xen_pcpu_lock 2708c2ecf20Sopenharmony_ci */ 2718c2ecf20Sopenharmony_cistatic int sync_pcpu(uint32_t cpu, uint32_t *max_cpu) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci int ret; 2748c2ecf20Sopenharmony_ci struct pcpu *pcpu = NULL; 2758c2ecf20Sopenharmony_ci struct xenpf_pcpuinfo *info; 2768c2ecf20Sopenharmony_ci struct xen_platform_op op = { 2778c2ecf20Sopenharmony_ci .cmd = XENPF_get_cpuinfo, 2788c2ecf20Sopenharmony_ci .interface_version = XENPF_INTERFACE_VERSION, 2798c2ecf20Sopenharmony_ci .u.pcpu_info.xen_cpuid = cpu, 2808c2ecf20Sopenharmony_ci }; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci ret = HYPERVISOR_platform_op(&op); 2838c2ecf20Sopenharmony_ci if (ret) 2848c2ecf20Sopenharmony_ci return ret; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci info = &op.u.pcpu_info; 2878c2ecf20Sopenharmony_ci if (max_cpu) 2888c2ecf20Sopenharmony_ci *max_cpu = info->max_present; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci pcpu = get_pcpu(cpu); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* 2938c2ecf20Sopenharmony_ci * Only those at cpu present map has its sys interface. 2948c2ecf20Sopenharmony_ci */ 2958c2ecf20Sopenharmony_ci if (info->flags & XEN_PCPU_FLAGS_INVALID) { 2968c2ecf20Sopenharmony_ci unregister_and_remove_pcpu(pcpu); 2978c2ecf20Sopenharmony_ci return 0; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (!pcpu) { 3018c2ecf20Sopenharmony_ci pcpu = create_and_register_pcpu(info); 3028c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(pcpu)) 3038c2ecf20Sopenharmony_ci return -ENODEV; 3048c2ecf20Sopenharmony_ci } else 3058c2ecf20Sopenharmony_ci pcpu_online_status(info, pcpu); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci return 0; 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci/* 3118c2ecf20Sopenharmony_ci * Sync dom0's pcpu information with xen hypervisor's 3128c2ecf20Sopenharmony_ci */ 3138c2ecf20Sopenharmony_cistatic int xen_sync_pcpus(void) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci /* 3168c2ecf20Sopenharmony_ci * Boot cpu always have cpu_id 0 in xen 3178c2ecf20Sopenharmony_ci */ 3188c2ecf20Sopenharmony_ci uint32_t cpu = 0, max_cpu = 0; 3198c2ecf20Sopenharmony_ci int err = 0; 3208c2ecf20Sopenharmony_ci struct pcpu *pcpu, *tmp; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci mutex_lock(&xen_pcpu_lock); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci while (!err && (cpu <= max_cpu)) { 3258c2ecf20Sopenharmony_ci err = sync_pcpu(cpu, &max_cpu); 3268c2ecf20Sopenharmony_ci cpu++; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (err) 3308c2ecf20Sopenharmony_ci list_for_each_entry_safe(pcpu, tmp, &xen_pcpus, list) 3318c2ecf20Sopenharmony_ci unregister_and_remove_pcpu(pcpu); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci mutex_unlock(&xen_pcpu_lock); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci return err; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic void xen_pcpu_work_fn(struct work_struct *work) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci xen_sync_pcpus(); 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_cistatic DECLARE_WORK(xen_pcpu_work, xen_pcpu_work_fn); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic irqreturn_t xen_pcpu_interrupt(int irq, void *dev_id) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci schedule_work(&xen_pcpu_work); 3478c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci/* Sync with Xen hypervisor after cpu hotadded */ 3518c2ecf20Sopenharmony_civoid xen_pcpu_hotplug_sync(void) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci schedule_work(&xen_pcpu_work); 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xen_pcpu_hotplug_sync); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci/* 3588c2ecf20Sopenharmony_ci * For hypervisor presented cpu, return logic cpu id; 3598c2ecf20Sopenharmony_ci * For hypervisor non-presented cpu, return -ENODEV. 3608c2ecf20Sopenharmony_ci */ 3618c2ecf20Sopenharmony_ciint xen_pcpu_id(uint32_t acpi_id) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci int cpu_id = 0, max_id = 0; 3648c2ecf20Sopenharmony_ci struct xen_platform_op op; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci op.cmd = XENPF_get_cpuinfo; 3678c2ecf20Sopenharmony_ci while (cpu_id <= max_id) { 3688c2ecf20Sopenharmony_ci op.u.pcpu_info.xen_cpuid = cpu_id; 3698c2ecf20Sopenharmony_ci if (HYPERVISOR_platform_op(&op)) { 3708c2ecf20Sopenharmony_ci cpu_id++; 3718c2ecf20Sopenharmony_ci continue; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (acpi_id == op.u.pcpu_info.acpi_id) 3758c2ecf20Sopenharmony_ci return cpu_id; 3768c2ecf20Sopenharmony_ci if (op.u.pcpu_info.max_present > max_id) 3778c2ecf20Sopenharmony_ci max_id = op.u.pcpu_info.max_present; 3788c2ecf20Sopenharmony_ci cpu_id++; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci return -ENODEV; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xen_pcpu_id); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic int __init xen_pcpu_init(void) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci int irq, ret; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (!xen_initial_domain()) 3908c2ecf20Sopenharmony_ci return -ENODEV; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci irq = bind_virq_to_irqhandler(VIRQ_PCPU_STATE, 0, 3938c2ecf20Sopenharmony_ci xen_pcpu_interrupt, 0, 3948c2ecf20Sopenharmony_ci "xen-pcpu", NULL); 3958c2ecf20Sopenharmony_ci if (irq < 0) { 3968c2ecf20Sopenharmony_ci pr_warn("Failed to bind pcpu virq\n"); 3978c2ecf20Sopenharmony_ci return irq; 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci ret = subsys_system_register(&xen_pcpu_subsys, NULL); 4018c2ecf20Sopenharmony_ci if (ret) { 4028c2ecf20Sopenharmony_ci pr_warn("Failed to register pcpu subsys\n"); 4038c2ecf20Sopenharmony_ci goto err1; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci ret = xen_sync_pcpus(); 4078c2ecf20Sopenharmony_ci if (ret) { 4088c2ecf20Sopenharmony_ci pr_warn("Failed to sync pcpu info\n"); 4098c2ecf20Sopenharmony_ci goto err2; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci return 0; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cierr2: 4158c2ecf20Sopenharmony_ci bus_unregister(&xen_pcpu_subsys); 4168c2ecf20Sopenharmony_cierr1: 4178c2ecf20Sopenharmony_ci unbind_from_irqhandler(irq, NULL); 4188c2ecf20Sopenharmony_ci return ret; 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ciarch_initcall(xen_pcpu_init); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI 4238c2ecf20Sopenharmony_cibool __init xen_processor_present(uint32_t acpi_id) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci const struct pcpu *pcpu; 4268c2ecf20Sopenharmony_ci bool online = false; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci mutex_lock(&xen_pcpu_lock); 4298c2ecf20Sopenharmony_ci list_for_each_entry(pcpu, &xen_pcpus, list) 4308c2ecf20Sopenharmony_ci if (pcpu->acpi_id == acpi_id) { 4318c2ecf20Sopenharmony_ci online = pcpu->flags & XEN_PCPU_FLAGS_ONLINE; 4328c2ecf20Sopenharmony_ci break; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci mutex_unlock(&xen_pcpu_lock); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci return online; 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ci#endif 439