Commit 8801b3fc authored by Borislav Petkov's avatar Borislav Petkov Committed by Thomas Gleixner

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: default avatarBorislav Petkov <bp@suse.de>
Link: http://lkml.kernel.org/r/20170120202955.4091-8-bp@alien8.deSigned-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent f454177f
...@@ -64,43 +64,6 @@ static u16 this_equiv_id; ...@@ -64,43 +64,6 @@ static u16 this_equiv_id;
static const char static const char
ucode_path[] __maybe_unused = "kernel/x86/microcode/AuthenticAMD.bin"; 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) static u16 find_equiv_id(struct equiv_cpu_entry *equiv_table, u32 sig)
{ {
for (; equiv_table && equiv_table->installed_cpu; equiv_table++) { 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) ...@@ -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 * This scans the ucode blob for the proper container as we can have multiple
* containers glued together. Returns the equivalence ID from the equivalence * containers glued together. Returns the equivalence ID from the equivalence
* table or 0 if none found. * 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 static ssize_t parse_container(u8 *ucode, ssize_t size, struct cont_desc *desc)
find_proper_container(u8 *ucode, size_t size, struct cont_desc *desc)
{ {
struct cont_desc ret = { 0 };
u32 eax, ebx, ecx, edx;
struct equiv_cpu_entry *eq; struct equiv_cpu_entry *eq;
int offset, left; ssize_t orig_size = size;
u16 eq_id = 0; u32 *hdr = (u32 *)ucode;
u32 *header; u32 eax, ebx, ecx, edx;
u8 *data; u16 eq_id;
u8 *buf;
data = ucode; /* Am I looking at an equivalence table header? */
left = size; if (hdr[0] != UCODE_MAGIC ||
header = (u32 *)data; hdr[1] != UCODE_EQUIV_CPU_TABLE_TYPE ||
hdr[2] == 0) {
desc->eq_id = 0;
return CONTAINER_HDR_SZ;
}
buf = ucode;
/* find equiv cpu table */ eq = (struct equiv_cpu_entry *)(buf + CONTAINER_HDR_SZ);
if (header[0] != UCODE_MAGIC ||
header[1] != UCODE_EQUIV_CPU_TABLE_TYPE || /* type */
header[2] == 0) /* size */
return eq_id;
eax = 0x00000001; eax = 1;
ecx = 0; ecx = 0;
native_cpuid(&eax, &ebx, &ecx, &edx); native_cpuid(&eax, &ebx, &ecx, &edx);
while (left > 0) { /* Find the equivalence ID of our CPU in this table: */
eq = (struct equiv_cpu_entry *)(data + CONTAINER_HDR_SZ); eq_id = find_equiv_id(eq, eax);
ret.data = data;
/* Advance past the container header */ buf += hdr[2] + CONTAINER_HDR_SZ;
offset = header[2] + CONTAINER_HDR_SZ; size -= hdr[2] + CONTAINER_HDR_SZ;
data += offset;
left -= offset;
eq_id = find_equiv_id(eq, eax); /*
if (eq_id) { * Scan through the rest of the container to find where it ends. We do
ret.size = compute_container_size(ret.data, left + offset); * some basic sanity-checking too.
*/
while (size > 0) {
struct microcode_amd *mc;
u32 patch_size;
/* hdr = (u32 *)buf;
* truncate how much we need to iterate over in the
* ucode update loop below
*/
left = ret.size - offset;
*desc = ret; if (hdr[0] != UCODE_UCODE_TYPE)
return eq_id; break;
}
/* /* Sanity-check patch size. */
* support multiple container files appended together. if this patch_size = hdr[1];
* one does not have a matching equivalent cpu entry, we fast if (patch_size > PATCH_MAX_SIZE)
* forward to the next container file. break;
*/
while (left > 0) {
header = (u32 *)data;
if (header[0] == UCODE_MAGIC && /* Skip patch section header: */
header[1] == UCODE_EQUIV_CPU_TABLE_TYPE) buf += SECTION_HDR_SIZE;
break; size -= SECTION_HDR_SIZE;
offset = header[1] + SECTION_HDR_SIZE; mc = (struct microcode_amd *)buf;
data += offset; if (eq_id == mc->hdr.processor_rev_id) {
left -= offset; desc->psize = patch_size;
desc->mc = mc;
} }
/* mark where the next microcode container file starts */ buf += patch_size;
offset = data - (u8 *)ucode; size -= patch_size;
ucode = data;
} }
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) static int __apply_microcode_amd(struct microcode_amd *mc)
...@@ -214,17 +203,16 @@ 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 * load_microcode_amd() to save equivalent cpu table and microcode patches in
* kernel heap memory. * 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, 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]; u8 (*patch)[PATCH_MAX_SIZE];
u32 rev, *header, *new_rev; struct microcode_amd *mc;
struct cont_desc ret; u32 rev, *new_rev;
int offset, left; bool ret = false;
u16 eq_id = 0;
u8 *data;
#ifdef CONFIG_X86_32 #ifdef CONFIG_X86_32
new_rev = (u32 *)__pa_nodebug(&ucode_new_rev); 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, ...@@ -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)) if (check_current_patch_level(&rev, true))
return false; return false;
eq_id = find_proper_container(ucode, size, &ret); scan_containers(ucode, size, &desc);
if (!eq_id) if (!desc.eq_id)
return false; return ret;
this_equiv_id = eq_id;
header = (u32 *)ret.data;
/* 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);
while (left > 0) {
struct microcode_amd *mc;
header = (u32 *)data;
if (header[0] != UCODE_UCODE_TYPE || /* type */
header[1] == 0) /* size */
break;
mc = (struct microcode_amd *)(data + SECTION_HDR_SIZE); this_equiv_id = desc.eq_id;
if (eq_id == mc->hdr.processor_rev_id && rev < mc->hdr.patch_id) { mc = desc.mc;
if (!mc)
return ret;
if (!__apply_microcode_amd(mc)) { if (rev >= mc->hdr.patch_id)
rev = mc->hdr.patch_id; return ret;
*new_rev = rev;
if (save_patch) if (!__apply_microcode_amd(mc)) {
memcpy(patch, mc, min_t(u32, header[1], PATCH_MAX_SIZE)); *new_rev = mc->hdr.patch_id;
} ret = true;
}
offset = header[1] + SECTION_HDR_SIZE; if (save_patch)
data += offset; memcpy(patch, mc, min_t(u32, desc.psize, PATCH_MAX_SIZE));
left -= offset;
} }
if (desc) if (ret_desc)
*desc = ret; *ret_desc = desc;
return true; return ret;
} }
static bool get_builtin_microcode(struct cpio_data *cp, unsigned int family) static bool get_builtin_microcode(struct cpio_data *cp, unsigned int family)
...@@ -396,6 +368,7 @@ void load_ucode_amd_ap(unsigned int family) ...@@ -396,6 +368,7 @@ void load_ucode_amd_ap(unsigned int family)
} }
if (!apply_microcode_early_amd(cp.data, cp.size, false, &cont)) { if (!apply_microcode_early_amd(cp.data, cp.size, false, &cont)) {
cont.data = NULL;
cont.size = -1; cont.size = -1;
return; return;
} }
...@@ -434,7 +407,6 @@ int __init save_microcode_in_initrd_amd(unsigned int fam) ...@@ -434,7 +407,6 @@ int __init save_microcode_in_initrd_amd(unsigned int fam)
{ {
enum ucode_state ret; enum ucode_state ret;
int retval = 0; int retval = 0;
u16 eq_id;
if (!cont.data) { if (!cont.data) {
if (IS_ENABLED(CONFIG_X86_32) && (cont.size != -1)) { if (IS_ENABLED(CONFIG_X86_32) && (cont.size != -1)) {
...@@ -450,8 +422,8 @@ int __init save_microcode_in_initrd_amd(unsigned int fam) ...@@ -450,8 +422,8 @@ int __init save_microcode_in_initrd_amd(unsigned int fam)
return -EINVAL; return -EINVAL;
} }
eq_id = find_proper_container(cp.data, cp.size, &cont); scan_containers(cp.data, cp.size, &cont);
if (!eq_id) { if (!cont.eq_id) {
cont.size = -1; cont.size = -1;
return -EINVAL; return -EINVAL;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment