Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 67bad2fd authored by Ard Biesheuvel's avatar Ard Biesheuvel Committed by Greg Kroah-Hartman
Browse files

cpu: add generic support for CPU feature based module autoloading



This patch adds support for advertising optional CPU features over udev
using the modalias, and for declaring compatibility with/dependency upon
such a feature in a module.

The mapping between feature numbers and actual features should be provided
by the architecture in a file called <asm/cpufeature.h> which exports the
following functions/macros:
- cpu_feature(FEAT), a preprocessor macro that maps token FEAT to a
  numeric index;
- bool cpu_have_feature(n), returning whether this CPU has support for
  feature #n;
- MAX_CPU_FEATURES, an upper bound for 'n' in the previous function.

The feature can then be enabled by setting CONFIG_GENERIC_CPU_AUTOPROBE
for the architecture.

For instance, a module that registers its module init function using

  module_cpu_feature_match(FEAT_X, module_init_function)

will be probed automatically when the CPU's support for the 'FEAT_X'
feature is advertised over udev, and will only allow the module to be
loaded by hand if the 'FEAT_X' feature is supported.

Signed-off-by: default avatarArd Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 91219a3b
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -185,6 +185,14 @@ config GENERIC_CPU_DEVICES
	bool
	default n

config HAVE_CPU_AUTOPROBE
	def_bool ARCH_HAS_CPU_AUTOPROBE

config GENERIC_CPU_AUTOPROBE
	bool
	depends on !ARCH_HAS_CPU_AUTOPROBE
	select HAVE_CPU_AUTOPROBE

config SOC_BUS
	bool

+45 −5
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
#include <linux/percpu.h>
#include <linux/acpi.h>
#include <linux/of.h>
#include <linux/cpufeature.h>

#include "base.h"

@@ -286,6 +287,45 @@ static void cpu_device_release(struct device *dev)
	 */
}

#ifdef CONFIG_HAVE_CPU_AUTOPROBE
#ifdef CONFIG_GENERIC_CPU_AUTOPROBE
static ssize_t print_cpu_modalias(struct device *dev,
				  struct device_attribute *attr,
				  char *buf)
{
	ssize_t n;
	u32 i;

	n = sprintf(buf, "cpu:type:" CPU_FEATURE_TYPEFMT ":feature:",
		    CPU_FEATURE_TYPEVAL);

	for (i = 0; i < MAX_CPU_FEATURES; i++)
		if (cpu_have_feature(i)) {
			if (PAGE_SIZE < n + sizeof(",XXXX\n")) {
				WARN(1, "CPU features overflow page\n");
				break;
			}
			n += sprintf(&buf[n], ",%04X", i);
		}
	buf[n++] = '\n';
	return n;
}
#else
#define print_cpu_modalias	arch_print_cpu_modalias
#endif

static int cpu_uevent(struct device *dev, struct kobj_uevent_env *env)
{
	char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
	if (buf) {
		print_cpu_modalias(NULL, NULL, buf);
		add_uevent_var(env, "MODALIAS=%s", buf);
		kfree(buf);
	}
	return 0;
}
#endif

/*
 * register_cpu - Setup a sysfs device for a CPU.
 * @cpu - cpu->hotpluggable field set to 1 will generate a control file in
@@ -306,8 +346,8 @@ int register_cpu(struct cpu *cpu, int num)
	cpu->dev.offline_disabled = !cpu->hotpluggable;
	cpu->dev.offline = !cpu_online(num);
	cpu->dev.of_node = of_get_cpu_node(num, NULL);
#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE
	cpu->dev.bus->uevent = arch_cpu_uevent;
#ifdef CONFIG_HAVE_CPU_AUTOPROBE
	cpu->dev.bus->uevent = cpu_uevent;
#endif
	cpu->dev.groups = common_cpu_attr_groups;
	if (cpu->hotpluggable)
@@ -330,8 +370,8 @@ struct device *get_cpu_device(unsigned cpu)
}
EXPORT_SYMBOL_GPL(get_cpu_device);

#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE
static DEVICE_ATTR(modalias, 0444, arch_print_cpu_modalias, NULL);
#ifdef CONFIG_HAVE_CPU_AUTOPROBE
static DEVICE_ATTR(modalias, 0444, print_cpu_modalias, NULL);
#endif

static struct attribute *cpu_root_attrs[] = {
@@ -344,7 +384,7 @@ static struct attribute *cpu_root_attrs[] = {
	&cpu_attrs[2].attr.attr,
	&dev_attr_kernel_max.attr,
	&dev_attr_offline.attr,
#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE
#ifdef CONFIG_HAVE_CPU_AUTOPROBE
	&dev_attr_modalias.attr,
#endif
	NULL
+60 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#ifndef __LINUX_CPUFEATURE_H
#define __LINUX_CPUFEATURE_H

#ifdef CONFIG_GENERIC_CPU_AUTOPROBE

#include <linux/mod_devicetable.h>
#include <asm/cpufeature.h>

/*
 * Macros imported from <asm/cpufeature.h>:
 * - cpu_feature(x)		ordinal value of feature called 'x'
 * - cpu_have_feature(u32 n)	whether feature #n is available
 * - MAX_CPU_FEATURES		upper bound for feature ordinal values
 * Optional:
 * - CPU_FEATURE_TYPEFMT	format string fragment for printing the cpu type
 * - CPU_FEATURE_TYPEVAL	set of values matching the format string above
 */

#ifndef CPU_FEATURE_TYPEFMT
#define CPU_FEATURE_TYPEFMT	"%s"
#endif

#ifndef CPU_FEATURE_TYPEVAL
#define CPU_FEATURE_TYPEVAL	ELF_PLATFORM
#endif

/*
 * Use module_cpu_feature_match(feature, module_init_function) to
 * declare that
 * a) the module shall be probed upon discovery of CPU feature 'feature'
 *    (typically at boot time using udev)
 * b) the module must not be loaded if CPU feature 'feature' is not present
 *    (not even by manual insmod).
 *
 * For a list of legal values for 'feature', please consult the file
 * 'asm/cpufeature.h' of your favorite architecture.
 */
#define module_cpu_feature_match(x, __init)			\
static struct cpu_feature const cpu_feature_match_ ## x[] =	\
	{ { .feature = cpu_feature(x) }, { } };			\
MODULE_DEVICE_TABLE(cpu, cpu_feature_match_ ## x);		\
								\
static int cpu_feature_match_ ## x ## _init(void)		\
{								\
	if (!cpu_have_feature(cpu_feature(x)))			\
		return -ENODEV;					\
	return __init();					\
}								\
module_init(cpu_feature_match_ ## x ## _init)

#endif
#endif
+9 −0
Original line number Diff line number Diff line
@@ -564,6 +564,15 @@ struct x86_cpu_id {
#define X86_MODEL_ANY  0
#define X86_FEATURE_ANY 0	/* Same as FPU, you can't test for that */

/*
 * Generic table type for matching CPU features.
 * @feature:	the bit number of the feature (0 - 65535)
 */

struct cpu_feature {
	__u16	feature;
};

#define IPACK_ANY_FORMAT 0xff
#define IPACK_ANY_ID (~0)
struct ipack_device_id {
+3 −0
Original line number Diff line number Diff line
@@ -174,6 +174,9 @@ int main(void)
	DEVID_FIELD(x86_cpu_id, model);
	DEVID_FIELD(x86_cpu_id, vendor);

	DEVID(cpu_feature);
	DEVID_FIELD(cpu_feature, feature);

	DEVID(mei_cl_device_id);
	DEVID_FIELD(mei_cl_device_id, name);

Loading