aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/include/asm/cpu_device_id.h13
-rw-r--r--arch/x86/kernel/cpu/Makefile1
-rw-r--r--arch/x86/kernel/cpu/match.c48
-rw-r--r--arch/x86/kernel/cpuid.c59
-rw-r--r--include/linux/mod_devicetable.h21
-rw-r--r--scripts/mod/file2alias.c24
6 files changed, 165 insertions, 1 deletions
diff --git a/arch/x86/include/asm/cpu_device_id.h b/arch/x86/include/asm/cpu_device_id.h
new file mode 100644
index 000000000000..ff501e511d91
--- /dev/null
+++ b/arch/x86/include/asm/cpu_device_id.h
@@ -0,0 +1,13 @@
1#ifndef _CPU_DEVICE_ID
2#define _CPU_DEVICE_ID 1
3
4/*
5 * Declare drivers belonging to specific x86 CPUs
6 * Similar in spirit to pci_device_id and related PCI functions
7 */
8
9#include <linux/mod_devicetable.h>
10
11extern const struct x86_cpu_id *x86_match_cpu(const struct x86_cpu_id *match);
12
13#endif
diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile
index 25f24dccdcfa..6ab6aa2fdfdd 100644
--- a/arch/x86/kernel/cpu/Makefile
+++ b/arch/x86/kernel/cpu/Makefile
@@ -16,6 +16,7 @@ obj-y := intel_cacheinfo.o scattered.o topology.o
16obj-y += proc.o capflags.o powerflags.o common.o 16obj-y += proc.o capflags.o powerflags.o common.o
17obj-y += vmware.o hypervisor.o sched.o mshyperv.o 17obj-y += vmware.o hypervisor.o sched.o mshyperv.o
18obj-y += rdrand.o 18obj-y += rdrand.o
19obj-y += match.o
19 20
20obj-$(CONFIG_X86_32) += bugs.o 21obj-$(CONFIG_X86_32) += bugs.o
21obj-$(CONFIG_X86_64) += bugs_64.o 22obj-$(CONFIG_X86_64) += bugs_64.o
diff --git a/arch/x86/kernel/cpu/match.c b/arch/x86/kernel/cpu/match.c
new file mode 100644
index 000000000000..7acc961422e7
--- /dev/null
+++ b/arch/x86/kernel/cpu/match.c
@@ -0,0 +1,48 @@
1#include <asm/cpu_device_id.h>
2#include <asm/processor.h>
3#include <linux/cpu.h>
4#include <linux/module.h>
5
6/**
7 * x86_match_cpu - match current CPU again an array of x86_cpu_ids
8 * @match: Pointer to array of x86_cpu_ids. Last entry terminated with
9 * {}.
10 *
11 * Return the entry if the current CPU matches the entries in the
12 * passed x86_cpu_id match table. Otherwise NULL. The match table
13 * contains vendor (X86_VENDOR_*), family, model and feature bits or
14 * respective wildcard entries.
15 *
16 * A typical table entry would be to match a specific CPU
17 * { X86_VENDOR_INTEL, 6, 0x12 }
18 * or to match a specific CPU feature
19 * { X86_FEATURE_MATCH(X86_FEATURE_FOOBAR) }
20 *
21 * Fields can be wildcarded with %X86_VENDOR_ANY, %X86_FAMILY_ANY,
22 * %X86_MODEL_ANY, %X86_FEATURE_ANY or 0 (except for vendor)
23 *
24 * Arrays used to match for this should also be declared using
25 * MODULE_DEVICE_TABLE(x86_cpu, ...)
26 *
27 * This always matches against the boot cpu, assuming models and features are
28 * consistent over all CPUs.
29 */
30const struct x86_cpu_id *x86_match_cpu(const struct x86_cpu_id *match)
31{
32 const struct x86_cpu_id *m;
33 struct cpuinfo_x86 *c = &boot_cpu_data;
34
35 for (m = match; m->vendor | m->family | m->model | m->feature; m++) {
36 if (m->vendor != X86_VENDOR_ANY && c->x86_vendor != m->vendor)
37 continue;
38 if (m->family != X86_FAMILY_ANY && c->x86 != m->family)
39 continue;
40 if (m->model != X86_MODEL_ANY && c->x86_model != m->model)
41 continue;
42 if (m->feature != X86_FEATURE_ANY && !cpu_has(c, m->feature))
43 continue;
44 return m;
45 }
46 return NULL;
47}
48EXPORT_SYMBOL(x86_match_cpu);
diff --git a/arch/x86/kernel/cpuid.c b/arch/x86/kernel/cpuid.c
index a524353d93f2..7c89880eefd0 100644
--- a/arch/x86/kernel/cpuid.c
+++ b/arch/x86/kernel/cpuid.c
@@ -40,6 +40,7 @@
40#include <linux/notifier.h> 40#include <linux/notifier.h>
41#include <linux/uaccess.h> 41#include <linux/uaccess.h>
42#include <linux/gfp.h> 42#include <linux/gfp.h>
43#include <linux/slab.h>
43 44
44#include <asm/processor.h> 45#include <asm/processor.h>
45#include <asm/msr.h> 46#include <asm/msr.h>
@@ -138,13 +139,57 @@ static const struct file_operations cpuid_fops = {
138 .open = cpuid_open, 139 .open = cpuid_open,
139}; 140};
140 141
142static ssize_t print_cpu_modalias(struct device *dev,
143 struct device_attribute *attr,
144 char *bufptr)
145{
146 int size = PAGE_SIZE;
147 int i, n;
148 char *buf = bufptr;
149
150 n = snprintf(buf, size, "x86cpu:vendor:%04X:family:"
151 "%04X:model:%04X:feature:",
152 boot_cpu_data.x86_vendor,
153 boot_cpu_data.x86,
154 boot_cpu_data.x86_model);
155 size -= n;
156 buf += n;
157 size -= 2;
158 for (i = 0; i < NCAPINTS*32; i++) {
159 if (boot_cpu_has(i)) {
160 n = snprintf(buf, size, ",%04X", i);
161 if (n < 0) {
162 WARN(1, "x86 features overflow page\n");
163 break;
164 }
165 size -= n;
166 buf += n;
167 }
168 }
169 *buf++ = ',';
170 *buf++ = '\n';
171 return buf - bufptr;
172}
173
174static DEVICE_ATTR(modalias, 0444, print_cpu_modalias, NULL);
175
141static __cpuinit int cpuid_device_create(int cpu) 176static __cpuinit int cpuid_device_create(int cpu)
142{ 177{
143 struct device *dev; 178 struct device *dev;
179 int err;
144 180
145 dev = device_create(cpuid_class, NULL, MKDEV(CPUID_MAJOR, cpu), NULL, 181 dev = device_create(cpuid_class, NULL, MKDEV(CPUID_MAJOR, cpu), NULL,
146 "cpu%d", cpu); 182 "cpu%d", cpu);
147 return IS_ERR(dev) ? PTR_ERR(dev) : 0; 183 if (IS_ERR(dev))
184 return PTR_ERR(dev);
185
186 err = device_create_file(dev, &dev_attr_modalias);
187 if (err) {
188 /* keep device around on error. attribute is optional. */
189 err = 0;
190 }
191
192 return 0;
148} 193}
149 194
150static void cpuid_device_destroy(int cpu) 195static void cpuid_device_destroy(int cpu)
@@ -182,6 +227,17 @@ static char *cpuid_devnode(struct device *dev, umode_t *mode)
182 return kasprintf(GFP_KERNEL, "cpu/%u/cpuid", MINOR(dev->devt)); 227 return kasprintf(GFP_KERNEL, "cpu/%u/cpuid", MINOR(dev->devt));
183} 228}
184 229
230static int cpuid_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
231{
232 char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
233 if (buf) {
234 print_cpu_modalias(NULL, NULL, buf);
235 add_uevent_var(env, "MODALIAS=%s", buf);
236 kfree(buf);
237 }
238 return 0;
239}
240
185static int __init cpuid_init(void) 241static int __init cpuid_init(void)
186{ 242{
187 int i, err = 0; 243 int i, err = 0;
@@ -200,6 +256,7 @@ static int __init cpuid_init(void)
200 goto out_chrdev; 256 goto out_chrdev;
201 } 257 }
202 cpuid_class->devnode = cpuid_devnode; 258 cpuid_class->devnode = cpuid_devnode;
259 cpuid_class->dev_uevent = cpuid_dev_uevent;
203 for_each_online_cpu(i) { 260 for_each_online_cpu(i) {
204 err = cpuid_device_create(i); 261 err = cpuid_device_create(i);
205 if (err != 0) 262 if (err != 0)
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index b29e7f6f8fa5..cff2cc08f45a 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -571,4 +571,25 @@ struct amba_id {
571#endif 571#endif
572}; 572};
573 573
574/*
575 * Match x86 CPUs for CPU specific drivers.
576 * See documentation of "x86_match_cpu" for details.
577 */
578
579struct x86_cpu_id {
580 __u16 vendor;
581 __u16 family;
582 __u16 model;
583 __u16 feature; /* bit index */
584 kernel_ulong_t driver_data;
585};
586
587#define X86_FEATURE_MATCH(x) \
588 { X86_VENDOR_ANY, X86_FAMILY_ANY, X86_MODEL_ANY, x }
589
590#define X86_VENDOR_ANY 0xffff
591#define X86_FAMILY_ANY 0
592#define X86_MODEL_ANY 0
593#define X86_FEATURE_ANY 0 /* Same as FPU, you can't test for that */
594
574#endif /* LINUX_MOD_DEVICETABLE_H */ 595#endif /* LINUX_MOD_DEVICETABLE_H */
diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
index c0e14b3f2306..026ba38759ca 100644
--- a/scripts/mod/file2alias.c
+++ b/scripts/mod/file2alias.c
@@ -1013,6 +1013,30 @@ static int do_amba_entry(const char *filename,
1013} 1013}
1014ADD_TO_DEVTABLE("amba", struct amba_id, do_amba_entry); 1014ADD_TO_DEVTABLE("amba", struct amba_id, do_amba_entry);
1015 1015
1016/* LOOKS like x86cpu:vendor:VVVV:family:FFFF:model:MMMM:feature:*,FEAT,*
1017 * All fields are numbers. It would be nicer to use strings for vendor
1018 * and feature, but getting those out of the build system here is too
1019 * complicated.
1020 */
1021
1022static int do_x86cpu_entry(const char *filename, struct x86_cpu_id *id,
1023 char *alias)
1024{
1025 id->feature = TO_NATIVE(id->feature);
1026 id->family = TO_NATIVE(id->family);
1027 id->model = TO_NATIVE(id->model);
1028 id->vendor = TO_NATIVE(id->vendor);
1029
1030 strcpy(alias, "x86cpu:");
1031 ADD(alias, "vendor:", id->vendor != X86_VENDOR_ANY, id->vendor);
1032 ADD(alias, ":family:", id->family != X86_FAMILY_ANY, id->family);
1033 ADD(alias, ":model:", id->model != X86_MODEL_ANY, id->model);
1034 ADD(alias, ":feature:*,", id->feature != X86_FEATURE_ANY, id->feature);
1035 strcat(alias, ",*");
1036 return 1;
1037}
1038ADD_TO_DEVTABLE("x86cpu", struct x86_cpu_id, do_x86cpu_entry);
1039
1016/* Does namelen bytes of name exactly match the symbol? */ 1040/* Does namelen bytes of name exactly match the symbol? */
1017static bool sym_is(const char *name, unsigned namelen, const char *symbol) 1041static bool sym_is(const char *name, unsigned namelen, const char *symbol)
1018{ 1042{