14581Ssherrym /*
24581Ssherrym * CDDL HEADER START
34581Ssherrym *
44581Ssherrym * The contents of this file are subject to the terms of the
54581Ssherrym * Common Development and Distribution License (the "License").
64581Ssherrym * You may not use this file except in compliance with the License.
74581Ssherrym *
84581Ssherrym * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94581Ssherrym * or http://www.opensolaris.org/os/licensing.
104581Ssherrym * See the License for the specific language governing permissions
114581Ssherrym * and limitations under the License.
124581Ssherrym *
134581Ssherrym * When distributing Covered Code, include this CDDL HEADER in each
144581Ssherrym * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154581Ssherrym * If applicable, add the following below this CDDL HEADER, with the
164581Ssherrym * fields enclosed by brackets "[]" replaced with your own identifying
174581Ssherrym * information: Portions Copyright [yyyy] [name of copyright owner]
184581Ssherrym *
194581Ssherrym * CDDL HEADER END
204581Ssherrym */
214581Ssherrym
224581Ssherrym /*
238647SMark.Johnson@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
244581Ssherrym * Use is subject to license terms.
254581Ssherrym */
264581Ssherrym
274581Ssherrym #include <sys/asm_linkage.h>
284581Ssherrym #include <sys/bootconf.h>
294581Ssherrym #include <sys/cpuvar.h>
304581Ssherrym #include <sys/cmn_err.h>
314581Ssherrym #include <sys/controlregs.h>
324581Ssherrym #include <sys/debug.h>
334581Ssherrym #include <sys/kobj.h>
344581Ssherrym #include <sys/kobj_impl.h>
354581Ssherrym #include <sys/machsystm.h>
364581Ssherrym #include <sys/param.h>
374581Ssherrym #include <sys/machparam.h>
384581Ssherrym #include <sys/promif.h>
394581Ssherrym #include <sys/sysmacros.h>
404581Ssherrym #include <sys/systm.h>
414581Ssherrym #include <sys/types.h>
424581Ssherrym #include <sys/thread.h>
434581Ssherrym #include <sys/ucode.h>
444581Ssherrym #include <sys/x86_archext.h>
454581Ssherrym #include <sys/x_call.h>
465084Sjohnlev #ifdef __xpv
475084Sjohnlev #include <sys/hypervisor.h>
485084Sjohnlev #endif
494581Ssherrym
504581Ssherrym /*
517605SMark.Johnson@Sun.COM * AMD-specific equivalence table
524581Ssherrym */
537605SMark.Johnson@Sun.COM static ucode_eqtbl_amd_t *ucode_eqtbl_amd;
544581Ssherrym
554581Ssherrym /*
564581Ssherrym * mcpu_ucode_info for the boot CPU. Statically allocated.
574581Ssherrym */
584581Ssherrym static struct cpu_ucode_info cpu_ucode_info0;
594581Ssherrym
607605SMark.Johnson@Sun.COM static ucode_file_t ucodefile;
617605SMark.Johnson@Sun.COM
627605SMark.Johnson@Sun.COM static void* ucode_zalloc(processorid_t, size_t);
637605SMark.Johnson@Sun.COM static void ucode_free(processorid_t, void *, size_t);
647605SMark.Johnson@Sun.COM
657605SMark.Johnson@Sun.COM static int ucode_capable_amd(cpu_t *);
667605SMark.Johnson@Sun.COM static int ucode_capable_intel(cpu_t *);
677605SMark.Johnson@Sun.COM
687605SMark.Johnson@Sun.COM static ucode_errno_t ucode_extract_amd(ucode_update_t *, uint8_t *, int);
697605SMark.Johnson@Sun.COM static ucode_errno_t ucode_extract_intel(ucode_update_t *, uint8_t *,
707605SMark.Johnson@Sun.COM int);
717605SMark.Johnson@Sun.COM
727605SMark.Johnson@Sun.COM static void ucode_file_reset_amd(ucode_file_t *, processorid_t);
737605SMark.Johnson@Sun.COM static void ucode_file_reset_intel(ucode_file_t *, processorid_t);
747605SMark.Johnson@Sun.COM
757605SMark.Johnson@Sun.COM static uint32_t ucode_load_amd(ucode_file_t *, cpu_ucode_info_t *, cpu_t *);
767605SMark.Johnson@Sun.COM static uint32_t ucode_load_intel(ucode_file_t *, cpu_ucode_info_t *, cpu_t *);
777605SMark.Johnson@Sun.COM
787605SMark.Johnson@Sun.COM #ifdef __xpv
797605SMark.Johnson@Sun.COM static void ucode_load_xpv(ucode_update_t *);
808647SMark.Johnson@Sun.COM static void ucode_chipset_amd(uint8_t *, int);
817605SMark.Johnson@Sun.COM #endif
827605SMark.Johnson@Sun.COM
838647SMark.Johnson@Sun.COM static int ucode_equiv_cpu_amd(cpu_t *, uint16_t *);
847605SMark.Johnson@Sun.COM
857605SMark.Johnson@Sun.COM static ucode_errno_t ucode_locate_amd(cpu_t *, cpu_ucode_info_t *,
867605SMark.Johnson@Sun.COM ucode_file_t *);
877605SMark.Johnson@Sun.COM static ucode_errno_t ucode_locate_intel(cpu_t *, cpu_ucode_info_t *,
887605SMark.Johnson@Sun.COM ucode_file_t *);
894581Ssherrym
908647SMark.Johnson@Sun.COM #ifndef __xpv
918647SMark.Johnson@Sun.COM static ucode_errno_t ucode_match_amd(uint16_t, cpu_ucode_info_t *,
927605SMark.Johnson@Sun.COM ucode_file_amd_t *, int);
938647SMark.Johnson@Sun.COM #endif
947605SMark.Johnson@Sun.COM static ucode_errno_t ucode_match_intel(int, cpu_ucode_info_t *,
957605SMark.Johnson@Sun.COM ucode_header_intel_t *, ucode_ext_table_intel_t *);
967605SMark.Johnson@Sun.COM
977605SMark.Johnson@Sun.COM static void ucode_read_rev_amd(cpu_ucode_info_t *);
987605SMark.Johnson@Sun.COM static void ucode_read_rev_intel(cpu_ucode_info_t *);
997605SMark.Johnson@Sun.COM
1007605SMark.Johnson@Sun.COM static const struct ucode_ops ucode_amd = {
1017605SMark.Johnson@Sun.COM MSR_AMD_PATCHLOADER,
1027605SMark.Johnson@Sun.COM ucode_capable_amd,
1037605SMark.Johnson@Sun.COM ucode_file_reset_amd,
1047605SMark.Johnson@Sun.COM ucode_read_rev_amd,
1057605SMark.Johnson@Sun.COM ucode_load_amd,
1067605SMark.Johnson@Sun.COM ucode_validate_amd,
1077605SMark.Johnson@Sun.COM ucode_extract_amd,
1087605SMark.Johnson@Sun.COM ucode_locate_amd
1097605SMark.Johnson@Sun.COM };
1107605SMark.Johnson@Sun.COM
1117605SMark.Johnson@Sun.COM static const struct ucode_ops ucode_intel = {
1127605SMark.Johnson@Sun.COM MSR_INTC_UCODE_WRITE,
1137605SMark.Johnson@Sun.COM ucode_capable_intel,
1147605SMark.Johnson@Sun.COM ucode_file_reset_intel,
1157605SMark.Johnson@Sun.COM ucode_read_rev_intel,
1167605SMark.Johnson@Sun.COM ucode_load_intel,
1177605SMark.Johnson@Sun.COM ucode_validate_intel,
1187605SMark.Johnson@Sun.COM ucode_extract_intel,
1197605SMark.Johnson@Sun.COM ucode_locate_intel
1207605SMark.Johnson@Sun.COM };
1217605SMark.Johnson@Sun.COM
1227605SMark.Johnson@Sun.COM const struct ucode_ops *ucode;
1234581Ssherrym
1244581Ssherrym static const char ucode_failure_fmt[] =
1256519Ssherrym "cpu%d: failed to update microcode from version 0x%x to 0x%x\n";
1264581Ssherrym static const char ucode_success_fmt[] =
1276519Ssherrym "?cpu%d: microcode has been updated from version 0x%x to 0x%x\n";
1284581Ssherrym
1294581Ssherrym /*
1304581Ssherrym * Force flag. If set, the first microcode binary that matches
1314581Ssherrym * signature and platform id will be used for microcode update,
1324581Ssherrym * regardless of version. Should only be used for debugging.
1334581Ssherrym */
1344581Ssherrym int ucode_force_update = 0;
1354581Ssherrym
1364581Ssherrym /*
1374581Ssherrym * Allocate space for mcpu_ucode_info in the machcpu structure
1384581Ssherrym * for all non-boot CPUs.
1394581Ssherrym */
1404581Ssherrym void
ucode_alloc_space(cpu_t * cp)1414581Ssherrym ucode_alloc_space(cpu_t *cp)
1424581Ssherrym {
1434581Ssherrym ASSERT(cp->cpu_id != 0);
144*12004Sjiang.liu@intel.com ASSERT(cp->cpu_m.mcpu_ucode_info == NULL);
1454581Ssherrym cp->cpu_m.mcpu_ucode_info =
1464581Ssherrym kmem_zalloc(sizeof (*cp->cpu_m.mcpu_ucode_info), KM_SLEEP);
1474581Ssherrym }
1484581Ssherrym
1494581Ssherrym void
ucode_free_space(cpu_t * cp)1504581Ssherrym ucode_free_space(cpu_t *cp)
1514581Ssherrym {
152*12004Sjiang.liu@intel.com ASSERT(cp->cpu_m.mcpu_ucode_info != NULL);
153*12004Sjiang.liu@intel.com ASSERT(cp->cpu_m.mcpu_ucode_info != &cpu_ucode_info0);
1544581Ssherrym kmem_free(cp->cpu_m.mcpu_ucode_info,
1554581Ssherrym sizeof (*cp->cpu_m.mcpu_ucode_info));
156*12004Sjiang.liu@intel.com cp->cpu_m.mcpu_ucode_info = NULL;
1574581Ssherrym }
1584581Ssherrym
1594581Ssherrym /*
1604581Ssherrym * Called when we are done with microcode update on all processors to free up
1614581Ssherrym * space allocated for the microcode file.
1624581Ssherrym */
1634581Ssherrym void
ucode_cleanup()1647605SMark.Johnson@Sun.COM ucode_cleanup()
1654581Ssherrym {
1669627SMark.Johnson@Sun.COM if (ucode == NULL)
1679627SMark.Johnson@Sun.COM return;
1687605SMark.Johnson@Sun.COM
1697605SMark.Johnson@Sun.COM ucode->file_reset(&ucodefile, -1);
1707605SMark.Johnson@Sun.COM }
1717605SMark.Johnson@Sun.COM
1727605SMark.Johnson@Sun.COM /*
1737605SMark.Johnson@Sun.COM * Allocate/free a buffer used to hold ucode data. Space for the boot CPU is
1747605SMark.Johnson@Sun.COM * allocated with BOP_ALLOC() and does not require a free.
1757605SMark.Johnson@Sun.COM */
1767605SMark.Johnson@Sun.COM static void*
ucode_zalloc(processorid_t id,size_t size)1777605SMark.Johnson@Sun.COM ucode_zalloc(processorid_t id, size_t size)
1787605SMark.Johnson@Sun.COM {
1797605SMark.Johnson@Sun.COM if (id)
1807605SMark.Johnson@Sun.COM return (kmem_zalloc(size, KM_NOSLEEP));
1817605SMark.Johnson@Sun.COM
1827605SMark.Johnson@Sun.COM /* BOP_ALLOC() failure results in panic */
1837605SMark.Johnson@Sun.COM return (BOP_ALLOC(bootops, NULL, size, MMU_PAGESIZE));
1847605SMark.Johnson@Sun.COM }
1857605SMark.Johnson@Sun.COM
1867605SMark.Johnson@Sun.COM static void
ucode_free(processorid_t id,void * buf,size_t size)1877605SMark.Johnson@Sun.COM ucode_free(processorid_t id, void* buf, size_t size)
1887605SMark.Johnson@Sun.COM {
1897605SMark.Johnson@Sun.COM if (id)
1907605SMark.Johnson@Sun.COM kmem_free(buf, size);
1914581Ssherrym }
1924581Ssherrym
1934581Ssherrym /*
1944581Ssherrym * Check whether or not a processor is capable of microcode operations
1954581Ssherrym * Returns 1 if it is capable, 0 if not.
1967605SMark.Johnson@Sun.COM *
1977605SMark.Johnson@Sun.COM * At this point we only support microcode update for:
1987605SMark.Johnson@Sun.COM * - Intel processors family 6 and above, and
1997605SMark.Johnson@Sun.COM * - AMD processors family 0x10 and above.
2007605SMark.Johnson@Sun.COM *
2017605SMark.Johnson@Sun.COM * We also assume that we don't support a mix of Intel and
2027605SMark.Johnson@Sun.COM * AMD processors in the same box.
2037605SMark.Johnson@Sun.COM *
2047605SMark.Johnson@Sun.COM * An i86xpv guest domain can't update the microcode.
2054581Ssherrym */
2067605SMark.Johnson@Sun.COM /*ARGSUSED*/
2074581Ssherrym static int
ucode_capable_amd(cpu_t * cp)2087605SMark.Johnson@Sun.COM ucode_capable_amd(cpu_t *cp)
2094581Ssherrym {
2109000SStuart.Maybee@Sun.COM int hwenv = get_hwenv();
2119000SStuart.Maybee@Sun.COM
2129000SStuart.Maybee@Sun.COM if (hwenv == HW_XEN_HVM || (hwenv == HW_XEN_PV && !is_controldom())) {
2137605SMark.Johnson@Sun.COM return (0);
2147605SMark.Johnson@Sun.COM }
2158647SMark.Johnson@Sun.COM return (cpuid_getfamily(cp) >= 0x10);
2168647SMark.Johnson@Sun.COM }
2177605SMark.Johnson@Sun.COM
2187605SMark.Johnson@Sun.COM static int
ucode_capable_intel(cpu_t * cp)2197605SMark.Johnson@Sun.COM ucode_capable_intel(cpu_t *cp)
2207605SMark.Johnson@Sun.COM {
2219000SStuart.Maybee@Sun.COM int hwenv = get_hwenv();
2229000SStuart.Maybee@Sun.COM
2239000SStuart.Maybee@Sun.COM if (hwenv == HW_XEN_HVM || (hwenv == HW_XEN_PV && !is_controldom())) {
2247347SMark.Johnson@Sun.COM return (0);
2257347SMark.Johnson@Sun.COM }
2267605SMark.Johnson@Sun.COM return (cpuid_getfamily(cp) >= 6);
2274581Ssherrym }
2284581Ssherrym
2294581Ssherrym /*
2304581Ssherrym * Called when it is no longer necessary to keep the microcode around,
2314581Ssherrym * or when the cached microcode doesn't match the CPU being processed.
2324581Ssherrym */
2334581Ssherrym static void
ucode_file_reset_amd(ucode_file_t * ufp,processorid_t id)2347605SMark.Johnson@Sun.COM ucode_file_reset_amd(ucode_file_t *ufp, processorid_t id)
2354581Ssherrym {
2367605SMark.Johnson@Sun.COM ucode_file_amd_t *ucodefp = ufp->amd;
2374581Ssherrym
2384581Ssherrym if (ucodefp == NULL)
2394581Ssherrym return;
2404581Ssherrym
2417605SMark.Johnson@Sun.COM ucode_free(id, ucodefp, sizeof (ucode_file_amd_t));
2427605SMark.Johnson@Sun.COM ufp->amd = NULL;
2437605SMark.Johnson@Sun.COM }
2447605SMark.Johnson@Sun.COM
2457605SMark.Johnson@Sun.COM static void
ucode_file_reset_intel(ucode_file_t * ufp,processorid_t id)2467605SMark.Johnson@Sun.COM ucode_file_reset_intel(ucode_file_t *ufp, processorid_t id)
2477605SMark.Johnson@Sun.COM {
2487605SMark.Johnson@Sun.COM ucode_file_intel_t *ucodefp = &ufp->intel;
2497605SMark.Johnson@Sun.COM int total_size, body_size;
2507605SMark.Johnson@Sun.COM
2517605SMark.Johnson@Sun.COM if (ucodefp == NULL || ucodefp->uf_header == NULL)
2527605SMark.Johnson@Sun.COM return;
2537605SMark.Johnson@Sun.COM
2547605SMark.Johnson@Sun.COM total_size = UCODE_TOTAL_SIZE_INTEL(ucodefp->uf_header->uh_total_size);
2557605SMark.Johnson@Sun.COM body_size = UCODE_BODY_SIZE_INTEL(ucodefp->uf_header->uh_body_size);
2564581Ssherrym if (ucodefp->uf_body) {
2577605SMark.Johnson@Sun.COM ucode_free(id, ucodefp->uf_body, body_size);
2584581Ssherrym ucodefp->uf_body = NULL;
2594581Ssherrym }
2604581Ssherrym
2614581Ssherrym if (ucodefp->uf_ext_table) {
2627605SMark.Johnson@Sun.COM int size = total_size - body_size - UCODE_HEADER_SIZE_INTEL;
2637605SMark.Johnson@Sun.COM
2647605SMark.Johnson@Sun.COM ucode_free(id, ucodefp->uf_ext_table, size);
2654581Ssherrym ucodefp->uf_ext_table = NULL;
2664581Ssherrym }
2674581Ssherrym
2687605SMark.Johnson@Sun.COM ucode_free(id, ucodefp->uf_header, UCODE_HEADER_SIZE_INTEL);
2697605SMark.Johnson@Sun.COM ucodefp->uf_header = NULL;
2707605SMark.Johnson@Sun.COM }
2717605SMark.Johnson@Sun.COM
2727605SMark.Johnson@Sun.COM /*
2737605SMark.Johnson@Sun.COM * Find the equivalent CPU id in the equivalence table.
2747605SMark.Johnson@Sun.COM */
2757605SMark.Johnson@Sun.COM static int
ucode_equiv_cpu_amd(cpu_t * cp,uint16_t * eq_sig)2768647SMark.Johnson@Sun.COM ucode_equiv_cpu_amd(cpu_t *cp, uint16_t *eq_sig)
2777605SMark.Johnson@Sun.COM {
2787605SMark.Johnson@Sun.COM char name[MAXPATHLEN];
2797605SMark.Johnson@Sun.COM intptr_t fd;
2807605SMark.Johnson@Sun.COM int count;
2817605SMark.Johnson@Sun.COM int offset = 0, cpi_sig = cpuid_getsig(cp);
2827605SMark.Johnson@Sun.COM ucode_eqtbl_amd_t *eqtbl = ucode_eqtbl_amd;
2837605SMark.Johnson@Sun.COM
2847605SMark.Johnson@Sun.COM (void) snprintf(name, MAXPATHLEN, "/%s/%s/equivalence-table",
2857605SMark.Johnson@Sun.COM UCODE_INSTALL_PATH, cpuid_getvendorstr(cp));
2867605SMark.Johnson@Sun.COM
2877605SMark.Johnson@Sun.COM /*
2887605SMark.Johnson@Sun.COM * No kmem_zalloc() etc. available on boot cpu.
2897605SMark.Johnson@Sun.COM */
2907605SMark.Johnson@Sun.COM if (cp->cpu_id == 0) {
2917605SMark.Johnson@Sun.COM if ((fd = kobj_open(name)) == -1)
2927605SMark.Johnson@Sun.COM return (EM_OPENFILE);
2937605SMark.Johnson@Sun.COM /* ucode_zalloc() cannot fail on boot cpu */
2947605SMark.Johnson@Sun.COM eqtbl = ucode_zalloc(cp->cpu_id, sizeof (*eqtbl));
2957605SMark.Johnson@Sun.COM ASSERT(eqtbl);
2967605SMark.Johnson@Sun.COM do {
2977605SMark.Johnson@Sun.COM count = kobj_read(fd, (int8_t *)eqtbl,
2987605SMark.Johnson@Sun.COM sizeof (*eqtbl), offset);
2997605SMark.Johnson@Sun.COM if (count != sizeof (*eqtbl)) {
3007605SMark.Johnson@Sun.COM (void) kobj_close(fd);
3017605SMark.Johnson@Sun.COM return (EM_HIGHERREV);
3027605SMark.Johnson@Sun.COM }
3037605SMark.Johnson@Sun.COM offset += count;
3047605SMark.Johnson@Sun.COM } while (eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != cpi_sig);
3057605SMark.Johnson@Sun.COM (void) kobj_close(fd);
3067605SMark.Johnson@Sun.COM }
3077605SMark.Johnson@Sun.COM
3087605SMark.Johnson@Sun.COM /*
3097605SMark.Johnson@Sun.COM * If not already done, load the equivalence table.
3107605SMark.Johnson@Sun.COM * Not done on boot CPU.
3117605SMark.Johnson@Sun.COM */
3127605SMark.Johnson@Sun.COM if (eqtbl == NULL) {
3137605SMark.Johnson@Sun.COM struct _buf *eq;
3147605SMark.Johnson@Sun.COM uint64_t size;
3157605SMark.Johnson@Sun.COM
3167605SMark.Johnson@Sun.COM if ((eq = kobj_open_file(name)) == (struct _buf *)-1)
3177605SMark.Johnson@Sun.COM return (EM_OPENFILE);
3187605SMark.Johnson@Sun.COM
3197605SMark.Johnson@Sun.COM if (kobj_get_filesize(eq, &size) < 0) {
3207605SMark.Johnson@Sun.COM kobj_close_file(eq);
3217605SMark.Johnson@Sun.COM return (EM_OPENFILE);
3227605SMark.Johnson@Sun.COM }
3237605SMark.Johnson@Sun.COM
3247605SMark.Johnson@Sun.COM ucode_eqtbl_amd = kmem_zalloc(size, KM_NOSLEEP);
3257605SMark.Johnson@Sun.COM if (ucode_eqtbl_amd == NULL) {
3267605SMark.Johnson@Sun.COM kobj_close_file(eq);
3277605SMark.Johnson@Sun.COM return (EM_NOMEM);
3287605SMark.Johnson@Sun.COM }
3297605SMark.Johnson@Sun.COM
3307605SMark.Johnson@Sun.COM count = kobj_read_file(eq, (char *)ucode_eqtbl_amd, size, 0);
3317605SMark.Johnson@Sun.COM kobj_close_file(eq);
3327605SMark.Johnson@Sun.COM
3337605SMark.Johnson@Sun.COM if (count != size)
3347605SMark.Johnson@Sun.COM return (EM_FILESIZE);
3357605SMark.Johnson@Sun.COM }
3367605SMark.Johnson@Sun.COM
3377605SMark.Johnson@Sun.COM /* Get the equivalent CPU id. */
3387605SMark.Johnson@Sun.COM if (cp->cpu_id)
3397605SMark.Johnson@Sun.COM for (eqtbl = ucode_eqtbl_amd;
3407605SMark.Johnson@Sun.COM eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != cpi_sig;
3417605SMark.Johnson@Sun.COM eqtbl++)
3427605SMark.Johnson@Sun.COM ;
3437605SMark.Johnson@Sun.COM
3447605SMark.Johnson@Sun.COM *eq_sig = eqtbl->ue_equiv_cpu;
3457605SMark.Johnson@Sun.COM
3467605SMark.Johnson@Sun.COM /* No equivalent CPU id found, assume outdated microcode file. */
3477605SMark.Johnson@Sun.COM if (*eq_sig == 0)
3487605SMark.Johnson@Sun.COM return (EM_HIGHERREV);
3497605SMark.Johnson@Sun.COM
3507605SMark.Johnson@Sun.COM return (EM_OK);
3514581Ssherrym }
3524581Ssherrym
3534581Ssherrym /*
3548647SMark.Johnson@Sun.COM * xVM cannot check for the presence of PCI devices. Look for chipset-
3558647SMark.Johnson@Sun.COM * specific microcode patches in the container file and disable them
3568647SMark.Johnson@Sun.COM * by setting their CPU revision to an invalid value.
3578647SMark.Johnson@Sun.COM */
3588647SMark.Johnson@Sun.COM #ifdef __xpv
3598647SMark.Johnson@Sun.COM static void
ucode_chipset_amd(uint8_t * buf,int size)3608647SMark.Johnson@Sun.COM ucode_chipset_amd(uint8_t *buf, int size)
3618647SMark.Johnson@Sun.COM {
3628647SMark.Johnson@Sun.COM ucode_header_amd_t *uh;
3638647SMark.Johnson@Sun.COM uint32_t *ptr = (uint32_t *)buf;
3648647SMark.Johnson@Sun.COM int len = 0;
3658647SMark.Johnson@Sun.COM
3668647SMark.Johnson@Sun.COM /* skip to first microcode patch */
3678647SMark.Johnson@Sun.COM ptr += 2; len = *ptr++; ptr += len >> 2; size -= len;
3688647SMark.Johnson@Sun.COM
3698647SMark.Johnson@Sun.COM while (size >= sizeof (ucode_header_amd_t) + 8) {
3708647SMark.Johnson@Sun.COM ptr++; len = *ptr++;
3718647SMark.Johnson@Sun.COM uh = (ucode_header_amd_t *)ptr;
3728647SMark.Johnson@Sun.COM ptr += len >> 2; size -= len;
3738647SMark.Johnson@Sun.COM
3748647SMark.Johnson@Sun.COM if (uh->uh_nb_id) {
3758647SMark.Johnson@Sun.COM cmn_err(CE_WARN, "ignoring northbridge-specific ucode: "
3768647SMark.Johnson@Sun.COM "chipset id %x, revision %x",
3778647SMark.Johnson@Sun.COM uh->uh_nb_id, uh->uh_nb_rev);
3788647SMark.Johnson@Sun.COM uh->uh_cpu_rev = 0xffff;
3798647SMark.Johnson@Sun.COM }
3808647SMark.Johnson@Sun.COM
3818647SMark.Johnson@Sun.COM if (uh->uh_sb_id) {
3828647SMark.Johnson@Sun.COM cmn_err(CE_WARN, "ignoring southbridge-specific ucode: "
3838647SMark.Johnson@Sun.COM "chipset id %x, revision %x",
3848647SMark.Johnson@Sun.COM uh->uh_sb_id, uh->uh_sb_rev);
3858647SMark.Johnson@Sun.COM uh->uh_cpu_rev = 0xffff;
3868647SMark.Johnson@Sun.COM }
3878647SMark.Johnson@Sun.COM }
3888647SMark.Johnson@Sun.COM }
3898647SMark.Johnson@Sun.COM #endif
3908647SMark.Johnson@Sun.COM
3918647SMark.Johnson@Sun.COM /*
3924581Ssherrym * Populate the ucode file structure from microcode file corresponding to
3934581Ssherrym * this CPU, if exists.
3944581Ssherrym *
3954581Ssherrym * Return EM_OK on success, corresponding error code on failure.
3964581Ssherrym */
3978647SMark.Johnson@Sun.COM /*ARGSUSED*/
3984581Ssherrym static ucode_errno_t
ucode_locate_amd(cpu_t * cp,cpu_ucode_info_t * uinfop,ucode_file_t * ufp)3997605SMark.Johnson@Sun.COM ucode_locate_amd(cpu_t *cp, cpu_ucode_info_t *uinfop, ucode_file_t *ufp)
4007605SMark.Johnson@Sun.COM {
4017605SMark.Johnson@Sun.COM char name[MAXPATHLEN];
4027605SMark.Johnson@Sun.COM intptr_t fd;
4038647SMark.Johnson@Sun.COM int count, rc;
4047605SMark.Johnson@Sun.COM ucode_file_amd_t *ucodefp = ufp->amd;
4057605SMark.Johnson@Sun.COM
4068647SMark.Johnson@Sun.COM #ifndef __xpv
4078647SMark.Johnson@Sun.COM uint16_t eq_sig = 0;
4088647SMark.Johnson@Sun.COM int i;
4098647SMark.Johnson@Sun.COM
4107605SMark.Johnson@Sun.COM /* get equivalent CPU id */
4117605SMark.Johnson@Sun.COM if ((rc = ucode_equiv_cpu_amd(cp, &eq_sig)) != EM_OK)
4127605SMark.Johnson@Sun.COM return (rc);
4137605SMark.Johnson@Sun.COM
4147605SMark.Johnson@Sun.COM /*
4157605SMark.Johnson@Sun.COM * Allocate a buffer for the microcode patch. If the buffer has been
4167605SMark.Johnson@Sun.COM * allocated before, check for a matching microcode to avoid loading
4177605SMark.Johnson@Sun.COM * the file again.
4187605SMark.Johnson@Sun.COM */
4197605SMark.Johnson@Sun.COM if (ucodefp == NULL)
4207605SMark.Johnson@Sun.COM ucodefp = ucode_zalloc(cp->cpu_id, sizeof (*ucodefp));
4217605SMark.Johnson@Sun.COM else if (ucode_match_amd(eq_sig, uinfop, ucodefp, sizeof (*ucodefp))
4227605SMark.Johnson@Sun.COM == EM_OK)
4237605SMark.Johnson@Sun.COM return (EM_OK);
4247605SMark.Johnson@Sun.COM
4257605SMark.Johnson@Sun.COM if (ucodefp == NULL)
4267605SMark.Johnson@Sun.COM return (EM_NOMEM);
4277605SMark.Johnson@Sun.COM
4287605SMark.Johnson@Sun.COM ufp->amd = ucodefp;
4297605SMark.Johnson@Sun.COM
4307605SMark.Johnson@Sun.COM /*
4317605SMark.Johnson@Sun.COM * Find the patch for this CPU. The patch files are named XXXX-YY, where
4327605SMark.Johnson@Sun.COM * XXXX is the equivalent CPU id and YY is the running patch number.
4337605SMark.Johnson@Sun.COM * Patches specific to certain chipsets are guaranteed to have lower
4347605SMark.Johnson@Sun.COM * numbers than less specific patches, so we can just load the first
4357605SMark.Johnson@Sun.COM * patch that matches.
4367605SMark.Johnson@Sun.COM */
4377605SMark.Johnson@Sun.COM
4387605SMark.Johnson@Sun.COM for (i = 0; i < 0xff; i++) {
4397605SMark.Johnson@Sun.COM (void) snprintf(name, MAXPATHLEN, "/%s/%s/%04X-%02X",
4407605SMark.Johnson@Sun.COM UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), eq_sig, i);
4417605SMark.Johnson@Sun.COM if ((fd = kobj_open(name)) == -1)
4427605SMark.Johnson@Sun.COM return (EM_NOMATCH);
4437605SMark.Johnson@Sun.COM count = kobj_read(fd, (char *)ucodefp, sizeof (*ucodefp), 0);
4447605SMark.Johnson@Sun.COM (void) kobj_close(fd);
4457605SMark.Johnson@Sun.COM
4467605SMark.Johnson@Sun.COM if (ucode_match_amd(eq_sig, uinfop, ucodefp, count) == EM_OK)
4477605SMark.Johnson@Sun.COM return (EM_OK);
4487605SMark.Johnson@Sun.COM }
4497605SMark.Johnson@Sun.COM return (EM_NOMATCH);
4508647SMark.Johnson@Sun.COM #else
4518647SMark.Johnson@Sun.COM int size = 0;
4528647SMark.Johnson@Sun.COM char c;
4538647SMark.Johnson@Sun.COM
4548647SMark.Johnson@Sun.COM /*
4558647SMark.Johnson@Sun.COM * The xVM case is special. To support mixed-revision systems, the
4568647SMark.Johnson@Sun.COM * hypervisor will choose which patch to load for which CPU, so the
4578647SMark.Johnson@Sun.COM * whole microcode patch container file will have to be loaded.
4588647SMark.Johnson@Sun.COM *
4598647SMark.Johnson@Sun.COM * Since this code is only run on the boot cpu, we don't have to care
4608647SMark.Johnson@Sun.COM * about failing ucode_zalloc() or freeing allocated memory.
4618647SMark.Johnson@Sun.COM */
4628647SMark.Johnson@Sun.COM if (cp->cpu_id != 0)
4638647SMark.Johnson@Sun.COM return (EM_INVALIDARG);
4648647SMark.Johnson@Sun.COM
4658647SMark.Johnson@Sun.COM (void) snprintf(name, MAXPATHLEN, "/%s/%s/container",
4668647SMark.Johnson@Sun.COM UCODE_INSTALL_PATH, cpuid_getvendorstr(cp));
4678647SMark.Johnson@Sun.COM
4688647SMark.Johnson@Sun.COM if ((fd = kobj_open(name)) == -1)
4698647SMark.Johnson@Sun.COM return (EM_OPENFILE);
4708647SMark.Johnson@Sun.COM
4718647SMark.Johnson@Sun.COM /* get the file size by counting bytes */
4728647SMark.Johnson@Sun.COM do {
4738647SMark.Johnson@Sun.COM count = kobj_read(fd, &c, 1, size);
4748647SMark.Johnson@Sun.COM size += count;
4758647SMark.Johnson@Sun.COM } while (count);
4768647SMark.Johnson@Sun.COM
4778647SMark.Johnson@Sun.COM ucodefp = ucode_zalloc(cp->cpu_id, sizeof (*ucodefp));
4788647SMark.Johnson@Sun.COM ASSERT(ucodefp);
4798647SMark.Johnson@Sun.COM ufp->amd = ucodefp;
4808647SMark.Johnson@Sun.COM
4818647SMark.Johnson@Sun.COM ucodefp->usize = size;
4828647SMark.Johnson@Sun.COM ucodefp->ucodep = ucode_zalloc(cp->cpu_id, size);
4838647SMark.Johnson@Sun.COM ASSERT(ucodefp->ucodep);
4848647SMark.Johnson@Sun.COM
4858647SMark.Johnson@Sun.COM /* load the microcode patch container file */
4868647SMark.Johnson@Sun.COM count = kobj_read(fd, (char *)ucodefp->ucodep, size, 0);
4878647SMark.Johnson@Sun.COM (void) kobj_close(fd);
4888647SMark.Johnson@Sun.COM
4898647SMark.Johnson@Sun.COM if (count != size)
4908647SMark.Johnson@Sun.COM return (EM_FILESIZE);
4918647SMark.Johnson@Sun.COM
4928647SMark.Johnson@Sun.COM /* make sure the container file is valid */
4938647SMark.Johnson@Sun.COM rc = ucode->validate(ucodefp->ucodep, ucodefp->usize);
4948647SMark.Johnson@Sun.COM
4958647SMark.Johnson@Sun.COM if (rc != EM_OK)
4968647SMark.Johnson@Sun.COM return (rc);
4978647SMark.Johnson@Sun.COM
4988647SMark.Johnson@Sun.COM /* disable chipset-specific patches */
4998647SMark.Johnson@Sun.COM ucode_chipset_amd(ucodefp->ucodep, ucodefp->usize);
5008647SMark.Johnson@Sun.COM
5018647SMark.Johnson@Sun.COM return (EM_OK);
5028647SMark.Johnson@Sun.COM #endif
5037605SMark.Johnson@Sun.COM }
5047605SMark.Johnson@Sun.COM
5057605SMark.Johnson@Sun.COM static ucode_errno_t
ucode_locate_intel(cpu_t * cp,cpu_ucode_info_t * uinfop,ucode_file_t * ufp)5067605SMark.Johnson@Sun.COM ucode_locate_intel(cpu_t *cp, cpu_ucode_info_t *uinfop, ucode_file_t *ufp)
5074581Ssherrym {
5084581Ssherrym char name[MAXPATHLEN];
5094581Ssherrym intptr_t fd;
5104581Ssherrym int count;
5117605SMark.Johnson@Sun.COM int header_size = UCODE_HEADER_SIZE_INTEL;
5124581Ssherrym int cpi_sig = cpuid_getsig(cp);
5134581Ssherrym ucode_errno_t rc = EM_OK;
5147605SMark.Johnson@Sun.COM ucode_file_intel_t *ucodefp = &ufp->intel;
5157605SMark.Johnson@Sun.COM
5167605SMark.Johnson@Sun.COM ASSERT(ucode);
5174581Ssherrym
5184581Ssherrym /*
5194581Ssherrym * If the microcode matches the CPU we are processing, use it.
5204581Ssherrym */
5217605SMark.Johnson@Sun.COM if (ucode_match_intel(cpi_sig, uinfop, ucodefp->uf_header,
5224581Ssherrym ucodefp->uf_ext_table) == EM_OK && ucodefp->uf_body != NULL) {
5234581Ssherrym return (EM_OK);
5244581Ssherrym }
5254581Ssherrym
5264581Ssherrym /*
5274581Ssherrym * Look for microcode file with the right name.
5284581Ssherrym */
5294581Ssherrym (void) snprintf(name, MAXPATHLEN, "/%s/%s/%08X-%02X",
5304581Ssherrym UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), cpi_sig,
5314581Ssherrym uinfop->cui_platid);
5324581Ssherrym if ((fd = kobj_open(name)) == -1) {
5334581Ssherrym return (EM_OPENFILE);
5344581Ssherrym }
5354581Ssherrym
5364581Ssherrym /*
5374581Ssherrym * We found a microcode file for the CPU we are processing,
5384581Ssherrym * reset the microcode data structure and read in the new
5394581Ssherrym * file.
5404581Ssherrym */
5417605SMark.Johnson@Sun.COM ucode->file_reset(ufp, cp->cpu_id);
5424581Ssherrym
5437605SMark.Johnson@Sun.COM ucodefp->uf_header = ucode_zalloc(cp->cpu_id, header_size);
5447605SMark.Johnson@Sun.COM if (ucodefp->uf_header == NULL)
5457605SMark.Johnson@Sun.COM return (EM_NOMEM);
5467605SMark.Johnson@Sun.COM
5477605SMark.Johnson@Sun.COM count = kobj_read(fd, (char *)ucodefp->uf_header, header_size, 0);
5484581Ssherrym
5494581Ssherrym switch (count) {
5507605SMark.Johnson@Sun.COM case UCODE_HEADER_SIZE_INTEL: {
5514581Ssherrym
5527605SMark.Johnson@Sun.COM ucode_header_intel_t *uhp = ucodefp->uf_header;
5534581Ssherrym uint32_t offset = header_size;
5544581Ssherrym int total_size, body_size, ext_size;
5554581Ssherrym uint32_t sum = 0;
5564581Ssherrym
5574581Ssherrym /*
5584581Ssherrym * Make sure that the header contains valid fields.
5594581Ssherrym */
5607605SMark.Johnson@Sun.COM if ((rc = ucode_header_validate_intel(uhp)) == EM_OK) {
5617605SMark.Johnson@Sun.COM total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
5627605SMark.Johnson@Sun.COM body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size);
5637605SMark.Johnson@Sun.COM ucodefp->uf_body = ucode_zalloc(cp->cpu_id, body_size);
5647605SMark.Johnson@Sun.COM if (ucodefp->uf_body == NULL) {
5657605SMark.Johnson@Sun.COM rc = EM_NOMEM;
5667605SMark.Johnson@Sun.COM break;
5674581Ssherrym }
5684581Ssherrym
5694581Ssherrym if (kobj_read(fd, (char *)ucodefp->uf_body,
5704581Ssherrym body_size, offset) != body_size)
5714581Ssherrym rc = EM_FILESIZE;
5724581Ssherrym }
5734581Ssherrym
5744581Ssherrym if (rc)
5754581Ssherrym break;
5764581Ssherrym
5777605SMark.Johnson@Sun.COM sum = ucode_checksum_intel(0, header_size,
5787605SMark.Johnson@Sun.COM (uint8_t *)ucodefp->uf_header);
5797605SMark.Johnson@Sun.COM if (ucode_checksum_intel(sum, body_size, ucodefp->uf_body)) {
5804581Ssherrym rc = EM_CHECKSUM;
5814581Ssherrym break;
5824581Ssherrym }
5834581Ssherrym
5844581Ssherrym /*
5854581Ssherrym * Check to see if there is extended signature table.
5864581Ssherrym */
5874581Ssherrym offset = body_size + header_size;
5884581Ssherrym ext_size = total_size - offset;
5894581Ssherrym
5904581Ssherrym if (ext_size <= 0)
5914581Ssherrym break;
5924581Ssherrym
5937605SMark.Johnson@Sun.COM ucodefp->uf_ext_table = ucode_zalloc(cp->cpu_id, ext_size);
5947605SMark.Johnson@Sun.COM if (ucodefp->uf_ext_table == NULL) {
5957605SMark.Johnson@Sun.COM rc = EM_NOMEM;
5967605SMark.Johnson@Sun.COM break;
5974581Ssherrym }
5984581Ssherrym
5994581Ssherrym if (kobj_read(fd, (char *)ucodefp->uf_ext_table,
6004581Ssherrym ext_size, offset) != ext_size) {
6014581Ssherrym rc = EM_FILESIZE;
6027605SMark.Johnson@Sun.COM } else if (ucode_checksum_intel(0, ext_size,
6034581Ssherrym (uint8_t *)(ucodefp->uf_ext_table))) {
6044581Ssherrym rc = EM_CHECKSUM;
6054581Ssherrym } else {
6064581Ssherrym int i;
6074581Ssherrym
6087605SMark.Johnson@Sun.COM ext_size -= UCODE_EXT_TABLE_SIZE_INTEL;
6094581Ssherrym for (i = 0; i < ucodefp->uf_ext_table->uet_count;
6104581Ssherrym i++) {
6117605SMark.Johnson@Sun.COM if (ucode_checksum_intel(0,
6127605SMark.Johnson@Sun.COM UCODE_EXT_SIG_SIZE_INTEL,
6134581Ssherrym (uint8_t *)(&(ucodefp->uf_ext_table->
6144581Ssherrym uet_ext_sig[i])))) {
6154581Ssherrym rc = EM_CHECKSUM;
6164581Ssherrym break;
6174581Ssherrym }
6184581Ssherrym }
6194581Ssherrym }
6204581Ssherrym break;
6214581Ssherrym }
6224581Ssherrym
6234581Ssherrym default:
6244581Ssherrym rc = EM_FILESIZE;
6254581Ssherrym break;
6264581Ssherrym }
6274581Ssherrym
6284581Ssherrym kobj_close(fd);
6294581Ssherrym
6304581Ssherrym if (rc != EM_OK)
6314581Ssherrym return (rc);
6324581Ssherrym
6337605SMark.Johnson@Sun.COM rc = ucode_match_intel(cpi_sig, uinfop, ucodefp->uf_header,
6344581Ssherrym ucodefp->uf_ext_table);
6354581Ssherrym
6364581Ssherrym return (rc);
6374581Ssherrym }
6384581Ssherrym
6398647SMark.Johnson@Sun.COM #ifndef __xpv
6407605SMark.Johnson@Sun.COM static ucode_errno_t
ucode_match_amd(uint16_t eq_sig,cpu_ucode_info_t * uinfop,ucode_file_amd_t * ucodefp,int size)6418647SMark.Johnson@Sun.COM ucode_match_amd(uint16_t eq_sig, cpu_ucode_info_t *uinfop,
6428647SMark.Johnson@Sun.COM ucode_file_amd_t *ucodefp, int size)
6437605SMark.Johnson@Sun.COM {
6447605SMark.Johnson@Sun.COM ucode_header_amd_t *uh;
6457605SMark.Johnson@Sun.COM
6467605SMark.Johnson@Sun.COM if (ucodefp == NULL || size < sizeof (ucode_header_amd_t))
6477605SMark.Johnson@Sun.COM return (EM_NOMATCH);
6487605SMark.Johnson@Sun.COM
6497605SMark.Johnson@Sun.COM /*
6507605SMark.Johnson@Sun.COM * Don't even think about loading patches that would require code
6517605SMark.Johnson@Sun.COM * execution.
6527605SMark.Johnson@Sun.COM */
6537605SMark.Johnson@Sun.COM if (size > offsetof(ucode_file_amd_t, uf_code_present) &&
6547605SMark.Johnson@Sun.COM ucodefp->uf_code_present)
6557605SMark.Johnson@Sun.COM return (EM_NOMATCH);
6567605SMark.Johnson@Sun.COM
6577605SMark.Johnson@Sun.COM uh = &ucodefp->uf_header;
6587605SMark.Johnson@Sun.COM
6597605SMark.Johnson@Sun.COM if (eq_sig != uh->uh_cpu_rev)
6607605SMark.Johnson@Sun.COM return (EM_NOMATCH);
6617605SMark.Johnson@Sun.COM
6627605SMark.Johnson@Sun.COM if (uh->uh_nb_id) {
6637605SMark.Johnson@Sun.COM cmn_err(CE_WARN, "ignoring northbridge-specific ucode: "
6647605SMark.Johnson@Sun.COM "chipset id %x, revision %x", uh->uh_nb_id, uh->uh_nb_rev);
6657605SMark.Johnson@Sun.COM return (EM_NOMATCH);
6667605SMark.Johnson@Sun.COM }
6677605SMark.Johnson@Sun.COM
6687605SMark.Johnson@Sun.COM if (uh->uh_sb_id) {
6697605SMark.Johnson@Sun.COM cmn_err(CE_WARN, "ignoring southbridge-specific ucode: "
6707605SMark.Johnson@Sun.COM "chipset id %x, revision %x", uh->uh_sb_id, uh->uh_sb_rev);
6717605SMark.Johnson@Sun.COM return (EM_NOMATCH);
6727605SMark.Johnson@Sun.COM }
6737605SMark.Johnson@Sun.COM
6747605SMark.Johnson@Sun.COM if (uh->uh_patch_id <= uinfop->cui_rev)
6757605SMark.Johnson@Sun.COM return (EM_HIGHERREV);
6767605SMark.Johnson@Sun.COM
6777605SMark.Johnson@Sun.COM return (EM_OK);
6787605SMark.Johnson@Sun.COM }
6798647SMark.Johnson@Sun.COM #endif
6804581Ssherrym
6814581Ssherrym /*
6824581Ssherrym * Returns 1 if the microcode is for this processor; 0 otherwise.
6834581Ssherrym */
6844581Ssherrym static ucode_errno_t
ucode_match_intel(int cpi_sig,cpu_ucode_info_t * uinfop,ucode_header_intel_t * uhp,ucode_ext_table_intel_t * uetp)6857605SMark.Johnson@Sun.COM ucode_match_intel(int cpi_sig, cpu_ucode_info_t *uinfop,
6867605SMark.Johnson@Sun.COM ucode_header_intel_t *uhp, ucode_ext_table_intel_t *uetp)
6874581Ssherrym {
6887605SMark.Johnson@Sun.COM if (uhp == NULL)
6897605SMark.Johnson@Sun.COM return (EM_NOMATCH);
6904581Ssherrym
6917605SMark.Johnson@Sun.COM if (UCODE_MATCH_INTEL(cpi_sig, uhp->uh_signature,
6924581Ssherrym uinfop->cui_platid, uhp->uh_proc_flags)) {
6934581Ssherrym
6944581Ssherrym if (uinfop->cui_rev >= uhp->uh_rev && !ucode_force_update)
6954581Ssherrym return (EM_HIGHERREV);
6964581Ssherrym
6974581Ssherrym return (EM_OK);
6984581Ssherrym }
6994581Ssherrym
7004581Ssherrym if (uetp != NULL) {
7014581Ssherrym int i;
7024581Ssherrym
7034581Ssherrym for (i = 0; i < uetp->uet_count; i++) {
7047605SMark.Johnson@Sun.COM ucode_ext_sig_intel_t *uesp;
7054581Ssherrym
7064581Ssherrym uesp = &uetp->uet_ext_sig[i];
7074581Ssherrym
7087605SMark.Johnson@Sun.COM if (UCODE_MATCH_INTEL(cpi_sig, uesp->ues_signature,
7094581Ssherrym uinfop->cui_platid, uesp->ues_proc_flags)) {
7104581Ssherrym
7114581Ssherrym if (uinfop->cui_rev >= uhp->uh_rev &&
7124581Ssherrym !ucode_force_update)
7134581Ssherrym return (EM_HIGHERREV);
7144581Ssherrym
7154581Ssherrym return (EM_OK);
7164581Ssherrym }
7174581Ssherrym }
7184581Ssherrym }
7194581Ssherrym
7204581Ssherrym return (EM_NOMATCH);
7214581Ssherrym }
7224581Ssherrym
7234581Ssherrym /*ARGSUSED*/
7244581Ssherrym static int
ucode_write(xc_arg_t arg1,xc_arg_t unused2,xc_arg_t unused3)7254581Ssherrym ucode_write(xc_arg_t arg1, xc_arg_t unused2, xc_arg_t unused3)
7264581Ssherrym {
7277605SMark.Johnson@Sun.COM ucode_update_t *uusp = (ucode_update_t *)arg1;
7287605SMark.Johnson@Sun.COM cpu_ucode_info_t *uinfop = CPU->cpu_m.mcpu_ucode_info;
7294581Ssherrym
7307605SMark.Johnson@Sun.COM ASSERT(ucode);
7314581Ssherrym ASSERT(uusp->ucodep);
7324581Ssherrym
7337347SMark.Johnson@Sun.COM #ifndef __xpv
7344581Ssherrym /*
7354581Ssherrym * Check one more time to see if it is really necessary to update
7364581Ssherrym * microcode just in case this is a hyperthreaded processor where
7374581Ssherrym * the threads share the same microcode.
7384581Ssherrym */
7394581Ssherrym if (!ucode_force_update) {
7407605SMark.Johnson@Sun.COM ucode->read_rev(uinfop);
7414581Ssherrym uusp->new_rev = uinfop->cui_rev;
7424581Ssherrym if (uinfop->cui_rev >= uusp->expected_rev)
7434581Ssherrym return (0);
7444581Ssherrym }
7454581Ssherrym
7467605SMark.Johnson@Sun.COM wrmsr(ucode->write_msr, (uintptr_t)uusp->ucodep);
7477347SMark.Johnson@Sun.COM #endif
7487605SMark.Johnson@Sun.COM ucode->read_rev(uinfop);
7494581Ssherrym uusp->new_rev = uinfop->cui_rev;
7504581Ssherrym
7514581Ssherrym return (0);
7524581Ssherrym }
7534581Ssherrym
7547605SMark.Johnson@Sun.COM /*ARGSUSED*/
7557605SMark.Johnson@Sun.COM static uint32_t
ucode_load_amd(ucode_file_t * ufp,cpu_ucode_info_t * uinfop,cpu_t * cp)7567605SMark.Johnson@Sun.COM ucode_load_amd(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp)
7577605SMark.Johnson@Sun.COM {
7587605SMark.Johnson@Sun.COM ucode_file_amd_t *ucodefp = ufp->amd;
7597605SMark.Johnson@Sun.COM #ifdef __xpv
7607605SMark.Johnson@Sun.COM ucode_update_t uus;
7617605SMark.Johnson@Sun.COM #endif
7624581Ssherrym
7637605SMark.Johnson@Sun.COM ASSERT(ucode);
7647605SMark.Johnson@Sun.COM ASSERT(ucodefp);
7657605SMark.Johnson@Sun.COM
7667605SMark.Johnson@Sun.COM #ifndef __xpv
7677605SMark.Johnson@Sun.COM kpreempt_disable();
7687605SMark.Johnson@Sun.COM wrmsr(ucode->write_msr, (uintptr_t)ucodefp);
7697605SMark.Johnson@Sun.COM ucode->read_rev(uinfop);
7707605SMark.Johnson@Sun.COM kpreempt_enable();
7718647SMark.Johnson@Sun.COM
7728647SMark.Johnson@Sun.COM return (ucodefp->uf_header.uh_patch_id);
7737605SMark.Johnson@Sun.COM #else
7748647SMark.Johnson@Sun.COM uus.ucodep = ucodefp->ucodep;
7758647SMark.Johnson@Sun.COM uus.usize = ucodefp->usize;
7767605SMark.Johnson@Sun.COM ucode_load_xpv(&uus);
7777605SMark.Johnson@Sun.COM ucode->read_rev(uinfop);
7787605SMark.Johnson@Sun.COM uus.new_rev = uinfop->cui_rev;
7798647SMark.Johnson@Sun.COM
7808647SMark.Johnson@Sun.COM return (uus.new_rev);
7817605SMark.Johnson@Sun.COM #endif
7827605SMark.Johnson@Sun.COM }
7837605SMark.Johnson@Sun.COM
7847605SMark.Johnson@Sun.COM /*ARGSUSED2*/
7857605SMark.Johnson@Sun.COM static uint32_t
ucode_load_intel(ucode_file_t * ufp,cpu_ucode_info_t * uinfop,cpu_t * cp)7867605SMark.Johnson@Sun.COM ucode_load_intel(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp)
7874581Ssherrym {
7887605SMark.Johnson@Sun.COM ucode_file_intel_t *ucodefp = &ufp->intel;
7897605SMark.Johnson@Sun.COM #ifdef __xpv
7907605SMark.Johnson@Sun.COM uint32_t ext_offset;
7917605SMark.Johnson@Sun.COM uint32_t body_size;
7927605SMark.Johnson@Sun.COM uint32_t ext_size;
7937605SMark.Johnson@Sun.COM uint8_t *ustart;
7947605SMark.Johnson@Sun.COM uint32_t usize;
7957605SMark.Johnson@Sun.COM ucode_update_t uus;
7967605SMark.Johnson@Sun.COM #endif
7977605SMark.Johnson@Sun.COM
7987605SMark.Johnson@Sun.COM ASSERT(ucode);
7997605SMark.Johnson@Sun.COM
8007605SMark.Johnson@Sun.COM #ifdef __xpv
8017605SMark.Johnson@Sun.COM /*
8027605SMark.Johnson@Sun.COM * the hypervisor wants the header, data, and extended
8037605SMark.Johnson@Sun.COM * signature tables. We can only get here from the boot
8047605SMark.Johnson@Sun.COM * CPU (cpu #0), we don't need to free as ucode_zalloc() will
8057605SMark.Johnson@Sun.COM * use BOP_ALLOC().
8067605SMark.Johnson@Sun.COM */
8077605SMark.Johnson@Sun.COM usize = UCODE_TOTAL_SIZE_INTEL(ucodefp->uf_header->uh_total_size);
8087605SMark.Johnson@Sun.COM ustart = ucode_zalloc(cp->cpu_id, usize);
8097605SMark.Johnson@Sun.COM ASSERT(ustart);
8107605SMark.Johnson@Sun.COM
8117605SMark.Johnson@Sun.COM body_size = UCODE_BODY_SIZE_INTEL(ucodefp->uf_header->uh_body_size);
8127605SMark.Johnson@Sun.COM ext_offset = body_size + UCODE_HEADER_SIZE_INTEL;
8137605SMark.Johnson@Sun.COM ext_size = usize - ext_offset;
8147605SMark.Johnson@Sun.COM ASSERT(ext_size >= 0);
8157605SMark.Johnson@Sun.COM
8167605SMark.Johnson@Sun.COM (void) memcpy(ustart, ucodefp->uf_header, UCODE_HEADER_SIZE_INTEL);
8177605SMark.Johnson@Sun.COM (void) memcpy(&ustart[UCODE_HEADER_SIZE_INTEL], ucodefp->uf_body,
8187605SMark.Johnson@Sun.COM body_size);
8197605SMark.Johnson@Sun.COM if (ext_size > 0) {
8207605SMark.Johnson@Sun.COM (void) memcpy(&ustart[ext_offset],
8217605SMark.Johnson@Sun.COM ucodefp->uf_ext_table, ext_size);
8227605SMark.Johnson@Sun.COM }
8237605SMark.Johnson@Sun.COM uus.ucodep = ustart;
8247605SMark.Johnson@Sun.COM uus.usize = usize;
8257605SMark.Johnson@Sun.COM ucode_load_xpv(&uus);
8267605SMark.Johnson@Sun.COM ucode->read_rev(uinfop);
8277605SMark.Johnson@Sun.COM uus.new_rev = uinfop->cui_rev;
8287605SMark.Johnson@Sun.COM #else
8294581Ssherrym kpreempt_disable();
8307605SMark.Johnson@Sun.COM wrmsr(ucode->write_msr, (uintptr_t)ucodefp->uf_body);
8317605SMark.Johnson@Sun.COM ucode->read_rev(uinfop);
8324581Ssherrym kpreempt_enable();
8337605SMark.Johnson@Sun.COM #endif
8347605SMark.Johnson@Sun.COM
8357605SMark.Johnson@Sun.COM return (ucodefp->uf_header->uh_rev);
8364581Ssherrym }
8374581Ssherrym
8387347SMark.Johnson@Sun.COM
8397347SMark.Johnson@Sun.COM #ifdef __xpv
8407347SMark.Johnson@Sun.COM static void
ucode_load_xpv(ucode_update_t * uusp)8417605SMark.Johnson@Sun.COM ucode_load_xpv(ucode_update_t *uusp)
8427347SMark.Johnson@Sun.COM {
8437347SMark.Johnson@Sun.COM xen_platform_op_t op;
8447347SMark.Johnson@Sun.COM int e;
8457347SMark.Johnson@Sun.COM
8467347SMark.Johnson@Sun.COM ASSERT(DOMAIN_IS_INITDOMAIN(xen_info));
8477347SMark.Johnson@Sun.COM
8487347SMark.Johnson@Sun.COM kpreempt_disable();
8497347SMark.Johnson@Sun.COM op.cmd = XENPF_microcode_update;
8507347SMark.Johnson@Sun.COM op.interface_version = XENPF_INTERFACE_VERSION;
8517347SMark.Johnson@Sun.COM /*LINTED: constant in conditional context*/
8527605SMark.Johnson@Sun.COM set_xen_guest_handle(op.u.microcode.data, uusp->ucodep);
8537605SMark.Johnson@Sun.COM op.u.microcode.length = uusp->usize;
8547347SMark.Johnson@Sun.COM e = HYPERVISOR_platform_op(&op);
8557347SMark.Johnson@Sun.COM if (e != 0) {
8567347SMark.Johnson@Sun.COM cmn_err(CE_WARN, "hypervisor failed to accept uCode update");
8577347SMark.Johnson@Sun.COM }
8587347SMark.Johnson@Sun.COM kpreempt_enable();
8597347SMark.Johnson@Sun.COM }
8607347SMark.Johnson@Sun.COM #endif /* __xpv */
8617347SMark.Johnson@Sun.COM
8627605SMark.Johnson@Sun.COM static void
ucode_read_rev_amd(cpu_ucode_info_t * uinfop)8637605SMark.Johnson@Sun.COM ucode_read_rev_amd(cpu_ucode_info_t *uinfop)
8647605SMark.Johnson@Sun.COM {
8657605SMark.Johnson@Sun.COM uinfop->cui_rev = rdmsr(MSR_AMD_PATCHLEVEL);
8667605SMark.Johnson@Sun.COM }
8677347SMark.Johnson@Sun.COM
8684581Ssherrym static void
ucode_read_rev_intel(cpu_ucode_info_t * uinfop)8697605SMark.Johnson@Sun.COM ucode_read_rev_intel(cpu_ucode_info_t *uinfop)
8704581Ssherrym {
8714581Ssherrym struct cpuid_regs crs;
8724581Ssherrym
8734581Ssherrym /*
8744581Ssherrym * The Intel 64 and IA-32 Architecture Software Developer's Manual
8754581Ssherrym * recommends that MSR_INTC_UCODE_REV be loaded with 0 first, then
8764581Ssherrym * execute cpuid to guarantee the correct reading of this register.
8774581Ssherrym */
8784581Ssherrym wrmsr(MSR_INTC_UCODE_REV, 0);
8794581Ssherrym (void) __cpuid_insn(&crs);
8804581Ssherrym uinfop->cui_rev = (rdmsr(MSR_INTC_UCODE_REV) >> INTC_UCODE_REV_SHIFT);
8814581Ssherrym }
8824581Ssherrym
8837605SMark.Johnson@Sun.COM static ucode_errno_t
ucode_extract_amd(ucode_update_t * uusp,uint8_t * ucodep,int size)8847605SMark.Johnson@Sun.COM ucode_extract_amd(ucode_update_t *uusp, uint8_t *ucodep, int size)
8857605SMark.Johnson@Sun.COM {
8868647SMark.Johnson@Sun.COM #ifndef __xpv
8877605SMark.Johnson@Sun.COM uint32_t *ptr = (uint32_t *)ucodep;
8887605SMark.Johnson@Sun.COM ucode_eqtbl_amd_t *eqtbl;
8897605SMark.Johnson@Sun.COM ucode_file_amd_t *ufp;
8908647SMark.Johnson@Sun.COM int count;
8918647SMark.Johnson@Sun.COM int higher = 0;
8928647SMark.Johnson@Sun.COM ucode_errno_t rc = EM_NOMATCH;
8938647SMark.Johnson@Sun.COM uint16_t eq_sig;
8947605SMark.Johnson@Sun.COM
8957605SMark.Johnson@Sun.COM /* skip over magic number & equivalence table header */
8967605SMark.Johnson@Sun.COM ptr += 2; size -= 8;
8977605SMark.Johnson@Sun.COM
8987605SMark.Johnson@Sun.COM count = *ptr++; size -= 4;
8997605SMark.Johnson@Sun.COM for (eqtbl = (ucode_eqtbl_amd_t *)ptr;
9007605SMark.Johnson@Sun.COM eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != uusp->sig;
9017605SMark.Johnson@Sun.COM eqtbl++)
9027605SMark.Johnson@Sun.COM ;
9037605SMark.Johnson@Sun.COM
9047605SMark.Johnson@Sun.COM eq_sig = eqtbl->ue_equiv_cpu;
9057605SMark.Johnson@Sun.COM
9067605SMark.Johnson@Sun.COM /* No equivalent CPU id found, assume outdated microcode file. */
9077605SMark.Johnson@Sun.COM if (eq_sig == 0)
9087605SMark.Johnson@Sun.COM return (EM_HIGHERREV);
9097605SMark.Johnson@Sun.COM
9107605SMark.Johnson@Sun.COM /* Use the first microcode patch that matches. */
9117605SMark.Johnson@Sun.COM do {
9127605SMark.Johnson@Sun.COM ptr += count >> 2; size -= count;
9137605SMark.Johnson@Sun.COM
9147605SMark.Johnson@Sun.COM if (!size)
9158647SMark.Johnson@Sun.COM return (higher ? EM_HIGHERREV : EM_NOMATCH);
9167605SMark.Johnson@Sun.COM
9177605SMark.Johnson@Sun.COM ptr++; size -= 4;
9187605SMark.Johnson@Sun.COM count = *ptr++; size -= 4;
9197605SMark.Johnson@Sun.COM ufp = (ucode_file_amd_t *)ptr;
9208647SMark.Johnson@Sun.COM
9218647SMark.Johnson@Sun.COM rc = ucode_match_amd(eq_sig, &uusp->info, ufp, count);
9228647SMark.Johnson@Sun.COM if (rc == EM_HIGHERREV)
9238647SMark.Johnson@Sun.COM higher = 1;
9248647SMark.Johnson@Sun.COM } while (rc != EM_OK);
9257605SMark.Johnson@Sun.COM
9267605SMark.Johnson@Sun.COM uusp->ucodep = (uint8_t *)ufp;
9277605SMark.Johnson@Sun.COM uusp->usize = count;
9287605SMark.Johnson@Sun.COM uusp->expected_rev = ufp->uf_header.uh_patch_id;
9298647SMark.Johnson@Sun.COM #else
9308647SMark.Johnson@Sun.COM /*
9318647SMark.Johnson@Sun.COM * The hypervisor will choose the patch to load, so there is no way to
9328647SMark.Johnson@Sun.COM * know the "expected revision" in advance. This is especially true on
9338647SMark.Johnson@Sun.COM * mixed-revision systems where more than one patch will be loaded.
9348647SMark.Johnson@Sun.COM */
9358647SMark.Johnson@Sun.COM uusp->expected_rev = 0;
9368647SMark.Johnson@Sun.COM uusp->ucodep = ucodep;
9378647SMark.Johnson@Sun.COM uusp->usize = size;
9388647SMark.Johnson@Sun.COM
9398647SMark.Johnson@Sun.COM ucode_chipset_amd(ucodep, size);
9408647SMark.Johnson@Sun.COM #endif
9417605SMark.Johnson@Sun.COM
9427605SMark.Johnson@Sun.COM return (EM_OK);
9437605SMark.Johnson@Sun.COM }
9447605SMark.Johnson@Sun.COM
9457605SMark.Johnson@Sun.COM static ucode_errno_t
ucode_extract_intel(ucode_update_t * uusp,uint8_t * ucodep,int size)9467605SMark.Johnson@Sun.COM ucode_extract_intel(ucode_update_t *uusp, uint8_t *ucodep, int size)
9477605SMark.Johnson@Sun.COM {
9487605SMark.Johnson@Sun.COM uint32_t header_size = UCODE_HEADER_SIZE_INTEL;
9497605SMark.Johnson@Sun.COM int remaining;
9507605SMark.Johnson@Sun.COM int found = 0;
9517605SMark.Johnson@Sun.COM ucode_errno_t search_rc = EM_NOMATCH; /* search result */
9527605SMark.Johnson@Sun.COM
9537605SMark.Johnson@Sun.COM /*
9547605SMark.Johnson@Sun.COM * Go through the whole buffer in case there are
9557605SMark.Johnson@Sun.COM * multiple versions of matching microcode for this
9567605SMark.Johnson@Sun.COM * processor.
9577605SMark.Johnson@Sun.COM */
9587605SMark.Johnson@Sun.COM for (remaining = size; remaining > 0; ) {
9597605SMark.Johnson@Sun.COM int total_size, body_size, ext_size;
9607605SMark.Johnson@Sun.COM uint8_t *curbuf = &ucodep[size - remaining];
9617605SMark.Johnson@Sun.COM ucode_header_intel_t *uhp = (ucode_header_intel_t *)curbuf;
9627605SMark.Johnson@Sun.COM ucode_ext_table_intel_t *uetp = NULL;
9637605SMark.Johnson@Sun.COM ucode_errno_t tmprc;
9647605SMark.Johnson@Sun.COM
9657605SMark.Johnson@Sun.COM total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
9667605SMark.Johnson@Sun.COM body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size);
9677605SMark.Johnson@Sun.COM ext_size = total_size - (header_size + body_size);
9687605SMark.Johnson@Sun.COM
9697605SMark.Johnson@Sun.COM if (ext_size > 0)
9707605SMark.Johnson@Sun.COM uetp = (ucode_ext_table_intel_t *)
9717605SMark.Johnson@Sun.COM &curbuf[header_size + body_size];
9727605SMark.Johnson@Sun.COM
9737605SMark.Johnson@Sun.COM tmprc = ucode_match_intel(uusp->sig, &uusp->info, uhp, uetp);
9747605SMark.Johnson@Sun.COM
9757605SMark.Johnson@Sun.COM /*
9767605SMark.Johnson@Sun.COM * Since we are searching through a big file
9777605SMark.Johnson@Sun.COM * containing microcode for pretty much all the
9787605SMark.Johnson@Sun.COM * processors, we are bound to get EM_NOMATCH
9797605SMark.Johnson@Sun.COM * at one point. However, if we return
9807605SMark.Johnson@Sun.COM * EM_NOMATCH to users, it will really confuse
9817605SMark.Johnson@Sun.COM * them. Therefore, if we ever find a match of
9827605SMark.Johnson@Sun.COM * a lower rev, we will set return code to
9837605SMark.Johnson@Sun.COM * EM_HIGHERREV.
9847605SMark.Johnson@Sun.COM */
9857605SMark.Johnson@Sun.COM if (tmprc == EM_HIGHERREV)
9867605SMark.Johnson@Sun.COM search_rc = EM_HIGHERREV;
9877605SMark.Johnson@Sun.COM
9887605SMark.Johnson@Sun.COM if (tmprc == EM_OK &&
9897605SMark.Johnson@Sun.COM uusp->expected_rev < uhp->uh_rev) {
9907605SMark.Johnson@Sun.COM #ifndef __xpv
9917605SMark.Johnson@Sun.COM uusp->ucodep = (uint8_t *)&curbuf[header_size];
9927605SMark.Johnson@Sun.COM #else
9937605SMark.Johnson@Sun.COM uusp->ucodep = (uint8_t *)curbuf;
9947605SMark.Johnson@Sun.COM #endif
9957605SMark.Johnson@Sun.COM uusp->usize =
9967605SMark.Johnson@Sun.COM UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
9977605SMark.Johnson@Sun.COM uusp->expected_rev = uhp->uh_rev;
9987605SMark.Johnson@Sun.COM found = 1;
9997605SMark.Johnson@Sun.COM }
10007605SMark.Johnson@Sun.COM
10017605SMark.Johnson@Sun.COM remaining -= total_size;
10027605SMark.Johnson@Sun.COM }
10037605SMark.Johnson@Sun.COM
10047605SMark.Johnson@Sun.COM if (!found)
10057605SMark.Johnson@Sun.COM return (search_rc);
10067605SMark.Johnson@Sun.COM
10077605SMark.Johnson@Sun.COM return (EM_OK);
10087605SMark.Johnson@Sun.COM }
10094581Ssherrym /*
10104581Ssherrym * Entry point to microcode update from the ucode_drv driver.
10114581Ssherrym *
10124581Ssherrym * Returns EM_OK on success, corresponding error code on failure.
10134581Ssherrym */
10144581Ssherrym ucode_errno_t
ucode_update(uint8_t * ucodep,int size)10154581Ssherrym ucode_update(uint8_t *ucodep, int size)
10164581Ssherrym {
10174581Ssherrym int found = 0;
10184581Ssherrym processorid_t id;
10197605SMark.Johnson@Sun.COM ucode_update_t cached = { 0 };
10207605SMark.Johnson@Sun.COM ucode_update_t *cachedp = NULL;
10214581Ssherrym ucode_errno_t rc = EM_OK;
10224581Ssherrym ucode_errno_t search_rc = EM_NOMATCH; /* search result */
10234581Ssherrym cpuset_t cpuset;
10244581Ssherrym
10257605SMark.Johnson@Sun.COM ASSERT(ucode);
10264581Ssherrym ASSERT(ucodep);
10274581Ssherrym CPUSET_ZERO(cpuset);
10284581Ssherrym
10297605SMark.Johnson@Sun.COM if (!ucode->capable(CPU))
10304581Ssherrym return (EM_NOTSUP);
10314581Ssherrym
10324581Ssherrym mutex_enter(&cpu_lock);
10334581Ssherrym
10344581Ssherrym for (id = 0; id < max_ncpus; id++) {
10354581Ssherrym cpu_t *cpu;
10367605SMark.Johnson@Sun.COM ucode_update_t uus = { 0 };
10377605SMark.Johnson@Sun.COM ucode_update_t *uusp = &uus;
10384581Ssherrym
10394581Ssherrym /*
10404581Ssherrym * If there is no such CPU or it is not xcall ready, skip it.
10414581Ssherrym */
10424581Ssherrym if ((cpu = cpu_get(id)) == NULL ||
10434581Ssherrym !(cpu->cpu_flags & CPU_READY))
10444581Ssherrym continue;
10454581Ssherrym
10464581Ssherrym uusp->sig = cpuid_getsig(cpu);
10474581Ssherrym bcopy(cpu->cpu_m.mcpu_ucode_info, &uusp->info,
10484581Ssherrym sizeof (uusp->info));
10494581Ssherrym
10504581Ssherrym /*
10514581Ssherrym * If the current CPU has the same signature and platform
10524581Ssherrym * id as the previous one we processed, reuse the information.
10534581Ssherrym */
10544581Ssherrym if (cachedp && cachedp->sig == cpuid_getsig(cpu) &&
10554581Ssherrym cachedp->info.cui_platid == uusp->info.cui_platid) {
10564581Ssherrym uusp->ucodep = cachedp->ucodep;
10574581Ssherrym uusp->expected_rev = cachedp->expected_rev;
10584581Ssherrym /*
10594581Ssherrym * Intuitively we should check here to see whether the
10604581Ssherrym * running microcode rev is >= the expected rev, and
10614581Ssherrym * quit if it is. But we choose to proceed with the
10624581Ssherrym * xcall regardless of the running version so that
10634581Ssherrym * the other threads in an HT processor can update
10644581Ssherrym * the cpu_ucode_info structure in machcpu.
10654581Ssherrym */
10667605SMark.Johnson@Sun.COM } else if ((search_rc = ucode->extract(uusp, ucodep, size))
10677605SMark.Johnson@Sun.COM == EM_OK) {
10687605SMark.Johnson@Sun.COM bcopy(uusp, &cached, sizeof (cached));
10697605SMark.Johnson@Sun.COM cachedp = &cached;
10707605SMark.Johnson@Sun.COM found = 1;
10714581Ssherrym }
10724581Ssherrym
10734581Ssherrym /* Nothing to do */
10744581Ssherrym if (uusp->ucodep == NULL)
10754581Ssherrym continue;
10764581Ssherrym
10777347SMark.Johnson@Sun.COM #ifdef __xpv
10787347SMark.Johnson@Sun.COM /*
10797347SMark.Johnson@Sun.COM * for i86xpv, the hypervisor will update all the CPUs.
10807347SMark.Johnson@Sun.COM * the hypervisor wants the header, data, and extended
10817347SMark.Johnson@Sun.COM * signature tables. ucode_write will just read in the
10827347SMark.Johnson@Sun.COM * updated version on all the CPUs after the update has
10837347SMark.Johnson@Sun.COM * completed.
10847347SMark.Johnson@Sun.COM */
10857402SMark.Johnson@Sun.COM if (id == 0) {
10867605SMark.Johnson@Sun.COM ucode_load_xpv(uusp);
10877402SMark.Johnson@Sun.COM }
10887347SMark.Johnson@Sun.COM #endif
10897347SMark.Johnson@Sun.COM
10904581Ssherrym CPUSET_ADD(cpuset, id);
10914581Ssherrym kpreempt_disable();
10929489SJoe.Bonasera@sun.com xc_sync((xc_arg_t)uusp, 0, 0, CPUSET2BV(cpuset), ucode_write);
10934581Ssherrym kpreempt_enable();
10944581Ssherrym CPUSET_DEL(cpuset, id);
10954581Ssherrym
10968647SMark.Johnson@Sun.COM if (uusp->new_rev != 0 && uusp->info.cui_rev == uusp->new_rev) {
10978647SMark.Johnson@Sun.COM rc = EM_HIGHERREV;
10988647SMark.Johnson@Sun.COM } else if ((uusp->new_rev == 0) || (uusp->expected_rev != 0 &&
10998647SMark.Johnson@Sun.COM uusp->expected_rev != uusp->new_rev)) {
11004581Ssherrym cmn_err(CE_WARN, ucode_failure_fmt,
11014581Ssherrym id, uusp->info.cui_rev, uusp->expected_rev);
11024581Ssherrym rc = EM_UPDATE;
11038647SMark.Johnson@Sun.COM } else {
11048647SMark.Johnson@Sun.COM cmn_err(CE_CONT, ucode_success_fmt,
11058647SMark.Johnson@Sun.COM id, uusp->info.cui_rev, uusp->new_rev);
11064581Ssherrym }
11074581Ssherrym }
11084581Ssherrym
11094581Ssherrym mutex_exit(&cpu_lock);
11104581Ssherrym
11114581Ssherrym if (!found)
11124581Ssherrym rc = search_rc;
11134581Ssherrym
11144581Ssherrym return (rc);
11154581Ssherrym }
11164581Ssherrym
11174581Ssherrym /*
11184581Ssherrym * Initialize mcpu_ucode_info, and perform microcode update if necessary.
11194581Ssherrym * This is the entry point from boot path where pointer to CPU structure
11204581Ssherrym * is available.
11214581Ssherrym *
11224581Ssherrym * cpuid_info must be initialized before ucode_check can be called.
11234581Ssherrym */
11244581Ssherrym void
ucode_check(cpu_t * cp)11254581Ssherrym ucode_check(cpu_t *cp)
11264581Ssherrym {
11277605SMark.Johnson@Sun.COM cpu_ucode_info_t *uinfop;
11284581Ssherrym ucode_errno_t rc = EM_OK;
11297605SMark.Johnson@Sun.COM uint32_t new_rev = 0;
11304581Ssherrym
11314581Ssherrym ASSERT(cp);
1132*12004Sjiang.liu@intel.com /*
1133*12004Sjiang.liu@intel.com * Space statically allocated for BSP, ensure pointer is set
1134*12004Sjiang.liu@intel.com */
1135*12004Sjiang.liu@intel.com if (cp->cpu_id == 0 && cp->cpu_m.mcpu_ucode_info == NULL)
11364581Ssherrym cp->cpu_m.mcpu_ucode_info = &cpu_ucode_info0;
11374581Ssherrym
11384581Ssherrym uinfop = cp->cpu_m.mcpu_ucode_info;
11394581Ssherrym ASSERT(uinfop);
11404581Ssherrym
11417605SMark.Johnson@Sun.COM /* set up function pointers if not already done */
11427605SMark.Johnson@Sun.COM if (!ucode)
11437605SMark.Johnson@Sun.COM switch (cpuid_getvendor(cp)) {
11447605SMark.Johnson@Sun.COM case X86_VENDOR_AMD:
11457605SMark.Johnson@Sun.COM ucode = &ucode_amd;
11467605SMark.Johnson@Sun.COM break;
11477605SMark.Johnson@Sun.COM case X86_VENDOR_Intel:
11487605SMark.Johnson@Sun.COM ucode = &ucode_intel;
11497605SMark.Johnson@Sun.COM break;
11507605SMark.Johnson@Sun.COM default:
11519627SMark.Johnson@Sun.COM ucode = NULL;
11527605SMark.Johnson@Sun.COM return;
11537605SMark.Johnson@Sun.COM }
11547605SMark.Johnson@Sun.COM
11557605SMark.Johnson@Sun.COM if (!ucode->capable(cp))
11564581Ssherrym return;
11574581Ssherrym
11584581Ssherrym /*
11594581Ssherrym * The MSR_INTC_PLATFORM_ID is supported in Celeron and Xeon
11604581Ssherrym * (Family 6, model 5 and above) and all processors after.
11614581Ssherrym */
11627605SMark.Johnson@Sun.COM if ((cpuid_getvendor(cp) == X86_VENDOR_Intel) &&
11637605SMark.Johnson@Sun.COM ((cpuid_getmodel(cp) >= 5) || (cpuid_getfamily(cp) > 6))) {
11644581Ssherrym uinfop->cui_platid = 1 << ((rdmsr(MSR_INTC_PLATFORM_ID) >>
11654581Ssherrym INTC_PLATFORM_ID_SHIFT) & INTC_PLATFORM_ID_MASK);
11664581Ssherrym }
11674581Ssherrym
11687605SMark.Johnson@Sun.COM ucode->read_rev(uinfop);
11694581Ssherrym
11707402SMark.Johnson@Sun.COM #ifdef __xpv
11717402SMark.Johnson@Sun.COM /*
11727402SMark.Johnson@Sun.COM * for i86xpv, the hypervisor will update all the CPUs. We only need
11737402SMark.Johnson@Sun.COM * do do this on one of the CPUs (and there always is a CPU 0).
11747402SMark.Johnson@Sun.COM */
11757402SMark.Johnson@Sun.COM if (cp->cpu_id != 0) {
11767402SMark.Johnson@Sun.COM return;
11777402SMark.Johnson@Sun.COM }
11787402SMark.Johnson@Sun.COM #endif
11797402SMark.Johnson@Sun.COM
11804581Ssherrym /*
11814581Ssherrym * Check to see if we need ucode update
11824581Ssherrym */
11837605SMark.Johnson@Sun.COM if ((rc = ucode->locate(cp, uinfop, &ucodefile)) == EM_OK) {
11847605SMark.Johnson@Sun.COM new_rev = ucode->load(&ucodefile, uinfop, cp);
11857347SMark.Johnson@Sun.COM
11867605SMark.Johnson@Sun.COM if (uinfop->cui_rev != new_rev)
11874581Ssherrym cmn_err(CE_WARN, ucode_failure_fmt, cp->cpu_id,
11887605SMark.Johnson@Sun.COM uinfop->cui_rev, new_rev);
11894581Ssherrym }
11904581Ssherrym
11914581Ssherrym /*
11924581Ssherrym * If we fail to find a match for any reason, free the file structure
11934581Ssherrym * just in case we have read in a partial file.
11944581Ssherrym *
11954581Ssherrym * Since the scratch memory for holding the microcode for the boot CPU
11964581Ssherrym * came from BOP_ALLOC, we will reset the data structure as if we
11974581Ssherrym * never did the allocation so we don't have to keep track of this
11984581Ssherrym * special chunk of memory. We free the memory used for the rest
11994581Ssherrym * of the CPUs in start_other_cpus().
12004581Ssherrym */
12014581Ssherrym if (rc != EM_OK || cp->cpu_id == 0)
12027605SMark.Johnson@Sun.COM ucode->file_reset(&ucodefile, cp->cpu_id);
12034581Ssherrym }
12044581Ssherrym
12054581Ssherrym /*
12064581Ssherrym * Returns microcode revision from the machcpu structure.
12074581Ssherrym */
12084581Ssherrym ucode_errno_t
ucode_get_rev(uint32_t * revp)12094581Ssherrym ucode_get_rev(uint32_t *revp)
12104581Ssherrym {
12114581Ssherrym int i;
12124581Ssherrym
12137605SMark.Johnson@Sun.COM ASSERT(ucode);
12144581Ssherrym ASSERT(revp);
12154581Ssherrym
12167605SMark.Johnson@Sun.COM if (!ucode->capable(CPU))
12174581Ssherrym return (EM_NOTSUP);
12184581Ssherrym
12194581Ssherrym mutex_enter(&cpu_lock);
12204581Ssherrym for (i = 0; i < max_ncpus; i++) {
12214581Ssherrym cpu_t *cpu;
12224581Ssherrym
12234581Ssherrym if ((cpu = cpu_get(i)) == NULL)
12244581Ssherrym continue;
12254581Ssherrym
12264581Ssherrym revp[i] = cpu->cpu_m.mcpu_ucode_info->cui_rev;
12274581Ssherrym }
12284581Ssherrym mutex_exit(&cpu_lock);
12294581Ssherrym
12304581Ssherrym return (EM_OK);
12314581Ssherrym }
1232