162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * CPU subsystem support
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/kernel.h>
762306a36Sopenharmony_ci#include <linux/module.h>
862306a36Sopenharmony_ci#include <linux/init.h>
962306a36Sopenharmony_ci#include <linux/sched.h>
1062306a36Sopenharmony_ci#include <linux/cpu.h>
1162306a36Sopenharmony_ci#include <linux/topology.h>
1262306a36Sopenharmony_ci#include <linux/device.h>
1362306a36Sopenharmony_ci#include <linux/node.h>
1462306a36Sopenharmony_ci#include <linux/gfp.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <linux/percpu.h>
1762306a36Sopenharmony_ci#include <linux/acpi.h>
1862306a36Sopenharmony_ci#include <linux/of.h>
1962306a36Sopenharmony_ci#include <linux/cpufeature.h>
2062306a36Sopenharmony_ci#include <linux/tick.h>
2162306a36Sopenharmony_ci#include <linux/pm_qos.h>
2262306a36Sopenharmony_ci#include <linux/delay.h>
2362306a36Sopenharmony_ci#include <linux/sched/isolation.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include "base.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct device *, cpu_sys_devices);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic int cpu_subsys_match(struct device *dev, struct device_driver *drv)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	/* ACPI style match is the only one that may succeed. */
3262306a36Sopenharmony_ci	if (acpi_driver_match_device(dev, drv))
3362306a36Sopenharmony_ci		return 1;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	return 0;
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
3962306a36Sopenharmony_cistatic void change_cpu_under_node(struct cpu *cpu,
4062306a36Sopenharmony_ci			unsigned int from_nid, unsigned int to_nid)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	int cpuid = cpu->dev.id;
4362306a36Sopenharmony_ci	unregister_cpu_under_node(cpuid, from_nid);
4462306a36Sopenharmony_ci	register_cpu_under_node(cpuid, to_nid);
4562306a36Sopenharmony_ci	cpu->node_id = to_nid;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int cpu_subsys_online(struct device *dev)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct cpu *cpu = container_of(dev, struct cpu, dev);
5162306a36Sopenharmony_ci	int cpuid = dev->id;
5262306a36Sopenharmony_ci	int from_nid, to_nid;
5362306a36Sopenharmony_ci	int ret;
5462306a36Sopenharmony_ci	int retries = 0;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	from_nid = cpu_to_node(cpuid);
5762306a36Sopenharmony_ci	if (from_nid == NUMA_NO_NODE)
5862306a36Sopenharmony_ci		return -ENODEV;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ciretry:
6162306a36Sopenharmony_ci	ret = cpu_device_up(dev);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	/*
6462306a36Sopenharmony_ci	 * If -EBUSY is returned, it is likely that hotplug is temporarily
6562306a36Sopenharmony_ci	 * disabled when cpu_hotplug_disable() was called. This condition is
6662306a36Sopenharmony_ci	 * transient. So we retry after waiting for an exponentially
6762306a36Sopenharmony_ci	 * increasing delay up to a total of at least 620ms as some PCI
6862306a36Sopenharmony_ci	 * device initialization can take quite a while.
6962306a36Sopenharmony_ci	 */
7062306a36Sopenharmony_ci	if (ret == -EBUSY) {
7162306a36Sopenharmony_ci		retries++;
7262306a36Sopenharmony_ci		if (retries > 5)
7362306a36Sopenharmony_ci			return ret;
7462306a36Sopenharmony_ci		msleep(10 * (1 << retries));
7562306a36Sopenharmony_ci		goto retry;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	/*
7962306a36Sopenharmony_ci	 * When hot adding memory to memoryless node and enabling a cpu
8062306a36Sopenharmony_ci	 * on the node, node number of the cpu may internally change.
8162306a36Sopenharmony_ci	 */
8262306a36Sopenharmony_ci	to_nid = cpu_to_node(cpuid);
8362306a36Sopenharmony_ci	if (from_nid != to_nid)
8462306a36Sopenharmony_ci		change_cpu_under_node(cpu, from_nid, to_nid);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	return ret;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic int cpu_subsys_offline(struct device *dev)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	return cpu_device_down(dev);
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_civoid unregister_cpu(struct cpu *cpu)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	int logical_cpu = cpu->dev.id;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	unregister_cpu_under_node(logical_cpu, cpu_to_node(logical_cpu));
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	device_unregister(&cpu->dev);
10162306a36Sopenharmony_ci	per_cpu(cpu_sys_devices, logical_cpu) = NULL;
10262306a36Sopenharmony_ci	return;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
10662306a36Sopenharmony_cistatic ssize_t cpu_probe_store(struct device *dev,
10762306a36Sopenharmony_ci			       struct device_attribute *attr,
10862306a36Sopenharmony_ci			       const char *buf,
10962306a36Sopenharmony_ci			       size_t count)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	ssize_t cnt;
11262306a36Sopenharmony_ci	int ret;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	ret = lock_device_hotplug_sysfs();
11562306a36Sopenharmony_ci	if (ret)
11662306a36Sopenharmony_ci		return ret;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	cnt = arch_cpu_probe(buf, count);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	unlock_device_hotplug();
12162306a36Sopenharmony_ci	return cnt;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic ssize_t cpu_release_store(struct device *dev,
12562306a36Sopenharmony_ci				 struct device_attribute *attr,
12662306a36Sopenharmony_ci				 const char *buf,
12762306a36Sopenharmony_ci				 size_t count)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	ssize_t cnt;
13062306a36Sopenharmony_ci	int ret;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	ret = lock_device_hotplug_sysfs();
13362306a36Sopenharmony_ci	if (ret)
13462306a36Sopenharmony_ci		return ret;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	cnt = arch_cpu_release(buf, count);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	unlock_device_hotplug();
13962306a36Sopenharmony_ci	return cnt;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic DEVICE_ATTR(probe, S_IWUSR, NULL, cpu_probe_store);
14362306a36Sopenharmony_cistatic DEVICE_ATTR(release, S_IWUSR, NULL, cpu_release_store);
14462306a36Sopenharmony_ci#endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */
14562306a36Sopenharmony_ci#endif /* CONFIG_HOTPLUG_CPU */
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci#ifdef CONFIG_KEXEC_CORE
14862306a36Sopenharmony_ci#include <linux/kexec.h>
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic ssize_t crash_notes_show(struct device *dev,
15162306a36Sopenharmony_ci				struct device_attribute *attr,
15262306a36Sopenharmony_ci				char *buf)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	struct cpu *cpu = container_of(dev, struct cpu, dev);
15562306a36Sopenharmony_ci	unsigned long long addr;
15662306a36Sopenharmony_ci	int cpunum;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	cpunum = cpu->dev.id;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/*
16162306a36Sopenharmony_ci	 * Might be reading other cpu's data based on which cpu read thread
16262306a36Sopenharmony_ci	 * has been scheduled. But cpu data (memory) is allocated once during
16362306a36Sopenharmony_ci	 * boot up and this data does not change there after. Hence this
16462306a36Sopenharmony_ci	 * operation should be safe. No locking required.
16562306a36Sopenharmony_ci	 */
16662306a36Sopenharmony_ci	addr = per_cpu_ptr_to_phys(per_cpu_ptr(crash_notes, cpunum));
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	return sysfs_emit(buf, "%llx\n", addr);
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_cistatic DEVICE_ATTR_ADMIN_RO(crash_notes);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic ssize_t crash_notes_size_show(struct device *dev,
17362306a36Sopenharmony_ci				     struct device_attribute *attr,
17462306a36Sopenharmony_ci				     char *buf)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	return sysfs_emit(buf, "%zu\n", sizeof(note_buf_t));
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_cistatic DEVICE_ATTR_ADMIN_RO(crash_notes_size);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic struct attribute *crash_note_cpu_attrs[] = {
18162306a36Sopenharmony_ci	&dev_attr_crash_notes.attr,
18262306a36Sopenharmony_ci	&dev_attr_crash_notes_size.attr,
18362306a36Sopenharmony_ci	NULL
18462306a36Sopenharmony_ci};
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic const struct attribute_group crash_note_cpu_attr_group = {
18762306a36Sopenharmony_ci	.attrs = crash_note_cpu_attrs,
18862306a36Sopenharmony_ci};
18962306a36Sopenharmony_ci#endif
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci#ifdef CONFIG_CPU_ISOLATION_OPT
19262306a36Sopenharmony_cistatic ssize_t isolate_show(struct device *dev,
19362306a36Sopenharmony_ci			    struct device_attribute *attr, char *buf)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	struct cpu *cpu = container_of(dev, struct cpu, dev);
19662306a36Sopenharmony_ci	ssize_t rc;
19762306a36Sopenharmony_ci	int cpuid = cpu->dev.id;
19862306a36Sopenharmony_ci	unsigned int isolated = cpu_isolated(cpuid);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	rc = sysfs_emit(buf, "%d\n", isolated);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	return rc;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(isolate);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic struct attribute *cpu_isolated_attrs[] = {
20862306a36Sopenharmony_ci	&dev_attr_isolate.attr,
20962306a36Sopenharmony_ci	NULL
21062306a36Sopenharmony_ci};
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic struct attribute_group cpu_isolated_attr_group = {
21362306a36Sopenharmony_ci	.attrs = cpu_isolated_attrs,
21462306a36Sopenharmony_ci};
21562306a36Sopenharmony_ci#endif
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic const struct attribute_group *common_cpu_attr_groups[] = {
21862306a36Sopenharmony_ci#ifdef CONFIG_KEXEC_CORE
21962306a36Sopenharmony_ci	&crash_note_cpu_attr_group,
22062306a36Sopenharmony_ci#endif
22162306a36Sopenharmony_ci#ifdef CONFIG_CPU_ISOLATION_OPT
22262306a36Sopenharmony_ci	&cpu_isolated_attr_group,
22362306a36Sopenharmony_ci#endif
22462306a36Sopenharmony_ci	NULL
22562306a36Sopenharmony_ci};
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic const struct attribute_group *hotplugable_cpu_attr_groups[] = {
22862306a36Sopenharmony_ci#ifdef CONFIG_KEXEC_CORE
22962306a36Sopenharmony_ci	&crash_note_cpu_attr_group,
23062306a36Sopenharmony_ci#endif
23162306a36Sopenharmony_ci#ifdef CONFIG_CPU_ISOLATION_OPT
23262306a36Sopenharmony_ci	&cpu_isolated_attr_group,
23362306a36Sopenharmony_ci#endif
23462306a36Sopenharmony_ci	NULL
23562306a36Sopenharmony_ci};
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci/*
23862306a36Sopenharmony_ci * Print cpu online, possible, present, and system maps
23962306a36Sopenharmony_ci */
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistruct cpu_attr {
24262306a36Sopenharmony_ci	struct device_attribute attr;
24362306a36Sopenharmony_ci	const struct cpumask *const map;
24462306a36Sopenharmony_ci};
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic ssize_t show_cpus_attr(struct device *dev,
24762306a36Sopenharmony_ci			      struct device_attribute *attr,
24862306a36Sopenharmony_ci			      char *buf)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	struct cpu_attr *ca = container_of(attr, struct cpu_attr, attr);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	return cpumap_print_to_pagebuf(true, buf, ca->map);
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci#define _CPU_ATTR(name, map) \
25662306a36Sopenharmony_ci	{ __ATTR(name, 0444, show_cpus_attr, NULL), map }
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci/* Keep in sync with cpu_subsys_attrs */
25962306a36Sopenharmony_cistatic struct cpu_attr cpu_attrs[] = {
26062306a36Sopenharmony_ci	_CPU_ATTR(online, &__cpu_online_mask),
26162306a36Sopenharmony_ci	_CPU_ATTR(possible, &__cpu_possible_mask),
26262306a36Sopenharmony_ci	_CPU_ATTR(present, &__cpu_present_mask),
26362306a36Sopenharmony_ci#ifdef CONFIG_CPU_ISOLATION_OPT
26462306a36Sopenharmony_ci	_CPU_ATTR(core_ctl_isolated, &__cpu_isolated_mask),
26562306a36Sopenharmony_ci#endif
26662306a36Sopenharmony_ci};
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci/*
26962306a36Sopenharmony_ci * Print values for NR_CPUS and offlined cpus
27062306a36Sopenharmony_ci */
27162306a36Sopenharmony_cistatic ssize_t print_cpus_kernel_max(struct device *dev,
27262306a36Sopenharmony_ci				     struct device_attribute *attr, char *buf)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", NR_CPUS - 1);
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_cistatic DEVICE_ATTR(kernel_max, 0444, print_cpus_kernel_max, NULL);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci/* arch-optional setting to enable display of offline cpus >= nr_cpu_ids */
27962306a36Sopenharmony_ciunsigned int total_cpus;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cistatic ssize_t print_cpus_offline(struct device *dev,
28262306a36Sopenharmony_ci				  struct device_attribute *attr, char *buf)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	int len = 0;
28562306a36Sopenharmony_ci	cpumask_var_t offline;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	/* display offline cpus < nr_cpu_ids */
28862306a36Sopenharmony_ci	if (!alloc_cpumask_var(&offline, GFP_KERNEL))
28962306a36Sopenharmony_ci		return -ENOMEM;
29062306a36Sopenharmony_ci	cpumask_andnot(offline, cpu_possible_mask, cpu_online_mask);
29162306a36Sopenharmony_ci	len += sysfs_emit_at(buf, len, "%*pbl", cpumask_pr_args(offline));
29262306a36Sopenharmony_ci	free_cpumask_var(offline);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	/* display offline cpus >= nr_cpu_ids */
29562306a36Sopenharmony_ci	if (total_cpus && nr_cpu_ids < total_cpus) {
29662306a36Sopenharmony_ci		len += sysfs_emit_at(buf, len, ",");
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci		if (nr_cpu_ids == total_cpus-1)
29962306a36Sopenharmony_ci			len += sysfs_emit_at(buf, len, "%u", nr_cpu_ids);
30062306a36Sopenharmony_ci		else
30162306a36Sopenharmony_ci			len += sysfs_emit_at(buf, len, "%u-%d",
30262306a36Sopenharmony_ci					     nr_cpu_ids, total_cpus - 1);
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	len += sysfs_emit_at(buf, len, "\n");
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	return len;
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_cistatic DEVICE_ATTR(offline, 0444, print_cpus_offline, NULL);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic ssize_t print_cpus_isolated(struct device *dev,
31262306a36Sopenharmony_ci				  struct device_attribute *attr, char *buf)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	int len;
31562306a36Sopenharmony_ci	cpumask_var_t isolated;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	if (!alloc_cpumask_var(&isolated, GFP_KERNEL))
31862306a36Sopenharmony_ci		return -ENOMEM;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	cpumask_andnot(isolated, cpu_possible_mask,
32162306a36Sopenharmony_ci		       housekeeping_cpumask(HK_TYPE_DOMAIN));
32262306a36Sopenharmony_ci	len = sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(isolated));
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	free_cpumask_var(isolated);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	return len;
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_cistatic DEVICE_ATTR(isolated, 0444, print_cpus_isolated, NULL);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci#ifdef CONFIG_NO_HZ_FULL
33162306a36Sopenharmony_cistatic ssize_t print_cpus_nohz_full(struct device *dev,
33262306a36Sopenharmony_ci				    struct device_attribute *attr, char *buf)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(tick_nohz_full_mask));
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_cistatic DEVICE_ATTR(nohz_full, 0444, print_cpus_nohz_full, NULL);
33762306a36Sopenharmony_ci#endif
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci#ifdef CONFIG_CRASH_HOTPLUG
34062306a36Sopenharmony_cistatic ssize_t crash_hotplug_show(struct device *dev,
34162306a36Sopenharmony_ci				     struct device_attribute *attr,
34262306a36Sopenharmony_ci				     char *buf)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", crash_hotplug_cpu_support());
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_cistatic DEVICE_ATTR_ADMIN_RO(crash_hotplug);
34762306a36Sopenharmony_ci#endif
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic void cpu_device_release(struct device *dev)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	/*
35262306a36Sopenharmony_ci	 * This is an empty function to prevent the driver core from spitting a
35362306a36Sopenharmony_ci	 * warning at us.  Yes, I know this is directly opposite of what the
35462306a36Sopenharmony_ci	 * documentation for the driver core and kobjects say, and the author
35562306a36Sopenharmony_ci	 * of this code has already been publically ridiculed for doing
35662306a36Sopenharmony_ci	 * something as foolish as this.  However, at this point in time, it is
35762306a36Sopenharmony_ci	 * the only way to handle the issue of statically allocated cpu
35862306a36Sopenharmony_ci	 * devices.  The different architectures will have their cpu device
35962306a36Sopenharmony_ci	 * code reworked to properly handle this in the near future, so this
36062306a36Sopenharmony_ci	 * function will then be changed to correctly free up the memory held
36162306a36Sopenharmony_ci	 * by the cpu device.
36262306a36Sopenharmony_ci	 *
36362306a36Sopenharmony_ci	 * Never copy this way of doing things, or you too will be made fun of
36462306a36Sopenharmony_ci	 * on the linux-kernel list, you have been warned.
36562306a36Sopenharmony_ci	 */
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci#ifdef CONFIG_GENERIC_CPU_AUTOPROBE
36962306a36Sopenharmony_cistatic ssize_t print_cpu_modalias(struct device *dev,
37062306a36Sopenharmony_ci				  struct device_attribute *attr,
37162306a36Sopenharmony_ci				  char *buf)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	int len = 0;
37462306a36Sopenharmony_ci	u32 i;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	len += sysfs_emit_at(buf, len,
37762306a36Sopenharmony_ci			     "cpu:type:" CPU_FEATURE_TYPEFMT ":feature:",
37862306a36Sopenharmony_ci			     CPU_FEATURE_TYPEVAL);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	for (i = 0; i < MAX_CPU_FEATURES; i++)
38162306a36Sopenharmony_ci		if (cpu_have_feature(i)) {
38262306a36Sopenharmony_ci			if (len + sizeof(",XXXX\n") >= PAGE_SIZE) {
38362306a36Sopenharmony_ci				WARN(1, "CPU features overflow page\n");
38462306a36Sopenharmony_ci				break;
38562306a36Sopenharmony_ci			}
38662306a36Sopenharmony_ci			len += sysfs_emit_at(buf, len, ",%04X", i);
38762306a36Sopenharmony_ci		}
38862306a36Sopenharmony_ci	len += sysfs_emit_at(buf, len, "\n");
38962306a36Sopenharmony_ci	return len;
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cistatic int cpu_uevent(const struct device *dev, struct kobj_uevent_env *env)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
39562306a36Sopenharmony_ci	if (buf) {
39662306a36Sopenharmony_ci		print_cpu_modalias(NULL, NULL, buf);
39762306a36Sopenharmony_ci		add_uevent_var(env, "MODALIAS=%s", buf);
39862306a36Sopenharmony_ci		kfree(buf);
39962306a36Sopenharmony_ci	}
40062306a36Sopenharmony_ci	return 0;
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci#endif
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistruct bus_type cpu_subsys = {
40562306a36Sopenharmony_ci	.name = "cpu",
40662306a36Sopenharmony_ci	.dev_name = "cpu",
40762306a36Sopenharmony_ci	.match = cpu_subsys_match,
40862306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
40962306a36Sopenharmony_ci	.online = cpu_subsys_online,
41062306a36Sopenharmony_ci	.offline = cpu_subsys_offline,
41162306a36Sopenharmony_ci#endif
41262306a36Sopenharmony_ci#ifdef CONFIG_GENERIC_CPU_AUTOPROBE
41362306a36Sopenharmony_ci	.uevent = cpu_uevent,
41462306a36Sopenharmony_ci#endif
41562306a36Sopenharmony_ci};
41662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cpu_subsys);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci/*
41962306a36Sopenharmony_ci * register_cpu - Setup a sysfs device for a CPU.
42062306a36Sopenharmony_ci * @cpu - cpu->hotpluggable field set to 1 will generate a control file in
42162306a36Sopenharmony_ci *	  sysfs for this CPU.
42262306a36Sopenharmony_ci * @num - CPU number to use when creating the device.
42362306a36Sopenharmony_ci *
42462306a36Sopenharmony_ci * Initialize and register the CPU device.
42562306a36Sopenharmony_ci */
42662306a36Sopenharmony_ciint register_cpu(struct cpu *cpu, int num)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	int error;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	cpu->node_id = cpu_to_node(num);
43162306a36Sopenharmony_ci	memset(&cpu->dev, 0x00, sizeof(struct device));
43262306a36Sopenharmony_ci	cpu->dev.id = num;
43362306a36Sopenharmony_ci	cpu->dev.bus = &cpu_subsys;
43462306a36Sopenharmony_ci	cpu->dev.release = cpu_device_release;
43562306a36Sopenharmony_ci	cpu->dev.offline_disabled = !cpu->hotpluggable;
43662306a36Sopenharmony_ci	cpu->dev.offline = !cpu_online(num);
43762306a36Sopenharmony_ci	cpu->dev.of_node = of_get_cpu_node(num, NULL);
43862306a36Sopenharmony_ci	cpu->dev.groups = common_cpu_attr_groups;
43962306a36Sopenharmony_ci	if (cpu->hotpluggable)
44062306a36Sopenharmony_ci		cpu->dev.groups = hotplugable_cpu_attr_groups;
44162306a36Sopenharmony_ci	error = device_register(&cpu->dev);
44262306a36Sopenharmony_ci	if (error) {
44362306a36Sopenharmony_ci		put_device(&cpu->dev);
44462306a36Sopenharmony_ci		return error;
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	per_cpu(cpu_sys_devices, num) = &cpu->dev;
44862306a36Sopenharmony_ci	register_cpu_under_node(num, cpu_to_node(num));
44962306a36Sopenharmony_ci	dev_pm_qos_expose_latency_limit(&cpu->dev,
45062306a36Sopenharmony_ci					PM_QOS_RESUME_LATENCY_NO_CONSTRAINT);
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	return 0;
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistruct device *get_cpu_device(unsigned int cpu)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	if (cpu < nr_cpu_ids && cpu_possible(cpu))
45862306a36Sopenharmony_ci		return per_cpu(cpu_sys_devices, cpu);
45962306a36Sopenharmony_ci	else
46062306a36Sopenharmony_ci		return NULL;
46162306a36Sopenharmony_ci}
46262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(get_cpu_device);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_cistatic void device_create_release(struct device *dev)
46562306a36Sopenharmony_ci{
46662306a36Sopenharmony_ci	kfree(dev);
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci__printf(4, 0)
47062306a36Sopenharmony_cistatic struct device *
47162306a36Sopenharmony_ci__cpu_device_create(struct device *parent, void *drvdata,
47262306a36Sopenharmony_ci		    const struct attribute_group **groups,
47362306a36Sopenharmony_ci		    const char *fmt, va_list args)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	struct device *dev = NULL;
47662306a36Sopenharmony_ci	int retval = -ENOMEM;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
47962306a36Sopenharmony_ci	if (!dev)
48062306a36Sopenharmony_ci		goto error;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	device_initialize(dev);
48362306a36Sopenharmony_ci	dev->parent = parent;
48462306a36Sopenharmony_ci	dev->groups = groups;
48562306a36Sopenharmony_ci	dev->release = device_create_release;
48662306a36Sopenharmony_ci	device_set_pm_not_required(dev);
48762306a36Sopenharmony_ci	dev_set_drvdata(dev, drvdata);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	retval = kobject_set_name_vargs(&dev->kobj, fmt, args);
49062306a36Sopenharmony_ci	if (retval)
49162306a36Sopenharmony_ci		goto error;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	retval = device_add(dev);
49462306a36Sopenharmony_ci	if (retval)
49562306a36Sopenharmony_ci		goto error;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	return dev;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cierror:
50062306a36Sopenharmony_ci	put_device(dev);
50162306a36Sopenharmony_ci	return ERR_PTR(retval);
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistruct device *cpu_device_create(struct device *parent, void *drvdata,
50562306a36Sopenharmony_ci				 const struct attribute_group **groups,
50662306a36Sopenharmony_ci				 const char *fmt, ...)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	va_list vargs;
50962306a36Sopenharmony_ci	struct device *dev;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	va_start(vargs, fmt);
51262306a36Sopenharmony_ci	dev = __cpu_device_create(parent, drvdata, groups, fmt, vargs);
51362306a36Sopenharmony_ci	va_end(vargs);
51462306a36Sopenharmony_ci	return dev;
51562306a36Sopenharmony_ci}
51662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cpu_device_create);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci#ifdef CONFIG_GENERIC_CPU_AUTOPROBE
51962306a36Sopenharmony_cistatic DEVICE_ATTR(modalias, 0444, print_cpu_modalias, NULL);
52062306a36Sopenharmony_ci#endif
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_cistatic struct attribute *cpu_root_attrs[] = {
52362306a36Sopenharmony_ci#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
52462306a36Sopenharmony_ci	&dev_attr_probe.attr,
52562306a36Sopenharmony_ci	&dev_attr_release.attr,
52662306a36Sopenharmony_ci#endif
52762306a36Sopenharmony_ci	&cpu_attrs[0].attr.attr,
52862306a36Sopenharmony_ci	&cpu_attrs[1].attr.attr,
52962306a36Sopenharmony_ci	&cpu_attrs[2].attr.attr,
53062306a36Sopenharmony_ci#ifdef CONFIG_CPU_ISOLATION_OPT
53162306a36Sopenharmony_ci	&cpu_attrs[3].attr.attr,
53262306a36Sopenharmony_ci#endif
53362306a36Sopenharmony_ci	&dev_attr_kernel_max.attr,
53462306a36Sopenharmony_ci	&dev_attr_offline.attr,
53562306a36Sopenharmony_ci	&dev_attr_isolated.attr,
53662306a36Sopenharmony_ci#ifdef CONFIG_NO_HZ_FULL
53762306a36Sopenharmony_ci	&dev_attr_nohz_full.attr,
53862306a36Sopenharmony_ci#endif
53962306a36Sopenharmony_ci#ifdef CONFIG_CRASH_HOTPLUG
54062306a36Sopenharmony_ci	&dev_attr_crash_hotplug.attr,
54162306a36Sopenharmony_ci#endif
54262306a36Sopenharmony_ci#ifdef CONFIG_GENERIC_CPU_AUTOPROBE
54362306a36Sopenharmony_ci	&dev_attr_modalias.attr,
54462306a36Sopenharmony_ci#endif
54562306a36Sopenharmony_ci	NULL
54662306a36Sopenharmony_ci};
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_cistatic const struct attribute_group cpu_root_attr_group = {
54962306a36Sopenharmony_ci	.attrs = cpu_root_attrs,
55062306a36Sopenharmony_ci};
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_cistatic const struct attribute_group *cpu_root_attr_groups[] = {
55362306a36Sopenharmony_ci	&cpu_root_attr_group,
55462306a36Sopenharmony_ci	NULL,
55562306a36Sopenharmony_ci};
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_cibool cpu_is_hotpluggable(unsigned int cpu)
55862306a36Sopenharmony_ci{
55962306a36Sopenharmony_ci	struct device *dev = get_cpu_device(cpu);
56062306a36Sopenharmony_ci	return dev && container_of(dev, struct cpu, dev)->hotpluggable
56162306a36Sopenharmony_ci		&& tick_nohz_cpu_hotpluggable(cpu);
56262306a36Sopenharmony_ci}
56362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cpu_is_hotpluggable);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci#ifdef CONFIG_GENERIC_CPU_DEVICES
56662306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct cpu, cpu_devices);
56762306a36Sopenharmony_ci#endif
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_cistatic void __init cpu_dev_register_generic(void)
57062306a36Sopenharmony_ci{
57162306a36Sopenharmony_ci#ifdef CONFIG_GENERIC_CPU_DEVICES
57262306a36Sopenharmony_ci	int i;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	for_each_possible_cpu(i) {
57562306a36Sopenharmony_ci		if (register_cpu(&per_cpu(cpu_devices, i), i))
57662306a36Sopenharmony_ci			panic("Failed to register CPU device");
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci#endif
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci#ifdef CONFIG_GENERIC_CPU_VULNERABILITIES
58262306a36Sopenharmony_cistatic ssize_t cpu_show_not_affected(struct device *dev,
58362306a36Sopenharmony_ci			      struct device_attribute *attr, char *buf)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	return sysfs_emit(buf, "Not affected\n");
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci#define CPU_SHOW_VULN_FALLBACK(func)					\
58962306a36Sopenharmony_ci	ssize_t cpu_show_##func(struct device *,			\
59062306a36Sopenharmony_ci				  struct device_attribute *, char *)	\
59162306a36Sopenharmony_ci		 __attribute__((weak, alias("cpu_show_not_affected")))
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(meltdown);
59462306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(spectre_v1);
59562306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(spectre_v2);
59662306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(spec_store_bypass);
59762306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(l1tf);
59862306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(mds);
59962306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(tsx_async_abort);
60062306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(itlb_multihit);
60162306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(srbds);
60262306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(mmio_stale_data);
60362306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(retbleed);
60462306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(spec_rstack_overflow);
60562306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(gds);
60662306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(reg_file_data_sampling);
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_cistatic DEVICE_ATTR(meltdown, 0444, cpu_show_meltdown, NULL);
60962306a36Sopenharmony_cistatic DEVICE_ATTR(spectre_v1, 0444, cpu_show_spectre_v1, NULL);
61062306a36Sopenharmony_cistatic DEVICE_ATTR(spectre_v2, 0444, cpu_show_spectre_v2, NULL);
61162306a36Sopenharmony_cistatic DEVICE_ATTR(spec_store_bypass, 0444, cpu_show_spec_store_bypass, NULL);
61262306a36Sopenharmony_cistatic DEVICE_ATTR(l1tf, 0444, cpu_show_l1tf, NULL);
61362306a36Sopenharmony_cistatic DEVICE_ATTR(mds, 0444, cpu_show_mds, NULL);
61462306a36Sopenharmony_cistatic DEVICE_ATTR(tsx_async_abort, 0444, cpu_show_tsx_async_abort, NULL);
61562306a36Sopenharmony_cistatic DEVICE_ATTR(itlb_multihit, 0444, cpu_show_itlb_multihit, NULL);
61662306a36Sopenharmony_cistatic DEVICE_ATTR(srbds, 0444, cpu_show_srbds, NULL);
61762306a36Sopenharmony_cistatic DEVICE_ATTR(mmio_stale_data, 0444, cpu_show_mmio_stale_data, NULL);
61862306a36Sopenharmony_cistatic DEVICE_ATTR(retbleed, 0444, cpu_show_retbleed, NULL);
61962306a36Sopenharmony_cistatic DEVICE_ATTR(spec_rstack_overflow, 0444, cpu_show_spec_rstack_overflow, NULL);
62062306a36Sopenharmony_cistatic DEVICE_ATTR(gather_data_sampling, 0444, cpu_show_gds, NULL);
62162306a36Sopenharmony_cistatic DEVICE_ATTR(reg_file_data_sampling, 0444, cpu_show_reg_file_data_sampling, NULL);
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_cistatic struct attribute *cpu_root_vulnerabilities_attrs[] = {
62462306a36Sopenharmony_ci	&dev_attr_meltdown.attr,
62562306a36Sopenharmony_ci	&dev_attr_spectre_v1.attr,
62662306a36Sopenharmony_ci	&dev_attr_spectre_v2.attr,
62762306a36Sopenharmony_ci	&dev_attr_spec_store_bypass.attr,
62862306a36Sopenharmony_ci	&dev_attr_l1tf.attr,
62962306a36Sopenharmony_ci	&dev_attr_mds.attr,
63062306a36Sopenharmony_ci	&dev_attr_tsx_async_abort.attr,
63162306a36Sopenharmony_ci	&dev_attr_itlb_multihit.attr,
63262306a36Sopenharmony_ci	&dev_attr_srbds.attr,
63362306a36Sopenharmony_ci	&dev_attr_mmio_stale_data.attr,
63462306a36Sopenharmony_ci	&dev_attr_retbleed.attr,
63562306a36Sopenharmony_ci	&dev_attr_spec_rstack_overflow.attr,
63662306a36Sopenharmony_ci	&dev_attr_gather_data_sampling.attr,
63762306a36Sopenharmony_ci	&dev_attr_reg_file_data_sampling.attr,
63862306a36Sopenharmony_ci	NULL
63962306a36Sopenharmony_ci};
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_cistatic const struct attribute_group cpu_root_vulnerabilities_group = {
64262306a36Sopenharmony_ci	.name  = "vulnerabilities",
64362306a36Sopenharmony_ci	.attrs = cpu_root_vulnerabilities_attrs,
64462306a36Sopenharmony_ci};
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_cistatic void __init cpu_register_vulnerabilities(void)
64762306a36Sopenharmony_ci{
64862306a36Sopenharmony_ci	struct device *dev = bus_get_dev_root(&cpu_subsys);
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	if (dev) {
65162306a36Sopenharmony_ci		if (sysfs_create_group(&dev->kobj, &cpu_root_vulnerabilities_group))
65262306a36Sopenharmony_ci			pr_err("Unable to register CPU vulnerabilities\n");
65362306a36Sopenharmony_ci		put_device(dev);
65462306a36Sopenharmony_ci	}
65562306a36Sopenharmony_ci}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci#else
65862306a36Sopenharmony_cistatic inline void cpu_register_vulnerabilities(void) { }
65962306a36Sopenharmony_ci#endif
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_civoid __init cpu_dev_init(void)
66262306a36Sopenharmony_ci{
66362306a36Sopenharmony_ci	if (subsys_system_register(&cpu_subsys, cpu_root_attr_groups))
66462306a36Sopenharmony_ci		panic("Failed to register CPU subsystem");
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	cpu_dev_register_generic();
66762306a36Sopenharmony_ci	cpu_register_vulnerabilities();
66862306a36Sopenharmony_ci}
669