Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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.de


Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent f454177f
Loading
Loading
Loading
Loading
+105 −133
Original line number Original line Diff line number Diff line
@@ -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;

		hdr = (u32 *)buf;

		if (hdr[0] != UCODE_UCODE_TYPE)
			break;

		/* Sanity-check patch size. */
		patch_size = hdr[1];
		if (patch_size > PATCH_MAX_SIZE)
			break;


			*desc = ret;
		/* Skip patch section header: */
			return eq_id;
		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;
		}

		buf  += patch_size;
		size -= patch_size;
	}
	}


	/*
	/*
		 * support multiple container files appended together. if this
	 * If we have found a patch (desc->mc), it means we're looking at the
		 * one does not have a matching equivalent cpu entry, we fast
	 * container which has a patch for this CPU so return 0 to mean, @ucode
		 * forward to the next container file.
	 * 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.
	 */
	 */
		while (left > 0) {
	if (desc->mc) {
			header = (u32 *)data;
		desc->eq_id = eq_id;

		desc->data  = ucode;
			if (header[0] == UCODE_MAGIC &&
		desc->size  = orig_size - size;
			    header[1] == UCODE_EQUIV_CPU_TABLE_TYPE)
				break;


			offset = header[1] + SECTION_HDR_SIZE;
		return 0;
			data  += offset;
			left  -= offset;
	}
	}


		/* mark where the next microcode container file starts */
	return orig_size - size;
		offset    = data - (u8 *)ucode;
		ucode     = data;
}
}


	return eq_id;
/*
 * 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;
	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;
	this_equiv_id = desc.eq_id;
		if (header[0] != UCODE_UCODE_TYPE || /* type */
		    header[1] == 0)                  /* size */
			break;


		mc = (struct microcode_amd *)(data + SECTION_HDR_SIZE);
	mc = desc.mc;
	if (!mc)
		return ret;


		if (eq_id == mc->hdr.processor_rev_id && rev < mc->hdr.patch_id) {
	if (rev >= mc->hdr.patch_id)
		return ret;


	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;
		data   += offset;
		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)
		}
		}


		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;
			}
			}