The branch main has been updated by chs:

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

commit 34467bd762a77345915d07f4f0630c3fe0b53a23
Author:     Chuck Silvers <c...@freebsd.org>
AuthorDate: 2024-02-22 18:03:53 +0000
Commit:     Chuck Silvers <c...@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 <s...@freebsd.org>.
+ * All rights reserved.
+ * Copyright (c) 2012 Andriy Gapon <a...@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);

Reply via email to