diff options
author | Mark Johnson <Mark.Johnson@Sun.COM> | 2009-01-28 17:49:05 -0800 |
---|---|---|
committer | Mark Johnson <Mark.Johnson@Sun.COM> | 2009-01-28 17:49:05 -0800 |
commit | 0ba6f73dcd1c9160a44b2872ed0e0d81b51d168f (patch) | |
tree | 3196b1b2966525867879d370a13c21371e1041fa /usr/src | |
parent | 4be06494508a1d70b3f0065f16685a74b355cedd (diff) | |
download | illumos-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/Makefile | 10 | ||||
-rw-r--r-- | usr/src/cmd/ucodeadm/amd-ucode.bin | bin | 0 -> 2012 bytes | |||
-rw-r--r-- | usr/src/cmd/ucodeadm/ucodeadm.c | 24 | ||||
-rw-r--r-- | usr/src/pkgdefs/SUNWcakr.i/prototype_com | 1 | ||||
-rw-r--r-- | usr/src/uts/common/sys/ucode.h | 10 | ||||
-rw-r--r-- | usr/src/uts/i86pc/os/microcode.c | 175 |
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 Binary files differnew file mode 100644 index 0000000000..f76a056a7a --- /dev/null +++ b/usr/src/cmd/ucodeadm/amd-ucode.bin 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); } } |