xref: /onnv-gate/usr/src/uts/i86pc/os/microcode.c (revision 12004:93f274d4a367)
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