xref: /onnv-gate/usr/src/uts/i86pc/os/microcode.c (revision 6519:efbb6fbdbae0)
14581Ssherrym /*
24581Ssherrym  * CDDL HEADER START
34581Ssherrym  *
44581Ssherrym  * The contents of this file are subject to the terms of the
54581Ssherrym  * Common Development and Distribution License (the "License").
64581Ssherrym  * You may not use this file except in compliance with the License.
74581Ssherrym  *
84581Ssherrym  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94581Ssherrym  * or http://www.opensolaris.org/os/licensing.
104581Ssherrym  * See the License for the specific language governing permissions
114581Ssherrym  * and limitations under the License.
124581Ssherrym  *
134581Ssherrym  * When distributing Covered Code, include this CDDL HEADER in each
144581Ssherrym  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154581Ssherrym  * If applicable, add the following below this CDDL HEADER, with the
164581Ssherrym  * fields enclosed by brackets "[]" replaced with your own identifying
174581Ssherrym  * information: Portions Copyright [yyyy] [name of copyright owner]
184581Ssherrym  *
194581Ssherrym  * CDDL HEADER END
204581Ssherrym  */
214581Ssherrym 
224581Ssherrym /*
23*6519Ssherrym  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
244581Ssherrym  * Use is subject to license terms.
254581Ssherrym  */
264581Ssherrym 
274581Ssherrym #pragma ident	"%Z%%M%	%I%	%E% SMI"
284581Ssherrym 
294581Ssherrym #include <sys/asm_linkage.h>
304581Ssherrym #include <sys/bootconf.h>
314581Ssherrym #include <sys/cpuvar.h>
324581Ssherrym #include <sys/cmn_err.h>
334581Ssherrym #include <sys/controlregs.h>
344581Ssherrym #include <sys/debug.h>
354581Ssherrym #include <sys/kobj.h>
364581Ssherrym #include <sys/kobj_impl.h>
374581Ssherrym #include <sys/machsystm.h>
384581Ssherrym #include <sys/param.h>
394581Ssherrym #include <sys/machparam.h>
404581Ssherrym #include <sys/promif.h>
414581Ssherrym #include <sys/sysmacros.h>
424581Ssherrym #include <sys/systm.h>
434581Ssherrym #include <sys/types.h>
444581Ssherrym #include <sys/thread.h>
454581Ssherrym #include <sys/ucode.h>
464581Ssherrym #include <sys/x86_archext.h>
474581Ssherrym #include <sys/x_call.h>
485084Sjohnlev #ifdef	__xpv
495084Sjohnlev #include <sys/hypervisor.h>
505084Sjohnlev #endif
514581Ssherrym 
524581Ssherrym /*
534581Ssherrym  * Microcode specific information per core
544581Ssherrym  */
554581Ssherrym struct cpu_ucode_info {
564581Ssherrym 	uint32_t	cui_platid;	/* platform id */
574581Ssherrym 	uint32_t	cui_rev;	/* microcode revision */
584581Ssherrym };
594581Ssherrym 
604581Ssherrym /*
614581Ssherrym  * Data structure used for xcall
624581Ssherrym  */
634581Ssherrym struct ucode_update_struct {
644581Ssherrym 	uint32_t		sig;	/* signature */
654581Ssherrym 	struct cpu_ucode_info	info;	/* ucode info */
664581Ssherrym 	uint32_t		expected_rev;
674581Ssherrym 	uint32_t		new_rev;
684581Ssherrym 	uint8_t			*ucodep; /* pointer to ucode body */
694581Ssherrym };
704581Ssherrym 
714581Ssherrym /*
724581Ssherrym  * mcpu_ucode_info for the boot CPU.  Statically allocated.
734581Ssherrym  */
744581Ssherrym static struct cpu_ucode_info cpu_ucode_info0;
754581Ssherrym 
764581Ssherrym static ucode_file_t ucodefile = { 0 };
774581Ssherrym 
784581Ssherrym static int ucode_capable(cpu_t *);
794581Ssherrym static void ucode_file_reset(ucode_file_t *, processorid_t);
804581Ssherrym static ucode_errno_t ucode_match(int, struct cpu_ucode_info *,
814581Ssherrym     ucode_header_t *, ucode_ext_table_t *);
824581Ssherrym static ucode_errno_t ucode_locate(cpu_t *, struct cpu_ucode_info *,
834581Ssherrym     ucode_file_t *);
844581Ssherrym static void ucode_update_intel(uint8_t *, struct cpu_ucode_info *);
854581Ssherrym static void ucode_read_rev(struct cpu_ucode_info *);
864581Ssherrym 
874581Ssherrym static const char ucode_failure_fmt[] =
88*6519Ssherrym 	"cpu%d: failed to update microcode from version 0x%x to 0x%x\n";
894581Ssherrym static const char ucode_success_fmt[] =
90*6519Ssherrym 	"?cpu%d: microcode has been updated from version 0x%x to 0x%x\n";
914581Ssherrym 
924581Ssherrym /*
934581Ssherrym  * Force flag.  If set, the first microcode binary that matches
944581Ssherrym  * signature and platform id will be used for microcode update,
954581Ssherrym  * regardless of version.  Should only be used for debugging.
964581Ssherrym  */
974581Ssherrym int ucode_force_update = 0;
984581Ssherrym 
994581Ssherrym /*
1004581Ssherrym  * Allocate space for mcpu_ucode_info in the machcpu structure
1014581Ssherrym  * for all non-boot CPUs.
1024581Ssherrym  */
1034581Ssherrym void
1044581Ssherrym ucode_alloc_space(cpu_t *cp)
1054581Ssherrym {
1064581Ssherrym 	ASSERT(cp->cpu_id != 0);
1074581Ssherrym 	cp->cpu_m.mcpu_ucode_info =
1084581Ssherrym 	    kmem_zalloc(sizeof (*cp->cpu_m.mcpu_ucode_info), KM_SLEEP);
1094581Ssherrym }
1104581Ssherrym 
1114581Ssherrym void
1124581Ssherrym ucode_free_space(cpu_t *cp)
1134581Ssherrym {
1144581Ssherrym 	ASSERT(cp->cpu_id != 0);
1154581Ssherrym 	kmem_free(cp->cpu_m.mcpu_ucode_info,
1164581Ssherrym 	    sizeof (*cp->cpu_m.mcpu_ucode_info));
1174581Ssherrym }
1184581Ssherrym 
1194581Ssherrym /*
1204581Ssherrym  * Called when we are done with microcode update on all processors to free up
1214581Ssherrym  * space allocated for the microcode file.
1224581Ssherrym  */
1234581Ssherrym void
1244581Ssherrym ucode_free()
1254581Ssherrym {
1264581Ssherrym 	ucode_file_reset(&ucodefile, -1);
1274581Ssherrym }
1284581Ssherrym 
1294581Ssherrym /*
1304581Ssherrym  * Check whether or not a processor is capable of microcode operations
1314581Ssherrym  * Returns 1 if it is capable, 0 if not.
1324581Ssherrym  */
1335084Sjohnlev /*ARGSUSED*/
1344581Ssherrym static int
1354581Ssherrym ucode_capable(cpu_t *cp)
1364581Ssherrym {
1375084Sjohnlev 	/* i86xpv guest domain can't update microcode */
1385084Sjohnlev #ifdef	__xpv
1395084Sjohnlev 	if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
1405084Sjohnlev 		return (0);
1415084Sjohnlev 	}
1425084Sjohnlev #endif
1435084Sjohnlev 
1445084Sjohnlev #ifndef	__xpv
1454581Ssherrym 	/*
1464581Ssherrym 	 * At this point we only support microcode update for Intel
1474581Ssherrym 	 * processors family 6 and above.
1484581Ssherrym 	 *
1494581Ssherrym 	 * We also assume that we don't support a mix of Intel and
1504581Ssherrym 	 * AMD processors in the same box.
1514581Ssherrym 	 */
1524581Ssherrym 	if (cpuid_getvendor(cp) != X86_VENDOR_Intel ||
1534581Ssherrym 	    cpuid_getfamily(cp) < 6)
1544581Ssherrym 		return (0);
1554581Ssherrym 	else
1564581Ssherrym 		return (1);
1575084Sjohnlev #else
1585084Sjohnlev 	/*
1595084Sjohnlev 	 * XXPV - remove when microcode loading works in dom0. Don't support
1605084Sjohnlev 	 * microcode loading in dom0 right now.
1615084Sjohnlev 	 */
1625084Sjohnlev 	return (0);
1635084Sjohnlev #endif
1644581Ssherrym }
1654581Ssherrym 
1664581Ssherrym /*
1674581Ssherrym  * Called when it is no longer necessary to keep the microcode around,
1684581Ssherrym  * or when the cached microcode doesn't match the CPU being processed.
1694581Ssherrym  */
1704581Ssherrym static void
1714581Ssherrym ucode_file_reset(ucode_file_t *ucodefp, processorid_t id)
1724581Ssherrym {
1734581Ssherrym 	int total_size, body_size;
1744581Ssherrym 
1754581Ssherrym 	if (ucodefp == NULL)
1764581Ssherrym 		return;
1774581Ssherrym 
1784581Ssherrym 	total_size = UCODE_TOTAL_SIZE(ucodefp->uf_header.uh_total_size);
1794581Ssherrym 	body_size = UCODE_BODY_SIZE(ucodefp->uf_header.uh_body_size);
1804581Ssherrym 	if (ucodefp->uf_body) {
1814581Ssherrym 		/*
1824581Ssherrym 		 * Space for the boot CPU is allocated with BOP_ALLOC()
1834581Ssherrym 		 * and does not require a free.
1844581Ssherrym 		 */
1854581Ssherrym 		if (id != 0)
1864581Ssherrym 			kmem_free(ucodefp->uf_body, body_size);
1874581Ssherrym 		ucodefp->uf_body = NULL;
1884581Ssherrym 	}
1894581Ssherrym 
1904581Ssherrym 	if (ucodefp->uf_ext_table) {
1914581Ssherrym 		int size = total_size - body_size - UCODE_HEADER_SIZE;
1924581Ssherrym 		/*
1934581Ssherrym 		 * Space for the boot CPU is allocated with BOP_ALLOC()
1944581Ssherrym 		 * and does not require a free.
1954581Ssherrym 		 */
1964581Ssherrym 		if (id != 0)
1974581Ssherrym 			kmem_free(ucodefp->uf_ext_table, size);
1984581Ssherrym 		ucodefp->uf_ext_table = NULL;
1994581Ssherrym 	}
2004581Ssherrym 
2014581Ssherrym 	bzero(&ucodefp->uf_header, UCODE_HEADER_SIZE);
2024581Ssherrym }
2034581Ssherrym 
2044581Ssherrym /*
2054581Ssherrym  * Populate the ucode file structure from microcode file corresponding to
2064581Ssherrym  * this CPU, if exists.
2074581Ssherrym  *
2084581Ssherrym  * Return EM_OK on success, corresponding error code on failure.
2094581Ssherrym  */
2104581Ssherrym static ucode_errno_t
2114581Ssherrym ucode_locate(cpu_t *cp, struct cpu_ucode_info *uinfop, ucode_file_t *ucodefp)
2124581Ssherrym {
2134581Ssherrym 	char		name[MAXPATHLEN];
2144581Ssherrym 	intptr_t	fd;
2154581Ssherrym 	int		count;
2164581Ssherrym 	int		header_size = UCODE_HEADER_SIZE;
2174581Ssherrym 	int		cpi_sig = cpuid_getsig(cp);
2184581Ssherrym 	ucode_errno_t	rc = EM_OK;
2194581Ssherrym 
2204581Ssherrym 	/*
2214581Ssherrym 	 * If the microcode matches the CPU we are processing, use it.
2224581Ssherrym 	 */
2234581Ssherrym 	if (ucode_match(cpi_sig, uinfop, &ucodefp->uf_header,
2244581Ssherrym 	    ucodefp->uf_ext_table) == EM_OK && ucodefp->uf_body != NULL) {
2254581Ssherrym 		return (EM_OK);
2264581Ssherrym 	}
2274581Ssherrym 
2284581Ssherrym 	/*
2294581Ssherrym 	 * Look for microcode file with the right name.
2304581Ssherrym 	 */
2314581Ssherrym 	(void) snprintf(name, MAXPATHLEN, "/%s/%s/%08X-%02X",
2324581Ssherrym 	    UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), cpi_sig,
2334581Ssherrym 	    uinfop->cui_platid);
2344581Ssherrym 	if ((fd = kobj_open(name)) == -1) {
2354581Ssherrym 		return (EM_OPENFILE);
2364581Ssherrym 	}
2374581Ssherrym 
2384581Ssherrym 	/*
2394581Ssherrym 	 * We found a microcode file for the CPU we are processing,
2404581Ssherrym 	 * reset the microcode data structure and read in the new
2414581Ssherrym 	 * file.
2424581Ssherrym 	 */
2434581Ssherrym 	ucode_file_reset(ucodefp, cp->cpu_id);
2444581Ssherrym 
2454581Ssherrym 	count = kobj_read(fd, (char *)&ucodefp->uf_header, header_size, 0);
2464581Ssherrym 
2474581Ssherrym 	switch (count) {
2484581Ssherrym 	case UCODE_HEADER_SIZE: {
2494581Ssherrym 
2504581Ssherrym 		ucode_header_t	*uhp = &ucodefp->uf_header;
2514581Ssherrym 		uint32_t	offset = header_size;
2524581Ssherrym 		int		total_size, body_size, ext_size;
2534581Ssherrym 		uint32_t	sum = 0;
2544581Ssherrym 
2554581Ssherrym 		/*
2564581Ssherrym 		 * Make sure that the header contains valid fields.
2574581Ssherrym 		 */
2584581Ssherrym 		if ((rc = ucode_header_validate(uhp)) == EM_OK) {
2594581Ssherrym 			total_size = UCODE_TOTAL_SIZE(uhp->uh_total_size);
2604581Ssherrym 			body_size = UCODE_BODY_SIZE(uhp->uh_body_size);
2614581Ssherrym 			if (cp->cpu_id != 0) {
2624581Ssherrym 				if ((ucodefp->uf_body = kmem_zalloc(body_size,
2634581Ssherrym 				    KM_NOSLEEP)) == NULL) {
2644581Ssherrym 					rc = EM_NOMEM;
2654581Ssherrym 					break;
2664581Ssherrym 				}
2674581Ssherrym 			} else {
2684581Ssherrym 				/*
2694581Ssherrym 				 * BOP_ALLOC() failure results in panic so we
2704581Ssherrym 				 * don't have to check for NULL return.
2714581Ssherrym 				 */
2724581Ssherrym 				ucodefp->uf_body =
2734581Ssherrym 				    (uint8_t *)BOP_ALLOC(bootops,
2744581Ssherrym 				    NULL, body_size, MMU_PAGESIZE);
2754581Ssherrym 			}
2764581Ssherrym 
2774581Ssherrym 			if (kobj_read(fd, (char *)ucodefp->uf_body,
2784581Ssherrym 			    body_size, offset) != body_size)
2794581Ssherrym 				rc = EM_FILESIZE;
2804581Ssherrym 		}
2814581Ssherrym 
2824581Ssherrym 		if (rc)
2834581Ssherrym 			break;
2844581Ssherrym 
2854581Ssherrym 		sum = ucode_checksum(0, header_size,
2864581Ssherrym 		    (uint8_t *)&ucodefp->uf_header);
2874581Ssherrym 		if (ucode_checksum(sum, body_size, ucodefp->uf_body)) {
2884581Ssherrym 			rc = EM_CHECKSUM;
2894581Ssherrym 			break;
2904581Ssherrym 		}
2914581Ssherrym 
2924581Ssherrym 		/*
2934581Ssherrym 		 * Check to see if there is extended signature table.
2944581Ssherrym 		 */
2954581Ssherrym 		offset = body_size + header_size;
2964581Ssherrym 		ext_size = total_size - offset;
2974581Ssherrym 
2984581Ssherrym 		if (ext_size <= 0)
2994581Ssherrym 			break;
3004581Ssherrym 
3014581Ssherrym 		if (cp->cpu_id != 0) {
3024581Ssherrym 			if ((ucodefp->uf_ext_table = kmem_zalloc(ext_size,
3034581Ssherrym 			    KM_NOSLEEP)) == NULL) {
3044581Ssherrym 				rc = EM_NOMEM;
3054581Ssherrym 				break;
3064581Ssherrym 			}
3074581Ssherrym 		} else {
3084581Ssherrym 			/*
3094581Ssherrym 			 * BOP_ALLOC() failure results in panic so we
3104581Ssherrym 			 * don't have to check for NULL return.
3114581Ssherrym 			 */
3124581Ssherrym 			ucodefp->uf_ext_table =
3134581Ssherrym 			    (ucode_ext_table_t *)BOP_ALLOC(bootops, NULL,
3144581Ssherrym 			    ext_size, MMU_PAGESIZE);
3154581Ssherrym 		}
3164581Ssherrym 
3174581Ssherrym 		if (kobj_read(fd, (char *)ucodefp->uf_ext_table,
3184581Ssherrym 		    ext_size, offset) != ext_size) {
3194581Ssherrym 			rc = EM_FILESIZE;
3204581Ssherrym 		} else if (ucode_checksum(0, ext_size,
3214581Ssherrym 		    (uint8_t *)(ucodefp->uf_ext_table))) {
3224581Ssherrym 			rc = EM_CHECKSUM;
3234581Ssherrym 		} else {
3244581Ssherrym 			int i;
3254581Ssherrym 
3264581Ssherrym 			ext_size -= UCODE_EXT_TABLE_SIZE;
3274581Ssherrym 			for (i = 0; i < ucodefp->uf_ext_table->uet_count;
3284581Ssherrym 			    i++) {
3294581Ssherrym 				if (ucode_checksum(0, UCODE_EXT_SIG_SIZE,
3304581Ssherrym 				    (uint8_t *)(&(ucodefp->uf_ext_table->
3314581Ssherrym 				    uet_ext_sig[i])))) {
3324581Ssherrym 					rc = EM_CHECKSUM;
3334581Ssherrym 					break;
3344581Ssherrym 				}
3354581Ssherrym 			}
3364581Ssherrym 		}
3374581Ssherrym 		break;
3384581Ssherrym 	}
3394581Ssherrym 
3404581Ssherrym 	default:
3414581Ssherrym 		rc = EM_FILESIZE;
3424581Ssherrym 		break;
3434581Ssherrym 	}
3444581Ssherrym 
3454581Ssherrym 	kobj_close(fd);
3464581Ssherrym 
3474581Ssherrym 	if (rc != EM_OK)
3484581Ssherrym 		return (rc);
3494581Ssherrym 
3504581Ssherrym 	rc = ucode_match(cpi_sig, uinfop, &ucodefp->uf_header,
3514581Ssherrym 	    ucodefp->uf_ext_table);
3524581Ssherrym 
3534581Ssherrym 	return (rc);
3544581Ssherrym }
3554581Ssherrym 
3564581Ssherrym 
3574581Ssherrym /*
3584581Ssherrym  * Returns 1 if the microcode is for this processor; 0 otherwise.
3594581Ssherrym  */
3604581Ssherrym static ucode_errno_t
3614581Ssherrym ucode_match(int cpi_sig, struct cpu_ucode_info *uinfop,
3624581Ssherrym     ucode_header_t *uhp, ucode_ext_table_t *uetp)
3634581Ssherrym {
3644581Ssherrym 	ASSERT(uhp);
3654581Ssherrym 
3664581Ssherrym 	if (UCODE_MATCH(cpi_sig, uhp->uh_signature,
3674581Ssherrym 	    uinfop->cui_platid, uhp->uh_proc_flags)) {
3684581Ssherrym 
3694581Ssherrym 		if (uinfop->cui_rev >= uhp->uh_rev && !ucode_force_update)
3704581Ssherrym 			return (EM_HIGHERREV);
3714581Ssherrym 
3724581Ssherrym 		return (EM_OK);
3734581Ssherrym 	}
3744581Ssherrym 
3754581Ssherrym 	if (uetp != NULL) {
3764581Ssherrym 		int i;
3774581Ssherrym 
3784581Ssherrym 		for (i = 0; i < uetp->uet_count; i++) {
3794581Ssherrym 			ucode_ext_sig_t *uesp;
3804581Ssherrym 
3814581Ssherrym 			uesp = &uetp->uet_ext_sig[i];
3824581Ssherrym 
3834581Ssherrym 			if (UCODE_MATCH(cpi_sig, uesp->ues_signature,
3844581Ssherrym 			    uinfop->cui_platid, uesp->ues_proc_flags)) {
3854581Ssherrym 
3864581Ssherrym 				if (uinfop->cui_rev >= uhp->uh_rev &&
3874581Ssherrym 				    !ucode_force_update)
3884581Ssherrym 					return (EM_HIGHERREV);
3894581Ssherrym 
3904581Ssherrym 				return (EM_OK);
3914581Ssherrym 			}
3924581Ssherrym 		}
3934581Ssherrym 	}
3944581Ssherrym 
3954581Ssherrym 	return (EM_NOMATCH);
3964581Ssherrym }
3974581Ssherrym 
3984581Ssherrym /*ARGSUSED*/
3994581Ssherrym static int
4004581Ssherrym ucode_write(xc_arg_t arg1, xc_arg_t unused2, xc_arg_t unused3)
4014581Ssherrym {
4024581Ssherrym 	struct ucode_update_struct *uusp = (struct ucode_update_struct *)arg1;
4034581Ssherrym 	struct cpu_ucode_info *uinfop = CPU->cpu_m.mcpu_ucode_info;
4044581Ssherrym 
4054581Ssherrym 	ASSERT(uusp->ucodep);
4064581Ssherrym 
4074581Ssherrym 	/*
4084581Ssherrym 	 * Check one more time to see if it is really necessary to update
4094581Ssherrym 	 * microcode just in case this is a hyperthreaded processor where
4104581Ssherrym 	 * the threads share the same microcode.
4114581Ssherrym 	 */
4124581Ssherrym 	if (!ucode_force_update) {
4134581Ssherrym 		ucode_read_rev(uinfop);
4144581Ssherrym 		uusp->new_rev = uinfop->cui_rev;
4154581Ssherrym 		if (uinfop->cui_rev >= uusp->expected_rev)
4164581Ssherrym 			return (0);
4174581Ssherrym 	}
4184581Ssherrym 
4194581Ssherrym 	wrmsr(MSR_INTC_UCODE_WRITE,
4204581Ssherrym 	    (uint64_t)(intptr_t)(uusp->ucodep));
4214581Ssherrym 	ucode_read_rev(uinfop);
4224581Ssherrym 	uusp->new_rev = uinfop->cui_rev;
4234581Ssherrym 
4244581Ssherrym 	return (0);
4254581Ssherrym }
4264581Ssherrym 
4274581Ssherrym 
4284581Ssherrym static void
4294581Ssherrym ucode_update_intel(uint8_t *ucode_body, struct cpu_ucode_info *uinfop)
4304581Ssherrym {
4314581Ssherrym 	kpreempt_disable();
4324581Ssherrym 	wrmsr(MSR_INTC_UCODE_WRITE, (uint64_t)(uintptr_t)ucode_body);
4334581Ssherrym 	ucode_read_rev(uinfop);
4344581Ssherrym 	kpreempt_enable();
4354581Ssherrym }
4364581Ssherrym 
4374581Ssherrym static void
4384581Ssherrym ucode_read_rev(struct cpu_ucode_info *uinfop)
4394581Ssherrym {
4404581Ssherrym 	struct cpuid_regs crs;
4414581Ssherrym 
4424581Ssherrym 	/*
4434581Ssherrym 	 * The Intel 64 and IA-32 Architecture Software Developer's Manual
4444581Ssherrym 	 * recommends that MSR_INTC_UCODE_REV be loaded with 0 first, then
4454581Ssherrym 	 * execute cpuid to guarantee the correct reading of this register.
4464581Ssherrym 	 */
4474581Ssherrym 	wrmsr(MSR_INTC_UCODE_REV, 0);
4484581Ssherrym 	(void) __cpuid_insn(&crs);
4494581Ssherrym 	uinfop->cui_rev = (rdmsr(MSR_INTC_UCODE_REV) >> INTC_UCODE_REV_SHIFT);
4504581Ssherrym }
4514581Ssherrym 
4524581Ssherrym /*
4534581Ssherrym  * Entry point to microcode update from the ucode_drv driver.
4544581Ssherrym  *
4554581Ssherrym  * Returns EM_OK on success, corresponding error code on failure.
4564581Ssherrym  */
4574581Ssherrym ucode_errno_t
4584581Ssherrym ucode_update(uint8_t *ucodep, int size)
4594581Ssherrym {
4604581Ssherrym 	uint32_t	header_size = UCODE_HEADER_SIZE;
4614581Ssherrym 	int		remaining;
4624581Ssherrym 	int		found = 0;
4634581Ssherrym 	processorid_t	id;
4644581Ssherrym 	struct ucode_update_struct cached = { 0 };
4654581Ssherrym 	struct ucode_update_struct *cachedp = NULL;
4664581Ssherrym 	ucode_errno_t	rc = EM_OK;
4674581Ssherrym 	ucode_errno_t	search_rc = EM_NOMATCH; /* search result */
4684581Ssherrym 	cpuset_t cpuset;
4694581Ssherrym 
4704581Ssherrym 	ASSERT(ucodep);
4714581Ssherrym 
4724581Ssherrym 	CPUSET_ZERO(cpuset);
4734581Ssherrym 
4744581Ssherrym 	if (!ucode_capable(CPU))
4754581Ssherrym 		return (EM_NOTSUP);
4764581Ssherrym 
4774581Ssherrym 	mutex_enter(&cpu_lock);
4784581Ssherrym 
4794581Ssherrym 	for (id = 0; id < max_ncpus; id++) {
4804581Ssherrym 		cpu_t *cpu;
4814581Ssherrym 		struct ucode_update_struct uus = { 0 };
4824581Ssherrym 		struct ucode_update_struct *uusp = &uus;
4834581Ssherrym 
4844581Ssherrym 		/*
4854581Ssherrym 		 * If there is no such CPU or it is not xcall ready, skip it.
4864581Ssherrym 		 */
4874581Ssherrym 		if ((cpu = cpu_get(id)) == NULL ||
4884581Ssherrym 		    !(cpu->cpu_flags & CPU_READY))
4894581Ssherrym 			continue;
4904581Ssherrym 
4914581Ssherrym 		uusp->sig = cpuid_getsig(cpu);
4924581Ssherrym 		bcopy(cpu->cpu_m.mcpu_ucode_info, &uusp->info,
4934581Ssherrym 		    sizeof (uusp->info));
4944581Ssherrym 
4954581Ssherrym 		/*
4964581Ssherrym 		 * If the current CPU has the same signature and platform
4974581Ssherrym 		 * id as the previous one we processed, reuse the information.
4984581Ssherrym 		 */
4994581Ssherrym 		if (cachedp && cachedp->sig == cpuid_getsig(cpu) &&
5004581Ssherrym 		    cachedp->info.cui_platid == uusp->info.cui_platid) {
5014581Ssherrym 			uusp->ucodep = cachedp->ucodep;
5024581Ssherrym 			uusp->expected_rev = cachedp->expected_rev;
5034581Ssherrym 			/*
5044581Ssherrym 			 * Intuitively we should check here to see whether the
5054581Ssherrym 			 * running microcode rev is >= the expected rev, and
5064581Ssherrym 			 * quit if it is.  But we choose to proceed with the
5074581Ssherrym 			 * xcall regardless of the running version so that
5084581Ssherrym 			 * the other threads in an HT processor can update
5094581Ssherrym 			 * the cpu_ucode_info structure in machcpu.
5104581Ssherrym 			 */
5114581Ssherrym 		} else {
5124581Ssherrym 			/*
5134581Ssherrym 			 * Go through the whole buffer in case there are
5144581Ssherrym 			 * multiple versions of matching microcode for this
5154581Ssherrym 			 * processor.
5164581Ssherrym 			 */
5174581Ssherrym 			for (remaining = size; remaining > 0; ) {
5184581Ssherrym 				int	total_size, body_size, ext_size;
5194581Ssherrym 				uint8_t	*curbuf = &ucodep[size - remaining];
5204581Ssherrym 				ucode_header_t	*uhp = (ucode_header_t *)curbuf;
5214581Ssherrym 				ucode_ext_table_t *uetp = NULL;
5224581Ssherrym 				ucode_errno_t tmprc;
5234581Ssherrym 
5244581Ssherrym 				total_size =
5254581Ssherrym 				    UCODE_TOTAL_SIZE(uhp->uh_total_size);
5264581Ssherrym 				body_size = UCODE_BODY_SIZE(uhp->uh_body_size);
5274581Ssherrym 				ext_size = total_size -
5284581Ssherrym 				    (header_size + body_size);
5294581Ssherrym 
5304581Ssherrym 				if (ext_size > 0)
5314581Ssherrym 					uetp = (ucode_ext_table_t *)
5324581Ssherrym 					    &curbuf[header_size + body_size];
5334581Ssherrym 
5344581Ssherrym 				tmprc = ucode_match(uusp->sig, &uusp->info,
5354581Ssherrym 				    uhp, uetp);
5364581Ssherrym 
5374581Ssherrym 				/*
5384581Ssherrym 				 * Since we are searching through a big file
5394581Ssherrym 				 * containing microcode for pretty much all the
5404581Ssherrym 				 * processors, we are bound to get EM_NOMATCH
5414581Ssherrym 				 * at one point.  However, if we return
5424581Ssherrym 				 * EM_NOMATCH to users, it will really confuse
5434581Ssherrym 				 * them.  Therefore, if we ever find a match of
5444581Ssherrym 				 * a lower rev, we will set return code to
5454581Ssherrym 				 * EM_HIGHERREV.
5464581Ssherrym 				 */
5474581Ssherrym 				if (tmprc == EM_HIGHERREV)
5484581Ssherrym 					search_rc = EM_HIGHERREV;
5494581Ssherrym 
5504581Ssherrym 				if (tmprc == EM_OK &&
5514581Ssherrym 				    uusp->expected_rev < uhp->uh_rev) {
5524581Ssherrym 					uusp->ucodep = &curbuf[header_size];
5534581Ssherrym 					uusp->expected_rev = uhp->uh_rev;
5544581Ssherrym 					bcopy(uusp, &cached, sizeof (cached));
5554581Ssherrym 					cachedp = &cached;
5564581Ssherrym 					found = 1;
5574581Ssherrym 				}
5584581Ssherrym 
5594581Ssherrym 				remaining -= total_size;
5604581Ssherrym 			}
5614581Ssherrym 		}
5624581Ssherrym 
5634581Ssherrym 		/* Nothing to do */
5644581Ssherrym 		if (uusp->ucodep == NULL)
5654581Ssherrym 			continue;
5664581Ssherrym 
5674581Ssherrym 		CPUSET_ADD(cpuset, id);
5684581Ssherrym 		kpreempt_disable();
5694581Ssherrym 		xc_sync((xc_arg_t)uusp, 0, 0, X_CALL_HIPRI, cpuset,
5704581Ssherrym 		    ucode_write);
5714581Ssherrym 		kpreempt_enable();
5724581Ssherrym 		CPUSET_DEL(cpuset, id);
5734581Ssherrym 
5744581Ssherrym 		if (uusp->expected_rev == uusp->new_rev) {
5754581Ssherrym 			cmn_err(CE_CONT, ucode_success_fmt,
5764581Ssherrym 			    id, uusp->info.cui_rev, uusp->expected_rev);
5774581Ssherrym 		} else {
5784581Ssherrym 			cmn_err(CE_WARN, ucode_failure_fmt,
5794581Ssherrym 			    id, uusp->info.cui_rev, uusp->expected_rev);
5804581Ssherrym 			rc = EM_UPDATE;
5814581Ssherrym 		}
5824581Ssherrym 	}
5834581Ssherrym 
5844581Ssherrym 	mutex_exit(&cpu_lock);
5854581Ssherrym 
5864581Ssherrym 	if (!found)
5874581Ssherrym 		rc = search_rc;
5884581Ssherrym 
5894581Ssherrym 	return (rc);
5904581Ssherrym }
5914581Ssherrym 
5924581Ssherrym /*
5934581Ssherrym  * Initialize mcpu_ucode_info, and perform microcode update if necessary.
5944581Ssherrym  * This is the entry point from boot path where pointer to CPU structure
5954581Ssherrym  * is available.
5964581Ssherrym  *
5974581Ssherrym  * cpuid_info must be initialized before ucode_check can be called.
5984581Ssherrym  */
5994581Ssherrym void
6004581Ssherrym ucode_check(cpu_t *cp)
6014581Ssherrym {
6024581Ssherrym 	struct cpu_ucode_info *uinfop;
6034581Ssherrym 	ucode_errno_t rc = EM_OK;
6044581Ssherrym 
6054581Ssherrym 	ASSERT(cp);
6064581Ssherrym 	if (cp->cpu_id == 0)
6074581Ssherrym 		cp->cpu_m.mcpu_ucode_info = &cpu_ucode_info0;
6084581Ssherrym 
6094581Ssherrym 	uinfop = cp->cpu_m.mcpu_ucode_info;
6104581Ssherrym 	ASSERT(uinfop);
6114581Ssherrym 
6124581Ssherrym 	if (!ucode_capable(cp))
6134581Ssherrym 		return;
6144581Ssherrym 
6154581Ssherrym 	/*
6164581Ssherrym 	 * The MSR_INTC_PLATFORM_ID is supported in Celeron and Xeon
6174581Ssherrym 	 * (Family 6, model 5 and above) and all processors after.
6184581Ssherrym 	 */
6194581Ssherrym 	if ((cpuid_getmodel(cp) >= 5) || (cpuid_getfamily(cp) > 6)) {
6204581Ssherrym 		uinfop->cui_platid = 1 << ((rdmsr(MSR_INTC_PLATFORM_ID) >>
6214581Ssherrym 		    INTC_PLATFORM_ID_SHIFT) & INTC_PLATFORM_ID_MASK);
6224581Ssherrym 	}
6234581Ssherrym 
6244581Ssherrym 	ucode_read_rev(uinfop);
6254581Ssherrym 
6264581Ssherrym 	/*
6274581Ssherrym 	 * Check to see if we need ucode update
6284581Ssherrym 	 */
6294581Ssherrym 	if ((rc = ucode_locate(cp, uinfop, &ucodefile)) == EM_OK) {
6304581Ssherrym 		ucode_update_intel(ucodefile.uf_body, uinfop);
6314581Ssherrym 
6324581Ssherrym 		if (uinfop->cui_rev != ucodefile.uf_header.uh_rev)
6334581Ssherrym 			cmn_err(CE_WARN, ucode_failure_fmt, cp->cpu_id,
6344581Ssherrym 			    uinfop->cui_rev, ucodefile.uf_header.uh_rev);
6354581Ssherrym 	}
6364581Ssherrym 
6374581Ssherrym 	/*
6384581Ssherrym 	 * If we fail to find a match for any reason, free the file structure
6394581Ssherrym 	 * just in case we have read in a partial file.
6404581Ssherrym 	 *
6414581Ssherrym 	 * Since the scratch memory for holding the microcode for the boot CPU
6424581Ssherrym 	 * came from BOP_ALLOC, we will reset the data structure as if we
6434581Ssherrym 	 * never did the allocation so we don't have to keep track of this
6444581Ssherrym 	 * special chunk of memory.  We free the memory used for the rest
6454581Ssherrym 	 * of the CPUs in start_other_cpus().
6464581Ssherrym 	 */
6474581Ssherrym 	if (rc != EM_OK || cp->cpu_id == 0)
6484581Ssherrym 		ucode_file_reset(&ucodefile, cp->cpu_id);
6494581Ssherrym }
6504581Ssherrym 
6514581Ssherrym /*
6524581Ssherrym  * Returns microcode revision from the machcpu structure.
6534581Ssherrym  */
6544581Ssherrym ucode_errno_t
6554581Ssherrym ucode_get_rev(uint32_t *revp)
6564581Ssherrym {
6574581Ssherrym 	int i;
6584581Ssherrym 
6594581Ssherrym 	ASSERT(revp);
6604581Ssherrym 
6614581Ssherrym 	if (!ucode_capable(CPU))
6624581Ssherrym 		return (EM_NOTSUP);
6634581Ssherrym 
6644581Ssherrym 	mutex_enter(&cpu_lock);
6654581Ssherrym 	for (i = 0; i < max_ncpus; i++) {
6664581Ssherrym 		cpu_t *cpu;
6674581Ssherrym 
6684581Ssherrym 		if ((cpu = cpu_get(i)) == NULL)
6694581Ssherrym 			continue;
6704581Ssherrym 
6714581Ssherrym 		revp[i] = cpu->cpu_m.mcpu_ucode_info->cui_rev;
6724581Ssherrym 	}
6734581Ssherrym 	mutex_exit(&cpu_lock);
6744581Ssherrym 
6754581Ssherrym 	return (EM_OK);
6764581Ssherrym }
677