Commit 77f9b1fb6244878ab4aab342a814880d17253b75

Authored by Simon Glass
1 parent 94060ff278

x86: ivybridge: Perform Intel microcode update on boot

Microcode updates are stored in the device tree. Work through these and
apply any that are needed.

Signed-off-by: Simon Glass <sjg@chromium.org>

Showing 6 changed files with 179 additions and 0 deletions Side-by-side Diff

arch/x86/cpu/ivybridge/Makefile
... ... @@ -7,6 +7,7 @@
7 7 obj-y += car.o
8 8 obj-y += cpu.o
9 9 obj-y += lpc.o
  10 +obj-y += microcode_intel.o
10 11 obj-y += pci.o
11 12 obj-y += sdram.o
arch/x86/cpu/ivybridge/cpu.c
... ... @@ -21,6 +21,7 @@
21 21 #include <asm/post.h>
22 22 #include <asm/processor.h>
23 23 #include <asm/arch/model_206ax.h>
  24 +#include <asm/arch/microcode.h>
24 25 #include <asm/arch/pch.h>
25 26  
26 27 DECLARE_GLOBAL_DATA_PTR;
... ... @@ -198,6 +199,10 @@
198 199 /* Halt if there was a built in self test failure */
199 200 ret = report_bist_failure();
200 201 if (ret)
  202 + return ret;
  203 +
  204 + ret = microcode_update_intel();
  205 + if (ret && ret != -ENOENT && ret != -EEXIST)
201 206 return ret;
202 207  
203 208 /* Print processor name */
arch/x86/cpu/ivybridge/microcode_intel.c
  1 +/*
  2 + * Copyright (c) 2014 Google, Inc
  3 + * Copyright (C) 2000 Ronald G. Minnich
  4 + *
  5 + * Microcode update for Intel PIII and later CPUs
  6 + *
  7 + * SPDX-License-Identifier: GPL-2.0
  8 + */
  9 +
  10 +#include <common.h>
  11 +#include <errno.h>
  12 +#include <fdtdec.h>
  13 +#include <libfdt.h>
  14 +#include <asm/cpu.h>
  15 +#include <asm/msr.h>
  16 +#include <asm/processor.h>
  17 +
  18 +/**
  19 + * struct microcode_update - standard microcode header from Intel
  20 + *
  21 + * We read this information out of the device tree and use it to determine
  22 + * whether the update is applicable or not. We also use the same structure
  23 + * to read information from the CPU.
  24 + */
  25 +struct microcode_update {
  26 + uint header_version;
  27 + uint update_revision;
  28 + uint date_code;
  29 + uint processor_signature;
  30 + uint checksum;
  31 + uint loader_revision;
  32 + uint processor_flags;
  33 + const void *data;
  34 + int size;
  35 +};
  36 +
  37 +static int microcode_decode_node(const void *blob, int node,
  38 + struct microcode_update *update)
  39 +{
  40 + update->data = fdt_getprop(blob, node, "data", &update->size);
  41 + if (!update->data)
  42 + return -EINVAL;
  43 +
  44 + update->header_version = fdtdec_get_int(blob, node,
  45 + "intel,header-version", 0);
  46 + update->update_revision = fdtdec_get_int(blob, node,
  47 + "intel,update-revision", 0);
  48 + update->date_code = fdtdec_get_int(blob, node,
  49 + "intel,date-code", 0);
  50 + update->processor_signature = fdtdec_get_int(blob, node,
  51 + "intel.processor-signature", 0);
  52 + update->checksum = fdtdec_get_int(blob, node, "intel,checksum", 0);
  53 + update->loader_revision = fdtdec_get_int(blob, node,
  54 + "loader-revision", 0);
  55 + update->processor_flags = fdtdec_get_int(blob, node,
  56 + "processor-flags", 0);
  57 +
  58 + return 0;
  59 +}
  60 +
  61 +static uint32_t microcode_read_rev(void)
  62 +{
  63 + /*
  64 + * Some Intel CPUs can be very finicky about the CPUID sequence used.
  65 + * So this is implemented in assembly so that it works reliably.
  66 + */
  67 + uint32_t low, high;
  68 +
  69 + asm volatile (
  70 + "xorl %%eax, %%eax\n"
  71 + "xorl %%edx, %%edx\n"
  72 + "movl $0x8b, %%ecx\n"
  73 + "wrmsr\n"
  74 + "movl $0x01, %%eax\n"
  75 + "cpuid\n"
  76 + "movl $0x8b, %%ecx\n"
  77 + "rdmsr\n"
  78 + : /* outputs */
  79 + "=a" (low), "=d" (high)
  80 + : /* inputs */
  81 + : /* clobbers */
  82 + "ebx", "ecx"
  83 + );
  84 +
  85 + return high;
  86 +}
  87 +
  88 +static void microcode_read_cpu(struct microcode_update *cpu)
  89 +{
  90 + /* CPUID sets MSR 0x8B iff a microcode update has been loaded. */
  91 + unsigned int x86_model, x86_family;
  92 + struct cpuid_result result;
  93 + uint32_t low, high;
  94 +
  95 + wrmsr(0x8b, 0, 0);
  96 + result = cpuid(1);
  97 + rdmsr(0x8b, low, cpu->update_revision);
  98 + x86_model = (result.eax >> 4) & 0x0f;
  99 + x86_family = (result.eax >> 8) & 0x0f;
  100 + cpu->processor_signature = result.eax;
  101 +
  102 + cpu->processor_flags = 0;
  103 + if ((x86_model >= 5) || (x86_family > 6)) {
  104 + rdmsr(0x17, low, high);
  105 + cpu->processor_flags = 1 << ((high >> 18) & 7);
  106 + }
  107 + debug("microcode: sig=%#x pf=%#x revision=%#x\n",
  108 + cpu->processor_signature, cpu->processor_flags,
  109 + cpu->update_revision);
  110 +}
  111 +
  112 +/* Get a microcode update from the device tree and apply it */
  113 +int microcode_update_intel(void)
  114 +{
  115 + struct microcode_update cpu, update;
  116 + const void *blob = gd->fdt_blob;
  117 + int count;
  118 + int node;
  119 + int ret;
  120 +
  121 + microcode_read_cpu(&cpu);
  122 + node = 0;
  123 + count = 0;
  124 + do {
  125 + node = fdtdec_next_compatible(blob, node,
  126 + COMPAT_INTEL_MICROCODE);
  127 + if (node < 0) {
  128 + debug("%s: Found %d updates\n", __func__, count);
  129 + return count ? 0 : -ENOENT;
  130 + }
  131 +
  132 + ret = microcode_decode_node(blob, node, &update);
  133 + if (ret) {
  134 + debug("%s: Unable to decode update: %d\n", __func__,
  135 + ret);
  136 + return ret;
  137 + }
  138 + if (update.processor_signature == cpu.processor_signature &&
  139 + (update.processor_flags & cpu.processor_flags)) {
  140 + debug("%s: Update already exists\n", __func__);
  141 + return -EEXIST;
  142 + }
  143 +
  144 + wrmsr(0x79, (ulong)update.data, 0);
  145 + debug("microcode: updated to revision 0x%x date=%04x-%02x-%02x\n",
  146 + microcode_read_rev(), update.date_code & 0xffff,
  147 + (update.date_code >> 24) & 0xff,
  148 + (update.date_code >> 16) & 0xff);
  149 + count++;
  150 + } while (1);
  151 +}
arch/x86/include/asm/arch-ivybridge/microcode.h
  1 +/*
  2 + * Copyright (c) 2015 Google, Inc
  3 + *
  4 + * SPDX-License-Identifier: GPL-2.0+
  5 + */
  6 +
  7 +#ifndef __ASM_ARCH_MICROCODE_H
  8 +#define __ASM_ARCH_MICROCODE_H
  9 +
  10 +/**
  11 + * microcode_update_intel() - Apply microcode updates
  12 + *
  13 + * Applies any microcode updates in the device tree.
  14 + *
  15 + * @return 0 if OK, -EEXIST if the updates were already applied, -ENOENT if
  16 + * not updates were found, -EINVAL if an update was invalid
  17 + */
  18 +int microcode_update_intel(void);
  19 +
  20 +#endif
... ... @@ -118,6 +118,7 @@
118 118 COMPAT_SAMSUNG_EXYNOS_SYSMMU, /* Exynos sysmmu */
119 119 COMPAT_PARADE_PS8625, /* Parade PS8622 EDP->LVDS bridge */
120 120 COMPAT_INTEL_LPC, /* Intel Low Pin Count I/F */
  121 + COMPAT_INTEL_MICROCODE, /* Intel microcode update */
121 122  
122 123 COMPAT_COUNT,
123 124 };
... ... @@ -73,6 +73,7 @@
73 73 COMPAT(SAMSUNG_EXYNOS_SYSMMU, "samsung,sysmmu-v3.3"),
74 74 COMPAT(PARADE_PS8625, "parade,ps8625"),
75 75 COMPAT(COMPAT_INTEL_LPC, "intel,lpc"),
  76 + COMPAT(INTEL_MICROCODE, "intel,microcode"),
76 77 };
77 78  
78 79 const char *fdtdec_get_compatible(enum fdt_compat_id id)