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.
Unified · Split
| @@ -185,6 +185,14 @@ config GENERIC_CPU_DEVICES · 184 unmodified lines | ||
| 185 | 185 | bool |
| 186 | 186 | default n |
| 187 | 187 | |
| 188 | +config HAVE_CPU_AUTOPROBE | |
| 189 | + def_bool ARCH_HAS_CPU_AUTOPROBE | |
| 190 | + | |
| 191 | +config GENERIC_CPU_AUTOPROBE | |
| 192 | + bool | |
| 193 | + depends on !ARCH_HAS_CPU_AUTOPROBE | |
| 194 | + select HAVE_CPU_AUTOPROBE | |
| 195 | + | |
| 188 | 196 | config SOC_BUS |
| 189 | 197 | bool |
| 190 | 198 | |
| @@ -15,6 +15,7 @@ · 14 unmodified lines | ||
| 15 | 15 | #include <linux/percpu.h> |
| 16 | 16 | #include <linux/acpi.h> |
| 17 | 17 | #include <linux/of.h> |
| 18 | +#include <linux/cpufeature.h> | |
| 18 | 19 | |
| 19 | 20 | #include "base.h" |
| 20 | 21 | |
| @@ -286,6 +287,45 @@ static void cpu_device_release(struct device *dev) · 265 unmodified lines | ||
| 286 | 287 | */ |
| 287 | 288 | } |
| 288 | 289 | |
| 290 | +#ifdef CONFIG_HAVE_CPU_AUTOPROBE | |
| 291 | +#ifdef CONFIG_GENERIC_CPU_AUTOPROBE | |
| 292 | +static ssize_t print_cpu_modalias(struct device *dev, | |
| 293 | + struct device_attribute *attr, | |
| 294 | + char *buf) | |
| 295 | +{ | |
| 296 | + ssize_t n; | |
| 297 | + u32 i; | |
| 298 | + | |
| 299 | + n = sprintf(buf, "cpu:type:" CPU_FEATURE_TYPEFMT ":feature:", | |
| 300 | + CPU_FEATURE_TYPEVAL); | |
| 301 | + | |
| 302 | + for (i = 0; i < MAX_CPU_FEATURES; i++) | |
| 303 | + if (cpu_have_feature(i)) { | |
| 304 | + if (PAGE_SIZE < n + sizeof(",XXXX\n")) { | |
| 305 | + WARN(1, "CPU features overflow page\n"); | |
| 306 | + break; | |
| 307 | + } | |
| 308 | + n += sprintf(&buf[n], ",%04X", i); | |
| 309 | + } | |
| 310 | + buf[n++] = '\n'; | |
| 311 | + return n; | |
| 312 | +} | |
| 313 | +#else | |
| 314 | +#define print_cpu_modalias arch_print_cpu_modalias | |
| 315 | +#endif | |
| 316 | + | |
| 317 | +static int cpu_uevent(struct device *dev, struct kobj_uevent_env *env) | |
| 318 | +{ | |
| 319 | + char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL); | |
| 320 | + if (buf) { | |
| 321 | + print_cpu_modalias(NULL, NULL, buf); | |
| 322 | + add_uevent_var(env, "MODALIAS=%s", buf); | |
| 323 | + kfree(buf); | |
| 324 | + } | |
| 325 | + return 0; | |
| 326 | +} | |
| 327 | +#endif | |
| 328 | + | |
| 289 | 329 | /* |
| 290 | 330 | * register_cpu - Setup a sysfs device for a CPU. |
| 291 | 331 | * @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) · 14 unmodified lines | ||
| 306 | 346 | cpu->dev.offline_disabled = !cpu->hotpluggable; |
| 307 | 347 | cpu->dev.offline = !cpu_online(num); |
| 308 | 348 | cpu->dev.of_node = of_get_cpu_node(num, NULL); |
| 309 | −#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE | |
| 310 | − cpu->dev.bus->uevent = arch_cpu_uevent; | |
| 349 | +#ifdef CONFIG_HAVE_CPU_AUTOPROBE | |
| 350 | + cpu->dev.bus->uevent = cpu_uevent; | |
| 311 | 351 | #endif |
| 312 | 352 | cpu->dev.groups = common_cpu_attr_groups; |
| 313 | 353 | if (cpu->hotpluggable) |
| @@ -330,8 +370,8 @@ struct device *get_cpu_device(unsigned cpu) · 16 unmodified lines | ||
| 330 | 370 | } |
| 331 | 371 | EXPORT_SYMBOL_GPL(get_cpu_device); |
| 332 | 372 | |
| 333 | −#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE | |
| 334 | −static DEVICE_ATTR(modalias, 0444, arch_print_cpu_modalias, NULL); | |
| 373 | +#ifdef CONFIG_HAVE_CPU_AUTOPROBE | |
| 374 | +static DEVICE_ATTR(modalias, 0444, print_cpu_modalias, NULL); | |
| 335 | 375 | #endif |
| 336 | 376 | |
| 337 | 377 | static struct attribute *cpu_root_attrs[] = { |
| @@ -344,7 +384,7 @@ static struct attribute *cpu_root_attrs[] = { · 6 unmodified lines | ||
| 344 | 384 | &cpu_attrs[2].attr.attr, |
| 345 | 385 | &dev_attr_kernel_max.attr, |
| 346 | 386 | &dev_attr_offline.attr, |
| 347 | −#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE | |
| 387 | +#ifdef CONFIG_HAVE_CPU_AUTOPROBE | |
| 348 | 388 | &dev_attr_modalias.attr, |
| 349 | 389 | #endif |
| 350 | 390 | NULL |
| @@ -0,0 +1,60 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org> | |
| 3 | + * | |
| 4 | + * This program is free software; you can redistribute it and/or modify | |
| 5 | + * it under the terms of the GNU General Public License version 2 as | |
| 6 | + * published by the Free Software Foundation. | |
| 7 | + */ | |
| 8 | + | |
| 9 | +#ifndef __LINUX_CPUFEATURE_H | |
| 10 | +#define __LINUX_CPUFEATURE_H | |
| 11 | + | |
| 12 | +#ifdef CONFIG_GENERIC_CPU_AUTOPROBE | |
| 13 | + | |
| 14 | +#include <linux/mod_devicetable.h> | |
| 15 | +#include <asm/cpufeature.h> | |
| 16 | + | |
| 17 | +/* | |
| 18 | + * Macros imported from <asm/cpufeature.h>: | |
| 19 | + * - cpu_feature(x) ordinal value of feature called 'x' | |
| 20 | + * - cpu_have_feature(u32 n) whether feature #n is available | |
| 21 | + * - MAX_CPU_FEATURES upper bound for feature ordinal values | |
| 22 | + * Optional: | |
| 23 | + * - CPU_FEATURE_TYPEFMT format string fragment for printing the cpu type | |
| 24 | + * - CPU_FEATURE_TYPEVAL set of values matching the format string above | |
| 25 | + */ | |
| 26 | + | |
| 27 | +#ifndef CPU_FEATURE_TYPEFMT | |
| 28 | +#define CPU_FEATURE_TYPEFMT "%s" | |
| 29 | +#endif | |
| 30 | + | |
| 31 | +#ifndef CPU_FEATURE_TYPEVAL | |
| 32 | +#define CPU_FEATURE_TYPEVAL ELF_PLATFORM | |
| 33 | +#endif | |
| 34 | + | |
| 35 | +/* | |
| 36 | + * Use module_cpu_feature_match(feature, module_init_function) to | |
| 37 | + * declare that | |
| 38 | + * a) the module shall be probed upon discovery of CPU feature 'feature' | |
| 39 | + * (typically at boot time using udev) | |
| 40 | + * b) the module must not be loaded if CPU feature 'feature' is not present | |
| 41 | + * (not even by manual insmod). | |
| 42 | + * | |
| 43 | + * For a list of legal values for 'feature', please consult the file | |
| 44 | + * 'asm/cpufeature.h' of your favorite architecture. | |
| 45 | + */ | |
| 46 | +#define module_cpu_feature_match(x, __init) \ | |
| 47 | +static struct cpu_feature const cpu_feature_match_ ## x[] = \ | |
| 48 | + { { .feature = cpu_feature(x) }, { } }; \ | |
| 49 | +MODULE_DEVICE_TABLE(cpu, cpu_feature_match_ ## x); \ | |
| 50 | + \ | |
| 51 | +static int cpu_feature_match_ ## x ## _init(void) \ | |
| 52 | +{ \ | |
| 53 | + if (!cpu_have_feature(cpu_feature(x))) \ | |
| 54 | + return -ENODEV; \ | |
| 55 | + return __init(); \ | |
| 56 | +} \ | |
| 57 | +module_init(cpu_feature_match_ ## x ## _init) | |
| 58 | + | |
| 59 | +#endif | |
| 60 | +#endif | |
| @@ -564,6 +564,15 @@ struct x86_cpu_id { · 563 unmodified lines | ||
| 564 | 564 | #define X86_MODEL_ANY 0 |
| 565 | 565 | #define X86_FEATURE_ANY 0 /* Same as FPU, you can't test for that */ |
| 566 | 566 | |
| 567 | +/* | |
| 568 | + * Generic table type for matching CPU features. | |
| 569 | + * @feature: the bit number of the feature (0 - 65535) | |
| 570 | + */ | |
| 571 | + | |
| 572 | +struct cpu_feature { | |
| 573 | + __u16 feature; | |
| 574 | +}; | |
| 575 | + | |
| 567 | 576 | #define IPACK_ANY_FORMAT 0xff |
| 568 | 577 | #define IPACK_ANY_ID (~0) |
| 569 | 578 | struct ipack_device_id { |
| @@ -174,6 +174,9 @@ int main(void) · 173 unmodified lines | ||
| 174 | 174 | DEVID_FIELD(x86_cpu_id, model); |
| 175 | 175 | DEVID_FIELD(x86_cpu_id, vendor); |
| 176 | 176 | |
| 177 | + DEVID(cpu_feature); | |
| 178 | + DEVID_FIELD(cpu_feature, feature); | |
| 179 | + | |
| 177 | 180 | DEVID(mei_cl_device_id); |
| 178 | 181 | DEVID_FIELD(mei_cl_device_id, name); |
| 179 | 182 | |
| @@ -1135,6 +1135,16 @@ static int do_x86cpu_entry(const char *filename, void *symval, · 1134 unmodified lines | ||
| 1135 | 1135 | } |
| 1136 | 1136 | ADD_TO_DEVTABLE("x86cpu", x86_cpu_id, do_x86cpu_entry); |
| 1137 | 1137 | |
| 1138 | +/* LOOKS like cpu:type:*:feature:*FEAT* */ | |
| 1139 | +static int do_cpu_entry(const char *filename, void *symval, char *alias) | |
| 1140 | +{ | |
| 1141 | + DEF_FIELD(symval, cpu_feature, feature); | |
| 1142 | + | |
| 1143 | + sprintf(alias, "cpu:type:*:feature:*%04X*", feature); | |
| 1144 | + return 1; | |
| 1145 | +} | |
| 1146 | +ADD_TO_DEVTABLE("cpu", cpu_feature, do_cpu_entry); | |
| 1147 | + | |
| 1138 | 1148 | /* Looks like: mei:S */ |
| 1139 | 1149 | static int do_mei_entry(const char *filename, void *symval, |
| 1140 | 1150 | char *alias) |