Skip to content

Commit

Permalink
x86/microcode/AMD: Load only SHA256-checksummed patches
Browse files Browse the repository at this point in the history
Load patches for which the driver carries a SHA256 checksum of the patch
blob.

This can be disabled by adding "microcode.amd_sha_check=off" on the
kernel cmdline. But it is highly NOT recommended.

Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
  • Loading branch information
Borislav Petkov (AMD) committed Feb 22, 2025
1 parent 037e81f commit 50cef76
Show file tree
Hide file tree
Showing 3 changed files with 554 additions and 2 deletions.
1 change: 1 addition & 0 deletions arch/x86/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1341,6 +1341,7 @@ config X86_REBOOTFIXUPS
config MICROCODE
def_bool y
depends on CPU_SUP_AMD || CPU_SUP_INTEL
select CRYPTO_LIB_SHA256 if CPU_SUP_AMD

config MICROCODE_INITRD32
def_bool y
Expand Down
111 changes: 109 additions & 2 deletions arch/x86/kernel/cpu/microcode/amd.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,18 @@

#include <linux/earlycpio.h>
#include <linux/firmware.h>
#include <linux/bsearch.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include <linux/initrd.h>
#include <linux/kernel.h>
#include <linux/pci.h>

#include <crypto/sha2.h>

#include <asm/microcode.h>
#include <asm/processor.h>
#include <asm/cmdline.h>
#include <asm/setup.h>
#include <asm/cpu.h>
#include <asm/msr.h>
Expand Down Expand Up @@ -145,6 +149,98 @@ ucode_path[] __maybe_unused = "kernel/x86/microcode/AuthenticAMD.bin";
*/
static u32 bsp_cpuid_1_eax __ro_after_init;

static bool sha_check = true;

struct patch_digest {
u32 patch_id;
u8 sha256[SHA256_DIGEST_SIZE];
};

#include "amd_shas.c"

static int cmp_id(const void *key, const void *elem)
{
struct patch_digest *pd = (struct patch_digest *)elem;
u32 patch_id = *(u32 *)key;

if (patch_id == pd->patch_id)
return 0;
else if (patch_id < pd->patch_id)
return -1;
else
return 1;
}

static bool need_sha_check(u32 cur_rev)
{
switch (cur_rev >> 8) {
case 0x80012: return cur_rev <= 0x800126f; break;
case 0x83010: return cur_rev <= 0x830107c; break;
case 0x86001: return cur_rev <= 0x860010e; break;
case 0x86081: return cur_rev <= 0x8608108; break;
case 0x87010: return cur_rev <= 0x8701034; break;
case 0x8a000: return cur_rev <= 0x8a0000a; break;
case 0xa0011: return cur_rev <= 0xa0011da; break;
case 0xa0012: return cur_rev <= 0xa001243; break;
case 0xa1011: return cur_rev <= 0xa101153; break;
case 0xa1012: return cur_rev <= 0xa10124e; break;
case 0xa1081: return cur_rev <= 0xa108109; break;
case 0xa2010: return cur_rev <= 0xa20102f; break;
case 0xa2012: return cur_rev <= 0xa201212; break;
case 0xa6012: return cur_rev <= 0xa60120a; break;
case 0xa7041: return cur_rev <= 0xa704109; break;
case 0xa7052: return cur_rev <= 0xa705208; break;
case 0xa7080: return cur_rev <= 0xa708009; break;
case 0xa70c0: return cur_rev <= 0xa70C009; break;
case 0xaa002: return cur_rev <= 0xaa00218; break;
default: break;
}

pr_info("You should not be seeing this. Please send the following couple of lines to x86-<at>-kernel.org\n");
pr_info("CPUID(1).EAX: 0x%x, current revision: 0x%x\n", bsp_cpuid_1_eax, cur_rev);
return true;
}

static bool verify_sha256_digest(u32 patch_id, u32 cur_rev, const u8 *data, unsigned int len)
{
struct patch_digest *pd = NULL;
u8 digest[SHA256_DIGEST_SIZE];
struct sha256_state s;
int i;

if (x86_family(bsp_cpuid_1_eax) < 0x17 ||
x86_family(bsp_cpuid_1_eax) > 0x19)
return true;

if (!need_sha_check(cur_rev))
return true;

if (!sha_check)
return true;

pd = bsearch(&patch_id, phashes, ARRAY_SIZE(phashes), sizeof(struct patch_digest), cmp_id);
if (!pd) {
pr_err("No sha256 digest for patch ID: 0x%x found\n", patch_id);
return false;
}

sha256_init(&s);
sha256_update(&s, data, len);
sha256_final(&s, digest);

if (memcmp(digest, pd->sha256, sizeof(digest))) {
pr_err("Patch 0x%x SHA256 digest mismatch!\n", patch_id);

for (i = 0; i < SHA256_DIGEST_SIZE; i++)
pr_cont("0x%x ", digest[i]);
pr_info("\n");

return false;
}

return true;
}

static u32 get_patch_level(void)
{
u32 rev, dummy __always_unused;
Expand Down Expand Up @@ -497,6 +593,9 @@ static bool __apply_microcode_amd(struct microcode_amd *mc, u32 *cur_rev,
{
unsigned long p_addr = (unsigned long)&mc->hdr.data_code;

if (!verify_sha256_digest(mc->hdr.patch_id, *cur_rev, (const u8 *)p_addr, psize))
return -1;

native_wrmsrl(MSR_AMD64_PATCH_LOADER, p_addr);

if (x86_family(bsp_cpuid_1_eax) == 0x17) {
Expand Down Expand Up @@ -571,8 +670,17 @@ void __init load_ucode_amd_bsp(struct early_load_data *ed, unsigned int cpuid_1_
struct cont_desc desc = { };
struct microcode_amd *mc;
struct cpio_data cp = { };
char buf[4];
u32 rev;

if (cmdline_find_option(boot_command_line, "microcode.amd_sha_check", buf, 4)) {
if (!strncmp(buf, "off", 3)) {
sha_check = false;
pr_warn_once("It is a very very bad idea to disable the blobs SHA check!\n");
add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK);
}
}

bsp_cpuid_1_eax = cpuid_1_eax;

rev = get_patch_level();
Expand Down Expand Up @@ -902,8 +1010,7 @@ static int verify_and_add_patch(u8 family, u8 *fw, unsigned int leftover,
}

/* Scan the blob in @data and add microcode patches to the cache. */
static enum ucode_state __load_microcode_amd(u8 family, const u8 *data,
size_t size)
static enum ucode_state __load_microcode_amd(u8 family, const u8 *data, size_t size)
{
u8 *fw = (u8 *)data;
size_t offset;
Expand Down
Loading

0 comments on commit 50cef76

Please sign in to comment.