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