diff options
-rw-r--r-- | arch/x86/include/asm/microcode.h | 2 | ||||
-rw-r--r-- | arch/x86/kernel/microcode_amd.c | 87 | ||||
-rw-r--r-- | arch/x86/kernel/microcode_core.c | 8 |
3 files changed, 55 insertions, 42 deletions
diff --git a/arch/x86/include/asm/microcode.h b/arch/x86/include/asm/microcode.h index ef51b501e22a..c24ca9a56458 100644 --- a/arch/x86/include/asm/microcode.h +++ b/arch/x86/include/asm/microcode.h | |||
@@ -12,6 +12,8 @@ struct device; | |||
12 | enum ucode_state { UCODE_ERROR, UCODE_OK, UCODE_NFOUND }; | 12 | enum ucode_state { UCODE_ERROR, UCODE_OK, UCODE_NFOUND }; |
13 | 13 | ||
14 | struct microcode_ops { | 14 | struct microcode_ops { |
15 | void (*init)(struct device *device); | ||
16 | void (*fini)(void); | ||
15 | enum ucode_state (*request_microcode_user) (int cpu, | 17 | enum ucode_state (*request_microcode_user) (int cpu, |
16 | const void __user *buf, size_t size); | 18 | const void __user *buf, size_t size); |
17 | 19 | ||
diff --git a/arch/x86/kernel/microcode_amd.c b/arch/x86/kernel/microcode_amd.c index f4c538b681ca..63123d902103 100644 --- a/arch/x86/kernel/microcode_amd.c +++ b/arch/x86/kernel/microcode_amd.c | |||
@@ -33,6 +33,9 @@ MODULE_LICENSE("GPL v2"); | |||
33 | #define UCODE_EQUIV_CPU_TABLE_TYPE 0x00000000 | 33 | #define UCODE_EQUIV_CPU_TABLE_TYPE 0x00000000 |
34 | #define UCODE_UCODE_TYPE 0x00000001 | 34 | #define UCODE_UCODE_TYPE 0x00000001 |
35 | 35 | ||
36 | const struct firmware *firmware; | ||
37 | static int supported_cpu; | ||
38 | |||
36 | struct equiv_cpu_entry { | 39 | struct equiv_cpu_entry { |
37 | u32 installed_cpu; | 40 | u32 installed_cpu; |
38 | u32 fixed_errata_mask; | 41 | u32 fixed_errata_mask; |
@@ -71,17 +74,14 @@ static struct equiv_cpu_entry *equiv_cpu_table; | |||
71 | 74 | ||
72 | static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig) | 75 | static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig) |
73 | { | 76 | { |
74 | struct cpuinfo_x86 *c = &cpu_data(cpu); | ||
75 | u32 dummy; | 77 | u32 dummy; |
76 | 78 | ||
77 | memset(csig, 0, sizeof(*csig)); | 79 | if (!supported_cpu) |
78 | if (c->x86_vendor != X86_VENDOR_AMD || c->x86 < 0x10) { | ||
79 | printk(KERN_WARNING "microcode: CPU%d: AMD CPU family 0x%x not " | ||
80 | "supported\n", cpu, c->x86); | ||
81 | return -1; | 80 | return -1; |
82 | } | 81 | |
82 | memset(csig, 0, sizeof(*csig)); | ||
83 | rdmsr(MSR_AMD64_PATCH_LEVEL, csig->rev, dummy); | 83 | rdmsr(MSR_AMD64_PATCH_LEVEL, csig->rev, dummy); |
84 | printk(KERN_INFO "microcode: CPU%d: patch_level=0x%x\n", cpu, csig->rev); | 84 | pr_info("microcode: CPU%d: patch_level=0x%x\n", cpu, csig->rev); |
85 | return 0; | 85 | return 0; |
86 | } | 86 | } |
87 | 87 | ||
@@ -103,22 +103,15 @@ static int get_matching_microcode(int cpu, void *mc, int rev) | |||
103 | i++; | 103 | i++; |
104 | } | 104 | } |
105 | 105 | ||
106 | if (!equiv_cpu_id) { | 106 | if (!equiv_cpu_id) |
107 | printk(KERN_WARNING "microcode: CPU%d: cpu revision " | ||
108 | "not listed in equivalent cpu table\n", cpu); | ||
109 | return 0; | 107 | return 0; |
110 | } | ||
111 | 108 | ||
112 | if (mc_header->processor_rev_id != equiv_cpu_id) { | 109 | if (mc_header->processor_rev_id != equiv_cpu_id) |
113 | printk(KERN_ERR "microcode: CPU%d: patch mismatch " | ||
114 | "(processor_rev_id: %x, equiv_cpu_id: %x)\n", | ||
115 | cpu, mc_header->processor_rev_id, equiv_cpu_id); | ||
116 | return 0; | 110 | return 0; |
117 | } | ||
118 | 111 | ||
119 | /* ucode might be chipset specific -- currently we don't support this */ | 112 | /* ucode might be chipset specific -- currently we don't support this */ |
120 | if (mc_header->nb_dev_id || mc_header->sb_dev_id) { | 113 | if (mc_header->nb_dev_id || mc_header->sb_dev_id) { |
121 | printk(KERN_ERR "microcode: CPU%d: loading of chipset " | 114 | pr_err(KERN_ERR "microcode: CPU%d: loading of chipset " |
122 | "specific code not yet supported\n", cpu); | 115 | "specific code not yet supported\n", cpu); |
123 | return 0; | 116 | return 0; |
124 | } | 117 | } |
@@ -148,14 +141,12 @@ static int apply_microcode_amd(int cpu) | |||
148 | 141 | ||
149 | /* check current patch id and patch's id for match */ | 142 | /* check current patch id and patch's id for match */ |
150 | if (rev != mc_amd->hdr.patch_id) { | 143 | if (rev != mc_amd->hdr.patch_id) { |
151 | printk(KERN_ERR "microcode: CPU%d: update failed " | 144 | pr_err("microcode: CPU%d: update failed " |
152 | "(for patch_level=0x%x)\n", cpu, mc_amd->hdr.patch_id); | 145 | "(for patch_level=0x%x)\n", cpu, mc_amd->hdr.patch_id); |
153 | return -1; | 146 | return -1; |
154 | } | 147 | } |
155 | 148 | ||
156 | printk(KERN_INFO "microcode: CPU%d: updated (new patch_level=0x%x)\n", | 149 | pr_info("microcode: CPU%d: updated (new patch_level=0x%x)\n", cpu, rev); |
157 | cpu, rev); | ||
158 | |||
159 | uci->cpu_sig.rev = rev; | 150 | uci->cpu_sig.rev = rev; |
160 | 151 | ||
161 | return 0; | 152 | return 0; |
@@ -178,18 +169,15 @@ get_next_ucode(const u8 *buf, unsigned int size, unsigned int *mc_size) | |||
178 | return NULL; | 169 | return NULL; |
179 | 170 | ||
180 | if (section_hdr[0] != UCODE_UCODE_TYPE) { | 171 | if (section_hdr[0] != UCODE_UCODE_TYPE) { |
181 | printk(KERN_ERR "microcode: error: invalid type field in " | 172 | pr_err("microcode: error: invalid type field in " |
182 | "container file section header\n"); | 173 | "container file section header\n"); |
183 | return NULL; | 174 | return NULL; |
184 | } | 175 | } |
185 | 176 | ||
186 | total_size = (unsigned long) (section_hdr[4] + (section_hdr[5] << 8)); | 177 | total_size = (unsigned long) (section_hdr[4] + (section_hdr[5] << 8)); |
187 | 178 | ||
188 | printk(KERN_DEBUG "microcode: size %u, total_size %u\n", | ||
189 | size, total_size); | ||
190 | |||
191 | if (total_size > size || total_size > UCODE_MAX_SIZE) { | 179 | if (total_size > size || total_size > UCODE_MAX_SIZE) { |
192 | printk(KERN_ERR "microcode: error: size mismatch\n"); | 180 | pr_err("microcode: error: size mismatch\n"); |
193 | return NULL; | 181 | return NULL; |
194 | } | 182 | } |
195 | 183 | ||
@@ -218,15 +206,14 @@ static int install_equiv_cpu_table(const u8 *buf) | |||
218 | size = buf_pos[2]; | 206 | size = buf_pos[2]; |
219 | 207 | ||
220 | if (buf_pos[1] != UCODE_EQUIV_CPU_TABLE_TYPE || !size) { | 208 | if (buf_pos[1] != UCODE_EQUIV_CPU_TABLE_TYPE || !size) { |
221 | printk(KERN_ERR "microcode: error: invalid type field in " | 209 | pr_err("microcode: error: invalid type field in " |
222 | "container file section header\n"); | 210 | "container file section header\n"); |
223 | return 0; | 211 | return 0; |
224 | } | 212 | } |
225 | 213 | ||
226 | equiv_cpu_table = (struct equiv_cpu_entry *) vmalloc(size); | 214 | equiv_cpu_table = (struct equiv_cpu_entry *) vmalloc(size); |
227 | if (!equiv_cpu_table) { | 215 | if (!equiv_cpu_table) { |
228 | printk(KERN_ERR "microcode: failed to allocate " | 216 | pr_err("microcode: failed to allocate equivalent CPU table\n"); |
229 | "equivalent CPU table\n"); | ||
230 | return 0; | 217 | return 0; |
231 | } | 218 | } |
232 | 219 | ||
@@ -259,8 +246,7 @@ generic_load_microcode(int cpu, const u8 *data, size_t size) | |||
259 | 246 | ||
260 | offset = install_equiv_cpu_table(ucode_ptr); | 247 | offset = install_equiv_cpu_table(ucode_ptr); |
261 | if (!offset) { | 248 | if (!offset) { |
262 | printk(KERN_ERR "microcode: failed to create " | 249 | pr_err("microcode: failed to create equivalent cpu table\n"); |
263 | "equivalent cpu table\n"); | ||
264 | return UCODE_ERROR; | 250 | return UCODE_ERROR; |
265 | } | 251 | } |
266 | 252 | ||
@@ -308,33 +294,27 @@ generic_load_microcode(int cpu, const u8 *data, size_t size) | |||
308 | 294 | ||
309 | static enum ucode_state request_microcode_fw(int cpu, struct device *device) | 295 | static enum ucode_state request_microcode_fw(int cpu, struct device *device) |
310 | { | 296 | { |
311 | const char *fw_name = "amd-ucode/microcode_amd.bin"; | ||
312 | const struct firmware *firmware; | ||
313 | enum ucode_state ret; | 297 | enum ucode_state ret; |
314 | 298 | ||
315 | if (request_firmware(&firmware, fw_name, device)) { | 299 | if (firmware == NULL) |
316 | printk(KERN_ERR "microcode: failed to load file %s\n", fw_name); | ||
317 | return UCODE_NFOUND; | 300 | return UCODE_NFOUND; |
318 | } | ||
319 | 301 | ||
320 | if (*(u32 *)firmware->data != UCODE_MAGIC) { | 302 | if (*(u32 *)firmware->data != UCODE_MAGIC) { |
321 | printk(KERN_ERR "microcode: invalid UCODE_MAGIC (0x%08x)\n", | 303 | pr_err("microcode: invalid UCODE_MAGIC (0x%08x)\n", |
322 | *(u32 *)firmware->data); | 304 | *(u32 *)firmware->data); |
323 | return UCODE_ERROR; | 305 | return UCODE_ERROR; |
324 | } | 306 | } |
325 | 307 | ||
326 | ret = generic_load_microcode(cpu, firmware->data, firmware->size); | 308 | ret = generic_load_microcode(cpu, firmware->data, firmware->size); |
327 | 309 | ||
328 | release_firmware(firmware); | ||
329 | |||
330 | return ret; | 310 | return ret; |
331 | } | 311 | } |
332 | 312 | ||
333 | static enum ucode_state | 313 | static enum ucode_state |
334 | request_microcode_user(int cpu, const void __user *buf, size_t size) | 314 | request_microcode_user(int cpu, const void __user *buf, size_t size) |
335 | { | 315 | { |
336 | printk(KERN_INFO "microcode: AMD microcode update via " | 316 | pr_info("microcode: AMD microcode update via " |
337 | "/dev/cpu/microcode not supported\n"); | 317 | "/dev/cpu/microcode not supported\n"); |
338 | return UCODE_ERROR; | 318 | return UCODE_ERROR; |
339 | } | 319 | } |
340 | 320 | ||
@@ -346,7 +326,32 @@ static void microcode_fini_cpu_amd(int cpu) | |||
346 | uci->mc = NULL; | 326 | uci->mc = NULL; |
347 | } | 327 | } |
348 | 328 | ||
329 | void init_microcode_amd(struct device *device) | ||
330 | { | ||
331 | const char *fw_name = "amd-ucode/microcode_amd.bin"; | ||
332 | struct cpuinfo_x86 *c = &boot_cpu_data; | ||
333 | |||
334 | WARN_ON(c->x86_vendor != X86_VENDOR_AMD); | ||
335 | |||
336 | if (c->x86 < 0x10) { | ||
337 | pr_warning("microcode: AMD CPU family 0x%x not supported\n", | ||
338 | c->x86); | ||
339 | return; | ||
340 | } | ||
341 | supported_cpu = 1; | ||
342 | |||
343 | if (request_firmware(&firmware, fw_name, device)) | ||
344 | pr_err("microcode: failed to load file %s\n", fw_name); | ||
345 | } | ||
346 | |||
347 | void fini_microcode_amd(void) | ||
348 | { | ||
349 | release_firmware(firmware); | ||
350 | } | ||
351 | |||
349 | static struct microcode_ops microcode_amd_ops = { | 352 | static struct microcode_ops microcode_amd_ops = { |
353 | .init = init_microcode_amd, | ||
354 | .fini = fini_microcode_amd, | ||
350 | .request_microcode_user = request_microcode_user, | 355 | .request_microcode_user = request_microcode_user, |
351 | .request_microcode_fw = request_microcode_fw, | 356 | .request_microcode_fw = request_microcode_fw, |
352 | .collect_cpu_info = collect_cpu_info_amd, | 357 | .collect_cpu_info = collect_cpu_info_amd, |
diff --git a/arch/x86/kernel/microcode_core.c b/arch/x86/kernel/microcode_core.c index 2bcad3926edb..e68aae397869 100644 --- a/arch/x86/kernel/microcode_core.c +++ b/arch/x86/kernel/microcode_core.c | |||
@@ -391,7 +391,7 @@ static enum ucode_state microcode_update_cpu(int cpu) | |||
391 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | 391 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; |
392 | enum ucode_state ustate; | 392 | enum ucode_state ustate; |
393 | 393 | ||
394 | if (uci->valid) | 394 | if (uci->valid && uci->mc) |
395 | ustate = microcode_resume_cpu(cpu); | 395 | ustate = microcode_resume_cpu(cpu); |
396 | else | 396 | else |
397 | ustate = microcode_init_cpu(cpu); | 397 | ustate = microcode_init_cpu(cpu); |
@@ -518,6 +518,9 @@ static int __init microcode_init(void) | |||
518 | return PTR_ERR(microcode_pdev); | 518 | return PTR_ERR(microcode_pdev); |
519 | } | 519 | } |
520 | 520 | ||
521 | if (microcode_ops->init) | ||
522 | microcode_ops->init(µcode_pdev->dev); | ||
523 | |||
521 | get_online_cpus(); | 524 | get_online_cpus(); |
522 | mutex_lock(µcode_mutex); | 525 | mutex_lock(µcode_mutex); |
523 | 526 | ||
@@ -561,6 +564,9 @@ static void __exit microcode_exit(void) | |||
561 | 564 | ||
562 | platform_device_unregister(microcode_pdev); | 565 | platform_device_unregister(microcode_pdev); |
563 | 566 | ||
567 | if (microcode_ops->fini) | ||
568 | microcode_ops->fini(); | ||
569 | |||
564 | microcode_ops = NULL; | 570 | microcode_ops = NULL; |
565 | 571 | ||
566 | pr_info("Microcode Update Driver: v" MICROCODE_VERSION " removed.\n"); | 572 | pr_info("Microcode Update Driver: v" MICROCODE_VERSION " removed.\n"); |