summaryrefslogtreecommitdiff
path: root/usr/src/uts/i86pc/os/microcode.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/i86pc/os/microcode.c')
-rw-r--r--usr/src/uts/i86pc/os/microcode.c317
1 files changed, 166 insertions, 151 deletions
diff --git a/usr/src/uts/i86pc/os/microcode.c b/usr/src/uts/i86pc/os/microcode.c
index cbcecadbef..3e2e1a260d 100644
--- a/usr/src/uts/i86pc/os/microcode.c
+++ b/usr/src/uts/i86pc/os/microcode.c
@@ -26,6 +26,7 @@
* Copyright 2012 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2018, Joyent, Inc.
* Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
+ * Copyright 2022 Oxide Computer Company
*/
#include <sys/asm_linkage.h>
@@ -56,6 +57,7 @@
* AMD-specific equivalence table
*/
static ucode_eqtbl_amd_t *ucode_eqtbl_amd;
+static uint_t ucode_eqtbl_amd_entries;
/*
* mcpu_ucode_info for the boot CPU. Statically allocated.
@@ -67,67 +69,60 @@ static ucode_file_t ucodefile;
static void* ucode_zalloc(processorid_t, size_t);
static void ucode_free(processorid_t, void *, size_t);
-static int ucode_capable_amd(cpu_t *);
+static uint32_t ucode_load_intel(ucode_file_t *, cpu_ucode_info_t *, cpu_t *);
static int ucode_capable_intel(cpu_t *);
+static ucode_errno_t ucode_extract_intel(ucode_update_t *, uint8_t *, int);
+static void ucode_file_reset_intel(ucode_file_t *, processorid_t);
+static ucode_errno_t ucode_locate_intel(cpu_t *, cpu_ucode_info_t *,
+ ucode_file_t *);
+static ucode_errno_t ucode_match_intel(int, cpu_ucode_info_t *,
+ ucode_header_intel_t *, ucode_ext_table_intel_t *);
+static void ucode_read_rev_intel(cpu_ucode_info_t *);
-static ucode_errno_t ucode_extract_amd(ucode_update_t *, uint8_t *, int);
-static ucode_errno_t ucode_extract_intel(ucode_update_t *, uint8_t *,
- int);
+static int ucode_capable_amd(cpu_t *);
+static ucode_errno_t ucode_extract_amd(ucode_update_t *, uint8_t *, int);
static void ucode_file_reset_amd(ucode_file_t *, processorid_t);
-static void ucode_file_reset_intel(ucode_file_t *, processorid_t);
-
static uint32_t ucode_load_amd(ucode_file_t *, cpu_ucode_info_t *, cpu_t *);
-static uint32_t ucode_load_intel(ucode_file_t *, cpu_ucode_info_t *, cpu_t *);
-
-#ifdef __xpv
-static void ucode_load_xpv(ucode_update_t *);
-static void ucode_chipset_amd(uint8_t *, int);
-#endif
-
static int ucode_equiv_cpu_amd(cpu_t *, uint16_t *);
-
static ucode_errno_t ucode_locate_amd(cpu_t *, cpu_ucode_info_t *,
ucode_file_t *);
-static ucode_errno_t ucode_locate_intel(cpu_t *, cpu_ucode_info_t *,
- ucode_file_t *);
-
#ifndef __xpv
static ucode_errno_t ucode_match_amd(uint16_t, cpu_ucode_info_t *,
ucode_file_amd_t *, int);
+#else
+static void ucode_load_xpv(ucode_update_t *);
+static void ucode_chipset_amd(uint8_t *, int);
#endif
-static ucode_errno_t ucode_match_intel(int, cpu_ucode_info_t *,
- ucode_header_intel_t *, ucode_ext_table_intel_t *);
-
static void ucode_read_rev_amd(cpu_ucode_info_t *);
-static void ucode_read_rev_intel(cpu_ucode_info_t *);
+
static const struct ucode_ops ucode_amd = {
- MSR_AMD_PATCHLOADER,
- ucode_capable_amd,
- ucode_file_reset_amd,
- ucode_read_rev_amd,
- ucode_load_amd,
- ucode_validate_amd,
- ucode_extract_amd,
- ucode_locate_amd
+ .write_msr = MSR_AMD_PATCHLOADER,
+ .capable = ucode_capable_amd,
+ .file_reset = ucode_file_reset_amd,
+ .read_rev = ucode_read_rev_amd,
+ .load = ucode_load_amd,
+ .validate = ucode_validate_amd,
+ .extract = ucode_extract_amd,
+ .locate = ucode_locate_amd
};
static const struct ucode_ops ucode_intel = {
- MSR_INTC_UCODE_WRITE,
- ucode_capable_intel,
- ucode_file_reset_intel,
- ucode_read_rev_intel,
- ucode_load_intel,
- ucode_validate_intel,
- ucode_extract_intel,
- ucode_locate_intel
+ .write_msr = MSR_INTC_UCODE_WRITE,
+ .capable = ucode_capable_intel,
+ .file_reset = ucode_file_reset_intel,
+ .read_rev = ucode_read_rev_intel,
+ .load = ucode_load_intel,
+ .validate = ucode_validate_intel,
+ .extract = ucode_extract_intel,
+ .locate = ucode_locate_intel
};
const struct ucode_ops *ucode;
static const char ucode_failure_fmt[] =
- "cpu%d: failed to update microcode from version 0x%x to 0x%x\n";
+ "cpu%d: failed to update microcode from version 0x%x to 0x%x";
static const char ucode_success_fmt[] =
"?cpu%d: microcode has been updated from version 0x%x to 0x%x\n";
@@ -181,7 +176,7 @@ ucode_cleanup()
static void*
ucode_zalloc(processorid_t id, size_t size)
{
- if (id)
+ if (id != 0)
return (kmem_zalloc(size, KM_NOSLEEP));
/* BOP_ALLOC() failure results in panic */
@@ -191,7 +186,7 @@ ucode_zalloc(processorid_t id, size_t size)
static void
ucode_free(processorid_t id, void* buf, size_t size)
{
- if (id)
+ if (id != 0)
kmem_free(buf, size);
}
@@ -212,7 +207,6 @@ ucode_free(processorid_t id, void* buf, size_t size)
#define XPVDOMU_OR_HVM \
((hwenv == HW_XEN_PV && !is_controldom()) || (hwenv & HW_VIRTUAL) != 0)
-/*ARGSUSED*/
static int
ucode_capable_amd(cpu_t *cp)
{
@@ -285,76 +279,94 @@ static int
ucode_equiv_cpu_amd(cpu_t *cp, uint16_t *eq_sig)
{
char name[MAXPATHLEN];
- intptr_t fd;
- int count;
- int offset = 0, cpi_sig = cpuid_getsig(cp);
- ucode_eqtbl_amd_t *eqtbl = ucode_eqtbl_amd;
+ int cpi_sig = cpuid_getsig(cp);
(void) snprintf(name, MAXPATHLEN, "/%s/%s/equivalence-table",
UCODE_INSTALL_PATH, cpuid_getvendorstr(cp));
- /*
- * No kmem_zalloc() etc. available on boot cpu.
- */
if (cp->cpu_id == 0) {
+ /*
+ * No kmem_zalloc() etc. available on boot cpu.
+ */
+ ucode_eqtbl_amd_t eqtbl;
+ int count, offset = 0;
+ intptr_t fd;
+
if ((fd = kobj_open(name)) == -1)
return (EM_OPENFILE);
- /* ucode_zalloc() cannot fail on boot cpu */
- eqtbl = ucode_zalloc(cp->cpu_id, sizeof (*eqtbl));
- ASSERT(eqtbl);
do {
- count = kobj_read(fd, (int8_t *)eqtbl,
- sizeof (*eqtbl), offset);
- if (count != sizeof (*eqtbl)) {
+ count = kobj_read(fd, (int8_t *)&eqtbl,
+ sizeof (eqtbl), offset);
+ if (count != sizeof (eqtbl)) {
(void) kobj_close(fd);
return (EM_HIGHERREV);
}
offset += count;
- } while (eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != cpi_sig);
+ } while (eqtbl.ue_inst_cpu != 0 &&
+ eqtbl.ue_inst_cpu != cpi_sig);
(void) kobj_close(fd);
- }
-
- /*
- * If not already done, load the equivalence table.
- * Not done on boot CPU.
- */
- if (eqtbl == NULL) {
- struct _buf *eq;
- uint64_t size;
+ *eq_sig = eqtbl.ue_equiv_cpu;
+ } else {
+ ucode_eqtbl_amd_t *eqtbl;
- if ((eq = kobj_open_file(name)) == (struct _buf *)-1)
- return (EM_OPENFILE);
+ /*
+ * If not already done, load the equivalence table.
+ * Not done on boot CPU.
+ */
+ if (ucode_eqtbl_amd == NULL) {
+ struct _buf *eq;
+ uint64_t size;
+ int count;
- if (kobj_get_filesize(eq, &size) < 0) {
- kobj_close_file(eq);
- return (EM_OPENFILE);
- }
+ if ((eq = kobj_open_file(name)) == (struct _buf *)-1)
+ return (EM_OPENFILE);
- ucode_eqtbl_amd = kmem_zalloc(size, KM_NOSLEEP);
- if (ucode_eqtbl_amd == NULL) {
- kobj_close_file(eq);
- return (EM_NOMEM);
- }
+ if (kobj_get_filesize(eq, &size) < 0) {
+ kobj_close_file(eq);
+ return (EM_OPENFILE);
+ }
- count = kobj_read_file(eq, (char *)ucode_eqtbl_amd, size, 0);
- kobj_close_file(eq);
+ if (size == 0 ||
+ size % sizeof (*ucode_eqtbl_amd) != 0) {
+ kobj_close_file(eq);
+ return (EM_HIGHERREV);
+ }
- if (count != size)
- return (EM_FILESIZE);
- }
+ ucode_eqtbl_amd = kmem_zalloc(size, KM_NOSLEEP);
+ if (ucode_eqtbl_amd == NULL) {
+ kobj_close_file(eq);
+ return (EM_NOMEM);
+ }
+ count = kobj_read_file(eq, (char *)ucode_eqtbl_amd,
+ size, 0);
+ kobj_close_file(eq);
- /* Get the equivalent CPU id. */
- if (cp->cpu_id)
- for (eqtbl = ucode_eqtbl_amd;
- eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != cpi_sig;
- eqtbl++)
- ;
+ if (count != size) {
+ ucode_eqtbl_amd_entries = 0;
+ return (EM_FILESIZE);
+ }
- *eq_sig = eqtbl->ue_equiv_cpu;
+ ucode_eqtbl_amd_entries =
+ size / sizeof (*ucode_eqtbl_amd);
+ }
- /* No equivalent CPU id found, assume outdated microcode file. */
- if (*eq_sig == 0)
+ eqtbl = ucode_eqtbl_amd;
+ *eq_sig = 0;
+ for (uint_t i = 0; i < ucode_eqtbl_amd_entries; i++, eqtbl++) {
+ if (eqtbl->ue_inst_cpu == 0) {
+ /* End of table */
+ return (EM_HIGHERREV);
+ }
+ if (eqtbl->ue_inst_cpu == cpi_sig) {
+ *eq_sig = eqtbl->ue_equiv_cpu;
+ return (EM_OK);
+ }
+ }
+ /*
+ * No equivalent CPU id found, assume outdated microcode file.
+ */
return (EM_HIGHERREV);
+ }
return (EM_OK);
}
@@ -403,7 +415,6 @@ ucode_chipset_amd(uint8_t *buf, int size)
*
* Return EM_OK on success, corresponding error code on failure.
*/
-/*ARGSUSED*/
static ucode_errno_t
ucode_locate_amd(cpu_t *cp, cpu_ucode_info_t *uinfop, ucode_file_t *ufp)
{
@@ -414,7 +425,6 @@ ucode_locate_amd(cpu_t *cp, cpu_ucode_info_t *uinfop, ucode_file_t *ufp)
#ifndef __xpv
uint16_t eq_sig = 0;
- int i;
/* get equivalent CPU id */
if ((rc = ucode_equiv_cpu_amd(cp, &eq_sig)) != EM_OK)
@@ -425,11 +435,13 @@ ucode_locate_amd(cpu_t *cp, cpu_ucode_info_t *uinfop, ucode_file_t *ufp)
* allocated before, check for a matching microcode to avoid loading
* the file again.
*/
- if (ucodefp == NULL)
+
+ if (ucodefp == NULL) {
ucodefp = ucode_zalloc(cp->cpu_id, sizeof (*ucodefp));
- else if (ucode_match_amd(eq_sig, uinfop, ucodefp, sizeof (*ucodefp))
- == EM_OK)
+ } else if (ucode_match_amd(eq_sig, uinfop, ucodefp, sizeof (*ucodefp))
+ == EM_OK) {
return (EM_OK);
+ }
if (ucodefp == NULL)
return (EM_NOMEM);
@@ -444,7 +456,7 @@ ucode_locate_amd(cpu_t *cp, cpu_ucode_info_t *uinfop, ucode_file_t *ufp)
* patch that matches.
*/
- for (i = 0; i < 0xff; i++) {
+ for (uint_t i = 0; i < 0xff; i++) {
(void) snprintf(name, MAXPATHLEN, "/%s/%s/%04X-%02X",
UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), eq_sig, i);
if ((fd = kobj_open(name)) == -1)
@@ -663,8 +675,9 @@ ucode_match_amd(uint16_t eq_sig, cpu_ucode_info_t *uinfop,
*/
if (uh->uh_cpu_rev < 0x5000 &&
size > offsetof(ucode_file_amd_t, uf_code_present) &&
- ucodefp->uf_code_present)
+ ucodefp->uf_code_present) {
return (EM_NOMATCH);
+ }
if (eq_sig != uh->uh_cpu_rev)
return (EM_NOMATCH);
@@ -730,51 +743,6 @@ ucode_match_intel(int cpi_sig, cpu_ucode_info_t *uinfop,
return (EM_NOMATCH);
}
-/*ARGSUSED*/
-static int
-ucode_write(xc_arg_t arg1, xc_arg_t unused2, xc_arg_t unused3)
-{
- ucode_update_t *uusp = (ucode_update_t *)arg1;
- cpu_ucode_info_t *uinfop = CPU->cpu_m.mcpu_ucode_info;
-#ifndef __xpv
- on_trap_data_t otd;
-#endif
-
- ASSERT(ucode);
- ASSERT(uusp->ucodep);
-
-#ifndef __xpv
- /*
- * Check one more time to see if it is really necessary to update
- * microcode just in case this is a hyperthreaded processor where
- * the threads share the same microcode.
- */
- if (!ucode_force_update) {
- ucode->read_rev(uinfop);
- uusp->new_rev = uinfop->cui_rev;
- if (uinfop->cui_rev >= uusp->expected_rev)
- return (0);
- }
-
- if (!on_trap(&otd, OT_DATA_ACCESS)) {
- /*
- * On some platforms a cache invalidation is required for the
- * ucode update to be successful due to the parts of the
- * processor that the microcode is updating.
- */
- invalidate_cache();
- wrmsr(ucode->write_msr, (uintptr_t)uusp->ucodep);
- }
-
- no_trap();
-#endif
- ucode->read_rev(uinfop);
- uusp->new_rev = uinfop->cui_rev;
-
- return (0);
-}
-
-/*ARGSUSED*/
static uint32_t
ucode_load_amd(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp)
{
@@ -792,14 +760,14 @@ ucode_load_amd(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp)
kpreempt_disable();
if (on_trap(&otd, OT_DATA_ACCESS)) {
no_trap();
- kpreempt_enable();
- return (0);
+ goto out;
}
wrmsr(ucode->write_msr, (uintptr_t)ucodefp);
no_trap();
ucode->read_rev(uinfop);
- kpreempt_enable();
+out:
+ kpreempt_enable();
return (ucodefp->uf_header.uh_patch_id);
#else
uus.ucodep = ucodefp->ucodep;
@@ -812,7 +780,6 @@ ucode_load_amd(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp)
#endif
}
-/*ARGSUSED2*/
static uint32_t
ucode_load_intel(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp)
{
@@ -1043,6 +1010,50 @@ ucode_extract_intel(ucode_update_t *uusp, uint8_t *ucodep, int size)
return (EM_OK);
}
+
+static int
+ucode_write(xc_arg_t arg1, xc_arg_t unused2, xc_arg_t unused3)
+{
+ ucode_update_t *uusp = (ucode_update_t *)arg1;
+ cpu_ucode_info_t *uinfop = CPU->cpu_m.mcpu_ucode_info;
+#ifndef __xpv
+ on_trap_data_t otd;
+#endif
+
+ ASSERT(ucode);
+ ASSERT(uusp->ucodep);
+
+#ifndef __xpv
+ /*
+ * Check one more time to see if it is really necessary to update
+ * microcode just in case this is a hyperthreaded processor where
+ * the threads share the same microcode.
+ */
+ if (!ucode_force_update) {
+ ucode->read_rev(uinfop);
+ uusp->new_rev = uinfop->cui_rev;
+ if (uinfop->cui_rev >= uusp->expected_rev)
+ return (0);
+ }
+
+ if (!on_trap(&otd, OT_DATA_ACCESS)) {
+ /*
+ * On some platforms a cache invalidation is required for the
+ * ucode update to be successful due to the parts of the
+ * processor that the microcode is updating.
+ */
+ invalidate_cache();
+ wrmsr(ucode->write_msr, (uintptr_t)uusp->ucodep);
+ }
+
+ no_trap();
+#endif
+ ucode->read_rev(uinfop);
+ uusp->new_rev = uinfop->cui_rev;
+
+ return (0);
+}
+
/*
* Entry point to microcode update from the ucode_drv driver.
*
@@ -1156,10 +1167,8 @@ ucode_update(uint8_t *ucodep, int size)
}
/*
+ * Entry point to microcode update from mlsetup() and mp_startup()
* Initialize mcpu_ucode_info, and perform microcode update if necessary.
- * This is the entry point from boot path where pointer to CPU structure
- * is available.
- *
* cpuid_info must be initialized before ucode_check can be called.
*/
void
@@ -1167,7 +1176,6 @@ ucode_check(cpu_t *cp)
{
cpu_ucode_info_t *uinfop;
ucode_errno_t rc = EM_OK;
- uint32_t new_rev = 0;
ASSERT(cp);
/*
@@ -1222,11 +1230,18 @@ ucode_check(cpu_t *cp)
* Check to see if we need ucode update
*/
if ((rc = ucode->locate(cp, uinfop, &ucodefile)) == EM_OK) {
+ uint32_t old_rev, new_rev;
+
+ old_rev = uinfop->cui_rev;
new_rev = ucode->load(&ucodefile, uinfop, cp);
- if (uinfop->cui_rev != new_rev)
+ if (uinfop->cui_rev != new_rev) {
cmn_err(CE_WARN, ucode_failure_fmt, cp->cpu_id,
- uinfop->cui_rev, new_rev);
+ old_rev, new_rev);
+ } else {
+ cmn_err(CE_CONT, ucode_success_fmt, cp->cpu_id,
+ old_rev, new_rev);
+ }
}
/*