mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
Family 0x1a covers both Zen5 and Zen6, but the latter is not believed to be vulnerable to entrysign. Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com> Reviewed-by: Jan Beulich <jbeulich@suse.com>
691 lines
21 KiB
C
691 lines
21 KiB
C
/*
|
|
* AMD CPU Microcode Update Driver for Linux
|
|
* Copyright (C) 2008 Advanced Micro Devices Inc.
|
|
*
|
|
* Author: Peter Oruba <peter.oruba@amd.com>
|
|
*
|
|
* Based on work by:
|
|
* Tigran Aivazian <tigran@aivazian.fsnet.co.uk>
|
|
*
|
|
* This driver allows to upgrade microcode on AMD
|
|
* family 0x10 and later.
|
|
*
|
|
* Licensed unter the terms of the GNU General Public
|
|
* License version 2. See file COPYING for details.
|
|
*/
|
|
|
|
#include <xen/bsearch.h>
|
|
#include <xen/err.h>
|
|
#include <xen/init.h>
|
|
#include <xen/mm.h> /* TODO: Fix asm/tlbflush.h breakage */
|
|
#include <xen/sha2.h>
|
|
|
|
#include <asm/amd.h>
|
|
#include <asm/msr.h>
|
|
|
|
#include "private.h"
|
|
|
|
#define pr_debug(x...) ((void)0)
|
|
|
|
struct equiv_cpu_entry {
|
|
uint32_t installed_cpu;
|
|
uint32_t fixed_errata_mask;
|
|
uint32_t fixed_errata_compare;
|
|
uint16_t equiv_cpu;
|
|
uint16_t reserved;
|
|
};
|
|
|
|
struct microcode_patch {
|
|
uint16_t year;
|
|
uint8_t day;
|
|
uint8_t month;
|
|
uint32_t patch_id;
|
|
uint8_t mc_patch_data_id[2];
|
|
uint8_t mc_patch_data_len;
|
|
uint8_t init_flag;
|
|
union {
|
|
uint32_t checksum; /* Fam12h and earlier */
|
|
uint32_t min_rev; /* Zen3-5, post Entrysign */
|
|
};
|
|
uint32_t nb_dev_id;
|
|
uint32_t sb_dev_id;
|
|
uint16_t processor_rev_id;
|
|
uint8_t nb_rev_id;
|
|
uint8_t sb_rev_id;
|
|
uint8_t bios_api_rev;
|
|
uint8_t reserved1[3];
|
|
};
|
|
|
|
#define UCODE_MAGIC 0x00414d44
|
|
#define UCODE_EQUIV_TYPE 0x00000000
|
|
#define UCODE_UCODE_TYPE 0x00000001
|
|
|
|
struct container_equiv_table {
|
|
uint32_t type; /* UCODE_EQUIV_TYPE */
|
|
uint32_t len;
|
|
struct equiv_cpu_entry eq[];
|
|
};
|
|
struct container_microcode {
|
|
uint32_t type; /* UCODE_UCODE_TYPE */
|
|
uint32_t len;
|
|
struct microcode_patch patch[];
|
|
};
|
|
|
|
/*
|
|
* Microcode updates for different CPUs are distinguished by their
|
|
* processor_rev_id in the header. This denotes the format of the internals
|
|
* of the microcode engine, and is fixed for an individual CPU.
|
|
*
|
|
* There is a mapping from the CPU signature (CPUID.1.EAX -
|
|
* family/model/stepping) to the "equivalent CPU identifier" which is
|
|
* similarly fixed. In some cases, multiple different CPU signatures map to
|
|
* the same equiv_id for processor lines which share identical microcode
|
|
* facilities.
|
|
*
|
|
* This mapping can't be calculated in the general case, but is provided in
|
|
* the microcode container, so the correct piece of microcode for the CPU can
|
|
* be identified. We cache it the first time we encounter the correct mapping
|
|
* for this system.
|
|
*
|
|
* Note: for now, we assume a fully homogeneous setup, meaning that there is
|
|
* exactly one equiv_id we need to worry about for microcode blob
|
|
* identification. This may need revisiting in due course.
|
|
*/
|
|
static struct {
|
|
uint32_t sig;
|
|
uint16_t id;
|
|
} equiv __read_mostly;
|
|
|
|
static const struct patch_digest {
|
|
uint32_t patch_id;
|
|
uint8_t digest[SHA2_256_DIGEST_SIZE];
|
|
} patch_digests[] = {
|
|
#include "amd-patch-digests.c"
|
|
};
|
|
static bool __ro_after_init entrysign_mitigated_in_firmware;
|
|
|
|
static int cf_check cmp_patch_id(const void *key, const void *elem)
|
|
{
|
|
const struct patch_digest *pd = elem;
|
|
uint32_t patch_id = *(const uint32_t *)key;
|
|
|
|
if ( patch_id == pd->patch_id )
|
|
return 0;
|
|
else if ( patch_id < pd->patch_id )
|
|
return -1;
|
|
return 1;
|
|
}
|
|
|
|
static bool check_digest(const struct container_microcode *mc)
|
|
{
|
|
const struct microcode_patch *patch = mc->patch;
|
|
const struct patch_digest *pd;
|
|
uint8_t digest[SHA2_256_DIGEST_SIZE];
|
|
|
|
/*
|
|
* Zen1 thru Zen5 CPUs are known to use a weak signature algorithm on
|
|
* microcode updates. If this has not been mitigated in firmware, check
|
|
* the digest of the patch against a list of known provenance.
|
|
*/
|
|
if ( boot_cpu_data.family < 0x17 || boot_cpu_data.family > 0x1a ||
|
|
entrysign_mitigated_in_firmware || !opt_digest_check )
|
|
return true;
|
|
|
|
pd = bsearch(&patch->patch_id, patch_digests, ARRAY_SIZE(patch_digests),
|
|
sizeof(struct patch_digest), cmp_patch_id);
|
|
if ( !pd )
|
|
{
|
|
printk(XENLOG_WARNING "No digest found for patch_id %08x\n",
|
|
patch->patch_id);
|
|
return false;
|
|
}
|
|
|
|
sha2_256(digest, patch, mc->len);
|
|
|
|
if ( memcmp(digest, pd->digest, sizeof(digest)) )
|
|
{
|
|
printk(XENLOG_WARNING "Patch %08x SHA256 mismatch:\n"
|
|
" expected %" STR(SHA2_256_DIGEST_SIZE) "phN\n"
|
|
" got %" STR(SHA2_256_DIGEST_SIZE) "phN\n",
|
|
patch->patch_id, pd->digest, digest);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void cf_check collect_cpu_info(void)
|
|
{
|
|
struct cpu_signature *csig = &this_cpu(cpu_sig);
|
|
|
|
memset(csig, 0, sizeof(*csig));
|
|
|
|
csig->sig = cpuid_eax(1);
|
|
rdmsrl(MSR_AMD_PATCHLEVEL, csig->rev);
|
|
|
|
pr_debug("microcode: CPU%d collect_cpu_info: patch_id=%#x\n",
|
|
smp_processor_id(), csig->rev);
|
|
}
|
|
|
|
static bool verify_patch_size(uint32_t patch_size)
|
|
{
|
|
uint32_t max_size;
|
|
|
|
#define F1XH_MPB_MAX_SIZE 2048
|
|
#define F14H_MPB_MAX_SIZE 1824
|
|
#define F15H_MPB_MAX_SIZE 4096
|
|
#define F16H_MPB_MAX_SIZE 3458
|
|
#define F17H_MPB_MAX_SIZE 3200
|
|
#define F19H_MPB_MAX_SIZE 5568
|
|
#define F1AH_MPB_MAX_SIZE 15296
|
|
|
|
switch ( boot_cpu_data.family )
|
|
{
|
|
case 0x14:
|
|
max_size = F14H_MPB_MAX_SIZE;
|
|
break;
|
|
case 0x15:
|
|
max_size = F15H_MPB_MAX_SIZE;
|
|
break;
|
|
case 0x16:
|
|
max_size = F16H_MPB_MAX_SIZE;
|
|
break;
|
|
case 0x17:
|
|
max_size = F17H_MPB_MAX_SIZE;
|
|
break;
|
|
case 0x19:
|
|
max_size = F19H_MPB_MAX_SIZE;
|
|
break;
|
|
case 0x1a:
|
|
max_size = F1AH_MPB_MAX_SIZE;
|
|
break;
|
|
default:
|
|
max_size = F1XH_MPB_MAX_SIZE;
|
|
break;
|
|
}
|
|
|
|
return patch_size <= max_size;
|
|
}
|
|
|
|
static bool check_final_patch_levels(const struct cpu_signature *sig)
|
|
{
|
|
/*
|
|
* The 'final_levels' of patch ids have been obtained empirically.
|
|
* Refer bug https://bugzilla.suse.com/show_bug.cgi?id=913996
|
|
* for details of the issue. The short version is that people
|
|
* using certain Fam10h systems noticed system hang issues when
|
|
* trying to update microcode levels beyond the patch IDs below.
|
|
* From internal discussions, we gathered that OS/hypervisor
|
|
* cannot reliably perform microcode updates beyond these levels
|
|
* due to hardware issues. Therefore, we need to abort microcode
|
|
* update process if we hit any of these levels.
|
|
*/
|
|
static const unsigned int final_levels[] = {
|
|
0x01000098,
|
|
0x0100009f,
|
|
0x010000af,
|
|
};
|
|
unsigned int i;
|
|
|
|
if ( boot_cpu_data.family != 0x10 )
|
|
return false;
|
|
|
|
for ( i = 0; i < ARRAY_SIZE(final_levels); i++ )
|
|
if ( sig->rev == final_levels[i] )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static int compare_revisions(uint32_t old_rev, uint32_t new_rev)
|
|
{
|
|
if ( new_rev > old_rev )
|
|
return NEW_UCODE;
|
|
|
|
if ( new_rev == old_rev )
|
|
return SAME_UCODE;
|
|
|
|
return OLD_UCODE;
|
|
}
|
|
|
|
/*
|
|
* Check whether this microcode patch is applicable for the current CPU.
|
|
*
|
|
* AMD microcode blobs only have the "equivalent CPU identifier" which is a 16
|
|
* bit contraction of the 32 bit Family/Model/Stepping.
|
|
*
|
|
* We expect to only be run after scan_equiv_cpu_table() has found a valid
|
|
* mapping for the current CPU. If this is violated, the 0 in equiv.id will
|
|
* cause the patch to be rejected too.
|
|
*/
|
|
static bool microcode_fits_cpu(const struct microcode_patch *patch)
|
|
{
|
|
ASSERT(equiv.sig);
|
|
|
|
return equiv.id == patch->processor_rev_id;
|
|
}
|
|
|
|
static int cf_check amd_compare(
|
|
const struct microcode_patch *old, const struct microcode_patch *new)
|
|
{
|
|
/* Both patches to compare are supposed to be applicable to local CPU. */
|
|
ASSERT(microcode_fits_cpu(new));
|
|
ASSERT(microcode_fits_cpu(old));
|
|
|
|
return compare_revisions(old->patch_id, new->patch_id);
|
|
}
|
|
|
|
/*
|
|
* Check whether this patch has a minimum revision given, and whether the
|
|
* condition is satisfied.
|
|
*
|
|
* In linux-firmware for CPUs suffering from the Entrysign vulnerability,
|
|
* ucodes signed with the updated signature algorithm have reused the checksum
|
|
* field as a min-revision field. From public archives, the checksum field
|
|
* appears to have been unused since Fam12h.
|
|
*
|
|
* Returns false if there is a min revision given, and it suggests that that
|
|
* the patch cannot be loaded on the current system. True otherwise.
|
|
*/
|
|
static bool check_min_rev(const struct microcode_patch *patch)
|
|
{
|
|
ASSERT(microcode_fits_cpu(patch));
|
|
|
|
if ( patch->processor_rev_id < 0xa000 || /* pre Zen3? */
|
|
patch->min_rev == 0 ) /* No min rev specified */
|
|
return true;
|
|
|
|
/*
|
|
* Sanity check, as this is a reused field. If this is a true
|
|
* min_revision field, it will differ only in the bottom byte from the
|
|
* patch_id. Otherwise, it's probably a checksum.
|
|
*/
|
|
if ( (patch->patch_id ^ patch->min_rev) & ~0xff )
|
|
{
|
|
printk(XENLOG_WARNING
|
|
"microcode: patch %#x has unexpected min_rev %#x\n",
|
|
patch->patch_id, patch->min_rev);
|
|
return true;
|
|
}
|
|
|
|
return this_cpu(cpu_sig).rev >= patch->min_rev;
|
|
}
|
|
|
|
static int cf_check amd_ucode_load(const struct microcode_patch *patch,
|
|
unsigned int flags)
|
|
{
|
|
int hw_err, result;
|
|
unsigned int cpu = smp_processor_id();
|
|
struct cpu_signature *sig = &per_cpu(cpu_sig, cpu);
|
|
uint32_t rev, old_rev = sig->rev;
|
|
bool ucode_force = flags & XENPF_UCODE_FORCE;
|
|
|
|
if ( !microcode_fits_cpu(patch) )
|
|
return -EINVAL;
|
|
|
|
result = compare_revisions(old_rev, patch->patch_id);
|
|
|
|
/*
|
|
* Allow application of the same revision to pick up SMT-specific changes
|
|
* even if the revision of the other SMT thread is already up-to-date.
|
|
*/
|
|
if ( !ucode_force && (result == SAME_UCODE || result == OLD_UCODE) )
|
|
return -EEXIST;
|
|
|
|
if ( check_final_patch_levels(sig) )
|
|
{
|
|
printk(XENLOG_ERR
|
|
"microcode: CPU%u current rev %#x unsafe to update\n",
|
|
cpu, sig->rev);
|
|
return -ENXIO;
|
|
}
|
|
|
|
if ( !ucode_force && !check_min_rev(patch) )
|
|
{
|
|
printk(XENLOG_ERR
|
|
"microcode: CPU%u current rev %#x below patch min_rev %#x\n",
|
|
cpu, sig->rev, patch->min_rev);
|
|
return -ENXIO;
|
|
}
|
|
|
|
hw_err = wrmsr_safe(MSR_AMD_PATCHLOADER, (unsigned long)patch);
|
|
|
|
/* get patch id after patching */
|
|
rdmsrl(MSR_AMD_PATCHLEVEL, rev);
|
|
sig->rev = rev;
|
|
|
|
/*
|
|
* Fam17h processors leave the mapping of the ucode as UC after the
|
|
* update. Flush the mapping to regain normal cacheability.
|
|
*
|
|
* We do not know the granularity of mapping, and at 3200 bytes in size
|
|
* there is a good chance of crossing a 4k page boundary. Shoot-down the
|
|
* start and end just to be safe.
|
|
*/
|
|
if ( boot_cpu_data.family == 0x17 )
|
|
{
|
|
invlpg(patch);
|
|
invlpg((const void *)patch + F17H_MPB_MAX_SIZE - 1);
|
|
}
|
|
|
|
/* check current patch id and patch's id for match */
|
|
if ( hw_err || (rev != patch->patch_id) )
|
|
{
|
|
printk(XENLOG_ERR
|
|
"microcode: CPU%u update rev %#x to %#x failed, result %#x\n",
|
|
cpu, old_rev, patch->patch_id, rev);
|
|
return -EIO;
|
|
}
|
|
|
|
printk(XENLOG_WARNING
|
|
"microcode: CPU%u updated from revision %#x to %#x, date = %04x-%02x-%02x\n",
|
|
cpu, old_rev, rev, patch->year, patch->month, patch->day);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int scan_equiv_cpu_table(const struct container_equiv_table *et)
|
|
{
|
|
const struct cpu_signature *sig = &this_cpu(cpu_sig);
|
|
unsigned int i, nr = et->len / sizeof(et->eq[0]);
|
|
|
|
/* Search the equiv_cpu_table for the current CPU. */
|
|
for ( i = 0; i < nr && et->eq[i].installed_cpu; ++i )
|
|
{
|
|
if ( et->eq[i].installed_cpu != sig->sig )
|
|
continue;
|
|
|
|
if ( !equiv.sig ) /* Cache details on first find. */
|
|
{
|
|
equiv.sig = sig->sig;
|
|
equiv.id = et->eq[i].equiv_cpu;
|
|
return 0;
|
|
}
|
|
|
|
if ( equiv.sig != sig->sig || equiv.id != et->eq[i].equiv_cpu )
|
|
{
|
|
/*
|
|
* This can only occur if two equiv tables have been seen with
|
|
* different mappings for the same CPU. The mapping is fixed, so
|
|
* one of the tables is wrong. As we can't calculate the mapping,
|
|
* we trusted the first table we saw.
|
|
*/
|
|
printk(XENLOG_ERR
|
|
"microcode: Equiv mismatch: cpu %08x, got %04x, cached %04x\n",
|
|
sig->sig, et->eq[i].equiv_cpu, equiv.id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* equiv_cpu_table was fine, but nothing found for the current CPU. */
|
|
return -ESRCH;
|
|
}
|
|
|
|
static struct microcode_patch *cf_check amd_ucode_parse(
|
|
const void *buf, size_t size, bool make_copy)
|
|
{
|
|
const struct microcode_patch *saved = NULL;
|
|
struct microcode_patch *patch = NULL;
|
|
size_t saved_size = 0;
|
|
int error = 0;
|
|
|
|
while ( size )
|
|
{
|
|
const struct container_equiv_table *et;
|
|
bool skip_ucode;
|
|
|
|
if ( size < 4 || *(const uint32_t *)buf != UCODE_MAGIC )
|
|
{
|
|
printk(XENLOG_ERR "microcode: Wrong microcode patch file magic\n");
|
|
error = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
/* Move over UCODE_MAGIC. */
|
|
buf += 4;
|
|
size -= 4;
|
|
|
|
if ( size < sizeof(*et) || /* No space for header? */
|
|
(et = buf)->type != UCODE_EQUIV_TYPE || /* Not an Equivalence Table? */
|
|
size - sizeof(*et) < et->len || /* No space for table? */
|
|
et->len % sizeof(et->eq[0]) ) /* Not multiple of equiv_cpu_entry? */
|
|
{
|
|
printk(XENLOG_ERR "microcode: Bad equivalent cpu table\n");
|
|
error = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
/* Move over the Equiv table. */
|
|
buf += sizeof(*et) + et->len;
|
|
size -= sizeof(*et) + et->len;
|
|
|
|
error = scan_equiv_cpu_table(et);
|
|
|
|
/*
|
|
* -ESRCH means no applicable microcode in this container. But, there
|
|
* might be subsequent containers in the blob. Skipping to the end of
|
|
* this container still requires us to follow the UCODE_UCODE_TYPE/len
|
|
* metadata because there's no overall container length given.
|
|
*/
|
|
if ( error && error != -ESRCH )
|
|
break;
|
|
skip_ucode = error;
|
|
error = 0;
|
|
|
|
while ( size )
|
|
{
|
|
const struct container_microcode *mc;
|
|
|
|
if ( size < sizeof(*mc) || /* No space for container header? */
|
|
(mc = buf)->type != UCODE_UCODE_TYPE || /* Not a ucode blob? */
|
|
size - sizeof(*mc) < mc->len || /* No space for blob? */
|
|
mc->len < sizeof(struct microcode_patch) ) /* No space for patch header? */
|
|
{
|
|
printk(XENLOG_ERR "microcode: Bad microcode data\n");
|
|
error = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
if ( skip_ucode )
|
|
goto skip;
|
|
|
|
if ( !verify_patch_size(mc->len) )
|
|
{
|
|
printk(XENLOG_WARNING
|
|
"microcode: Bad microcode length 0x%08x for cpu 0x%04x\n",
|
|
mc->len, mc->patch->processor_rev_id);
|
|
/*
|
|
* If the blob size sanity check fails, trust the container
|
|
* length which has already been checked to be at least
|
|
* plausible at this point.
|
|
*/
|
|
goto skip;
|
|
}
|
|
|
|
/*
|
|
* If the new ucode covers current CPU, compare ucodes and store the
|
|
* one with higher revision.
|
|
*/
|
|
if ( microcode_fits_cpu(mc->patch) &&
|
|
(!saved ||
|
|
compare_revisions(saved->patch_id,
|
|
mc->patch->patch_id) == NEW_UCODE) &&
|
|
check_digest(mc) )
|
|
{
|
|
saved = mc->patch;
|
|
saved_size = mc->len;
|
|
}
|
|
|
|
/* Move over the microcode blob. */
|
|
skip:
|
|
buf += sizeof(*mc) + mc->len;
|
|
size -= sizeof(*mc) + mc->len;
|
|
|
|
/*
|
|
* Peek ahead. If we see the start of another container, we've
|
|
* exhaused all microcode blobs in this container. Exit cleanly.
|
|
*/
|
|
if ( size >= 4 && *(const uint32_t *)buf == UCODE_MAGIC )
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Any error means we didn't get cleanly to the end of the microcode
|
|
* container. There isn't an overall length field, so we've got no
|
|
* way of skipping to the next container in the stream.
|
|
*/
|
|
if ( error )
|
|
break;
|
|
}
|
|
|
|
if ( saved )
|
|
{
|
|
if ( make_copy )
|
|
{
|
|
patch = xmemdup_bytes(saved, saved_size);
|
|
if ( !patch )
|
|
error = -ENOMEM;
|
|
}
|
|
else
|
|
patch = (struct microcode_patch *)saved;
|
|
}
|
|
|
|
if ( error && !patch )
|
|
patch = ERR_PTR(error);
|
|
|
|
return patch;
|
|
}
|
|
|
|
static const char __initconst amd_cpio_path[] =
|
|
"kernel/x86/microcode/AuthenticAMD.bin";
|
|
|
|
static const struct microcode_ops __initconst_cf_clobber amd_ucode_ops = {
|
|
.collect_cpu_info = collect_cpu_info,
|
|
.parse = MICROCODE_OP(amd_ucode_parse),
|
|
.load = MICROCODE_OP(amd_ucode_load),
|
|
.compare = MICROCODE_OP(amd_compare),
|
|
.cpio_path = MICROCODE_OP(amd_cpio_path),
|
|
};
|
|
|
|
void __init ucode_probe_amd(struct microcode_ops *ops)
|
|
{
|
|
/*
|
|
* The Entrysign vulnerability (SB-7033, CVE-2024-36347) affects Zen1-5
|
|
* CPUs. Taint Xen if digest checking is turned off.
|
|
*/
|
|
if ( IS_ENABLED(CONFIG_MICROCODE_LOADING) &&
|
|
boot_cpu_data.family >= 0x17 && boot_cpu_data.family <= 0x1a &&
|
|
!is_zen6_uarch() && !opt_digest_check )
|
|
{
|
|
printk(XENLOG_WARNING
|
|
"Microcode patch additional digest checks disabled\n");
|
|
add_taint(TAINT_CPU_OUT_OF_SPEC);
|
|
}
|
|
|
|
if ( boot_cpu_data.family < 0x10 )
|
|
return;
|
|
|
|
*ops = amd_ucode_ops;
|
|
}
|
|
|
|
#if 0 /* Manual CONFIG_SELF_TESTS */
|
|
static void __init __constructor test_digests_sorted(void)
|
|
{
|
|
for ( unsigned int i = 1; i < ARRAY_SIZE(patch_digests); ++i )
|
|
{
|
|
if ( patch_digests[i - 1].patch_id < patch_digests[i].patch_id )
|
|
continue;
|
|
|
|
panic("patch_digests[] not sorted: %08x >= %08x\n",
|
|
patch_digests[i - 1].patch_id,
|
|
patch_digests[i].patch_id);
|
|
}
|
|
}
|
|
#endif /* CONFIG_SELF_TESTS */
|
|
|
|
/*
|
|
* The Entrysign vulnerability affects all Zen1 thru Zen5 CPUs. Firmware
|
|
* fixes were produced from Nov 2024. Zen3 thru Zen5 can continue to take
|
|
* OS-loadable microcode updates using a new signature scheme, as long as
|
|
* firmware has been updated first.
|
|
*/
|
|
void __init amd_check_entrysign(void)
|
|
{
|
|
unsigned int curr_rev;
|
|
uint8_t fixed_rev;
|
|
|
|
if ( !IS_ENABLED(CONFIG_MICROCODE_LOADING) ||
|
|
boot_cpu_data.vendor != X86_VENDOR_AMD ||
|
|
boot_cpu_data.family < 0x17 ||
|
|
boot_cpu_data.family > 0x1a ||
|
|
is_zen6_uarch() )
|
|
return;
|
|
|
|
/*
|
|
* Table taken from Linux, which is the only known source of information
|
|
* about client revisions. Note, Linux expresses "last-vulnerable-rev"
|
|
* while Xen wants "first-fixed-rev".
|
|
*/
|
|
curr_rev = this_cpu(cpu_sig).rev;
|
|
switch ( curr_rev >> 8 )
|
|
{
|
|
case 0x080012: fixed_rev = 0x78; break;
|
|
case 0x080082: fixed_rev = 0x10; break;
|
|
case 0x083010: fixed_rev = 0x7d; break;
|
|
case 0x086001: fixed_rev = 0x0f; break;
|
|
case 0x086081: fixed_rev = 0x09; break;
|
|
case 0x087010: fixed_rev = 0x35; break;
|
|
case 0x08a000: fixed_rev = 0x0b; break;
|
|
case 0x0a0010: fixed_rev = 0x7b; break;
|
|
case 0x0a0011: fixed_rev = 0xdb; break;
|
|
case 0x0a0012: fixed_rev = 0x44; break;
|
|
case 0x0a0082: fixed_rev = 0x0f; break;
|
|
case 0x0a1011: fixed_rev = 0x54; break;
|
|
case 0x0a1012: fixed_rev = 0x4f; break;
|
|
case 0x0a1081: fixed_rev = 0x0a; break;
|
|
case 0x0a2010: fixed_rev = 0x30; break;
|
|
case 0x0a2012: fixed_rev = 0x13; break;
|
|
case 0x0a4041: fixed_rev = 0x0a; break;
|
|
case 0x0a5000: fixed_rev = 0x14; break;
|
|
case 0x0a6012: fixed_rev = 0x0b; break;
|
|
case 0x0a7041: fixed_rev = 0x0a; break;
|
|
case 0x0a7052: fixed_rev = 0x09; break;
|
|
case 0x0a7080: fixed_rev = 0x0a; break;
|
|
case 0x0a70c0: fixed_rev = 0x0a; break;
|
|
case 0x0aa001: fixed_rev = 0x17; break;
|
|
case 0x0aa002: fixed_rev = 0x19; break;
|
|
case 0x0b0021: fixed_rev = 0x47; break;
|
|
case 0x0b0081: fixed_rev = 0x12; break;
|
|
case 0x0b1010: fixed_rev = 0x47; break;
|
|
case 0x0b2040: fixed_rev = 0x32; break;
|
|
case 0x0b4040: fixed_rev = 0x32; break;
|
|
case 0x0b4041: fixed_rev = 0x02; break;
|
|
case 0x0b6000: fixed_rev = 0x32; break;
|
|
case 0x0b6080: fixed_rev = 0x32; break;
|
|
case 0x0b7000: fixed_rev = 0x32; break;
|
|
default:
|
|
printk(XENLOG_WARNING
|
|
"Unrecognised CPU %02x-%02x-%02x ucode 0x%08x, assuming vulnerable to Entrysign\n",
|
|
boot_cpu_data.family, boot_cpu_data.model,
|
|
boot_cpu_data.stepping, curr_rev);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* This check is best-effort. If the platform looks to be out of date, it
|
|
* probably is. If the platform looks to be fixed, it either genuinely
|
|
* is, or malware has gotten in before Xen booted and all bets are off.
|
|
*/
|
|
if ( (uint8_t)curr_rev >= fixed_rev )
|
|
{
|
|
entrysign_mitigated_in_firmware = true;
|
|
return;
|
|
}
|
|
|
|
printk(XENLOG_WARNING
|
|
"WARNING: Platform vulnerable to Entrysign (SB-7033, CVE-2024-36347) - firmware update required\n");
|
|
add_taint(TAINT_CPU_OUT_OF_SPEC);
|
|
}
|