Commit 80cc9f1020f49c9e5b50898c102fd444de70a0a3

Authored by Peter Oruba
Committed by Ingo Molnar
1 parent 8d86f390d9

x86: AMD microcode patch loading support

This patch introduces microcode patch loading for AMD
processors. It is based on previous corresponding work
for Intel processors.

It hooks into the general patch loading module. Main
difference is that a container file format is used to hold
all patch data for multiple processors as well as an
equivalent CPU table, which comes seperately, as opposed
to Intel's microcode patching solution.

Kconfig and Makefile have been changed provice config
and build option for new source file.

Signed-off-by: Peter Oruba <peter.oruba@amd.com>
Cc: Tigran Aivazian <tigran@aivazian.fsnet.co.uk>
Signed-off-by: Ingo Molnar <mingo@elte.hu>

Showing 3 changed files with 539 additions and 4 deletions Side-by-side Diff

... ... @@ -786,10 +786,12 @@
786 786 select FW_LOADER
787 787 ---help---
788 788 If you say Y here, you will be able to update the microcode on
789   - Intel processors in the IA32 family, e.g. Pentium Pro, Pentium II,
790   - Pentium III, Pentium 4, Xeon etc. You will obviously need the
791   - actual microcode binary data itself which is not shipped with the
792   - Linux kernel.
  789 + certain Intel and AMD processors. The Intel support is for the
  790 + IA32 family, e.g. Pentium Pro, Pentium II, Pentium III,
  791 + Pentium 4, Xeon etc. The AMD support is for family 0x10 and
  792 + 0x11 processors, e.g. Opteron, Phenom and Turion 64 Ultra.
  793 + You will obviously need the actual microcode binary data itself
  794 + which is not shipped with the Linux kernel.
793 795  
794 796 This option selects the general module only, you need to select
795 797 at least one vendor specific module as well.
... ... @@ -812,6 +814,17 @@
812 814  
813 815 This driver is only available as a module: the module
814 816 will be called microcode_intel.
  817 +
  818 +config MICROCODE_AMD
  819 + tristate "AMD microcode patch loading support"
  820 + depends on MICROCODE
  821 + select FW_LOADER
  822 + --help---
  823 + If you select this option, microcode patch loading support for AMD
  824 + processors will be enabled.
  825 +
  826 + This driver is only available as a module: the module
  827 + will be called microcode_amd.
815 828  
816 829 config MICROCODE_OLD_INTERFACE
817 830 def_bool y
arch/x86/kernel/Makefile
... ... @@ -53,6 +53,7 @@
53 53 obj-$(CONFIG_X86_CPUID) += cpuid.o
54 54 obj-$(CONFIG_MICROCODE) += microcode.o
55 55 obj-$(CONFIG_MICROCODE_INTEL) += microcode_intel.o
  56 +obj-$(CONFIG_MICROCODE_AMD) += microcode_amd.o
56 57 obj-$(CONFIG_PCI) += early-quirks.o
57 58 apm-y := apm_32.o
58 59 obj-$(CONFIG_APM) += apm.o
arch/x86/kernel/microcode_amd.c
  1 +/*
  2 + * AMD CPU Microcode Update Driver for Linux
  3 + * Copyright (C) 2008 Advanced Micro Devices Inc.
  4 + *
  5 + * Author: Peter Oruba <peter.oruba@amd.com>
  6 + *
  7 + * Based on work by:
  8 + * Tigran Aivazian <tigran@aivazian.fsnet.co.uk>
  9 + *
  10 + * This driver allows to upgrade microcode on AMD
  11 + * family 0x10 and 0x11 processors.
  12 + *
  13 + * Licensed unter the terms of the GNU General Public
  14 + * License version 2. See file COPYING for details.
  15 +*/
  16 +
  17 +#include <linux/capability.h>
  18 +#include <linux/kernel.h>
  19 +#include <linux/init.h>
  20 +#include <linux/sched.h>
  21 +#include <linux/cpumask.h>
  22 +#include <linux/module.h>
  23 +#include <linux/slab.h>
  24 +#include <linux/vmalloc.h>
  25 +#include <linux/miscdevice.h>
  26 +#include <linux/spinlock.h>
  27 +#include <linux/mm.h>
  28 +#include <linux/fs.h>
  29 +#include <linux/mutex.h>
  30 +#include <linux/cpu.h>
  31 +#include <linux/firmware.h>
  32 +#include <linux/platform_device.h>
  33 +#include <linux/pci.h>
  34 +#include <linux/pci_ids.h>
  35 +
  36 +#include <asm/msr.h>
  37 +#include <asm/uaccess.h>
  38 +#include <asm/processor.h>
  39 +#include <asm/microcode.h>
  40 +
  41 +MODULE_DESCRIPTION("AMD Microcode Update Driver");
  42 +MODULE_AUTHOR("Peter Oruba <peter.oruba@amd.com>");
  43 +MODULE_LICENSE("GPLv2");
  44 +
  45 +#define UCODE_MAGIC 0x00414d44
  46 +#define UCODE_EQUIV_CPU_TABLE_TYPE 0x00000000
  47 +#define UCODE_UCODE_TYPE 0x00000001
  48 +
  49 +#define UCODE_MAX_SIZE (2048)
  50 +#define DEFAULT_UCODE_DATASIZE (896) /* 896 bytes */
  51 +#define MC_HEADER_SIZE (sizeof(struct microcode_header_amd)) /* 64 bytes */
  52 +#define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE) /* 960 bytes */
  53 +#define DWSIZE (sizeof(u32))
  54 +/* For now we support a fixed ucode total size only */
  55 +#define get_totalsize(mc) \
  56 + ((((struct microcode_amd *)mc)->hdr.mc_patch_data_len * 28) \
  57 + + MC_HEADER_SIZE)
  58 +
  59 +extern int microcode_init(void *opaque, struct module *module);
  60 +extern void microcode_exit(void);
  61 +
  62 +/* serialize access to the physical write */
  63 +static DEFINE_SPINLOCK(microcode_update_lock);
  64 +
  65 +/* no concurrent ->write()s are allowed on /dev/cpu/microcode */
  66 +extern struct mutex (microcode_mutex);
  67 +
  68 +struct equiv_cpu_entry *equiv_cpu_table;
  69 +
  70 +extern struct ucode_cpu_info ucode_cpu_info[NR_CPUS];
  71 +
  72 +static void collect_cpu_info_amd(int cpu)
  73 +{
  74 + struct cpuinfo_x86 *c = &cpu_data(cpu);
  75 + struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
  76 +
  77 + /* We should bind the task to the CPU */
  78 + BUG_ON(raw_smp_processor_id() != cpu);
  79 + uci->rev = 0;
  80 + uci->pf = 0;
  81 + uci->mc.mc_amd = NULL;
  82 + uci->valid = 1;
  83 +
  84 + if (c->x86_vendor != X86_VENDOR_AMD || c->x86 < 0x10) {
  85 + printk(KERN_ERR "microcode: CPU%d not a capable AMD processor\n",
  86 + cpu);
  87 + uci->valid = 0;
  88 + return;
  89 + }
  90 +
  91 + asm volatile("movl %1, %%ecx; rdmsr"
  92 + : "=a" (uci->rev)
  93 + : "i" (0x0000008B) : "ecx");
  94 +
  95 + printk(KERN_INFO "microcode: collect_cpu_info_amd : patch_id=0x%x\n",
  96 + uci->rev);
  97 +}
  98 +
  99 +static int get_matching_microcode_amd(void *mc, int cpu)
  100 +{
  101 + struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
  102 + struct microcode_header_amd *mc_header = mc;
  103 + unsigned long total_size = get_totalsize(mc_header);
  104 + void *new_mc;
  105 + struct pci_dev *nb_pci_dev, *sb_pci_dev;
  106 + unsigned int current_cpu_id;
  107 + unsigned int equiv_cpu_id = 0x00;
  108 + unsigned int i = 0;
  109 +
  110 + /* We should bind the task to the CPU */
  111 + BUG_ON(cpu != raw_smp_processor_id());
  112 +
  113 + /* This is a tricky part. We might be called from a write operation */
  114 + /* to the device file instead of the usual process of firmware */
  115 + /* loading. This routine needs to be able to distinguish both */
  116 +/* cases. This is done by checking if there alread is a equivalent */
  117 + /* CPU table installed. If not, we're written through */
  118 + /* /dev/cpu/microcode. */
  119 +/* Since we ignore all checks. The error case in which going through */
  120 +/* firmware loading and that table is not loaded has already been */
  121 + /* checked earlier. */
  122 + if (equiv_cpu_table == NULL) {
  123 + printk(KERN_INFO "microcode: CPU%d microcode update with "
  124 + "version 0x%x (current=0x%x)\n",
  125 + cpu, mc_header->patch_id, uci->rev);
  126 + goto out;
  127 + }
  128 +
  129 + current_cpu_id = cpuid_eax(0x00000001);
  130 +
  131 + while (equiv_cpu_table[i].installed_cpu != 0) {
  132 + if (current_cpu_id == equiv_cpu_table[i].installed_cpu) {
  133 + equiv_cpu_id = equiv_cpu_table[i].equiv_cpu;
  134 + break;
  135 + }
  136 + i++;
  137 + }
  138 +
  139 + if (!equiv_cpu_id) {
  140 + printk(KERN_ERR "microcode: CPU%d cpu_id "
  141 + "not found in equivalent cpu table \n", cpu);
  142 + return 0;
  143 + }
  144 +
  145 + if ((mc_header->processor_rev_id[0]) != (equiv_cpu_id & 0xff)) {
  146 + printk(KERN_ERR
  147 + "microcode: CPU%d patch does not match "
  148 + "(patch is %x, cpu extended is %x) \n",
  149 + cpu, mc_header->processor_rev_id[0],
  150 + (equiv_cpu_id & 0xff));
  151 + return 0;
  152 + }
  153 +
  154 + if ((mc_header->processor_rev_id[1]) != ((equiv_cpu_id >> 16) & 0xff)) {
  155 + printk(KERN_ERR "microcode: CPU%d patch does not match "
  156 + "(patch is %x, cpu base id is %x) \n",
  157 + cpu, mc_header->processor_rev_id[1],
  158 + ((equiv_cpu_id >> 16) & 0xff));
  159 +
  160 + return 0;
  161 + }
  162 +
  163 + /* ucode may be northbridge specific */
  164 + if (mc_header->nb_dev_id) {
  165 + nb_pci_dev = pci_get_device(PCI_VENDOR_ID_AMD,
  166 + (mc_header->nb_dev_id & 0xff),
  167 + NULL);
  168 + if ((!nb_pci_dev) ||
  169 + (mc_header->nb_rev_id != nb_pci_dev->revision)) {
  170 + printk(KERN_ERR "microcode: CPU%d NB mismatch \n", cpu);
  171 + pci_dev_put(nb_pci_dev);
  172 + return 0;
  173 + }
  174 + pci_dev_put(nb_pci_dev);
  175 + }
  176 +
  177 + /* ucode may be southbridge specific */
  178 + if (mc_header->sb_dev_id) {
  179 + sb_pci_dev = pci_get_device(PCI_VENDOR_ID_AMD,
  180 + (mc_header->sb_dev_id & 0xff),
  181 + NULL);
  182 + if ((!sb_pci_dev) ||
  183 + (mc_header->sb_rev_id != sb_pci_dev->revision)) {
  184 + printk(KERN_ERR "microcode: CPU%d SB mismatch \n", cpu);
  185 + pci_dev_put(sb_pci_dev);
  186 + return 0;
  187 + }
  188 + pci_dev_put(sb_pci_dev);
  189 + }
  190 +
  191 + if (mc_header->patch_id <= uci->rev)
  192 + return 0;
  193 +
  194 + printk(KERN_INFO "microcode: CPU%d found a matching microcode "
  195 + "update with version 0x%x (current=0x%x)\n",
  196 + cpu, mc_header->patch_id, uci->rev);
  197 +
  198 +out:
  199 + new_mc = vmalloc(UCODE_MAX_SIZE);
  200 + if (!new_mc) {
  201 + printk(KERN_ERR "microcode: error, can't allocate memory\n");
  202 + return -ENOMEM;
  203 + }
  204 + memset(new_mc, 0, UCODE_MAX_SIZE);
  205 +
  206 + /* free previous update file */
  207 + vfree(uci->mc.mc_amd);
  208 +
  209 + memcpy(new_mc, mc, total_size);
  210 +
  211 + uci->mc.mc_amd = new_mc;
  212 + return 1;
  213 +}
  214 +
  215 +static void apply_microcode_amd(int cpu)
  216 +{
  217 + unsigned long flags;
  218 + unsigned int eax, edx;
  219 + unsigned int rev;
  220 + int cpu_num = raw_smp_processor_id();
  221 + struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num;
  222 +
  223 + /* We should bind the task to the CPU */
  224 + BUG_ON(cpu_num != cpu);
  225 +
  226 + if (uci->mc.mc_amd == NULL)
  227 + return;
  228 +
  229 + spin_lock_irqsave(&microcode_update_lock, flags);
  230 +
  231 + edx = (unsigned int)(((unsigned long)
  232 + &(uci->mc.mc_amd->hdr.data_code)) >> 32);
  233 + eax = (unsigned int)(((unsigned long)
  234 + &(uci->mc.mc_amd->hdr.data_code)) & 0xffffffffL);
  235 +
  236 + asm volatile("movl %0, %%ecx; wrmsr" :
  237 + : "i" (0xc0010020), "a" (eax), "d" (edx) : "ecx");
  238 +
  239 + /* get patch id after patching */
  240 + asm volatile("movl %1, %%ecx; rdmsr"
  241 + : "=a" (rev)
  242 + : "i" (0x0000008B) : "ecx");
  243 +
  244 + spin_unlock_irqrestore(&microcode_update_lock, flags);
  245 +
  246 + /* check current patch id and patch's id for match */
  247 + if (rev != uci->mc.mc_amd->hdr.patch_id) {
  248 + printk(KERN_ERR "microcode: CPU%d update from revision "
  249 + "0x%x to 0x%x failed\n", cpu_num,
  250 + uci->mc.mc_amd->hdr.patch_id, rev);
  251 + return;
  252 + }
  253 +
  254 + printk(KERN_INFO "microcode: CPU%d updated from revision "
  255 + "0x%x to 0x%x \n",
  256 + cpu_num, uci->rev, uci->mc.mc_amd->hdr.patch_id);
  257 +
  258 + uci->rev = rev;
  259 +}
  260 +
  261 +#ifdef CONFIG_MICROCODE_OLD_INTERFACE
  262 +extern void __user *user_buffer; /* user area microcode data buffer */
  263 +extern unsigned int user_buffer_size; /* it's size */
  264 +
  265 +static long get_next_ucode_amd(void **mc, long offset)
  266 +{
  267 + struct microcode_header_amd mc_header;
  268 + unsigned long total_size;
  269 +
  270 + /* No more data */
  271 + if (offset >= user_buffer_size)
  272 + return 0;
  273 + if (copy_from_user(&mc_header, user_buffer + offset, MC_HEADER_SIZE)) {
  274 + printk(KERN_ERR "microcode: error! Can not read user data\n");
  275 + return -EFAULT;
  276 + }
  277 + total_size = get_totalsize(&mc_header);
  278 + if (offset + total_size > user_buffer_size) {
  279 + printk(KERN_ERR "microcode: error! Bad total size in microcode "
  280 + "data file\n");
  281 + return -EINVAL;
  282 + }
  283 + *mc = vmalloc(UCODE_MAX_SIZE);
  284 + if (!*mc)
  285 + return -ENOMEM;
  286 + memset(*mc, 0, UCODE_MAX_SIZE);
  287 +
  288 + if (copy_from_user(*mc, user_buffer + offset, total_size)) {
  289 + printk(KERN_ERR "microcode: error! Can not read user data\n");
  290 + vfree(*mc);
  291 + return -EFAULT;
  292 + }
  293 + return offset + total_size;
  294 +}
  295 +#else
  296 +#define get_next_ucode_amd() NULL
  297 +#endif
  298 +
  299 +static long get_next_ucode_from_buffer_amd(void **mc, void *buf,
  300 + unsigned long size, long offset)
  301 +{
  302 + struct microcode_header_amd *mc_header;
  303 + unsigned long total_size;
  304 + unsigned char *buf_pos = buf;
  305 +
  306 + /* No more data */
  307 + if (offset >= size)
  308 + return 0;
  309 +
  310 + if (buf_pos[offset] != UCODE_UCODE_TYPE) {
  311 + printk(KERN_ERR "microcode: error! "
  312 + "Wrong microcode payload type field\n");
  313 + return -EINVAL;
  314 + }
  315 +
  316 + mc_header = (struct microcode_header_amd *)(&buf_pos[offset+8]);
  317 +
  318 + total_size = (unsigned long) (buf_pos[offset+4] +
  319 + (buf_pos[offset+5] << 8));
  320 +
  321 + printk(KERN_INFO "microcode: size %lu, total_size %lu, offset %ld\n",
  322 + size, total_size, offset);
  323 +
  324 + if (offset + total_size > size) {
  325 + printk(KERN_ERR "microcode: error! Bad data in microcode data file\n");
  326 + return -EINVAL;
  327 + }
  328 +
  329 + *mc = vmalloc(UCODE_MAX_SIZE);
  330 + if (!*mc) {
  331 + printk(KERN_ERR "microcode: error! "
  332 + "Can not allocate memory for microcode patch\n");
  333 + return -ENOMEM;
  334 + }
  335 +
  336 + memset(*mc, 0, UCODE_MAX_SIZE);
  337 + memcpy(*mc, buf + offset + 8, total_size);
  338 +
  339 + return offset + total_size + 8;
  340 +}
  341 +
  342 +static long install_equiv_cpu_table(void *buf, unsigned long size, long offset)
  343 +{
  344 + unsigned int *buf_pos = buf;
  345 +
  346 + /* No more data */
  347 + if (offset >= size)
  348 + return 0;
  349 +
  350 + if (buf_pos[1] != UCODE_EQUIV_CPU_TABLE_TYPE) {
  351 + printk(KERN_ERR "microcode: error! "
  352 + "Wrong microcode equivalnet cpu table type field\n");
  353 + return 0;
  354 + }
  355 +
  356 + if (size == 0) {
  357 + printk(KERN_ERR "microcode: error! "
  358 + "Wrong microcode equivalnet cpu table length\n");
  359 + return 0;
  360 + }
  361 +
  362 + equiv_cpu_table = (struct equiv_cpu_entry *) vmalloc(size);
  363 + if (!equiv_cpu_table) {
  364 + printk(KERN_ERR "microcode: error, can't allocate memory for equiv CPU table\n");
  365 + return 0;
  366 + }
  367 +
  368 + memset(equiv_cpu_table, 0, size);
  369 + memcpy(equiv_cpu_table, &buf_pos[3], size);
  370 +
  371 + return size + 12; /* add header length */
  372 +}
  373 +
  374 +/* fake device for request_firmware */
  375 +extern struct platform_device *microcode_pdev;
  376 +
  377 +static int cpu_request_microcode_amd(int cpu)
  378 +{
  379 + char name[30];
  380 + const struct firmware *firmware;
  381 + void *buf;
  382 + unsigned int *buf_pos;
  383 + unsigned long size;
  384 + long offset = 0;
  385 + int error;
  386 + void *mc;
  387 +
  388 + /* We should bind the task to the CPU */
  389 + BUG_ON(cpu != raw_smp_processor_id());
  390 +
  391 + sprintf(name, "amd-ucode/microcode_amd.bin");
  392 + error = request_firmware(&firmware, "amd-ucode/microcode_amd.bin",
  393 + &microcode_pdev->dev);
  394 + if (error) {
  395 + printk(KERN_ERR "microcode: ucode data file %s load failed\n",
  396 + name);
  397 + return error;
  398 + }
  399 +
  400 + buf_pos = buf = firmware->data;
  401 + size = firmware->size;
  402 +
  403 + if (buf_pos[0] != UCODE_MAGIC) {
  404 + printk(KERN_ERR "microcode: error! Wrong microcode patch file magic\n");
  405 + return -EINVAL;
  406 + }
  407 +
  408 + offset = install_equiv_cpu_table(buf, buf_pos[2], offset);
  409 +
  410 + if (!offset) {
  411 + printk(KERN_ERR "microcode: installing equivalent cpu table failed\n");
  412 + return -EINVAL;
  413 + }
  414 +
  415 + while ((offset =
  416 + get_next_ucode_from_buffer_amd(&mc, buf, size, offset)) > 0) {
  417 + error = get_matching_microcode_amd(mc, cpu);
  418 + if (error < 0)
  419 + break;
  420 + /*
  421 + * It's possible the data file has multiple matching ucode,
  422 + * lets keep searching till the latest version
  423 + */
  424 + if (error == 1) {
  425 + apply_microcode_amd(cpu);
  426 + error = 0;
  427 + }
  428 + vfree(mc);
  429 + }
  430 + if (offset > 0) {
  431 + vfree(mc);
  432 + vfree(equiv_cpu_table);
  433 + equiv_cpu_table = NULL;
  434 + }
  435 + if (offset < 0)
  436 + error = offset;
  437 + release_firmware(firmware);
  438 +
  439 + return error;
  440 +}
  441 +
  442 +static int apply_microcode_check_cpu_amd(int cpu)
  443 +{
  444 + struct cpuinfo_x86 *c = &cpu_data(cpu);
  445 + struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
  446 + unsigned int rev;
  447 + cpumask_t old;
  448 + cpumask_of_cpu_ptr(newmask, cpu);
  449 + int err = 0;
  450 +
  451 + /* Check if the microcode is available */
  452 + if (!uci->mc.mc_amd)
  453 + return 0;
  454 +
  455 + old = current->cpus_allowed;
  456 + set_cpus_allowed(current, newmask);
  457 +
  458 + /* Check if the microcode we have in memory matches the CPU */
  459 + if (c->x86_vendor != X86_VENDOR_AMD || c->x86 < 16)
  460 + err = -EINVAL;
  461 +
  462 + if (!err) {
  463 + asm volatile("movl %1, %%ecx; rdmsr"
  464 + : "=a" (rev)
  465 + : "i" (0x0000008B) : "ecx");
  466 +
  467 + if (uci->rev != rev)
  468 + err = -EINVAL;
  469 + }
  470 +
  471 + if (!err)
  472 + apply_microcode_amd(cpu);
  473 + else
  474 + printk(KERN_ERR "microcode: Could not apply microcode to CPU%d:"
  475 + " rev=0x%x\n",
  476 + cpu, uci->rev);
  477 +
  478 + set_cpus_allowed(current, old);
  479 + return err;
  480 +}
  481 +
  482 +static void microcode_fini_cpu_amd(int cpu)
  483 +{
  484 + struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
  485 +
  486 + mutex_lock(&microcode_mutex);
  487 + uci->valid = 0;
  488 + vfree(uci->mc.mc_amd);
  489 + uci->mc.mc_amd = NULL;
  490 + mutex_unlock(&microcode_mutex);
  491 +}
  492 +
  493 +static struct microcode_ops microcode_amd_ops = {
  494 + .get_next_ucode = get_next_ucode_amd,
  495 + .get_matching_microcode = get_matching_microcode_amd,
  496 + .microcode_sanity_check = NULL,
  497 + .apply_microcode_check_cpu = apply_microcode_check_cpu_amd,
  498 + .cpu_request_microcode = cpu_request_microcode_amd,
  499 + .collect_cpu_info = collect_cpu_info_amd,
  500 + .apply_microcode = apply_microcode_amd,
  501 + .microcode_fini_cpu = microcode_fini_cpu_amd,
  502 +};
  503 +
  504 +static int __init microcode_amd_module_init(void)
  505 +{
  506 + struct cpuinfo_x86 *c = &cpu_data(get_cpu());
  507 +
  508 + equiv_cpu_table = NULL;
  509 + if (c->x86_vendor == X86_VENDOR_AMD)
  510 + return microcode_init(&microcode_amd_ops, THIS_MODULE);
  511 + else
  512 + return -ENODEV;
  513 +}
  514 +
  515 +static void __exit microcode_amd_module_exit(void)
  516 +{
  517 + microcode_exit();
  518 +}
  519 +
  520 +module_init(microcode_amd_module_init)
  521 +module_exit(microcode_amd_module_exit)