xref: /onnv-gate/usr/src/uts/i86pc/os/microcode.c (revision 7347:e6998aa65d2d)
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 /*
236519Ssherrym  * Copyright 2008 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 /*
514581Ssherrym  * Microcode specific information per core
524581Ssherrym  */
534581Ssherrym struct cpu_ucode_info {
544581Ssherrym 	uint32_t	cui_platid;	/* platform id */
554581Ssherrym 	uint32_t	cui_rev;	/* microcode revision */
564581Ssherrym };
574581Ssherrym 
584581Ssherrym /*
594581Ssherrym  * Data structure used for xcall
604581Ssherrym  */
614581Ssherrym struct ucode_update_struct {
624581Ssherrym 	uint32_t		sig;	/* signature */
634581Ssherrym 	struct cpu_ucode_info	info;	/* ucode info */
644581Ssherrym 	uint32_t		expected_rev;
654581Ssherrym 	uint32_t		new_rev;
664581Ssherrym 	uint8_t			*ucodep; /* pointer to ucode body */
674581Ssherrym };
684581Ssherrym 
694581Ssherrym /*
704581Ssherrym  * mcpu_ucode_info for the boot CPU.  Statically allocated.
714581Ssherrym  */
724581Ssherrym static struct cpu_ucode_info cpu_ucode_info0;
734581Ssherrym 
744581Ssherrym static ucode_file_t ucodefile = { 0 };
754581Ssherrym 
764581Ssherrym static int ucode_capable(cpu_t *);
774581Ssherrym static void ucode_file_reset(ucode_file_t *, processorid_t);
784581Ssherrym static ucode_errno_t ucode_match(int, struct cpu_ucode_info *,
794581Ssherrym     ucode_header_t *, ucode_ext_table_t *);
804581Ssherrym static ucode_errno_t ucode_locate(cpu_t *, struct cpu_ucode_info *,
814581Ssherrym     ucode_file_t *);
824581Ssherrym static void ucode_update_intel(uint8_t *, struct cpu_ucode_info *);
834581Ssherrym static void ucode_read_rev(struct cpu_ucode_info *);
84*7347SMark.Johnson@Sun.COM #ifdef	__xpv
85*7347SMark.Johnson@Sun.COM static void ucode_update_xpv(struct ucode_update_struct *, uint8_t *, uint32_t);
86*7347SMark.Johnson@Sun.COM #endif
874581Ssherrym 
884581Ssherrym static const char ucode_failure_fmt[] =
896519Ssherrym 	"cpu%d: failed to update microcode from version 0x%x to 0x%x\n";
904581Ssherrym static const char ucode_success_fmt[] =
916519Ssherrym 	"?cpu%d: microcode has been updated from version 0x%x to 0x%x\n";
924581Ssherrym 
934581Ssherrym /*
944581Ssherrym  * Force flag.  If set, the first microcode binary that matches
954581Ssherrym  * signature and platform id will be used for microcode update,
964581Ssherrym  * regardless of version.  Should only be used for debugging.
974581Ssherrym  */
984581Ssherrym int ucode_force_update = 0;
994581Ssherrym 
1004581Ssherrym /*
1014581Ssherrym  * Allocate space for mcpu_ucode_info in the machcpu structure
1024581Ssherrym  * for all non-boot CPUs.
1034581Ssherrym  */
1044581Ssherrym void
1054581Ssherrym ucode_alloc_space(cpu_t *cp)
1064581Ssherrym {
1074581Ssherrym 	ASSERT(cp->cpu_id != 0);
1084581Ssherrym 	cp->cpu_m.mcpu_ucode_info =
1094581Ssherrym 	    kmem_zalloc(sizeof (*cp->cpu_m.mcpu_ucode_info), KM_SLEEP);
1104581Ssherrym }
1114581Ssherrym 
1124581Ssherrym void
1134581Ssherrym ucode_free_space(cpu_t *cp)
1144581Ssherrym {
1154581Ssherrym 	ASSERT(cp->cpu_id != 0);
1164581Ssherrym 	kmem_free(cp->cpu_m.mcpu_ucode_info,
1174581Ssherrym 	    sizeof (*cp->cpu_m.mcpu_ucode_info));
1184581Ssherrym }
1194581Ssherrym 
1204581Ssherrym /*
1214581Ssherrym  * Called when we are done with microcode update on all processors to free up
1224581Ssherrym  * space allocated for the microcode file.
1234581Ssherrym  */
1244581Ssherrym void
1254581Ssherrym ucode_free()
1264581Ssherrym {
1274581Ssherrym 	ucode_file_reset(&ucodefile, -1);
1284581Ssherrym }
1294581Ssherrym 
1304581Ssherrym /*
1314581Ssherrym  * Check whether or not a processor is capable of microcode operations
1324581Ssherrym  * Returns 1 if it is capable, 0 if not.
1334581Ssherrym  */
1344581Ssherrym static int
1354581Ssherrym ucode_capable(cpu_t *cp)
1364581Ssherrym {
1375084Sjohnlev 	/* i86xpv guest domain can't update microcode */
138*7347SMark.Johnson@Sun.COM #ifndef	__xpv
139*7347SMark.Johnson@Sun.COM 	extern int xpv_is_hvm;
140*7347SMark.Johnson@Sun.COM 	if (xpv_is_hvm) {
141*7347SMark.Johnson@Sun.COM 		return (0);
142*7347SMark.Johnson@Sun.COM 	}
143*7347SMark.Johnson@Sun.COM #else
1445084Sjohnlev 	if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
1455084Sjohnlev 		return (0);
1465084Sjohnlev 	}
1475084Sjohnlev #endif
1485084Sjohnlev 
1494581Ssherrym 	/*
1504581Ssherrym 	 * At this point we only support microcode update for Intel
1514581Ssherrym 	 * processors family 6 and above.
1524581Ssherrym 	 *
1534581Ssherrym 	 * We also assume that we don't support a mix of Intel and
1544581Ssherrym 	 * AMD processors in the same box.
1554581Ssherrym 	 */
1564581Ssherrym 	if (cpuid_getvendor(cp) != X86_VENDOR_Intel ||
1574581Ssherrym 	    cpuid_getfamily(cp) < 6)
1584581Ssherrym 		return (0);
1594581Ssherrym 	else
1604581Ssherrym 		return (1);
1614581Ssherrym }
1624581Ssherrym 
1634581Ssherrym /*
1644581Ssherrym  * Called when it is no longer necessary to keep the microcode around,
1654581Ssherrym  * or when the cached microcode doesn't match the CPU being processed.
1664581Ssherrym  */
1674581Ssherrym static void
1684581Ssherrym ucode_file_reset(ucode_file_t *ucodefp, processorid_t id)
1694581Ssherrym {
1704581Ssherrym 	int total_size, body_size;
1714581Ssherrym 
1724581Ssherrym 	if (ucodefp == NULL)
1734581Ssherrym 		return;
1744581Ssherrym 
1754581Ssherrym 	total_size = UCODE_TOTAL_SIZE(ucodefp->uf_header.uh_total_size);
1764581Ssherrym 	body_size = UCODE_BODY_SIZE(ucodefp->uf_header.uh_body_size);
1774581Ssherrym 	if (ucodefp->uf_body) {
1784581Ssherrym 		/*
1794581Ssherrym 		 * Space for the boot CPU is allocated with BOP_ALLOC()
1804581Ssherrym 		 * and does not require a free.
1814581Ssherrym 		 */
1824581Ssherrym 		if (id != 0)
1834581Ssherrym 			kmem_free(ucodefp->uf_body, body_size);
1844581Ssherrym 		ucodefp->uf_body = NULL;
1854581Ssherrym 	}
1864581Ssherrym 
1874581Ssherrym 	if (ucodefp->uf_ext_table) {
1884581Ssherrym 		int size = total_size - body_size - UCODE_HEADER_SIZE;
1894581Ssherrym 		/*
1904581Ssherrym 		 * Space for the boot CPU is allocated with BOP_ALLOC()
1914581Ssherrym 		 * and does not require a free.
1924581Ssherrym 		 */
1934581Ssherrym 		if (id != 0)
1944581Ssherrym 			kmem_free(ucodefp->uf_ext_table, size);
1954581Ssherrym 		ucodefp->uf_ext_table = NULL;
1964581Ssherrym 	}
1974581Ssherrym 
1984581Ssherrym 	bzero(&ucodefp->uf_header, UCODE_HEADER_SIZE);
1994581Ssherrym }
2004581Ssherrym 
2014581Ssherrym /*
2024581Ssherrym  * Populate the ucode file structure from microcode file corresponding to
2034581Ssherrym  * this CPU, if exists.
2044581Ssherrym  *
2054581Ssherrym  * Return EM_OK on success, corresponding error code on failure.
2064581Ssherrym  */
2074581Ssherrym static ucode_errno_t
2084581Ssherrym ucode_locate(cpu_t *cp, struct cpu_ucode_info *uinfop, ucode_file_t *ucodefp)
2094581Ssherrym {
2104581Ssherrym 	char		name[MAXPATHLEN];
2114581Ssherrym 	intptr_t	fd;
2124581Ssherrym 	int		count;
2134581Ssherrym 	int		header_size = UCODE_HEADER_SIZE;
2144581Ssherrym 	int		cpi_sig = cpuid_getsig(cp);
2154581Ssherrym 	ucode_errno_t	rc = EM_OK;
2164581Ssherrym 
2174581Ssherrym 	/*
2184581Ssherrym 	 * If the microcode matches the CPU we are processing, use it.
2194581Ssherrym 	 */
2204581Ssherrym 	if (ucode_match(cpi_sig, uinfop, &ucodefp->uf_header,
2214581Ssherrym 	    ucodefp->uf_ext_table) == EM_OK && ucodefp->uf_body != NULL) {
2224581Ssherrym 		return (EM_OK);
2234581Ssherrym 	}
2244581Ssherrym 
2254581Ssherrym 	/*
2264581Ssherrym 	 * Look for microcode file with the right name.
2274581Ssherrym 	 */
2284581Ssherrym 	(void) snprintf(name, MAXPATHLEN, "/%s/%s/%08X-%02X",
2294581Ssherrym 	    UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), cpi_sig,
2304581Ssherrym 	    uinfop->cui_platid);
2314581Ssherrym 	if ((fd = kobj_open(name)) == -1) {
2324581Ssherrym 		return (EM_OPENFILE);
2334581Ssherrym 	}
2344581Ssherrym 
2354581Ssherrym 	/*
2364581Ssherrym 	 * We found a microcode file for the CPU we are processing,
2374581Ssherrym 	 * reset the microcode data structure and read in the new
2384581Ssherrym 	 * file.
2394581Ssherrym 	 */
2404581Ssherrym 	ucode_file_reset(ucodefp, cp->cpu_id);
2414581Ssherrym 
2424581Ssherrym 	count = kobj_read(fd, (char *)&ucodefp->uf_header, header_size, 0);
2434581Ssherrym 
2444581Ssherrym 	switch (count) {
2454581Ssherrym 	case UCODE_HEADER_SIZE: {
2464581Ssherrym 
2474581Ssherrym 		ucode_header_t	*uhp = &ucodefp->uf_header;
2484581Ssherrym 		uint32_t	offset = header_size;
2494581Ssherrym 		int		total_size, body_size, ext_size;
2504581Ssherrym 		uint32_t	sum = 0;
2514581Ssherrym 
2524581Ssherrym 		/*
2534581Ssherrym 		 * Make sure that the header contains valid fields.
2544581Ssherrym 		 */
2554581Ssherrym 		if ((rc = ucode_header_validate(uhp)) == EM_OK) {
2564581Ssherrym 			total_size = UCODE_TOTAL_SIZE(uhp->uh_total_size);
2574581Ssherrym 			body_size = UCODE_BODY_SIZE(uhp->uh_body_size);
2584581Ssherrym 			if (cp->cpu_id != 0) {
2594581Ssherrym 				if ((ucodefp->uf_body = kmem_zalloc(body_size,
2604581Ssherrym 				    KM_NOSLEEP)) == NULL) {
2614581Ssherrym 					rc = EM_NOMEM;
2624581Ssherrym 					break;
2634581Ssherrym 				}
2644581Ssherrym 			} else {
2654581Ssherrym 				/*
2664581Ssherrym 				 * BOP_ALLOC() failure results in panic so we
2674581Ssherrym 				 * don't have to check for NULL return.
2684581Ssherrym 				 */
2694581Ssherrym 				ucodefp->uf_body =
2704581Ssherrym 				    (uint8_t *)BOP_ALLOC(bootops,
2714581Ssherrym 				    NULL, body_size, MMU_PAGESIZE);
2724581Ssherrym 			}
2734581Ssherrym 
2744581Ssherrym 			if (kobj_read(fd, (char *)ucodefp->uf_body,
2754581Ssherrym 			    body_size, offset) != body_size)
2764581Ssherrym 				rc = EM_FILESIZE;
2774581Ssherrym 		}
2784581Ssherrym 
2794581Ssherrym 		if (rc)
2804581Ssherrym 			break;
2814581Ssherrym 
2824581Ssherrym 		sum = ucode_checksum(0, header_size,
2834581Ssherrym 		    (uint8_t *)&ucodefp->uf_header);
2844581Ssherrym 		if (ucode_checksum(sum, body_size, ucodefp->uf_body)) {
2854581Ssherrym 			rc = EM_CHECKSUM;
2864581Ssherrym 			break;
2874581Ssherrym 		}
2884581Ssherrym 
2894581Ssherrym 		/*
2904581Ssherrym 		 * Check to see if there is extended signature table.
2914581Ssherrym 		 */
2924581Ssherrym 		offset = body_size + header_size;
2934581Ssherrym 		ext_size = total_size - offset;
2944581Ssherrym 
2954581Ssherrym 		if (ext_size <= 0)
2964581Ssherrym 			break;
2974581Ssherrym 
2984581Ssherrym 		if (cp->cpu_id != 0) {
2994581Ssherrym 			if ((ucodefp->uf_ext_table = kmem_zalloc(ext_size,
3004581Ssherrym 			    KM_NOSLEEP)) == NULL) {
3014581Ssherrym 				rc = EM_NOMEM;
3024581Ssherrym 				break;
3034581Ssherrym 			}
3044581Ssherrym 		} else {
3054581Ssherrym 			/*
3064581Ssherrym 			 * BOP_ALLOC() failure results in panic so we
3074581Ssherrym 			 * don't have to check for NULL return.
3084581Ssherrym 			 */
3094581Ssherrym 			ucodefp->uf_ext_table =
3104581Ssherrym 			    (ucode_ext_table_t *)BOP_ALLOC(bootops, NULL,
3114581Ssherrym 			    ext_size, MMU_PAGESIZE);
3124581Ssherrym 		}
3134581Ssherrym 
3144581Ssherrym 		if (kobj_read(fd, (char *)ucodefp->uf_ext_table,
3154581Ssherrym 		    ext_size, offset) != ext_size) {
3164581Ssherrym 			rc = EM_FILESIZE;
3174581Ssherrym 		} else if (ucode_checksum(0, ext_size,
3184581Ssherrym 		    (uint8_t *)(ucodefp->uf_ext_table))) {
3194581Ssherrym 			rc = EM_CHECKSUM;
3204581Ssherrym 		} else {
3214581Ssherrym 			int i;
3224581Ssherrym 
3234581Ssherrym 			ext_size -= UCODE_EXT_TABLE_SIZE;
3244581Ssherrym 			for (i = 0; i < ucodefp->uf_ext_table->uet_count;
3254581Ssherrym 			    i++) {
3264581Ssherrym 				if (ucode_checksum(0, UCODE_EXT_SIG_SIZE,
3274581Ssherrym 				    (uint8_t *)(&(ucodefp->uf_ext_table->
3284581Ssherrym 				    uet_ext_sig[i])))) {
3294581Ssherrym 					rc = EM_CHECKSUM;
3304581Ssherrym 					break;
3314581Ssherrym 				}
3324581Ssherrym 			}
3334581Ssherrym 		}
3344581Ssherrym 		break;
3354581Ssherrym 	}
3364581Ssherrym 
3374581Ssherrym 	default:
3384581Ssherrym 		rc = EM_FILESIZE;
3394581Ssherrym 		break;
3404581Ssherrym 	}
3414581Ssherrym 
3424581Ssherrym 	kobj_close(fd);
3434581Ssherrym 
3444581Ssherrym 	if (rc != EM_OK)
3454581Ssherrym 		return (rc);
3464581Ssherrym 
3474581Ssherrym 	rc = ucode_match(cpi_sig, uinfop, &ucodefp->uf_header,
3484581Ssherrym 	    ucodefp->uf_ext_table);
3494581Ssherrym 
3504581Ssherrym 	return (rc);
3514581Ssherrym }
3524581Ssherrym 
3534581Ssherrym 
3544581Ssherrym /*
3554581Ssherrym  * Returns 1 if the microcode is for this processor; 0 otherwise.
3564581Ssherrym  */
3574581Ssherrym static ucode_errno_t
3584581Ssherrym ucode_match(int cpi_sig, struct cpu_ucode_info *uinfop,
3594581Ssherrym     ucode_header_t *uhp, ucode_ext_table_t *uetp)
3604581Ssherrym {
3614581Ssherrym 	ASSERT(uhp);
3624581Ssherrym 
3634581Ssherrym 	if (UCODE_MATCH(cpi_sig, uhp->uh_signature,
3644581Ssherrym 	    uinfop->cui_platid, uhp->uh_proc_flags)) {
3654581Ssherrym 
3664581Ssherrym 		if (uinfop->cui_rev >= uhp->uh_rev && !ucode_force_update)
3674581Ssherrym 			return (EM_HIGHERREV);
3684581Ssherrym 
3694581Ssherrym 		return (EM_OK);
3704581Ssherrym 	}
3714581Ssherrym 
3724581Ssherrym 	if (uetp != NULL) {
3734581Ssherrym 		int i;
3744581Ssherrym 
3754581Ssherrym 		for (i = 0; i < uetp->uet_count; i++) {
3764581Ssherrym 			ucode_ext_sig_t *uesp;
3774581Ssherrym 
3784581Ssherrym 			uesp = &uetp->uet_ext_sig[i];
3794581Ssherrym 
3804581Ssherrym 			if (UCODE_MATCH(cpi_sig, uesp->ues_signature,
3814581Ssherrym 			    uinfop->cui_platid, uesp->ues_proc_flags)) {
3824581Ssherrym 
3834581Ssherrym 				if (uinfop->cui_rev >= uhp->uh_rev &&
3844581Ssherrym 				    !ucode_force_update)
3854581Ssherrym 					return (EM_HIGHERREV);
3864581Ssherrym 
3874581Ssherrym 				return (EM_OK);
3884581Ssherrym 			}
3894581Ssherrym 		}
3904581Ssherrym 	}
3914581Ssherrym 
3924581Ssherrym 	return (EM_NOMATCH);
3934581Ssherrym }
3944581Ssherrym 
3954581Ssherrym /*ARGSUSED*/
3964581Ssherrym static int
3974581Ssherrym ucode_write(xc_arg_t arg1, xc_arg_t unused2, xc_arg_t unused3)
3984581Ssherrym {
3994581Ssherrym 	struct ucode_update_struct *uusp = (struct ucode_update_struct *)arg1;
4004581Ssherrym 	struct cpu_ucode_info *uinfop = CPU->cpu_m.mcpu_ucode_info;
4014581Ssherrym 
4024581Ssherrym 	ASSERT(uusp->ucodep);
4034581Ssherrym 
404*7347SMark.Johnson@Sun.COM #ifndef	__xpv
4054581Ssherrym 	/*
4064581Ssherrym 	 * Check one more time to see if it is really necessary to update
4074581Ssherrym 	 * microcode just in case this is a hyperthreaded processor where
4084581Ssherrym 	 * the threads share the same microcode.
4094581Ssherrym 	 */
4104581Ssherrym 	if (!ucode_force_update) {
4114581Ssherrym 		ucode_read_rev(uinfop);
4124581Ssherrym 		uusp->new_rev = uinfop->cui_rev;
4134581Ssherrym 		if (uinfop->cui_rev >= uusp->expected_rev)
4144581Ssherrym 			return (0);
4154581Ssherrym 	}
4164581Ssherrym 
4174581Ssherrym 	wrmsr(MSR_INTC_UCODE_WRITE,
4184581Ssherrym 	    (uint64_t)(intptr_t)(uusp->ucodep));
419*7347SMark.Johnson@Sun.COM #endif
4204581Ssherrym 	ucode_read_rev(uinfop);
4214581Ssherrym 	uusp->new_rev = uinfop->cui_rev;
4224581Ssherrym 
4234581Ssherrym 	return (0);
4244581Ssherrym }
4254581Ssherrym 
4264581Ssherrym 
4274581Ssherrym static void
4284581Ssherrym ucode_update_intel(uint8_t *ucode_body, struct cpu_ucode_info *uinfop)
4294581Ssherrym {
4304581Ssherrym 	kpreempt_disable();
4314581Ssherrym 	wrmsr(MSR_INTC_UCODE_WRITE, (uint64_t)(uintptr_t)ucode_body);
4324581Ssherrym 	ucode_read_rev(uinfop);
4334581Ssherrym 	kpreempt_enable();
4344581Ssherrym }
4354581Ssherrym 
436*7347SMark.Johnson@Sun.COM 
437*7347SMark.Johnson@Sun.COM #ifdef	__xpv
438*7347SMark.Johnson@Sun.COM static void
439*7347SMark.Johnson@Sun.COM ucode_update_xpv(struct ucode_update_struct *uusp, uint8_t *ucode,
440*7347SMark.Johnson@Sun.COM     uint32_t size)
441*7347SMark.Johnson@Sun.COM {
442*7347SMark.Johnson@Sun.COM 	struct cpu_ucode_info *uinfop;
443*7347SMark.Johnson@Sun.COM 	xen_platform_op_t op;
444*7347SMark.Johnson@Sun.COM 	int e;
445*7347SMark.Johnson@Sun.COM 
446*7347SMark.Johnson@Sun.COM 	ASSERT(DOMAIN_IS_INITDOMAIN(xen_info));
447*7347SMark.Johnson@Sun.COM 
448*7347SMark.Johnson@Sun.COM 	kpreempt_disable();
449*7347SMark.Johnson@Sun.COM 	uinfop = CPU->cpu_m.mcpu_ucode_info;
450*7347SMark.Johnson@Sun.COM 	op.cmd = XENPF_microcode_update;
451*7347SMark.Johnson@Sun.COM 	op.interface_version = XENPF_INTERFACE_VERSION;
452*7347SMark.Johnson@Sun.COM 	/*LINTED: constant in conditional context*/
453*7347SMark.Johnson@Sun.COM 	set_xen_guest_handle(op.u.microcode.data, ucode);
454*7347SMark.Johnson@Sun.COM 	op.u.microcode.length = size;
455*7347SMark.Johnson@Sun.COM 	e = HYPERVISOR_platform_op(&op);
456*7347SMark.Johnson@Sun.COM 	if (e != 0) {
457*7347SMark.Johnson@Sun.COM 		cmn_err(CE_WARN, "hypervisor failed to accept uCode update");
458*7347SMark.Johnson@Sun.COM 	}
459*7347SMark.Johnson@Sun.COM 	ucode_read_rev(uinfop);
460*7347SMark.Johnson@Sun.COM 	if (uusp != NULL) {
461*7347SMark.Johnson@Sun.COM 		uusp->new_rev = uinfop->cui_rev;
462*7347SMark.Johnson@Sun.COM 	}
463*7347SMark.Johnson@Sun.COM 	kpreempt_enable();
464*7347SMark.Johnson@Sun.COM }
465*7347SMark.Johnson@Sun.COM #endif /* __xpv */
466*7347SMark.Johnson@Sun.COM 
467*7347SMark.Johnson@Sun.COM 
4684581Ssherrym static void
4694581Ssherrym ucode_read_rev(struct cpu_ucode_info *uinfop)
4704581Ssherrym {
4714581Ssherrym 	struct cpuid_regs crs;
4724581Ssherrym 
4734581Ssherrym 	/*
4744581Ssherrym 	 * The Intel 64 and IA-32 Architecture Software Developer's Manual
4754581Ssherrym 	 * recommends that MSR_INTC_UCODE_REV be loaded with 0 first, then
4764581Ssherrym 	 * execute cpuid to guarantee the correct reading of this register.
4774581Ssherrym 	 */
4784581Ssherrym 	wrmsr(MSR_INTC_UCODE_REV, 0);
4794581Ssherrym 	(void) __cpuid_insn(&crs);
4804581Ssherrym 	uinfop->cui_rev = (rdmsr(MSR_INTC_UCODE_REV) >> INTC_UCODE_REV_SHIFT);
4814581Ssherrym }
4824581Ssherrym 
4834581Ssherrym /*
4844581Ssherrym  * Entry point to microcode update from the ucode_drv driver.
4854581Ssherrym  *
4864581Ssherrym  * Returns EM_OK on success, corresponding error code on failure.
4874581Ssherrym  */
4884581Ssherrym ucode_errno_t
4894581Ssherrym ucode_update(uint8_t *ucodep, int size)
4904581Ssherrym {
4914581Ssherrym 	uint32_t	header_size = UCODE_HEADER_SIZE;
4924581Ssherrym 	int		remaining;
4934581Ssherrym 	int		found = 0;
4944581Ssherrym 	processorid_t	id;
4954581Ssherrym 	struct ucode_update_struct cached = { 0 };
4964581Ssherrym 	struct ucode_update_struct *cachedp = NULL;
4974581Ssherrym 	ucode_errno_t	rc = EM_OK;
4984581Ssherrym 	ucode_errno_t	search_rc = EM_NOMATCH; /* search result */
4994581Ssherrym 	cpuset_t cpuset;
500*7347SMark.Johnson@Sun.COM #ifdef	__xpv
501*7347SMark.Johnson@Sun.COM 	uint8_t *ustart;
502*7347SMark.Johnson@Sun.COM 	uint32_t usize;
503*7347SMark.Johnson@Sun.COM #endif
5044581Ssherrym 
5054581Ssherrym 	ASSERT(ucodep);
5064581Ssherrym 	CPUSET_ZERO(cpuset);
5074581Ssherrym 
5084581Ssherrym 	if (!ucode_capable(CPU))
5094581Ssherrym 		return (EM_NOTSUP);
5104581Ssherrym 
5114581Ssherrym 	mutex_enter(&cpu_lock);
5124581Ssherrym 
5134581Ssherrym 	for (id = 0; id < max_ncpus; id++) {
5144581Ssherrym 		cpu_t *cpu;
5154581Ssherrym 		struct ucode_update_struct uus = { 0 };
5164581Ssherrym 		struct ucode_update_struct *uusp = &uus;
5174581Ssherrym 
5184581Ssherrym 		/*
5194581Ssherrym 		 * If there is no such CPU or it is not xcall ready, skip it.
5204581Ssherrym 		 */
5214581Ssherrym 		if ((cpu = cpu_get(id)) == NULL ||
5224581Ssherrym 		    !(cpu->cpu_flags & CPU_READY))
5234581Ssherrym 			continue;
5244581Ssherrym 
5254581Ssherrym 		uusp->sig = cpuid_getsig(cpu);
5264581Ssherrym 		bcopy(cpu->cpu_m.mcpu_ucode_info, &uusp->info,
5274581Ssherrym 		    sizeof (uusp->info));
5284581Ssherrym 
5294581Ssherrym 		/*
5304581Ssherrym 		 * If the current CPU has the same signature and platform
5314581Ssherrym 		 * id as the previous one we processed, reuse the information.
5324581Ssherrym 		 */
5334581Ssherrym 		if (cachedp && cachedp->sig == cpuid_getsig(cpu) &&
5344581Ssherrym 		    cachedp->info.cui_platid == uusp->info.cui_platid) {
5354581Ssherrym 			uusp->ucodep = cachedp->ucodep;
5364581Ssherrym 			uusp->expected_rev = cachedp->expected_rev;
5374581Ssherrym 			/*
5384581Ssherrym 			 * Intuitively we should check here to see whether the
5394581Ssherrym 			 * running microcode rev is >= the expected rev, and
5404581Ssherrym 			 * quit if it is.  But we choose to proceed with the
5414581Ssherrym 			 * xcall regardless of the running version so that
5424581Ssherrym 			 * the other threads in an HT processor can update
5434581Ssherrym 			 * the cpu_ucode_info structure in machcpu.
5444581Ssherrym 			 */
5454581Ssherrym 		} else {
5464581Ssherrym 			/*
5474581Ssherrym 			 * Go through the whole buffer in case there are
5484581Ssherrym 			 * multiple versions of matching microcode for this
5494581Ssherrym 			 * processor.
5504581Ssherrym 			 */
5514581Ssherrym 			for (remaining = size; remaining > 0; ) {
5524581Ssherrym 				int	total_size, body_size, ext_size;
5534581Ssherrym 				uint8_t	*curbuf = &ucodep[size - remaining];
5544581Ssherrym 				ucode_header_t	*uhp = (ucode_header_t *)curbuf;
5554581Ssherrym 				ucode_ext_table_t *uetp = NULL;
5564581Ssherrym 				ucode_errno_t tmprc;
5574581Ssherrym 
5584581Ssherrym 				total_size =
5594581Ssherrym 				    UCODE_TOTAL_SIZE(uhp->uh_total_size);
5604581Ssherrym 				body_size = UCODE_BODY_SIZE(uhp->uh_body_size);
5614581Ssherrym 				ext_size = total_size -
5624581Ssherrym 				    (header_size + body_size);
5634581Ssherrym 
5644581Ssherrym 				if (ext_size > 0)
5654581Ssherrym 					uetp = (ucode_ext_table_t *)
5664581Ssherrym 					    &curbuf[header_size + body_size];
5674581Ssherrym 
5684581Ssherrym 				tmprc = ucode_match(uusp->sig, &uusp->info,
5694581Ssherrym 				    uhp, uetp);
5704581Ssherrym 
5714581Ssherrym 				/*
5724581Ssherrym 				 * Since we are searching through a big file
5734581Ssherrym 				 * containing microcode for pretty much all the
5744581Ssherrym 				 * processors, we are bound to get EM_NOMATCH
5754581Ssherrym 				 * at one point.  However, if we return
5764581Ssherrym 				 * EM_NOMATCH to users, it will really confuse
5774581Ssherrym 				 * them.  Therefore, if we ever find a match of
5784581Ssherrym 				 * a lower rev, we will set return code to
5794581Ssherrym 				 * EM_HIGHERREV.
5804581Ssherrym 				 */
5814581Ssherrym 				if (tmprc == EM_HIGHERREV)
5824581Ssherrym 					search_rc = EM_HIGHERREV;
5834581Ssherrym 
5844581Ssherrym 				if (tmprc == EM_OK &&
5854581Ssherrym 				    uusp->expected_rev < uhp->uh_rev) {
5864581Ssherrym 					uusp->ucodep = &curbuf[header_size];
587*7347SMark.Johnson@Sun.COM #ifdef	__xpv
588*7347SMark.Johnson@Sun.COM 					ustart = (uint8_t *)curbuf;
589*7347SMark.Johnson@Sun.COM 					usize = UCODE_TOTAL_SIZE(
590*7347SMark.Johnson@Sun.COM 					    uhp->uh_total_size);
591*7347SMark.Johnson@Sun.COM #endif
5924581Ssherrym 					uusp->expected_rev = uhp->uh_rev;
5934581Ssherrym 					bcopy(uusp, &cached, sizeof (cached));
5944581Ssherrym 					cachedp = &cached;
5954581Ssherrym 					found = 1;
5964581Ssherrym 				}
5974581Ssherrym 
5984581Ssherrym 				remaining -= total_size;
5994581Ssherrym 			}
6004581Ssherrym 		}
6014581Ssherrym 
6024581Ssherrym 		/* Nothing to do */
6034581Ssherrym 		if (uusp->ucodep == NULL)
6044581Ssherrym 			continue;
6054581Ssherrym 
606*7347SMark.Johnson@Sun.COM #ifdef	__xpv
607*7347SMark.Johnson@Sun.COM 		/*
608*7347SMark.Johnson@Sun.COM 		 * for i86xpv, the hypervisor will update all the CPUs.
609*7347SMark.Johnson@Sun.COM 		 * the hypervisor wants the header, data, and extended
610*7347SMark.Johnson@Sun.COM 		 * signature tables. ucode_write will just read in the
611*7347SMark.Johnson@Sun.COM 		 * updated version on all the CPUs after the update has
612*7347SMark.Johnson@Sun.COM 		 * completed.
613*7347SMark.Johnson@Sun.COM 		 */
614*7347SMark.Johnson@Sun.COM 		ucode_update_xpv(uusp, ustart, usize);
615*7347SMark.Johnson@Sun.COM #endif
616*7347SMark.Johnson@Sun.COM 
6174581Ssherrym 		CPUSET_ADD(cpuset, id);
6184581Ssherrym 		kpreempt_disable();
6194581Ssherrym 		xc_sync((xc_arg_t)uusp, 0, 0, X_CALL_HIPRI, cpuset,
6204581Ssherrym 		    ucode_write);
6214581Ssherrym 		kpreempt_enable();
6224581Ssherrym 		CPUSET_DEL(cpuset, id);
6234581Ssherrym 
6244581Ssherrym 		if (uusp->expected_rev == uusp->new_rev) {
6254581Ssherrym 			cmn_err(CE_CONT, ucode_success_fmt,
6264581Ssherrym 			    id, uusp->info.cui_rev, uusp->expected_rev);
6274581Ssherrym 		} else {
6284581Ssherrym 			cmn_err(CE_WARN, ucode_failure_fmt,
6294581Ssherrym 			    id, uusp->info.cui_rev, uusp->expected_rev);
6304581Ssherrym 			rc = EM_UPDATE;
6314581Ssherrym 		}
6324581Ssherrym 	}
6334581Ssherrym 
6344581Ssherrym 	mutex_exit(&cpu_lock);
6354581Ssherrym 
6364581Ssherrym 	if (!found)
6374581Ssherrym 		rc = search_rc;
6384581Ssherrym 
6394581Ssherrym 	return (rc);
6404581Ssherrym }
6414581Ssherrym 
6424581Ssherrym /*
6434581Ssherrym  * Initialize mcpu_ucode_info, and perform microcode update if necessary.
6444581Ssherrym  * This is the entry point from boot path where pointer to CPU structure
6454581Ssherrym  * is available.
6464581Ssherrym  *
6474581Ssherrym  * cpuid_info must be initialized before ucode_check can be called.
6484581Ssherrym  */
6494581Ssherrym void
6504581Ssherrym ucode_check(cpu_t *cp)
6514581Ssherrym {
6524581Ssherrym 	struct cpu_ucode_info *uinfop;
6534581Ssherrym 	ucode_errno_t rc = EM_OK;
654*7347SMark.Johnson@Sun.COM #ifdef	__xpv
655*7347SMark.Johnson@Sun.COM 	uint32_t ext_offset;
656*7347SMark.Johnson@Sun.COM 	uint32_t body_size;
657*7347SMark.Johnson@Sun.COM 	uint32_t ext_size;
658*7347SMark.Johnson@Sun.COM 	uint8_t *ustart;
659*7347SMark.Johnson@Sun.COM 	uint32_t usize;
660*7347SMark.Johnson@Sun.COM #endif
6614581Ssherrym 
6624581Ssherrym 	ASSERT(cp);
6634581Ssherrym 	if (cp->cpu_id == 0)
6644581Ssherrym 		cp->cpu_m.mcpu_ucode_info = &cpu_ucode_info0;
6654581Ssherrym 
6664581Ssherrym 	uinfop = cp->cpu_m.mcpu_ucode_info;
6674581Ssherrym 	ASSERT(uinfop);
6684581Ssherrym 
6694581Ssherrym 	if (!ucode_capable(cp))
6704581Ssherrym 		return;
6714581Ssherrym 
672*7347SMark.Johnson@Sun.COM #ifdef	__xpv
673*7347SMark.Johnson@Sun.COM 	/*
674*7347SMark.Johnson@Sun.COM 	 * for i86xpv, the hypervisor will update all the CPUs. We only need
675*7347SMark.Johnson@Sun.COM 	 * do do this on one of the CPUs (and there always is a CPU 0). We do
676*7347SMark.Johnson@Sun.COM 	 * need to update the CPU version though. Do that before returning.
677*7347SMark.Johnson@Sun.COM 	 */
678*7347SMark.Johnson@Sun.COM 	if (cp->cpu_id != 0) {
679*7347SMark.Johnson@Sun.COM 		ucode_read_rev(uinfop);
680*7347SMark.Johnson@Sun.COM 		return;
681*7347SMark.Johnson@Sun.COM 	}
682*7347SMark.Johnson@Sun.COM #endif
683*7347SMark.Johnson@Sun.COM 
6844581Ssherrym 	/*
6854581Ssherrym 	 * The MSR_INTC_PLATFORM_ID is supported in Celeron and Xeon
6864581Ssherrym 	 * (Family 6, model 5 and above) and all processors after.
6874581Ssherrym 	 */
6884581Ssherrym 	if ((cpuid_getmodel(cp) >= 5) || (cpuid_getfamily(cp) > 6)) {
6894581Ssherrym 		uinfop->cui_platid = 1 << ((rdmsr(MSR_INTC_PLATFORM_ID) >>
6904581Ssherrym 		    INTC_PLATFORM_ID_SHIFT) & INTC_PLATFORM_ID_MASK);
6914581Ssherrym 	}
6924581Ssherrym 
6934581Ssherrym 	ucode_read_rev(uinfop);
6944581Ssherrym 
6954581Ssherrym 	/*
6964581Ssherrym 	 * Check to see if we need ucode update
6974581Ssherrym 	 */
6984581Ssherrym 	if ((rc = ucode_locate(cp, uinfop, &ucodefile)) == EM_OK) {
699*7347SMark.Johnson@Sun.COM #ifndef	__xpv
7004581Ssherrym 		ucode_update_intel(ucodefile.uf_body, uinfop);
701*7347SMark.Johnson@Sun.COM #else
702*7347SMark.Johnson@Sun.COM 		/*
703*7347SMark.Johnson@Sun.COM 		 * the hypervisor wants the header, data, and extended
704*7347SMark.Johnson@Sun.COM 		 * signature tables. We can only get here from the boot
705*7347SMark.Johnson@Sun.COM 		 * CPU (cpu #0), so use BOP_ALLOC. Since we're using BOP_ALLOC,
706*7347SMark.Johnson@Sun.COM 		 * We don't need to free.
707*7347SMark.Johnson@Sun.COM 		 */
708*7347SMark.Johnson@Sun.COM 		usize = UCODE_TOTAL_SIZE(ucodefile.uf_header.uh_total_size);
709*7347SMark.Johnson@Sun.COM 		ustart = (uint8_t *)BOP_ALLOC(bootops, NULL, usize,
710*7347SMark.Johnson@Sun.COM 		    MMU_PAGESIZE);
711*7347SMark.Johnson@Sun.COM 
712*7347SMark.Johnson@Sun.COM 		body_size = UCODE_BODY_SIZE(ucodefile.uf_header.uh_body_size);
713*7347SMark.Johnson@Sun.COM 		ext_offset = body_size + UCODE_HEADER_SIZE;
714*7347SMark.Johnson@Sun.COM 		ext_size = usize - ext_offset;
715*7347SMark.Johnson@Sun.COM 		ASSERT(ext_size >= 0);
716*7347SMark.Johnson@Sun.COM 
717*7347SMark.Johnson@Sun.COM 		(void) memcpy(ustart, &ucodefile.uf_header, UCODE_HEADER_SIZE);
718*7347SMark.Johnson@Sun.COM 		(void) memcpy(&ustart[UCODE_HEADER_SIZE], ucodefile.uf_body,
719*7347SMark.Johnson@Sun.COM 		    body_size);
720*7347SMark.Johnson@Sun.COM 		if (ext_size > 0) {
721*7347SMark.Johnson@Sun.COM 			(void) memcpy(&ustart[ext_offset],
722*7347SMark.Johnson@Sun.COM 			    ucodefile.uf_ext_table, ext_size);
723*7347SMark.Johnson@Sun.COM 		}
724*7347SMark.Johnson@Sun.COM 		ucode_update_xpv(NULL, ustart, usize);
725*7347SMark.Johnson@Sun.COM #endif
7264581Ssherrym 
7274581Ssherrym 		if (uinfop->cui_rev != ucodefile.uf_header.uh_rev)
7284581Ssherrym 			cmn_err(CE_WARN, ucode_failure_fmt, cp->cpu_id,
7294581Ssherrym 			    uinfop->cui_rev, ucodefile.uf_header.uh_rev);
7304581Ssherrym 	}
7314581Ssherrym 
7324581Ssherrym 	/*
7334581Ssherrym 	 * If we fail to find a match for any reason, free the file structure
7344581Ssherrym 	 * just in case we have read in a partial file.
7354581Ssherrym 	 *
7364581Ssherrym 	 * Since the scratch memory for holding the microcode for the boot CPU
7374581Ssherrym 	 * came from BOP_ALLOC, we will reset the data structure as if we
7384581Ssherrym 	 * never did the allocation so we don't have to keep track of this
7394581Ssherrym 	 * special chunk of memory.  We free the memory used for the rest
7404581Ssherrym 	 * of the CPUs in start_other_cpus().
7414581Ssherrym 	 */
7424581Ssherrym 	if (rc != EM_OK || cp->cpu_id == 0)
7434581Ssherrym 		ucode_file_reset(&ucodefile, cp->cpu_id);
7444581Ssherrym }
7454581Ssherrym 
7464581Ssherrym /*
7474581Ssherrym  * Returns microcode revision from the machcpu structure.
7484581Ssherrym  */
7494581Ssherrym ucode_errno_t
7504581Ssherrym ucode_get_rev(uint32_t *revp)
7514581Ssherrym {
7524581Ssherrym 	int i;
7534581Ssherrym 
7544581Ssherrym 	ASSERT(revp);
7554581Ssherrym 
7564581Ssherrym 	if (!ucode_capable(CPU))
7574581Ssherrym 		return (EM_NOTSUP);
7584581Ssherrym 
7594581Ssherrym 	mutex_enter(&cpu_lock);
7604581Ssherrym 	for (i = 0; i < max_ncpus; i++) {
7614581Ssherrym 		cpu_t *cpu;
7624581Ssherrym 
7634581Ssherrym 		if ((cpu = cpu_get(i)) == NULL)
7644581Ssherrym 			continue;
7654581Ssherrym 
7664581Ssherrym 		revp[i] = cpu->cpu_m.mcpu_ucode_info->cui_rev;
7674581Ssherrym 	}
7684581Ssherrym 	mutex_exit(&cpu_lock);
7694581Ssherrym 
7704581Ssherrym 	return (EM_OK);
7714581Ssherrym }
772