xref: /onnv-gate/usr/src/uts/i86pc/os/microcode.c (revision 8647:c2d855200ce1)
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 /*
23*8647SMark.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 *);
80*8647SMark.Johnson@Sun.COM static void ucode_chipset_amd(uint8_t *, int);
817605SMark.Johnson@Sun.COM #endif
827605SMark.Johnson@Sun.COM 
83*8647SMark.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 
90*8647SMark.Johnson@Sun.COM #ifndef __xpv
91*8647SMark.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);
93*8647SMark.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
1414581Ssherrym ucode_alloc_space(cpu_t *cp)
1424581Ssherrym {
1434581Ssherrym 	ASSERT(cp->cpu_id != 0);
1444581Ssherrym 	cp->cpu_m.mcpu_ucode_info =
1454581Ssherrym 	    kmem_zalloc(sizeof (*cp->cpu_m.mcpu_ucode_info), KM_SLEEP);
1464581Ssherrym }
1474581Ssherrym 
1484581Ssherrym void
1494581Ssherrym ucode_free_space(cpu_t *cp)
1504581Ssherrym {
1514581Ssherrym 	ASSERT(cp->cpu_id != 0);
1524581Ssherrym 	kmem_free(cp->cpu_m.mcpu_ucode_info,
1534581Ssherrym 	    sizeof (*cp->cpu_m.mcpu_ucode_info));
1544581Ssherrym }
1554581Ssherrym 
1564581Ssherrym /*
1574581Ssherrym  * Called when we are done with microcode update on all processors to free up
1584581Ssherrym  * space allocated for the microcode file.
1594581Ssherrym  */
1604581Ssherrym void
1617605SMark.Johnson@Sun.COM ucode_cleanup()
1624581Ssherrym {
1637605SMark.Johnson@Sun.COM 	ASSERT(ucode);
1647605SMark.Johnson@Sun.COM 
1657605SMark.Johnson@Sun.COM 	ucode->file_reset(&ucodefile, -1);
1667605SMark.Johnson@Sun.COM }
1677605SMark.Johnson@Sun.COM 
1687605SMark.Johnson@Sun.COM /*
1697605SMark.Johnson@Sun.COM  * Allocate/free a buffer used to hold ucode data. Space for the boot CPU is
1707605SMark.Johnson@Sun.COM  * allocated with BOP_ALLOC() and does not require a free.
1717605SMark.Johnson@Sun.COM  */
1727605SMark.Johnson@Sun.COM static void*
1737605SMark.Johnson@Sun.COM ucode_zalloc(processorid_t id, size_t size)
1747605SMark.Johnson@Sun.COM {
1757605SMark.Johnson@Sun.COM 	if (id)
1767605SMark.Johnson@Sun.COM 		return (kmem_zalloc(size, KM_NOSLEEP));
1777605SMark.Johnson@Sun.COM 
1787605SMark.Johnson@Sun.COM 	/* BOP_ALLOC() failure results in panic */
1797605SMark.Johnson@Sun.COM 	return (BOP_ALLOC(bootops, NULL, size, MMU_PAGESIZE));
1807605SMark.Johnson@Sun.COM }
1817605SMark.Johnson@Sun.COM 
1827605SMark.Johnson@Sun.COM static void
1837605SMark.Johnson@Sun.COM ucode_free(processorid_t id, void* buf, size_t size)
1847605SMark.Johnson@Sun.COM {
1857605SMark.Johnson@Sun.COM 	if (id)
1867605SMark.Johnson@Sun.COM 		kmem_free(buf, size);
1874581Ssherrym }
1884581Ssherrym 
1894581Ssherrym /*
1904581Ssherrym  * Check whether or not a processor is capable of microcode operations
1914581Ssherrym  * Returns 1 if it is capable, 0 if not.
1927605SMark.Johnson@Sun.COM  *
1937605SMark.Johnson@Sun.COM  * At this point we only support microcode update for:
1947605SMark.Johnson@Sun.COM  * - Intel processors family 6 and above, and
1957605SMark.Johnson@Sun.COM  * - AMD processors family 0x10 and above.
1967605SMark.Johnson@Sun.COM  *
1977605SMark.Johnson@Sun.COM  * We also assume that we don't support a mix of Intel and
1987605SMark.Johnson@Sun.COM  * AMD processors in the same box.
1997605SMark.Johnson@Sun.COM  *
2007605SMark.Johnson@Sun.COM  * An i86xpv guest domain can't update the microcode.
2014581Ssherrym  */
2027605SMark.Johnson@Sun.COM /*ARGSUSED*/
2034581Ssherrym static int
2047605SMark.Johnson@Sun.COM ucode_capable_amd(cpu_t *cp)
2054581Ssherrym {
2067605SMark.Johnson@Sun.COM #ifndef	__xpv
2077605SMark.Johnson@Sun.COM 	extern int xpv_is_hvm;
2087605SMark.Johnson@Sun.COM 	if (xpv_is_hvm) {
2097605SMark.Johnson@Sun.COM 		return (0);
2107605SMark.Johnson@Sun.COM 	}
2117605SMark.Johnson@Sun.COM #else
2127605SMark.Johnson@Sun.COM 	if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
2137605SMark.Johnson@Sun.COM 		return (0);
2147605SMark.Johnson@Sun.COM 	}
215*8647SMark.Johnson@Sun.COM #endif
216*8647SMark.Johnson@Sun.COM 	return (cpuid_getfamily(cp) >= 0x10);
217*8647SMark.Johnson@Sun.COM }
2187605SMark.Johnson@Sun.COM 
2197605SMark.Johnson@Sun.COM static int
2207605SMark.Johnson@Sun.COM ucode_capable_intel(cpu_t *cp)
2217605SMark.Johnson@Sun.COM {
2227347SMark.Johnson@Sun.COM #ifndef	__xpv
2237347SMark.Johnson@Sun.COM 	extern int xpv_is_hvm;
2247347SMark.Johnson@Sun.COM 	if (xpv_is_hvm) {
2257347SMark.Johnson@Sun.COM 		return (0);
2267347SMark.Johnson@Sun.COM 	}
2277347SMark.Johnson@Sun.COM #else
2285084Sjohnlev 	if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
2295084Sjohnlev 		return (0);
2305084Sjohnlev 	}
2315084Sjohnlev #endif
2327605SMark.Johnson@Sun.COM 	return (cpuid_getfamily(cp) >= 6);
2334581Ssherrym }
2344581Ssherrym 
2354581Ssherrym /*
2364581Ssherrym  * Called when it is no longer necessary to keep the microcode around,
2374581Ssherrym  * or when the cached microcode doesn't match the CPU being processed.
2384581Ssherrym  */
2394581Ssherrym static void
2407605SMark.Johnson@Sun.COM ucode_file_reset_amd(ucode_file_t *ufp, processorid_t id)
2414581Ssherrym {
2427605SMark.Johnson@Sun.COM 	ucode_file_amd_t *ucodefp = ufp->amd;
2434581Ssherrym 
2444581Ssherrym 	if (ucodefp == NULL)
2454581Ssherrym 		return;
2464581Ssherrym 
2477605SMark.Johnson@Sun.COM 	ucode_free(id, ucodefp, sizeof (ucode_file_amd_t));
2487605SMark.Johnson@Sun.COM 	ufp->amd = NULL;
2497605SMark.Johnson@Sun.COM }
2507605SMark.Johnson@Sun.COM 
2517605SMark.Johnson@Sun.COM static void
2527605SMark.Johnson@Sun.COM ucode_file_reset_intel(ucode_file_t *ufp, processorid_t id)
2537605SMark.Johnson@Sun.COM {
2547605SMark.Johnson@Sun.COM 	ucode_file_intel_t *ucodefp = &ufp->intel;
2557605SMark.Johnson@Sun.COM 	int total_size, body_size;
2567605SMark.Johnson@Sun.COM 
2577605SMark.Johnson@Sun.COM 	if (ucodefp == NULL || ucodefp->uf_header == NULL)
2587605SMark.Johnson@Sun.COM 		return;
2597605SMark.Johnson@Sun.COM 
2607605SMark.Johnson@Sun.COM 	total_size = UCODE_TOTAL_SIZE_INTEL(ucodefp->uf_header->uh_total_size);
2617605SMark.Johnson@Sun.COM 	body_size = UCODE_BODY_SIZE_INTEL(ucodefp->uf_header->uh_body_size);
2624581Ssherrym 	if (ucodefp->uf_body) {
2637605SMark.Johnson@Sun.COM 		ucode_free(id, ucodefp->uf_body, body_size);
2644581Ssherrym 		ucodefp->uf_body = NULL;
2654581Ssherrym 	}
2664581Ssherrym 
2674581Ssherrym 	if (ucodefp->uf_ext_table) {
2687605SMark.Johnson@Sun.COM 		int size = total_size - body_size - UCODE_HEADER_SIZE_INTEL;
2697605SMark.Johnson@Sun.COM 
2707605SMark.Johnson@Sun.COM 		ucode_free(id, ucodefp->uf_ext_table, size);
2714581Ssherrym 		ucodefp->uf_ext_table = NULL;
2724581Ssherrym 	}
2734581Ssherrym 
2747605SMark.Johnson@Sun.COM 	ucode_free(id, ucodefp->uf_header, UCODE_HEADER_SIZE_INTEL);
2757605SMark.Johnson@Sun.COM 	ucodefp->uf_header = NULL;
2767605SMark.Johnson@Sun.COM }
2777605SMark.Johnson@Sun.COM 
2787605SMark.Johnson@Sun.COM /*
2797605SMark.Johnson@Sun.COM  * Find the equivalent CPU id in the equivalence table.
2807605SMark.Johnson@Sun.COM  */
2817605SMark.Johnson@Sun.COM static int
282*8647SMark.Johnson@Sun.COM ucode_equiv_cpu_amd(cpu_t *cp, uint16_t *eq_sig)
2837605SMark.Johnson@Sun.COM {
2847605SMark.Johnson@Sun.COM 	char name[MAXPATHLEN];
2857605SMark.Johnson@Sun.COM 	intptr_t fd;
2867605SMark.Johnson@Sun.COM 	int count;
2877605SMark.Johnson@Sun.COM 	int offset = 0, cpi_sig = cpuid_getsig(cp);
2887605SMark.Johnson@Sun.COM 	ucode_eqtbl_amd_t *eqtbl = ucode_eqtbl_amd;
2897605SMark.Johnson@Sun.COM 
2907605SMark.Johnson@Sun.COM 	(void) snprintf(name, MAXPATHLEN, "/%s/%s/equivalence-table",
2917605SMark.Johnson@Sun.COM 	    UCODE_INSTALL_PATH, cpuid_getvendorstr(cp));
2927605SMark.Johnson@Sun.COM 
2937605SMark.Johnson@Sun.COM 	/*
2947605SMark.Johnson@Sun.COM 	 * No kmem_zalloc() etc. available on boot cpu.
2957605SMark.Johnson@Sun.COM 	 */
2967605SMark.Johnson@Sun.COM 	if (cp->cpu_id == 0) {
2977605SMark.Johnson@Sun.COM 		if ((fd = kobj_open(name)) == -1)
2987605SMark.Johnson@Sun.COM 			return (EM_OPENFILE);
2997605SMark.Johnson@Sun.COM 		/* ucode_zalloc() cannot fail on boot cpu */
3007605SMark.Johnson@Sun.COM 		eqtbl = ucode_zalloc(cp->cpu_id, sizeof (*eqtbl));
3017605SMark.Johnson@Sun.COM 		ASSERT(eqtbl);
3027605SMark.Johnson@Sun.COM 		do {
3037605SMark.Johnson@Sun.COM 			count = kobj_read(fd, (int8_t *)eqtbl,
3047605SMark.Johnson@Sun.COM 			    sizeof (*eqtbl), offset);
3057605SMark.Johnson@Sun.COM 			if (count != sizeof (*eqtbl)) {
3067605SMark.Johnson@Sun.COM 				(void) kobj_close(fd);
3077605SMark.Johnson@Sun.COM 				return (EM_HIGHERREV);
3087605SMark.Johnson@Sun.COM 			}
3097605SMark.Johnson@Sun.COM 			offset += count;
3107605SMark.Johnson@Sun.COM 		} while (eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != cpi_sig);
3117605SMark.Johnson@Sun.COM 		(void) kobj_close(fd);
3127605SMark.Johnson@Sun.COM 	}
3137605SMark.Johnson@Sun.COM 
3147605SMark.Johnson@Sun.COM 	/*
3157605SMark.Johnson@Sun.COM 	 * If not already done, load the equivalence table.
3167605SMark.Johnson@Sun.COM 	 * Not done on boot CPU.
3177605SMark.Johnson@Sun.COM 	 */
3187605SMark.Johnson@Sun.COM 	if (eqtbl == NULL) {
3197605SMark.Johnson@Sun.COM 		struct _buf *eq;
3207605SMark.Johnson@Sun.COM 		uint64_t size;
3217605SMark.Johnson@Sun.COM 
3227605SMark.Johnson@Sun.COM 		if ((eq = kobj_open_file(name)) == (struct _buf *)-1)
3237605SMark.Johnson@Sun.COM 			return (EM_OPENFILE);
3247605SMark.Johnson@Sun.COM 
3257605SMark.Johnson@Sun.COM 		if (kobj_get_filesize(eq, &size) < 0) {
3267605SMark.Johnson@Sun.COM 			kobj_close_file(eq);
3277605SMark.Johnson@Sun.COM 			return (EM_OPENFILE);
3287605SMark.Johnson@Sun.COM 		}
3297605SMark.Johnson@Sun.COM 
3307605SMark.Johnson@Sun.COM 		ucode_eqtbl_amd = kmem_zalloc(size, KM_NOSLEEP);
3317605SMark.Johnson@Sun.COM 		if (ucode_eqtbl_amd == NULL) {
3327605SMark.Johnson@Sun.COM 			kobj_close_file(eq);
3337605SMark.Johnson@Sun.COM 			return (EM_NOMEM);
3347605SMark.Johnson@Sun.COM 		}
3357605SMark.Johnson@Sun.COM 
3367605SMark.Johnson@Sun.COM 		count = kobj_read_file(eq, (char *)ucode_eqtbl_amd, size, 0);
3377605SMark.Johnson@Sun.COM 		kobj_close_file(eq);
3387605SMark.Johnson@Sun.COM 
3397605SMark.Johnson@Sun.COM 		if (count != size)
3407605SMark.Johnson@Sun.COM 			return (EM_FILESIZE);
3417605SMark.Johnson@Sun.COM 	}
3427605SMark.Johnson@Sun.COM 
3437605SMark.Johnson@Sun.COM 	/* Get the equivalent CPU id. */
3447605SMark.Johnson@Sun.COM 	if (cp->cpu_id)
3457605SMark.Johnson@Sun.COM 		for (eqtbl = ucode_eqtbl_amd;
3467605SMark.Johnson@Sun.COM 		    eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != cpi_sig;
3477605SMark.Johnson@Sun.COM 		    eqtbl++)
3487605SMark.Johnson@Sun.COM 			;
3497605SMark.Johnson@Sun.COM 
3507605SMark.Johnson@Sun.COM 	*eq_sig = eqtbl->ue_equiv_cpu;
3517605SMark.Johnson@Sun.COM 
3527605SMark.Johnson@Sun.COM 	/* No equivalent CPU id found, assume outdated microcode file. */
3537605SMark.Johnson@Sun.COM 	if (*eq_sig == 0)
3547605SMark.Johnson@Sun.COM 		return (EM_HIGHERREV);
3557605SMark.Johnson@Sun.COM 
3567605SMark.Johnson@Sun.COM 	return (EM_OK);
3574581Ssherrym }
3584581Ssherrym 
3594581Ssherrym /*
360*8647SMark.Johnson@Sun.COM  * xVM cannot check for the presence of PCI devices. Look for chipset-
361*8647SMark.Johnson@Sun.COM  * specific microcode patches in the container file and disable them
362*8647SMark.Johnson@Sun.COM  * by setting their CPU revision to an invalid value.
363*8647SMark.Johnson@Sun.COM  */
364*8647SMark.Johnson@Sun.COM #ifdef __xpv
365*8647SMark.Johnson@Sun.COM static void
366*8647SMark.Johnson@Sun.COM ucode_chipset_amd(uint8_t *buf, int size)
367*8647SMark.Johnson@Sun.COM {
368*8647SMark.Johnson@Sun.COM 	ucode_header_amd_t *uh;
369*8647SMark.Johnson@Sun.COM 	uint32_t *ptr = (uint32_t *)buf;
370*8647SMark.Johnson@Sun.COM 	int len = 0;
371*8647SMark.Johnson@Sun.COM 
372*8647SMark.Johnson@Sun.COM 	/* skip to first microcode patch */
373*8647SMark.Johnson@Sun.COM 	ptr += 2; len = *ptr++; ptr += len >> 2; size -= len;
374*8647SMark.Johnson@Sun.COM 
375*8647SMark.Johnson@Sun.COM 	while (size >= sizeof (ucode_header_amd_t) + 8) {
376*8647SMark.Johnson@Sun.COM 		ptr++; len = *ptr++;
377*8647SMark.Johnson@Sun.COM 		uh = (ucode_header_amd_t *)ptr;
378*8647SMark.Johnson@Sun.COM 		ptr += len >> 2; size -= len;
379*8647SMark.Johnson@Sun.COM 
380*8647SMark.Johnson@Sun.COM 		if (uh->uh_nb_id) {
381*8647SMark.Johnson@Sun.COM 			cmn_err(CE_WARN, "ignoring northbridge-specific ucode: "
382*8647SMark.Johnson@Sun.COM 			    "chipset id %x, revision %x",
383*8647SMark.Johnson@Sun.COM 			    uh->uh_nb_id, uh->uh_nb_rev);
384*8647SMark.Johnson@Sun.COM 			uh->uh_cpu_rev = 0xffff;
385*8647SMark.Johnson@Sun.COM 		}
386*8647SMark.Johnson@Sun.COM 
387*8647SMark.Johnson@Sun.COM 		if (uh->uh_sb_id) {
388*8647SMark.Johnson@Sun.COM 			cmn_err(CE_WARN, "ignoring southbridge-specific ucode: "
389*8647SMark.Johnson@Sun.COM 			    "chipset id %x, revision %x",
390*8647SMark.Johnson@Sun.COM 			    uh->uh_sb_id, uh->uh_sb_rev);
391*8647SMark.Johnson@Sun.COM 			uh->uh_cpu_rev = 0xffff;
392*8647SMark.Johnson@Sun.COM 		}
393*8647SMark.Johnson@Sun.COM 	}
394*8647SMark.Johnson@Sun.COM }
395*8647SMark.Johnson@Sun.COM #endif
396*8647SMark.Johnson@Sun.COM 
397*8647SMark.Johnson@Sun.COM /*
3984581Ssherrym  * Populate the ucode file structure from microcode file corresponding to
3994581Ssherrym  * this CPU, if exists.
4004581Ssherrym  *
4014581Ssherrym  * Return EM_OK on success, corresponding error code on failure.
4024581Ssherrym  */
403*8647SMark.Johnson@Sun.COM /*ARGSUSED*/
4044581Ssherrym static ucode_errno_t
4057605SMark.Johnson@Sun.COM ucode_locate_amd(cpu_t *cp, cpu_ucode_info_t *uinfop, ucode_file_t *ufp)
4067605SMark.Johnson@Sun.COM {
4077605SMark.Johnson@Sun.COM 	char name[MAXPATHLEN];
4087605SMark.Johnson@Sun.COM 	intptr_t fd;
409*8647SMark.Johnson@Sun.COM 	int count, rc;
4107605SMark.Johnson@Sun.COM 	ucode_file_amd_t *ucodefp = ufp->amd;
4117605SMark.Johnson@Sun.COM 
412*8647SMark.Johnson@Sun.COM #ifndef __xpv
413*8647SMark.Johnson@Sun.COM 	uint16_t eq_sig = 0;
414*8647SMark.Johnson@Sun.COM 	int i;
415*8647SMark.Johnson@Sun.COM 
4167605SMark.Johnson@Sun.COM 	/* get equivalent CPU id */
4177605SMark.Johnson@Sun.COM 	if ((rc = ucode_equiv_cpu_amd(cp, &eq_sig)) != EM_OK)
4187605SMark.Johnson@Sun.COM 		return (rc);
4197605SMark.Johnson@Sun.COM 
4207605SMark.Johnson@Sun.COM 	/*
4217605SMark.Johnson@Sun.COM 	 * Allocate a buffer for the microcode patch. If the buffer has been
4227605SMark.Johnson@Sun.COM 	 * allocated before, check for a matching microcode to avoid loading
4237605SMark.Johnson@Sun.COM 	 * the file again.
4247605SMark.Johnson@Sun.COM 	 */
4257605SMark.Johnson@Sun.COM 	if (ucodefp == NULL)
4267605SMark.Johnson@Sun.COM 		ucodefp = ucode_zalloc(cp->cpu_id, sizeof (*ucodefp));
4277605SMark.Johnson@Sun.COM 	else if (ucode_match_amd(eq_sig, uinfop, ucodefp, sizeof (*ucodefp))
4287605SMark.Johnson@Sun.COM 	    == EM_OK)
4297605SMark.Johnson@Sun.COM 		return (EM_OK);
4307605SMark.Johnson@Sun.COM 
4317605SMark.Johnson@Sun.COM 	if (ucodefp == NULL)
4327605SMark.Johnson@Sun.COM 		return (EM_NOMEM);
4337605SMark.Johnson@Sun.COM 
4347605SMark.Johnson@Sun.COM 	ufp->amd = ucodefp;
4357605SMark.Johnson@Sun.COM 
4367605SMark.Johnson@Sun.COM 	/*
4377605SMark.Johnson@Sun.COM 	 * Find the patch for this CPU. The patch files are named XXXX-YY, where
4387605SMark.Johnson@Sun.COM 	 * XXXX is the equivalent CPU id and YY is the running patch number.
4397605SMark.Johnson@Sun.COM 	 * Patches specific to certain chipsets are guaranteed to have lower
4407605SMark.Johnson@Sun.COM 	 * numbers than less specific patches, so we can just load the first
4417605SMark.Johnson@Sun.COM 	 * patch that matches.
4427605SMark.Johnson@Sun.COM 	 */
4437605SMark.Johnson@Sun.COM 
4447605SMark.Johnson@Sun.COM 	for (i = 0; i < 0xff; i++) {
4457605SMark.Johnson@Sun.COM 		(void) snprintf(name, MAXPATHLEN, "/%s/%s/%04X-%02X",
4467605SMark.Johnson@Sun.COM 		    UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), eq_sig, i);
4477605SMark.Johnson@Sun.COM 		if ((fd = kobj_open(name)) == -1)
4487605SMark.Johnson@Sun.COM 			return (EM_NOMATCH);
4497605SMark.Johnson@Sun.COM 		count = kobj_read(fd, (char *)ucodefp, sizeof (*ucodefp), 0);
4507605SMark.Johnson@Sun.COM 		(void) kobj_close(fd);
4517605SMark.Johnson@Sun.COM 
4527605SMark.Johnson@Sun.COM 		if (ucode_match_amd(eq_sig, uinfop, ucodefp, count) == EM_OK)
4537605SMark.Johnson@Sun.COM 			return (EM_OK);
4547605SMark.Johnson@Sun.COM 	}
4557605SMark.Johnson@Sun.COM 	return (EM_NOMATCH);
456*8647SMark.Johnson@Sun.COM #else
457*8647SMark.Johnson@Sun.COM 	int size = 0;
458*8647SMark.Johnson@Sun.COM 	char c;
459*8647SMark.Johnson@Sun.COM 
460*8647SMark.Johnson@Sun.COM 	/*
461*8647SMark.Johnson@Sun.COM 	 * The xVM case is special. To support mixed-revision systems, the
462*8647SMark.Johnson@Sun.COM 	 * hypervisor will choose which patch to load for which CPU, so the
463*8647SMark.Johnson@Sun.COM 	 * whole microcode patch container file will have to be loaded.
464*8647SMark.Johnson@Sun.COM 	 *
465*8647SMark.Johnson@Sun.COM 	 * Since this code is only run on the boot cpu, we don't have to care
466*8647SMark.Johnson@Sun.COM 	 * about failing ucode_zalloc() or freeing allocated memory.
467*8647SMark.Johnson@Sun.COM 	 */
468*8647SMark.Johnson@Sun.COM 	if (cp->cpu_id != 0)
469*8647SMark.Johnson@Sun.COM 		return (EM_INVALIDARG);
470*8647SMark.Johnson@Sun.COM 
471*8647SMark.Johnson@Sun.COM 	(void) snprintf(name, MAXPATHLEN, "/%s/%s/container",
472*8647SMark.Johnson@Sun.COM 	    UCODE_INSTALL_PATH, cpuid_getvendorstr(cp));
473*8647SMark.Johnson@Sun.COM 
474*8647SMark.Johnson@Sun.COM 	if ((fd = kobj_open(name)) == -1)
475*8647SMark.Johnson@Sun.COM 		return (EM_OPENFILE);
476*8647SMark.Johnson@Sun.COM 
477*8647SMark.Johnson@Sun.COM 	/* get the file size by counting bytes */
478*8647SMark.Johnson@Sun.COM 	do {
479*8647SMark.Johnson@Sun.COM 		count = kobj_read(fd, &c, 1, size);
480*8647SMark.Johnson@Sun.COM 		size += count;
481*8647SMark.Johnson@Sun.COM 	} while (count);
482*8647SMark.Johnson@Sun.COM 
483*8647SMark.Johnson@Sun.COM 	ucodefp = ucode_zalloc(cp->cpu_id, sizeof (*ucodefp));
484*8647SMark.Johnson@Sun.COM 	ASSERT(ucodefp);
485*8647SMark.Johnson@Sun.COM 	ufp->amd = ucodefp;
486*8647SMark.Johnson@Sun.COM 
487*8647SMark.Johnson@Sun.COM 	ucodefp->usize = size;
488*8647SMark.Johnson@Sun.COM 	ucodefp->ucodep = ucode_zalloc(cp->cpu_id, size);
489*8647SMark.Johnson@Sun.COM 	ASSERT(ucodefp->ucodep);
490*8647SMark.Johnson@Sun.COM 
491*8647SMark.Johnson@Sun.COM 	/* load the microcode patch container file */
492*8647SMark.Johnson@Sun.COM 	count = kobj_read(fd, (char *)ucodefp->ucodep, size, 0);
493*8647SMark.Johnson@Sun.COM 	(void) kobj_close(fd);
494*8647SMark.Johnson@Sun.COM 
495*8647SMark.Johnson@Sun.COM 	if (count != size)
496*8647SMark.Johnson@Sun.COM 		return (EM_FILESIZE);
497*8647SMark.Johnson@Sun.COM 
498*8647SMark.Johnson@Sun.COM 	/* make sure the container file is valid */
499*8647SMark.Johnson@Sun.COM 	rc = ucode->validate(ucodefp->ucodep, ucodefp->usize);
500*8647SMark.Johnson@Sun.COM 
501*8647SMark.Johnson@Sun.COM 	if (rc != EM_OK)
502*8647SMark.Johnson@Sun.COM 		return (rc);
503*8647SMark.Johnson@Sun.COM 
504*8647SMark.Johnson@Sun.COM 	/* disable chipset-specific patches */
505*8647SMark.Johnson@Sun.COM 	ucode_chipset_amd(ucodefp->ucodep, ucodefp->usize);
506*8647SMark.Johnson@Sun.COM 
507*8647SMark.Johnson@Sun.COM 	return (EM_OK);
508*8647SMark.Johnson@Sun.COM #endif
5097605SMark.Johnson@Sun.COM }
5107605SMark.Johnson@Sun.COM 
5117605SMark.Johnson@Sun.COM static ucode_errno_t
5127605SMark.Johnson@Sun.COM ucode_locate_intel(cpu_t *cp, cpu_ucode_info_t *uinfop, ucode_file_t *ufp)
5134581Ssherrym {
5144581Ssherrym 	char		name[MAXPATHLEN];
5154581Ssherrym 	intptr_t	fd;
5164581Ssherrym 	int		count;
5177605SMark.Johnson@Sun.COM 	int		header_size = UCODE_HEADER_SIZE_INTEL;
5184581Ssherrym 	int		cpi_sig = cpuid_getsig(cp);
5194581Ssherrym 	ucode_errno_t	rc = EM_OK;
5207605SMark.Johnson@Sun.COM 	ucode_file_intel_t *ucodefp = &ufp->intel;
5217605SMark.Johnson@Sun.COM 
5227605SMark.Johnson@Sun.COM 	ASSERT(ucode);
5234581Ssherrym 
5244581Ssherrym 	/*
5254581Ssherrym 	 * If the microcode matches the CPU we are processing, use it.
5264581Ssherrym 	 */
5277605SMark.Johnson@Sun.COM 	if (ucode_match_intel(cpi_sig, uinfop, ucodefp->uf_header,
5284581Ssherrym 	    ucodefp->uf_ext_table) == EM_OK && ucodefp->uf_body != NULL) {
5294581Ssherrym 		return (EM_OK);
5304581Ssherrym 	}
5314581Ssherrym 
5324581Ssherrym 	/*
5334581Ssherrym 	 * Look for microcode file with the right name.
5344581Ssherrym 	 */
5354581Ssherrym 	(void) snprintf(name, MAXPATHLEN, "/%s/%s/%08X-%02X",
5364581Ssherrym 	    UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), cpi_sig,
5374581Ssherrym 	    uinfop->cui_platid);
5384581Ssherrym 	if ((fd = kobj_open(name)) == -1) {
5394581Ssherrym 		return (EM_OPENFILE);
5404581Ssherrym 	}
5414581Ssherrym 
5424581Ssherrym 	/*
5434581Ssherrym 	 * We found a microcode file for the CPU we are processing,
5444581Ssherrym 	 * reset the microcode data structure and read in the new
5454581Ssherrym 	 * file.
5464581Ssherrym 	 */
5477605SMark.Johnson@Sun.COM 	ucode->file_reset(ufp, cp->cpu_id);
5484581Ssherrym 
5497605SMark.Johnson@Sun.COM 	ucodefp->uf_header = ucode_zalloc(cp->cpu_id, header_size);
5507605SMark.Johnson@Sun.COM 	if (ucodefp->uf_header == NULL)
5517605SMark.Johnson@Sun.COM 		return (EM_NOMEM);
5527605SMark.Johnson@Sun.COM 
5537605SMark.Johnson@Sun.COM 	count = kobj_read(fd, (char *)ucodefp->uf_header, header_size, 0);
5544581Ssherrym 
5554581Ssherrym 	switch (count) {
5567605SMark.Johnson@Sun.COM 	case UCODE_HEADER_SIZE_INTEL: {
5574581Ssherrym 
5587605SMark.Johnson@Sun.COM 		ucode_header_intel_t	*uhp = ucodefp->uf_header;
5594581Ssherrym 		uint32_t	offset = header_size;
5604581Ssherrym 		int		total_size, body_size, ext_size;
5614581Ssherrym 		uint32_t	sum = 0;
5624581Ssherrym 
5634581Ssherrym 		/*
5644581Ssherrym 		 * Make sure that the header contains valid fields.
5654581Ssherrym 		 */
5667605SMark.Johnson@Sun.COM 		if ((rc = ucode_header_validate_intel(uhp)) == EM_OK) {
5677605SMark.Johnson@Sun.COM 			total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
5687605SMark.Johnson@Sun.COM 			body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size);
5697605SMark.Johnson@Sun.COM 			ucodefp->uf_body = ucode_zalloc(cp->cpu_id, body_size);
5707605SMark.Johnson@Sun.COM 			if (ucodefp->uf_body == NULL) {
5717605SMark.Johnson@Sun.COM 				rc = EM_NOMEM;
5727605SMark.Johnson@Sun.COM 				break;
5734581Ssherrym 			}
5744581Ssherrym 
5754581Ssherrym 			if (kobj_read(fd, (char *)ucodefp->uf_body,
5764581Ssherrym 			    body_size, offset) != body_size)
5774581Ssherrym 				rc = EM_FILESIZE;
5784581Ssherrym 		}
5794581Ssherrym 
5804581Ssherrym 		if (rc)
5814581Ssherrym 			break;
5824581Ssherrym 
5837605SMark.Johnson@Sun.COM 		sum = ucode_checksum_intel(0, header_size,
5847605SMark.Johnson@Sun.COM 		    (uint8_t *)ucodefp->uf_header);
5857605SMark.Johnson@Sun.COM 		if (ucode_checksum_intel(sum, body_size, ucodefp->uf_body)) {
5864581Ssherrym 			rc = EM_CHECKSUM;
5874581Ssherrym 			break;
5884581Ssherrym 		}
5894581Ssherrym 
5904581Ssherrym 		/*
5914581Ssherrym 		 * Check to see if there is extended signature table.
5924581Ssherrym 		 */
5934581Ssherrym 		offset = body_size + header_size;
5944581Ssherrym 		ext_size = total_size - offset;
5954581Ssherrym 
5964581Ssherrym 		if (ext_size <= 0)
5974581Ssherrym 			break;
5984581Ssherrym 
5997605SMark.Johnson@Sun.COM 		ucodefp->uf_ext_table = ucode_zalloc(cp->cpu_id, ext_size);
6007605SMark.Johnson@Sun.COM 		if (ucodefp->uf_ext_table == NULL) {
6017605SMark.Johnson@Sun.COM 			rc = EM_NOMEM;
6027605SMark.Johnson@Sun.COM 			break;
6034581Ssherrym 		}
6044581Ssherrym 
6054581Ssherrym 		if (kobj_read(fd, (char *)ucodefp->uf_ext_table,
6064581Ssherrym 		    ext_size, offset) != ext_size) {
6074581Ssherrym 			rc = EM_FILESIZE;
6087605SMark.Johnson@Sun.COM 		} else if (ucode_checksum_intel(0, ext_size,
6094581Ssherrym 		    (uint8_t *)(ucodefp->uf_ext_table))) {
6104581Ssherrym 			rc = EM_CHECKSUM;
6114581Ssherrym 		} else {
6124581Ssherrym 			int i;
6134581Ssherrym 
6147605SMark.Johnson@Sun.COM 			ext_size -= UCODE_EXT_TABLE_SIZE_INTEL;
6154581Ssherrym 			for (i = 0; i < ucodefp->uf_ext_table->uet_count;
6164581Ssherrym 			    i++) {
6177605SMark.Johnson@Sun.COM 				if (ucode_checksum_intel(0,
6187605SMark.Johnson@Sun.COM 				    UCODE_EXT_SIG_SIZE_INTEL,
6194581Ssherrym 				    (uint8_t *)(&(ucodefp->uf_ext_table->
6204581Ssherrym 				    uet_ext_sig[i])))) {
6214581Ssherrym 					rc = EM_CHECKSUM;
6224581Ssherrym 					break;
6234581Ssherrym 				}
6244581Ssherrym 			}
6254581Ssherrym 		}
6264581Ssherrym 		break;
6274581Ssherrym 	}
6284581Ssherrym 
6294581Ssherrym 	default:
6304581Ssherrym 		rc = EM_FILESIZE;
6314581Ssherrym 		break;
6324581Ssherrym 	}
6334581Ssherrym 
6344581Ssherrym 	kobj_close(fd);
6354581Ssherrym 
6364581Ssherrym 	if (rc != EM_OK)
6374581Ssherrym 		return (rc);
6384581Ssherrym 
6397605SMark.Johnson@Sun.COM 	rc = ucode_match_intel(cpi_sig, uinfop, ucodefp->uf_header,
6404581Ssherrym 	    ucodefp->uf_ext_table);
6414581Ssherrym 
6424581Ssherrym 	return (rc);
6434581Ssherrym }
6444581Ssherrym 
645*8647SMark.Johnson@Sun.COM #ifndef __xpv
6467605SMark.Johnson@Sun.COM static ucode_errno_t
647*8647SMark.Johnson@Sun.COM ucode_match_amd(uint16_t eq_sig, cpu_ucode_info_t *uinfop,
648*8647SMark.Johnson@Sun.COM     ucode_file_amd_t *ucodefp, int size)
6497605SMark.Johnson@Sun.COM {
6507605SMark.Johnson@Sun.COM 	ucode_header_amd_t *uh;
6517605SMark.Johnson@Sun.COM 
6527605SMark.Johnson@Sun.COM 	if (ucodefp == NULL || size < sizeof (ucode_header_amd_t))
6537605SMark.Johnson@Sun.COM 		return (EM_NOMATCH);
6547605SMark.Johnson@Sun.COM 
6557605SMark.Johnson@Sun.COM 	/*
6567605SMark.Johnson@Sun.COM 	 * Don't even think about loading patches that would require code
6577605SMark.Johnson@Sun.COM 	 * execution.
6587605SMark.Johnson@Sun.COM 	 */
6597605SMark.Johnson@Sun.COM 	if (size > offsetof(ucode_file_amd_t, uf_code_present) &&
6607605SMark.Johnson@Sun.COM 	    ucodefp->uf_code_present)
6617605SMark.Johnson@Sun.COM 		return (EM_NOMATCH);
6627605SMark.Johnson@Sun.COM 
6637605SMark.Johnson@Sun.COM 	uh = &ucodefp->uf_header;
6647605SMark.Johnson@Sun.COM 
6657605SMark.Johnson@Sun.COM 	if (eq_sig != uh->uh_cpu_rev)
6667605SMark.Johnson@Sun.COM 		return (EM_NOMATCH);
6677605SMark.Johnson@Sun.COM 
6687605SMark.Johnson@Sun.COM 	if (uh->uh_nb_id) {
6697605SMark.Johnson@Sun.COM 		cmn_err(CE_WARN, "ignoring northbridge-specific ucode: "
6707605SMark.Johnson@Sun.COM 		    "chipset id %x, revision %x", uh->uh_nb_id, uh->uh_nb_rev);
6717605SMark.Johnson@Sun.COM 		return (EM_NOMATCH);
6727605SMark.Johnson@Sun.COM 	}
6737605SMark.Johnson@Sun.COM 
6747605SMark.Johnson@Sun.COM 	if (uh->uh_sb_id) {
6757605SMark.Johnson@Sun.COM 		cmn_err(CE_WARN, "ignoring southbridge-specific ucode: "
6767605SMark.Johnson@Sun.COM 		    "chipset id %x, revision %x", uh->uh_sb_id, uh->uh_sb_rev);
6777605SMark.Johnson@Sun.COM 		return (EM_NOMATCH);
6787605SMark.Johnson@Sun.COM 	}
6797605SMark.Johnson@Sun.COM 
6807605SMark.Johnson@Sun.COM 	if (uh->uh_patch_id <= uinfop->cui_rev)
6817605SMark.Johnson@Sun.COM 		return (EM_HIGHERREV);
6827605SMark.Johnson@Sun.COM 
6837605SMark.Johnson@Sun.COM 	return (EM_OK);
6847605SMark.Johnson@Sun.COM }
685*8647SMark.Johnson@Sun.COM #endif
6864581Ssherrym 
6874581Ssherrym /*
6884581Ssherrym  * Returns 1 if the microcode is for this processor; 0 otherwise.
6894581Ssherrym  */
6904581Ssherrym static ucode_errno_t
6917605SMark.Johnson@Sun.COM ucode_match_intel(int cpi_sig, cpu_ucode_info_t *uinfop,
6927605SMark.Johnson@Sun.COM     ucode_header_intel_t *uhp, ucode_ext_table_intel_t *uetp)
6934581Ssherrym {
6947605SMark.Johnson@Sun.COM 	if (uhp == NULL)
6957605SMark.Johnson@Sun.COM 		return (EM_NOMATCH);
6964581Ssherrym 
6977605SMark.Johnson@Sun.COM 	if (UCODE_MATCH_INTEL(cpi_sig, uhp->uh_signature,
6984581Ssherrym 	    uinfop->cui_platid, uhp->uh_proc_flags)) {
6994581Ssherrym 
7004581Ssherrym 		if (uinfop->cui_rev >= uhp->uh_rev && !ucode_force_update)
7014581Ssherrym 			return (EM_HIGHERREV);
7024581Ssherrym 
7034581Ssherrym 		return (EM_OK);
7044581Ssherrym 	}
7054581Ssherrym 
7064581Ssherrym 	if (uetp != NULL) {
7074581Ssherrym 		int i;
7084581Ssherrym 
7094581Ssherrym 		for (i = 0; i < uetp->uet_count; i++) {
7107605SMark.Johnson@Sun.COM 			ucode_ext_sig_intel_t *uesp;
7114581Ssherrym 
7124581Ssherrym 			uesp = &uetp->uet_ext_sig[i];
7134581Ssherrym 
7147605SMark.Johnson@Sun.COM 			if (UCODE_MATCH_INTEL(cpi_sig, uesp->ues_signature,
7154581Ssherrym 			    uinfop->cui_platid, uesp->ues_proc_flags)) {
7164581Ssherrym 
7174581Ssherrym 				if (uinfop->cui_rev >= uhp->uh_rev &&
7184581Ssherrym 				    !ucode_force_update)
7194581Ssherrym 					return (EM_HIGHERREV);
7204581Ssherrym 
7214581Ssherrym 				return (EM_OK);
7224581Ssherrym 			}
7234581Ssherrym 		}
7244581Ssherrym 	}
7254581Ssherrym 
7264581Ssherrym 	return (EM_NOMATCH);
7274581Ssherrym }
7284581Ssherrym 
7294581Ssherrym /*ARGSUSED*/
7304581Ssherrym static int
7314581Ssherrym ucode_write(xc_arg_t arg1, xc_arg_t unused2, xc_arg_t unused3)
7324581Ssherrym {
7337605SMark.Johnson@Sun.COM 	ucode_update_t *uusp = (ucode_update_t *)arg1;
7347605SMark.Johnson@Sun.COM 	cpu_ucode_info_t *uinfop = CPU->cpu_m.mcpu_ucode_info;
7354581Ssherrym 
7367605SMark.Johnson@Sun.COM 	ASSERT(ucode);
7374581Ssherrym 	ASSERT(uusp->ucodep);
7384581Ssherrym 
7397347SMark.Johnson@Sun.COM #ifndef	__xpv
7404581Ssherrym 	/*
7414581Ssherrym 	 * Check one more time to see if it is really necessary to update
7424581Ssherrym 	 * microcode just in case this is a hyperthreaded processor where
7434581Ssherrym 	 * the threads share the same microcode.
7444581Ssherrym 	 */
7454581Ssherrym 	if (!ucode_force_update) {
7467605SMark.Johnson@Sun.COM 		ucode->read_rev(uinfop);
7474581Ssherrym 		uusp->new_rev = uinfop->cui_rev;
7484581Ssherrym 		if (uinfop->cui_rev >= uusp->expected_rev)
7494581Ssherrym 			return (0);
7504581Ssherrym 	}
7514581Ssherrym 
7527605SMark.Johnson@Sun.COM 	wrmsr(ucode->write_msr, (uintptr_t)uusp->ucodep);
7537347SMark.Johnson@Sun.COM #endif
7547605SMark.Johnson@Sun.COM 	ucode->read_rev(uinfop);
7554581Ssherrym 	uusp->new_rev = uinfop->cui_rev;
7564581Ssherrym 
7574581Ssherrym 	return (0);
7584581Ssherrym }
7594581Ssherrym 
7607605SMark.Johnson@Sun.COM /*ARGSUSED*/
7617605SMark.Johnson@Sun.COM static uint32_t
7627605SMark.Johnson@Sun.COM ucode_load_amd(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp)
7637605SMark.Johnson@Sun.COM {
7647605SMark.Johnson@Sun.COM 	ucode_file_amd_t *ucodefp = ufp->amd;
7657605SMark.Johnson@Sun.COM #ifdef	__xpv
7667605SMark.Johnson@Sun.COM 	ucode_update_t uus;
7677605SMark.Johnson@Sun.COM #endif
7684581Ssherrym 
7697605SMark.Johnson@Sun.COM 	ASSERT(ucode);
7707605SMark.Johnson@Sun.COM 	ASSERT(ucodefp);
7717605SMark.Johnson@Sun.COM 
7727605SMark.Johnson@Sun.COM #ifndef	__xpv
7737605SMark.Johnson@Sun.COM 	kpreempt_disable();
7747605SMark.Johnson@Sun.COM 	wrmsr(ucode->write_msr, (uintptr_t)ucodefp);
7757605SMark.Johnson@Sun.COM 	ucode->read_rev(uinfop);
7767605SMark.Johnson@Sun.COM 	kpreempt_enable();
777*8647SMark.Johnson@Sun.COM 
778*8647SMark.Johnson@Sun.COM 	return (ucodefp->uf_header.uh_patch_id);
7797605SMark.Johnson@Sun.COM #else
780*8647SMark.Johnson@Sun.COM 	uus.ucodep = ucodefp->ucodep;
781*8647SMark.Johnson@Sun.COM 	uus.usize = ucodefp->usize;
7827605SMark.Johnson@Sun.COM 	ucode_load_xpv(&uus);
7837605SMark.Johnson@Sun.COM 	ucode->read_rev(uinfop);
7847605SMark.Johnson@Sun.COM 	uus.new_rev = uinfop->cui_rev;
785*8647SMark.Johnson@Sun.COM 
786*8647SMark.Johnson@Sun.COM 	return (uus.new_rev);
7877605SMark.Johnson@Sun.COM #endif
7887605SMark.Johnson@Sun.COM }
7897605SMark.Johnson@Sun.COM 
7907605SMark.Johnson@Sun.COM /*ARGSUSED2*/
7917605SMark.Johnson@Sun.COM static uint32_t
7927605SMark.Johnson@Sun.COM ucode_load_intel(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp)
7934581Ssherrym {
7947605SMark.Johnson@Sun.COM 	ucode_file_intel_t *ucodefp = &ufp->intel;
7957605SMark.Johnson@Sun.COM #ifdef __xpv
7967605SMark.Johnson@Sun.COM 	uint32_t ext_offset;
7977605SMark.Johnson@Sun.COM 	uint32_t body_size;
7987605SMark.Johnson@Sun.COM 	uint32_t ext_size;
7997605SMark.Johnson@Sun.COM 	uint8_t *ustart;
8007605SMark.Johnson@Sun.COM 	uint32_t usize;
8017605SMark.Johnson@Sun.COM 	ucode_update_t uus;
8027605SMark.Johnson@Sun.COM #endif
8037605SMark.Johnson@Sun.COM 
8047605SMark.Johnson@Sun.COM 	ASSERT(ucode);
8057605SMark.Johnson@Sun.COM 
8067605SMark.Johnson@Sun.COM #ifdef __xpv
8077605SMark.Johnson@Sun.COM 	/*
8087605SMark.Johnson@Sun.COM 	 * the hypervisor wants the header, data, and extended
8097605SMark.Johnson@Sun.COM 	 * signature tables. We can only get here from the boot
8107605SMark.Johnson@Sun.COM 	 * CPU (cpu #0), we don't need to free as ucode_zalloc() will
8117605SMark.Johnson@Sun.COM 	 * use BOP_ALLOC().
8127605SMark.Johnson@Sun.COM 	 */
8137605SMark.Johnson@Sun.COM 	usize = UCODE_TOTAL_SIZE_INTEL(ucodefp->uf_header->uh_total_size);
8147605SMark.Johnson@Sun.COM 	ustart = ucode_zalloc(cp->cpu_id, usize);
8157605SMark.Johnson@Sun.COM 	ASSERT(ustart);
8167605SMark.Johnson@Sun.COM 
8177605SMark.Johnson@Sun.COM 	body_size = UCODE_BODY_SIZE_INTEL(ucodefp->uf_header->uh_body_size);
8187605SMark.Johnson@Sun.COM 	ext_offset = body_size + UCODE_HEADER_SIZE_INTEL;
8197605SMark.Johnson@Sun.COM 	ext_size = usize - ext_offset;
8207605SMark.Johnson@Sun.COM 	ASSERT(ext_size >= 0);
8217605SMark.Johnson@Sun.COM 
8227605SMark.Johnson@Sun.COM 	(void) memcpy(ustart, ucodefp->uf_header, UCODE_HEADER_SIZE_INTEL);
8237605SMark.Johnson@Sun.COM 	(void) memcpy(&ustart[UCODE_HEADER_SIZE_INTEL], ucodefp->uf_body,
8247605SMark.Johnson@Sun.COM 	    body_size);
8257605SMark.Johnson@Sun.COM 	if (ext_size > 0) {
8267605SMark.Johnson@Sun.COM 		(void) memcpy(&ustart[ext_offset],
8277605SMark.Johnson@Sun.COM 		    ucodefp->uf_ext_table, ext_size);
8287605SMark.Johnson@Sun.COM 	}
8297605SMark.Johnson@Sun.COM 	uus.ucodep = ustart;
8307605SMark.Johnson@Sun.COM 	uus.usize = usize;
8317605SMark.Johnson@Sun.COM 	ucode_load_xpv(&uus);
8327605SMark.Johnson@Sun.COM 	ucode->read_rev(uinfop);
8337605SMark.Johnson@Sun.COM 	uus.new_rev = uinfop->cui_rev;
8347605SMark.Johnson@Sun.COM #else
8354581Ssherrym 	kpreempt_disable();
8367605SMark.Johnson@Sun.COM 	wrmsr(ucode->write_msr, (uintptr_t)ucodefp->uf_body);
8377605SMark.Johnson@Sun.COM 	ucode->read_rev(uinfop);
8384581Ssherrym 	kpreempt_enable();
8397605SMark.Johnson@Sun.COM #endif
8407605SMark.Johnson@Sun.COM 
8417605SMark.Johnson@Sun.COM 	return (ucodefp->uf_header->uh_rev);
8424581Ssherrym }
8434581Ssherrym 
8447347SMark.Johnson@Sun.COM 
8457347SMark.Johnson@Sun.COM #ifdef	__xpv
8467347SMark.Johnson@Sun.COM static void
8477605SMark.Johnson@Sun.COM ucode_load_xpv(ucode_update_t *uusp)
8487347SMark.Johnson@Sun.COM {
8497347SMark.Johnson@Sun.COM 	xen_platform_op_t op;
8507347SMark.Johnson@Sun.COM 	int e;
8517347SMark.Johnson@Sun.COM 
8527347SMark.Johnson@Sun.COM 	ASSERT(DOMAIN_IS_INITDOMAIN(xen_info));
8537347SMark.Johnson@Sun.COM 
8547347SMark.Johnson@Sun.COM 	kpreempt_disable();
8557347SMark.Johnson@Sun.COM 	op.cmd = XENPF_microcode_update;
8567347SMark.Johnson@Sun.COM 	op.interface_version = XENPF_INTERFACE_VERSION;
8577347SMark.Johnson@Sun.COM 	/*LINTED: constant in conditional context*/
8587605SMark.Johnson@Sun.COM 	set_xen_guest_handle(op.u.microcode.data, uusp->ucodep);
8597605SMark.Johnson@Sun.COM 	op.u.microcode.length = uusp->usize;
8607347SMark.Johnson@Sun.COM 	e = HYPERVISOR_platform_op(&op);
8617347SMark.Johnson@Sun.COM 	if (e != 0) {
8627347SMark.Johnson@Sun.COM 		cmn_err(CE_WARN, "hypervisor failed to accept uCode update");
8637347SMark.Johnson@Sun.COM 	}
8647347SMark.Johnson@Sun.COM 	kpreempt_enable();
8657347SMark.Johnson@Sun.COM }
8667347SMark.Johnson@Sun.COM #endif /* __xpv */
8677347SMark.Johnson@Sun.COM 
8687605SMark.Johnson@Sun.COM static void
8697605SMark.Johnson@Sun.COM ucode_read_rev_amd(cpu_ucode_info_t *uinfop)
8707605SMark.Johnson@Sun.COM {
8717605SMark.Johnson@Sun.COM 	uinfop->cui_rev = rdmsr(MSR_AMD_PATCHLEVEL);
8727605SMark.Johnson@Sun.COM }
8737347SMark.Johnson@Sun.COM 
8744581Ssherrym static void
8757605SMark.Johnson@Sun.COM ucode_read_rev_intel(cpu_ucode_info_t *uinfop)
8764581Ssherrym {
8774581Ssherrym 	struct cpuid_regs crs;
8784581Ssherrym 
8794581Ssherrym 	/*
8804581Ssherrym 	 * The Intel 64 and IA-32 Architecture Software Developer's Manual
8814581Ssherrym 	 * recommends that MSR_INTC_UCODE_REV be loaded with 0 first, then
8824581Ssherrym 	 * execute cpuid to guarantee the correct reading of this register.
8834581Ssherrym 	 */
8844581Ssherrym 	wrmsr(MSR_INTC_UCODE_REV, 0);
8854581Ssherrym 	(void) __cpuid_insn(&crs);
8864581Ssherrym 	uinfop->cui_rev = (rdmsr(MSR_INTC_UCODE_REV) >> INTC_UCODE_REV_SHIFT);
8874581Ssherrym }
8884581Ssherrym 
8897605SMark.Johnson@Sun.COM static ucode_errno_t
8907605SMark.Johnson@Sun.COM ucode_extract_amd(ucode_update_t *uusp, uint8_t *ucodep, int size)
8917605SMark.Johnson@Sun.COM {
892*8647SMark.Johnson@Sun.COM #ifndef __xpv
8937605SMark.Johnson@Sun.COM 	uint32_t *ptr = (uint32_t *)ucodep;
8947605SMark.Johnson@Sun.COM 	ucode_eqtbl_amd_t *eqtbl;
8957605SMark.Johnson@Sun.COM 	ucode_file_amd_t *ufp;
896*8647SMark.Johnson@Sun.COM 	int count;
897*8647SMark.Johnson@Sun.COM 	int higher = 0;
898*8647SMark.Johnson@Sun.COM 	ucode_errno_t rc = EM_NOMATCH;
899*8647SMark.Johnson@Sun.COM 	uint16_t eq_sig;
9007605SMark.Johnson@Sun.COM 
9017605SMark.Johnson@Sun.COM 	/* skip over magic number & equivalence table header */
9027605SMark.Johnson@Sun.COM 	ptr += 2; size -= 8;
9037605SMark.Johnson@Sun.COM 
9047605SMark.Johnson@Sun.COM 	count = *ptr++; size -= 4;
9057605SMark.Johnson@Sun.COM 	for (eqtbl = (ucode_eqtbl_amd_t *)ptr;
9067605SMark.Johnson@Sun.COM 	    eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != uusp->sig;
9077605SMark.Johnson@Sun.COM 	    eqtbl++)
9087605SMark.Johnson@Sun.COM 		;
9097605SMark.Johnson@Sun.COM 
9107605SMark.Johnson@Sun.COM 	eq_sig = eqtbl->ue_equiv_cpu;
9117605SMark.Johnson@Sun.COM 
9127605SMark.Johnson@Sun.COM 	/* No equivalent CPU id found, assume outdated microcode file. */
9137605SMark.Johnson@Sun.COM 	if (eq_sig == 0)
9147605SMark.Johnson@Sun.COM 		return (EM_HIGHERREV);
9157605SMark.Johnson@Sun.COM 
9167605SMark.Johnson@Sun.COM 	/* Use the first microcode patch that matches. */
9177605SMark.Johnson@Sun.COM 	do {
9187605SMark.Johnson@Sun.COM 		ptr += count >> 2; size -= count;
9197605SMark.Johnson@Sun.COM 
9207605SMark.Johnson@Sun.COM 		if (!size)
921*8647SMark.Johnson@Sun.COM 			return (higher ? EM_HIGHERREV : EM_NOMATCH);
9227605SMark.Johnson@Sun.COM 
9237605SMark.Johnson@Sun.COM 		ptr++; size -= 4;
9247605SMark.Johnson@Sun.COM 		count = *ptr++; size -= 4;
9257605SMark.Johnson@Sun.COM 		ufp = (ucode_file_amd_t *)ptr;
926*8647SMark.Johnson@Sun.COM 
927*8647SMark.Johnson@Sun.COM 		rc = ucode_match_amd(eq_sig, &uusp->info, ufp, count);
928*8647SMark.Johnson@Sun.COM 		if (rc == EM_HIGHERREV)
929*8647SMark.Johnson@Sun.COM 			higher = 1;
930*8647SMark.Johnson@Sun.COM 	} while (rc != EM_OK);
9317605SMark.Johnson@Sun.COM 
9327605SMark.Johnson@Sun.COM 	uusp->ucodep = (uint8_t *)ufp;
9337605SMark.Johnson@Sun.COM 	uusp->usize = count;
9347605SMark.Johnson@Sun.COM 	uusp->expected_rev = ufp->uf_header.uh_patch_id;
935*8647SMark.Johnson@Sun.COM #else
936*8647SMark.Johnson@Sun.COM 	/*
937*8647SMark.Johnson@Sun.COM 	 * The hypervisor will choose the patch to load, so there is no way to
938*8647SMark.Johnson@Sun.COM 	 * know the "expected revision" in advance. This is especially true on
939*8647SMark.Johnson@Sun.COM 	 * mixed-revision systems where more than one patch will be loaded.
940*8647SMark.Johnson@Sun.COM 	 */
941*8647SMark.Johnson@Sun.COM 	uusp->expected_rev = 0;
942*8647SMark.Johnson@Sun.COM 	uusp->ucodep = ucodep;
943*8647SMark.Johnson@Sun.COM 	uusp->usize = size;
944*8647SMark.Johnson@Sun.COM 
945*8647SMark.Johnson@Sun.COM 	ucode_chipset_amd(ucodep, size);
946*8647SMark.Johnson@Sun.COM #endif
9477605SMark.Johnson@Sun.COM 
9487605SMark.Johnson@Sun.COM 	return (EM_OK);
9497605SMark.Johnson@Sun.COM }
9507605SMark.Johnson@Sun.COM 
9517605SMark.Johnson@Sun.COM static ucode_errno_t
9527605SMark.Johnson@Sun.COM ucode_extract_intel(ucode_update_t *uusp, uint8_t *ucodep, int size)
9537605SMark.Johnson@Sun.COM {
9547605SMark.Johnson@Sun.COM 	uint32_t	header_size = UCODE_HEADER_SIZE_INTEL;
9557605SMark.Johnson@Sun.COM 	int		remaining;
9567605SMark.Johnson@Sun.COM 	int		found = 0;
9577605SMark.Johnson@Sun.COM 	ucode_errno_t	search_rc = EM_NOMATCH; /* search result */
9587605SMark.Johnson@Sun.COM 
9597605SMark.Johnson@Sun.COM 	/*
9607605SMark.Johnson@Sun.COM 	 * Go through the whole buffer in case there are
9617605SMark.Johnson@Sun.COM 	 * multiple versions of matching microcode for this
9627605SMark.Johnson@Sun.COM 	 * processor.
9637605SMark.Johnson@Sun.COM 	 */
9647605SMark.Johnson@Sun.COM 	for (remaining = size; remaining > 0; ) {
9657605SMark.Johnson@Sun.COM 		int	total_size, body_size, ext_size;
9667605SMark.Johnson@Sun.COM 		uint8_t	*curbuf = &ucodep[size - remaining];
9677605SMark.Johnson@Sun.COM 		ucode_header_intel_t *uhp = (ucode_header_intel_t *)curbuf;
9687605SMark.Johnson@Sun.COM 		ucode_ext_table_intel_t *uetp = NULL;
9697605SMark.Johnson@Sun.COM 		ucode_errno_t tmprc;
9707605SMark.Johnson@Sun.COM 
9717605SMark.Johnson@Sun.COM 		total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
9727605SMark.Johnson@Sun.COM 		body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size);
9737605SMark.Johnson@Sun.COM 		ext_size = total_size - (header_size + body_size);
9747605SMark.Johnson@Sun.COM 
9757605SMark.Johnson@Sun.COM 		if (ext_size > 0)
9767605SMark.Johnson@Sun.COM 			uetp = (ucode_ext_table_intel_t *)
9777605SMark.Johnson@Sun.COM 			    &curbuf[header_size + body_size];
9787605SMark.Johnson@Sun.COM 
9797605SMark.Johnson@Sun.COM 		tmprc = ucode_match_intel(uusp->sig, &uusp->info, uhp, uetp);
9807605SMark.Johnson@Sun.COM 
9817605SMark.Johnson@Sun.COM 		/*
9827605SMark.Johnson@Sun.COM 		 * Since we are searching through a big file
9837605SMark.Johnson@Sun.COM 		 * containing microcode for pretty much all the
9847605SMark.Johnson@Sun.COM 		 * processors, we are bound to get EM_NOMATCH
9857605SMark.Johnson@Sun.COM 		 * at one point.  However, if we return
9867605SMark.Johnson@Sun.COM 		 * EM_NOMATCH to users, it will really confuse
9877605SMark.Johnson@Sun.COM 		 * them.  Therefore, if we ever find a match of
9887605SMark.Johnson@Sun.COM 		 * a lower rev, we will set return code to
9897605SMark.Johnson@Sun.COM 		 * EM_HIGHERREV.
9907605SMark.Johnson@Sun.COM 		 */
9917605SMark.Johnson@Sun.COM 		if (tmprc == EM_HIGHERREV)
9927605SMark.Johnson@Sun.COM 			search_rc = EM_HIGHERREV;
9937605SMark.Johnson@Sun.COM 
9947605SMark.Johnson@Sun.COM 		if (tmprc == EM_OK &&
9957605SMark.Johnson@Sun.COM 		    uusp->expected_rev < uhp->uh_rev) {
9967605SMark.Johnson@Sun.COM #ifndef __xpv
9977605SMark.Johnson@Sun.COM 			uusp->ucodep = (uint8_t *)&curbuf[header_size];
9987605SMark.Johnson@Sun.COM #else
9997605SMark.Johnson@Sun.COM 			uusp->ucodep = (uint8_t *)curbuf;
10007605SMark.Johnson@Sun.COM #endif
10017605SMark.Johnson@Sun.COM 			uusp->usize =
10027605SMark.Johnson@Sun.COM 			    UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
10037605SMark.Johnson@Sun.COM 			uusp->expected_rev = uhp->uh_rev;
10047605SMark.Johnson@Sun.COM 			found = 1;
10057605SMark.Johnson@Sun.COM 		}
10067605SMark.Johnson@Sun.COM 
10077605SMark.Johnson@Sun.COM 		remaining -= total_size;
10087605SMark.Johnson@Sun.COM 	}
10097605SMark.Johnson@Sun.COM 
10107605SMark.Johnson@Sun.COM 	if (!found)
10117605SMark.Johnson@Sun.COM 		return (search_rc);
10127605SMark.Johnson@Sun.COM 
10137605SMark.Johnson@Sun.COM 	return (EM_OK);
10147605SMark.Johnson@Sun.COM }
10154581Ssherrym /*
10164581Ssherrym  * Entry point to microcode update from the ucode_drv driver.
10174581Ssherrym  *
10184581Ssherrym  * Returns EM_OK on success, corresponding error code on failure.
10194581Ssherrym  */
10204581Ssherrym ucode_errno_t
10214581Ssherrym ucode_update(uint8_t *ucodep, int size)
10224581Ssherrym {
10234581Ssherrym 	int		found = 0;
10244581Ssherrym 	processorid_t	id;
10257605SMark.Johnson@Sun.COM 	ucode_update_t	cached = { 0 };
10267605SMark.Johnson@Sun.COM 	ucode_update_t	*cachedp = NULL;
10274581Ssherrym 	ucode_errno_t	rc = EM_OK;
10284581Ssherrym 	ucode_errno_t	search_rc = EM_NOMATCH; /* search result */
10294581Ssherrym 	cpuset_t cpuset;
10304581Ssherrym 
10317605SMark.Johnson@Sun.COM 	ASSERT(ucode);
10324581Ssherrym 	ASSERT(ucodep);
10334581Ssherrym 	CPUSET_ZERO(cpuset);
10344581Ssherrym 
10357605SMark.Johnson@Sun.COM 	if (!ucode->capable(CPU))
10364581Ssherrym 		return (EM_NOTSUP);
10374581Ssherrym 
10384581Ssherrym 	mutex_enter(&cpu_lock);
10394581Ssherrym 
10404581Ssherrym 	for (id = 0; id < max_ncpus; id++) {
10414581Ssherrym 		cpu_t *cpu;
10427605SMark.Johnson@Sun.COM 		ucode_update_t uus = { 0 };
10437605SMark.Johnson@Sun.COM 		ucode_update_t *uusp = &uus;
10444581Ssherrym 
10454581Ssherrym 		/*
10464581Ssherrym 		 * If there is no such CPU or it is not xcall ready, skip it.
10474581Ssherrym 		 */
10484581Ssherrym 		if ((cpu = cpu_get(id)) == NULL ||
10494581Ssherrym 		    !(cpu->cpu_flags & CPU_READY))
10504581Ssherrym 			continue;
10514581Ssherrym 
10524581Ssherrym 		uusp->sig = cpuid_getsig(cpu);
10534581Ssherrym 		bcopy(cpu->cpu_m.mcpu_ucode_info, &uusp->info,
10544581Ssherrym 		    sizeof (uusp->info));
10554581Ssherrym 
10564581Ssherrym 		/*
10574581Ssherrym 		 * If the current CPU has the same signature and platform
10584581Ssherrym 		 * id as the previous one we processed, reuse the information.
10594581Ssherrym 		 */
10604581Ssherrym 		if (cachedp && cachedp->sig == cpuid_getsig(cpu) &&
10614581Ssherrym 		    cachedp->info.cui_platid == uusp->info.cui_platid) {
10624581Ssherrym 			uusp->ucodep = cachedp->ucodep;
10634581Ssherrym 			uusp->expected_rev = cachedp->expected_rev;
10644581Ssherrym 			/*
10654581Ssherrym 			 * Intuitively we should check here to see whether the
10664581Ssherrym 			 * running microcode rev is >= the expected rev, and
10674581Ssherrym 			 * quit if it is.  But we choose to proceed with the
10684581Ssherrym 			 * xcall regardless of the running version so that
10694581Ssherrym 			 * the other threads in an HT processor can update
10704581Ssherrym 			 * the cpu_ucode_info structure in machcpu.
10714581Ssherrym 			 */
10727605SMark.Johnson@Sun.COM 		} else if ((search_rc = ucode->extract(uusp, ucodep, size))
10737605SMark.Johnson@Sun.COM 		    == EM_OK) {
10747605SMark.Johnson@Sun.COM 			bcopy(uusp, &cached, sizeof (cached));
10757605SMark.Johnson@Sun.COM 			cachedp = &cached;
10767605SMark.Johnson@Sun.COM 			found = 1;
10774581Ssherrym 		}
10784581Ssherrym 
10794581Ssherrym 		/* Nothing to do */
10804581Ssherrym 		if (uusp->ucodep == NULL)
10814581Ssherrym 			continue;
10824581Ssherrym 
10837347SMark.Johnson@Sun.COM #ifdef	__xpv
10847347SMark.Johnson@Sun.COM 		/*
10857347SMark.Johnson@Sun.COM 		 * for i86xpv, the hypervisor will update all the CPUs.
10867347SMark.Johnson@Sun.COM 		 * the hypervisor wants the header, data, and extended
10877347SMark.Johnson@Sun.COM 		 * signature tables. ucode_write will just read in the
10887347SMark.Johnson@Sun.COM 		 * updated version on all the CPUs after the update has
10897347SMark.Johnson@Sun.COM 		 * completed.
10907347SMark.Johnson@Sun.COM 		 */
10917402SMark.Johnson@Sun.COM 		if (id == 0) {
10927605SMark.Johnson@Sun.COM 			ucode_load_xpv(uusp);
10937402SMark.Johnson@Sun.COM 		}
10947347SMark.Johnson@Sun.COM #endif
10957347SMark.Johnson@Sun.COM 
10964581Ssherrym 		CPUSET_ADD(cpuset, id);
10974581Ssherrym 		kpreempt_disable();
10984581Ssherrym 		xc_sync((xc_arg_t)uusp, 0, 0, X_CALL_HIPRI, cpuset,
10994581Ssherrym 		    ucode_write);
11004581Ssherrym 		kpreempt_enable();
11014581Ssherrym 		CPUSET_DEL(cpuset, id);
11024581Ssherrym 
1103*8647SMark.Johnson@Sun.COM 		if (uusp->new_rev != 0 && uusp->info.cui_rev == uusp->new_rev) {
1104*8647SMark.Johnson@Sun.COM 			rc = EM_HIGHERREV;
1105*8647SMark.Johnson@Sun.COM 		} else if ((uusp->new_rev == 0) || (uusp->expected_rev != 0 &&
1106*8647SMark.Johnson@Sun.COM 		    uusp->expected_rev != uusp->new_rev)) {
11074581Ssherrym 			cmn_err(CE_WARN, ucode_failure_fmt,
11084581Ssherrym 			    id, uusp->info.cui_rev, uusp->expected_rev);
11094581Ssherrym 			rc = EM_UPDATE;
1110*8647SMark.Johnson@Sun.COM 		} else {
1111*8647SMark.Johnson@Sun.COM 			cmn_err(CE_CONT, ucode_success_fmt,
1112*8647SMark.Johnson@Sun.COM 			    id, uusp->info.cui_rev, uusp->new_rev);
11134581Ssherrym 		}
11144581Ssherrym 	}
11154581Ssherrym 
11164581Ssherrym 	mutex_exit(&cpu_lock);
11174581Ssherrym 
11184581Ssherrym 	if (!found)
11194581Ssherrym 		rc = search_rc;
11204581Ssherrym 
11214581Ssherrym 	return (rc);
11224581Ssherrym }
11234581Ssherrym 
11244581Ssherrym /*
11254581Ssherrym  * Initialize mcpu_ucode_info, and perform microcode update if necessary.
11264581Ssherrym  * This is the entry point from boot path where pointer to CPU structure
11274581Ssherrym  * is available.
11284581Ssherrym  *
11294581Ssherrym  * cpuid_info must be initialized before ucode_check can be called.
11304581Ssherrym  */
11314581Ssherrym void
11324581Ssherrym ucode_check(cpu_t *cp)
11334581Ssherrym {
11347605SMark.Johnson@Sun.COM 	cpu_ucode_info_t *uinfop;
11354581Ssherrym 	ucode_errno_t rc = EM_OK;
11367605SMark.Johnson@Sun.COM 	uint32_t new_rev = 0;
11374581Ssherrym 
11384581Ssherrym 	ASSERT(cp);
11394581Ssherrym 	if (cp->cpu_id == 0)
11404581Ssherrym 		cp->cpu_m.mcpu_ucode_info = &cpu_ucode_info0;
11414581Ssherrym 
11424581Ssherrym 	uinfop = cp->cpu_m.mcpu_ucode_info;
11434581Ssherrym 	ASSERT(uinfop);
11444581Ssherrym 
11457605SMark.Johnson@Sun.COM 	/* set up function pointers if not already done */
11467605SMark.Johnson@Sun.COM 	if (!ucode)
11477605SMark.Johnson@Sun.COM 		switch (cpuid_getvendor(cp)) {
11487605SMark.Johnson@Sun.COM 		case X86_VENDOR_AMD:
11497605SMark.Johnson@Sun.COM 			ucode = &ucode_amd;
11507605SMark.Johnson@Sun.COM 			break;
11517605SMark.Johnson@Sun.COM 		case X86_VENDOR_Intel:
11527605SMark.Johnson@Sun.COM 			ucode = &ucode_intel;
11537605SMark.Johnson@Sun.COM 			break;
11547605SMark.Johnson@Sun.COM 		default:
11557605SMark.Johnson@Sun.COM 			return;
11567605SMark.Johnson@Sun.COM 		}
11577605SMark.Johnson@Sun.COM 
11587605SMark.Johnson@Sun.COM 	if (!ucode->capable(cp))
11594581Ssherrym 		return;
11604581Ssherrym 
11614581Ssherrym 	/*
11624581Ssherrym 	 * The MSR_INTC_PLATFORM_ID is supported in Celeron and Xeon
11634581Ssherrym 	 * (Family 6, model 5 and above) and all processors after.
11644581Ssherrym 	 */
11657605SMark.Johnson@Sun.COM 	if ((cpuid_getvendor(cp) == X86_VENDOR_Intel) &&
11667605SMark.Johnson@Sun.COM 	    ((cpuid_getmodel(cp) >= 5) || (cpuid_getfamily(cp) > 6))) {
11674581Ssherrym 		uinfop->cui_platid = 1 << ((rdmsr(MSR_INTC_PLATFORM_ID) >>
11684581Ssherrym 		    INTC_PLATFORM_ID_SHIFT) & INTC_PLATFORM_ID_MASK);
11694581Ssherrym 	}
11704581Ssherrym 
11717605SMark.Johnson@Sun.COM 	ucode->read_rev(uinfop);
11724581Ssherrym 
11737402SMark.Johnson@Sun.COM #ifdef	__xpv
11747402SMark.Johnson@Sun.COM 	/*
11757402SMark.Johnson@Sun.COM 	 * for i86xpv, the hypervisor will update all the CPUs. We only need
11767402SMark.Johnson@Sun.COM 	 * do do this on one of the CPUs (and there always is a CPU 0).
11777402SMark.Johnson@Sun.COM 	 */
11787402SMark.Johnson@Sun.COM 	if (cp->cpu_id != 0) {
11797402SMark.Johnson@Sun.COM 		return;
11807402SMark.Johnson@Sun.COM 	}
11817402SMark.Johnson@Sun.COM #endif
11827402SMark.Johnson@Sun.COM 
11834581Ssherrym 	/*
11844581Ssherrym 	 * Check to see if we need ucode update
11854581Ssherrym 	 */
11867605SMark.Johnson@Sun.COM 	if ((rc = ucode->locate(cp, uinfop, &ucodefile)) == EM_OK) {
11877605SMark.Johnson@Sun.COM 		new_rev = ucode->load(&ucodefile, uinfop, cp);
11887347SMark.Johnson@Sun.COM 
11897605SMark.Johnson@Sun.COM 		if (uinfop->cui_rev != new_rev)
11904581Ssherrym 			cmn_err(CE_WARN, ucode_failure_fmt, cp->cpu_id,
11917605SMark.Johnson@Sun.COM 			    uinfop->cui_rev, new_rev);
11924581Ssherrym 	}
11934581Ssherrym 
11944581Ssherrym 	/*
11954581Ssherrym 	 * If we fail to find a match for any reason, free the file structure
11964581Ssherrym 	 * just in case we have read in a partial file.
11974581Ssherrym 	 *
11984581Ssherrym 	 * Since the scratch memory for holding the microcode for the boot CPU
11994581Ssherrym 	 * came from BOP_ALLOC, we will reset the data structure as if we
12004581Ssherrym 	 * never did the allocation so we don't have to keep track of this
12014581Ssherrym 	 * special chunk of memory.  We free the memory used for the rest
12024581Ssherrym 	 * of the CPUs in start_other_cpus().
12034581Ssherrym 	 */
12044581Ssherrym 	if (rc != EM_OK || cp->cpu_id == 0)
12057605SMark.Johnson@Sun.COM 		ucode->file_reset(&ucodefile, cp->cpu_id);
12064581Ssherrym }
12074581Ssherrym 
12084581Ssherrym /*
12094581Ssherrym  * Returns microcode revision from the machcpu structure.
12104581Ssherrym  */
12114581Ssherrym ucode_errno_t
12124581Ssherrym ucode_get_rev(uint32_t *revp)
12134581Ssherrym {
12144581Ssherrym 	int i;
12154581Ssherrym 
12167605SMark.Johnson@Sun.COM 	ASSERT(ucode);
12174581Ssherrym 	ASSERT(revp);
12184581Ssherrym 
12197605SMark.Johnson@Sun.COM 	if (!ucode->capable(CPU))
12204581Ssherrym 		return (EM_NOTSUP);
12214581Ssherrym 
12224581Ssherrym 	mutex_enter(&cpu_lock);
12234581Ssherrym 	for (i = 0; i < max_ncpus; i++) {
12244581Ssherrym 		cpu_t *cpu;
12254581Ssherrym 
12264581Ssherrym 		if ((cpu = cpu_get(i)) == NULL)
12274581Ssherrym 			continue;
12284581Ssherrym 
12294581Ssherrym 		revp[i] = cpu->cpu_m.mcpu_ucode_info->cui_rev;
12304581Ssherrym 	}
12314581Ssherrym 	mutex_exit(&cpu_lock);
12324581Ssherrym 
12334581Ssherrym 	return (EM_OK);
12344581Ssherrym }
1235