mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-02 17:41:08 +00:00
83d290c56f
When U-Boot started using SPDX tags we were among the early adopters and there weren't a lot of other examples to borrow from. So we picked the area of the file that usually had a full license text and replaced it with an appropriate SPDX-License-Identifier: entry. Since then, the Linux Kernel has adopted SPDX tags and they place it as the very first line in a file (except where shebangs are used, then it's second line) and with slightly different comment styles than us. In part due to community overlap, in part due to better tag visibility and in part for other minor reasons, switch over to that style. This commit changes all instances where we have a single declared license in the tag as both the before and after are identical in tag contents. There's also a few places where I found we did not have a tag and have introduced one. Signed-off-by: Tom Rini <trini@konsulko.com>
182 lines
4.7 KiB
C
182 lines
4.7 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2014 Google, Inc
|
|
* Copyright (C) 2000 Ronald G. Minnich
|
|
*
|
|
* Microcode update for Intel PIII and later CPUs
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <errno.h>
|
|
#include <fdtdec.h>
|
|
#include <linux/libfdt.h>
|
|
#include <asm/cpu.h>
|
|
#include <asm/microcode.h>
|
|
#include <asm/msr.h>
|
|
#include <asm/msr-index.h>
|
|
#include <asm/processor.h>
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
/**
|
|
* struct microcode_update - standard microcode header from Intel
|
|
*
|
|
* We read this information out of the device tree and use it to determine
|
|
* whether the update is applicable or not. We also use the same structure
|
|
* to read information from the CPU.
|
|
*/
|
|
struct microcode_update {
|
|
uint header_version;
|
|
uint update_revision;
|
|
uint date_code;
|
|
uint processor_signature;
|
|
uint checksum;
|
|
uint loader_revision;
|
|
uint processor_flags;
|
|
const void *data;
|
|
int size;
|
|
};
|
|
|
|
static int microcode_decode_node(const void *blob, int node,
|
|
struct microcode_update *update)
|
|
{
|
|
update->data = fdt_getprop(blob, node, "data", &update->size);
|
|
if (!update->data)
|
|
return -ENOENT;
|
|
update->data += UCODE_HEADER_LEN;
|
|
update->size -= UCODE_HEADER_LEN;
|
|
|
|
update->header_version = fdtdec_get_int(blob, node,
|
|
"intel,header-version", 0);
|
|
update->update_revision = fdtdec_get_int(blob, node,
|
|
"intel,update-revision", 0);
|
|
update->date_code = fdtdec_get_int(blob, node,
|
|
"intel,date-code", 0);
|
|
update->processor_signature = fdtdec_get_int(blob, node,
|
|
"intel,processor-signature", 0);
|
|
update->checksum = fdtdec_get_int(blob, node, "intel,checksum", 0);
|
|
update->loader_revision = fdtdec_get_int(blob, node,
|
|
"intel,loader-revision", 0);
|
|
update->processor_flags = fdtdec_get_int(blob, node,
|
|
"intel,processor-flags", 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int microcode_read_rev(void)
|
|
{
|
|
/* Quark does not have microcode MSRs */
|
|
#ifdef CONFIG_INTEL_QUARK
|
|
return 0;
|
|
#else
|
|
/*
|
|
* Some Intel CPUs can be very finicky about the CPUID sequence used.
|
|
* So this is implemented in assembly so that it works reliably.
|
|
*/
|
|
uint32_t low, high;
|
|
|
|
asm volatile (
|
|
"xorl %%eax, %%eax\n"
|
|
"xorl %%edx, %%edx\n"
|
|
"movl %2, %%ecx\n"
|
|
"wrmsr\n"
|
|
"movl $0x01, %%eax\n"
|
|
"cpuid\n"
|
|
"movl %2, %%ecx\n"
|
|
"rdmsr\n"
|
|
: /* outputs */
|
|
"=a" (low), "=d" (high)
|
|
: /* inputs */
|
|
"i" (MSR_IA32_UCODE_REV)
|
|
: /* clobbers */
|
|
"ebx", "ecx"
|
|
);
|
|
|
|
return high;
|
|
#endif
|
|
}
|
|
|
|
static void microcode_read_cpu(struct microcode_update *cpu)
|
|
{
|
|
/* CPUID sets MSR 0x8B iff a microcode update has been loaded. */
|
|
unsigned int x86_model, x86_family;
|
|
struct cpuid_result result;
|
|
uint32_t low, high;
|
|
|
|
wrmsr(MSR_IA32_UCODE_REV, 0, 0);
|
|
result = cpuid(1);
|
|
rdmsr(MSR_IA32_UCODE_REV, low, cpu->update_revision);
|
|
x86_model = (result.eax >> 4) & 0x0f;
|
|
x86_family = (result.eax >> 8) & 0x0f;
|
|
cpu->processor_signature = result.eax;
|
|
|
|
cpu->processor_flags = 0;
|
|
if ((x86_model >= 5) || (x86_family > 6)) {
|
|
rdmsr(0x17, low, high);
|
|
cpu->processor_flags = 1 << ((high >> 18) & 7);
|
|
}
|
|
debug("microcode: sig=%#x pf=%#x revision=%#x\n",
|
|
cpu->processor_signature, cpu->processor_flags,
|
|
cpu->update_revision);
|
|
}
|
|
|
|
/* Get a microcode update from the device tree and apply it */
|
|
int microcode_update_intel(void)
|
|
{
|
|
struct microcode_update cpu, update;
|
|
const void *blob = gd->fdt_blob;
|
|
int skipped;
|
|
int count;
|
|
int node;
|
|
int ret;
|
|
int rev;
|
|
|
|
microcode_read_cpu(&cpu);
|
|
node = 0;
|
|
count = 0;
|
|
skipped = 0;
|
|
do {
|
|
node = fdtdec_next_compatible(blob, node,
|
|
COMPAT_INTEL_MICROCODE);
|
|
if (node < 0) {
|
|
debug("%s: Found %d updates\n", __func__, count);
|
|
return count ? 0 : skipped ? -EEXIST : -ENOENT;
|
|
}
|
|
|
|
ret = microcode_decode_node(blob, node, &update);
|
|
if (ret == -ENOENT && ucode_base) {
|
|
/*
|
|
* The microcode has been removed from the device tree
|
|
* in the build system. In that case it will have
|
|
* already been updated in car_init().
|
|
*/
|
|
debug("%s: Microcode data not available\n", __func__);
|
|
skipped++;
|
|
continue;
|
|
}
|
|
if (ret) {
|
|
debug("%s: Unable to decode update: %d\n", __func__,
|
|
ret);
|
|
return ret;
|
|
}
|
|
if (!(update.processor_signature == cpu.processor_signature &&
|
|
(update.processor_flags & cpu.processor_flags))) {
|
|
debug("%s: Skipping non-matching update, sig=%x, pf=%x\n",
|
|
__func__, update.processor_signature,
|
|
update.processor_flags);
|
|
skipped++;
|
|
continue;
|
|
}
|
|
wrmsr(MSR_IA32_UCODE_WRITE, (ulong)update.data, 0);
|
|
rev = microcode_read_rev();
|
|
debug("microcode: updated to revision 0x%x date=%04x-%02x-%02x\n",
|
|
rev, update.date_code & 0xffff,
|
|
(update.date_code >> 24) & 0xff,
|
|
(update.date_code >> 16) & 0xff);
|
|
if (update.update_revision != rev) {
|
|
printf("Microcode update failed\n");
|
|
return -EFAULT;
|
|
}
|
|
count++;
|
|
} while (1);
|
|
}
|