xref: /onnv-gate/usr/src/uts/i86pc/os/microcode.c (revision 7605:b4a19682e632)
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 /*
51*7605SMark.Johnson@Sun.COM  * AMD-specific equivalence table
524581Ssherrym  */
53*7605SMark.Johnson@Sun.COM static ucode_eqtbl_amd_t *ucode_eqtbl_amd;
544581Ssherrym 
554581Ssherrym /*
564581Ssherrym  * mcpu_ucode_info for the boot CPU.  Statically allocated.
574581Ssherrym  */
584581Ssherrym static struct cpu_ucode_info cpu_ucode_info0;
594581Ssherrym 
60*7605SMark.Johnson@Sun.COM static ucode_file_t ucodefile;
61*7605SMark.Johnson@Sun.COM 
62*7605SMark.Johnson@Sun.COM static void* ucode_zalloc(processorid_t, size_t);
63*7605SMark.Johnson@Sun.COM static void ucode_free(processorid_t, void *, size_t);
64*7605SMark.Johnson@Sun.COM 
65*7605SMark.Johnson@Sun.COM static int ucode_capable_amd(cpu_t *);
66*7605SMark.Johnson@Sun.COM static int ucode_capable_intel(cpu_t *);
67*7605SMark.Johnson@Sun.COM 
68*7605SMark.Johnson@Sun.COM static ucode_errno_t ucode_extract_amd(ucode_update_t *, uint8_t *, int);
69*7605SMark.Johnson@Sun.COM static ucode_errno_t ucode_extract_intel(ucode_update_t *, uint8_t *,
70*7605SMark.Johnson@Sun.COM     int);
71*7605SMark.Johnson@Sun.COM 
72*7605SMark.Johnson@Sun.COM static void ucode_file_reset_amd(ucode_file_t *, processorid_t);
73*7605SMark.Johnson@Sun.COM static void ucode_file_reset_intel(ucode_file_t *, processorid_t);
74*7605SMark.Johnson@Sun.COM 
75*7605SMark.Johnson@Sun.COM static uint32_t ucode_load_amd(ucode_file_t *, cpu_ucode_info_t *, cpu_t *);
76*7605SMark.Johnson@Sun.COM static uint32_t ucode_load_intel(ucode_file_t *, cpu_ucode_info_t *, cpu_t *);
77*7605SMark.Johnson@Sun.COM 
78*7605SMark.Johnson@Sun.COM #ifdef	__xpv
79*7605SMark.Johnson@Sun.COM static void ucode_load_xpv(ucode_update_t *);
80*7605SMark.Johnson@Sun.COM #endif
81*7605SMark.Johnson@Sun.COM 
82*7605SMark.Johnson@Sun.COM static int ucode_equiv_cpu_amd(cpu_t *, int *);
83*7605SMark.Johnson@Sun.COM 
84*7605SMark.Johnson@Sun.COM static ucode_errno_t ucode_locate_amd(cpu_t *, cpu_ucode_info_t *,
85*7605SMark.Johnson@Sun.COM     ucode_file_t *);
86*7605SMark.Johnson@Sun.COM static ucode_errno_t ucode_locate_intel(cpu_t *, cpu_ucode_info_t *,
87*7605SMark.Johnson@Sun.COM     ucode_file_t *);
884581Ssherrym 
89*7605SMark.Johnson@Sun.COM static ucode_errno_t ucode_match_amd(int, cpu_ucode_info_t *,
90*7605SMark.Johnson@Sun.COM     ucode_file_amd_t *, int);
91*7605SMark.Johnson@Sun.COM static ucode_errno_t ucode_match_intel(int, cpu_ucode_info_t *,
92*7605SMark.Johnson@Sun.COM     ucode_header_intel_t *, ucode_ext_table_intel_t *);
93*7605SMark.Johnson@Sun.COM 
94*7605SMark.Johnson@Sun.COM static void ucode_read_rev_amd(cpu_ucode_info_t *);
95*7605SMark.Johnson@Sun.COM static void ucode_read_rev_intel(cpu_ucode_info_t *);
96*7605SMark.Johnson@Sun.COM 
97*7605SMark.Johnson@Sun.COM static const struct ucode_ops ucode_amd = {
98*7605SMark.Johnson@Sun.COM 	MSR_AMD_PATCHLOADER,
99*7605SMark.Johnson@Sun.COM 	ucode_capable_amd,
100*7605SMark.Johnson@Sun.COM 	ucode_file_reset_amd,
101*7605SMark.Johnson@Sun.COM 	ucode_read_rev_amd,
102*7605SMark.Johnson@Sun.COM 	ucode_load_amd,
103*7605SMark.Johnson@Sun.COM 	ucode_validate_amd,
104*7605SMark.Johnson@Sun.COM 	ucode_extract_amd,
105*7605SMark.Johnson@Sun.COM 	ucode_locate_amd
106*7605SMark.Johnson@Sun.COM };
107*7605SMark.Johnson@Sun.COM 
108*7605SMark.Johnson@Sun.COM static const struct ucode_ops ucode_intel = {
109*7605SMark.Johnson@Sun.COM 	MSR_INTC_UCODE_WRITE,
110*7605SMark.Johnson@Sun.COM 	ucode_capable_intel,
111*7605SMark.Johnson@Sun.COM 	ucode_file_reset_intel,
112*7605SMark.Johnson@Sun.COM 	ucode_read_rev_intel,
113*7605SMark.Johnson@Sun.COM 	ucode_load_intel,
114*7605SMark.Johnson@Sun.COM 	ucode_validate_intel,
115*7605SMark.Johnson@Sun.COM 	ucode_extract_intel,
116*7605SMark.Johnson@Sun.COM 	ucode_locate_intel
117*7605SMark.Johnson@Sun.COM };
118*7605SMark.Johnson@Sun.COM 
119*7605SMark.Johnson@Sun.COM const struct ucode_ops *ucode;
1204581Ssherrym 
1214581Ssherrym static const char ucode_failure_fmt[] =
1226519Ssherrym 	"cpu%d: failed to update microcode from version 0x%x to 0x%x\n";
1234581Ssherrym static const char ucode_success_fmt[] =
1246519Ssherrym 	"?cpu%d: microcode has been updated from version 0x%x to 0x%x\n";
1254581Ssherrym 
1264581Ssherrym /*
1274581Ssherrym  * Force flag.  If set, the first microcode binary that matches
1284581Ssherrym  * signature and platform id will be used for microcode update,
1294581Ssherrym  * regardless of version.  Should only be used for debugging.
1304581Ssherrym  */
1314581Ssherrym int ucode_force_update = 0;
1324581Ssherrym 
1334581Ssherrym /*
1344581Ssherrym  * Allocate space for mcpu_ucode_info in the machcpu structure
1354581Ssherrym  * for all non-boot CPUs.
1364581Ssherrym  */
1374581Ssherrym void
1384581Ssherrym ucode_alloc_space(cpu_t *cp)
1394581Ssherrym {
1404581Ssherrym 	ASSERT(cp->cpu_id != 0);
1414581Ssherrym 	cp->cpu_m.mcpu_ucode_info =
1424581Ssherrym 	    kmem_zalloc(sizeof (*cp->cpu_m.mcpu_ucode_info), KM_SLEEP);
1434581Ssherrym }
1444581Ssherrym 
1454581Ssherrym void
1464581Ssherrym ucode_free_space(cpu_t *cp)
1474581Ssherrym {
1484581Ssherrym 	ASSERT(cp->cpu_id != 0);
1494581Ssherrym 	kmem_free(cp->cpu_m.mcpu_ucode_info,
1504581Ssherrym 	    sizeof (*cp->cpu_m.mcpu_ucode_info));
1514581Ssherrym }
1524581Ssherrym 
1534581Ssherrym /*
1544581Ssherrym  * Called when we are done with microcode update on all processors to free up
1554581Ssherrym  * space allocated for the microcode file.
1564581Ssherrym  */
1574581Ssherrym void
158*7605SMark.Johnson@Sun.COM ucode_cleanup()
1594581Ssherrym {
160*7605SMark.Johnson@Sun.COM 	ASSERT(ucode);
161*7605SMark.Johnson@Sun.COM 
162*7605SMark.Johnson@Sun.COM 	ucode->file_reset(&ucodefile, -1);
163*7605SMark.Johnson@Sun.COM }
164*7605SMark.Johnson@Sun.COM 
165*7605SMark.Johnson@Sun.COM /*
166*7605SMark.Johnson@Sun.COM  * Allocate/free a buffer used to hold ucode data. Space for the boot CPU is
167*7605SMark.Johnson@Sun.COM  * allocated with BOP_ALLOC() and does not require a free.
168*7605SMark.Johnson@Sun.COM  */
169*7605SMark.Johnson@Sun.COM static void*
170*7605SMark.Johnson@Sun.COM ucode_zalloc(processorid_t id, size_t size)
171*7605SMark.Johnson@Sun.COM {
172*7605SMark.Johnson@Sun.COM 	if (id)
173*7605SMark.Johnson@Sun.COM 		return (kmem_zalloc(size, KM_NOSLEEP));
174*7605SMark.Johnson@Sun.COM 
175*7605SMark.Johnson@Sun.COM 	/* BOP_ALLOC() failure results in panic */
176*7605SMark.Johnson@Sun.COM 	return (BOP_ALLOC(bootops, NULL, size, MMU_PAGESIZE));
177*7605SMark.Johnson@Sun.COM }
178*7605SMark.Johnson@Sun.COM 
179*7605SMark.Johnson@Sun.COM static void
180*7605SMark.Johnson@Sun.COM ucode_free(processorid_t id, void* buf, size_t size)
181*7605SMark.Johnson@Sun.COM {
182*7605SMark.Johnson@Sun.COM 	if (id)
183*7605SMark.Johnson@Sun.COM 		kmem_free(buf, size);
1844581Ssherrym }
1854581Ssherrym 
1864581Ssherrym /*
1874581Ssherrym  * Check whether or not a processor is capable of microcode operations
1884581Ssherrym  * Returns 1 if it is capable, 0 if not.
189*7605SMark.Johnson@Sun.COM  *
190*7605SMark.Johnson@Sun.COM  * At this point we only support microcode update for:
191*7605SMark.Johnson@Sun.COM  * - Intel processors family 6 and above, and
192*7605SMark.Johnson@Sun.COM  * - AMD processors family 0x10 and above.
193*7605SMark.Johnson@Sun.COM  *
194*7605SMark.Johnson@Sun.COM  * We also assume that we don't support a mix of Intel and
195*7605SMark.Johnson@Sun.COM  * AMD processors in the same box.
196*7605SMark.Johnson@Sun.COM  *
197*7605SMark.Johnson@Sun.COM  * An i86xpv guest domain can't update the microcode.
1984581Ssherrym  */
199*7605SMark.Johnson@Sun.COM /*ARGSUSED*/
2004581Ssherrym static int
201*7605SMark.Johnson@Sun.COM ucode_capable_amd(cpu_t *cp)
2024581Ssherrym {
203*7605SMark.Johnson@Sun.COM #ifndef	__xpv
204*7605SMark.Johnson@Sun.COM 	extern int xpv_is_hvm;
205*7605SMark.Johnson@Sun.COM 	if (xpv_is_hvm) {
206*7605SMark.Johnson@Sun.COM 		return (0);
207*7605SMark.Johnson@Sun.COM 	}
208*7605SMark.Johnson@Sun.COM 
209*7605SMark.Johnson@Sun.COM 	return (cpuid_getfamily(cp) >= 0x10);
210*7605SMark.Johnson@Sun.COM #else
211*7605SMark.Johnson@Sun.COM 	if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
212*7605SMark.Johnson@Sun.COM 		return (0);
213*7605SMark.Johnson@Sun.COM 	}
214*7605SMark.Johnson@Sun.COM 
215*7605SMark.Johnson@Sun.COM 	/*
216*7605SMark.Johnson@Sun.COM 	 * XXPV - change when microcode loading works in dom0. Don't support
217*7605SMark.Johnson@Sun.COM 	 * microcode loading in dom0 right now for AMD.
218*7605SMark.Johnson@Sun.COM 	 */
219*7605SMark.Johnson@Sun.COM 	return (0);
220*7605SMark.Johnson@Sun.COM #endif
221*7605SMark.Johnson@Sun.COM }
222*7605SMark.Johnson@Sun.COM static int
223*7605SMark.Johnson@Sun.COM ucode_capable_intel(cpu_t *cp)
224*7605SMark.Johnson@Sun.COM {
2257347SMark.Johnson@Sun.COM #ifndef	__xpv
2267347SMark.Johnson@Sun.COM 	extern int xpv_is_hvm;
2277347SMark.Johnson@Sun.COM 	if (xpv_is_hvm) {
2287347SMark.Johnson@Sun.COM 		return (0);
2297347SMark.Johnson@Sun.COM 	}
2307347SMark.Johnson@Sun.COM #else
2315084Sjohnlev 	if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
2325084Sjohnlev 		return (0);
2335084Sjohnlev 	}
2345084Sjohnlev #endif
235*7605SMark.Johnson@Sun.COM 	return (cpuid_getfamily(cp) >= 6);
2364581Ssherrym }
2374581Ssherrym 
2384581Ssherrym /*
2394581Ssherrym  * Called when it is no longer necessary to keep the microcode around,
2404581Ssherrym  * or when the cached microcode doesn't match the CPU being processed.
2414581Ssherrym  */
2424581Ssherrym static void
243*7605SMark.Johnson@Sun.COM ucode_file_reset_amd(ucode_file_t *ufp, processorid_t id)
2444581Ssherrym {
245*7605SMark.Johnson@Sun.COM 	ucode_file_amd_t *ucodefp = ufp->amd;
2464581Ssherrym 
2474581Ssherrym 	if (ucodefp == NULL)
2484581Ssherrym 		return;
2494581Ssherrym 
250*7605SMark.Johnson@Sun.COM 	ucode_free(id, ucodefp, sizeof (ucode_file_amd_t));
251*7605SMark.Johnson@Sun.COM 	ufp->amd = NULL;
252*7605SMark.Johnson@Sun.COM }
253*7605SMark.Johnson@Sun.COM 
254*7605SMark.Johnson@Sun.COM static void
255*7605SMark.Johnson@Sun.COM ucode_file_reset_intel(ucode_file_t *ufp, processorid_t id)
256*7605SMark.Johnson@Sun.COM {
257*7605SMark.Johnson@Sun.COM 	ucode_file_intel_t *ucodefp = &ufp->intel;
258*7605SMark.Johnson@Sun.COM 	int total_size, body_size;
259*7605SMark.Johnson@Sun.COM 
260*7605SMark.Johnson@Sun.COM 	if (ucodefp == NULL || ucodefp->uf_header == NULL)
261*7605SMark.Johnson@Sun.COM 		return;
262*7605SMark.Johnson@Sun.COM 
263*7605SMark.Johnson@Sun.COM 	total_size = UCODE_TOTAL_SIZE_INTEL(ucodefp->uf_header->uh_total_size);
264*7605SMark.Johnson@Sun.COM 	body_size = UCODE_BODY_SIZE_INTEL(ucodefp->uf_header->uh_body_size);
2654581Ssherrym 	if (ucodefp->uf_body) {
266*7605SMark.Johnson@Sun.COM 		ucode_free(id, ucodefp->uf_body, body_size);
2674581Ssherrym 		ucodefp->uf_body = NULL;
2684581Ssherrym 	}
2694581Ssherrym 
2704581Ssherrym 	if (ucodefp->uf_ext_table) {
271*7605SMark.Johnson@Sun.COM 		int size = total_size - body_size - UCODE_HEADER_SIZE_INTEL;
272*7605SMark.Johnson@Sun.COM 
273*7605SMark.Johnson@Sun.COM 		ucode_free(id, ucodefp->uf_ext_table, size);
2744581Ssherrym 		ucodefp->uf_ext_table = NULL;
2754581Ssherrym 	}
2764581Ssherrym 
277*7605SMark.Johnson@Sun.COM 	ucode_free(id, ucodefp->uf_header, UCODE_HEADER_SIZE_INTEL);
278*7605SMark.Johnson@Sun.COM 	ucodefp->uf_header = NULL;
279*7605SMark.Johnson@Sun.COM }
280*7605SMark.Johnson@Sun.COM 
281*7605SMark.Johnson@Sun.COM /*
282*7605SMark.Johnson@Sun.COM  * Find the equivalent CPU id in the equivalence table.
283*7605SMark.Johnson@Sun.COM  */
284*7605SMark.Johnson@Sun.COM static int
285*7605SMark.Johnson@Sun.COM ucode_equiv_cpu_amd(cpu_t *cp, int *eq_sig)
286*7605SMark.Johnson@Sun.COM {
287*7605SMark.Johnson@Sun.COM 	char name[MAXPATHLEN];
288*7605SMark.Johnson@Sun.COM 	intptr_t fd;
289*7605SMark.Johnson@Sun.COM 	int count;
290*7605SMark.Johnson@Sun.COM 	int offset = 0, cpi_sig = cpuid_getsig(cp);
291*7605SMark.Johnson@Sun.COM 	ucode_eqtbl_amd_t *eqtbl = ucode_eqtbl_amd;
292*7605SMark.Johnson@Sun.COM 
293*7605SMark.Johnson@Sun.COM 	(void) snprintf(name, MAXPATHLEN, "/%s/%s/equivalence-table",
294*7605SMark.Johnson@Sun.COM 	    UCODE_INSTALL_PATH, cpuid_getvendorstr(cp));
295*7605SMark.Johnson@Sun.COM 
296*7605SMark.Johnson@Sun.COM 	/*
297*7605SMark.Johnson@Sun.COM 	 * No kmem_zalloc() etc. available on boot cpu.
298*7605SMark.Johnson@Sun.COM 	 */
299*7605SMark.Johnson@Sun.COM 	if (cp->cpu_id == 0) {
300*7605SMark.Johnson@Sun.COM 		if ((fd = kobj_open(name)) == -1)
301*7605SMark.Johnson@Sun.COM 			return (EM_OPENFILE);
302*7605SMark.Johnson@Sun.COM 		/* ucode_zalloc() cannot fail on boot cpu */
303*7605SMark.Johnson@Sun.COM 		eqtbl = ucode_zalloc(cp->cpu_id, sizeof (*eqtbl));
304*7605SMark.Johnson@Sun.COM 		ASSERT(eqtbl);
305*7605SMark.Johnson@Sun.COM 		do {
306*7605SMark.Johnson@Sun.COM 			count = kobj_read(fd, (int8_t *)eqtbl,
307*7605SMark.Johnson@Sun.COM 			    sizeof (*eqtbl), offset);
308*7605SMark.Johnson@Sun.COM 			if (count != sizeof (*eqtbl)) {
309*7605SMark.Johnson@Sun.COM 				(void) kobj_close(fd);
310*7605SMark.Johnson@Sun.COM 				return (EM_HIGHERREV);
311*7605SMark.Johnson@Sun.COM 			}
312*7605SMark.Johnson@Sun.COM 			offset += count;
313*7605SMark.Johnson@Sun.COM 		} while (eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != cpi_sig);
314*7605SMark.Johnson@Sun.COM 		(void) kobj_close(fd);
315*7605SMark.Johnson@Sun.COM 	}
316*7605SMark.Johnson@Sun.COM 
317*7605SMark.Johnson@Sun.COM 	/*
318*7605SMark.Johnson@Sun.COM 	 * If not already done, load the equivalence table.
319*7605SMark.Johnson@Sun.COM 	 * Not done on boot CPU.
320*7605SMark.Johnson@Sun.COM 	 */
321*7605SMark.Johnson@Sun.COM 	if (eqtbl == NULL) {
322*7605SMark.Johnson@Sun.COM 		struct _buf *eq;
323*7605SMark.Johnson@Sun.COM 		uint64_t size;
324*7605SMark.Johnson@Sun.COM 
325*7605SMark.Johnson@Sun.COM 		if ((eq = kobj_open_file(name)) == (struct _buf *)-1)
326*7605SMark.Johnson@Sun.COM 			return (EM_OPENFILE);
327*7605SMark.Johnson@Sun.COM 
328*7605SMark.Johnson@Sun.COM 		if (kobj_get_filesize(eq, &size) < 0) {
329*7605SMark.Johnson@Sun.COM 			kobj_close_file(eq);
330*7605SMark.Johnson@Sun.COM 			return (EM_OPENFILE);
331*7605SMark.Johnson@Sun.COM 		}
332*7605SMark.Johnson@Sun.COM 
333*7605SMark.Johnson@Sun.COM 		ucode_eqtbl_amd = kmem_zalloc(size, KM_NOSLEEP);
334*7605SMark.Johnson@Sun.COM 		if (ucode_eqtbl_amd == NULL) {
335*7605SMark.Johnson@Sun.COM 			kobj_close_file(eq);
336*7605SMark.Johnson@Sun.COM 			return (EM_NOMEM);
337*7605SMark.Johnson@Sun.COM 		}
338*7605SMark.Johnson@Sun.COM 
339*7605SMark.Johnson@Sun.COM 		count = kobj_read_file(eq, (char *)ucode_eqtbl_amd, size, 0);
340*7605SMark.Johnson@Sun.COM 		kobj_close_file(eq);
341*7605SMark.Johnson@Sun.COM 
342*7605SMark.Johnson@Sun.COM 		if (count != size)
343*7605SMark.Johnson@Sun.COM 			return (EM_FILESIZE);
344*7605SMark.Johnson@Sun.COM 	}
345*7605SMark.Johnson@Sun.COM 
346*7605SMark.Johnson@Sun.COM 	/* Get the equivalent CPU id. */
347*7605SMark.Johnson@Sun.COM 	if (cp->cpu_id)
348*7605SMark.Johnson@Sun.COM 		for (eqtbl = ucode_eqtbl_amd;
349*7605SMark.Johnson@Sun.COM 		    eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != cpi_sig;
350*7605SMark.Johnson@Sun.COM 		    eqtbl++)
351*7605SMark.Johnson@Sun.COM 			;
352*7605SMark.Johnson@Sun.COM 
353*7605SMark.Johnson@Sun.COM 	*eq_sig = eqtbl->ue_equiv_cpu;
354*7605SMark.Johnson@Sun.COM 	*eq_sig = ((*eq_sig >> 8) & 0xff00) | (*eq_sig & 0xff);
355*7605SMark.Johnson@Sun.COM 
356*7605SMark.Johnson@Sun.COM 	/* No equivalent CPU id found, assume outdated microcode file. */
357*7605SMark.Johnson@Sun.COM 	if (*eq_sig == 0)
358*7605SMark.Johnson@Sun.COM 		return (EM_HIGHERREV);
359*7605SMark.Johnson@Sun.COM 
360*7605SMark.Johnson@Sun.COM 	return (EM_OK);
3614581Ssherrym }
3624581Ssherrym 
3634581Ssherrym /*
3644581Ssherrym  * Populate the ucode file structure from microcode file corresponding to
3654581Ssherrym  * this CPU, if exists.
3664581Ssherrym  *
3674581Ssherrym  * Return EM_OK on success, corresponding error code on failure.
3684581Ssherrym  */
3694581Ssherrym static ucode_errno_t
370*7605SMark.Johnson@Sun.COM ucode_locate_amd(cpu_t *cp, cpu_ucode_info_t *uinfop, ucode_file_t *ufp)
371*7605SMark.Johnson@Sun.COM {
372*7605SMark.Johnson@Sun.COM 	char name[MAXPATHLEN];
373*7605SMark.Johnson@Sun.COM 	intptr_t fd;
374*7605SMark.Johnson@Sun.COM 	int count, i, rc;
375*7605SMark.Johnson@Sun.COM 	int eq_sig = 0;
376*7605SMark.Johnson@Sun.COM 	ucode_file_amd_t *ucodefp = ufp->amd;
377*7605SMark.Johnson@Sun.COM 
378*7605SMark.Johnson@Sun.COM 	/* get equivalent CPU id */
379*7605SMark.Johnson@Sun.COM 	if ((rc = ucode_equiv_cpu_amd(cp, &eq_sig)) != EM_OK)
380*7605SMark.Johnson@Sun.COM 		return (rc);
381*7605SMark.Johnson@Sun.COM 
382*7605SMark.Johnson@Sun.COM 	/*
383*7605SMark.Johnson@Sun.COM 	 * Allocate a buffer for the microcode patch. If the buffer has been
384*7605SMark.Johnson@Sun.COM 	 * allocated before, check for a matching microcode to avoid loading
385*7605SMark.Johnson@Sun.COM 	 * the file again.
386*7605SMark.Johnson@Sun.COM 	 */
387*7605SMark.Johnson@Sun.COM 	if (ucodefp == NULL)
388*7605SMark.Johnson@Sun.COM 		ucodefp = ucode_zalloc(cp->cpu_id, sizeof (*ucodefp));
389*7605SMark.Johnson@Sun.COM 	else if (ucode_match_amd(eq_sig, uinfop, ucodefp, sizeof (*ucodefp))
390*7605SMark.Johnson@Sun.COM 	    == EM_OK)
391*7605SMark.Johnson@Sun.COM 		return (EM_OK);
392*7605SMark.Johnson@Sun.COM 
393*7605SMark.Johnson@Sun.COM 	if (ucodefp == NULL)
394*7605SMark.Johnson@Sun.COM 		return (EM_NOMEM);
395*7605SMark.Johnson@Sun.COM 
396*7605SMark.Johnson@Sun.COM 	ufp->amd = ucodefp;
397*7605SMark.Johnson@Sun.COM 
398*7605SMark.Johnson@Sun.COM 	/*
399*7605SMark.Johnson@Sun.COM 	 * Find the patch for this CPU. The patch files are named XXXX-YY, where
400*7605SMark.Johnson@Sun.COM 	 * XXXX is the equivalent CPU id and YY is the running patch number.
401*7605SMark.Johnson@Sun.COM 	 * Patches specific to certain chipsets are guaranteed to have lower
402*7605SMark.Johnson@Sun.COM 	 * numbers than less specific patches, so we can just load the first
403*7605SMark.Johnson@Sun.COM 	 * patch that matches.
404*7605SMark.Johnson@Sun.COM 	 */
405*7605SMark.Johnson@Sun.COM 
406*7605SMark.Johnson@Sun.COM 	for (i = 0; i < 0xff; i++) {
407*7605SMark.Johnson@Sun.COM 		(void) snprintf(name, MAXPATHLEN, "/%s/%s/%04X-%02X",
408*7605SMark.Johnson@Sun.COM 		    UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), eq_sig, i);
409*7605SMark.Johnson@Sun.COM 		if ((fd = kobj_open(name)) == -1)
410*7605SMark.Johnson@Sun.COM 			return (EM_NOMATCH);
411*7605SMark.Johnson@Sun.COM 		count = kobj_read(fd, (char *)ucodefp, sizeof (*ucodefp), 0);
412*7605SMark.Johnson@Sun.COM 		(void) kobj_close(fd);
413*7605SMark.Johnson@Sun.COM 
414*7605SMark.Johnson@Sun.COM 		if (ucode_match_amd(eq_sig, uinfop, ucodefp, count) == EM_OK)
415*7605SMark.Johnson@Sun.COM 			return (EM_OK);
416*7605SMark.Johnson@Sun.COM 	}
417*7605SMark.Johnson@Sun.COM 	return (EM_NOMATCH);
418*7605SMark.Johnson@Sun.COM }
419*7605SMark.Johnson@Sun.COM 
420*7605SMark.Johnson@Sun.COM static ucode_errno_t
421*7605SMark.Johnson@Sun.COM ucode_locate_intel(cpu_t *cp, cpu_ucode_info_t *uinfop, ucode_file_t *ufp)
4224581Ssherrym {
4234581Ssherrym 	char		name[MAXPATHLEN];
4244581Ssherrym 	intptr_t	fd;
4254581Ssherrym 	int		count;
426*7605SMark.Johnson@Sun.COM 	int		header_size = UCODE_HEADER_SIZE_INTEL;
4274581Ssherrym 	int		cpi_sig = cpuid_getsig(cp);
4284581Ssherrym 	ucode_errno_t	rc = EM_OK;
429*7605SMark.Johnson@Sun.COM 	ucode_file_intel_t *ucodefp = &ufp->intel;
430*7605SMark.Johnson@Sun.COM 
431*7605SMark.Johnson@Sun.COM 	ASSERT(ucode);
4324581Ssherrym 
4334581Ssherrym 	/*
4344581Ssherrym 	 * If the microcode matches the CPU we are processing, use it.
4354581Ssherrym 	 */
436*7605SMark.Johnson@Sun.COM 	if (ucode_match_intel(cpi_sig, uinfop, ucodefp->uf_header,
4374581Ssherrym 	    ucodefp->uf_ext_table) == EM_OK && ucodefp->uf_body != NULL) {
4384581Ssherrym 		return (EM_OK);
4394581Ssherrym 	}
4404581Ssherrym 
4414581Ssherrym 	/*
4424581Ssherrym 	 * Look for microcode file with the right name.
4434581Ssherrym 	 */
4444581Ssherrym 	(void) snprintf(name, MAXPATHLEN, "/%s/%s/%08X-%02X",
4454581Ssherrym 	    UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), cpi_sig,
4464581Ssherrym 	    uinfop->cui_platid);
4474581Ssherrym 	if ((fd = kobj_open(name)) == -1) {
4484581Ssherrym 		return (EM_OPENFILE);
4494581Ssherrym 	}
4504581Ssherrym 
4514581Ssherrym 	/*
4524581Ssherrym 	 * We found a microcode file for the CPU we are processing,
4534581Ssherrym 	 * reset the microcode data structure and read in the new
4544581Ssherrym 	 * file.
4554581Ssherrym 	 */
456*7605SMark.Johnson@Sun.COM 	ucode->file_reset(ufp, cp->cpu_id);
4574581Ssherrym 
458*7605SMark.Johnson@Sun.COM 	ucodefp->uf_header = ucode_zalloc(cp->cpu_id, header_size);
459*7605SMark.Johnson@Sun.COM 	if (ucodefp->uf_header == NULL)
460*7605SMark.Johnson@Sun.COM 		return (EM_NOMEM);
461*7605SMark.Johnson@Sun.COM 
462*7605SMark.Johnson@Sun.COM 	count = kobj_read(fd, (char *)ucodefp->uf_header, header_size, 0);
4634581Ssherrym 
4644581Ssherrym 	switch (count) {
465*7605SMark.Johnson@Sun.COM 	case UCODE_HEADER_SIZE_INTEL: {
4664581Ssherrym 
467*7605SMark.Johnson@Sun.COM 		ucode_header_intel_t	*uhp = ucodefp->uf_header;
4684581Ssherrym 		uint32_t	offset = header_size;
4694581Ssherrym 		int		total_size, body_size, ext_size;
4704581Ssherrym 		uint32_t	sum = 0;
4714581Ssherrym 
4724581Ssherrym 		/*
4734581Ssherrym 		 * Make sure that the header contains valid fields.
4744581Ssherrym 		 */
475*7605SMark.Johnson@Sun.COM 		if ((rc = ucode_header_validate_intel(uhp)) == EM_OK) {
476*7605SMark.Johnson@Sun.COM 			total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
477*7605SMark.Johnson@Sun.COM 			body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size);
478*7605SMark.Johnson@Sun.COM 			ucodefp->uf_body = ucode_zalloc(cp->cpu_id, body_size);
479*7605SMark.Johnson@Sun.COM 			if (ucodefp->uf_body == NULL) {
480*7605SMark.Johnson@Sun.COM 				rc = EM_NOMEM;
481*7605SMark.Johnson@Sun.COM 				break;
4824581Ssherrym 			}
4834581Ssherrym 
4844581Ssherrym 			if (kobj_read(fd, (char *)ucodefp->uf_body,
4854581Ssherrym 			    body_size, offset) != body_size)
4864581Ssherrym 				rc = EM_FILESIZE;
4874581Ssherrym 		}
4884581Ssherrym 
4894581Ssherrym 		if (rc)
4904581Ssherrym 			break;
4914581Ssherrym 
492*7605SMark.Johnson@Sun.COM 		sum = ucode_checksum_intel(0, header_size,
493*7605SMark.Johnson@Sun.COM 		    (uint8_t *)ucodefp->uf_header);
494*7605SMark.Johnson@Sun.COM 		if (ucode_checksum_intel(sum, body_size, ucodefp->uf_body)) {
4954581Ssherrym 			rc = EM_CHECKSUM;
4964581Ssherrym 			break;
4974581Ssherrym 		}
4984581Ssherrym 
4994581Ssherrym 		/*
5004581Ssherrym 		 * Check to see if there is extended signature table.
5014581Ssherrym 		 */
5024581Ssherrym 		offset = body_size + header_size;
5034581Ssherrym 		ext_size = total_size - offset;
5044581Ssherrym 
5054581Ssherrym 		if (ext_size <= 0)
5064581Ssherrym 			break;
5074581Ssherrym 
508*7605SMark.Johnson@Sun.COM 		ucodefp->uf_ext_table = ucode_zalloc(cp->cpu_id, ext_size);
509*7605SMark.Johnson@Sun.COM 		if (ucodefp->uf_ext_table == NULL) {
510*7605SMark.Johnson@Sun.COM 			rc = EM_NOMEM;
511*7605SMark.Johnson@Sun.COM 			break;
5124581Ssherrym 		}
5134581Ssherrym 
5144581Ssherrym 		if (kobj_read(fd, (char *)ucodefp->uf_ext_table,
5154581Ssherrym 		    ext_size, offset) != ext_size) {
5164581Ssherrym 			rc = EM_FILESIZE;
517*7605SMark.Johnson@Sun.COM 		} else if (ucode_checksum_intel(0, ext_size,
5184581Ssherrym 		    (uint8_t *)(ucodefp->uf_ext_table))) {
5194581Ssherrym 			rc = EM_CHECKSUM;
5204581Ssherrym 		} else {
5214581Ssherrym 			int i;
5224581Ssherrym 
523*7605SMark.Johnson@Sun.COM 			ext_size -= UCODE_EXT_TABLE_SIZE_INTEL;
5244581Ssherrym 			for (i = 0; i < ucodefp->uf_ext_table->uet_count;
5254581Ssherrym 			    i++) {
526*7605SMark.Johnson@Sun.COM 				if (ucode_checksum_intel(0,
527*7605SMark.Johnson@Sun.COM 				    UCODE_EXT_SIG_SIZE_INTEL,
5284581Ssherrym 				    (uint8_t *)(&(ucodefp->uf_ext_table->
5294581Ssherrym 				    uet_ext_sig[i])))) {
5304581Ssherrym 					rc = EM_CHECKSUM;
5314581Ssherrym 					break;
5324581Ssherrym 				}
5334581Ssherrym 			}
5344581Ssherrym 		}
5354581Ssherrym 		break;
5364581Ssherrym 	}
5374581Ssherrym 
5384581Ssherrym 	default:
5394581Ssherrym 		rc = EM_FILESIZE;
5404581Ssherrym 		break;
5414581Ssherrym 	}
5424581Ssherrym 
5434581Ssherrym 	kobj_close(fd);
5444581Ssherrym 
5454581Ssherrym 	if (rc != EM_OK)
5464581Ssherrym 		return (rc);
5474581Ssherrym 
548*7605SMark.Johnson@Sun.COM 	rc = ucode_match_intel(cpi_sig, uinfop, ucodefp->uf_header,
5494581Ssherrym 	    ucodefp->uf_ext_table);
5504581Ssherrym 
5514581Ssherrym 	return (rc);
5524581Ssherrym }
5534581Ssherrym 
554*7605SMark.Johnson@Sun.COM static ucode_errno_t
555*7605SMark.Johnson@Sun.COM ucode_match_amd(int eq_sig, cpu_ucode_info_t *uinfop, ucode_file_amd_t *ucodefp,
556*7605SMark.Johnson@Sun.COM     int size)
557*7605SMark.Johnson@Sun.COM {
558*7605SMark.Johnson@Sun.COM 	ucode_header_amd_t *uh;
559*7605SMark.Johnson@Sun.COM 
560*7605SMark.Johnson@Sun.COM 	if (ucodefp == NULL || size < sizeof (ucode_header_amd_t))
561*7605SMark.Johnson@Sun.COM 		return (EM_NOMATCH);
562*7605SMark.Johnson@Sun.COM 
563*7605SMark.Johnson@Sun.COM 	/*
564*7605SMark.Johnson@Sun.COM 	 * Don't even think about loading patches that would require code
565*7605SMark.Johnson@Sun.COM 	 * execution.
566*7605SMark.Johnson@Sun.COM 	 */
567*7605SMark.Johnson@Sun.COM 	if (size > offsetof(ucode_file_amd_t, uf_code_present) &&
568*7605SMark.Johnson@Sun.COM 	    ucodefp->uf_code_present)
569*7605SMark.Johnson@Sun.COM 		return (EM_NOMATCH);
570*7605SMark.Johnson@Sun.COM 
571*7605SMark.Johnson@Sun.COM 	uh = &ucodefp->uf_header;
572*7605SMark.Johnson@Sun.COM 
573*7605SMark.Johnson@Sun.COM 	if (eq_sig != uh->uh_cpu_rev)
574*7605SMark.Johnson@Sun.COM 		return (EM_NOMATCH);
575*7605SMark.Johnson@Sun.COM 
576*7605SMark.Johnson@Sun.COM 	if (uh->uh_nb_id) {
577*7605SMark.Johnson@Sun.COM 		cmn_err(CE_WARN, "ignoring northbridge-specific ucode: "
578*7605SMark.Johnson@Sun.COM 		    "chipset id %x, revision %x", uh->uh_nb_id, uh->uh_nb_rev);
579*7605SMark.Johnson@Sun.COM 		return (EM_NOMATCH);
580*7605SMark.Johnson@Sun.COM 	}
581*7605SMark.Johnson@Sun.COM 
582*7605SMark.Johnson@Sun.COM 	if (uh->uh_sb_id) {
583*7605SMark.Johnson@Sun.COM 		cmn_err(CE_WARN, "ignoring southbridge-specific ucode: "
584*7605SMark.Johnson@Sun.COM 		    "chipset id %x, revision %x", uh->uh_sb_id, uh->uh_sb_rev);
585*7605SMark.Johnson@Sun.COM 		return (EM_NOMATCH);
586*7605SMark.Johnson@Sun.COM 	}
587*7605SMark.Johnson@Sun.COM 
588*7605SMark.Johnson@Sun.COM 	if (uh->uh_patch_id <= uinfop->cui_rev)
589*7605SMark.Johnson@Sun.COM 		return (EM_HIGHERREV);
590*7605SMark.Johnson@Sun.COM 
591*7605SMark.Johnson@Sun.COM 	return (EM_OK);
592*7605SMark.Johnson@Sun.COM }
5934581Ssherrym 
5944581Ssherrym /*
5954581Ssherrym  * Returns 1 if the microcode is for this processor; 0 otherwise.
5964581Ssherrym  */
5974581Ssherrym static ucode_errno_t
598*7605SMark.Johnson@Sun.COM ucode_match_intel(int cpi_sig, cpu_ucode_info_t *uinfop,
599*7605SMark.Johnson@Sun.COM     ucode_header_intel_t *uhp, ucode_ext_table_intel_t *uetp)
6004581Ssherrym {
601*7605SMark.Johnson@Sun.COM 	if (uhp == NULL)
602*7605SMark.Johnson@Sun.COM 		return (EM_NOMATCH);
6034581Ssherrym 
604*7605SMark.Johnson@Sun.COM 	if (UCODE_MATCH_INTEL(cpi_sig, uhp->uh_signature,
6054581Ssherrym 	    uinfop->cui_platid, uhp->uh_proc_flags)) {
6064581Ssherrym 
6074581Ssherrym 		if (uinfop->cui_rev >= uhp->uh_rev && !ucode_force_update)
6084581Ssherrym 			return (EM_HIGHERREV);
6094581Ssherrym 
6104581Ssherrym 		return (EM_OK);
6114581Ssherrym 	}
6124581Ssherrym 
6134581Ssherrym 	if (uetp != NULL) {
6144581Ssherrym 		int i;
6154581Ssherrym 
6164581Ssherrym 		for (i = 0; i < uetp->uet_count; i++) {
617*7605SMark.Johnson@Sun.COM 			ucode_ext_sig_intel_t *uesp;
6184581Ssherrym 
6194581Ssherrym 			uesp = &uetp->uet_ext_sig[i];
6204581Ssherrym 
621*7605SMark.Johnson@Sun.COM 			if (UCODE_MATCH_INTEL(cpi_sig, uesp->ues_signature,
6224581Ssherrym 			    uinfop->cui_platid, uesp->ues_proc_flags)) {
6234581Ssherrym 
6244581Ssherrym 				if (uinfop->cui_rev >= uhp->uh_rev &&
6254581Ssherrym 				    !ucode_force_update)
6264581Ssherrym 					return (EM_HIGHERREV);
6274581Ssherrym 
6284581Ssherrym 				return (EM_OK);
6294581Ssherrym 			}
6304581Ssherrym 		}
6314581Ssherrym 	}
6324581Ssherrym 
6334581Ssherrym 	return (EM_NOMATCH);
6344581Ssherrym }
6354581Ssherrym 
6364581Ssherrym /*ARGSUSED*/
6374581Ssherrym static int
6384581Ssherrym ucode_write(xc_arg_t arg1, xc_arg_t unused2, xc_arg_t unused3)
6394581Ssherrym {
640*7605SMark.Johnson@Sun.COM 	ucode_update_t *uusp = (ucode_update_t *)arg1;
641*7605SMark.Johnson@Sun.COM 	cpu_ucode_info_t *uinfop = CPU->cpu_m.mcpu_ucode_info;
6424581Ssherrym 
643*7605SMark.Johnson@Sun.COM 	ASSERT(ucode);
6444581Ssherrym 	ASSERT(uusp->ucodep);
6454581Ssherrym 
6467347SMark.Johnson@Sun.COM #ifndef	__xpv
6474581Ssherrym 	/*
6484581Ssherrym 	 * Check one more time to see if it is really necessary to update
6494581Ssherrym 	 * microcode just in case this is a hyperthreaded processor where
6504581Ssherrym 	 * the threads share the same microcode.
6514581Ssherrym 	 */
6524581Ssherrym 	if (!ucode_force_update) {
653*7605SMark.Johnson@Sun.COM 		ucode->read_rev(uinfop);
6544581Ssherrym 		uusp->new_rev = uinfop->cui_rev;
6554581Ssherrym 		if (uinfop->cui_rev >= uusp->expected_rev)
6564581Ssherrym 			return (0);
6574581Ssherrym 	}
6584581Ssherrym 
659*7605SMark.Johnson@Sun.COM 	wrmsr(ucode->write_msr, (uintptr_t)uusp->ucodep);
6607347SMark.Johnson@Sun.COM #endif
661*7605SMark.Johnson@Sun.COM 	ucode->read_rev(uinfop);
6624581Ssherrym 	uusp->new_rev = uinfop->cui_rev;
6634581Ssherrym 
6644581Ssherrym 	return (0);
6654581Ssherrym }
6664581Ssherrym 
667*7605SMark.Johnson@Sun.COM /*ARGSUSED*/
668*7605SMark.Johnson@Sun.COM static uint32_t
669*7605SMark.Johnson@Sun.COM ucode_load_amd(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp)
670*7605SMark.Johnson@Sun.COM {
671*7605SMark.Johnson@Sun.COM 	ucode_file_amd_t *ucodefp = ufp->amd;
672*7605SMark.Johnson@Sun.COM #ifdef	__xpv
673*7605SMark.Johnson@Sun.COM 	ucode_update_t uus;
674*7605SMark.Johnson@Sun.COM #endif
6754581Ssherrym 
676*7605SMark.Johnson@Sun.COM 	ASSERT(ucode);
677*7605SMark.Johnson@Sun.COM 	ASSERT(ucodefp);
678*7605SMark.Johnson@Sun.COM 
679*7605SMark.Johnson@Sun.COM #ifndef	__xpv
680*7605SMark.Johnson@Sun.COM 	kpreempt_disable();
681*7605SMark.Johnson@Sun.COM 	wrmsr(ucode->write_msr, (uintptr_t)ucodefp);
682*7605SMark.Johnson@Sun.COM 	ucode->read_rev(uinfop);
683*7605SMark.Johnson@Sun.COM 	kpreempt_enable();
684*7605SMark.Johnson@Sun.COM #else
685*7605SMark.Johnson@Sun.COM 	uus.ucodep = (uint8_t *)ucodefp;
686*7605SMark.Johnson@Sun.COM 	uus.usize = sizeof (*ucodefp);
687*7605SMark.Johnson@Sun.COM 	ucode_load_xpv(&uus);
688*7605SMark.Johnson@Sun.COM 	ucode->read_rev(uinfop);
689*7605SMark.Johnson@Sun.COM 	uus.new_rev = uinfop->cui_rev;
690*7605SMark.Johnson@Sun.COM #endif
691*7605SMark.Johnson@Sun.COM 
692*7605SMark.Johnson@Sun.COM 	return (ucodefp->uf_header.uh_patch_id);
693*7605SMark.Johnson@Sun.COM }
694*7605SMark.Johnson@Sun.COM 
695*7605SMark.Johnson@Sun.COM /*ARGSUSED2*/
696*7605SMark.Johnson@Sun.COM static uint32_t
697*7605SMark.Johnson@Sun.COM ucode_load_intel(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp)
6984581Ssherrym {
699*7605SMark.Johnson@Sun.COM 	ucode_file_intel_t *ucodefp = &ufp->intel;
700*7605SMark.Johnson@Sun.COM #ifdef __xpv
701*7605SMark.Johnson@Sun.COM 	uint32_t ext_offset;
702*7605SMark.Johnson@Sun.COM 	uint32_t body_size;
703*7605SMark.Johnson@Sun.COM 	uint32_t ext_size;
704*7605SMark.Johnson@Sun.COM 	uint8_t *ustart;
705*7605SMark.Johnson@Sun.COM 	uint32_t usize;
706*7605SMark.Johnson@Sun.COM 	ucode_update_t uus;
707*7605SMark.Johnson@Sun.COM #endif
708*7605SMark.Johnson@Sun.COM 
709*7605SMark.Johnson@Sun.COM 	ASSERT(ucode);
710*7605SMark.Johnson@Sun.COM 
711*7605SMark.Johnson@Sun.COM #ifdef __xpv
712*7605SMark.Johnson@Sun.COM 	/*
713*7605SMark.Johnson@Sun.COM 	 * the hypervisor wants the header, data, and extended
714*7605SMark.Johnson@Sun.COM 	 * signature tables. We can only get here from the boot
715*7605SMark.Johnson@Sun.COM 	 * CPU (cpu #0), we don't need to free as ucode_zalloc() will
716*7605SMark.Johnson@Sun.COM 	 * use BOP_ALLOC().
717*7605SMark.Johnson@Sun.COM 	 */
718*7605SMark.Johnson@Sun.COM 	usize = UCODE_TOTAL_SIZE_INTEL(ucodefp->uf_header->uh_total_size);
719*7605SMark.Johnson@Sun.COM 	ustart = ucode_zalloc(cp->cpu_id, usize);
720*7605SMark.Johnson@Sun.COM 	ASSERT(ustart);
721*7605SMark.Johnson@Sun.COM 
722*7605SMark.Johnson@Sun.COM 	body_size = UCODE_BODY_SIZE_INTEL(ucodefp->uf_header->uh_body_size);
723*7605SMark.Johnson@Sun.COM 	ext_offset = body_size + UCODE_HEADER_SIZE_INTEL;
724*7605SMark.Johnson@Sun.COM 	ext_size = usize - ext_offset;
725*7605SMark.Johnson@Sun.COM 	ASSERT(ext_size >= 0);
726*7605SMark.Johnson@Sun.COM 
727*7605SMark.Johnson@Sun.COM 	(void) memcpy(ustart, ucodefp->uf_header, UCODE_HEADER_SIZE_INTEL);
728*7605SMark.Johnson@Sun.COM 	(void) memcpy(&ustart[UCODE_HEADER_SIZE_INTEL], ucodefp->uf_body,
729*7605SMark.Johnson@Sun.COM 	    body_size);
730*7605SMark.Johnson@Sun.COM 	if (ext_size > 0) {
731*7605SMark.Johnson@Sun.COM 		(void) memcpy(&ustart[ext_offset],
732*7605SMark.Johnson@Sun.COM 		    ucodefp->uf_ext_table, ext_size);
733*7605SMark.Johnson@Sun.COM 	}
734*7605SMark.Johnson@Sun.COM 	uus.ucodep = ustart;
735*7605SMark.Johnson@Sun.COM 	uus.usize = usize;
736*7605SMark.Johnson@Sun.COM 	ucode_load_xpv(&uus);
737*7605SMark.Johnson@Sun.COM 	ucode->read_rev(uinfop);
738*7605SMark.Johnson@Sun.COM 	uus.new_rev = uinfop->cui_rev;
739*7605SMark.Johnson@Sun.COM #else
7404581Ssherrym 	kpreempt_disable();
741*7605SMark.Johnson@Sun.COM 	wrmsr(ucode->write_msr, (uintptr_t)ucodefp->uf_body);
742*7605SMark.Johnson@Sun.COM 	ucode->read_rev(uinfop);
7434581Ssherrym 	kpreempt_enable();
744*7605SMark.Johnson@Sun.COM #endif
745*7605SMark.Johnson@Sun.COM 
746*7605SMark.Johnson@Sun.COM 	return (ucodefp->uf_header->uh_rev);
7474581Ssherrym }
7484581Ssherrym 
7497347SMark.Johnson@Sun.COM 
7507347SMark.Johnson@Sun.COM #ifdef	__xpv
7517347SMark.Johnson@Sun.COM static void
752*7605SMark.Johnson@Sun.COM ucode_load_xpv(ucode_update_t *uusp)
7537347SMark.Johnson@Sun.COM {
7547347SMark.Johnson@Sun.COM 	xen_platform_op_t op;
7557347SMark.Johnson@Sun.COM 	int e;
7567347SMark.Johnson@Sun.COM 
7577347SMark.Johnson@Sun.COM 	ASSERT(DOMAIN_IS_INITDOMAIN(xen_info));
7587347SMark.Johnson@Sun.COM 
7597347SMark.Johnson@Sun.COM 	kpreempt_disable();
7607347SMark.Johnson@Sun.COM 	op.cmd = XENPF_microcode_update;
7617347SMark.Johnson@Sun.COM 	op.interface_version = XENPF_INTERFACE_VERSION;
7627347SMark.Johnson@Sun.COM 	/*LINTED: constant in conditional context*/
763*7605SMark.Johnson@Sun.COM 	set_xen_guest_handle(op.u.microcode.data, uusp->ucodep);
764*7605SMark.Johnson@Sun.COM 	op.u.microcode.length = uusp->usize;
7657347SMark.Johnson@Sun.COM 	e = HYPERVISOR_platform_op(&op);
7667347SMark.Johnson@Sun.COM 	if (e != 0) {
7677347SMark.Johnson@Sun.COM 		cmn_err(CE_WARN, "hypervisor failed to accept uCode update");
7687347SMark.Johnson@Sun.COM 	}
7697347SMark.Johnson@Sun.COM 	kpreempt_enable();
7707347SMark.Johnson@Sun.COM }
7717347SMark.Johnson@Sun.COM #endif /* __xpv */
7727347SMark.Johnson@Sun.COM 
773*7605SMark.Johnson@Sun.COM static void
774*7605SMark.Johnson@Sun.COM ucode_read_rev_amd(cpu_ucode_info_t *uinfop)
775*7605SMark.Johnson@Sun.COM {
776*7605SMark.Johnson@Sun.COM 	uinfop->cui_rev = rdmsr(MSR_AMD_PATCHLEVEL);
777*7605SMark.Johnson@Sun.COM }
7787347SMark.Johnson@Sun.COM 
7794581Ssherrym static void
780*7605SMark.Johnson@Sun.COM ucode_read_rev_intel(cpu_ucode_info_t *uinfop)
7814581Ssherrym {
7824581Ssherrym 	struct cpuid_regs crs;
7834581Ssherrym 
7844581Ssherrym 	/*
7854581Ssherrym 	 * The Intel 64 and IA-32 Architecture Software Developer's Manual
7864581Ssherrym 	 * recommends that MSR_INTC_UCODE_REV be loaded with 0 first, then
7874581Ssherrym 	 * execute cpuid to guarantee the correct reading of this register.
7884581Ssherrym 	 */
7894581Ssherrym 	wrmsr(MSR_INTC_UCODE_REV, 0);
7904581Ssherrym 	(void) __cpuid_insn(&crs);
7914581Ssherrym 	uinfop->cui_rev = (rdmsr(MSR_INTC_UCODE_REV) >> INTC_UCODE_REV_SHIFT);
7924581Ssherrym }
7934581Ssherrym 
794*7605SMark.Johnson@Sun.COM static ucode_errno_t
795*7605SMark.Johnson@Sun.COM ucode_extract_amd(ucode_update_t *uusp, uint8_t *ucodep, int size)
796*7605SMark.Johnson@Sun.COM {
797*7605SMark.Johnson@Sun.COM 	uint32_t *ptr = (uint32_t *)ucodep;
798*7605SMark.Johnson@Sun.COM 	ucode_eqtbl_amd_t *eqtbl;
799*7605SMark.Johnson@Sun.COM 	ucode_file_amd_t *ufp;
800*7605SMark.Johnson@Sun.COM 	int count, eq_sig;
801*7605SMark.Johnson@Sun.COM 
802*7605SMark.Johnson@Sun.COM 	/* skip over magic number & equivalence table header */
803*7605SMark.Johnson@Sun.COM 	ptr += 2; size -= 8;
804*7605SMark.Johnson@Sun.COM 
805*7605SMark.Johnson@Sun.COM 	count = *ptr++; size -= 4;
806*7605SMark.Johnson@Sun.COM 	for (eqtbl = (ucode_eqtbl_amd_t *)ptr;
807*7605SMark.Johnson@Sun.COM 	    eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != uusp->sig;
808*7605SMark.Johnson@Sun.COM 	    eqtbl++)
809*7605SMark.Johnson@Sun.COM 		;
810*7605SMark.Johnson@Sun.COM 
811*7605SMark.Johnson@Sun.COM 	eq_sig = eqtbl->ue_equiv_cpu;
812*7605SMark.Johnson@Sun.COM 	eq_sig = ((eq_sig >> 8) & 0xff00) | (eq_sig & 0xff);
813*7605SMark.Johnson@Sun.COM 
814*7605SMark.Johnson@Sun.COM 	/* No equivalent CPU id found, assume outdated microcode file. */
815*7605SMark.Johnson@Sun.COM 	if (eq_sig == 0)
816*7605SMark.Johnson@Sun.COM 		return (EM_HIGHERREV);
817*7605SMark.Johnson@Sun.COM 
818*7605SMark.Johnson@Sun.COM 	/* Use the first microcode patch that matches. */
819*7605SMark.Johnson@Sun.COM 	do {
820*7605SMark.Johnson@Sun.COM 		ptr += count >> 2; size -= count;
821*7605SMark.Johnson@Sun.COM 
822*7605SMark.Johnson@Sun.COM 		if (!size)
823*7605SMark.Johnson@Sun.COM 			return (EM_NOMATCH);
824*7605SMark.Johnson@Sun.COM 
825*7605SMark.Johnson@Sun.COM 		ptr++; size -= 4;
826*7605SMark.Johnson@Sun.COM 		count = *ptr++; size -= 4;
827*7605SMark.Johnson@Sun.COM 		ufp = (ucode_file_amd_t *)ptr;
828*7605SMark.Johnson@Sun.COM 	} while (ucode_match_amd(eq_sig, &uusp->info, ufp, count) != EM_OK);
829*7605SMark.Johnson@Sun.COM 
830*7605SMark.Johnson@Sun.COM 	uusp->ucodep = (uint8_t *)ufp;
831*7605SMark.Johnson@Sun.COM 	uusp->usize = count;
832*7605SMark.Johnson@Sun.COM 	uusp->expected_rev = ufp->uf_header.uh_patch_id;
833*7605SMark.Johnson@Sun.COM 
834*7605SMark.Johnson@Sun.COM 	return (EM_OK);
835*7605SMark.Johnson@Sun.COM }
836*7605SMark.Johnson@Sun.COM 
837*7605SMark.Johnson@Sun.COM static ucode_errno_t
838*7605SMark.Johnson@Sun.COM ucode_extract_intel(ucode_update_t *uusp, uint8_t *ucodep, int size)
839*7605SMark.Johnson@Sun.COM {
840*7605SMark.Johnson@Sun.COM 	uint32_t	header_size = UCODE_HEADER_SIZE_INTEL;
841*7605SMark.Johnson@Sun.COM 	int		remaining;
842*7605SMark.Johnson@Sun.COM 	int		found = 0;
843*7605SMark.Johnson@Sun.COM 	ucode_errno_t	search_rc = EM_NOMATCH; /* search result */
844*7605SMark.Johnson@Sun.COM 
845*7605SMark.Johnson@Sun.COM 	/*
846*7605SMark.Johnson@Sun.COM 	 * Go through the whole buffer in case there are
847*7605SMark.Johnson@Sun.COM 	 * multiple versions of matching microcode for this
848*7605SMark.Johnson@Sun.COM 	 * processor.
849*7605SMark.Johnson@Sun.COM 	 */
850*7605SMark.Johnson@Sun.COM 	for (remaining = size; remaining > 0; ) {
851*7605SMark.Johnson@Sun.COM 		int	total_size, body_size, ext_size;
852*7605SMark.Johnson@Sun.COM 		uint8_t	*curbuf = &ucodep[size - remaining];
853*7605SMark.Johnson@Sun.COM 		ucode_header_intel_t *uhp = (ucode_header_intel_t *)curbuf;
854*7605SMark.Johnson@Sun.COM 		ucode_ext_table_intel_t *uetp = NULL;
855*7605SMark.Johnson@Sun.COM 		ucode_errno_t tmprc;
856*7605SMark.Johnson@Sun.COM 
857*7605SMark.Johnson@Sun.COM 		total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
858*7605SMark.Johnson@Sun.COM 		body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size);
859*7605SMark.Johnson@Sun.COM 		ext_size = total_size - (header_size + body_size);
860*7605SMark.Johnson@Sun.COM 
861*7605SMark.Johnson@Sun.COM 		if (ext_size > 0)
862*7605SMark.Johnson@Sun.COM 			uetp = (ucode_ext_table_intel_t *)
863*7605SMark.Johnson@Sun.COM 			    &curbuf[header_size + body_size];
864*7605SMark.Johnson@Sun.COM 
865*7605SMark.Johnson@Sun.COM 		tmprc = ucode_match_intel(uusp->sig, &uusp->info, uhp, uetp);
866*7605SMark.Johnson@Sun.COM 
867*7605SMark.Johnson@Sun.COM 		/*
868*7605SMark.Johnson@Sun.COM 		 * Since we are searching through a big file
869*7605SMark.Johnson@Sun.COM 		 * containing microcode for pretty much all the
870*7605SMark.Johnson@Sun.COM 		 * processors, we are bound to get EM_NOMATCH
871*7605SMark.Johnson@Sun.COM 		 * at one point.  However, if we return
872*7605SMark.Johnson@Sun.COM 		 * EM_NOMATCH to users, it will really confuse
873*7605SMark.Johnson@Sun.COM 		 * them.  Therefore, if we ever find a match of
874*7605SMark.Johnson@Sun.COM 		 * a lower rev, we will set return code to
875*7605SMark.Johnson@Sun.COM 		 * EM_HIGHERREV.
876*7605SMark.Johnson@Sun.COM 		 */
877*7605SMark.Johnson@Sun.COM 		if (tmprc == EM_HIGHERREV)
878*7605SMark.Johnson@Sun.COM 			search_rc = EM_HIGHERREV;
879*7605SMark.Johnson@Sun.COM 
880*7605SMark.Johnson@Sun.COM 		if (tmprc == EM_OK &&
881*7605SMark.Johnson@Sun.COM 		    uusp->expected_rev < uhp->uh_rev) {
882*7605SMark.Johnson@Sun.COM #ifndef __xpv
883*7605SMark.Johnson@Sun.COM 			uusp->ucodep = (uint8_t *)&curbuf[header_size];
884*7605SMark.Johnson@Sun.COM #else
885*7605SMark.Johnson@Sun.COM 			uusp->ucodep = (uint8_t *)curbuf;
886*7605SMark.Johnson@Sun.COM #endif
887*7605SMark.Johnson@Sun.COM 			uusp->usize =
888*7605SMark.Johnson@Sun.COM 			    UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
889*7605SMark.Johnson@Sun.COM 			uusp->expected_rev = uhp->uh_rev;
890*7605SMark.Johnson@Sun.COM 			found = 1;
891*7605SMark.Johnson@Sun.COM 		}
892*7605SMark.Johnson@Sun.COM 
893*7605SMark.Johnson@Sun.COM 		remaining -= total_size;
894*7605SMark.Johnson@Sun.COM 	}
895*7605SMark.Johnson@Sun.COM 
896*7605SMark.Johnson@Sun.COM 	if (!found)
897*7605SMark.Johnson@Sun.COM 		return (search_rc);
898*7605SMark.Johnson@Sun.COM 
899*7605SMark.Johnson@Sun.COM 	return (EM_OK);
900*7605SMark.Johnson@Sun.COM }
9014581Ssherrym /*
9024581Ssherrym  * Entry point to microcode update from the ucode_drv driver.
9034581Ssherrym  *
9044581Ssherrym  * Returns EM_OK on success, corresponding error code on failure.
9054581Ssherrym  */
9064581Ssherrym ucode_errno_t
9074581Ssherrym ucode_update(uint8_t *ucodep, int size)
9084581Ssherrym {
9094581Ssherrym 	int		found = 0;
9104581Ssherrym 	processorid_t	id;
911*7605SMark.Johnson@Sun.COM 	ucode_update_t	cached = { 0 };
912*7605SMark.Johnson@Sun.COM 	ucode_update_t	*cachedp = NULL;
9134581Ssherrym 	ucode_errno_t	rc = EM_OK;
9144581Ssherrym 	ucode_errno_t	search_rc = EM_NOMATCH; /* search result */
9154581Ssherrym 	cpuset_t cpuset;
9164581Ssherrym 
917*7605SMark.Johnson@Sun.COM 	ASSERT(ucode);
9184581Ssherrym 	ASSERT(ucodep);
9194581Ssherrym 	CPUSET_ZERO(cpuset);
9204581Ssherrym 
921*7605SMark.Johnson@Sun.COM 	if (!ucode->capable(CPU))
9224581Ssherrym 		return (EM_NOTSUP);
9234581Ssherrym 
9244581Ssherrym 	mutex_enter(&cpu_lock);
9254581Ssherrym 
9264581Ssherrym 	for (id = 0; id < max_ncpus; id++) {
9274581Ssherrym 		cpu_t *cpu;
928*7605SMark.Johnson@Sun.COM 		ucode_update_t uus = { 0 };
929*7605SMark.Johnson@Sun.COM 		ucode_update_t *uusp = &uus;
9304581Ssherrym 
9314581Ssherrym 		/*
9324581Ssherrym 		 * If there is no such CPU or it is not xcall ready, skip it.
9334581Ssherrym 		 */
9344581Ssherrym 		if ((cpu = cpu_get(id)) == NULL ||
9354581Ssherrym 		    !(cpu->cpu_flags & CPU_READY))
9364581Ssherrym 			continue;
9374581Ssherrym 
9384581Ssherrym 		uusp->sig = cpuid_getsig(cpu);
9394581Ssherrym 		bcopy(cpu->cpu_m.mcpu_ucode_info, &uusp->info,
9404581Ssherrym 		    sizeof (uusp->info));
9414581Ssherrym 
9424581Ssherrym 		/*
9434581Ssherrym 		 * If the current CPU has the same signature and platform
9444581Ssherrym 		 * id as the previous one we processed, reuse the information.
9454581Ssherrym 		 */
9464581Ssherrym 		if (cachedp && cachedp->sig == cpuid_getsig(cpu) &&
9474581Ssherrym 		    cachedp->info.cui_platid == uusp->info.cui_platid) {
9484581Ssherrym 			uusp->ucodep = cachedp->ucodep;
9494581Ssherrym 			uusp->expected_rev = cachedp->expected_rev;
9504581Ssherrym 			/*
9514581Ssherrym 			 * Intuitively we should check here to see whether the
9524581Ssherrym 			 * running microcode rev is >= the expected rev, and
9534581Ssherrym 			 * quit if it is.  But we choose to proceed with the
9544581Ssherrym 			 * xcall regardless of the running version so that
9554581Ssherrym 			 * the other threads in an HT processor can update
9564581Ssherrym 			 * the cpu_ucode_info structure in machcpu.
9574581Ssherrym 			 */
958*7605SMark.Johnson@Sun.COM 		} else if ((search_rc = ucode->extract(uusp, ucodep, size))
959*7605SMark.Johnson@Sun.COM 		    == EM_OK) {
960*7605SMark.Johnson@Sun.COM 			bcopy(uusp, &cached, sizeof (cached));
961*7605SMark.Johnson@Sun.COM 			cachedp = &cached;
962*7605SMark.Johnson@Sun.COM 			found = 1;
9634581Ssherrym 		}
9644581Ssherrym 
9654581Ssherrym 		/* Nothing to do */
9664581Ssherrym 		if (uusp->ucodep == NULL)
9674581Ssherrym 			continue;
9684581Ssherrym 
9697347SMark.Johnson@Sun.COM #ifdef	__xpv
9707347SMark.Johnson@Sun.COM 		/*
9717347SMark.Johnson@Sun.COM 		 * for i86xpv, the hypervisor will update all the CPUs.
9727347SMark.Johnson@Sun.COM 		 * the hypervisor wants the header, data, and extended
9737347SMark.Johnson@Sun.COM 		 * signature tables. ucode_write will just read in the
9747347SMark.Johnson@Sun.COM 		 * updated version on all the CPUs after the update has
9757347SMark.Johnson@Sun.COM 		 * completed.
9767347SMark.Johnson@Sun.COM 		 */
9777402SMark.Johnson@Sun.COM 		if (id == 0) {
978*7605SMark.Johnson@Sun.COM 			ucode_load_xpv(uusp);
9797402SMark.Johnson@Sun.COM 		}
9807347SMark.Johnson@Sun.COM #endif
9817347SMark.Johnson@Sun.COM 
9824581Ssherrym 		CPUSET_ADD(cpuset, id);
9834581Ssherrym 		kpreempt_disable();
9844581Ssherrym 		xc_sync((xc_arg_t)uusp, 0, 0, X_CALL_HIPRI, cpuset,
9854581Ssherrym 		    ucode_write);
9864581Ssherrym 		kpreempt_enable();
9874581Ssherrym 		CPUSET_DEL(cpuset, id);
9884581Ssherrym 
9894581Ssherrym 		if (uusp->expected_rev == uusp->new_rev) {
9904581Ssherrym 			cmn_err(CE_CONT, ucode_success_fmt,
9914581Ssherrym 			    id, uusp->info.cui_rev, uusp->expected_rev);
9924581Ssherrym 		} else {
9934581Ssherrym 			cmn_err(CE_WARN, ucode_failure_fmt,
9944581Ssherrym 			    id, uusp->info.cui_rev, uusp->expected_rev);
9954581Ssherrym 			rc = EM_UPDATE;
9964581Ssherrym 		}
9974581Ssherrym 	}
9984581Ssherrym 
9994581Ssherrym 	mutex_exit(&cpu_lock);
10004581Ssherrym 
10014581Ssherrym 	if (!found)
10024581Ssherrym 		rc = search_rc;
10034581Ssherrym 
10044581Ssherrym 	return (rc);
10054581Ssherrym }
10064581Ssherrym 
10074581Ssherrym /*
10084581Ssherrym  * Initialize mcpu_ucode_info, and perform microcode update if necessary.
10094581Ssherrym  * This is the entry point from boot path where pointer to CPU structure
10104581Ssherrym  * is available.
10114581Ssherrym  *
10124581Ssherrym  * cpuid_info must be initialized before ucode_check can be called.
10134581Ssherrym  */
10144581Ssherrym void
10154581Ssherrym ucode_check(cpu_t *cp)
10164581Ssherrym {
1017*7605SMark.Johnson@Sun.COM 	cpu_ucode_info_t *uinfop;
10184581Ssherrym 	ucode_errno_t rc = EM_OK;
1019*7605SMark.Johnson@Sun.COM 	uint32_t new_rev = 0;
10204581Ssherrym 
10214581Ssherrym 	ASSERT(cp);
10224581Ssherrym 	if (cp->cpu_id == 0)
10234581Ssherrym 		cp->cpu_m.mcpu_ucode_info = &cpu_ucode_info0;
10244581Ssherrym 
10254581Ssherrym 	uinfop = cp->cpu_m.mcpu_ucode_info;
10264581Ssherrym 	ASSERT(uinfop);
10274581Ssherrym 
1028*7605SMark.Johnson@Sun.COM 	/* set up function pointers if not already done */
1029*7605SMark.Johnson@Sun.COM 	if (!ucode)
1030*7605SMark.Johnson@Sun.COM 		switch (cpuid_getvendor(cp)) {
1031*7605SMark.Johnson@Sun.COM 		case X86_VENDOR_AMD:
1032*7605SMark.Johnson@Sun.COM 			ucode = &ucode_amd;
1033*7605SMark.Johnson@Sun.COM 			break;
1034*7605SMark.Johnson@Sun.COM 		case X86_VENDOR_Intel:
1035*7605SMark.Johnson@Sun.COM 			ucode = &ucode_intel;
1036*7605SMark.Johnson@Sun.COM 			break;
1037*7605SMark.Johnson@Sun.COM 		default:
1038*7605SMark.Johnson@Sun.COM 			return;
1039*7605SMark.Johnson@Sun.COM 		}
1040*7605SMark.Johnson@Sun.COM 
1041*7605SMark.Johnson@Sun.COM 	if (!ucode->capable(cp))
10424581Ssherrym 		return;
10434581Ssherrym 
10444581Ssherrym 	/*
10454581Ssherrym 	 * The MSR_INTC_PLATFORM_ID is supported in Celeron and Xeon
10464581Ssherrym 	 * (Family 6, model 5 and above) and all processors after.
10474581Ssherrym 	 */
1048*7605SMark.Johnson@Sun.COM 	if ((cpuid_getvendor(cp) == X86_VENDOR_Intel) &&
1049*7605SMark.Johnson@Sun.COM 	    ((cpuid_getmodel(cp) >= 5) || (cpuid_getfamily(cp) > 6))) {
10504581Ssherrym 		uinfop->cui_platid = 1 << ((rdmsr(MSR_INTC_PLATFORM_ID) >>
10514581Ssherrym 		    INTC_PLATFORM_ID_SHIFT) & INTC_PLATFORM_ID_MASK);
10524581Ssherrym 	}
10534581Ssherrym 
1054*7605SMark.Johnson@Sun.COM 	ucode->read_rev(uinfop);
10554581Ssherrym 
10567402SMark.Johnson@Sun.COM #ifdef	__xpv
10577402SMark.Johnson@Sun.COM 	/*
10587402SMark.Johnson@Sun.COM 	 * for i86xpv, the hypervisor will update all the CPUs. We only need
10597402SMark.Johnson@Sun.COM 	 * do do this on one of the CPUs (and there always is a CPU 0).
10607402SMark.Johnson@Sun.COM 	 */
10617402SMark.Johnson@Sun.COM 	if (cp->cpu_id != 0) {
10627402SMark.Johnson@Sun.COM 		return;
10637402SMark.Johnson@Sun.COM 	}
10647402SMark.Johnson@Sun.COM #endif
10657402SMark.Johnson@Sun.COM 
10664581Ssherrym 	/*
10674581Ssherrym 	 * Check to see if we need ucode update
10684581Ssherrym 	 */
1069*7605SMark.Johnson@Sun.COM 	if ((rc = ucode->locate(cp, uinfop, &ucodefile)) == EM_OK) {
1070*7605SMark.Johnson@Sun.COM 		new_rev = ucode->load(&ucodefile, uinfop, cp);
10717347SMark.Johnson@Sun.COM 
1072*7605SMark.Johnson@Sun.COM 		if (uinfop->cui_rev != new_rev)
10734581Ssherrym 			cmn_err(CE_WARN, ucode_failure_fmt, cp->cpu_id,
1074*7605SMark.Johnson@Sun.COM 			    uinfop->cui_rev, new_rev);
10754581Ssherrym 	}
10764581Ssherrym 
10774581Ssherrym 	/*
10784581Ssherrym 	 * If we fail to find a match for any reason, free the file structure
10794581Ssherrym 	 * just in case we have read in a partial file.
10804581Ssherrym 	 *
10814581Ssherrym 	 * Since the scratch memory for holding the microcode for the boot CPU
10824581Ssherrym 	 * came from BOP_ALLOC, we will reset the data structure as if we
10834581Ssherrym 	 * never did the allocation so we don't have to keep track of this
10844581Ssherrym 	 * special chunk of memory.  We free the memory used for the rest
10854581Ssherrym 	 * of the CPUs in start_other_cpus().
10864581Ssherrym 	 */
10874581Ssherrym 	if (rc != EM_OK || cp->cpu_id == 0)
1088*7605SMark.Johnson@Sun.COM 		ucode->file_reset(&ucodefile, cp->cpu_id);
10894581Ssherrym }
10904581Ssherrym 
10914581Ssherrym /*
10924581Ssherrym  * Returns microcode revision from the machcpu structure.
10934581Ssherrym  */
10944581Ssherrym ucode_errno_t
10954581Ssherrym ucode_get_rev(uint32_t *revp)
10964581Ssherrym {
10974581Ssherrym 	int i;
10984581Ssherrym 
1099*7605SMark.Johnson@Sun.COM 	ASSERT(ucode);
11004581Ssherrym 	ASSERT(revp);
11014581Ssherrym 
1102*7605SMark.Johnson@Sun.COM 	if (!ucode->capable(CPU))
11034581Ssherrym 		return (EM_NOTSUP);
11044581Ssherrym 
11054581Ssherrym 	mutex_enter(&cpu_lock);
11064581Ssherrym 	for (i = 0; i < max_ncpus; i++) {
11074581Ssherrym 		cpu_t *cpu;
11084581Ssherrym 
11094581Ssherrym 		if ((cpu = cpu_get(i)) == NULL)
11104581Ssherrym 			continue;
11114581Ssherrym 
11124581Ssherrym 		revp[i] = cpu->cpu_m.mcpu_ucode_info->cui_rev;
11134581Ssherrym 	}
11144581Ssherrym 	mutex_exit(&cpu_lock);
11154581Ssherrym 
11164581Ssherrym 	return (EM_OK);
11174581Ssherrym }
1118