git: 34467bd762a7 - main - x86/ucode: add support for early loading of CPU ucode on AMD.

From: Chuck Silvers <chs_at_FreeBSD.org>
Date: Thu, 22 Feb 2024 18:07:14 UTC
The branch main has been updated by chs:

URL: https://cgit.FreeBSD.org/src/commit/?id=34467bd762a77345915d07f4f0630c3fe0b53a23

commit 34467bd762a77345915d07f4f0630c3fe0b53a23
Author:     Chuck Silvers <chs@FreeBSD.org>
AuthorDate: 2024-02-22 18:03:53 +0000
Commit:     Chuck Silvers <chs@FreeBSD.org>
CommitDate: 2024-02-22 18:04:31 +0000

    x86/ucode: add support for early loading of CPU ucode on AMD.
    
    Sponsored by:   Netflix
    Reviewed by:    markj
    Differential Revision:  https://reviews.freebsd.org/D43318
---
 sys/conf/files.x86           |   1 +
 sys/x86/include/ucode.h      |  11 ++
 sys/x86/x86/ucode.c          |  55 ++++++++++
 sys/x86/x86/ucode_subr.c     | 239 +++++++++++++++++++++++++++++++++++++++++++
 usr.sbin/cpucontrol/Makefile |   6 +-
 usr.sbin/cpucontrol/amd.h    |  41 --------
 usr.sbin/cpucontrol/amd10h.c | 141 +------------------------
 7 files changed, 316 insertions(+), 178 deletions(-)

diff --git a/sys/conf/files.x86 b/sys/conf/files.x86
index 15781eea8fee..8bd58ab4d339 100644
--- a/sys/conf/files.x86
+++ b/sys/conf/files.x86
@@ -347,6 +347,7 @@ x86/x86/pvclock.c		optional	kvm_clock | xenhvm
 x86/x86/stack_machdep.c		optional	ddb | stack
 x86/x86/tsc.c			standard
 x86/x86/ucode.c			standard
+x86/x86/ucode_subr.c		standard
 x86/x86/delay.c			standard
 x86/xen/hvm.c			optional	xenhvm
 x86/xen/xen_apic.c		optional	xenhvm smp
diff --git a/sys/x86/include/ucode.h b/sys/x86/include/ucode.h
index e97d41c89ed0..0338d48a0832 100644
--- a/sys/x86/include/ucode.h
+++ b/sys/x86/include/ucode.h
@@ -31,6 +31,12 @@
 #ifndef _MACHINE_UCODE_H_
 #define	_MACHINE_UCODE_H_
 
+#ifdef _KERNEL
+#include <sys/types.h>
+#else
+#include <stdbool.h>
+#endif
+
 struct ucode_intel_header {
 	uint32_t	header_version;
 	int32_t		update_revision;
@@ -56,8 +62,13 @@ struct ucode_intel_extsig_table {
 	} entries[0];
 };
 
+const void *ucode_amd_find(const char *path, uint32_t signature,
+	    uint32_t revision, const uint8_t *fw_data, size_t fw_size,
+	    size_t *selected_sizep);
 int	ucode_intel_load(const void *data, bool unsafe,
 	    uint64_t *nrevp, uint64_t *orevp);
+int	ucode_amd_load(const void *data, bool unsafe,
+	    uint64_t *nrevp, uint64_t *orevp);
 size_t	ucode_load_bsp(uintptr_t free);
 void	ucode_load_ap(int cpu);
 void	ucode_reload(void);
diff --git a/sys/x86/x86/ucode.c b/sys/x86/x86/ucode.c
index 8e9f8e113c40..923d9e31b44e 100644
--- a/sys/x86/x86/ucode.c
+++ b/sys/x86/x86/ucode.c
@@ -54,6 +54,8 @@ static const void	*ucode_intel_match(const uint8_t *data, size_t *len);
 static int	ucode_intel_verify(const struct ucode_intel_header *hdr,
 		    size_t resid);
 
+static const void	*ucode_amd_match(const uint8_t *data, size_t *len);
+
 static struct ucode_ops {
 	const char *vendor;
 	int (*load)(const void *, bool, uint64_t *, uint64_t *);
@@ -64,6 +66,11 @@ static struct ucode_ops {
 		.load = ucode_intel_load,
 		.match = ucode_intel_match,
 	},
+	{
+		.vendor = AMD_VENDOR_ID,
+		.load = ucode_amd_load,
+		.match = ucode_amd_match,
+	},
 };
 
 /* Selected microcode update data. */
@@ -225,6 +232,54 @@ ucode_intel_match(const uint8_t *data, size_t *len)
 	return (NULL);
 }
 
+int
+ucode_amd_load(const void *data, bool unsafe, uint64_t *nrevp, uint64_t *orevp)
+{
+	uint64_t nrev, orev;
+	uint32_t cpuid[4];
+
+	orev = rdmsr(MSR_BIOS_SIGN);
+
+	/*
+	 * Perform update.
+	 */
+	if (unsafe)
+		wrmsr_safe(MSR_K8_UCODE_UPDATE, (uint64_t)(uintptr_t)data);
+	else
+		wrmsr(MSR_K8_UCODE_UPDATE, (uint64_t)(uintptr_t)data);
+
+	/*
+	 * Serialize instruction flow.
+	 */
+	do_cpuid(0, cpuid);
+
+	/*
+	 * Verify that the microcode revision changed.
+	 */
+	nrev = rdmsr(MSR_BIOS_SIGN);
+	if (nrevp != NULL)
+		*nrevp = nrev;
+	if (orevp != NULL)
+		*orevp = orev;
+	if (nrev <= orev)
+		return (EEXIST);
+	return (0);
+
+}
+
+static const void *
+ucode_amd_match(const uint8_t *data, size_t *len)
+{
+	uint32_t signature, revision;
+	uint32_t regs[4];
+
+	do_cpuid(1, regs);
+	signature = regs[0];
+	revision = rdmsr(MSR_BIOS_SIGN);
+
+	return (ucode_amd_find("loader blob", signature, revision, data, *len, len));
+}
+
 /*
  * Release any memory backing unused microcode blobs back to the system.
  * We copy the selected update and free the entire microcode file.
diff --git a/sys/x86/x86/ucode_subr.c b/sys/x86/x86/ucode_subr.c
new file mode 100644
index 000000000000..9e128ad2bf04
--- /dev/null
+++ b/sys/x86/x86/ucode_subr.c
@@ -0,0 +1,239 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2006, 2008 Stanislav Sedov <stas@FreeBSD.org>.
+ * All rights reserved.
+ * Copyright (c) 2012 Andriy Gapon <avg@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <x86/ucode.h>
+
+#ifndef _KERNEL
+#include <err.h>
+
+#include "cpucontrol.h"
+#endif
+
+
+#ifdef _KERNEL
+#define WARNX(level, ...)					\
+	if (bootverbose) {					\
+		printf(__VA_ARGS__);				\
+		printf("\n");					\
+	}
+#endif
+
+/*
+ * AMD family 10h and later.
+ */
+typedef struct amd_10h_fw_header {
+	uint32_t	data_code;
+	uint32_t	patch_id;
+	uint16_t	mc_patch_data_id;
+	uint8_t		mc_patch_data_len;
+	uint8_t		init_flag;
+	uint32_t	mc_patch_data_checksum;
+	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];
+	uint32_t	match_reg[8];
+} amd_10h_fw_header_t;
+
+typedef struct equiv_cpu_entry {
+	uint32_t	installed_cpu;
+	uint32_t	fixed_errata_mask;
+	uint32_t	fixed_errata_compare;
+	uint16_t	equiv_cpu;
+	uint16_t	res;
+} equiv_cpu_entry_t;
+
+typedef struct section_header {
+	uint32_t	type;
+	uint32_t	size;
+} section_header_t;
+
+typedef struct container_header {
+	uint32_t	magic;
+} container_header_t;
+
+#define	AMD_10H_MAGIC			0x414d44
+#define AMD_10H_EQUIV_TABLE_TYPE	0
+#define AMD_10H_uCODE_TYPE		1
+
+/*
+ * NB: the format of microcode update files is not documented by AMD.
+ * It has been reverse engineered from studying Coreboot, illumos and Linux
+ * source code.
+ */
+const void *
+ucode_amd_find(const char *path, uint32_t signature, uint32_t revision,
+    const uint8_t *fw_data, size_t fw_size, size_t *selected_sizep)
+{
+	const amd_10h_fw_header_t *fw_header;
+	const amd_10h_fw_header_t *selected_fw;
+	const equiv_cpu_entry_t *equiv_cpu_table;
+	const section_header_t *section_header;
+	const container_header_t *container_header;
+	size_t selected_size;
+	uint16_t equiv_id;
+	int i;
+
+	WARNX(1, "found cpu family %#x model %#x "
+	    "stepping %#x extfamily %#x extmodel %#x.",
+	    ((signature >> 8) & 0x0f) + ((signature >> 20) & 0xff),
+	    (signature >> 4) & 0x0f,
+	    (signature >> 0) & 0x0f, (signature >> 20) & 0xff,
+	    (signature >> 16) & 0x0f);
+	WARNX(1, "microcode revision %#x", revision);
+
+nextfile:
+	WARNX(1, "checking %s for update.", path);
+	WARNX(3, "processing next container file");
+	if (fw_size <
+	    (sizeof(*container_header) + sizeof(*section_header))) {
+		WARNX(2, "file too short: %s", path);
+		return (NULL);
+	}
+
+	container_header = (const container_header_t *)fw_data;
+	if (container_header->magic != AMD_10H_MAGIC) {
+		WARNX(2, "%s is not a valid amd firmware: bad magic", path);
+		return (NULL);
+	}
+	fw_data += sizeof(*container_header);
+	fw_size -= sizeof(*container_header);
+
+	section_header = (const section_header_t *)fw_data;
+	if (section_header->type != AMD_10H_EQUIV_TABLE_TYPE) {
+		WARNX(2, "%s is not a valid amd firmware: "
+		    "first section is not CPU equivalence table", path);
+		return (NULL);
+	}
+	if (section_header->size == 0) {
+		WARNX(2, "%s is not a valid amd firmware: "
+		    "first section is empty", path);
+		return (NULL);
+	}
+	fw_data += sizeof(*section_header);
+	fw_size -= sizeof(*section_header);
+
+	if (section_header->size > fw_size) {
+		WARNX(2, "%s is not a valid amd firmware: "
+		    "file is truncated", path);
+		return (NULL);
+	}
+	if (section_header->size < sizeof(*equiv_cpu_table)) {
+		WARNX(2, "%s is not a valid amd firmware: "
+		    "first section is too short", path);
+		return (NULL);
+	}
+	equiv_cpu_table = (const equiv_cpu_entry_t *)fw_data;
+	fw_data += section_header->size;
+	fw_size -= section_header->size;
+
+	equiv_id = 0;
+	for (i = 0; equiv_cpu_table[i].installed_cpu != 0; i++) {
+		WARNX(3, "signature 0x%x i %d installed_cpu 0x%x equiv 0x%x",
+		      signature, i, equiv_cpu_table[i].installed_cpu,
+		      equiv_cpu_table[i].equiv_cpu);
+		if (signature == equiv_cpu_table[i].installed_cpu) {
+			equiv_id = equiv_cpu_table[i].equiv_cpu;
+			WARNX(3, "equiv_id: %x, signature %8x,"
+			    " equiv_cpu_table[%d] %8x", equiv_id, signature,
+			    i, equiv_cpu_table[i].installed_cpu);
+			break;
+		}
+	}
+	if (equiv_id == 0) {
+		WARNX(2, "CPU is not found in the equivalence table");
+	}
+
+	while (fw_size >= sizeof(*section_header)) {
+		section_header = (const section_header_t *)fw_data;
+		if (section_header->type == AMD_10H_MAGIC) {
+			WARNX(2, "%s next section is actually a new container",
+			    path);
+			if (selected_fw != NULL)
+				goto found;
+			else
+				goto nextfile;
+		}
+		fw_data += sizeof(*section_header);
+		fw_size -= sizeof(*section_header);
+		if (section_header->type != AMD_10H_uCODE_TYPE) {
+			WARNX(2, "%s is not a valid amd firmware: "
+			    "section has incorrect type", path);
+			break;
+		}
+		if (section_header->size > fw_size) {
+			WARNX(2, "%s is not a valid amd firmware: "
+			    "file is truncated", path);
+			break;
+		}
+		if (section_header->size < sizeof(*fw_header)) {
+			WARNX(2, "%s is not a valid amd firmware: "
+			    "section is too short", path);
+			break;
+		}
+		fw_header = (const amd_10h_fw_header_t *)fw_data;
+		fw_data += section_header->size;
+		fw_size -= section_header->size;
+
+		if (fw_header->processor_rev_id != equiv_id) {
+			WARNX(1, "firmware processor_rev_id %x, equiv_id %x",
+			    fw_header->processor_rev_id, equiv_id);
+			continue; /* different cpu */
+		}
+		if (fw_header->patch_id <= revision) {
+			WARNX(1, "patch_id %x, revision %x",
+			    fw_header->patch_id, revision);
+			continue; /* not newer revision */
+		}
+		if (fw_header->nb_dev_id != 0 || fw_header->sb_dev_id != 0) {
+			WARNX(2, "Chipset-specific microcode is not supported");
+		}
+
+		WARNX(3, "selecting revision: %x", fw_header->patch_id);
+		revision = fw_header->patch_id;
+		selected_fw = fw_header;
+		selected_size = section_header->size;
+	}
+
+	if (fw_size != 0) {
+		WARNX(2, "%s is not a valid amd firmware: "
+		    "file is truncated", path);
+		selected_fw = NULL;
+	}
+
+found:
+	*selected_sizep = selected_size;
+	return (selected_fw);
+}
diff --git a/usr.sbin/cpucontrol/Makefile b/usr.sbin/cpucontrol/Makefile
index 41f573d02cc2..a468ca59b461 100644
--- a/usr.sbin/cpucontrol/Makefile
+++ b/usr.sbin/cpucontrol/Makefile
@@ -1,8 +1,12 @@
 
 PROG=	cpucontrol
 MAN=	cpucontrol.8
-SRCS=	cpucontrol.c intel.c amd.c amd10h.c via.c
+SRCS=	cpucontrol.c intel.c amd.c amd10h.c via.c ucode_subr.c
+
+.PATH:  ${SRCTOP}/sys/x86/x86
 
 NO_WCAST_ALIGN=
 
+CFLAGS+= -I${.CURDIR}
+
 .include <bsd.prog.mk>
diff --git a/usr.sbin/cpucontrol/amd.h b/usr.sbin/cpucontrol/amd.h
index 7e53048a81d7..8e4efe60dca6 100644
--- a/usr.sbin/cpucontrol/amd.h
+++ b/usr.sbin/cpucontrol/amd.h
@@ -48,45 +48,4 @@ typedef struct amd_fw_header {
 
 #define	AMD_MAGIC	0xaaaaaa
 
-/*
- * AMD family 10h and later.
- */
-typedef struct amd_10h_fw_header {
-	uint32_t	data_code;
-	uint32_t	patch_id;
-	uint16_t	mc_patch_data_id;
-	uint8_t		mc_patch_data_len;
-	uint8_t		init_flag;
-	uint32_t	mc_patch_data_checksum;
-	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];
-	uint32_t	match_reg[8];
-} amd_10h_fw_header_t;
-
-typedef struct equiv_cpu_entry {
-	uint32_t	installed_cpu;
-	uint32_t	fixed_errata_mask;
-	uint32_t	fixed_errata_compare;
-	uint16_t	equiv_cpu;
-	uint16_t	res;
-} equiv_cpu_entry_t;
-
-typedef struct section_header {
-	uint32_t	type;
-	uint32_t	size;
-} section_header_t;
-
-typedef struct container_header {
-	uint32_t	magic;
-} container_header_t;
-
-#define	AMD_10H_MAGIC			0x414d44
-#define AMD_10H_EQUIV_TABLE_TYPE	0
-#define AMD_10H_uCODE_TYPE		1
-
 #endif /* !AMD_H */
diff --git a/usr.sbin/cpucontrol/amd10h.c b/usr.sbin/cpucontrol/amd10h.c
index aceb6bfa2ba5..4fda44f0b797 100644
--- a/usr.sbin/cpucontrol/amd10h.c
+++ b/usr.sbin/cpucontrol/amd10h.c
@@ -33,6 +33,8 @@
 #include <machine/cpufunc.h>
 #include <machine/specialreg.h>
 
+#include <x86/ucode.h>
+
 #include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -79,33 +81,21 @@ amd10h_probe(int fd)
 	return (0);
 }
 
-/*
- * NB: the format of microcode update files is not documented by AMD.
- * It has been reverse engineered from studying Coreboot, illumos and Linux
- * source code.
- */
 void
 amd10h_update(const struct ucode_update_params *params)
 {
 	cpuctl_cpuid_args_t idargs;
 	cpuctl_msr_args_t msrargs;
 	cpuctl_update_args_t args;
-	const amd_10h_fw_header_t *fw_header;
-	const amd_10h_fw_header_t *selected_fw;
-	const equiv_cpu_entry_t *equiv_cpu_table;
-	const section_header_t *section_header;
-	const container_header_t *container_header;
-	const uint8_t *fw_data;
 	const uint8_t *fw_image;
 	const char *dev, *path;
+	const void *selected_fw;
 	size_t fw_size;
 	size_t selected_size;
 	uint32_t revision;
 	uint32_t new_rev;
 	uint32_t signature;
-	uint16_t equiv_id;
 	int devfd;
-	unsigned int i;
 	int error;
 
 	dev = params->dev_path;
@@ -133,129 +123,8 @@ amd10h_update(const struct ucode_update_params *params)
 	}
 	revision = (uint32_t)msrargs.data;
 
-	WARNX(1, "found cpu family %#x model %#x "
-	    "stepping %#x extfamily %#x extmodel %#x.",
-	    ((signature >> 8) & 0x0f) + ((signature >> 20) & 0xff),
-	    (signature >> 4) & 0x0f,
-	    (signature >> 0) & 0x0f, (signature >> 20) & 0xff,
-	    (signature >> 16) & 0x0f);
-	WARNX(1, "microcode revision %#x", revision);
-
-	/*
-	 * Open the firmware file.
-	 */
-	WARNX(1, "checking %s for update.", path);
-	if (fw_size <
-	    (sizeof(*container_header) + sizeof(*section_header))) {
-		WARNX(2, "file too short: %s", path);
-		goto done;
-	}
-
-	/*
-	 * mmap the whole image.
-	 */
-	fw_data = fw_image;
-	container_header = (const container_header_t *)fw_data;
-	if (container_header->magic != AMD_10H_MAGIC) {
-		WARNX(2, "%s is not a valid amd firmware: bad magic", path);
-		goto done;
-	}
-	fw_data += sizeof(*container_header);
-	fw_size -= sizeof(*container_header);
-
-	section_header = (const section_header_t *)fw_data;
-	if (section_header->type != AMD_10H_EQUIV_TABLE_TYPE) {
-		WARNX(2, "%s is not a valid amd firmware: "
-		    "first section is not CPU equivalence table", path);
-		goto done;
-	}
-	if (section_header->size == 0) {
-		WARNX(2, "%s is not a valid amd firmware: "
-		    "first section is empty", path);
-		goto done;
-	}
-	fw_data += sizeof(*section_header);
-	fw_size -= sizeof(*section_header);
-
-	if (section_header->size > fw_size) {
-		WARNX(2, "%s is not a valid amd firmware: "
-		    "file is truncated", path);
-		goto done;
-	}
-	if (section_header->size < sizeof(*equiv_cpu_table)) {
-		WARNX(2, "%s is not a valid amd firmware: "
-		    "first section is too short", path);
-		goto done;
-	}
-	equiv_cpu_table = (const equiv_cpu_entry_t *)fw_data;
-	fw_data += section_header->size;
-	fw_size -= section_header->size;
-
-	equiv_id = 0;
-	for (i = 0; equiv_cpu_table[i].installed_cpu != 0; i++) {
-		if (signature == equiv_cpu_table[i].installed_cpu) {
-			equiv_id = equiv_cpu_table[i].equiv_cpu;
-			WARNX(3, "equiv_id: %x, signature %8x,"
-			    " equiv_cpu_table[%d] %8x", equiv_id, signature,
-			    i, equiv_cpu_table[i].installed_cpu);
-			break;
-		}
-	}
-	if (equiv_id == 0) {
-		WARNX(2, "CPU is not found in the equivalence table");
-		goto done;
-	}
-
-	selected_fw = NULL;
-	selected_size = 0;
-	while (fw_size >= sizeof(*section_header)) {
-		section_header = (const section_header_t *)fw_data;
-		fw_data += sizeof(*section_header);
-		fw_size -= sizeof(*section_header);
-		if (section_header->type != AMD_10H_uCODE_TYPE) {
-			WARNX(2, "%s is not a valid amd firmware: "
-			    "section has incorrect type", path);
-			goto done;
-		}
-		if (section_header->size > fw_size) {
-			WARNX(2, "%s is not a valid amd firmware: "
-			    "file is truncated", path);
-			goto done;
-		}
-		if (section_header->size < sizeof(*fw_header)) {
-			WARNX(2, "%s is not a valid amd firmware: "
-			    "section is too short", path);
-			goto done;
-		}
-		fw_header = (const amd_10h_fw_header_t *)fw_data;
-		fw_data += section_header->size;
-		fw_size -= section_header->size;
-
-		if (fw_header->processor_rev_id != equiv_id) {
-			WARNX(1, "firmware processor_rev_id %x, equiv_id %x",
-			    fw_header->processor_rev_id, equiv_id);
-			continue; /* different cpu */
-		}
-		if (fw_header->patch_id <= revision) {
-			WARNX(1, "patch_id %x, revision %x",
-			    fw_header->patch_id, revision);
-			continue; /* not newer revision */
-		}
-		if (fw_header->nb_dev_id != 0 || fw_header->sb_dev_id != 0) {
-			WARNX(2, "Chipset-specific microcode is not supported");
-		}
-
-		WARNX(3, "selecting revision: %x", fw_header->patch_id);
-		revision = fw_header->patch_id;
-		selected_fw = fw_header;
-		selected_size = section_header->size;
-	}
-
-	if (fw_size != 0) {
-		WARNX(2, "%s is not a valid amd firmware: "
-		    "file is truncated", path);
-		goto done;
-	}
+	selected_fw = ucode_amd_find(path, signature, revision, fw_image,
+	    fw_size, &selected_size);
 
 	if (selected_fw != NULL) {
 		WARNX(1, "selected ucode size is %zu", selected_size);