]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Intel CPU Microcode Update Driver for Linux | |
3 | * | |
4 | * Copyright (C) 2000-2004 Tigran Aivazian | |
5 | * | |
6 | * This driver allows to upgrade microcode on Intel processors | |
7 | * belonging to IA-32 family - PentiumPro, Pentium II, | |
8 | * Pentium III, Xeon, Pentium 4, etc. | |
9 | * | |
10 | * Reference: Section 8.10 of Volume III, Intel Pentium 4 Manual, | |
11 | * Order Number 245472 or free download from: | |
12 | * | |
13 | * http://developer.intel.com/design/pentium4/manuals/245472.htm | |
14 | * | |
15 | * For more information, go to http://www.urbanmyth.org/microcode | |
16 | * | |
17 | * This program is free software; you can redistribute it and/or | |
18 | * modify it under the terms of the GNU General Public License | |
19 | * as published by the Free Software Foundation; either version | |
20 | * 2 of the License, or (at your option) any later version. | |
21 | * | |
22 | * 1.0 16 Feb 2000, Tigran Aivazian <tigran@sco.com> | |
23 | * Initial release. | |
24 | * 1.01 18 Feb 2000, Tigran Aivazian <tigran@sco.com> | |
25 | * Added read() support + cleanups. | |
26 | * 1.02 21 Feb 2000, Tigran Aivazian <tigran@sco.com> | |
27 | * Added 'device trimming' support. open(O_WRONLY) zeroes | |
28 | * and frees the saved copy of applied microcode. | |
29 | * 1.03 29 Feb 2000, Tigran Aivazian <tigran@sco.com> | |
30 | * Made to use devfs (/dev/cpu/microcode) + cleanups. | |
31 | * 1.04 06 Jun 2000, Simon Trimmer <simon@veritas.com> | |
32 | * Added misc device support (now uses both devfs and misc). | |
33 | * Added MICROCODE_IOCFREE ioctl to clear memory. | |
34 | * 1.05 09 Jun 2000, Simon Trimmer <simon@veritas.com> | |
35 | * Messages for error cases (non Intel & no suitable microcode). | |
36 | * 1.06 03 Aug 2000, Tigran Aivazian <tigran@veritas.com> | |
37 | * Removed ->release(). Removed exclusive open and status bitmap. | |
38 | * Added microcode_rwsem to serialize read()/write()/ioctl(). | |
39 | * Removed global kernel lock usage. | |
40 | * 1.07 07 Sep 2000, Tigran Aivazian <tigran@veritas.com> | |
41 | * Write 0 to 0x8B msr and then cpuid before reading revision, | |
42 | * so that it works even if there were no update done by the | |
43 | * BIOS. Otherwise, reading from 0x8B gives junk (which happened | |
44 | * to be 0 on my machine which is why it worked even when I | |
45 | * disabled update by the BIOS) | |
46 | * Thanks to Eric W. Biederman <ebiederman@lnxi.com> for the fix. | |
47 | * 1.08 11 Dec 2000, Richard Schaal <richard.schaal@intel.com> and | |
48 | * Tigran Aivazian <tigran@veritas.com> | |
49 | * Intel Pentium 4 processor support and bugfixes. | |
50 | * 1.09 30 Oct 2001, Tigran Aivazian <tigran@veritas.com> | |
51 | * Bugfix for HT (Hyper-Threading) enabled processors | |
52 | * whereby processor resources are shared by all logical processors | |
53 | * in a single CPU package. | |
54 | * 1.10 28 Feb 2002 Asit K Mallick <asit.k.mallick@intel.com> and | |
55 | * Tigran Aivazian <tigran@veritas.com>, | |
56 | * Serialize updates as required on HT processors due to speculative | |
57 | * nature of implementation. | |
58 | * 1.11 22 Mar 2002 Tigran Aivazian <tigran@veritas.com> | |
59 | * Fix the panic when writing zero-length microcode chunk. | |
60 | * 1.12 29 Sep 2003 Nitin Kamble <nitin.a.kamble@intel.com>, | |
61 | * Jun Nakajima <jun.nakajima@intel.com> | |
62 | * Support for the microcode updates in the new format. | |
63 | * 1.13 10 Oct 2003 Tigran Aivazian <tigran@veritas.com> | |
64 | * Removed ->read() method and obsoleted MICROCODE_IOCFREE ioctl | |
65 | * because we no longer hold a copy of applied microcode | |
66 | * in kernel memory. | |
67 | * 1.14 25 Jun 2004 Tigran Aivazian <tigran@veritas.com> | |
68 | * Fix sigmatch() macro to handle old CPUs with pf == 0. | |
69 | * Thanks to Stuart Swales for pointing out this bug. | |
70 | */ | |
71 | ||
72 | //#define DEBUG /* pr_debug */ | |
73 | #include <linux/kernel.h> | |
74 | #include <linux/init.h> | |
75 | #include <linux/sched.h> | |
76 | #include <linux/module.h> | |
77 | #include <linux/slab.h> | |
78 | #include <linux/vmalloc.h> | |
79 | #include <linux/miscdevice.h> | |
80 | #include <linux/spinlock.h> | |
81 | #include <linux/mm.h> | |
82 | ||
83 | #include <asm/msr.h> | |
84 | #include <asm/uaccess.h> | |
85 | #include <asm/processor.h> | |
86 | ||
87 | MODULE_DESCRIPTION("Intel CPU (IA-32) Microcode Update Driver"); | |
88 | MODULE_AUTHOR("Tigran Aivazian <tigran@veritas.com>"); | |
89 | MODULE_LICENSE("GPL"); | |
90 | ||
91 | #define MICROCODE_VERSION "1.14" | |
92 | ||
93 | #define DEFAULT_UCODE_DATASIZE (2000) /* 2000 bytes */ | |
94 | #define MC_HEADER_SIZE (sizeof (microcode_header_t)) /* 48 bytes */ | |
95 | #define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE) /* 2048 bytes */ | |
96 | #define EXT_HEADER_SIZE (sizeof (struct extended_sigtable)) /* 20 bytes */ | |
97 | #define EXT_SIGNATURE_SIZE (sizeof (struct extended_signature)) /* 12 bytes */ | |
98 | #define DWSIZE (sizeof (u32)) | |
99 | #define get_totalsize(mc) \ | |
100 | (((microcode_t *)mc)->hdr.totalsize ? \ | |
101 | ((microcode_t *)mc)->hdr.totalsize : DEFAULT_UCODE_TOTALSIZE) | |
102 | #define get_datasize(mc) \ | |
103 | (((microcode_t *)mc)->hdr.datasize ? \ | |
104 | ((microcode_t *)mc)->hdr.datasize : DEFAULT_UCODE_DATASIZE) | |
105 | ||
106 | #define sigmatch(s1, s2, p1, p2) \ | |
107 | (((s1) == (s2)) && (((p1) & (p2)) || (((p1) == 0) && ((p2) == 0)))) | |
108 | ||
109 | #define exttable_size(et) ((et)->count * EXT_SIGNATURE_SIZE + EXT_HEADER_SIZE) | |
110 | ||
111 | /* serialize access to the physical write to MSR 0x79 */ | |
112 | static DEFINE_SPINLOCK(microcode_update_lock); | |
113 | ||
114 | /* no concurrent ->write()s are allowed on /dev/cpu/microcode */ | |
115 | static DECLARE_MUTEX(microcode_sem); | |
116 | ||
117 | static void __user *user_buffer; /* user area microcode data buffer */ | |
118 | static unsigned int user_buffer_size; /* it's size */ | |
119 | ||
120 | typedef enum mc_error_code { | |
121 | MC_SUCCESS = 0, | |
122 | MC_NOTFOUND = 1, | |
123 | MC_MARKED = 2, | |
124 | MC_ALLOCATED = 3, | |
125 | } mc_error_code_t; | |
126 | ||
127 | static struct ucode_cpu_info { | |
128 | unsigned int sig; | |
129 | unsigned int pf; | |
130 | unsigned int rev; | |
131 | unsigned int cksum; | |
132 | mc_error_code_t err; | |
133 | microcode_t *mc; | |
134 | } ucode_cpu_info[NR_CPUS]; | |
135 | ||
136 | static int microcode_open (struct inode *unused1, struct file *unused2) | |
137 | { | |
138 | return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; | |
139 | } | |
140 | ||
141 | static void collect_cpu_info (void *unused) | |
142 | { | |
143 | int cpu_num = smp_processor_id(); | |
144 | struct cpuinfo_x86 *c = cpu_data + cpu_num; | |
145 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; | |
146 | unsigned int val[2]; | |
147 | ||
148 | uci->sig = uci->pf = uci->rev = uci->cksum = 0; | |
149 | uci->err = MC_NOTFOUND; | |
150 | uci->mc = NULL; | |
151 | ||
152 | if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6 || | |
153 | cpu_has(c, X86_FEATURE_IA64)) { | |
154 | printk(KERN_ERR "microcode: CPU%d not a capable Intel processor\n", cpu_num); | |
155 | return; | |
156 | } else { | |
157 | uci->sig = cpuid_eax(0x00000001); | |
158 | ||
159 | if ((c->x86_model >= 5) || (c->x86 > 6)) { | |
160 | /* get processor flags from MSR 0x17 */ | |
161 | rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]); | |
162 | uci->pf = 1 << ((val[1] >> 18) & 7); | |
163 | } | |
164 | } | |
165 | ||
166 | wrmsr(MSR_IA32_UCODE_REV, 0, 0); | |
245067d1 ZA |
167 | /* see notes above for revision 1.07. Apparent chip bug */ |
168 | serialize_cpu(); | |
1da177e4 LT |
169 | /* get the current revision from MSR 0x8B */ |
170 | rdmsr(MSR_IA32_UCODE_REV, val[0], uci->rev); | |
171 | pr_debug("microcode: collect_cpu_info : sig=0x%x, pf=0x%x, rev=0x%x\n", | |
172 | uci->sig, uci->pf, uci->rev); | |
173 | } | |
174 | ||
175 | static inline void mark_microcode_update (int cpu_num, microcode_header_t *mc_header, int sig, int pf, int cksum) | |
176 | { | |
177 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; | |
178 | ||
179 | pr_debug("Microcode Found.\n"); | |
180 | pr_debug(" Header Revision 0x%x\n", mc_header->hdrver); | |
181 | pr_debug(" Loader Revision 0x%x\n", mc_header->ldrver); | |
182 | pr_debug(" Revision 0x%x \n", mc_header->rev); | |
183 | pr_debug(" Date %x/%x/%x\n", | |
184 | ((mc_header->date >> 24 ) & 0xff), | |
185 | ((mc_header->date >> 16 ) & 0xff), | |
186 | (mc_header->date & 0xFFFF)); | |
187 | pr_debug(" Signature 0x%x\n", sig); | |
188 | pr_debug(" Type 0x%x Family 0x%x Model 0x%x Stepping 0x%x\n", | |
189 | ((sig >> 12) & 0x3), | |
190 | ((sig >> 8) & 0xf), | |
191 | ((sig >> 4) & 0xf), | |
192 | ((sig & 0xf))); | |
193 | pr_debug(" Processor Flags 0x%x\n", pf); | |
194 | pr_debug(" Checksum 0x%x\n", cksum); | |
195 | ||
196 | if (mc_header->rev < uci->rev) { | |
197 | printk(KERN_ERR "microcode: CPU%d not 'upgrading' to earlier revision" | |
198 | " 0x%x (current=0x%x)\n", cpu_num, mc_header->rev, uci->rev); | |
199 | goto out; | |
200 | } else if (mc_header->rev == uci->rev) { | |
201 | /* notify the caller of success on this cpu */ | |
202 | uci->err = MC_SUCCESS; | |
203 | printk(KERN_ERR "microcode: CPU%d already at revision" | |
204 | " 0x%x (current=0x%x)\n", cpu_num, mc_header->rev, uci->rev); | |
205 | goto out; | |
206 | } | |
207 | ||
208 | pr_debug("microcode: CPU%d found a matching microcode update with " | |
209 | " revision 0x%x (current=0x%x)\n", cpu_num, mc_header->rev, uci->rev); | |
210 | uci->cksum = cksum; | |
211 | uci->pf = pf; /* keep the original mc pf for cksum calculation */ | |
212 | uci->err = MC_MARKED; /* found the match */ | |
213 | out: | |
214 | return; | |
215 | } | |
216 | ||
217 | static int find_matching_ucodes (void) | |
218 | { | |
219 | int cursor = 0; | |
220 | int error = 0; | |
221 | ||
222 | while (cursor + MC_HEADER_SIZE < user_buffer_size) { | |
223 | microcode_header_t mc_header; | |
224 | void *newmc = NULL; | |
225 | int i, sum, cpu_num, allocated_flag, total_size, data_size, ext_table_size; | |
226 | ||
227 | if (copy_from_user(&mc_header, user_buffer + cursor, MC_HEADER_SIZE)) { | |
228 | printk(KERN_ERR "microcode: error! Can not read user data\n"); | |
229 | error = -EFAULT; | |
230 | goto out; | |
231 | } | |
232 | ||
233 | total_size = get_totalsize(&mc_header); | |
234 | if ((cursor + total_size > user_buffer_size) || (total_size < DEFAULT_UCODE_TOTALSIZE)) { | |
235 | printk(KERN_ERR "microcode: error! Bad data in microcode data file\n"); | |
236 | error = -EINVAL; | |
237 | goto out; | |
238 | } | |
239 | ||
240 | data_size = get_datasize(&mc_header); | |
241 | if ((data_size + MC_HEADER_SIZE > total_size) || (data_size < DEFAULT_UCODE_DATASIZE)) { | |
242 | printk(KERN_ERR "microcode: error! Bad data in microcode data file\n"); | |
243 | error = -EINVAL; | |
244 | goto out; | |
245 | } | |
246 | ||
247 | if (mc_header.ldrver != 1 || mc_header.hdrver != 1) { | |
248 | printk(KERN_ERR "microcode: error! Unknown microcode update format\n"); | |
249 | error = -EINVAL; | |
250 | goto out; | |
251 | } | |
252 | ||
253 | for (cpu_num = 0; cpu_num < num_online_cpus(); cpu_num++) { | |
254 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; | |
255 | if (uci->err != MC_NOTFOUND) /* already found a match or not an online cpu*/ | |
256 | continue; | |
257 | ||
258 | if (sigmatch(mc_header.sig, uci->sig, mc_header.pf, uci->pf)) | |
259 | mark_microcode_update(cpu_num, &mc_header, mc_header.sig, mc_header.pf, mc_header.cksum); | |
260 | } | |
261 | ||
262 | ext_table_size = total_size - (MC_HEADER_SIZE + data_size); | |
263 | if (ext_table_size) { | |
264 | struct extended_sigtable ext_header; | |
265 | struct extended_signature ext_sig; | |
266 | int ext_sigcount; | |
267 | ||
268 | if ((ext_table_size < EXT_HEADER_SIZE) | |
269 | || ((ext_table_size - EXT_HEADER_SIZE) % EXT_SIGNATURE_SIZE)) { | |
270 | printk(KERN_ERR "microcode: error! Bad data in microcode data file\n"); | |
271 | error = -EINVAL; | |
272 | goto out; | |
273 | } | |
274 | if (copy_from_user(&ext_header, user_buffer + cursor | |
275 | + MC_HEADER_SIZE + data_size, EXT_HEADER_SIZE)) { | |
276 | printk(KERN_ERR "microcode: error! Can not read user data\n"); | |
277 | error = -EFAULT; | |
278 | goto out; | |
279 | } | |
280 | if (ext_table_size != exttable_size(&ext_header)) { | |
281 | printk(KERN_ERR "microcode: error! Bad data in microcode data file\n"); | |
282 | error = -EFAULT; | |
283 | goto out; | |
284 | } | |
285 | ||
286 | ext_sigcount = ext_header.count; | |
287 | ||
288 | for (i = 0; i < ext_sigcount; i++) { | |
289 | if (copy_from_user(&ext_sig, user_buffer + cursor + MC_HEADER_SIZE + data_size + EXT_HEADER_SIZE | |
290 | + EXT_SIGNATURE_SIZE * i, EXT_SIGNATURE_SIZE)) { | |
291 | printk(KERN_ERR "microcode: error! Can not read user data\n"); | |
292 | error = -EFAULT; | |
293 | goto out; | |
294 | } | |
295 | for (cpu_num = 0; cpu_num < num_online_cpus(); cpu_num++) { | |
296 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; | |
297 | if (uci->err != MC_NOTFOUND) /* already found a match or not an online cpu*/ | |
298 | continue; | |
299 | if (sigmatch(ext_sig.sig, uci->sig, ext_sig.pf, uci->pf)) { | |
300 | mark_microcode_update(cpu_num, &mc_header, ext_sig.sig, ext_sig.pf, ext_sig.cksum); | |
301 | } | |
302 | } | |
303 | } | |
304 | } | |
305 | /* now check if any cpu has matched */ | |
306 | for (cpu_num = 0, allocated_flag = 0, sum = 0; cpu_num < num_online_cpus(); cpu_num++) { | |
307 | if (ucode_cpu_info[cpu_num].err == MC_MARKED) { | |
308 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; | |
309 | if (!allocated_flag) { | |
310 | allocated_flag = 1; | |
311 | newmc = vmalloc(total_size); | |
312 | if (!newmc) { | |
313 | printk(KERN_ERR "microcode: error! Can not allocate memory\n"); | |
314 | error = -ENOMEM; | |
315 | goto out; | |
316 | } | |
317 | if (copy_from_user(newmc + MC_HEADER_SIZE, | |
318 | user_buffer + cursor + MC_HEADER_SIZE, | |
319 | total_size - MC_HEADER_SIZE)) { | |
320 | printk(KERN_ERR "microcode: error! Can not read user data\n"); | |
321 | vfree(newmc); | |
322 | error = -EFAULT; | |
323 | goto out; | |
324 | } | |
325 | memcpy(newmc, &mc_header, MC_HEADER_SIZE); | |
326 | /* check extended table checksum */ | |
327 | if (ext_table_size) { | |
328 | int ext_table_sum = 0; | |
329 | int * ext_tablep = (((void *) newmc) + MC_HEADER_SIZE + data_size); | |
330 | i = ext_table_size / DWSIZE; | |
331 | while (i--) ext_table_sum += ext_tablep[i]; | |
332 | if (ext_table_sum) { | |
333 | printk(KERN_WARNING "microcode: aborting, bad extended signature table checksum\n"); | |
334 | vfree(newmc); | |
335 | error = -EINVAL; | |
336 | goto out; | |
337 | } | |
338 | } | |
339 | ||
340 | /* calculate the checksum */ | |
341 | i = (MC_HEADER_SIZE + data_size) / DWSIZE; | |
342 | while (i--) sum += ((int *)newmc)[i]; | |
343 | sum -= (mc_header.sig + mc_header.pf + mc_header.cksum); | |
344 | } | |
345 | ucode_cpu_info[cpu_num].mc = newmc; | |
346 | ucode_cpu_info[cpu_num].err = MC_ALLOCATED; /* mc updated */ | |
347 | if (sum + uci->sig + uci->pf + uci->cksum != 0) { | |
348 | printk(KERN_ERR "microcode: CPU%d aborting, bad checksum\n", cpu_num); | |
349 | error = -EINVAL; | |
350 | goto out; | |
351 | } | |
352 | } | |
353 | } | |
354 | cursor += total_size; /* goto the next update patch */ | |
355 | } /* end of while */ | |
356 | out: | |
357 | return error; | |
358 | } | |
359 | ||
360 | static void do_update_one (void * unused) | |
361 | { | |
362 | unsigned long flags; | |
363 | unsigned int val[2]; | |
364 | int cpu_num = smp_processor_id(); | |
365 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; | |
366 | ||
367 | if (uci->mc == NULL) { | |
368 | printk(KERN_INFO "microcode: No new microcode data for CPU%d\n", cpu_num); | |
369 | return; | |
370 | } | |
371 | ||
372 | /* serialize access to the physical write to MSR 0x79 */ | |
373 | spin_lock_irqsave(µcode_update_lock, flags); | |
374 | ||
375 | /* write microcode via MSR 0x79 */ | |
376 | wrmsr(MSR_IA32_UCODE_WRITE, | |
377 | (unsigned long) uci->mc->bits, | |
378 | (unsigned long) uci->mc->bits >> 16 >> 16); | |
379 | wrmsr(MSR_IA32_UCODE_REV, 0, 0); | |
380 | ||
245067d1 ZA |
381 | /* see notes above for revision 1.07. Apparent chip bug */ |
382 | serialize_cpu(); | |
383 | ||
1da177e4 LT |
384 | /* get the current revision from MSR 0x8B */ |
385 | rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]); | |
386 | ||
387 | /* notify the caller of success on this cpu */ | |
388 | uci->err = MC_SUCCESS; | |
389 | spin_unlock_irqrestore(µcode_update_lock, flags); | |
390 | printk(KERN_INFO "microcode: CPU%d updated from revision " | |
391 | "0x%x to 0x%x, date = %08x \n", | |
392 | cpu_num, uci->rev, val[1], uci->mc->hdr.date); | |
393 | return; | |
394 | } | |
395 | ||
396 | static int do_microcode_update (void) | |
397 | { | |
398 | int i, error; | |
399 | ||
400 | if (on_each_cpu(collect_cpu_info, NULL, 1, 1) != 0) { | |
401 | printk(KERN_ERR "microcode: Error! Could not run on all processors\n"); | |
402 | error = -EIO; | |
403 | goto out; | |
404 | } | |
405 | ||
406 | if ((error = find_matching_ucodes())) { | |
407 | printk(KERN_ERR "microcode: Error in the microcode data\n"); | |
408 | goto out_free; | |
409 | } | |
410 | ||
411 | if (on_each_cpu(do_update_one, NULL, 1, 1) != 0) { | |
412 | printk(KERN_ERR "microcode: Error! Could not run on all processors\n"); | |
413 | error = -EIO; | |
414 | } | |
415 | ||
416 | out_free: | |
417 | for (i = 0; i < num_online_cpus(); i++) { | |
418 | if (ucode_cpu_info[i].mc) { | |
419 | int j; | |
420 | void *tmp = ucode_cpu_info[i].mc; | |
421 | vfree(tmp); | |
422 | for (j = i; j < num_online_cpus(); j++) { | |
423 | if (ucode_cpu_info[j].mc == tmp) | |
424 | ucode_cpu_info[j].mc = NULL; | |
425 | } | |
426 | } | |
427 | } | |
428 | out: | |
429 | return error; | |
430 | } | |
431 | ||
432 | static ssize_t microcode_write (struct file *file, const char __user *buf, size_t len, loff_t *ppos) | |
433 | { | |
434 | ssize_t ret; | |
435 | ||
436 | if (len < DEFAULT_UCODE_TOTALSIZE) { | |
437 | printk(KERN_ERR "microcode: not enough data\n"); | |
438 | return -EINVAL; | |
439 | } | |
440 | ||
441 | if ((len >> PAGE_SHIFT) > num_physpages) { | |
442 | printk(KERN_ERR "microcode: too much data (max %ld pages)\n", num_physpages); | |
443 | return -EINVAL; | |
444 | } | |
445 | ||
446 | down(µcode_sem); | |
447 | ||
448 | user_buffer = (void __user *) buf; | |
449 | user_buffer_size = (int) len; | |
450 | ||
451 | ret = do_microcode_update(); | |
452 | if (!ret) | |
453 | ret = (ssize_t)len; | |
454 | ||
455 | up(µcode_sem); | |
456 | ||
457 | return ret; | |
458 | } | |
459 | ||
460 | static int microcode_ioctl (struct inode *inode, struct file *file, | |
461 | unsigned int cmd, unsigned long arg) | |
462 | { | |
463 | switch (cmd) { | |
464 | /* | |
465 | * XXX: will be removed after microcode_ctl | |
466 | * is updated to ignore failure of this ioctl() | |
467 | */ | |
468 | case MICROCODE_IOCFREE: | |
469 | return 0; | |
470 | default: | |
471 | return -EINVAL; | |
472 | } | |
473 | return -EINVAL; | |
474 | } | |
475 | ||
476 | static struct file_operations microcode_fops = { | |
477 | .owner = THIS_MODULE, | |
478 | .write = microcode_write, | |
479 | .ioctl = microcode_ioctl, | |
480 | .open = microcode_open, | |
481 | }; | |
482 | ||
483 | static struct miscdevice microcode_dev = { | |
484 | .minor = MICROCODE_MINOR, | |
485 | .name = "microcode", | |
486 | .devfs_name = "cpu/microcode", | |
487 | .fops = µcode_fops, | |
488 | }; | |
489 | ||
490 | static int __init microcode_init (void) | |
491 | { | |
492 | int error; | |
493 | ||
494 | error = misc_register(µcode_dev); | |
495 | if (error) { | |
496 | printk(KERN_ERR | |
497 | "microcode: can't misc_register on minor=%d\n", | |
498 | MICROCODE_MINOR); | |
499 | return error; | |
500 | } | |
501 | ||
502 | printk(KERN_INFO | |
503 | "IA-32 Microcode Update Driver: v" MICROCODE_VERSION " <tigran@veritas.com>\n"); | |
504 | return 0; | |
505 | } | |
506 | ||
507 | static void __exit microcode_exit (void) | |
508 | { | |
509 | misc_deregister(µcode_dev); | |
510 | printk(KERN_INFO "IA-32 Microcode Update Driver v" MICROCODE_VERSION " unregistered\n"); | |
511 | } | |
512 | ||
513 | module_init(microcode_init) | |
514 | module_exit(microcode_exit) | |
515 | MODULE_ALIAS_MISCDEV(MICROCODE_MINOR); |