summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorMark Johnson <Mark.Johnson@Sun.COM>2009-01-28 17:49:05 -0800
committerMark Johnson <Mark.Johnson@Sun.COM>2009-01-28 17:49:05 -0800
commit0ba6f73dcd1c9160a44b2872ed0e0d81b51d168f (patch)
tree3196b1b2966525867879d370a13c21371e1041fa /usr/src
parent4be06494508a1d70b3f0065f16685a74b355cedd (diff)
downloadillumos-gate-0ba6f73dcd1c9160a44b2872ed0e0d81b51d168f.tar.gz
6798170 AMD uCode 2009-01-20 update
Contributed by Hans Rosenfeld <hans.rosenfeld@amd.com>
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/ucodeadm/Makefile10
-rw-r--r--usr/src/cmd/ucodeadm/amd-ucode.binbin0 -> 2012 bytes
-rw-r--r--usr/src/cmd/ucodeadm/ucodeadm.c24
-rw-r--r--usr/src/pkgdefs/SUNWcakr.i/prototype_com1
-rw-r--r--usr/src/uts/common/sys/ucode.h10
-rw-r--r--usr/src/uts/i86pc/os/microcode.c175
6 files changed, 182 insertions, 38 deletions
diff --git a/usr/src/cmd/ucodeadm/Makefile b/usr/src/cmd/ucodeadm/Makefile
index e5786689ef..a7e2d7243b 100644
--- a/usr/src/cmd/ucodeadm/Makefile
+++ b/usr/src/cmd/ucodeadm/Makefile
@@ -19,11 +19,9 @@
# CDDL HEADER END
#
#
-# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# ident "%Z%%M% %I% %E% SMI"
-#
PROG = ucodeadm
@@ -40,10 +38,11 @@ POFILE = ucodeadm_all.po
POFILES = $(PROG_OBJS:%.o=%.po)
INTEL_UCODE_FILE = intel-ucode.txt
-AMD_UCODE_FILE = amd-ucode.txt
+AMD_UCODE_FILE = amd-ucode.bin
ROOTUCODEPATH = $(ROOT)/platform/i86pc/ucode
ROOTINTELUCODE = $(INTEL_UCODE_FILE:%=$(ROOTUCODEPATH)/%)
+ROOTAMDUCODE = $(AMD_UCODE_FILE:%=$(ROOTUCODEPATH)/%)
CPPFLAGS = -I../../common -I../../uts/common
CFLAGS += -v
@@ -57,6 +56,7 @@ OWNER = root
GROUP = sys
$(ROOTINTELUCODE) := FILEMODE = 0444
+$(ROOTAMDUCODE) := FILEMODE = 0444
install := TARGET = install
clobber := TARGET = clobber
@@ -67,7 +67,7 @@ CLEANFILES += $(PROG) $(OBJS) ucode_errno.c $(POFILES) $(POFILE)
all: $(PROG)
-install: all $(ROOTUSRSBINPROG) $(ROOTUCODEPATH) $(ROOTINTELUCODE)
+install: all $(ROOTUSRSBINPROG) $(ROOTUCODEPATH) $(ROOTINTELUCODE) $(ROOTAMDUCODE)
_msg: ucodeadm_all.po
diff --git a/usr/src/cmd/ucodeadm/amd-ucode.bin b/usr/src/cmd/ucodeadm/amd-ucode.bin
new file mode 100644
index 0000000000..f76a056a7a
--- /dev/null
+++ b/usr/src/cmd/ucodeadm/amd-ucode.bin
Binary files differ
diff --git a/usr/src/cmd/ucodeadm/ucodeadm.c b/usr/src/cmd/ucodeadm/ucodeadm.c
index 765099dd09..b4544cb1af 100644
--- a/usr/src/cmd/ucodeadm/ucodeadm.c
+++ b/usr/src/cmd/ucodeadm/ucodeadm.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -242,13 +242,33 @@ ucode_gen_files_amd(uint8_t *buf, int size, char *path)
ucode_header_amd_t *uh;
int last_cpu_rev = 0;
+ /* write container file */
+ (void) snprintf(path + plen, PATH_MAX - plen, "/container");
+
+ dprintf("path = %s\n", path);
+ fd = open(path, O_WRONLY | O_CREAT | O_TRUNC,
+ S_IRUSR | S_IRGRP | S_IROTH);
+
+ if (fd == -1) {
+ ucode_perror(path, EM_SYS);
+ return (EM_SYS);
+ }
+
+ if (write(fd, buf, size) != size) {
+ (void) close(fd);
+ ucode_perror(path, EM_SYS);
+ return (EM_SYS);
+ }
+
+ (void) close(fd);
+
/* skip over magic number & equivalence table header */
ptr += 2; size -= 8;
count = *ptr++; size -= 4;
/* equivalence table uses special name */
- (void) strlcat(path, "/equivalence-table", PATH_MAX);
+ (void) snprintf(path + plen, PATH_MAX - plen, "/equivalence-table");
for (;;) {
dprintf("path = %s\n", path);
diff --git a/usr/src/pkgdefs/SUNWcakr.i/prototype_com b/usr/src/pkgdefs/SUNWcakr.i/prototype_com
index 37a6b6ed88..706397ef42 100644
--- a/usr/src/pkgdefs/SUNWcakr.i/prototype_com
+++ b/usr/src/pkgdefs/SUNWcakr.i/prototype_com
@@ -98,5 +98,6 @@ f none platform/i86pc/kernel/misc/pcie 755 root sys
f none platform/i86pc/kernel/unix 755 root sys
f none platform/i86pc/multiboot 755 root sys
d none platform/i86pc/ucode 755 root sys
+v none platform/i86pc/ucode/amd-ucode.bin 444 root sys
v none platform/i86pc/ucode/intel-ucode.txt 444 root sys
f none platform/i86pc/kernel/drv/amd64/cpudrv 755 root sys
diff --git a/usr/src/uts/common/sys/ucode.h b/usr/src/uts/common/sys/ucode.h
index 0f37745cae..e4f01bfeb0 100644
--- a/usr/src/uts/common/sys/ucode.h
+++ b/usr/src/uts/common/sys/ucode.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -110,18 +110,24 @@ typedef struct ucode_header_amd {
} ucode_header_amd_t;
typedef struct ucode_file_amd {
+#ifndef __xpv
ucode_header_amd_t uf_header;
uint8_t uf_data[896];
uint8_t uf_resv[896];
uint8_t uf_code_present;
uint8_t uf_code[191];
+#else
+ uint8_t *ucodep;
+ uint32_t usize;
+#endif
} ucode_file_amd_t;
typedef struct ucode_eqtbl_amd {
uint32_t ue_inst_cpu;
uint32_t ue_fixed_mask;
uint32_t ue_fixed_comp;
- uint32_t ue_equiv_cpu;
+ uint16_t ue_equiv_cpu;
+ uint16_t ue_reserved;
} ucode_eqtbl_amd_t;
/*
diff --git a/usr/src/uts/i86pc/os/microcode.c b/usr/src/uts/i86pc/os/microcode.c
index 7dd2ef2e55..8cf5d99d24 100644
--- a/usr/src/uts/i86pc/os/microcode.c
+++ b/usr/src/uts/i86pc/os/microcode.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -77,17 +77,20 @@ 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 *, int *);
+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 *);
-static ucode_errno_t ucode_match_amd(int, cpu_ucode_info_t *,
+#ifndef __xpv
+static ucode_errno_t ucode_match_amd(uint16_t, cpu_ucode_info_t *,
ucode_file_amd_t *, int);
+#endif
static ucode_errno_t ucode_match_intel(int, cpu_ucode_info_t *,
ucode_header_intel_t *, ucode_ext_table_intel_t *);
@@ -205,20 +208,14 @@ ucode_capable_amd(cpu_t *cp)
if (xpv_is_hvm) {
return (0);
}
-
- return (cpuid_getfamily(cp) >= 0x10);
#else
if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
return (0);
}
-
- /*
- * XXPV - change when microcode loading works in dom0. Don't support
- * microcode loading in dom0 right now for AMD.
- */
- return (0);
#endif
+ return (cpuid_getfamily(cp) >= 0x10);
}
+
static int
ucode_capable_intel(cpu_t *cp)
{
@@ -282,7 +279,7 @@ ucode_file_reset_intel(ucode_file_t *ufp, processorid_t id)
* Find the equivalent CPU id in the equivalence table.
*/
static int
-ucode_equiv_cpu_amd(cpu_t *cp, int *eq_sig)
+ucode_equiv_cpu_amd(cpu_t *cp, uint16_t *eq_sig)
{
char name[MAXPATHLEN];
intptr_t fd;
@@ -351,7 +348,6 @@ ucode_equiv_cpu_amd(cpu_t *cp, int *eq_sig)
;
*eq_sig = eqtbl->ue_equiv_cpu;
- *eq_sig = ((*eq_sig >> 8) & 0xff00) | (*eq_sig & 0xff);
/* No equivalent CPU id found, assume outdated microcode file. */
if (*eq_sig == 0)
@@ -361,20 +357,62 @@ ucode_equiv_cpu_amd(cpu_t *cp, int *eq_sig)
}
/*
+ * xVM cannot check for the presence of PCI devices. Look for chipset-
+ * specific microcode patches in the container file and disable them
+ * by setting their CPU revision to an invalid value.
+ */
+#ifdef __xpv
+static void
+ucode_chipset_amd(uint8_t *buf, int size)
+{
+ ucode_header_amd_t *uh;
+ uint32_t *ptr = (uint32_t *)buf;
+ int len = 0;
+
+ /* skip to first microcode patch */
+ ptr += 2; len = *ptr++; ptr += len >> 2; size -= len;
+
+ while (size >= sizeof (ucode_header_amd_t) + 8) {
+ ptr++; len = *ptr++;
+ uh = (ucode_header_amd_t *)ptr;
+ ptr += len >> 2; size -= len;
+
+ if (uh->uh_nb_id) {
+ cmn_err(CE_WARN, "ignoring northbridge-specific ucode: "
+ "chipset id %x, revision %x",
+ uh->uh_nb_id, uh->uh_nb_rev);
+ uh->uh_cpu_rev = 0xffff;
+ }
+
+ if (uh->uh_sb_id) {
+ cmn_err(CE_WARN, "ignoring southbridge-specific ucode: "
+ "chipset id %x, revision %x",
+ uh->uh_sb_id, uh->uh_sb_rev);
+ uh->uh_cpu_rev = 0xffff;
+ }
+ }
+}
+#endif
+
+/*
* Populate the ucode file structure from microcode file corresponding to
* this CPU, if exists.
*
* 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)
{
char name[MAXPATHLEN];
intptr_t fd;
- int count, i, rc;
- int eq_sig = 0;
+ int count, rc;
ucode_file_amd_t *ucodefp = ufp->amd;
+#ifndef __xpv
+ uint16_t eq_sig = 0;
+ int i;
+
/* get equivalent CPU id */
if ((rc = ucode_equiv_cpu_amd(cp, &eq_sig)) != EM_OK)
return (rc);
@@ -415,6 +453,59 @@ ucode_locate_amd(cpu_t *cp, cpu_ucode_info_t *uinfop, ucode_file_t *ufp)
return (EM_OK);
}
return (EM_NOMATCH);
+#else
+ int size = 0;
+ char c;
+
+ /*
+ * The xVM case is special. To support mixed-revision systems, the
+ * hypervisor will choose which patch to load for which CPU, so the
+ * whole microcode patch container file will have to be loaded.
+ *
+ * Since this code is only run on the boot cpu, we don't have to care
+ * about failing ucode_zalloc() or freeing allocated memory.
+ */
+ if (cp->cpu_id != 0)
+ return (EM_INVALIDARG);
+
+ (void) snprintf(name, MAXPATHLEN, "/%s/%s/container",
+ UCODE_INSTALL_PATH, cpuid_getvendorstr(cp));
+
+ if ((fd = kobj_open(name)) == -1)
+ return (EM_OPENFILE);
+
+ /* get the file size by counting bytes */
+ do {
+ count = kobj_read(fd, &c, 1, size);
+ size += count;
+ } while (count);
+
+ ucodefp = ucode_zalloc(cp->cpu_id, sizeof (*ucodefp));
+ ASSERT(ucodefp);
+ ufp->amd = ucodefp;
+
+ ucodefp->usize = size;
+ ucodefp->ucodep = ucode_zalloc(cp->cpu_id, size);
+ ASSERT(ucodefp->ucodep);
+
+ /* load the microcode patch container file */
+ count = kobj_read(fd, (char *)ucodefp->ucodep, size, 0);
+ (void) kobj_close(fd);
+
+ if (count != size)
+ return (EM_FILESIZE);
+
+ /* make sure the container file is valid */
+ rc = ucode->validate(ucodefp->ucodep, ucodefp->usize);
+
+ if (rc != EM_OK)
+ return (rc);
+
+ /* disable chipset-specific patches */
+ ucode_chipset_amd(ucodefp->ucodep, ucodefp->usize);
+
+ return (EM_OK);
+#endif
}
static ucode_errno_t
@@ -551,9 +642,10 @@ ucode_locate_intel(cpu_t *cp, cpu_ucode_info_t *uinfop, ucode_file_t *ufp)
return (rc);
}
+#ifndef __xpv
static ucode_errno_t
-ucode_match_amd(int eq_sig, cpu_ucode_info_t *uinfop, ucode_file_amd_t *ucodefp,
- int size)
+ucode_match_amd(uint16_t eq_sig, cpu_ucode_info_t *uinfop,
+ ucode_file_amd_t *ucodefp, int size)
{
ucode_header_amd_t *uh;
@@ -590,6 +682,7 @@ ucode_match_amd(int eq_sig, cpu_ucode_info_t *uinfop, ucode_file_amd_t *ucodefp,
return (EM_OK);
}
+#endif
/*
* Returns 1 if the microcode is for this processor; 0 otherwise.
@@ -681,15 +774,17 @@ ucode_load_amd(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp)
wrmsr(ucode->write_msr, (uintptr_t)ucodefp);
ucode->read_rev(uinfop);
kpreempt_enable();
+
+ return (ucodefp->uf_header.uh_patch_id);
#else
- uus.ucodep = (uint8_t *)ucodefp;
- uus.usize = sizeof (*ucodefp);
+ uus.ucodep = ucodefp->ucodep;
+ uus.usize = ucodefp->usize;
ucode_load_xpv(&uus);
ucode->read_rev(uinfop);
uus.new_rev = uinfop->cui_rev;
-#endif
- return (ucodefp->uf_header.uh_patch_id);
+ return (uus.new_rev);
+#endif
}
/*ARGSUSED2*/
@@ -794,10 +889,14 @@ ucode_read_rev_intel(cpu_ucode_info_t *uinfop)
static ucode_errno_t
ucode_extract_amd(ucode_update_t *uusp, uint8_t *ucodep, int size)
{
+#ifndef __xpv
uint32_t *ptr = (uint32_t *)ucodep;
ucode_eqtbl_amd_t *eqtbl;
ucode_file_amd_t *ufp;
- int count, eq_sig;
+ int count;
+ int higher = 0;
+ ucode_errno_t rc = EM_NOMATCH;
+ uint16_t eq_sig;
/* skip over magic number & equivalence table header */
ptr += 2; size -= 8;
@@ -809,7 +908,6 @@ ucode_extract_amd(ucode_update_t *uusp, uint8_t *ucodep, int size)
;
eq_sig = eqtbl->ue_equiv_cpu;
- eq_sig = ((eq_sig >> 8) & 0xff00) | (eq_sig & 0xff);
/* No equivalent CPU id found, assume outdated microcode file. */
if (eq_sig == 0)
@@ -820,16 +918,32 @@ ucode_extract_amd(ucode_update_t *uusp, uint8_t *ucodep, int size)
ptr += count >> 2; size -= count;
if (!size)
- return (EM_NOMATCH);
+ return (higher ? EM_HIGHERREV : EM_NOMATCH);
ptr++; size -= 4;
count = *ptr++; size -= 4;
ufp = (ucode_file_amd_t *)ptr;
- } while (ucode_match_amd(eq_sig, &uusp->info, ufp, count) != EM_OK);
+
+ rc = ucode_match_amd(eq_sig, &uusp->info, ufp, count);
+ if (rc == EM_HIGHERREV)
+ higher = 1;
+ } while (rc != EM_OK);
uusp->ucodep = (uint8_t *)ufp;
uusp->usize = count;
uusp->expected_rev = ufp->uf_header.uh_patch_id;
+#else
+ /*
+ * The hypervisor will choose the patch to load, so there is no way to
+ * know the "expected revision" in advance. This is especially true on
+ * mixed-revision systems where more than one patch will be loaded.
+ */
+ uusp->expected_rev = 0;
+ uusp->ucodep = ucodep;
+ uusp->usize = size;
+
+ ucode_chipset_amd(ucodep, size);
+#endif
return (EM_OK);
}
@@ -986,13 +1100,16 @@ ucode_update(uint8_t *ucodep, int size)
kpreempt_enable();
CPUSET_DEL(cpuset, id);
- if (uusp->expected_rev == uusp->new_rev) {
- cmn_err(CE_CONT, ucode_success_fmt,
- id, uusp->info.cui_rev, uusp->expected_rev);
- } else {
+ if (uusp->new_rev != 0 && uusp->info.cui_rev == uusp->new_rev) {
+ rc = EM_HIGHERREV;
+ } else if ((uusp->new_rev == 0) || (uusp->expected_rev != 0 &&
+ uusp->expected_rev != uusp->new_rev)) {
cmn_err(CE_WARN, ucode_failure_fmt,
id, uusp->info.cui_rev, uusp->expected_rev);
rc = EM_UPDATE;
+ } else {
+ cmn_err(CE_CONT, ucode_success_fmt,
+ id, uusp->info.cui_rev, uusp->new_rev);
}
}