diff options
-rw-r--r-- | arch/x86/include/asm/cpu_device_id.h | 13 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/Makefile | 1 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/match.c | 48 | ||||
-rw-r--r-- | arch/x86/kernel/cpuid.c | 59 | ||||
-rw-r--r-- | include/linux/mod_devicetable.h | 21 | ||||
-rw-r--r-- | scripts/mod/file2alias.c | 24 |
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 | |||
11 | extern 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 | |||
16 | obj-y += proc.o capflags.o powerflags.o common.o | 16 | obj-y += proc.o capflags.o powerflags.o common.o |
17 | obj-y += vmware.o hypervisor.o sched.o mshyperv.o | 17 | obj-y += vmware.o hypervisor.o sched.o mshyperv.o |
18 | obj-y += rdrand.o | 18 | obj-y += rdrand.o |
19 | obj-y += match.o | ||
19 | 20 | ||
20 | obj-$(CONFIG_X86_32) += bugs.o | 21 | obj-$(CONFIG_X86_32) += bugs.o |
21 | obj-$(CONFIG_X86_64) += bugs_64.o | 22 | obj-$(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 | */ | ||
30 | const 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 | } | ||
48 | EXPORT_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 | ||
142 | static 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 | |||
174 | static DEVICE_ATTR(modalias, 0444, print_cpu_modalias, NULL); | ||
175 | |||
141 | static __cpuinit int cpuid_device_create(int cpu) | 176 | static __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 | ||
150 | static void cpuid_device_destroy(int cpu) | 195 | static 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 | ||
230 | static 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 | |||
185 | static int __init cpuid_init(void) | 241 | static 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 | |||
579 | struct 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 | } |
1014 | ADD_TO_DEVTABLE("amba", struct amba_id, do_amba_entry); | 1014 | ADD_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 | |||
1022 | static 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 | } | ||
1038 | ADD_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? */ |
1017 | static bool sym_is(const char *name, unsigned namelen, const char *symbol) | 1041 | static bool sym_is(const char *name, unsigned namelen, const char *symbol) |
1018 | { | 1042 | { |