xref: /titanic_51/usr/src/uts/i86pc/os/microcode.c (revision 88699bdd7d55fd959cedd294efadffd7589fb9fa)
12449e17fSsherrym /*
22449e17fSsherrym  * CDDL HEADER START
32449e17fSsherrym  *
42449e17fSsherrym  * The contents of this file are subject to the terms of the
52449e17fSsherrym  * Common Development and Distribution License (the "License").
62449e17fSsherrym  * You may not use this file except in compliance with the License.
72449e17fSsherrym  *
82449e17fSsherrym  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92449e17fSsherrym  * or http://www.opensolaris.org/os/licensing.
102449e17fSsherrym  * See the License for the specific language governing permissions
112449e17fSsherrym  * and limitations under the License.
122449e17fSsherrym  *
132449e17fSsherrym  * When distributing Covered Code, include this CDDL HEADER in each
142449e17fSsherrym  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152449e17fSsherrym  * If applicable, add the following below this CDDL HEADER, with the
162449e17fSsherrym  * fields enclosed by brackets "[]" replaced with your own identifying
172449e17fSsherrym  * information: Portions Copyright [yyyy] [name of copyright owner]
182449e17fSsherrym  *
192449e17fSsherrym  * CDDL HEADER END
202449e17fSsherrym  */
212449e17fSsherrym 
222449e17fSsherrym /*
23*88699bddSsherrym  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
242449e17fSsherrym  * Use is subject to license terms.
252449e17fSsherrym  */
262449e17fSsherrym 
272449e17fSsherrym #pragma ident	"%Z%%M%	%I%	%E% SMI"
282449e17fSsherrym 
292449e17fSsherrym #include <sys/asm_linkage.h>
302449e17fSsherrym #include <sys/bootconf.h>
312449e17fSsherrym #include <sys/cpuvar.h>
322449e17fSsherrym #include <sys/cmn_err.h>
332449e17fSsherrym #include <sys/controlregs.h>
342449e17fSsherrym #include <sys/debug.h>
352449e17fSsherrym #include <sys/kobj.h>
362449e17fSsherrym #include <sys/kobj_impl.h>
372449e17fSsherrym #include <sys/machsystm.h>
382449e17fSsherrym #include <sys/param.h>
392449e17fSsherrym #include <sys/machparam.h>
402449e17fSsherrym #include <sys/promif.h>
412449e17fSsherrym #include <sys/sysmacros.h>
422449e17fSsherrym #include <sys/systm.h>
432449e17fSsherrym #include <sys/types.h>
442449e17fSsherrym #include <sys/thread.h>
452449e17fSsherrym #include <sys/ucode.h>
462449e17fSsherrym #include <sys/x86_archext.h>
472449e17fSsherrym #include <sys/x_call.h>
48843e1988Sjohnlev #ifdef	__xpv
49843e1988Sjohnlev #include <sys/hypervisor.h>
50843e1988Sjohnlev #endif
512449e17fSsherrym 
522449e17fSsherrym /*
532449e17fSsherrym  * Microcode specific information per core
542449e17fSsherrym  */
552449e17fSsherrym struct cpu_ucode_info {
562449e17fSsherrym 	uint32_t	cui_platid;	/* platform id */
572449e17fSsherrym 	uint32_t	cui_rev;	/* microcode revision */
582449e17fSsherrym };
592449e17fSsherrym 
602449e17fSsherrym /*
612449e17fSsherrym  * Data structure used for xcall
622449e17fSsherrym  */
632449e17fSsherrym struct ucode_update_struct {
642449e17fSsherrym 	uint32_t		sig;	/* signature */
652449e17fSsherrym 	struct cpu_ucode_info	info;	/* ucode info */
662449e17fSsherrym 	uint32_t		expected_rev;
672449e17fSsherrym 	uint32_t		new_rev;
682449e17fSsherrym 	uint8_t			*ucodep; /* pointer to ucode body */
692449e17fSsherrym };
702449e17fSsherrym 
712449e17fSsherrym /*
722449e17fSsherrym  * mcpu_ucode_info for the boot CPU.  Statically allocated.
732449e17fSsherrym  */
742449e17fSsherrym static struct cpu_ucode_info cpu_ucode_info0;
752449e17fSsherrym 
762449e17fSsherrym static ucode_file_t ucodefile = { 0 };
772449e17fSsherrym 
782449e17fSsherrym static int ucode_capable(cpu_t *);
792449e17fSsherrym static void ucode_file_reset(ucode_file_t *, processorid_t);
802449e17fSsherrym static ucode_errno_t ucode_match(int, struct cpu_ucode_info *,
812449e17fSsherrym     ucode_header_t *, ucode_ext_table_t *);
822449e17fSsherrym static ucode_errno_t ucode_locate(cpu_t *, struct cpu_ucode_info *,
832449e17fSsherrym     ucode_file_t *);
842449e17fSsherrym static void ucode_update_intel(uint8_t *, struct cpu_ucode_info *);
852449e17fSsherrym static void ucode_read_rev(struct cpu_ucode_info *);
862449e17fSsherrym 
872449e17fSsherrym static const char ucode_failure_fmt[] =
88*88699bddSsherrym 	"cpu%d: failed to update microcode from version 0x%x to 0x%x\n";
892449e17fSsherrym static const char ucode_success_fmt[] =
90*88699bddSsherrym 	"?cpu%d: microcode has been updated from version 0x%x to 0x%x\n";
912449e17fSsherrym 
922449e17fSsherrym /*
932449e17fSsherrym  * Force flag.  If set, the first microcode binary that matches
942449e17fSsherrym  * signature and platform id will be used for microcode update,
952449e17fSsherrym  * regardless of version.  Should only be used for debugging.
962449e17fSsherrym  */
972449e17fSsherrym int ucode_force_update = 0;
982449e17fSsherrym 
992449e17fSsherrym /*
1002449e17fSsherrym  * Allocate space for mcpu_ucode_info in the machcpu structure
1012449e17fSsherrym  * for all non-boot CPUs.
1022449e17fSsherrym  */
1032449e17fSsherrym void
1042449e17fSsherrym ucode_alloc_space(cpu_t *cp)
1052449e17fSsherrym {
1062449e17fSsherrym 	ASSERT(cp->cpu_id != 0);
1072449e17fSsherrym 	cp->cpu_m.mcpu_ucode_info =
1082449e17fSsherrym 	    kmem_zalloc(sizeof (*cp->cpu_m.mcpu_ucode_info), KM_SLEEP);
1092449e17fSsherrym }
1102449e17fSsherrym 
1112449e17fSsherrym void
1122449e17fSsherrym ucode_free_space(cpu_t *cp)
1132449e17fSsherrym {
1142449e17fSsherrym 	ASSERT(cp->cpu_id != 0);
1152449e17fSsherrym 	kmem_free(cp->cpu_m.mcpu_ucode_info,
1162449e17fSsherrym 	    sizeof (*cp->cpu_m.mcpu_ucode_info));
1172449e17fSsherrym }
1182449e17fSsherrym 
1192449e17fSsherrym /*
1202449e17fSsherrym  * Called when we are done with microcode update on all processors to free up
1212449e17fSsherrym  * space allocated for the microcode file.
1222449e17fSsherrym  */
1232449e17fSsherrym void
1242449e17fSsherrym ucode_free()
1252449e17fSsherrym {
1262449e17fSsherrym 	ucode_file_reset(&ucodefile, -1);
1272449e17fSsherrym }
1282449e17fSsherrym 
1292449e17fSsherrym /*
1302449e17fSsherrym  * Check whether or not a processor is capable of microcode operations
1312449e17fSsherrym  * Returns 1 if it is capable, 0 if not.
1322449e17fSsherrym  */
133843e1988Sjohnlev /*ARGSUSED*/
1342449e17fSsherrym static int
1352449e17fSsherrym ucode_capable(cpu_t *cp)
1362449e17fSsherrym {
137843e1988Sjohnlev 	/* i86xpv guest domain can't update microcode */
138843e1988Sjohnlev #ifdef	__xpv
139843e1988Sjohnlev 	if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
140843e1988Sjohnlev 		return (0);
141843e1988Sjohnlev 	}
142843e1988Sjohnlev #endif
143843e1988Sjohnlev 
144843e1988Sjohnlev #ifndef	__xpv
1452449e17fSsherrym 	/*
1462449e17fSsherrym 	 * At this point we only support microcode update for Intel
1472449e17fSsherrym 	 * processors family 6 and above.
1482449e17fSsherrym 	 *
1492449e17fSsherrym 	 * We also assume that we don't support a mix of Intel and
1502449e17fSsherrym 	 * AMD processors in the same box.
1512449e17fSsherrym 	 */
1522449e17fSsherrym 	if (cpuid_getvendor(cp) != X86_VENDOR_Intel ||
1532449e17fSsherrym 	    cpuid_getfamily(cp) < 6)
1542449e17fSsherrym 		return (0);
1552449e17fSsherrym 	else
1562449e17fSsherrym 		return (1);
157843e1988Sjohnlev #else
158843e1988Sjohnlev 	/*
159843e1988Sjohnlev 	 * XXPV - remove when microcode loading works in dom0. Don't support
160843e1988Sjohnlev 	 * microcode loading in dom0 right now.
161843e1988Sjohnlev 	 */
162843e1988Sjohnlev 	return (0);
163843e1988Sjohnlev #endif
1642449e17fSsherrym }
1652449e17fSsherrym 
1662449e17fSsherrym /*
1672449e17fSsherrym  * Called when it is no longer necessary to keep the microcode around,
1682449e17fSsherrym  * or when the cached microcode doesn't match the CPU being processed.
1692449e17fSsherrym  */
1702449e17fSsherrym static void
1712449e17fSsherrym ucode_file_reset(ucode_file_t *ucodefp, processorid_t id)
1722449e17fSsherrym {
1732449e17fSsherrym 	int total_size, body_size;
1742449e17fSsherrym 
1752449e17fSsherrym 	if (ucodefp == NULL)
1762449e17fSsherrym 		return;
1772449e17fSsherrym 
1782449e17fSsherrym 	total_size = UCODE_TOTAL_SIZE(ucodefp->uf_header.uh_total_size);
1792449e17fSsherrym 	body_size = UCODE_BODY_SIZE(ucodefp->uf_header.uh_body_size);
1802449e17fSsherrym 	if (ucodefp->uf_body) {
1812449e17fSsherrym 		/*
1822449e17fSsherrym 		 * Space for the boot CPU is allocated with BOP_ALLOC()
1832449e17fSsherrym 		 * and does not require a free.
1842449e17fSsherrym 		 */
1852449e17fSsherrym 		if (id != 0)
1862449e17fSsherrym 			kmem_free(ucodefp->uf_body, body_size);
1872449e17fSsherrym 		ucodefp->uf_body = NULL;
1882449e17fSsherrym 	}
1892449e17fSsherrym 
1902449e17fSsherrym 	if (ucodefp->uf_ext_table) {
1912449e17fSsherrym 		int size = total_size - body_size - UCODE_HEADER_SIZE;
1922449e17fSsherrym 		/*
1932449e17fSsherrym 		 * Space for the boot CPU is allocated with BOP_ALLOC()
1942449e17fSsherrym 		 * and does not require a free.
1952449e17fSsherrym 		 */
1962449e17fSsherrym 		if (id != 0)
1972449e17fSsherrym 			kmem_free(ucodefp->uf_ext_table, size);
1982449e17fSsherrym 		ucodefp->uf_ext_table = NULL;
1992449e17fSsherrym 	}
2002449e17fSsherrym 
2012449e17fSsherrym 	bzero(&ucodefp->uf_header, UCODE_HEADER_SIZE);
2022449e17fSsherrym }
2032449e17fSsherrym 
2042449e17fSsherrym /*
2052449e17fSsherrym  * Populate the ucode file structure from microcode file corresponding to
2062449e17fSsherrym  * this CPU, if exists.
2072449e17fSsherrym  *
2082449e17fSsherrym  * Return EM_OK on success, corresponding error code on failure.
2092449e17fSsherrym  */
2102449e17fSsherrym static ucode_errno_t
2112449e17fSsherrym ucode_locate(cpu_t *cp, struct cpu_ucode_info *uinfop, ucode_file_t *ucodefp)
2122449e17fSsherrym {
2132449e17fSsherrym 	char		name[MAXPATHLEN];
2142449e17fSsherrym 	intptr_t	fd;
2152449e17fSsherrym 	int		count;
2162449e17fSsherrym 	int		header_size = UCODE_HEADER_SIZE;
2172449e17fSsherrym 	int		cpi_sig = cpuid_getsig(cp);
2182449e17fSsherrym 	ucode_errno_t	rc = EM_OK;
2192449e17fSsherrym 
2202449e17fSsherrym 	/*
2212449e17fSsherrym 	 * If the microcode matches the CPU we are processing, use it.
2222449e17fSsherrym 	 */
2232449e17fSsherrym 	if (ucode_match(cpi_sig, uinfop, &ucodefp->uf_header,
2242449e17fSsherrym 	    ucodefp->uf_ext_table) == EM_OK && ucodefp->uf_body != NULL) {
2252449e17fSsherrym 		return (EM_OK);
2262449e17fSsherrym 	}
2272449e17fSsherrym 
2282449e17fSsherrym 	/*
2292449e17fSsherrym 	 * Look for microcode file with the right name.
2302449e17fSsherrym 	 */
2312449e17fSsherrym 	(void) snprintf(name, MAXPATHLEN, "/%s/%s/%08X-%02X",
2322449e17fSsherrym 	    UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), cpi_sig,
2332449e17fSsherrym 	    uinfop->cui_platid);
2342449e17fSsherrym 	if ((fd = kobj_open(name)) == -1) {
2352449e17fSsherrym 		return (EM_OPENFILE);
2362449e17fSsherrym 	}
2372449e17fSsherrym 
2382449e17fSsherrym 	/*
2392449e17fSsherrym 	 * We found a microcode file for the CPU we are processing,
2402449e17fSsherrym 	 * reset the microcode data structure and read in the new
2412449e17fSsherrym 	 * file.
2422449e17fSsherrym 	 */
2432449e17fSsherrym 	ucode_file_reset(ucodefp, cp->cpu_id);
2442449e17fSsherrym 
2452449e17fSsherrym 	count = kobj_read(fd, (char *)&ucodefp->uf_header, header_size, 0);
2462449e17fSsherrym 
2472449e17fSsherrym 	switch (count) {
2482449e17fSsherrym 	case UCODE_HEADER_SIZE: {
2492449e17fSsherrym 
2502449e17fSsherrym 		ucode_header_t	*uhp = &ucodefp->uf_header;
2512449e17fSsherrym 		uint32_t	offset = header_size;
2522449e17fSsherrym 		int		total_size, body_size, ext_size;
2532449e17fSsherrym 		uint32_t	sum = 0;
2542449e17fSsherrym 
2552449e17fSsherrym 		/*
2562449e17fSsherrym 		 * Make sure that the header contains valid fields.
2572449e17fSsherrym 		 */
2582449e17fSsherrym 		if ((rc = ucode_header_validate(uhp)) == EM_OK) {
2592449e17fSsherrym 			total_size = UCODE_TOTAL_SIZE(uhp->uh_total_size);
2602449e17fSsherrym 			body_size = UCODE_BODY_SIZE(uhp->uh_body_size);
2612449e17fSsherrym 			if (cp->cpu_id != 0) {
2622449e17fSsherrym 				if ((ucodefp->uf_body = kmem_zalloc(body_size,
2632449e17fSsherrym 				    KM_NOSLEEP)) == NULL) {
2642449e17fSsherrym 					rc = EM_NOMEM;
2652449e17fSsherrym 					break;
2662449e17fSsherrym 				}
2672449e17fSsherrym 			} else {
2682449e17fSsherrym 				/*
2692449e17fSsherrym 				 * BOP_ALLOC() failure results in panic so we
2702449e17fSsherrym 				 * don't have to check for NULL return.
2712449e17fSsherrym 				 */
2722449e17fSsherrym 				ucodefp->uf_body =
2732449e17fSsherrym 				    (uint8_t *)BOP_ALLOC(bootops,
2742449e17fSsherrym 				    NULL, body_size, MMU_PAGESIZE);
2752449e17fSsherrym 			}
2762449e17fSsherrym 
2772449e17fSsherrym 			if (kobj_read(fd, (char *)ucodefp->uf_body,
2782449e17fSsherrym 			    body_size, offset) != body_size)
2792449e17fSsherrym 				rc = EM_FILESIZE;
2802449e17fSsherrym 		}
2812449e17fSsherrym 
2822449e17fSsherrym 		if (rc)
2832449e17fSsherrym 			break;
2842449e17fSsherrym 
2852449e17fSsherrym 		sum = ucode_checksum(0, header_size,
2862449e17fSsherrym 		    (uint8_t *)&ucodefp->uf_header);
2872449e17fSsherrym 		if (ucode_checksum(sum, body_size, ucodefp->uf_body)) {
2882449e17fSsherrym 			rc = EM_CHECKSUM;
2892449e17fSsherrym 			break;
2902449e17fSsherrym 		}
2912449e17fSsherrym 
2922449e17fSsherrym 		/*
2932449e17fSsherrym 		 * Check to see if there is extended signature table.
2942449e17fSsherrym 		 */
2952449e17fSsherrym 		offset = body_size + header_size;
2962449e17fSsherrym 		ext_size = total_size - offset;
2972449e17fSsherrym 
2982449e17fSsherrym 		if (ext_size <= 0)
2992449e17fSsherrym 			break;
3002449e17fSsherrym 
3012449e17fSsherrym 		if (cp->cpu_id != 0) {
3022449e17fSsherrym 			if ((ucodefp->uf_ext_table = kmem_zalloc(ext_size,
3032449e17fSsherrym 			    KM_NOSLEEP)) == NULL) {
3042449e17fSsherrym 				rc = EM_NOMEM;
3052449e17fSsherrym 				break;
3062449e17fSsherrym 			}
3072449e17fSsherrym 		} else {
3082449e17fSsherrym 			/*
3092449e17fSsherrym 			 * BOP_ALLOC() failure results in panic so we
3102449e17fSsherrym 			 * don't have to check for NULL return.
3112449e17fSsherrym 			 */
3122449e17fSsherrym 			ucodefp->uf_ext_table =
3132449e17fSsherrym 			    (ucode_ext_table_t *)BOP_ALLOC(bootops, NULL,
3142449e17fSsherrym 			    ext_size, MMU_PAGESIZE);
3152449e17fSsherrym 		}
3162449e17fSsherrym 
3172449e17fSsherrym 		if (kobj_read(fd, (char *)ucodefp->uf_ext_table,
3182449e17fSsherrym 		    ext_size, offset) != ext_size) {
3192449e17fSsherrym 			rc = EM_FILESIZE;
3202449e17fSsherrym 		} else if (ucode_checksum(0, ext_size,
3212449e17fSsherrym 		    (uint8_t *)(ucodefp->uf_ext_table))) {
3222449e17fSsherrym 			rc = EM_CHECKSUM;
3232449e17fSsherrym 		} else {
3242449e17fSsherrym 			int i;
3252449e17fSsherrym 
3262449e17fSsherrym 			ext_size -= UCODE_EXT_TABLE_SIZE;
3272449e17fSsherrym 			for (i = 0; i < ucodefp->uf_ext_table->uet_count;
3282449e17fSsherrym 			    i++) {
3292449e17fSsherrym 				if (ucode_checksum(0, UCODE_EXT_SIG_SIZE,
3302449e17fSsherrym 				    (uint8_t *)(&(ucodefp->uf_ext_table->
3312449e17fSsherrym 				    uet_ext_sig[i])))) {
3322449e17fSsherrym 					rc = EM_CHECKSUM;
3332449e17fSsherrym 					break;
3342449e17fSsherrym 				}
3352449e17fSsherrym 			}
3362449e17fSsherrym 		}
3372449e17fSsherrym 		break;
3382449e17fSsherrym 	}
3392449e17fSsherrym 
3402449e17fSsherrym 	default:
3412449e17fSsherrym 		rc = EM_FILESIZE;
3422449e17fSsherrym 		break;
3432449e17fSsherrym 	}
3442449e17fSsherrym 
3452449e17fSsherrym 	kobj_close(fd);
3462449e17fSsherrym 
3472449e17fSsherrym 	if (rc != EM_OK)
3482449e17fSsherrym 		return (rc);
3492449e17fSsherrym 
3502449e17fSsherrym 	rc = ucode_match(cpi_sig, uinfop, &ucodefp->uf_header,
3512449e17fSsherrym 	    ucodefp->uf_ext_table);
3522449e17fSsherrym 
3532449e17fSsherrym 	return (rc);
3542449e17fSsherrym }
3552449e17fSsherrym 
3562449e17fSsherrym 
3572449e17fSsherrym /*
3582449e17fSsherrym  * Returns 1 if the microcode is for this processor; 0 otherwise.
3592449e17fSsherrym  */
3602449e17fSsherrym static ucode_errno_t
3612449e17fSsherrym ucode_match(int cpi_sig, struct cpu_ucode_info *uinfop,
3622449e17fSsherrym     ucode_header_t *uhp, ucode_ext_table_t *uetp)
3632449e17fSsherrym {
3642449e17fSsherrym 	ASSERT(uhp);
3652449e17fSsherrym 
3662449e17fSsherrym 	if (UCODE_MATCH(cpi_sig, uhp->uh_signature,
3672449e17fSsherrym 	    uinfop->cui_platid, uhp->uh_proc_flags)) {
3682449e17fSsherrym 
3692449e17fSsherrym 		if (uinfop->cui_rev >= uhp->uh_rev && !ucode_force_update)
3702449e17fSsherrym 			return (EM_HIGHERREV);
3712449e17fSsherrym 
3722449e17fSsherrym 		return (EM_OK);
3732449e17fSsherrym 	}
3742449e17fSsherrym 
3752449e17fSsherrym 	if (uetp != NULL) {
3762449e17fSsherrym 		int i;
3772449e17fSsherrym 
3782449e17fSsherrym 		for (i = 0; i < uetp->uet_count; i++) {
3792449e17fSsherrym 			ucode_ext_sig_t *uesp;
3802449e17fSsherrym 
3812449e17fSsherrym 			uesp = &uetp->uet_ext_sig[i];
3822449e17fSsherrym 
3832449e17fSsherrym 			if (UCODE_MATCH(cpi_sig, uesp->ues_signature,
3842449e17fSsherrym 			    uinfop->cui_platid, uesp->ues_proc_flags)) {
3852449e17fSsherrym 
3862449e17fSsherrym 				if (uinfop->cui_rev >= uhp->uh_rev &&
3872449e17fSsherrym 				    !ucode_force_update)
3882449e17fSsherrym 					return (EM_HIGHERREV);
3892449e17fSsherrym 
3902449e17fSsherrym 				return (EM_OK);
3912449e17fSsherrym 			}
3922449e17fSsherrym 		}
3932449e17fSsherrym 	}
3942449e17fSsherrym 
3952449e17fSsherrym 	return (EM_NOMATCH);
3962449e17fSsherrym }
3972449e17fSsherrym 
3982449e17fSsherrym /*ARGSUSED*/
3992449e17fSsherrym static int
4002449e17fSsherrym ucode_write(xc_arg_t arg1, xc_arg_t unused2, xc_arg_t unused3)
4012449e17fSsherrym {
4022449e17fSsherrym 	struct ucode_update_struct *uusp = (struct ucode_update_struct *)arg1;
4032449e17fSsherrym 	struct cpu_ucode_info *uinfop = CPU->cpu_m.mcpu_ucode_info;
4042449e17fSsherrym 
4052449e17fSsherrym 	ASSERT(uusp->ucodep);
4062449e17fSsherrym 
4072449e17fSsherrym 	/*
4082449e17fSsherrym 	 * Check one more time to see if it is really necessary to update
4092449e17fSsherrym 	 * microcode just in case this is a hyperthreaded processor where
4102449e17fSsherrym 	 * the threads share the same microcode.
4112449e17fSsherrym 	 */
4122449e17fSsherrym 	if (!ucode_force_update) {
4132449e17fSsherrym 		ucode_read_rev(uinfop);
4142449e17fSsherrym 		uusp->new_rev = uinfop->cui_rev;
4152449e17fSsherrym 		if (uinfop->cui_rev >= uusp->expected_rev)
4162449e17fSsherrym 			return (0);
4172449e17fSsherrym 	}
4182449e17fSsherrym 
4192449e17fSsherrym 	wrmsr(MSR_INTC_UCODE_WRITE,
4202449e17fSsherrym 	    (uint64_t)(intptr_t)(uusp->ucodep));
4212449e17fSsherrym 	ucode_read_rev(uinfop);
4222449e17fSsherrym 	uusp->new_rev = uinfop->cui_rev;
4232449e17fSsherrym 
4242449e17fSsherrym 	return (0);
4252449e17fSsherrym }
4262449e17fSsherrym 
4272449e17fSsherrym 
4282449e17fSsherrym static void
4292449e17fSsherrym ucode_update_intel(uint8_t *ucode_body, struct cpu_ucode_info *uinfop)
4302449e17fSsherrym {
4312449e17fSsherrym 	kpreempt_disable();
4322449e17fSsherrym 	wrmsr(MSR_INTC_UCODE_WRITE, (uint64_t)(uintptr_t)ucode_body);
4332449e17fSsherrym 	ucode_read_rev(uinfop);
4342449e17fSsherrym 	kpreempt_enable();
4352449e17fSsherrym }
4362449e17fSsherrym 
4372449e17fSsherrym static void
4382449e17fSsherrym ucode_read_rev(struct cpu_ucode_info *uinfop)
4392449e17fSsherrym {
4402449e17fSsherrym 	struct cpuid_regs crs;
4412449e17fSsherrym 
4422449e17fSsherrym 	/*
4432449e17fSsherrym 	 * The Intel 64 and IA-32 Architecture Software Developer's Manual
4442449e17fSsherrym 	 * recommends that MSR_INTC_UCODE_REV be loaded with 0 first, then
4452449e17fSsherrym 	 * execute cpuid to guarantee the correct reading of this register.
4462449e17fSsherrym 	 */
4472449e17fSsherrym 	wrmsr(MSR_INTC_UCODE_REV, 0);
4482449e17fSsherrym 	(void) __cpuid_insn(&crs);
4492449e17fSsherrym 	uinfop->cui_rev = (rdmsr(MSR_INTC_UCODE_REV) >> INTC_UCODE_REV_SHIFT);
4502449e17fSsherrym }
4512449e17fSsherrym 
4522449e17fSsherrym /*
4532449e17fSsherrym  * Entry point to microcode update from the ucode_drv driver.
4542449e17fSsherrym  *
4552449e17fSsherrym  * Returns EM_OK on success, corresponding error code on failure.
4562449e17fSsherrym  */
4572449e17fSsherrym ucode_errno_t
4582449e17fSsherrym ucode_update(uint8_t *ucodep, int size)
4592449e17fSsherrym {
4602449e17fSsherrym 	uint32_t	header_size = UCODE_HEADER_SIZE;
4612449e17fSsherrym 	int		remaining;
4622449e17fSsherrym 	int		found = 0;
4632449e17fSsherrym 	processorid_t	id;
4642449e17fSsherrym 	struct ucode_update_struct cached = { 0 };
4652449e17fSsherrym 	struct ucode_update_struct *cachedp = NULL;
4662449e17fSsherrym 	ucode_errno_t	rc = EM_OK;
4672449e17fSsherrym 	ucode_errno_t	search_rc = EM_NOMATCH; /* search result */
4682449e17fSsherrym 	cpuset_t cpuset;
4692449e17fSsherrym 
4702449e17fSsherrym 	ASSERT(ucodep);
4712449e17fSsherrym 
4722449e17fSsherrym 	CPUSET_ZERO(cpuset);
4732449e17fSsherrym 
4742449e17fSsherrym 	if (!ucode_capable(CPU))
4752449e17fSsherrym 		return (EM_NOTSUP);
4762449e17fSsherrym 
4772449e17fSsherrym 	mutex_enter(&cpu_lock);
4782449e17fSsherrym 
4792449e17fSsherrym 	for (id = 0; id < max_ncpus; id++) {
4802449e17fSsherrym 		cpu_t *cpu;
4812449e17fSsherrym 		struct ucode_update_struct uus = { 0 };
4822449e17fSsherrym 		struct ucode_update_struct *uusp = &uus;
4832449e17fSsherrym 
4842449e17fSsherrym 		/*
4852449e17fSsherrym 		 * If there is no such CPU or it is not xcall ready, skip it.
4862449e17fSsherrym 		 */
4872449e17fSsherrym 		if ((cpu = cpu_get(id)) == NULL ||
4882449e17fSsherrym 		    !(cpu->cpu_flags & CPU_READY))
4892449e17fSsherrym 			continue;
4902449e17fSsherrym 
4912449e17fSsherrym 		uusp->sig = cpuid_getsig(cpu);
4922449e17fSsherrym 		bcopy(cpu->cpu_m.mcpu_ucode_info, &uusp->info,
4932449e17fSsherrym 		    sizeof (uusp->info));
4942449e17fSsherrym 
4952449e17fSsherrym 		/*
4962449e17fSsherrym 		 * If the current CPU has the same signature and platform
4972449e17fSsherrym 		 * id as the previous one we processed, reuse the information.
4982449e17fSsherrym 		 */
4992449e17fSsherrym 		if (cachedp && cachedp->sig == cpuid_getsig(cpu) &&
5002449e17fSsherrym 		    cachedp->info.cui_platid == uusp->info.cui_platid) {
5012449e17fSsherrym 			uusp->ucodep = cachedp->ucodep;
5022449e17fSsherrym 			uusp->expected_rev = cachedp->expected_rev;
5032449e17fSsherrym 			/*
5042449e17fSsherrym 			 * Intuitively we should check here to see whether the
5052449e17fSsherrym 			 * running microcode rev is >= the expected rev, and
5062449e17fSsherrym 			 * quit if it is.  But we choose to proceed with the
5072449e17fSsherrym 			 * xcall regardless of the running version so that
5082449e17fSsherrym 			 * the other threads in an HT processor can update
5092449e17fSsherrym 			 * the cpu_ucode_info structure in machcpu.
5102449e17fSsherrym 			 */
5112449e17fSsherrym 		} else {
5122449e17fSsherrym 			/*
5132449e17fSsherrym 			 * Go through the whole buffer in case there are
5142449e17fSsherrym 			 * multiple versions of matching microcode for this
5152449e17fSsherrym 			 * processor.
5162449e17fSsherrym 			 */
5172449e17fSsherrym 			for (remaining = size; remaining > 0; ) {
5182449e17fSsherrym 				int	total_size, body_size, ext_size;
5192449e17fSsherrym 				uint8_t	*curbuf = &ucodep[size - remaining];
5202449e17fSsherrym 				ucode_header_t	*uhp = (ucode_header_t *)curbuf;
5212449e17fSsherrym 				ucode_ext_table_t *uetp = NULL;
5222449e17fSsherrym 				ucode_errno_t tmprc;
5232449e17fSsherrym 
5242449e17fSsherrym 				total_size =
5252449e17fSsherrym 				    UCODE_TOTAL_SIZE(uhp->uh_total_size);
5262449e17fSsherrym 				body_size = UCODE_BODY_SIZE(uhp->uh_body_size);
5272449e17fSsherrym 				ext_size = total_size -
5282449e17fSsherrym 				    (header_size + body_size);
5292449e17fSsherrym 
5302449e17fSsherrym 				if (ext_size > 0)
5312449e17fSsherrym 					uetp = (ucode_ext_table_t *)
5322449e17fSsherrym 					    &curbuf[header_size + body_size];
5332449e17fSsherrym 
5342449e17fSsherrym 				tmprc = ucode_match(uusp->sig, &uusp->info,
5352449e17fSsherrym 				    uhp, uetp);
5362449e17fSsherrym 
5372449e17fSsherrym 				/*
5382449e17fSsherrym 				 * Since we are searching through a big file
5392449e17fSsherrym 				 * containing microcode for pretty much all the
5402449e17fSsherrym 				 * processors, we are bound to get EM_NOMATCH
5412449e17fSsherrym 				 * at one point.  However, if we return
5422449e17fSsherrym 				 * EM_NOMATCH to users, it will really confuse
5432449e17fSsherrym 				 * them.  Therefore, if we ever find a match of
5442449e17fSsherrym 				 * a lower rev, we will set return code to
5452449e17fSsherrym 				 * EM_HIGHERREV.
5462449e17fSsherrym 				 */
5472449e17fSsherrym 				if (tmprc == EM_HIGHERREV)
5482449e17fSsherrym 					search_rc = EM_HIGHERREV;
5492449e17fSsherrym 
5502449e17fSsherrym 				if (tmprc == EM_OK &&
5512449e17fSsherrym 				    uusp->expected_rev < uhp->uh_rev) {
5522449e17fSsherrym 					uusp->ucodep = &curbuf[header_size];
5532449e17fSsherrym 					uusp->expected_rev = uhp->uh_rev;
5542449e17fSsherrym 					bcopy(uusp, &cached, sizeof (cached));
5552449e17fSsherrym 					cachedp = &cached;
5562449e17fSsherrym 					found = 1;
5572449e17fSsherrym 				}
5582449e17fSsherrym 
5592449e17fSsherrym 				remaining -= total_size;
5602449e17fSsherrym 			}
5612449e17fSsherrym 		}
5622449e17fSsherrym 
5632449e17fSsherrym 		/* Nothing to do */
5642449e17fSsherrym 		if (uusp->ucodep == NULL)
5652449e17fSsherrym 			continue;
5662449e17fSsherrym 
5672449e17fSsherrym 		CPUSET_ADD(cpuset, id);
5682449e17fSsherrym 		kpreempt_disable();
5692449e17fSsherrym 		xc_sync((xc_arg_t)uusp, 0, 0, X_CALL_HIPRI, cpuset,
5702449e17fSsherrym 		    ucode_write);
5712449e17fSsherrym 		kpreempt_enable();
5722449e17fSsherrym 		CPUSET_DEL(cpuset, id);
5732449e17fSsherrym 
5742449e17fSsherrym 		if (uusp->expected_rev == uusp->new_rev) {
5752449e17fSsherrym 			cmn_err(CE_CONT, ucode_success_fmt,
5762449e17fSsherrym 			    id, uusp->info.cui_rev, uusp->expected_rev);
5772449e17fSsherrym 		} else {
5782449e17fSsherrym 			cmn_err(CE_WARN, ucode_failure_fmt,
5792449e17fSsherrym 			    id, uusp->info.cui_rev, uusp->expected_rev);
5802449e17fSsherrym 			rc = EM_UPDATE;
5812449e17fSsherrym 		}
5822449e17fSsherrym 	}
5832449e17fSsherrym 
5842449e17fSsherrym 	mutex_exit(&cpu_lock);
5852449e17fSsherrym 
5862449e17fSsherrym 	if (!found)
5872449e17fSsherrym 		rc = search_rc;
5882449e17fSsherrym 
5892449e17fSsherrym 	return (rc);
5902449e17fSsherrym }
5912449e17fSsherrym 
5922449e17fSsherrym /*
5932449e17fSsherrym  * Initialize mcpu_ucode_info, and perform microcode update if necessary.
5942449e17fSsherrym  * This is the entry point from boot path where pointer to CPU structure
5952449e17fSsherrym  * is available.
5962449e17fSsherrym  *
5972449e17fSsherrym  * cpuid_info must be initialized before ucode_check can be called.
5982449e17fSsherrym  */
5992449e17fSsherrym void
6002449e17fSsherrym ucode_check(cpu_t *cp)
6012449e17fSsherrym {
6022449e17fSsherrym 	struct cpu_ucode_info *uinfop;
6032449e17fSsherrym 	ucode_errno_t rc = EM_OK;
6042449e17fSsherrym 
6052449e17fSsherrym 	ASSERT(cp);
6062449e17fSsherrym 	if (cp->cpu_id == 0)
6072449e17fSsherrym 		cp->cpu_m.mcpu_ucode_info = &cpu_ucode_info0;
6082449e17fSsherrym 
6092449e17fSsherrym 	uinfop = cp->cpu_m.mcpu_ucode_info;
6102449e17fSsherrym 	ASSERT(uinfop);
6112449e17fSsherrym 
6122449e17fSsherrym 	if (!ucode_capable(cp))
6132449e17fSsherrym 		return;
6142449e17fSsherrym 
6152449e17fSsherrym 	/*
6162449e17fSsherrym 	 * The MSR_INTC_PLATFORM_ID is supported in Celeron and Xeon
6172449e17fSsherrym 	 * (Family 6, model 5 and above) and all processors after.
6182449e17fSsherrym 	 */
6192449e17fSsherrym 	if ((cpuid_getmodel(cp) >= 5) || (cpuid_getfamily(cp) > 6)) {
6202449e17fSsherrym 		uinfop->cui_platid = 1 << ((rdmsr(MSR_INTC_PLATFORM_ID) >>
6212449e17fSsherrym 		    INTC_PLATFORM_ID_SHIFT) & INTC_PLATFORM_ID_MASK);
6222449e17fSsherrym 	}
6232449e17fSsherrym 
6242449e17fSsherrym 	ucode_read_rev(uinfop);
6252449e17fSsherrym 
6262449e17fSsherrym 	/*
6272449e17fSsherrym 	 * Check to see if we need ucode update
6282449e17fSsherrym 	 */
6292449e17fSsherrym 	if ((rc = ucode_locate(cp, uinfop, &ucodefile)) == EM_OK) {
6302449e17fSsherrym 		ucode_update_intel(ucodefile.uf_body, uinfop);
6312449e17fSsherrym 
6322449e17fSsherrym 		if (uinfop->cui_rev != ucodefile.uf_header.uh_rev)
6332449e17fSsherrym 			cmn_err(CE_WARN, ucode_failure_fmt, cp->cpu_id,
6342449e17fSsherrym 			    uinfop->cui_rev, ucodefile.uf_header.uh_rev);
6352449e17fSsherrym 	}
6362449e17fSsherrym 
6372449e17fSsherrym 	/*
6382449e17fSsherrym 	 * If we fail to find a match for any reason, free the file structure
6392449e17fSsherrym 	 * just in case we have read in a partial file.
6402449e17fSsherrym 	 *
6412449e17fSsherrym 	 * Since the scratch memory for holding the microcode for the boot CPU
6422449e17fSsherrym 	 * came from BOP_ALLOC, we will reset the data structure as if we
6432449e17fSsherrym 	 * never did the allocation so we don't have to keep track of this
6442449e17fSsherrym 	 * special chunk of memory.  We free the memory used for the rest
6452449e17fSsherrym 	 * of the CPUs in start_other_cpus().
6462449e17fSsherrym 	 */
6472449e17fSsherrym 	if (rc != EM_OK || cp->cpu_id == 0)
6482449e17fSsherrym 		ucode_file_reset(&ucodefile, cp->cpu_id);
6492449e17fSsherrym }
6502449e17fSsherrym 
6512449e17fSsherrym /*
6522449e17fSsherrym  * Returns microcode revision from the machcpu structure.
6532449e17fSsherrym  */
6542449e17fSsherrym ucode_errno_t
6552449e17fSsherrym ucode_get_rev(uint32_t *revp)
6562449e17fSsherrym {
6572449e17fSsherrym 	int i;
6582449e17fSsherrym 
6592449e17fSsherrym 	ASSERT(revp);
6602449e17fSsherrym 
6612449e17fSsherrym 	if (!ucode_capable(CPU))
6622449e17fSsherrym 		return (EM_NOTSUP);
6632449e17fSsherrym 
6642449e17fSsherrym 	mutex_enter(&cpu_lock);
6652449e17fSsherrym 	for (i = 0; i < max_ncpus; i++) {
6662449e17fSsherrym 		cpu_t *cpu;
6672449e17fSsherrym 
6682449e17fSsherrym 		if ((cpu = cpu_get(i)) == NULL)
6692449e17fSsherrym 			continue;
6702449e17fSsherrym 
6712449e17fSsherrym 		revp[i] = cpu->cpu_m.mcpu_ucode_info->cui_rev;
6722449e17fSsherrym 	}
6732449e17fSsherrym 	mutex_exit(&cpu_lock);
6742449e17fSsherrym 
6752449e17fSsherrym 	return (EM_OK);
6762449e17fSsherrym }
677