/*
* AMD CPU Microcode Update Driver for Linux
*
* This driver allows to upgrade microcode on F10h AMD
* CPUs and later.
*
* Copyright (C) 2008-2011 Advanced Micro Devices Inc.
* 2013-2016 Borislav Petkov <bp@alien8.de>
*
* Author: Peter Oruba <peter.oruba@amd.com>
*
* Based on work by:
* Tigran Aivazian <tigran@aivazian.fsnet.co.uk>
*
* early loader:
* Copyright (C) 2013 Advanced Micro Devices, Inc.
*
* Author: Jacob Shin <jacob.shin@amd.com>
* Fixes: Borislav Petkov <bp@suse.de>
*
* Licensed under the terms of the GNU General Public
* License version 2. See file COPYING for details.
*/
#define pr_fmt(fmt) "microcode: " fmt
#include <linux/earlycpio.h>
#include <linux/firmware.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include <linux/initrd.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <asm/microcode_amd.h>
#include <asm/microcode.h>
#include <asm/processor.h>
#include <asm/setup.h>
#include <asm/cpu.h>
#include <asm/msr.h>
static struct equiv_cpu_entry *equiv_cpu_table;
/*
* This points to the current valid container of microcode patches which we will
* save from the initrd/builtin before jettisoning its contents.
*/
struct container {
u8 *data;
size_t size;
} cont;
static u32 ucode_new_rev;
static u8 amd_ucode_patch[PATCH_MAX_SIZE];
static u16 this_equiv_id;
/*
* Microcode patch container file is prepended to the initrd in cpio
* format. See Documentation/x86/early-microcode.txt
*/
static const char
ucode_path[] __maybe_unused = "kernel/x86/microcode/AuthenticAMD.bin";
static size_t compute_container_size(u8 *data, u32 total_size)
{
size_t size = 0;
u32 *header = (u32 *)data;
if (header[0] != UCODE_MAGIC ||
header[1] != UCODE_EQUIV_CPU_TABLE_TYPE || /* type */
header[2] == 0) /* size */
return size;
size = header[2] + CONTAINER_HDR_SZ;
total_size -= size;
data += size;
while (total_size) {
u16 patch_size;
header = (u32 *)data;
if (header[0] != UCODE_UCODE_TYPE)
break;
/*
* Sanity-check patch size.
*/
patch_size = header[1];
if (patch_size > PATCH_MAX_SIZE)
break;
size += patch_size + SECTION_HDR_SIZE;
data += patch_size + SECTION_HDR_SIZE;
total_size -= patch_size + SECTION_HDR_SIZE;
}
return size;
}
static inline u16 find_equiv_id(struct equiv_cpu_entry *equiv_cpu_table,
unsigned int sig)
{
int i = 0;
if (!equiv_cpu_table)
return 0;
while (equiv_cpu_table[i].installed_cpu != 0) {
if (sig == equiv_cpu_table[i].installed_cpu)
return equiv_cpu_table[i].equiv_cpu;
i++;
}
return 0;
}
/*
* This scans the ucode blob for the proper container as we can have multiple
* containers glued together. Returns the equivalence ID from the equivalence
* table or 0 if none found.
*/
static u16
find_proper_container(u8 *ucode, size_t size, struct container *ret_cont)
{
struct container ret = { NULL, 0 };
u32 eax, ebx, ecx, edx;
struct equiv_cpu_entry *eq;
int offset, left;
u16 eq_id = 0;
u32 *header;
u8 *data;
data = ucode;
left = size;
header = (u32 *)data;
/* find equiv cpu table */
if (header[0] != UCODE_MAGIC ||
header[1] != UCODE_EQUIV_CPU_TABLE_TYPE || /* type */
header[2] == 0) /* size */
return eq_id;
eax = 0x00000001;
ecx = 0;
native_cpuid(&eax, &ebx, &ecx, &edx);
while (left > 0) {
eq = (struct equiv_cpu_entry *)(data + CONTAINER_HDR_SZ);
ret.data = data;
/* Advance past the container header */
offset = header[2] + CONTAINER_HDR_SZ;
data += offset;
left -= offset;
eq_id = find_equiv_id(eq, eax);
if (eq_id) {
ret.size = compute_container_size(ret.data, left + offset);
/*
* truncate how much we need to iterate over in the
* ucode update loop below
*/
left = ret.size - offset;
*ret_cont = ret;
return eq_id;
}
/*
* support multiple container files appended together. if this
* one does not have a matching equivalent cpu entry, we fast
* forward to the next container file.
*/
while (left > 0) {
header = (u32 *<