x86/microcode/AMD: Rework container parsing

It was pretty clumsy before and the whole work of parsing the microcode
containers was spread around the functions wrongly.

Clean it up so that there's a main scan_containers() function which
iterates over the microcode blob and picks apart the containers glued
together. For each container, it calls a parse_container() helper which
concentrates on one container only: sanity-checking, parsing, counting
microcode patches in there, etc.

It makes much more sense now and it is actually very readable. Oh, and
we luvz a diffstat removing more crap than adding.

Signed-off-by: Borislav Petkov <bp@suse.de>
Link: http://lkml.kernel.org/r/20170120202955.4091-8-bp@alien8.de
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
This commit is contained in:
Borislav Petkov 2017-01-20 21:29:46 +01:00 committed by Thomas Gleixner
parent f454177f73
commit 8801b3fcb5

View File

@ -64,43 +64,6 @@ static u16 this_equiv_id;
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 u16 find_equiv_id(struct equiv_cpu_entry *equiv_table, u32 sig)
{
for (; equiv_table && equiv_table->installed_cpu; equiv_table++) {
@ -115,80 +78,106 @@ static u16 find_equiv_id(struct equiv_cpu_entry *equiv_table, u32 sig)
* 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.
* Returns the amount of bytes consumed while scanning. @desc contains all the
* data we're going to use in later stages of the application.
*/
static u16
find_proper_container(u8 *ucode, size_t size, struct cont_desc *desc)
static ssize_t parse_container(u8 *ucode, ssize_t size, struct cont_desc *desc)
{
struct cont_desc ret = { 0 };
u32 eax, ebx, ecx, edx;
struct equiv_cpu_entry *eq;
int offset, left;
u16 eq_id = 0;
u32 *header;
u8 *data;
ssize_t orig_size = size;
u32 *hdr = (u32 *)ucode;
u32 eax, ebx, ecx, edx;
u16 eq_id;
u8 *buf;
data = ucode;
left = size;
header = (u32 *)data;
/* Am I looking at an equivalence table header? */
if (hdr[0] != UCODE_MAGIC ||
hdr[1] != UCODE_EQUIV_CPU_TABLE_TYPE ||
hdr[2] == 0) {
desc->eq_id = 0;
return CONTAINER_HDR_SZ;
}
buf = ucode;
/* find equiv cpu table */
if (header[0] != UCODE_MAGIC ||
header[1] != UCODE_EQUIV_CPU_TABLE_TYPE || /* type */
header[2] == 0) /* size */
return eq_id;
eq = (struct equiv_cpu_entry *)(buf + CONTAINER_HDR_SZ);
eax = 0x00000001;
eax = 1;
ecx = 0;
native_cpuid(&eax, &ebx, &ecx, &edx);
while (left > 0) {
eq = (struct equiv_cpu_entry *)(data + CONTAINER_HDR_SZ);
/* Find the equivalence ID of our CPU in this table: */
eq_id = find_equiv_id(eq, eax);
ret.data = data;
buf += hdr[2] + CONTAINER_HDR_SZ;
size -= hdr[2] + CONTAINER_HDR_SZ;
/* Advance past the container header */
offset = header[2] + CONTAINER_HDR_SZ;
data += offset;
left -= offset;
/*
* Scan through the rest of the container to find where it ends. We do
* some basic sanity-checking too.
*/
while (size > 0) {
struct microcode_amd *mc;
u32 patch_size;
eq_id = find_equiv_id(eq, eax);
if (eq_id) {
ret.size = compute_container_size(ret.data, left + offset);
hdr = (u32 *)buf;
/*
* truncate how much we need to iterate over in the
* ucode update loop below
*/
left = ret.size - offset;
if (hdr[0] != UCODE_UCODE_TYPE)
break;
*desc = ret;
return eq_id;
/* Sanity-check patch size. */
patch_size = hdr[1];
if (patch_size > PATCH_MAX_SIZE)
break;
/* Skip patch section header: */
buf += SECTION_HDR_SIZE;
size -= SECTION_HDR_SIZE;
mc = (struct microcode_amd *)buf;
if (eq_id == mc->hdr.processor_rev_id) {
desc->psize = patch_size;
desc->mc = mc;
}
/*
* 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 *)data;
if (header[0] == UCODE_MAGIC &&
header[1] == UCODE_EQUIV_CPU_TABLE_TYPE)
break;
offset = header[1] + SECTION_HDR_SIZE;
data += offset;
left -= offset;
}
/* mark where the next microcode container file starts */
offset = data - (u8 *)ucode;
ucode = data;
buf += patch_size;
size -= patch_size;
}
return eq_id;
/*
* If we have found a patch (desc->mc), it means we're looking at the
* container which has a patch for this CPU so return 0 to mean, @ucode
* already points to the proper container. Otherwise, we return the size
* we scanned so that we can advance to the next container in the
* buffer.
*/
if (desc->mc) {
desc->eq_id = eq_id;
desc->data = ucode;
desc->size = orig_size - size;
return 0;
}
return orig_size - size;
}
/*
* Scan the ucode blob for the proper container as we can have multiple
* containers glued together.
*/
static void scan_containers(u8 *ucode, size_t size, struct cont_desc *desc)
{
ssize_t rem = size;
while (rem >= 0) {
ssize_t s = parse_container(ucode, rem, desc);
if (!s)
return;
ucode += s;
rem -= s;
}
}
static int __apply_microcode_amd(struct microcode_amd *mc)
@ -214,17 +203,16 @@ static int __apply_microcode_amd(struct microcode_amd *mc)
* load_microcode_amd() to save equivalent cpu table and microcode patches in
* kernel heap memory.
*
* Returns true if container found (sets @ret_cont), false otherwise.
* Returns true if container found (sets @desc), false otherwise.
*/
static bool apply_microcode_early_amd(void *ucode, size_t size, bool save_patch,
struct cont_desc *desc)
struct cont_desc *ret_desc)
{
struct cont_desc desc = { 0 };
u8 (*patch)[PATCH_MAX_SIZE];
u32 rev, *header, *new_rev;
struct cont_desc ret;
int offset, left;
u16 eq_id = 0;
u8 *data;
struct microcode_amd *mc;
u32 rev, *new_rev;
bool ret = false;
#ifdef CONFIG_X86_32
new_rev = (u32 *)__pa_nodebug(&ucode_new_rev);
@ -237,47 +225,31 @@ static bool apply_microcode_early_amd(void *ucode, size_t size, bool save_patch,
if (check_current_patch_level(&rev, true))
return false;
eq_id = find_proper_container(ucode, size, &ret);
if (!eq_id)
return false;
scan_containers(ucode, size, &desc);
if (!desc.eq_id)
return ret;
this_equiv_id = eq_id;
header = (u32 *)ret.data;
this_equiv_id = desc.eq_id;
/* We're pointing to an equiv table, skip over it. */
data = ret.data + header[2] + CONTAINER_HDR_SZ;
left = ret.size - (header[2] + CONTAINER_HDR_SZ);
mc = desc.mc;
if (!mc)
return ret;
while (left > 0) {
struct microcode_amd *mc;
if (rev >= mc->hdr.patch_id)
return ret;
header = (u32 *)data;
if (header[0] != UCODE_UCODE_TYPE || /* type */
header[1] == 0) /* size */
break;
if (!__apply_microcode_amd(mc)) {
*new_rev = mc->hdr.patch_id;
ret = true;
mc = (struct microcode_amd *)(data + SECTION_HDR_SIZE);
if (eq_id == mc->hdr.processor_rev_id && rev < mc->hdr.patch_id) {
if (!__apply_microcode_amd(mc)) {
rev = mc->hdr.patch_id;
*new_rev = rev;
if (save_patch)
memcpy(patch, mc, min_t(u32, header[1], PATCH_MAX_SIZE));
}
}
offset = header[1] + SECTION_HDR_SIZE;
data += offset;
left -= offset;
if (save_patch)
memcpy(patch, mc, min_t(u32, desc.psize, PATCH_MAX_SIZE));
}
if (desc)
*desc = ret;
if (ret_desc)
*ret_desc = desc;
return true;
return ret;
}
static bool get_builtin_microcode(struct cpio_data *cp, unsigned int family)
@ -396,6 +368,7 @@ reget:
}
if (!apply_microcode_early_amd(cp.data, cp.size, false, &cont)) {
cont.data = NULL;
cont.size = -1;
return;
}
@ -434,7 +407,6 @@ int __init save_microcode_in_initrd_amd(unsigned int fam)
{
enum ucode_state ret;
int retval = 0;
u16 eq_id;
if (!cont.data) {
if (IS_ENABLED(CONFIG_X86_32) && (cont.size != -1)) {
@ -450,8 +422,8 @@ int __init save_microcode_in_initrd_amd(unsigned int fam)
return -EINVAL;
}
eq_id = find_proper_container(cp.data, cp.size, &cont);
if (!eq_id) {
scan_containers(cp.data, cp.size, &cont);
if (!cont.eq_id) {
cont.size = -1;
return -EINVAL;
}