xref: /openbmc/linux/drivers/xen/pcpu.c (revision b97d6790d03b763eca08847a9a5869a4291b9f9a)
1f65c9bb3SLiu, Jinsong /******************************************************************************
2f65c9bb3SLiu, Jinsong  * pcpu.c
3f65c9bb3SLiu, Jinsong  * Management physical cpu in dom0, get pcpu info and provide sys interface
4f65c9bb3SLiu, Jinsong  *
5f65c9bb3SLiu, Jinsong  * Copyright (c) 2012 Intel Corporation
6f65c9bb3SLiu, Jinsong  * Author: Liu, Jinsong <jinsong.liu@intel.com>
7f65c9bb3SLiu, Jinsong  * Author: Jiang, Yunhong <yunhong.jiang@intel.com>
8f65c9bb3SLiu, Jinsong  *
9f65c9bb3SLiu, Jinsong  * This program is free software; you can redistribute it and/or
10f65c9bb3SLiu, Jinsong  * modify it under the terms of the GNU General Public License version 2
11f65c9bb3SLiu, Jinsong  * as published by the Free Software Foundation; or, when distributed
12f65c9bb3SLiu, Jinsong  * separately from the Linux kernel or incorporated into other
13f65c9bb3SLiu, Jinsong  * software packages, subject to the following license:
14f65c9bb3SLiu, Jinsong  *
15f65c9bb3SLiu, Jinsong  * Permission is hereby granted, free of charge, to any person obtaining a copy
16f65c9bb3SLiu, Jinsong  * of this source file (the "Software"), to deal in the Software without
17f65c9bb3SLiu, Jinsong  * restriction, including without limitation the rights to use, copy, modify,
18f65c9bb3SLiu, Jinsong  * merge, publish, distribute, sublicense, and/or sell copies of the Software,
19f65c9bb3SLiu, Jinsong  * and to permit persons to whom the Software is furnished to do so, subject to
20f65c9bb3SLiu, Jinsong  * the following conditions:
21f65c9bb3SLiu, Jinsong  *
22f65c9bb3SLiu, Jinsong  * The above copyright notice and this permission notice shall be included in
23f65c9bb3SLiu, Jinsong  * all copies or substantial portions of the Software.
24f65c9bb3SLiu, Jinsong  *
25f65c9bb3SLiu, Jinsong  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26f65c9bb3SLiu, Jinsong  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27f65c9bb3SLiu, Jinsong  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28f65c9bb3SLiu, Jinsong  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29f65c9bb3SLiu, Jinsong  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
30f65c9bb3SLiu, Jinsong  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
31f65c9bb3SLiu, Jinsong  * IN THE SOFTWARE.
32f65c9bb3SLiu, Jinsong  */
33f65c9bb3SLiu, Jinsong 
34283c0972SJoe Perches #define pr_fmt(fmt) "xen_cpu: " fmt
35283c0972SJoe Perches 
36f65c9bb3SLiu, Jinsong #include <linux/interrupt.h>
37f65c9bb3SLiu, Jinsong #include <linux/spinlock.h>
38f65c9bb3SLiu, Jinsong #include <linux/cpu.h>
39f65c9bb3SLiu, Jinsong #include <linux/stat.h>
40f65c9bb3SLiu, Jinsong #include <linux/capability.h>
41f65c9bb3SLiu, Jinsong 
42f65c9bb3SLiu, Jinsong #include <xen/xen.h>
436cb606f1SRashika Kheria #include <xen/acpi.h>
44f65c9bb3SLiu, Jinsong #include <xen/xenbus.h>
45f65c9bb3SLiu, Jinsong #include <xen/events.h>
46f65c9bb3SLiu, Jinsong #include <xen/interface/platform.h>
47f65c9bb3SLiu, Jinsong #include <asm/xen/hypervisor.h>
48f65c9bb3SLiu, Jinsong #include <asm/xen/hypercall.h>
49f65c9bb3SLiu, Jinsong 
50*32ca78deSRoger Pau Monne #ifdef CONFIG_ACPI
51*32ca78deSRoger Pau Monne #include <acpi/processor.h>
52*32ca78deSRoger Pau Monne #endif
53f65c9bb3SLiu, Jinsong 
54f65c9bb3SLiu, Jinsong /*
55f65c9bb3SLiu, Jinsong  * @cpu_id: Xen physical cpu logic number
56f65c9bb3SLiu, Jinsong  * @flags: Xen physical cpu status flag
57f65c9bb3SLiu, Jinsong  * - XEN_PCPU_FLAGS_ONLINE: cpu is online
58f65c9bb3SLiu, Jinsong  * - XEN_PCPU_FLAGS_INVALID: cpu is not present
59f65c9bb3SLiu, Jinsong  */
60f65c9bb3SLiu, Jinsong struct pcpu {
61f65c9bb3SLiu, Jinsong 	struct list_head list;
62f65c9bb3SLiu, Jinsong 	struct device dev;
63f65c9bb3SLiu, Jinsong 	uint32_t cpu_id;
64073828e9SRoger Pau Monne 	uint32_t acpi_id;
65f65c9bb3SLiu, Jinsong 	uint32_t flags;
66f65c9bb3SLiu, Jinsong };
67f65c9bb3SLiu, Jinsong 
68f65c9bb3SLiu, Jinsong static struct bus_type xen_pcpu_subsys = {
69f65c9bb3SLiu, Jinsong 	.name = "xen_cpu",
70f65c9bb3SLiu, Jinsong 	.dev_name = "xen_cpu",
71f65c9bb3SLiu, Jinsong };
72f65c9bb3SLiu, Jinsong 
73f65c9bb3SLiu, Jinsong static DEFINE_MUTEX(xen_pcpu_lock);
74f65c9bb3SLiu, Jinsong 
75f65c9bb3SLiu, Jinsong static LIST_HEAD(xen_pcpus);
76f65c9bb3SLiu, Jinsong 
xen_pcpu_down(uint32_t cpu_id)77f65c9bb3SLiu, Jinsong static int xen_pcpu_down(uint32_t cpu_id)
78f65c9bb3SLiu, Jinsong {
79f65c9bb3SLiu, Jinsong 	struct xen_platform_op op = {
80f65c9bb3SLiu, Jinsong 		.cmd			= XENPF_cpu_offline,
81f65c9bb3SLiu, Jinsong 		.interface_version	= XENPF_INTERFACE_VERSION,
82f65c9bb3SLiu, Jinsong 		.u.cpu_ol.cpuid		= cpu_id,
83f65c9bb3SLiu, Jinsong 	};
84f65c9bb3SLiu, Jinsong 
85cfafae94SStefano Stabellini 	return HYPERVISOR_platform_op(&op);
86f65c9bb3SLiu, Jinsong }
87f65c9bb3SLiu, Jinsong 
xen_pcpu_up(uint32_t cpu_id)88f65c9bb3SLiu, Jinsong static int xen_pcpu_up(uint32_t cpu_id)
89f65c9bb3SLiu, Jinsong {
90f65c9bb3SLiu, Jinsong 	struct xen_platform_op op = {
91f65c9bb3SLiu, Jinsong 		.cmd			= XENPF_cpu_online,
92f65c9bb3SLiu, Jinsong 		.interface_version	= XENPF_INTERFACE_VERSION,
93f65c9bb3SLiu, Jinsong 		.u.cpu_ol.cpuid		= cpu_id,
94f65c9bb3SLiu, Jinsong 	};
95f65c9bb3SLiu, Jinsong 
96cfafae94SStefano Stabellini 	return HYPERVISOR_platform_op(&op);
97f65c9bb3SLiu, Jinsong }
98f65c9bb3SLiu, Jinsong 
online_show(struct device * dev,struct device_attribute * attr,char * buf)9920600617SYueHaibing static ssize_t online_show(struct device *dev,
100f65c9bb3SLiu, Jinsong 			   struct device_attribute *attr,
101f65c9bb3SLiu, Jinsong 			   char *buf)
102f65c9bb3SLiu, Jinsong {
103f65c9bb3SLiu, Jinsong 	struct pcpu *cpu = container_of(dev, struct pcpu, dev);
104f65c9bb3SLiu, Jinsong 
105f65c9bb3SLiu, Jinsong 	return sprintf(buf, "%u\n", !!(cpu->flags & XEN_PCPU_FLAGS_ONLINE));
106f65c9bb3SLiu, Jinsong }
107f65c9bb3SLiu, Jinsong 
online_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)10820600617SYueHaibing static ssize_t __ref online_store(struct device *dev,
109f65c9bb3SLiu, Jinsong 				  struct device_attribute *attr,
110f65c9bb3SLiu, Jinsong 				  const char *buf, size_t count)
111f65c9bb3SLiu, Jinsong {
112f65c9bb3SLiu, Jinsong 	struct pcpu *pcpu = container_of(dev, struct pcpu, dev);
113f65c9bb3SLiu, Jinsong 	unsigned long long val;
114f65c9bb3SLiu, Jinsong 	ssize_t ret;
115f65c9bb3SLiu, Jinsong 
116f65c9bb3SLiu, Jinsong 	if (!capable(CAP_SYS_ADMIN))
117f65c9bb3SLiu, Jinsong 		return -EPERM;
118f65c9bb3SLiu, Jinsong 
119f65c9bb3SLiu, Jinsong 	if (kstrtoull(buf, 0, &val) < 0)
120f65c9bb3SLiu, Jinsong 		return -EINVAL;
121f65c9bb3SLiu, Jinsong 
122f65c9bb3SLiu, Jinsong 	switch (val) {
123f65c9bb3SLiu, Jinsong 	case 0:
124f65c9bb3SLiu, Jinsong 		ret = xen_pcpu_down(pcpu->cpu_id);
125f65c9bb3SLiu, Jinsong 		break;
126f65c9bb3SLiu, Jinsong 	case 1:
127f65c9bb3SLiu, Jinsong 		ret = xen_pcpu_up(pcpu->cpu_id);
128f65c9bb3SLiu, Jinsong 		break;
129f65c9bb3SLiu, Jinsong 	default:
130f65c9bb3SLiu, Jinsong 		ret = -EINVAL;
131f65c9bb3SLiu, Jinsong 	}
132f65c9bb3SLiu, Jinsong 
133f65c9bb3SLiu, Jinsong 	if (ret >= 0)
134f65c9bb3SLiu, Jinsong 		ret = count;
135f65c9bb3SLiu, Jinsong 	return ret;
136f65c9bb3SLiu, Jinsong }
13720600617SYueHaibing static DEVICE_ATTR_RW(online);
138f65c9bb3SLiu, Jinsong 
1394644e5abSTakashi Iwai static struct attribute *pcpu_dev_attrs[] = {
1404644e5abSTakashi Iwai 	&dev_attr_online.attr,
1414644e5abSTakashi Iwai 	NULL
1424644e5abSTakashi Iwai };
1434644e5abSTakashi Iwai 
pcpu_dev_is_visible(struct kobject * kobj,struct attribute * attr,int idx)1444644e5abSTakashi Iwai static umode_t pcpu_dev_is_visible(struct kobject *kobj,
1454644e5abSTakashi Iwai 				   struct attribute *attr, int idx)
1464644e5abSTakashi Iwai {
1474644e5abSTakashi Iwai 	struct device *dev = kobj_to_dev(kobj);
1484644e5abSTakashi Iwai 	/*
1494644e5abSTakashi Iwai 	 * Xen never offline cpu0 due to several restrictions
1504644e5abSTakashi Iwai 	 * and assumptions. This basically doesn't add a sys control
1514644e5abSTakashi Iwai 	 * to user, one cannot attempt to offline BSP.
1524644e5abSTakashi Iwai 	 */
1534644e5abSTakashi Iwai 	return dev->id ? attr->mode : 0;
1544644e5abSTakashi Iwai }
1554644e5abSTakashi Iwai 
1564644e5abSTakashi Iwai static const struct attribute_group pcpu_dev_group = {
1574644e5abSTakashi Iwai 	.attrs = pcpu_dev_attrs,
1584644e5abSTakashi Iwai 	.is_visible = pcpu_dev_is_visible,
1594644e5abSTakashi Iwai };
1604644e5abSTakashi Iwai 
1614644e5abSTakashi Iwai static const struct attribute_group *pcpu_dev_groups[] = {
1624644e5abSTakashi Iwai 	&pcpu_dev_group,
1634644e5abSTakashi Iwai 	NULL
1644644e5abSTakashi Iwai };
1654644e5abSTakashi Iwai 
xen_pcpu_online(uint32_t flags)166f65c9bb3SLiu, Jinsong static bool xen_pcpu_online(uint32_t flags)
167f65c9bb3SLiu, Jinsong {
168f65c9bb3SLiu, Jinsong 	return !!(flags & XEN_PCPU_FLAGS_ONLINE);
169f65c9bb3SLiu, Jinsong }
170f65c9bb3SLiu, Jinsong 
pcpu_online_status(struct xenpf_pcpuinfo * info,struct pcpu * pcpu)171f65c9bb3SLiu, Jinsong static void pcpu_online_status(struct xenpf_pcpuinfo *info,
172f65c9bb3SLiu, Jinsong 			       struct pcpu *pcpu)
173f65c9bb3SLiu, Jinsong {
174f65c9bb3SLiu, Jinsong 	if (xen_pcpu_online(info->flags) &&
175f65c9bb3SLiu, Jinsong 	   !xen_pcpu_online(pcpu->flags)) {
176f65c9bb3SLiu, Jinsong 		/* the pcpu is onlined */
177f65c9bb3SLiu, Jinsong 		pcpu->flags |= XEN_PCPU_FLAGS_ONLINE;
178f65c9bb3SLiu, Jinsong 		kobject_uevent(&pcpu->dev.kobj, KOBJ_ONLINE);
179f65c9bb3SLiu, Jinsong 	} else if (!xen_pcpu_online(info->flags) &&
180f65c9bb3SLiu, Jinsong 		    xen_pcpu_online(pcpu->flags)) {
181f65c9bb3SLiu, Jinsong 		/* The pcpu is offlined */
182f65c9bb3SLiu, Jinsong 		pcpu->flags &= ~XEN_PCPU_FLAGS_ONLINE;
183f65c9bb3SLiu, Jinsong 		kobject_uevent(&pcpu->dev.kobj, KOBJ_OFFLINE);
184f65c9bb3SLiu, Jinsong 	}
185f65c9bb3SLiu, Jinsong }
186f65c9bb3SLiu, Jinsong 
get_pcpu(uint32_t cpu_id)187f65c9bb3SLiu, Jinsong static struct pcpu *get_pcpu(uint32_t cpu_id)
188f65c9bb3SLiu, Jinsong {
189f65c9bb3SLiu, Jinsong 	struct pcpu *pcpu;
190f65c9bb3SLiu, Jinsong 
191f65c9bb3SLiu, Jinsong 	list_for_each_entry(pcpu, &xen_pcpus, list) {
192f65c9bb3SLiu, Jinsong 		if (pcpu->cpu_id == cpu_id)
193f65c9bb3SLiu, Jinsong 			return pcpu;
194f65c9bb3SLiu, Jinsong 	}
195f65c9bb3SLiu, Jinsong 
196f65c9bb3SLiu, Jinsong 	return NULL;
197f65c9bb3SLiu, Jinsong }
198f65c9bb3SLiu, Jinsong 
pcpu_release(struct device * dev)199f65c9bb3SLiu, Jinsong static void pcpu_release(struct device *dev)
200f65c9bb3SLiu, Jinsong {
201f65c9bb3SLiu, Jinsong 	struct pcpu *pcpu = container_of(dev, struct pcpu, dev);
202f65c9bb3SLiu, Jinsong 
203f65c9bb3SLiu, Jinsong 	list_del(&pcpu->list);
204f65c9bb3SLiu, Jinsong 	kfree(pcpu);
205f65c9bb3SLiu, Jinsong }
206f65c9bb3SLiu, Jinsong 
unregister_and_remove_pcpu(struct pcpu * pcpu)207f65c9bb3SLiu, Jinsong static void unregister_and_remove_pcpu(struct pcpu *pcpu)
208f65c9bb3SLiu, Jinsong {
209f65c9bb3SLiu, Jinsong 	struct device *dev;
210f65c9bb3SLiu, Jinsong 
211f65c9bb3SLiu, Jinsong 	if (!pcpu)
212f65c9bb3SLiu, Jinsong 		return;
213f65c9bb3SLiu, Jinsong 
214f65c9bb3SLiu, Jinsong 	dev = &pcpu->dev;
215f65c9bb3SLiu, Jinsong 	/* pcpu remove would be implicitly done */
216f65c9bb3SLiu, Jinsong 	device_unregister(dev);
217f65c9bb3SLiu, Jinsong }
218f65c9bb3SLiu, Jinsong 
register_pcpu(struct pcpu * pcpu)219f65c9bb3SLiu, Jinsong static int register_pcpu(struct pcpu *pcpu)
220f65c9bb3SLiu, Jinsong {
221f65c9bb3SLiu, Jinsong 	struct device *dev;
222f65c9bb3SLiu, Jinsong 	int err = -EINVAL;
223f65c9bb3SLiu, Jinsong 
224f65c9bb3SLiu, Jinsong 	if (!pcpu)
225f65c9bb3SLiu, Jinsong 		return err;
226f65c9bb3SLiu, Jinsong 
227f65c9bb3SLiu, Jinsong 	dev = &pcpu->dev;
228f65c9bb3SLiu, Jinsong 	dev->bus = &xen_pcpu_subsys;
229f65c9bb3SLiu, Jinsong 	dev->id = pcpu->cpu_id;
230f65c9bb3SLiu, Jinsong 	dev->release = pcpu_release;
2314644e5abSTakashi Iwai 	dev->groups = pcpu_dev_groups;
232f65c9bb3SLiu, Jinsong 
233f65c9bb3SLiu, Jinsong 	err = device_register(dev);
234f65c9bb3SLiu, Jinsong 	if (err) {
235da36a2a7SYang Yingliang 		put_device(dev);
236f65c9bb3SLiu, Jinsong 		return err;
237f65c9bb3SLiu, Jinsong 	}
238f65c9bb3SLiu, Jinsong 
239f65c9bb3SLiu, Jinsong 	return 0;
240f65c9bb3SLiu, Jinsong }
241f65c9bb3SLiu, Jinsong 
create_and_register_pcpu(struct xenpf_pcpuinfo * info)242f65c9bb3SLiu, Jinsong static struct pcpu *create_and_register_pcpu(struct xenpf_pcpuinfo *info)
243f65c9bb3SLiu, Jinsong {
244f65c9bb3SLiu, Jinsong 	struct pcpu *pcpu;
245f65c9bb3SLiu, Jinsong 	int err;
246f65c9bb3SLiu, Jinsong 
247f65c9bb3SLiu, Jinsong 	if (info->flags & XEN_PCPU_FLAGS_INVALID)
248f65c9bb3SLiu, Jinsong 		return ERR_PTR(-ENODEV);
249f65c9bb3SLiu, Jinsong 
250f65c9bb3SLiu, Jinsong 	pcpu = kzalloc(sizeof(struct pcpu), GFP_KERNEL);
251f65c9bb3SLiu, Jinsong 	if (!pcpu)
252f65c9bb3SLiu, Jinsong 		return ERR_PTR(-ENOMEM);
253f65c9bb3SLiu, Jinsong 
254f65c9bb3SLiu, Jinsong 	INIT_LIST_HEAD(&pcpu->list);
255f65c9bb3SLiu, Jinsong 	pcpu->cpu_id = info->xen_cpuid;
256073828e9SRoger Pau Monne 	pcpu->acpi_id = info->acpi_id;
257f65c9bb3SLiu, Jinsong 	pcpu->flags = info->flags;
258f65c9bb3SLiu, Jinsong 
259f65c9bb3SLiu, Jinsong 	/* Need hold on xen_pcpu_lock before pcpu list manipulations */
260f65c9bb3SLiu, Jinsong 	list_add_tail(&pcpu->list, &xen_pcpus);
261f65c9bb3SLiu, Jinsong 
262f65c9bb3SLiu, Jinsong 	err = register_pcpu(pcpu);
263f65c9bb3SLiu, Jinsong 	if (err) {
264283c0972SJoe Perches 		pr_warn("Failed to register pcpu%u\n", info->xen_cpuid);
265f65c9bb3SLiu, Jinsong 		return ERR_PTR(-ENOENT);
266f65c9bb3SLiu, Jinsong 	}
267f65c9bb3SLiu, Jinsong 
268f65c9bb3SLiu, Jinsong 	return pcpu;
269f65c9bb3SLiu, Jinsong }
270f65c9bb3SLiu, Jinsong 
271f65c9bb3SLiu, Jinsong /*
272f65c9bb3SLiu, Jinsong  * Caller should hold the xen_pcpu_lock
273f65c9bb3SLiu, Jinsong  */
sync_pcpu(uint32_t cpu,uint32_t * max_cpu)274f65c9bb3SLiu, Jinsong static int sync_pcpu(uint32_t cpu, uint32_t *max_cpu)
275f65c9bb3SLiu, Jinsong {
276f65c9bb3SLiu, Jinsong 	int ret;
277f65c9bb3SLiu, Jinsong 	struct pcpu *pcpu = NULL;
278f65c9bb3SLiu, Jinsong 	struct xenpf_pcpuinfo *info;
279f65c9bb3SLiu, Jinsong 	struct xen_platform_op op = {
280f65c9bb3SLiu, Jinsong 		.cmd                   = XENPF_get_cpuinfo,
281f65c9bb3SLiu, Jinsong 		.interface_version     = XENPF_INTERFACE_VERSION,
282f65c9bb3SLiu, Jinsong 		.u.pcpu_info.xen_cpuid = cpu,
283f65c9bb3SLiu, Jinsong 	};
284f65c9bb3SLiu, Jinsong 
285cfafae94SStefano Stabellini 	ret = HYPERVISOR_platform_op(&op);
286f65c9bb3SLiu, Jinsong 	if (ret)
287f65c9bb3SLiu, Jinsong 		return ret;
288f65c9bb3SLiu, Jinsong 
289f65c9bb3SLiu, Jinsong 	info = &op.u.pcpu_info;
290f65c9bb3SLiu, Jinsong 	if (max_cpu)
291f65c9bb3SLiu, Jinsong 		*max_cpu = info->max_present;
292f65c9bb3SLiu, Jinsong 
293f65c9bb3SLiu, Jinsong 	pcpu = get_pcpu(cpu);
294f65c9bb3SLiu, Jinsong 
295f65c9bb3SLiu, Jinsong 	/*
296f65c9bb3SLiu, Jinsong 	 * Only those at cpu present map has its sys interface.
297f65c9bb3SLiu, Jinsong 	 */
298f65c9bb3SLiu, Jinsong 	if (info->flags & XEN_PCPU_FLAGS_INVALID) {
299f65c9bb3SLiu, Jinsong 		unregister_and_remove_pcpu(pcpu);
300f65c9bb3SLiu, Jinsong 		return 0;
301f65c9bb3SLiu, Jinsong 	}
302f65c9bb3SLiu, Jinsong 
303f65c9bb3SLiu, Jinsong 	if (!pcpu) {
304f65c9bb3SLiu, Jinsong 		pcpu = create_and_register_pcpu(info);
305f65c9bb3SLiu, Jinsong 		if (IS_ERR_OR_NULL(pcpu))
306f65c9bb3SLiu, Jinsong 			return -ENODEV;
307f65c9bb3SLiu, Jinsong 	} else
308f65c9bb3SLiu, Jinsong 		pcpu_online_status(info, pcpu);
309f65c9bb3SLiu, Jinsong 
310f65c9bb3SLiu, Jinsong 	return 0;
311f65c9bb3SLiu, Jinsong }
312f65c9bb3SLiu, Jinsong 
313f65c9bb3SLiu, Jinsong /*
314f65c9bb3SLiu, Jinsong  * Sync dom0's pcpu information with xen hypervisor's
315f65c9bb3SLiu, Jinsong  */
xen_sync_pcpus(void)316f65c9bb3SLiu, Jinsong static int xen_sync_pcpus(void)
317f65c9bb3SLiu, Jinsong {
318f65c9bb3SLiu, Jinsong 	/*
319f65c9bb3SLiu, Jinsong 	 * Boot cpu always have cpu_id 0 in xen
320f65c9bb3SLiu, Jinsong 	 */
321f65c9bb3SLiu, Jinsong 	uint32_t cpu = 0, max_cpu = 0;
322f65c9bb3SLiu, Jinsong 	int err = 0;
323f65c9bb3SLiu, Jinsong 	struct pcpu *pcpu, *tmp;
324f65c9bb3SLiu, Jinsong 
325f65c9bb3SLiu, Jinsong 	mutex_lock(&xen_pcpu_lock);
326f65c9bb3SLiu, Jinsong 
327f65c9bb3SLiu, Jinsong 	while (!err && (cpu <= max_cpu)) {
328f65c9bb3SLiu, Jinsong 		err = sync_pcpu(cpu, &max_cpu);
329f65c9bb3SLiu, Jinsong 		cpu++;
330f65c9bb3SLiu, Jinsong 	}
331f65c9bb3SLiu, Jinsong 
332f65c9bb3SLiu, Jinsong 	if (err)
333f65c9bb3SLiu, Jinsong 		list_for_each_entry_safe(pcpu, tmp, &xen_pcpus, list)
334f65c9bb3SLiu, Jinsong 			unregister_and_remove_pcpu(pcpu);
335f65c9bb3SLiu, Jinsong 
336f65c9bb3SLiu, Jinsong 	mutex_unlock(&xen_pcpu_lock);
337f65c9bb3SLiu, Jinsong 
338f65c9bb3SLiu, Jinsong 	return err;
339f65c9bb3SLiu, Jinsong }
340f65c9bb3SLiu, Jinsong 
xen_pcpu_work_fn(struct work_struct * work)341f65c9bb3SLiu, Jinsong static void xen_pcpu_work_fn(struct work_struct *work)
342f65c9bb3SLiu, Jinsong {
343f65c9bb3SLiu, Jinsong 	xen_sync_pcpus();
344f65c9bb3SLiu, Jinsong }
345f65c9bb3SLiu, Jinsong static DECLARE_WORK(xen_pcpu_work, xen_pcpu_work_fn);
346f65c9bb3SLiu, Jinsong 
xen_pcpu_interrupt(int irq,void * dev_id)347f65c9bb3SLiu, Jinsong static irqreturn_t xen_pcpu_interrupt(int irq, void *dev_id)
348f65c9bb3SLiu, Jinsong {
349f65c9bb3SLiu, Jinsong 	schedule_work(&xen_pcpu_work);
350f65c9bb3SLiu, Jinsong 	return IRQ_HANDLED;
351f65c9bb3SLiu, Jinsong }
352f65c9bb3SLiu, Jinsong 
xen_pcpu_init(void)353f65c9bb3SLiu, Jinsong static int __init xen_pcpu_init(void)
354f65c9bb3SLiu, Jinsong {
355f65c9bb3SLiu, Jinsong 	int irq, ret;
356f65c9bb3SLiu, Jinsong 
357f65c9bb3SLiu, Jinsong 	if (!xen_initial_domain())
358f65c9bb3SLiu, Jinsong 		return -ENODEV;
359f65c9bb3SLiu, Jinsong 
360f65c9bb3SLiu, Jinsong 	irq = bind_virq_to_irqhandler(VIRQ_PCPU_STATE, 0,
361f65c9bb3SLiu, Jinsong 				      xen_pcpu_interrupt, 0,
362f65c9bb3SLiu, Jinsong 				      "xen-pcpu", NULL);
363f65c9bb3SLiu, Jinsong 	if (irq < 0) {
364283c0972SJoe Perches 		pr_warn("Failed to bind pcpu virq\n");
365f65c9bb3SLiu, Jinsong 		return irq;
366f65c9bb3SLiu, Jinsong 	}
367f65c9bb3SLiu, Jinsong 
368f65c9bb3SLiu, Jinsong 	ret = subsys_system_register(&xen_pcpu_subsys, NULL);
369f65c9bb3SLiu, Jinsong 	if (ret) {
370283c0972SJoe Perches 		pr_warn("Failed to register pcpu subsys\n");
371f65c9bb3SLiu, Jinsong 		goto err1;
372f65c9bb3SLiu, Jinsong 	}
373f65c9bb3SLiu, Jinsong 
374f65c9bb3SLiu, Jinsong 	ret = xen_sync_pcpus();
375f65c9bb3SLiu, Jinsong 	if (ret) {
376283c0972SJoe Perches 		pr_warn("Failed to sync pcpu info\n");
377f65c9bb3SLiu, Jinsong 		goto err2;
378f65c9bb3SLiu, Jinsong 	}
379f65c9bb3SLiu, Jinsong 
380f65c9bb3SLiu, Jinsong 	return 0;
381f65c9bb3SLiu, Jinsong 
382f65c9bb3SLiu, Jinsong err2:
383f65c9bb3SLiu, Jinsong 	bus_unregister(&xen_pcpu_subsys);
384f65c9bb3SLiu, Jinsong err1:
385f65c9bb3SLiu, Jinsong 	unbind_from_irqhandler(irq, NULL);
386f65c9bb3SLiu, Jinsong 	return ret;
387f65c9bb3SLiu, Jinsong }
388f65c9bb3SLiu, Jinsong arch_initcall(xen_pcpu_init);
389073828e9SRoger Pau Monne 
390073828e9SRoger Pau Monne #ifdef CONFIG_ACPI
xen_processor_present(uint32_t acpi_id)391073828e9SRoger Pau Monne bool __init xen_processor_present(uint32_t acpi_id)
392073828e9SRoger Pau Monne {
393073828e9SRoger Pau Monne 	const struct pcpu *pcpu;
394073828e9SRoger Pau Monne 	bool online = false;
395073828e9SRoger Pau Monne 
396073828e9SRoger Pau Monne 	mutex_lock(&xen_pcpu_lock);
397073828e9SRoger Pau Monne 	list_for_each_entry(pcpu, &xen_pcpus, list)
398073828e9SRoger Pau Monne 		if (pcpu->acpi_id == acpi_id) {
399073828e9SRoger Pau Monne 			online = pcpu->flags & XEN_PCPU_FLAGS_ONLINE;
400073828e9SRoger Pau Monne 			break;
401073828e9SRoger Pau Monne 		}
402073828e9SRoger Pau Monne 	mutex_unlock(&xen_pcpu_lock);
403073828e9SRoger Pau Monne 
404073828e9SRoger Pau Monne 	return online;
405073828e9SRoger Pau Monne }
406*32ca78deSRoger Pau Monne 
xen_sanitize_proc_cap_bits(uint32_t * cap)407*32ca78deSRoger Pau Monne void xen_sanitize_proc_cap_bits(uint32_t *cap)
408*32ca78deSRoger Pau Monne {
409*32ca78deSRoger Pau Monne 	struct xen_platform_op op = {
410*32ca78deSRoger Pau Monne 		.cmd			= XENPF_set_processor_pminfo,
411*32ca78deSRoger Pau Monne 		.u.set_pminfo.id	= -1,
412*32ca78deSRoger Pau Monne 		.u.set_pminfo.type	= XEN_PM_PDC,
413*32ca78deSRoger Pau Monne 	};
414*32ca78deSRoger Pau Monne 	u32 buf[3] = { ACPI_PDC_REVISION_ID, 1, *cap };
415*32ca78deSRoger Pau Monne 	int ret;
416*32ca78deSRoger Pau Monne 
417*32ca78deSRoger Pau Monne 	set_xen_guest_handle(op.u.set_pminfo.pdc, buf);
418*32ca78deSRoger Pau Monne 	ret = HYPERVISOR_platform_op(&op);
419*32ca78deSRoger Pau Monne 	if (ret)
420*32ca78deSRoger Pau Monne 		pr_err("sanitize of _PDC buffer bits from Xen failed: %d\n",
421*32ca78deSRoger Pau Monne 		       ret);
422*32ca78deSRoger Pau Monne 	else
423*32ca78deSRoger Pau Monne 		*cap = buf[2];
424*32ca78deSRoger Pau Monne }
425073828e9SRoger Pau Monne #endif
426