xref: /titanic_51/usr/src/uts/i86pc/os/microcode.c (revision c9b5d7d22ea8bfd18ef2f988a885f1cb268f2ec7)
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 /*
2388699bddSsherrym  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
242449e17fSsherrym  * Use is subject to license terms.
252449e17fSsherrym  */
262449e17fSsherrym 
272449e17fSsherrym #include <sys/asm_linkage.h>
282449e17fSsherrym #include <sys/bootconf.h>
292449e17fSsherrym #include <sys/cpuvar.h>
302449e17fSsherrym #include <sys/cmn_err.h>
312449e17fSsherrym #include <sys/controlregs.h>
322449e17fSsherrym #include <sys/debug.h>
332449e17fSsherrym #include <sys/kobj.h>
342449e17fSsherrym #include <sys/kobj_impl.h>
352449e17fSsherrym #include <sys/machsystm.h>
362449e17fSsherrym #include <sys/param.h>
372449e17fSsherrym #include <sys/machparam.h>
382449e17fSsherrym #include <sys/promif.h>
392449e17fSsherrym #include <sys/sysmacros.h>
402449e17fSsherrym #include <sys/systm.h>
412449e17fSsherrym #include <sys/types.h>
422449e17fSsherrym #include <sys/thread.h>
432449e17fSsherrym #include <sys/ucode.h>
442449e17fSsherrym #include <sys/x86_archext.h>
452449e17fSsherrym #include <sys/x_call.h>
46843e1988Sjohnlev #ifdef	__xpv
47843e1988Sjohnlev #include <sys/hypervisor.h>
48843e1988Sjohnlev #endif
492449e17fSsherrym 
502449e17fSsherrym /*
512449e17fSsherrym  * Microcode specific information per core
522449e17fSsherrym  */
532449e17fSsherrym struct cpu_ucode_info {
542449e17fSsherrym 	uint32_t	cui_platid;	/* platform id */
552449e17fSsherrym 	uint32_t	cui_rev;	/* microcode revision */
562449e17fSsherrym };
572449e17fSsherrym 
582449e17fSsherrym /*
592449e17fSsherrym  * Data structure used for xcall
602449e17fSsherrym  */
612449e17fSsherrym struct ucode_update_struct {
622449e17fSsherrym 	uint32_t		sig;	/* signature */
632449e17fSsherrym 	struct cpu_ucode_info	info;	/* ucode info */
642449e17fSsherrym 	uint32_t		expected_rev;
652449e17fSsherrym 	uint32_t		new_rev;
662449e17fSsherrym 	uint8_t			*ucodep; /* pointer to ucode body */
672449e17fSsherrym };
682449e17fSsherrym 
692449e17fSsherrym /*
702449e17fSsherrym  * mcpu_ucode_info for the boot CPU.  Statically allocated.
712449e17fSsherrym  */
722449e17fSsherrym static struct cpu_ucode_info cpu_ucode_info0;
732449e17fSsherrym 
742449e17fSsherrym static ucode_file_t ucodefile = { 0 };
752449e17fSsherrym 
762449e17fSsherrym static int ucode_capable(cpu_t *);
772449e17fSsherrym static void ucode_file_reset(ucode_file_t *, processorid_t);
782449e17fSsherrym static ucode_errno_t ucode_match(int, struct cpu_ucode_info *,
792449e17fSsherrym     ucode_header_t *, ucode_ext_table_t *);
802449e17fSsherrym static ucode_errno_t ucode_locate(cpu_t *, struct cpu_ucode_info *,
812449e17fSsherrym     ucode_file_t *);
822449e17fSsherrym static void ucode_update_intel(uint8_t *, struct cpu_ucode_info *);
832449e17fSsherrym static void ucode_read_rev(struct cpu_ucode_info *);
84882a7af5SMark Johnson #ifdef	__xpv
85882a7af5SMark Johnson static void ucode_update_xpv(struct ucode_update_struct *, uint8_t *, uint32_t);
86882a7af5SMark Johnson #endif
872449e17fSsherrym 
882449e17fSsherrym static const char ucode_failure_fmt[] =
8988699bddSsherrym 	"cpu%d: failed to update microcode from version 0x%x to 0x%x\n";
902449e17fSsherrym static const char ucode_success_fmt[] =
9188699bddSsherrym 	"?cpu%d: microcode has been updated from version 0x%x to 0x%x\n";
922449e17fSsherrym 
932449e17fSsherrym /*
942449e17fSsherrym  * Force flag.  If set, the first microcode binary that matches
952449e17fSsherrym  * signature and platform id will be used for microcode update,
962449e17fSsherrym  * regardless of version.  Should only be used for debugging.
972449e17fSsherrym  */
982449e17fSsherrym int ucode_force_update = 0;
992449e17fSsherrym 
1002449e17fSsherrym /*
1012449e17fSsherrym  * Allocate space for mcpu_ucode_info in the machcpu structure
1022449e17fSsherrym  * for all non-boot CPUs.
1032449e17fSsherrym  */
1042449e17fSsherrym void
1052449e17fSsherrym ucode_alloc_space(cpu_t *cp)
1062449e17fSsherrym {
1072449e17fSsherrym 	ASSERT(cp->cpu_id != 0);
1082449e17fSsherrym 	cp->cpu_m.mcpu_ucode_info =
1092449e17fSsherrym 	    kmem_zalloc(sizeof (*cp->cpu_m.mcpu_ucode_info), KM_SLEEP);
1102449e17fSsherrym }
1112449e17fSsherrym 
1122449e17fSsherrym void
1132449e17fSsherrym ucode_free_space(cpu_t *cp)
1142449e17fSsherrym {
1152449e17fSsherrym 	ASSERT(cp->cpu_id != 0);
1162449e17fSsherrym 	kmem_free(cp->cpu_m.mcpu_ucode_info,
1172449e17fSsherrym 	    sizeof (*cp->cpu_m.mcpu_ucode_info));
1182449e17fSsherrym }
1192449e17fSsherrym 
1202449e17fSsherrym /*
1212449e17fSsherrym  * Called when we are done with microcode update on all processors to free up
1222449e17fSsherrym  * space allocated for the microcode file.
1232449e17fSsherrym  */
1242449e17fSsherrym void
1252449e17fSsherrym ucode_free()
1262449e17fSsherrym {
1272449e17fSsherrym 	ucode_file_reset(&ucodefile, -1);
1282449e17fSsherrym }
1292449e17fSsherrym 
1302449e17fSsherrym /*
1312449e17fSsherrym  * Check whether or not a processor is capable of microcode operations
1322449e17fSsherrym  * Returns 1 if it is capable, 0 if not.
1332449e17fSsherrym  */
1342449e17fSsherrym static int
1352449e17fSsherrym ucode_capable(cpu_t *cp)
1362449e17fSsherrym {
137843e1988Sjohnlev 	/* i86xpv guest domain can't update microcode */
138882a7af5SMark Johnson #ifndef	__xpv
139882a7af5SMark Johnson 	extern int xpv_is_hvm;
140882a7af5SMark Johnson 	if (xpv_is_hvm) {
141882a7af5SMark Johnson 		return (0);
142882a7af5SMark Johnson 	}
143882a7af5SMark Johnson #else
144843e1988Sjohnlev 	if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
145843e1988Sjohnlev 		return (0);
146843e1988Sjohnlev 	}
147843e1988Sjohnlev #endif
148843e1988Sjohnlev 
1492449e17fSsherrym 	/*
1502449e17fSsherrym 	 * At this point we only support microcode update for Intel
1512449e17fSsherrym 	 * processors family 6 and above.
1522449e17fSsherrym 	 *
1532449e17fSsherrym 	 * We also assume that we don't support a mix of Intel and
1542449e17fSsherrym 	 * AMD processors in the same box.
1552449e17fSsherrym 	 */
1562449e17fSsherrym 	if (cpuid_getvendor(cp) != X86_VENDOR_Intel ||
1572449e17fSsherrym 	    cpuid_getfamily(cp) < 6)
1582449e17fSsherrym 		return (0);
1592449e17fSsherrym 	else
1602449e17fSsherrym 		return (1);
1612449e17fSsherrym }
1622449e17fSsherrym 
1632449e17fSsherrym /*
1642449e17fSsherrym  * Called when it is no longer necessary to keep the microcode around,
1652449e17fSsherrym  * or when the cached microcode doesn't match the CPU being processed.
1662449e17fSsherrym  */
1672449e17fSsherrym static void
1682449e17fSsherrym ucode_file_reset(ucode_file_t *ucodefp, processorid_t id)
1692449e17fSsherrym {
1702449e17fSsherrym 	int total_size, body_size;
1712449e17fSsherrym 
1722449e17fSsherrym 	if (ucodefp == NULL)
1732449e17fSsherrym 		return;
1742449e17fSsherrym 
1752449e17fSsherrym 	total_size = UCODE_TOTAL_SIZE(ucodefp->uf_header.uh_total_size);
1762449e17fSsherrym 	body_size = UCODE_BODY_SIZE(ucodefp->uf_header.uh_body_size);
1772449e17fSsherrym 	if (ucodefp->uf_body) {
1782449e17fSsherrym 		/*
1792449e17fSsherrym 		 * Space for the boot CPU is allocated with BOP_ALLOC()
1802449e17fSsherrym 		 * and does not require a free.
1812449e17fSsherrym 		 */
1822449e17fSsherrym 		if (id != 0)
1832449e17fSsherrym 			kmem_free(ucodefp->uf_body, body_size);
1842449e17fSsherrym 		ucodefp->uf_body = NULL;
1852449e17fSsherrym 	}
1862449e17fSsherrym 
1872449e17fSsherrym 	if (ucodefp->uf_ext_table) {
1882449e17fSsherrym 		int size = total_size - body_size - UCODE_HEADER_SIZE;
1892449e17fSsherrym 		/*
1902449e17fSsherrym 		 * Space for the boot CPU is allocated with BOP_ALLOC()
1912449e17fSsherrym 		 * and does not require a free.
1922449e17fSsherrym 		 */
1932449e17fSsherrym 		if (id != 0)
1942449e17fSsherrym 			kmem_free(ucodefp->uf_ext_table, size);
1952449e17fSsherrym 		ucodefp->uf_ext_table = NULL;
1962449e17fSsherrym 	}
1972449e17fSsherrym 
1982449e17fSsherrym 	bzero(&ucodefp->uf_header, UCODE_HEADER_SIZE);
1992449e17fSsherrym }
2002449e17fSsherrym 
2012449e17fSsherrym /*
2022449e17fSsherrym  * Populate the ucode file structure from microcode file corresponding to
2032449e17fSsherrym  * this CPU, if exists.
2042449e17fSsherrym  *
2052449e17fSsherrym  * Return EM_OK on success, corresponding error code on failure.
2062449e17fSsherrym  */
2072449e17fSsherrym static ucode_errno_t
2082449e17fSsherrym ucode_locate(cpu_t *cp, struct cpu_ucode_info *uinfop, ucode_file_t *ucodefp)
2092449e17fSsherrym {
2102449e17fSsherrym 	char		name[MAXPATHLEN];
2112449e17fSsherrym 	intptr_t	fd;
2122449e17fSsherrym 	int		count;
2132449e17fSsherrym 	int		header_size = UCODE_HEADER_SIZE;
2142449e17fSsherrym 	int		cpi_sig = cpuid_getsig(cp);
2152449e17fSsherrym 	ucode_errno_t	rc = EM_OK;
2162449e17fSsherrym 
2172449e17fSsherrym 	/*
2182449e17fSsherrym 	 * If the microcode matches the CPU we are processing, use it.
2192449e17fSsherrym 	 */
2202449e17fSsherrym 	if (ucode_match(cpi_sig, uinfop, &ucodefp->uf_header,
2212449e17fSsherrym 	    ucodefp->uf_ext_table) == EM_OK && ucodefp->uf_body != NULL) {
2222449e17fSsherrym 		return (EM_OK);
2232449e17fSsherrym 	}
2242449e17fSsherrym 
2252449e17fSsherrym 	/*
2262449e17fSsherrym 	 * Look for microcode file with the right name.
2272449e17fSsherrym 	 */
2282449e17fSsherrym 	(void) snprintf(name, MAXPATHLEN, "/%s/%s/%08X-%02X",
2292449e17fSsherrym 	    UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), cpi_sig,
2302449e17fSsherrym 	    uinfop->cui_platid);
2312449e17fSsherrym 	if ((fd = kobj_open(name)) == -1) {
2322449e17fSsherrym 		return (EM_OPENFILE);
2332449e17fSsherrym 	}
2342449e17fSsherrym 
2352449e17fSsherrym 	/*
2362449e17fSsherrym 	 * We found a microcode file for the CPU we are processing,
2372449e17fSsherrym 	 * reset the microcode data structure and read in the new
2382449e17fSsherrym 	 * file.
2392449e17fSsherrym 	 */
2402449e17fSsherrym 	ucode_file_reset(ucodefp, cp->cpu_id);
2412449e17fSsherrym 
2422449e17fSsherrym 	count = kobj_read(fd, (char *)&ucodefp->uf_header, header_size, 0);
2432449e17fSsherrym 
2442449e17fSsherrym 	switch (count) {
2452449e17fSsherrym 	case UCODE_HEADER_SIZE: {
2462449e17fSsherrym 
2472449e17fSsherrym 		ucode_header_t	*uhp = &ucodefp->uf_header;
2482449e17fSsherrym 		uint32_t	offset = header_size;
2492449e17fSsherrym 		int		total_size, body_size, ext_size;
2502449e17fSsherrym 		uint32_t	sum = 0;
2512449e17fSsherrym 
2522449e17fSsherrym 		/*
2532449e17fSsherrym 		 * Make sure that the header contains valid fields.
2542449e17fSsherrym 		 */
2552449e17fSsherrym 		if ((rc = ucode_header_validate(uhp)) == EM_OK) {
2562449e17fSsherrym 			total_size = UCODE_TOTAL_SIZE(uhp->uh_total_size);
2572449e17fSsherrym 			body_size = UCODE_BODY_SIZE(uhp->uh_body_size);
2582449e17fSsherrym 			if (cp->cpu_id != 0) {
2592449e17fSsherrym 				if ((ucodefp->uf_body = kmem_zalloc(body_size,
2602449e17fSsherrym 				    KM_NOSLEEP)) == NULL) {
2612449e17fSsherrym 					rc = EM_NOMEM;
2622449e17fSsherrym 					break;
2632449e17fSsherrym 				}
2642449e17fSsherrym 			} else {
2652449e17fSsherrym 				/*
2662449e17fSsherrym 				 * BOP_ALLOC() failure results in panic so we
2672449e17fSsherrym 				 * don't have to check for NULL return.
2682449e17fSsherrym 				 */
2692449e17fSsherrym 				ucodefp->uf_body =
2702449e17fSsherrym 				    (uint8_t *)BOP_ALLOC(bootops,
2712449e17fSsherrym 				    NULL, body_size, MMU_PAGESIZE);
2722449e17fSsherrym 			}
2732449e17fSsherrym 
2742449e17fSsherrym 			if (kobj_read(fd, (char *)ucodefp->uf_body,
2752449e17fSsherrym 			    body_size, offset) != body_size)
2762449e17fSsherrym 				rc = EM_FILESIZE;
2772449e17fSsherrym 		}
2782449e17fSsherrym 
2792449e17fSsherrym 		if (rc)
2802449e17fSsherrym 			break;
2812449e17fSsherrym 
2822449e17fSsherrym 		sum = ucode_checksum(0, header_size,
2832449e17fSsherrym 		    (uint8_t *)&ucodefp->uf_header);
2842449e17fSsherrym 		if (ucode_checksum(sum, body_size, ucodefp->uf_body)) {
2852449e17fSsherrym 			rc = EM_CHECKSUM;
2862449e17fSsherrym 			break;
2872449e17fSsherrym 		}
2882449e17fSsherrym 
2892449e17fSsherrym 		/*
2902449e17fSsherrym 		 * Check to see if there is extended signature table.
2912449e17fSsherrym 		 */
2922449e17fSsherrym 		offset = body_size + header_size;
2932449e17fSsherrym 		ext_size = total_size - offset;
2942449e17fSsherrym 
2952449e17fSsherrym 		if (ext_size <= 0)
2962449e17fSsherrym 			break;
2972449e17fSsherrym 
2982449e17fSsherrym 		if (cp->cpu_id != 0) {
2992449e17fSsherrym 			if ((ucodefp->uf_ext_table = kmem_zalloc(ext_size,
3002449e17fSsherrym 			    KM_NOSLEEP)) == NULL) {
3012449e17fSsherrym 				rc = EM_NOMEM;
3022449e17fSsherrym 				break;
3032449e17fSsherrym 			}
3042449e17fSsherrym 		} else {
3052449e17fSsherrym 			/*
3062449e17fSsherrym 			 * BOP_ALLOC() failure results in panic so we
3072449e17fSsherrym 			 * don't have to check for NULL return.
3082449e17fSsherrym 			 */
3092449e17fSsherrym 			ucodefp->uf_ext_table =
3102449e17fSsherrym 			    (ucode_ext_table_t *)BOP_ALLOC(bootops, NULL,
3112449e17fSsherrym 			    ext_size, MMU_PAGESIZE);
3122449e17fSsherrym 		}
3132449e17fSsherrym 
3142449e17fSsherrym 		if (kobj_read(fd, (char *)ucodefp->uf_ext_table,
3152449e17fSsherrym 		    ext_size, offset) != ext_size) {
3162449e17fSsherrym 			rc = EM_FILESIZE;
3172449e17fSsherrym 		} else if (ucode_checksum(0, ext_size,
3182449e17fSsherrym 		    (uint8_t *)(ucodefp->uf_ext_table))) {
3192449e17fSsherrym 			rc = EM_CHECKSUM;
3202449e17fSsherrym 		} else {
3212449e17fSsherrym 			int i;
3222449e17fSsherrym 
3232449e17fSsherrym 			ext_size -= UCODE_EXT_TABLE_SIZE;
3242449e17fSsherrym 			for (i = 0; i < ucodefp->uf_ext_table->uet_count;
3252449e17fSsherrym 			    i++) {
3262449e17fSsherrym 				if (ucode_checksum(0, UCODE_EXT_SIG_SIZE,
3272449e17fSsherrym 				    (uint8_t *)(&(ucodefp->uf_ext_table->
3282449e17fSsherrym 				    uet_ext_sig[i])))) {
3292449e17fSsherrym 					rc = EM_CHECKSUM;
3302449e17fSsherrym 					break;
3312449e17fSsherrym 				}
3322449e17fSsherrym 			}
3332449e17fSsherrym 		}
3342449e17fSsherrym 		break;
3352449e17fSsherrym 	}
3362449e17fSsherrym 
3372449e17fSsherrym 	default:
3382449e17fSsherrym 		rc = EM_FILESIZE;
3392449e17fSsherrym 		break;
3402449e17fSsherrym 	}
3412449e17fSsherrym 
3422449e17fSsherrym 	kobj_close(fd);
3432449e17fSsherrym 
3442449e17fSsherrym 	if (rc != EM_OK)
3452449e17fSsherrym 		return (rc);
3462449e17fSsherrym 
3472449e17fSsherrym 	rc = ucode_match(cpi_sig, uinfop, &ucodefp->uf_header,
3482449e17fSsherrym 	    ucodefp->uf_ext_table);
3492449e17fSsherrym 
3502449e17fSsherrym 	return (rc);
3512449e17fSsherrym }
3522449e17fSsherrym 
3532449e17fSsherrym 
3542449e17fSsherrym /*
3552449e17fSsherrym  * Returns 1 if the microcode is for this processor; 0 otherwise.
3562449e17fSsherrym  */
3572449e17fSsherrym static ucode_errno_t
3582449e17fSsherrym ucode_match(int cpi_sig, struct cpu_ucode_info *uinfop,
3592449e17fSsherrym     ucode_header_t *uhp, ucode_ext_table_t *uetp)
3602449e17fSsherrym {
3612449e17fSsherrym 	ASSERT(uhp);
3622449e17fSsherrym 
3632449e17fSsherrym 	if (UCODE_MATCH(cpi_sig, uhp->uh_signature,
3642449e17fSsherrym 	    uinfop->cui_platid, uhp->uh_proc_flags)) {
3652449e17fSsherrym 
3662449e17fSsherrym 		if (uinfop->cui_rev >= uhp->uh_rev && !ucode_force_update)
3672449e17fSsherrym 			return (EM_HIGHERREV);
3682449e17fSsherrym 
3692449e17fSsherrym 		return (EM_OK);
3702449e17fSsherrym 	}
3712449e17fSsherrym 
3722449e17fSsherrym 	if (uetp != NULL) {
3732449e17fSsherrym 		int i;
3742449e17fSsherrym 
3752449e17fSsherrym 		for (i = 0; i < uetp->uet_count; i++) {
3762449e17fSsherrym 			ucode_ext_sig_t *uesp;
3772449e17fSsherrym 
3782449e17fSsherrym 			uesp = &uetp->uet_ext_sig[i];
3792449e17fSsherrym 
3802449e17fSsherrym 			if (UCODE_MATCH(cpi_sig, uesp->ues_signature,
3812449e17fSsherrym 			    uinfop->cui_platid, uesp->ues_proc_flags)) {
3822449e17fSsherrym 
3832449e17fSsherrym 				if (uinfop->cui_rev >= uhp->uh_rev &&
3842449e17fSsherrym 				    !ucode_force_update)
3852449e17fSsherrym 					return (EM_HIGHERREV);
3862449e17fSsherrym 
3872449e17fSsherrym 				return (EM_OK);
3882449e17fSsherrym 			}
3892449e17fSsherrym 		}
3902449e17fSsherrym 	}
3912449e17fSsherrym 
3922449e17fSsherrym 	return (EM_NOMATCH);
3932449e17fSsherrym }
3942449e17fSsherrym 
3952449e17fSsherrym /*ARGSUSED*/
3962449e17fSsherrym static int
3972449e17fSsherrym ucode_write(xc_arg_t arg1, xc_arg_t unused2, xc_arg_t unused3)
3982449e17fSsherrym {
3992449e17fSsherrym 	struct ucode_update_struct *uusp = (struct ucode_update_struct *)arg1;
4002449e17fSsherrym 	struct cpu_ucode_info *uinfop = CPU->cpu_m.mcpu_ucode_info;
4012449e17fSsherrym 
4022449e17fSsherrym 	ASSERT(uusp->ucodep);
4032449e17fSsherrym 
404882a7af5SMark Johnson #ifndef	__xpv
4052449e17fSsherrym 	/*
4062449e17fSsherrym 	 * Check one more time to see if it is really necessary to update
4072449e17fSsherrym 	 * microcode just in case this is a hyperthreaded processor where
4082449e17fSsherrym 	 * the threads share the same microcode.
4092449e17fSsherrym 	 */
4102449e17fSsherrym 	if (!ucode_force_update) {
4112449e17fSsherrym 		ucode_read_rev(uinfop);
4122449e17fSsherrym 		uusp->new_rev = uinfop->cui_rev;
4132449e17fSsherrym 		if (uinfop->cui_rev >= uusp->expected_rev)
4142449e17fSsherrym 			return (0);
4152449e17fSsherrym 	}
4162449e17fSsherrym 
4172449e17fSsherrym 	wrmsr(MSR_INTC_UCODE_WRITE,
4182449e17fSsherrym 	    (uint64_t)(intptr_t)(uusp->ucodep));
419882a7af5SMark Johnson #endif
4202449e17fSsherrym 	ucode_read_rev(uinfop);
4212449e17fSsherrym 	uusp->new_rev = uinfop->cui_rev;
4222449e17fSsherrym 
4232449e17fSsherrym 	return (0);
4242449e17fSsherrym }
4252449e17fSsherrym 
4262449e17fSsherrym 
4272449e17fSsherrym static void
4282449e17fSsherrym ucode_update_intel(uint8_t *ucode_body, struct cpu_ucode_info *uinfop)
4292449e17fSsherrym {
4302449e17fSsherrym 	kpreempt_disable();
4312449e17fSsherrym 	wrmsr(MSR_INTC_UCODE_WRITE, (uint64_t)(uintptr_t)ucode_body);
4322449e17fSsherrym 	ucode_read_rev(uinfop);
4332449e17fSsherrym 	kpreempt_enable();
4342449e17fSsherrym }
4352449e17fSsherrym 
436882a7af5SMark Johnson 
437882a7af5SMark Johnson #ifdef	__xpv
438882a7af5SMark Johnson static void
439882a7af5SMark Johnson ucode_update_xpv(struct ucode_update_struct *uusp, uint8_t *ucode,
440882a7af5SMark Johnson     uint32_t size)
441882a7af5SMark Johnson {
442882a7af5SMark Johnson 	struct cpu_ucode_info *uinfop;
443882a7af5SMark Johnson 	xen_platform_op_t op;
444882a7af5SMark Johnson 	int e;
445882a7af5SMark Johnson 
446882a7af5SMark Johnson 	ASSERT(DOMAIN_IS_INITDOMAIN(xen_info));
447882a7af5SMark Johnson 
448882a7af5SMark Johnson 	kpreempt_disable();
449882a7af5SMark Johnson 	uinfop = CPU->cpu_m.mcpu_ucode_info;
450882a7af5SMark Johnson 	op.cmd = XENPF_microcode_update;
451882a7af5SMark Johnson 	op.interface_version = XENPF_INTERFACE_VERSION;
452882a7af5SMark Johnson 	/*LINTED: constant in conditional context*/
453882a7af5SMark Johnson 	set_xen_guest_handle(op.u.microcode.data, ucode);
454882a7af5SMark Johnson 	op.u.microcode.length = size;
455882a7af5SMark Johnson 	e = HYPERVISOR_platform_op(&op);
456882a7af5SMark Johnson 	if (e != 0) {
457882a7af5SMark Johnson 		cmn_err(CE_WARN, "hypervisor failed to accept uCode update");
458882a7af5SMark Johnson 	}
459882a7af5SMark Johnson 	ucode_read_rev(uinfop);
460882a7af5SMark Johnson 	if (uusp != NULL) {
461882a7af5SMark Johnson 		uusp->new_rev = uinfop->cui_rev;
462882a7af5SMark Johnson 	}
463882a7af5SMark Johnson 	kpreempt_enable();
464882a7af5SMark Johnson }
465882a7af5SMark Johnson #endif /* __xpv */
466882a7af5SMark Johnson 
467882a7af5SMark Johnson 
4682449e17fSsherrym static void
4692449e17fSsherrym ucode_read_rev(struct cpu_ucode_info *uinfop)
4702449e17fSsherrym {
4712449e17fSsherrym 	struct cpuid_regs crs;
4722449e17fSsherrym 
4732449e17fSsherrym 	/*
4742449e17fSsherrym 	 * The Intel 64 and IA-32 Architecture Software Developer's Manual
4752449e17fSsherrym 	 * recommends that MSR_INTC_UCODE_REV be loaded with 0 first, then
4762449e17fSsherrym 	 * execute cpuid to guarantee the correct reading of this register.
4772449e17fSsherrym 	 */
4782449e17fSsherrym 	wrmsr(MSR_INTC_UCODE_REV, 0);
4792449e17fSsherrym 	(void) __cpuid_insn(&crs);
4802449e17fSsherrym 	uinfop->cui_rev = (rdmsr(MSR_INTC_UCODE_REV) >> INTC_UCODE_REV_SHIFT);
4812449e17fSsherrym }
4822449e17fSsherrym 
4832449e17fSsherrym /*
4842449e17fSsherrym  * Entry point to microcode update from the ucode_drv driver.
4852449e17fSsherrym  *
4862449e17fSsherrym  * Returns EM_OK on success, corresponding error code on failure.
4872449e17fSsherrym  */
4882449e17fSsherrym ucode_errno_t
4892449e17fSsherrym ucode_update(uint8_t *ucodep, int size)
4902449e17fSsherrym {
4912449e17fSsherrym 	uint32_t	header_size = UCODE_HEADER_SIZE;
4922449e17fSsherrym 	int		remaining;
4932449e17fSsherrym 	int		found = 0;
4942449e17fSsherrym 	processorid_t	id;
4952449e17fSsherrym 	struct ucode_update_struct cached = { 0 };
4962449e17fSsherrym 	struct ucode_update_struct *cachedp = NULL;
4972449e17fSsherrym 	ucode_errno_t	rc = EM_OK;
4982449e17fSsherrym 	ucode_errno_t	search_rc = EM_NOMATCH; /* search result */
4992449e17fSsherrym 	cpuset_t cpuset;
500882a7af5SMark Johnson #ifdef	__xpv
501882a7af5SMark Johnson 	uint8_t *ustart;
502882a7af5SMark Johnson 	uint32_t usize;
503882a7af5SMark Johnson #endif
5042449e17fSsherrym 
5052449e17fSsherrym 	ASSERT(ucodep);
5062449e17fSsherrym 	CPUSET_ZERO(cpuset);
5072449e17fSsherrym 
5082449e17fSsherrym 	if (!ucode_capable(CPU))
5092449e17fSsherrym 		return (EM_NOTSUP);
5102449e17fSsherrym 
5112449e17fSsherrym 	mutex_enter(&cpu_lock);
5122449e17fSsherrym 
5132449e17fSsherrym 	for (id = 0; id < max_ncpus; id++) {
5142449e17fSsherrym 		cpu_t *cpu;
5152449e17fSsherrym 		struct ucode_update_struct uus = { 0 };
5162449e17fSsherrym 		struct ucode_update_struct *uusp = &uus;
5172449e17fSsherrym 
5182449e17fSsherrym 		/*
5192449e17fSsherrym 		 * If there is no such CPU or it is not xcall ready, skip it.
5202449e17fSsherrym 		 */
5212449e17fSsherrym 		if ((cpu = cpu_get(id)) == NULL ||
5222449e17fSsherrym 		    !(cpu->cpu_flags & CPU_READY))
5232449e17fSsherrym 			continue;
5242449e17fSsherrym 
5252449e17fSsherrym 		uusp->sig = cpuid_getsig(cpu);
5262449e17fSsherrym 		bcopy(cpu->cpu_m.mcpu_ucode_info, &uusp->info,
5272449e17fSsherrym 		    sizeof (uusp->info));
5282449e17fSsherrym 
5292449e17fSsherrym 		/*
5302449e17fSsherrym 		 * If the current CPU has the same signature and platform
5312449e17fSsherrym 		 * id as the previous one we processed, reuse the information.
5322449e17fSsherrym 		 */
5332449e17fSsherrym 		if (cachedp && cachedp->sig == cpuid_getsig(cpu) &&
5342449e17fSsherrym 		    cachedp->info.cui_platid == uusp->info.cui_platid) {
5352449e17fSsherrym 			uusp->ucodep = cachedp->ucodep;
5362449e17fSsherrym 			uusp->expected_rev = cachedp->expected_rev;
5372449e17fSsherrym 			/*
5382449e17fSsherrym 			 * Intuitively we should check here to see whether the
5392449e17fSsherrym 			 * running microcode rev is >= the expected rev, and
5402449e17fSsherrym 			 * quit if it is.  But we choose to proceed with the
5412449e17fSsherrym 			 * xcall regardless of the running version so that
5422449e17fSsherrym 			 * the other threads in an HT processor can update
5432449e17fSsherrym 			 * the cpu_ucode_info structure in machcpu.
5442449e17fSsherrym 			 */
5452449e17fSsherrym 		} else {
5462449e17fSsherrym 			/*
5472449e17fSsherrym 			 * Go through the whole buffer in case there are
5482449e17fSsherrym 			 * multiple versions of matching microcode for this
5492449e17fSsherrym 			 * processor.
5502449e17fSsherrym 			 */
5512449e17fSsherrym 			for (remaining = size; remaining > 0; ) {
5522449e17fSsherrym 				int	total_size, body_size, ext_size;
5532449e17fSsherrym 				uint8_t	*curbuf = &ucodep[size - remaining];
5542449e17fSsherrym 				ucode_header_t	*uhp = (ucode_header_t *)curbuf;
5552449e17fSsherrym 				ucode_ext_table_t *uetp = NULL;
5562449e17fSsherrym 				ucode_errno_t tmprc;
5572449e17fSsherrym 
5582449e17fSsherrym 				total_size =
5592449e17fSsherrym 				    UCODE_TOTAL_SIZE(uhp->uh_total_size);
5602449e17fSsherrym 				body_size = UCODE_BODY_SIZE(uhp->uh_body_size);
5612449e17fSsherrym 				ext_size = total_size -
5622449e17fSsherrym 				    (header_size + body_size);
5632449e17fSsherrym 
5642449e17fSsherrym 				if (ext_size > 0)
5652449e17fSsherrym 					uetp = (ucode_ext_table_t *)
5662449e17fSsherrym 					    &curbuf[header_size + body_size];
5672449e17fSsherrym 
5682449e17fSsherrym 				tmprc = ucode_match(uusp->sig, &uusp->info,
5692449e17fSsherrym 				    uhp, uetp);
5702449e17fSsherrym 
5712449e17fSsherrym 				/*
5722449e17fSsherrym 				 * Since we are searching through a big file
5732449e17fSsherrym 				 * containing microcode for pretty much all the
5742449e17fSsherrym 				 * processors, we are bound to get EM_NOMATCH
5752449e17fSsherrym 				 * at one point.  However, if we return
5762449e17fSsherrym 				 * EM_NOMATCH to users, it will really confuse
5772449e17fSsherrym 				 * them.  Therefore, if we ever find a match of
5782449e17fSsherrym 				 * a lower rev, we will set return code to
5792449e17fSsherrym 				 * EM_HIGHERREV.
5802449e17fSsherrym 				 */
5812449e17fSsherrym 				if (tmprc == EM_HIGHERREV)
5822449e17fSsherrym 					search_rc = EM_HIGHERREV;
5832449e17fSsherrym 
5842449e17fSsherrym 				if (tmprc == EM_OK &&
5852449e17fSsherrym 				    uusp->expected_rev < uhp->uh_rev) {
5862449e17fSsherrym 					uusp->ucodep = &curbuf[header_size];
587882a7af5SMark Johnson #ifdef	__xpv
588882a7af5SMark Johnson 					ustart = (uint8_t *)curbuf;
589882a7af5SMark Johnson 					usize = UCODE_TOTAL_SIZE(
590882a7af5SMark Johnson 					    uhp->uh_total_size);
591882a7af5SMark Johnson #endif
5922449e17fSsherrym 					uusp->expected_rev = uhp->uh_rev;
5932449e17fSsherrym 					bcopy(uusp, &cached, sizeof (cached));
5942449e17fSsherrym 					cachedp = &cached;
5952449e17fSsherrym 					found = 1;
5962449e17fSsherrym 				}
5972449e17fSsherrym 
5982449e17fSsherrym 				remaining -= total_size;
5992449e17fSsherrym 			}
6002449e17fSsherrym 		}
6012449e17fSsherrym 
6022449e17fSsherrym 		/* Nothing to do */
6032449e17fSsherrym 		if (uusp->ucodep == NULL)
6042449e17fSsherrym 			continue;
6052449e17fSsherrym 
606882a7af5SMark Johnson #ifdef	__xpv
607882a7af5SMark Johnson 		/*
608882a7af5SMark Johnson 		 * for i86xpv, the hypervisor will update all the CPUs.
609882a7af5SMark Johnson 		 * the hypervisor wants the header, data, and extended
610882a7af5SMark Johnson 		 * signature tables. ucode_write will just read in the
611882a7af5SMark Johnson 		 * updated version on all the CPUs after the update has
612882a7af5SMark Johnson 		 * completed.
613882a7af5SMark Johnson 		 */
614*c9b5d7d2SMark Johnson 		if (id == 0) {
615882a7af5SMark Johnson 			ucode_update_xpv(uusp, ustart, usize);
616*c9b5d7d2SMark Johnson 		}
617882a7af5SMark Johnson #endif
618882a7af5SMark Johnson 
6192449e17fSsherrym 		CPUSET_ADD(cpuset, id);
6202449e17fSsherrym 		kpreempt_disable();
6212449e17fSsherrym 		xc_sync((xc_arg_t)uusp, 0, 0, X_CALL_HIPRI, cpuset,
6222449e17fSsherrym 		    ucode_write);
6232449e17fSsherrym 		kpreempt_enable();
6242449e17fSsherrym 		CPUSET_DEL(cpuset, id);
6252449e17fSsherrym 
6262449e17fSsherrym 		if (uusp->expected_rev == uusp->new_rev) {
6272449e17fSsherrym 			cmn_err(CE_CONT, ucode_success_fmt,
6282449e17fSsherrym 			    id, uusp->info.cui_rev, uusp->expected_rev);
6292449e17fSsherrym 		} else {
6302449e17fSsherrym 			cmn_err(CE_WARN, ucode_failure_fmt,
6312449e17fSsherrym 			    id, uusp->info.cui_rev, uusp->expected_rev);
6322449e17fSsherrym 			rc = EM_UPDATE;
6332449e17fSsherrym 		}
6342449e17fSsherrym 	}
6352449e17fSsherrym 
6362449e17fSsherrym 	mutex_exit(&cpu_lock);
6372449e17fSsherrym 
6382449e17fSsherrym 	if (!found)
6392449e17fSsherrym 		rc = search_rc;
6402449e17fSsherrym 
6412449e17fSsherrym 	return (rc);
6422449e17fSsherrym }
6432449e17fSsherrym 
6442449e17fSsherrym /*
6452449e17fSsherrym  * Initialize mcpu_ucode_info, and perform microcode update if necessary.
6462449e17fSsherrym  * This is the entry point from boot path where pointer to CPU structure
6472449e17fSsherrym  * is available.
6482449e17fSsherrym  *
6492449e17fSsherrym  * cpuid_info must be initialized before ucode_check can be called.
6502449e17fSsherrym  */
6512449e17fSsherrym void
6522449e17fSsherrym ucode_check(cpu_t *cp)
6532449e17fSsherrym {
6542449e17fSsherrym 	struct cpu_ucode_info *uinfop;
6552449e17fSsherrym 	ucode_errno_t rc = EM_OK;
656882a7af5SMark Johnson #ifdef	__xpv
657882a7af5SMark Johnson 	uint32_t ext_offset;
658882a7af5SMark Johnson 	uint32_t body_size;
659882a7af5SMark Johnson 	uint32_t ext_size;
660882a7af5SMark Johnson 	uint8_t *ustart;
661882a7af5SMark Johnson 	uint32_t usize;
662882a7af5SMark Johnson #endif
6632449e17fSsherrym 
6642449e17fSsherrym 	ASSERT(cp);
6652449e17fSsherrym 	if (cp->cpu_id == 0)
6662449e17fSsherrym 		cp->cpu_m.mcpu_ucode_info = &cpu_ucode_info0;
6672449e17fSsherrym 
6682449e17fSsherrym 	uinfop = cp->cpu_m.mcpu_ucode_info;
6692449e17fSsherrym 	ASSERT(uinfop);
6702449e17fSsherrym 
6712449e17fSsherrym 	if (!ucode_capable(cp))
6722449e17fSsherrym 		return;
6732449e17fSsherrym 
6742449e17fSsherrym 	/*
6752449e17fSsherrym 	 * The MSR_INTC_PLATFORM_ID is supported in Celeron and Xeon
6762449e17fSsherrym 	 * (Family 6, model 5 and above) and all processors after.
6772449e17fSsherrym 	 */
6782449e17fSsherrym 	if ((cpuid_getmodel(cp) >= 5) || (cpuid_getfamily(cp) > 6)) {
6792449e17fSsherrym 		uinfop->cui_platid = 1 << ((rdmsr(MSR_INTC_PLATFORM_ID) >>
6802449e17fSsherrym 		    INTC_PLATFORM_ID_SHIFT) & INTC_PLATFORM_ID_MASK);
6812449e17fSsherrym 	}
6822449e17fSsherrym 
6832449e17fSsherrym 	ucode_read_rev(uinfop);
6842449e17fSsherrym 
685*c9b5d7d2SMark Johnson #ifdef	__xpv
686*c9b5d7d2SMark Johnson 	/*
687*c9b5d7d2SMark Johnson 	 * for i86xpv, the hypervisor will update all the CPUs. We only need
688*c9b5d7d2SMark Johnson 	 * do do this on one of the CPUs (and there always is a CPU 0).
689*c9b5d7d2SMark Johnson 	 */
690*c9b5d7d2SMark Johnson 	if (cp->cpu_id != 0) {
691*c9b5d7d2SMark Johnson 		return;
692*c9b5d7d2SMark Johnson 	}
693*c9b5d7d2SMark Johnson #endif
694*c9b5d7d2SMark Johnson 
6952449e17fSsherrym 	/*
6962449e17fSsherrym 	 * Check to see if we need ucode update
6972449e17fSsherrym 	 */
6982449e17fSsherrym 	if ((rc = ucode_locate(cp, uinfop, &ucodefile)) == EM_OK) {
699882a7af5SMark Johnson #ifndef	__xpv
7002449e17fSsherrym 		ucode_update_intel(ucodefile.uf_body, uinfop);
701882a7af5SMark Johnson #else
702882a7af5SMark Johnson 		/*
703882a7af5SMark Johnson 		 * the hypervisor wants the header, data, and extended
704882a7af5SMark Johnson 		 * signature tables. We can only get here from the boot
705882a7af5SMark Johnson 		 * CPU (cpu #0), so use BOP_ALLOC. Since we're using BOP_ALLOC,
706882a7af5SMark Johnson 		 * We don't need to free.
707882a7af5SMark Johnson 		 */
708882a7af5SMark Johnson 		usize = UCODE_TOTAL_SIZE(ucodefile.uf_header.uh_total_size);
709882a7af5SMark Johnson 		ustart = (uint8_t *)BOP_ALLOC(bootops, NULL, usize,
710882a7af5SMark Johnson 		    MMU_PAGESIZE);
711882a7af5SMark Johnson 
712882a7af5SMark Johnson 		body_size = UCODE_BODY_SIZE(ucodefile.uf_header.uh_body_size);
713882a7af5SMark Johnson 		ext_offset = body_size + UCODE_HEADER_SIZE;
714882a7af5SMark Johnson 		ext_size = usize - ext_offset;
715882a7af5SMark Johnson 		ASSERT(ext_size >= 0);
716882a7af5SMark Johnson 
717882a7af5SMark Johnson 		(void) memcpy(ustart, &ucodefile.uf_header, UCODE_HEADER_SIZE);
718882a7af5SMark Johnson 		(void) memcpy(&ustart[UCODE_HEADER_SIZE], ucodefile.uf_body,
719882a7af5SMark Johnson 		    body_size);
720882a7af5SMark Johnson 		if (ext_size > 0) {
721882a7af5SMark Johnson 			(void) memcpy(&ustart[ext_offset],
722882a7af5SMark Johnson 			    ucodefile.uf_ext_table, ext_size);
723882a7af5SMark Johnson 		}
724882a7af5SMark Johnson 		ucode_update_xpv(NULL, ustart, usize);
725882a7af5SMark Johnson #endif
7262449e17fSsherrym 
7272449e17fSsherrym 		if (uinfop->cui_rev != ucodefile.uf_header.uh_rev)
7282449e17fSsherrym 			cmn_err(CE_WARN, ucode_failure_fmt, cp->cpu_id,
7292449e17fSsherrym 			    uinfop->cui_rev, ucodefile.uf_header.uh_rev);
7302449e17fSsherrym 	}
7312449e17fSsherrym 
7322449e17fSsherrym 	/*
7332449e17fSsherrym 	 * If we fail to find a match for any reason, free the file structure
7342449e17fSsherrym 	 * just in case we have read in a partial file.
7352449e17fSsherrym 	 *
7362449e17fSsherrym 	 * Since the scratch memory for holding the microcode for the boot CPU
7372449e17fSsherrym 	 * came from BOP_ALLOC, we will reset the data structure as if we
7382449e17fSsherrym 	 * never did the allocation so we don't have to keep track of this
7392449e17fSsherrym 	 * special chunk of memory.  We free the memory used for the rest
7402449e17fSsherrym 	 * of the CPUs in start_other_cpus().
7412449e17fSsherrym 	 */
7422449e17fSsherrym 	if (rc != EM_OK || cp->cpu_id == 0)
7432449e17fSsherrym 		ucode_file_reset(&ucodefile, cp->cpu_id);
7442449e17fSsherrym }
7452449e17fSsherrym 
7462449e17fSsherrym /*
7472449e17fSsherrym  * Returns microcode revision from the machcpu structure.
7482449e17fSsherrym  */
7492449e17fSsherrym ucode_errno_t
7502449e17fSsherrym ucode_get_rev(uint32_t *revp)
7512449e17fSsherrym {
7522449e17fSsherrym 	int i;
7532449e17fSsherrym 
7542449e17fSsherrym 	ASSERT(revp);
7552449e17fSsherrym 
7562449e17fSsherrym 	if (!ucode_capable(CPU))
7572449e17fSsherrym 		return (EM_NOTSUP);
7582449e17fSsherrym 
7592449e17fSsherrym 	mutex_enter(&cpu_lock);
7602449e17fSsherrym 	for (i = 0; i < max_ncpus; i++) {
7612449e17fSsherrym 		cpu_t *cpu;
7622449e17fSsherrym 
7632449e17fSsherrym 		if ((cpu = cpu_get(i)) == NULL)
7642449e17fSsherrym 			continue;
7652449e17fSsherrym 
7662449e17fSsherrym 		revp[i] = cpu->cpu_m.mcpu_ucode_info->cui_rev;
7672449e17fSsherrym 	}
7682449e17fSsherrym 	mutex_exit(&cpu_lock);
7692449e17fSsherrym 
7702449e17fSsherrym 	return (EM_OK);
7712449e17fSsherrym }
772