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:
parent
f454177f73
commit
8801b3fcb5
1 changed files with 107 additions and 135 deletions
|
@ -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)
|
||||||
* 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);
|
|
||||||
|
|
||||||
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);
|
eq_id = find_equiv_id(eq, eax);
|
||||||
if (eq_id) {
|
|
||||||
ret.size = compute_container_size(ret.data, left + offset);
|
buf += hdr[2] + CONTAINER_HDR_SZ;
|
||||||
|
size -= hdr[2] + CONTAINER_HDR_SZ;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* truncate how much we need to iterate over in the
|
* Scan through the rest of the container to find where it ends. We do
|
||||||
* ucode update loop below
|
* some basic sanity-checking too.
|
||||||
*/
|
*/
|
||||||
left = ret.size - offset;
|
while (size > 0) {
|
||||||
|
struct microcode_amd *mc;
|
||||||
|
u32 patch_size;
|
||||||
|
|
||||||
*desc = ret;
|
hdr = (u32 *)buf;
|
||||||
return eq_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
if (hdr[0] != UCODE_UCODE_TYPE)
|
||||||
* 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;
|
break;
|
||||||
|
|
||||||
offset = header[1] + SECTION_HDR_SIZE;
|
/* Sanity-check patch size. */
|
||||||
data += offset;
|
patch_size = hdr[1];
|
||||||
left -= offset;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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)
|
||||||
* 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,
|
||||||
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;
|
this_equiv_id = desc.eq_id;
|
||||||
header = (u32 *)ret.data;
|
|
||||||
|
|
||||||
/* We're pointing to an equiv table, skip over it. */
|
mc = desc.mc;
|
||||||
data = ret.data + header[2] + CONTAINER_HDR_SZ;
|
if (!mc)
|
||||||
left = ret.size - (header[2] + CONTAINER_HDR_SZ);
|
return ret;
|
||||||
|
|
||||||
while (left > 0) {
|
if (rev >= mc->hdr.patch_id)
|
||||||
struct microcode_amd *mc;
|
return ret;
|
||||||
|
|
||||||
header = (u32 *)data;
|
|
||||||
if (header[0] != UCODE_UCODE_TYPE || /* type */
|
|
||||||
header[1] == 0) /* size */
|
|
||||||
break;
|
|
||||||
|
|
||||||
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)) {
|
if (!__apply_microcode_amd(mc)) {
|
||||||
rev = mc->hdr.patch_id;
|
*new_rev = mc->hdr.patch_id;
|
||||||
*new_rev = rev;
|
ret = true;
|
||||||
|
|
||||||
if (save_patch)
|
if (save_patch)
|
||||||
memcpy(patch, mc, min_t(u32, header[1], PATCH_MAX_SIZE));
|
memcpy(patch, mc, min_t(u32, desc.psize, PATCH_MAX_SIZE));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
offset = header[1] + SECTION_HDR_SIZE;
|
if (ret_desc)
|
||||||
data += offset;
|
*ret_desc = desc;
|
||||||
left -= offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (desc)
|
return ret;
|
||||||
*desc = ret;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 @@ reget:
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue