xref: /titanic_53/usr/src/uts/i86pc/os/microcode.c (revision adc586debf12d2592024c0b8b9e44ffa104f858c)
12449e17fSsherrym /*
22449e17fSsherrym  * CDDL HEADER START
32449e17fSsherrym  *
42449e17fSsherrym  * The contents of this file are subject to the terms of the
52449e17fSsherrym  * Common Development and Distribution License (the "License").
62449e17fSsherrym  * You may not use this file except in compliance with the License.
72449e17fSsherrym  *
82449e17fSsherrym  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92449e17fSsherrym  * or http://www.opensolaris.org/os/licensing.
102449e17fSsherrym  * See the License for the specific language governing permissions
112449e17fSsherrym  * and limitations under the License.
122449e17fSsherrym  *
132449e17fSsherrym  * When distributing Covered Code, include this CDDL HEADER in each
142449e17fSsherrym  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152449e17fSsherrym  * If applicable, add the following below this CDDL HEADER, with the
162449e17fSsherrym  * fields enclosed by brackets "[]" replaced with your own identifying
172449e17fSsherrym  * information: Portions Copyright [yyyy] [name of copyright owner]
182449e17fSsherrym  *
192449e17fSsherrym  * CDDL HEADER END
202449e17fSsherrym  */
212449e17fSsherrym 
222449e17fSsherrym /*
2388699bddSsherrym  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
242449e17fSsherrym  * Use is subject to license terms.
252449e17fSsherrym  */
262449e17fSsherrym 
272449e17fSsherrym #include <sys/asm_linkage.h>
282449e17fSsherrym #include <sys/bootconf.h>
292449e17fSsherrym #include <sys/cpuvar.h>
302449e17fSsherrym #include <sys/cmn_err.h>
312449e17fSsherrym #include <sys/controlregs.h>
322449e17fSsherrym #include <sys/debug.h>
332449e17fSsherrym #include <sys/kobj.h>
342449e17fSsherrym #include <sys/kobj_impl.h>
352449e17fSsherrym #include <sys/machsystm.h>
362449e17fSsherrym #include <sys/param.h>
372449e17fSsherrym #include <sys/machparam.h>
382449e17fSsherrym #include <sys/promif.h>
392449e17fSsherrym #include <sys/sysmacros.h>
402449e17fSsherrym #include <sys/systm.h>
412449e17fSsherrym #include <sys/types.h>
422449e17fSsherrym #include <sys/thread.h>
432449e17fSsherrym #include <sys/ucode.h>
442449e17fSsherrym #include <sys/x86_archext.h>
452449e17fSsherrym #include <sys/x_call.h>
46843e1988Sjohnlev #ifdef	__xpv
47843e1988Sjohnlev #include <sys/hypervisor.h>
48843e1988Sjohnlev #endif
492449e17fSsherrym 
502449e17fSsherrym /*
51*adc586deSMark Johnson  * AMD-specific equivalence table
522449e17fSsherrym  */
53*adc586deSMark Johnson static ucode_eqtbl_amd_t *ucode_eqtbl_amd;
542449e17fSsherrym 
552449e17fSsherrym /*
562449e17fSsherrym  * mcpu_ucode_info for the boot CPU.  Statically allocated.
572449e17fSsherrym  */
582449e17fSsherrym static struct cpu_ucode_info cpu_ucode_info0;
592449e17fSsherrym 
60*adc586deSMark Johnson static ucode_file_t ucodefile;
612449e17fSsherrym 
62*adc586deSMark Johnson static void* ucode_zalloc(processorid_t, size_t);
63*adc586deSMark Johnson static void ucode_free(processorid_t, void *, size_t);
64*adc586deSMark Johnson 
65*adc586deSMark Johnson static int ucode_capable_amd(cpu_t *);
66*adc586deSMark Johnson static int ucode_capable_intel(cpu_t *);
67*adc586deSMark Johnson 
68*adc586deSMark Johnson static ucode_errno_t ucode_extract_amd(ucode_update_t *, uint8_t *, int);
69*adc586deSMark Johnson static ucode_errno_t ucode_extract_intel(ucode_update_t *, uint8_t *,
70*adc586deSMark Johnson     int);
71*adc586deSMark Johnson 
72*adc586deSMark Johnson static void ucode_file_reset_amd(ucode_file_t *, processorid_t);
73*adc586deSMark Johnson static void ucode_file_reset_intel(ucode_file_t *, processorid_t);
74*adc586deSMark Johnson 
75*adc586deSMark Johnson static uint32_t ucode_load_amd(ucode_file_t *, cpu_ucode_info_t *, cpu_t *);
76*adc586deSMark Johnson static uint32_t ucode_load_intel(ucode_file_t *, cpu_ucode_info_t *, cpu_t *);
77*adc586deSMark Johnson 
78882a7af5SMark Johnson #ifdef	__xpv
79*adc586deSMark Johnson static void ucode_load_xpv(ucode_update_t *);
80882a7af5SMark Johnson #endif
812449e17fSsherrym 
82*adc586deSMark Johnson static int ucode_equiv_cpu_amd(cpu_t *, int *);
83*adc586deSMark Johnson 
84*adc586deSMark Johnson static ucode_errno_t ucode_locate_amd(cpu_t *, cpu_ucode_info_t *,
85*adc586deSMark Johnson     ucode_file_t *);
86*adc586deSMark Johnson static ucode_errno_t ucode_locate_intel(cpu_t *, cpu_ucode_info_t *,
87*adc586deSMark Johnson     ucode_file_t *);
88*adc586deSMark Johnson 
89*adc586deSMark Johnson static ucode_errno_t ucode_match_amd(int, cpu_ucode_info_t *,
90*adc586deSMark Johnson     ucode_file_amd_t *, int);
91*adc586deSMark Johnson static ucode_errno_t ucode_match_intel(int, cpu_ucode_info_t *,
92*adc586deSMark Johnson     ucode_header_intel_t *, ucode_ext_table_intel_t *);
93*adc586deSMark Johnson 
94*adc586deSMark Johnson static void ucode_read_rev_amd(cpu_ucode_info_t *);
95*adc586deSMark Johnson static void ucode_read_rev_intel(cpu_ucode_info_t *);
96*adc586deSMark Johnson 
97*adc586deSMark Johnson static const struct ucode_ops ucode_amd = {
98*adc586deSMark Johnson 	MSR_AMD_PATCHLOADER,
99*adc586deSMark Johnson 	ucode_capable_amd,
100*adc586deSMark Johnson 	ucode_file_reset_amd,
101*adc586deSMark Johnson 	ucode_read_rev_amd,
102*adc586deSMark Johnson 	ucode_load_amd,
103*adc586deSMark Johnson 	ucode_validate_amd,
104*adc586deSMark Johnson 	ucode_extract_amd,
105*adc586deSMark Johnson 	ucode_locate_amd
106*adc586deSMark Johnson };
107*adc586deSMark Johnson 
108*adc586deSMark Johnson static const struct ucode_ops ucode_intel = {
109*adc586deSMark Johnson 	MSR_INTC_UCODE_WRITE,
110*adc586deSMark Johnson 	ucode_capable_intel,
111*adc586deSMark Johnson 	ucode_file_reset_intel,
112*adc586deSMark Johnson 	ucode_read_rev_intel,
113*adc586deSMark Johnson 	ucode_load_intel,
114*adc586deSMark Johnson 	ucode_validate_intel,
115*adc586deSMark Johnson 	ucode_extract_intel,
116*adc586deSMark Johnson 	ucode_locate_intel
117*adc586deSMark Johnson };
118*adc586deSMark Johnson 
119*adc586deSMark Johnson const struct ucode_ops *ucode;
120*adc586deSMark Johnson 
1212449e17fSsherrym static const char ucode_failure_fmt[] =
12288699bddSsherrym 	"cpu%d: failed to update microcode from version 0x%x to 0x%x\n";
1232449e17fSsherrym static const char ucode_success_fmt[] =
12488699bddSsherrym 	"?cpu%d: microcode has been updated from version 0x%x to 0x%x\n";
1252449e17fSsherrym 
1262449e17fSsherrym /*
1272449e17fSsherrym  * Force flag.  If set, the first microcode binary that matches
1282449e17fSsherrym  * signature and platform id will be used for microcode update,
1292449e17fSsherrym  * regardless of version.  Should only be used for debugging.
1302449e17fSsherrym  */
1312449e17fSsherrym int ucode_force_update = 0;
1322449e17fSsherrym 
1332449e17fSsherrym /*
1342449e17fSsherrym  * Allocate space for mcpu_ucode_info in the machcpu structure
1352449e17fSsherrym  * for all non-boot CPUs.
1362449e17fSsherrym  */
1372449e17fSsherrym void
1382449e17fSsherrym ucode_alloc_space(cpu_t *cp)
1392449e17fSsherrym {
1402449e17fSsherrym 	ASSERT(cp->cpu_id != 0);
1412449e17fSsherrym 	cp->cpu_m.mcpu_ucode_info =
1422449e17fSsherrym 	    kmem_zalloc(sizeof (*cp->cpu_m.mcpu_ucode_info), KM_SLEEP);
1432449e17fSsherrym }
1442449e17fSsherrym 
1452449e17fSsherrym void
1462449e17fSsherrym ucode_free_space(cpu_t *cp)
1472449e17fSsherrym {
1482449e17fSsherrym 	ASSERT(cp->cpu_id != 0);
1492449e17fSsherrym 	kmem_free(cp->cpu_m.mcpu_ucode_info,
1502449e17fSsherrym 	    sizeof (*cp->cpu_m.mcpu_ucode_info));
1512449e17fSsherrym }
1522449e17fSsherrym 
1532449e17fSsherrym /*
1542449e17fSsherrym  * Called when we are done with microcode update on all processors to free up
1552449e17fSsherrym  * space allocated for the microcode file.
1562449e17fSsherrym  */
1572449e17fSsherrym void
158*adc586deSMark Johnson ucode_cleanup()
1592449e17fSsherrym {
160*adc586deSMark Johnson 	ASSERT(ucode);
161*adc586deSMark Johnson 
162*adc586deSMark Johnson 	ucode->file_reset(&ucodefile, -1);
163*adc586deSMark Johnson }
164*adc586deSMark Johnson 
165*adc586deSMark Johnson /*
166*adc586deSMark Johnson  * Allocate/free a buffer used to hold ucode data. Space for the boot CPU is
167*adc586deSMark Johnson  * allocated with BOP_ALLOC() and does not require a free.
168*adc586deSMark Johnson  */
169*adc586deSMark Johnson static void*
170*adc586deSMark Johnson ucode_zalloc(processorid_t id, size_t size)
171*adc586deSMark Johnson {
172*adc586deSMark Johnson 	if (id)
173*adc586deSMark Johnson 		return (kmem_zalloc(size, KM_NOSLEEP));
174*adc586deSMark Johnson 
175*adc586deSMark Johnson 	/* BOP_ALLOC() failure results in panic */
176*adc586deSMark Johnson 	return (BOP_ALLOC(bootops, NULL, size, MMU_PAGESIZE));
177*adc586deSMark Johnson }
178*adc586deSMark Johnson 
179*adc586deSMark Johnson static void
180*adc586deSMark Johnson ucode_free(processorid_t id, void* buf, size_t size)
181*adc586deSMark Johnson {
182*adc586deSMark Johnson 	if (id)
183*adc586deSMark Johnson 		kmem_free(buf, size);
1842449e17fSsherrym }
1852449e17fSsherrym 
1862449e17fSsherrym /*
1872449e17fSsherrym  * Check whether or not a processor is capable of microcode operations
1882449e17fSsherrym  * Returns 1 if it is capable, 0 if not.
189*adc586deSMark Johnson  *
190*adc586deSMark Johnson  * At this point we only support microcode update for:
191*adc586deSMark Johnson  * - Intel processors family 6 and above, and
192*adc586deSMark Johnson  * - AMD processors family 0x10 and above.
193*adc586deSMark Johnson  *
194*adc586deSMark Johnson  * We also assume that we don't support a mix of Intel and
195*adc586deSMark Johnson  * AMD processors in the same box.
196*adc586deSMark Johnson  *
197*adc586deSMark Johnson  * An i86xpv guest domain can't update the microcode.
1982449e17fSsherrym  */
199*adc586deSMark Johnson /*ARGSUSED*/
2002449e17fSsherrym static int
201*adc586deSMark Johnson ucode_capable_amd(cpu_t *cp)
2022449e17fSsherrym {
203*adc586deSMark Johnson #ifndef	__xpv
204*adc586deSMark Johnson 	extern int xpv_is_hvm;
205*adc586deSMark Johnson 	if (xpv_is_hvm) {
206*adc586deSMark Johnson 		return (0);
207*adc586deSMark Johnson 	}
208*adc586deSMark Johnson 
209*adc586deSMark Johnson 	return (cpuid_getfamily(cp) >= 0x10);
210*adc586deSMark Johnson #else
211*adc586deSMark Johnson 	if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
212*adc586deSMark Johnson 		return (0);
213*adc586deSMark Johnson 	}
214*adc586deSMark Johnson 
215*adc586deSMark Johnson 	/*
216*adc586deSMark Johnson 	 * XXPV - change when microcode loading works in dom0. Don't support
217*adc586deSMark Johnson 	 * microcode loading in dom0 right now for AMD.
218*adc586deSMark Johnson 	 */
219*adc586deSMark Johnson 	return (0);
220*adc586deSMark Johnson #endif
221*adc586deSMark Johnson }
222*adc586deSMark Johnson static int
223*adc586deSMark Johnson ucode_capable_intel(cpu_t *cp)
224*adc586deSMark Johnson {
225882a7af5SMark Johnson #ifndef	__xpv
226882a7af5SMark Johnson 	extern int xpv_is_hvm;
227882a7af5SMark Johnson 	if (xpv_is_hvm) {
228882a7af5SMark Johnson 		return (0);
229882a7af5SMark Johnson 	}
230882a7af5SMark Johnson #else
231843e1988Sjohnlev 	if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
232843e1988Sjohnlev 		return (0);
233843e1988Sjohnlev 	}
234843e1988Sjohnlev #endif
235*adc586deSMark Johnson 	return (cpuid_getfamily(cp) >= 6);
2362449e17fSsherrym }
2372449e17fSsherrym 
2382449e17fSsherrym /*
2392449e17fSsherrym  * Called when it is no longer necessary to keep the microcode around,
2402449e17fSsherrym  * or when the cached microcode doesn't match the CPU being processed.
2412449e17fSsherrym  */
2422449e17fSsherrym static void
243*adc586deSMark Johnson ucode_file_reset_amd(ucode_file_t *ufp, processorid_t id)
2442449e17fSsherrym {
245*adc586deSMark Johnson 	ucode_file_amd_t *ucodefp = ufp->amd;
2462449e17fSsherrym 
2472449e17fSsherrym 	if (ucodefp == NULL)
2482449e17fSsherrym 		return;
2492449e17fSsherrym 
250*adc586deSMark Johnson 	ucode_free(id, ucodefp, sizeof (ucode_file_amd_t));
251*adc586deSMark Johnson 	ufp->amd = NULL;
252*adc586deSMark Johnson }
253*adc586deSMark Johnson 
254*adc586deSMark Johnson static void
255*adc586deSMark Johnson ucode_file_reset_intel(ucode_file_t *ufp, processorid_t id)
256*adc586deSMark Johnson {
257*adc586deSMark Johnson 	ucode_file_intel_t *ucodefp = &ufp->intel;
258*adc586deSMark Johnson 	int total_size, body_size;
259*adc586deSMark Johnson 
260*adc586deSMark Johnson 	if (ucodefp == NULL || ucodefp->uf_header == NULL)
261*adc586deSMark Johnson 		return;
262*adc586deSMark Johnson 
263*adc586deSMark Johnson 	total_size = UCODE_TOTAL_SIZE_INTEL(ucodefp->uf_header->uh_total_size);
264*adc586deSMark Johnson 	body_size = UCODE_BODY_SIZE_INTEL(ucodefp->uf_header->uh_body_size);
2652449e17fSsherrym 	if (ucodefp->uf_body) {
266*adc586deSMark Johnson 		ucode_free(id, ucodefp->uf_body, body_size);
2672449e17fSsherrym 		ucodefp->uf_body = NULL;
2682449e17fSsherrym 	}
2692449e17fSsherrym 
2702449e17fSsherrym 	if (ucodefp->uf_ext_table) {
271*adc586deSMark Johnson 		int size = total_size - body_size - UCODE_HEADER_SIZE_INTEL;
272*adc586deSMark Johnson 
273*adc586deSMark Johnson 		ucode_free(id, ucodefp->uf_ext_table, size);
2742449e17fSsherrym 		ucodefp->uf_ext_table = NULL;
2752449e17fSsherrym 	}
2762449e17fSsherrym 
277*adc586deSMark Johnson 	ucode_free(id, ucodefp->uf_header, UCODE_HEADER_SIZE_INTEL);
278*adc586deSMark Johnson 	ucodefp->uf_header = NULL;
279*adc586deSMark Johnson }
280*adc586deSMark Johnson 
281*adc586deSMark Johnson /*
282*adc586deSMark Johnson  * Find the equivalent CPU id in the equivalence table.
283*adc586deSMark Johnson  */
284*adc586deSMark Johnson static int
285*adc586deSMark Johnson ucode_equiv_cpu_amd(cpu_t *cp, int *eq_sig)
286*adc586deSMark Johnson {
287*adc586deSMark Johnson 	char name[MAXPATHLEN];
288*adc586deSMark Johnson 	intptr_t fd;
289*adc586deSMark Johnson 	int count;
290*adc586deSMark Johnson 	int offset = 0, cpi_sig = cpuid_getsig(cp);
291*adc586deSMark Johnson 	ucode_eqtbl_amd_t *eqtbl = ucode_eqtbl_amd;
292*adc586deSMark Johnson 
293*adc586deSMark Johnson 	(void) snprintf(name, MAXPATHLEN, "/%s/%s/equivalence-table",
294*adc586deSMark Johnson 	    UCODE_INSTALL_PATH, cpuid_getvendorstr(cp));
295*adc586deSMark Johnson 
296*adc586deSMark Johnson 	/*
297*adc586deSMark Johnson 	 * No kmem_zalloc() etc. available on boot cpu.
298*adc586deSMark Johnson 	 */
299*adc586deSMark Johnson 	if (cp->cpu_id == 0) {
300*adc586deSMark Johnson 		if ((fd = kobj_open(name)) == -1)
301*adc586deSMark Johnson 			return (EM_OPENFILE);
302*adc586deSMark Johnson 		/* ucode_zalloc() cannot fail on boot cpu */
303*adc586deSMark Johnson 		eqtbl = ucode_zalloc(cp->cpu_id, sizeof (*eqtbl));
304*adc586deSMark Johnson 		ASSERT(eqtbl);
305*adc586deSMark Johnson 		do {
306*adc586deSMark Johnson 			count = kobj_read(fd, (int8_t *)eqtbl,
307*adc586deSMark Johnson 			    sizeof (*eqtbl), offset);
308*adc586deSMark Johnson 			if (count != sizeof (*eqtbl)) {
309*adc586deSMark Johnson 				(void) kobj_close(fd);
310*adc586deSMark Johnson 				return (EM_HIGHERREV);
311*adc586deSMark Johnson 			}
312*adc586deSMark Johnson 			offset += count;
313*adc586deSMark Johnson 		} while (eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != cpi_sig);
314*adc586deSMark Johnson 		(void) kobj_close(fd);
315*adc586deSMark Johnson 	}
316*adc586deSMark Johnson 
317*adc586deSMark Johnson 	/*
318*adc586deSMark Johnson 	 * If not already done, load the equivalence table.
319*adc586deSMark Johnson 	 * Not done on boot CPU.
320*adc586deSMark Johnson 	 */
321*adc586deSMark Johnson 	if (eqtbl == NULL) {
322*adc586deSMark Johnson 		struct _buf *eq;
323*adc586deSMark Johnson 		uint64_t size;
324*adc586deSMark Johnson 
325*adc586deSMark Johnson 		if ((eq = kobj_open_file(name)) == (struct _buf *)-1)
326*adc586deSMark Johnson 			return (EM_OPENFILE);
327*adc586deSMark Johnson 
328*adc586deSMark Johnson 		if (kobj_get_filesize(eq, &size) < 0) {
329*adc586deSMark Johnson 			kobj_close_file(eq);
330*adc586deSMark Johnson 			return (EM_OPENFILE);
331*adc586deSMark Johnson 		}
332*adc586deSMark Johnson 
333*adc586deSMark Johnson 		ucode_eqtbl_amd = kmem_zalloc(size, KM_NOSLEEP);
334*adc586deSMark Johnson 		if (ucode_eqtbl_amd == NULL) {
335*adc586deSMark Johnson 			kobj_close_file(eq);
336*adc586deSMark Johnson 			return (EM_NOMEM);
337*adc586deSMark Johnson 		}
338*adc586deSMark Johnson 
339*adc586deSMark Johnson 		count = kobj_read_file(eq, (char *)ucode_eqtbl_amd, size, 0);
340*adc586deSMark Johnson 		kobj_close_file(eq);
341*adc586deSMark Johnson 
342*adc586deSMark Johnson 		if (count != size)
343*adc586deSMark Johnson 			return (EM_FILESIZE);
344*adc586deSMark Johnson 	}
345*adc586deSMark Johnson 
346*adc586deSMark Johnson 	/* Get the equivalent CPU id. */
347*adc586deSMark Johnson 	if (cp->cpu_id)
348*adc586deSMark Johnson 		for (eqtbl = ucode_eqtbl_amd;
349*adc586deSMark Johnson 		    eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != cpi_sig;
350*adc586deSMark Johnson 		    eqtbl++)
351*adc586deSMark Johnson 			;
352*adc586deSMark Johnson 
353*adc586deSMark Johnson 	*eq_sig = eqtbl->ue_equiv_cpu;
354*adc586deSMark Johnson 	*eq_sig = ((*eq_sig >> 8) & 0xff00) | (*eq_sig & 0xff);
355*adc586deSMark Johnson 
356*adc586deSMark Johnson 	/* No equivalent CPU id found, assume outdated microcode file. */
357*adc586deSMark Johnson 	if (*eq_sig == 0)
358*adc586deSMark Johnson 		return (EM_HIGHERREV);
359*adc586deSMark Johnson 
360*adc586deSMark Johnson 	return (EM_OK);
3612449e17fSsherrym }
3622449e17fSsherrym 
3632449e17fSsherrym /*
3642449e17fSsherrym  * Populate the ucode file structure from microcode file corresponding to
3652449e17fSsherrym  * this CPU, if exists.
3662449e17fSsherrym  *
3672449e17fSsherrym  * Return EM_OK on success, corresponding error code on failure.
3682449e17fSsherrym  */
3692449e17fSsherrym static ucode_errno_t
370*adc586deSMark Johnson ucode_locate_amd(cpu_t *cp, cpu_ucode_info_t *uinfop, ucode_file_t *ufp)
371*adc586deSMark Johnson {
372*adc586deSMark Johnson 	char name[MAXPATHLEN];
373*adc586deSMark Johnson 	intptr_t fd;
374*adc586deSMark Johnson 	int count, i, rc;
375*adc586deSMark Johnson 	int eq_sig = 0;
376*adc586deSMark Johnson 	ucode_file_amd_t *ucodefp = ufp->amd;
377*adc586deSMark Johnson 
378*adc586deSMark Johnson 	/* get equivalent CPU id */
379*adc586deSMark Johnson 	if ((rc = ucode_equiv_cpu_amd(cp, &eq_sig)) != EM_OK)
380*adc586deSMark Johnson 		return (rc);
381*adc586deSMark Johnson 
382*adc586deSMark Johnson 	/*
383*adc586deSMark Johnson 	 * Allocate a buffer for the microcode patch. If the buffer has been
384*adc586deSMark Johnson 	 * allocated before, check for a matching microcode to avoid loading
385*adc586deSMark Johnson 	 * the file again.
386*adc586deSMark Johnson 	 */
387*adc586deSMark Johnson 	if (ucodefp == NULL)
388*adc586deSMark Johnson 		ucodefp = ucode_zalloc(cp->cpu_id, sizeof (*ucodefp));
389*adc586deSMark Johnson 	else if (ucode_match_amd(eq_sig, uinfop, ucodefp, sizeof (*ucodefp))
390*adc586deSMark Johnson 	    == EM_OK)
391*adc586deSMark Johnson 		return (EM_OK);
392*adc586deSMark Johnson 
393*adc586deSMark Johnson 	if (ucodefp == NULL)
394*adc586deSMark Johnson 		return (EM_NOMEM);
395*adc586deSMark Johnson 
396*adc586deSMark Johnson 	ufp->amd = ucodefp;
397*adc586deSMark Johnson 
398*adc586deSMark Johnson 	/*
399*adc586deSMark Johnson 	 * Find the patch for this CPU. The patch files are named XXXX-YY, where
400*adc586deSMark Johnson 	 * XXXX is the equivalent CPU id and YY is the running patch number.
401*adc586deSMark Johnson 	 * Patches specific to certain chipsets are guaranteed to have lower
402*adc586deSMark Johnson 	 * numbers than less specific patches, so we can just load the first
403*adc586deSMark Johnson 	 * patch that matches.
404*adc586deSMark Johnson 	 */
405*adc586deSMark Johnson 
406*adc586deSMark Johnson 	for (i = 0; i < 0xff; i++) {
407*adc586deSMark Johnson 		(void) snprintf(name, MAXPATHLEN, "/%s/%s/%04X-%02X",
408*adc586deSMark Johnson 		    UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), eq_sig, i);
409*adc586deSMark Johnson 		if ((fd = kobj_open(name)) == -1)
410*adc586deSMark Johnson 			return (EM_NOMATCH);
411*adc586deSMark Johnson 		count = kobj_read(fd, (char *)ucodefp, sizeof (*ucodefp), 0);
412*adc586deSMark Johnson 		(void) kobj_close(fd);
413*adc586deSMark Johnson 
414*adc586deSMark Johnson 		if (ucode_match_amd(eq_sig, uinfop, ucodefp, count) == EM_OK)
415*adc586deSMark Johnson 			return (EM_OK);
416*adc586deSMark Johnson 	}
417*adc586deSMark Johnson 	return (EM_NOMATCH);
418*adc586deSMark Johnson }
419*adc586deSMark Johnson 
420*adc586deSMark Johnson static ucode_errno_t
421*adc586deSMark Johnson ucode_locate_intel(cpu_t *cp, cpu_ucode_info_t *uinfop, ucode_file_t *ufp)
4222449e17fSsherrym {
4232449e17fSsherrym 	char		name[MAXPATHLEN];
4242449e17fSsherrym 	intptr_t	fd;
4252449e17fSsherrym 	int		count;
426*adc586deSMark Johnson 	int		header_size = UCODE_HEADER_SIZE_INTEL;
4272449e17fSsherrym 	int		cpi_sig = cpuid_getsig(cp);
4282449e17fSsherrym 	ucode_errno_t	rc = EM_OK;
429*adc586deSMark Johnson 	ucode_file_intel_t *ucodefp = &ufp->intel;
430*adc586deSMark Johnson 
431*adc586deSMark Johnson 	ASSERT(ucode);
4322449e17fSsherrym 
4332449e17fSsherrym 	/*
4342449e17fSsherrym 	 * If the microcode matches the CPU we are processing, use it.
4352449e17fSsherrym 	 */
436*adc586deSMark Johnson 	if (ucode_match_intel(cpi_sig, uinfop, ucodefp->uf_header,
4372449e17fSsherrym 	    ucodefp->uf_ext_table) == EM_OK && ucodefp->uf_body != NULL) {
4382449e17fSsherrym 		return (EM_OK);
4392449e17fSsherrym 	}
4402449e17fSsherrym 
4412449e17fSsherrym 	/*
4422449e17fSsherrym 	 * Look for microcode file with the right name.
4432449e17fSsherrym 	 */
4442449e17fSsherrym 	(void) snprintf(name, MAXPATHLEN, "/%s/%s/%08X-%02X",
4452449e17fSsherrym 	    UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), cpi_sig,
4462449e17fSsherrym 	    uinfop->cui_platid);
4472449e17fSsherrym 	if ((fd = kobj_open(name)) == -1) {
4482449e17fSsherrym 		return (EM_OPENFILE);
4492449e17fSsherrym 	}
4502449e17fSsherrym 
4512449e17fSsherrym 	/*
4522449e17fSsherrym 	 * We found a microcode file for the CPU we are processing,
4532449e17fSsherrym 	 * reset the microcode data structure and read in the new
4542449e17fSsherrym 	 * file.
4552449e17fSsherrym 	 */
456*adc586deSMark Johnson 	ucode->file_reset(ufp, cp->cpu_id);
4572449e17fSsherrym 
458*adc586deSMark Johnson 	ucodefp->uf_header = ucode_zalloc(cp->cpu_id, header_size);
459*adc586deSMark Johnson 	if (ucodefp->uf_header == NULL)
460*adc586deSMark Johnson 		return (EM_NOMEM);
461*adc586deSMark Johnson 
462*adc586deSMark Johnson 	count = kobj_read(fd, (char *)ucodefp->uf_header, header_size, 0);
4632449e17fSsherrym 
4642449e17fSsherrym 	switch (count) {
465*adc586deSMark Johnson 	case UCODE_HEADER_SIZE_INTEL: {
4662449e17fSsherrym 
467*adc586deSMark Johnson 		ucode_header_intel_t	*uhp = ucodefp->uf_header;
4682449e17fSsherrym 		uint32_t	offset = header_size;
4692449e17fSsherrym 		int		total_size, body_size, ext_size;
4702449e17fSsherrym 		uint32_t	sum = 0;
4712449e17fSsherrym 
4722449e17fSsherrym 		/*
4732449e17fSsherrym 		 * Make sure that the header contains valid fields.
4742449e17fSsherrym 		 */
475*adc586deSMark Johnson 		if ((rc = ucode_header_validate_intel(uhp)) == EM_OK) {
476*adc586deSMark Johnson 			total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
477*adc586deSMark Johnson 			body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size);
478*adc586deSMark Johnson 			ucodefp->uf_body = ucode_zalloc(cp->cpu_id, body_size);
479*adc586deSMark Johnson 			if (ucodefp->uf_body == NULL) {
4802449e17fSsherrym 				rc = EM_NOMEM;
4812449e17fSsherrym 				break;
4822449e17fSsherrym 			}
4832449e17fSsherrym 
4842449e17fSsherrym 			if (kobj_read(fd, (char *)ucodefp->uf_body,
4852449e17fSsherrym 			    body_size, offset) != body_size)
4862449e17fSsherrym 				rc = EM_FILESIZE;
4872449e17fSsherrym 		}
4882449e17fSsherrym 
4892449e17fSsherrym 		if (rc)
4902449e17fSsherrym 			break;
4912449e17fSsherrym 
492*adc586deSMark Johnson 		sum = ucode_checksum_intel(0, header_size,
493*adc586deSMark Johnson 		    (uint8_t *)ucodefp->uf_header);
494*adc586deSMark Johnson 		if (ucode_checksum_intel(sum, body_size, ucodefp->uf_body)) {
4952449e17fSsherrym 			rc = EM_CHECKSUM;
4962449e17fSsherrym 			break;
4972449e17fSsherrym 		}
4982449e17fSsherrym 
4992449e17fSsherrym 		/*
5002449e17fSsherrym 		 * Check to see if there is extended signature table.
5012449e17fSsherrym 		 */
5022449e17fSsherrym 		offset = body_size + header_size;
5032449e17fSsherrym 		ext_size = total_size - offset;
5042449e17fSsherrym 
5052449e17fSsherrym 		if (ext_size <= 0)
5062449e17fSsherrym 			break;
5072449e17fSsherrym 
508*adc586deSMark Johnson 		ucodefp->uf_ext_table = ucode_zalloc(cp->cpu_id, ext_size);
509*adc586deSMark Johnson 		if (ucodefp->uf_ext_table == NULL) {
5102449e17fSsherrym 			rc = EM_NOMEM;
5112449e17fSsherrym 			break;
5122449e17fSsherrym 		}
5132449e17fSsherrym 
5142449e17fSsherrym 		if (kobj_read(fd, (char *)ucodefp->uf_ext_table,
5152449e17fSsherrym 		    ext_size, offset) != ext_size) {
5162449e17fSsherrym 			rc = EM_FILESIZE;
517*adc586deSMark Johnson 		} else if (ucode_checksum_intel(0, ext_size,
5182449e17fSsherrym 		    (uint8_t *)(ucodefp->uf_ext_table))) {
5192449e17fSsherrym 			rc = EM_CHECKSUM;
5202449e17fSsherrym 		} else {
5212449e17fSsherrym 			int i;
5222449e17fSsherrym 
523*adc586deSMark Johnson 			ext_size -= UCODE_EXT_TABLE_SIZE_INTEL;
5242449e17fSsherrym 			for (i = 0; i < ucodefp->uf_ext_table->uet_count;
5252449e17fSsherrym 			    i++) {
526*adc586deSMark Johnson 				if (ucode_checksum_intel(0,
527*adc586deSMark Johnson 				    UCODE_EXT_SIG_SIZE_INTEL,
5282449e17fSsherrym 				    (uint8_t *)(&(ucodefp->uf_ext_table->
5292449e17fSsherrym 				    uet_ext_sig[i])))) {
5302449e17fSsherrym 					rc = EM_CHECKSUM;
5312449e17fSsherrym 					break;
5322449e17fSsherrym 				}
5332449e17fSsherrym 			}
5342449e17fSsherrym 		}
5352449e17fSsherrym 		break;
5362449e17fSsherrym 	}
5372449e17fSsherrym 
5382449e17fSsherrym 	default:
5392449e17fSsherrym 		rc = EM_FILESIZE;
5402449e17fSsherrym 		break;
5412449e17fSsherrym 	}
5422449e17fSsherrym 
5432449e17fSsherrym 	kobj_close(fd);
5442449e17fSsherrym 
5452449e17fSsherrym 	if (rc != EM_OK)
5462449e17fSsherrym 		return (rc);
5472449e17fSsherrym 
548*adc586deSMark Johnson 	rc = ucode_match_intel(cpi_sig, uinfop, ucodefp->uf_header,
5492449e17fSsherrym 	    ucodefp->uf_ext_table);
5502449e17fSsherrym 
5512449e17fSsherrym 	return (rc);
5522449e17fSsherrym }
5532449e17fSsherrym 
554*adc586deSMark Johnson static ucode_errno_t
555*adc586deSMark Johnson ucode_match_amd(int eq_sig, cpu_ucode_info_t *uinfop, ucode_file_amd_t *ucodefp,
556*adc586deSMark Johnson     int size)
557*adc586deSMark Johnson {
558*adc586deSMark Johnson 	ucode_header_amd_t *uh;
559*adc586deSMark Johnson 
560*adc586deSMark Johnson 	if (ucodefp == NULL || size < sizeof (ucode_header_amd_t))
561*adc586deSMark Johnson 		return (EM_NOMATCH);
562*adc586deSMark Johnson 
563*adc586deSMark Johnson 	/*
564*adc586deSMark Johnson 	 * Don't even think about loading patches that would require code
565*adc586deSMark Johnson 	 * execution.
566*adc586deSMark Johnson 	 */
567*adc586deSMark Johnson 	if (size > offsetof(ucode_file_amd_t, uf_code_present) &&
568*adc586deSMark Johnson 	    ucodefp->uf_code_present)
569*adc586deSMark Johnson 		return (EM_NOMATCH);
570*adc586deSMark Johnson 
571*adc586deSMark Johnson 	uh = &ucodefp->uf_header;
572*adc586deSMark Johnson 
573*adc586deSMark Johnson 	if (eq_sig != uh->uh_cpu_rev)
574*adc586deSMark Johnson 		return (EM_NOMATCH);
575*adc586deSMark Johnson 
576*adc586deSMark Johnson 	if (uh->uh_nb_id) {
577*adc586deSMark Johnson 		cmn_err(CE_WARN, "ignoring northbridge-specific ucode: "
578*adc586deSMark Johnson 		    "chipset id %x, revision %x", uh->uh_nb_id, uh->uh_nb_rev);
579*adc586deSMark Johnson 		return (EM_NOMATCH);
580*adc586deSMark Johnson 	}
581*adc586deSMark Johnson 
582*adc586deSMark Johnson 	if (uh->uh_sb_id) {
583*adc586deSMark Johnson 		cmn_err(CE_WARN, "ignoring southbridge-specific ucode: "
584*adc586deSMark Johnson 		    "chipset id %x, revision %x", uh->uh_sb_id, uh->uh_sb_rev);
585*adc586deSMark Johnson 		return (EM_NOMATCH);
586*adc586deSMark Johnson 	}
587*adc586deSMark Johnson 
588*adc586deSMark Johnson 	if (uh->uh_patch_id <= uinfop->cui_rev)
589*adc586deSMark Johnson 		return (EM_HIGHERREV);
590*adc586deSMark Johnson 
591*adc586deSMark Johnson 	return (EM_OK);
592*adc586deSMark Johnson }
5932449e17fSsherrym 
5942449e17fSsherrym /*
5952449e17fSsherrym  * Returns 1 if the microcode is for this processor; 0 otherwise.
5962449e17fSsherrym  */
5972449e17fSsherrym static ucode_errno_t
598*adc586deSMark Johnson ucode_match_intel(int cpi_sig, cpu_ucode_info_t *uinfop,
599*adc586deSMark Johnson     ucode_header_intel_t *uhp, ucode_ext_table_intel_t *uetp)
6002449e17fSsherrym {
601*adc586deSMark Johnson 	if (uhp == NULL)
602*adc586deSMark Johnson 		return (EM_NOMATCH);
6032449e17fSsherrym 
604*adc586deSMark Johnson 	if (UCODE_MATCH_INTEL(cpi_sig, uhp->uh_signature,
6052449e17fSsherrym 	    uinfop->cui_platid, uhp->uh_proc_flags)) {
6062449e17fSsherrym 
6072449e17fSsherrym 		if (uinfop->cui_rev >= uhp->uh_rev && !ucode_force_update)
6082449e17fSsherrym 			return (EM_HIGHERREV);
6092449e17fSsherrym 
6102449e17fSsherrym 		return (EM_OK);
6112449e17fSsherrym 	}
6122449e17fSsherrym 
6132449e17fSsherrym 	if (uetp != NULL) {
6142449e17fSsherrym 		int i;
6152449e17fSsherrym 
6162449e17fSsherrym 		for (i = 0; i < uetp->uet_count; i++) {
617*adc586deSMark Johnson 			ucode_ext_sig_intel_t *uesp;
6182449e17fSsherrym 
6192449e17fSsherrym 			uesp = &uetp->uet_ext_sig[i];
6202449e17fSsherrym 
621*adc586deSMark Johnson 			if (UCODE_MATCH_INTEL(cpi_sig, uesp->ues_signature,
6222449e17fSsherrym 			    uinfop->cui_platid, uesp->ues_proc_flags)) {
6232449e17fSsherrym 
6242449e17fSsherrym 				if (uinfop->cui_rev >= uhp->uh_rev &&
6252449e17fSsherrym 				    !ucode_force_update)
6262449e17fSsherrym 					return (EM_HIGHERREV);
6272449e17fSsherrym 
6282449e17fSsherrym 				return (EM_OK);
6292449e17fSsherrym 			}
6302449e17fSsherrym 		}
6312449e17fSsherrym 	}
6322449e17fSsherrym 
6332449e17fSsherrym 	return (EM_NOMATCH);
6342449e17fSsherrym }
6352449e17fSsherrym 
6362449e17fSsherrym /*ARGSUSED*/
6372449e17fSsherrym static int
6382449e17fSsherrym ucode_write(xc_arg_t arg1, xc_arg_t unused2, xc_arg_t unused3)
6392449e17fSsherrym {
640*adc586deSMark Johnson 	ucode_update_t *uusp = (ucode_update_t *)arg1;
641*adc586deSMark Johnson 	cpu_ucode_info_t *uinfop = CPU->cpu_m.mcpu_ucode_info;
6422449e17fSsherrym 
643*adc586deSMark Johnson 	ASSERT(ucode);
6442449e17fSsherrym 	ASSERT(uusp->ucodep);
6452449e17fSsherrym 
646882a7af5SMark Johnson #ifndef	__xpv
6472449e17fSsherrym 	/*
6482449e17fSsherrym 	 * Check one more time to see if it is really necessary to update
6492449e17fSsherrym 	 * microcode just in case this is a hyperthreaded processor where
6502449e17fSsherrym 	 * the threads share the same microcode.
6512449e17fSsherrym 	 */
6522449e17fSsherrym 	if (!ucode_force_update) {
653*adc586deSMark Johnson 		ucode->read_rev(uinfop);
6542449e17fSsherrym 		uusp->new_rev = uinfop->cui_rev;
6552449e17fSsherrym 		if (uinfop->cui_rev >= uusp->expected_rev)
6562449e17fSsherrym 			return (0);
6572449e17fSsherrym 	}
6582449e17fSsherrym 
659*adc586deSMark Johnson 	wrmsr(ucode->write_msr, (uintptr_t)uusp->ucodep);
660882a7af5SMark Johnson #endif
661*adc586deSMark Johnson 	ucode->read_rev(uinfop);
6622449e17fSsherrym 	uusp->new_rev = uinfop->cui_rev;
6632449e17fSsherrym 
6642449e17fSsherrym 	return (0);
6652449e17fSsherrym }
6662449e17fSsherrym 
667*adc586deSMark Johnson /*ARGSUSED*/
668*adc586deSMark Johnson static uint32_t
669*adc586deSMark Johnson ucode_load_amd(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp)
6702449e17fSsherrym {
671*adc586deSMark Johnson 	ucode_file_amd_t *ucodefp = ufp->amd;
672*adc586deSMark Johnson #ifdef	__xpv
673*adc586deSMark Johnson 	ucode_update_t uus;
674*adc586deSMark Johnson #endif
675*adc586deSMark Johnson 
676*adc586deSMark Johnson 	ASSERT(ucode);
677*adc586deSMark Johnson 	ASSERT(ucodefp);
678*adc586deSMark Johnson 
679*adc586deSMark Johnson #ifndef	__xpv
6802449e17fSsherrym 	kpreempt_disable();
681*adc586deSMark Johnson 	wrmsr(ucode->write_msr, (uintptr_t)ucodefp);
682*adc586deSMark Johnson 	ucode->read_rev(uinfop);
6832449e17fSsherrym 	kpreempt_enable();
684*adc586deSMark Johnson #else
685*adc586deSMark Johnson 	uus.ucodep = (uint8_t *)ucodefp;
686*adc586deSMark Johnson 	uus.usize = sizeof (*ucodefp);
687*adc586deSMark Johnson 	ucode_load_xpv(&uus);
688*adc586deSMark Johnson 	ucode->read_rev(uinfop);
689*adc586deSMark Johnson 	uus.new_rev = uinfop->cui_rev;
690*adc586deSMark Johnson #endif
691*adc586deSMark Johnson 
692*adc586deSMark Johnson 	return (ucodefp->uf_header.uh_patch_id);
693*adc586deSMark Johnson }
694*adc586deSMark Johnson 
695*adc586deSMark Johnson /*ARGSUSED2*/
696*adc586deSMark Johnson static uint32_t
697*adc586deSMark Johnson ucode_load_intel(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp)
698*adc586deSMark Johnson {
699*adc586deSMark Johnson 	ucode_file_intel_t *ucodefp = &ufp->intel;
700*adc586deSMark Johnson #ifdef __xpv
701*adc586deSMark Johnson 	uint32_t ext_offset;
702*adc586deSMark Johnson 	uint32_t body_size;
703*adc586deSMark Johnson 	uint32_t ext_size;
704*adc586deSMark Johnson 	uint8_t *ustart;
705*adc586deSMark Johnson 	uint32_t usize;
706*adc586deSMark Johnson 	ucode_update_t uus;
707*adc586deSMark Johnson #endif
708*adc586deSMark Johnson 
709*adc586deSMark Johnson 	ASSERT(ucode);
710*adc586deSMark Johnson 
711*adc586deSMark Johnson #ifdef __xpv
712*adc586deSMark Johnson 	/*
713*adc586deSMark Johnson 	 * the hypervisor wants the header, data, and extended
714*adc586deSMark Johnson 	 * signature tables. We can only get here from the boot
715*adc586deSMark Johnson 	 * CPU (cpu #0), we don't need to free as ucode_zalloc() will
716*adc586deSMark Johnson 	 * use BOP_ALLOC().
717*adc586deSMark Johnson 	 */
718*adc586deSMark Johnson 	usize = UCODE_TOTAL_SIZE_INTEL(ucodefp->uf_header->uh_total_size);
719*adc586deSMark Johnson 	ustart = ucode_zalloc(cp->cpu_id, usize);
720*adc586deSMark Johnson 	ASSERT(ustart);
721*adc586deSMark Johnson 
722*adc586deSMark Johnson 	body_size = UCODE_BODY_SIZE_INTEL(ucodefp->uf_header->uh_body_size);
723*adc586deSMark Johnson 	ext_offset = body_size + UCODE_HEADER_SIZE_INTEL;
724*adc586deSMark Johnson 	ext_size = usize - ext_offset;
725*adc586deSMark Johnson 	ASSERT(ext_size >= 0);
726*adc586deSMark Johnson 
727*adc586deSMark Johnson 	(void) memcpy(ustart, ucodefp->uf_header, UCODE_HEADER_SIZE_INTEL);
728*adc586deSMark Johnson 	(void) memcpy(&ustart[UCODE_HEADER_SIZE_INTEL], ucodefp->uf_body,
729*adc586deSMark Johnson 	    body_size);
730*adc586deSMark Johnson 	if (ext_size > 0) {
731*adc586deSMark Johnson 		(void) memcpy(&ustart[ext_offset],
732*adc586deSMark Johnson 		    ucodefp->uf_ext_table, ext_size);
733*adc586deSMark Johnson 	}
734*adc586deSMark Johnson 	uus.ucodep = ustart;
735*adc586deSMark Johnson 	uus.usize = usize;
736*adc586deSMark Johnson 	ucode_load_xpv(&uus);
737*adc586deSMark Johnson 	ucode->read_rev(uinfop);
738*adc586deSMark Johnson 	uus.new_rev = uinfop->cui_rev;
739*adc586deSMark Johnson #else
740*adc586deSMark Johnson 	kpreempt_disable();
741*adc586deSMark Johnson 	wrmsr(ucode->write_msr, (uintptr_t)ucodefp->uf_body);
742*adc586deSMark Johnson 	ucode->read_rev(uinfop);
743*adc586deSMark Johnson 	kpreempt_enable();
744*adc586deSMark Johnson #endif
745*adc586deSMark Johnson 
746*adc586deSMark Johnson 	return (ucodefp->uf_header->uh_rev);
7472449e17fSsherrym }
7482449e17fSsherrym 
749882a7af5SMark Johnson 
750882a7af5SMark Johnson #ifdef	__xpv
751882a7af5SMark Johnson static void
752*adc586deSMark Johnson ucode_load_xpv(ucode_update_t *uusp)
753882a7af5SMark Johnson {
754882a7af5SMark Johnson 	xen_platform_op_t op;
755882a7af5SMark Johnson 	int e;
756882a7af5SMark Johnson 
757882a7af5SMark Johnson 	ASSERT(DOMAIN_IS_INITDOMAIN(xen_info));
758882a7af5SMark Johnson 
759882a7af5SMark Johnson 	kpreempt_disable();
760882a7af5SMark Johnson 	op.cmd = XENPF_microcode_update;
761882a7af5SMark Johnson 	op.interface_version = XENPF_INTERFACE_VERSION;
762882a7af5SMark Johnson 	/*LINTED: constant in conditional context*/
763*adc586deSMark Johnson 	set_xen_guest_handle(op.u.microcode.data, uusp->ucodep);
764*adc586deSMark Johnson 	op.u.microcode.length = uusp->usize;
765882a7af5SMark Johnson 	e = HYPERVISOR_platform_op(&op);
766882a7af5SMark Johnson 	if (e != 0) {
767882a7af5SMark Johnson 		cmn_err(CE_WARN, "hypervisor failed to accept uCode update");
768882a7af5SMark Johnson 	}
769882a7af5SMark Johnson 	kpreempt_enable();
770882a7af5SMark Johnson }
771882a7af5SMark Johnson #endif /* __xpv */
772882a7af5SMark Johnson 
773*adc586deSMark Johnson static void
774*adc586deSMark Johnson ucode_read_rev_amd(cpu_ucode_info_t *uinfop)
775*adc586deSMark Johnson {
776*adc586deSMark Johnson 	uinfop->cui_rev = rdmsr(MSR_AMD_PATCHLEVEL);
777*adc586deSMark Johnson }
778882a7af5SMark Johnson 
7792449e17fSsherrym static void
780*adc586deSMark Johnson ucode_read_rev_intel(cpu_ucode_info_t *uinfop)
7812449e17fSsherrym {
7822449e17fSsherrym 	struct cpuid_regs crs;
7832449e17fSsherrym 
7842449e17fSsherrym 	/*
7852449e17fSsherrym 	 * The Intel 64 and IA-32 Architecture Software Developer's Manual
7862449e17fSsherrym 	 * recommends that MSR_INTC_UCODE_REV be loaded with 0 first, then
7872449e17fSsherrym 	 * execute cpuid to guarantee the correct reading of this register.
7882449e17fSsherrym 	 */
7892449e17fSsherrym 	wrmsr(MSR_INTC_UCODE_REV, 0);
7902449e17fSsherrym 	(void) __cpuid_insn(&crs);
7912449e17fSsherrym 	uinfop->cui_rev = (rdmsr(MSR_INTC_UCODE_REV) >> INTC_UCODE_REV_SHIFT);
7922449e17fSsherrym }
7932449e17fSsherrym 
794*adc586deSMark Johnson static ucode_errno_t
795*adc586deSMark Johnson ucode_extract_amd(ucode_update_t *uusp, uint8_t *ucodep, int size)
796*adc586deSMark Johnson {
797*adc586deSMark Johnson 	uint32_t *ptr = (uint32_t *)ucodep;
798*adc586deSMark Johnson 	ucode_eqtbl_amd_t *eqtbl;
799*adc586deSMark Johnson 	ucode_file_amd_t *ufp;
800*adc586deSMark Johnson 	int count, eq_sig;
801*adc586deSMark Johnson 
802*adc586deSMark Johnson 	/* skip over magic number & equivalence table header */
803*adc586deSMark Johnson 	ptr += 2; size -= 8;
804*adc586deSMark Johnson 
805*adc586deSMark Johnson 	count = *ptr++; size -= 4;
806*adc586deSMark Johnson 	for (eqtbl = (ucode_eqtbl_amd_t *)ptr;
807*adc586deSMark Johnson 	    eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != uusp->sig;
808*adc586deSMark Johnson 	    eqtbl++)
809*adc586deSMark Johnson 		;
810*adc586deSMark Johnson 
811*adc586deSMark Johnson 	eq_sig = eqtbl->ue_equiv_cpu;
812*adc586deSMark Johnson 	eq_sig = ((eq_sig >> 8) & 0xff00) | (eq_sig & 0xff);
813*adc586deSMark Johnson 
814*adc586deSMark Johnson 	/* No equivalent CPU id found, assume outdated microcode file. */
815*adc586deSMark Johnson 	if (eq_sig == 0)
816*adc586deSMark Johnson 		return (EM_HIGHERREV);
817*adc586deSMark Johnson 
818*adc586deSMark Johnson 	/* Use the first microcode patch that matches. */
819*adc586deSMark Johnson 	do {
820*adc586deSMark Johnson 		ptr += count >> 2; size -= count;
821*adc586deSMark Johnson 
822*adc586deSMark Johnson 		if (!size)
823*adc586deSMark Johnson 			return (EM_NOMATCH);
824*adc586deSMark Johnson 
825*adc586deSMark Johnson 		ptr++; size -= 4;
826*adc586deSMark Johnson 		count = *ptr++; size -= 4;
827*adc586deSMark Johnson 		ufp = (ucode_file_amd_t *)ptr;
828*adc586deSMark Johnson 	} while (ucode_match_amd(eq_sig, &uusp->info, ufp, count) != EM_OK);
829*adc586deSMark Johnson 
830*adc586deSMark Johnson 	uusp->ucodep = (uint8_t *)ufp;
831*adc586deSMark Johnson 	uusp->usize = count;
832*adc586deSMark Johnson 	uusp->expected_rev = ufp->uf_header.uh_patch_id;
833*adc586deSMark Johnson 
834*adc586deSMark Johnson 	return (EM_OK);
835*adc586deSMark Johnson }
836*adc586deSMark Johnson 
837*adc586deSMark Johnson static ucode_errno_t
838*adc586deSMark Johnson ucode_extract_intel(ucode_update_t *uusp, uint8_t *ucodep, int size)
839*adc586deSMark Johnson {
840*adc586deSMark Johnson 	uint32_t	header_size = UCODE_HEADER_SIZE_INTEL;
841*adc586deSMark Johnson 	int		remaining;
842*adc586deSMark Johnson 	int		found = 0;
843*adc586deSMark Johnson 	ucode_errno_t	search_rc = EM_NOMATCH; /* search result */
844*adc586deSMark Johnson 
845*adc586deSMark Johnson 	/*
846*adc586deSMark Johnson 	 * Go through the whole buffer in case there are
847*adc586deSMark Johnson 	 * multiple versions of matching microcode for this
848*adc586deSMark Johnson 	 * processor.
849*adc586deSMark Johnson 	 */
850*adc586deSMark Johnson 	for (remaining = size; remaining > 0; ) {
851*adc586deSMark Johnson 		int	total_size, body_size, ext_size;
852*adc586deSMark Johnson 		uint8_t	*curbuf = &ucodep[size - remaining];
853*adc586deSMark Johnson 		ucode_header_intel_t *uhp = (ucode_header_intel_t *)curbuf;
854*adc586deSMark Johnson 		ucode_ext_table_intel_t *uetp = NULL;
855*adc586deSMark Johnson 		ucode_errno_t tmprc;
856*adc586deSMark Johnson 
857*adc586deSMark Johnson 		total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
858*adc586deSMark Johnson 		body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size);
859*adc586deSMark Johnson 		ext_size = total_size - (header_size + body_size);
860*adc586deSMark Johnson 
861*adc586deSMark Johnson 		if (ext_size > 0)
862*adc586deSMark Johnson 			uetp = (ucode_ext_table_intel_t *)
863*adc586deSMark Johnson 			    &curbuf[header_size + body_size];
864*adc586deSMark Johnson 
865*adc586deSMark Johnson 		tmprc = ucode_match_intel(uusp->sig, &uusp->info, uhp, uetp);
866*adc586deSMark Johnson 
867*adc586deSMark Johnson 		/*
868*adc586deSMark Johnson 		 * Since we are searching through a big file
869*adc586deSMark Johnson 		 * containing microcode for pretty much all the
870*adc586deSMark Johnson 		 * processors, we are bound to get EM_NOMATCH
871*adc586deSMark Johnson 		 * at one point.  However, if we return
872*adc586deSMark Johnson 		 * EM_NOMATCH to users, it will really confuse
873*adc586deSMark Johnson 		 * them.  Therefore, if we ever find a match of
874*adc586deSMark Johnson 		 * a lower rev, we will set return code to
875*adc586deSMark Johnson 		 * EM_HIGHERREV.
876*adc586deSMark Johnson 		 */
877*adc586deSMark Johnson 		if (tmprc == EM_HIGHERREV)
878*adc586deSMark Johnson 			search_rc = EM_HIGHERREV;
879*adc586deSMark Johnson 
880*adc586deSMark Johnson 		if (tmprc == EM_OK &&
881*adc586deSMark Johnson 		    uusp->expected_rev < uhp->uh_rev) {
882*adc586deSMark Johnson #ifndef __xpv
883*adc586deSMark Johnson 			uusp->ucodep = (uint8_t *)&curbuf[header_size];
884*adc586deSMark Johnson #else
885*adc586deSMark Johnson 			uusp->ucodep = (uint8_t *)curbuf;
886*adc586deSMark Johnson #endif
887*adc586deSMark Johnson 			uusp->usize =
888*adc586deSMark Johnson 			    UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
889*adc586deSMark Johnson 			uusp->expected_rev = uhp->uh_rev;
890*adc586deSMark Johnson 			found = 1;
891*adc586deSMark Johnson 		}
892*adc586deSMark Johnson 
893*adc586deSMark Johnson 		remaining -= total_size;
894*adc586deSMark Johnson 	}
895*adc586deSMark Johnson 
896*adc586deSMark Johnson 	if (!found)
897*adc586deSMark Johnson 		return (search_rc);
898*adc586deSMark Johnson 
899*adc586deSMark Johnson 	return (EM_OK);
900*adc586deSMark Johnson }
9012449e17fSsherrym /*
9022449e17fSsherrym  * Entry point to microcode update from the ucode_drv driver.
9032449e17fSsherrym  *
9042449e17fSsherrym  * Returns EM_OK on success, corresponding error code on failure.
9052449e17fSsherrym  */
9062449e17fSsherrym ucode_errno_t
9072449e17fSsherrym ucode_update(uint8_t *ucodep, int size)
9082449e17fSsherrym {
9092449e17fSsherrym 	int		found = 0;
9102449e17fSsherrym 	processorid_t	id;
911*adc586deSMark Johnson 	ucode_update_t	cached = { 0 };
912*adc586deSMark Johnson 	ucode_update_t	*cachedp = NULL;
9132449e17fSsherrym 	ucode_errno_t	rc = EM_OK;
9142449e17fSsherrym 	ucode_errno_t	search_rc = EM_NOMATCH; /* search result */
9152449e17fSsherrym 	cpuset_t cpuset;
9162449e17fSsherrym 
917*adc586deSMark Johnson 	ASSERT(ucode);
9182449e17fSsherrym 	ASSERT(ucodep);
9192449e17fSsherrym 	CPUSET_ZERO(cpuset);
9202449e17fSsherrym 
921*adc586deSMark Johnson 	if (!ucode->capable(CPU))
9222449e17fSsherrym 		return (EM_NOTSUP);
9232449e17fSsherrym 
9242449e17fSsherrym 	mutex_enter(&cpu_lock);
9252449e17fSsherrym 
9262449e17fSsherrym 	for (id = 0; id < max_ncpus; id++) {
9272449e17fSsherrym 		cpu_t *cpu;
928*adc586deSMark Johnson 		ucode_update_t uus = { 0 };
929*adc586deSMark Johnson 		ucode_update_t *uusp = &uus;
9302449e17fSsherrym 
9312449e17fSsherrym 		/*
9322449e17fSsherrym 		 * If there is no such CPU or it is not xcall ready, skip it.
9332449e17fSsherrym 		 */
9342449e17fSsherrym 		if ((cpu = cpu_get(id)) == NULL ||
9352449e17fSsherrym 		    !(cpu->cpu_flags & CPU_READY))
9362449e17fSsherrym 			continue;
9372449e17fSsherrym 
9382449e17fSsherrym 		uusp->sig = cpuid_getsig(cpu);
9392449e17fSsherrym 		bcopy(cpu->cpu_m.mcpu_ucode_info, &uusp->info,
9402449e17fSsherrym 		    sizeof (uusp->info));
9412449e17fSsherrym 
9422449e17fSsherrym 		/*
9432449e17fSsherrym 		 * If the current CPU has the same signature and platform
9442449e17fSsherrym 		 * id as the previous one we processed, reuse the information.
9452449e17fSsherrym 		 */
9462449e17fSsherrym 		if (cachedp && cachedp->sig == cpuid_getsig(cpu) &&
9472449e17fSsherrym 		    cachedp->info.cui_platid == uusp->info.cui_platid) {
9482449e17fSsherrym 			uusp->ucodep = cachedp->ucodep;
9492449e17fSsherrym 			uusp->expected_rev = cachedp->expected_rev;
9502449e17fSsherrym 			/*
9512449e17fSsherrym 			 * Intuitively we should check here to see whether the
9522449e17fSsherrym 			 * running microcode rev is >= the expected rev, and
9532449e17fSsherrym 			 * quit if it is.  But we choose to proceed with the
9542449e17fSsherrym 			 * xcall regardless of the running version so that
9552449e17fSsherrym 			 * the other threads in an HT processor can update
9562449e17fSsherrym 			 * the cpu_ucode_info structure in machcpu.
9572449e17fSsherrym 			 */
958*adc586deSMark Johnson 		} else if ((search_rc = ucode->extract(uusp, ucodep, size))
959*adc586deSMark Johnson 		    == EM_OK) {
9602449e17fSsherrym 			bcopy(uusp, &cached, sizeof (cached));
9612449e17fSsherrym 			cachedp = &cached;
9622449e17fSsherrym 			found = 1;
9632449e17fSsherrym 		}
9642449e17fSsherrym 
9652449e17fSsherrym 		/* Nothing to do */
9662449e17fSsherrym 		if (uusp->ucodep == NULL)
9672449e17fSsherrym 			continue;
9682449e17fSsherrym 
969882a7af5SMark Johnson #ifdef	__xpv
970882a7af5SMark Johnson 		/*
971882a7af5SMark Johnson 		 * for i86xpv, the hypervisor will update all the CPUs.
972882a7af5SMark Johnson 		 * the hypervisor wants the header, data, and extended
973882a7af5SMark Johnson 		 * signature tables. ucode_write will just read in the
974882a7af5SMark Johnson 		 * updated version on all the CPUs after the update has
975882a7af5SMark Johnson 		 * completed.
976882a7af5SMark Johnson 		 */
977c9b5d7d2SMark Johnson 		if (id == 0) {
978*adc586deSMark Johnson 			ucode_load_xpv(uusp);
979c9b5d7d2SMark Johnson 		}
980882a7af5SMark Johnson #endif
981882a7af5SMark Johnson 
9822449e17fSsherrym 		CPUSET_ADD(cpuset, id);
9832449e17fSsherrym 		kpreempt_disable();
9842449e17fSsherrym 		xc_sync((xc_arg_t)uusp, 0, 0, X_CALL_HIPRI, cpuset,
9852449e17fSsherrym 		    ucode_write);
9862449e17fSsherrym 		kpreempt_enable();
9872449e17fSsherrym 		CPUSET_DEL(cpuset, id);
9882449e17fSsherrym 
9892449e17fSsherrym 		if (uusp->expected_rev == uusp->new_rev) {
9902449e17fSsherrym 			cmn_err(CE_CONT, ucode_success_fmt,
9912449e17fSsherrym 			    id, uusp->info.cui_rev, uusp->expected_rev);
9922449e17fSsherrym 		} else {
9932449e17fSsherrym 			cmn_err(CE_WARN, ucode_failure_fmt,
9942449e17fSsherrym 			    id, uusp->info.cui_rev, uusp->expected_rev);
9952449e17fSsherrym 			rc = EM_UPDATE;
9962449e17fSsherrym 		}
9972449e17fSsherrym 	}
9982449e17fSsherrym 
9992449e17fSsherrym 	mutex_exit(&cpu_lock);
10002449e17fSsherrym 
10012449e17fSsherrym 	if (!found)
10022449e17fSsherrym 		rc = search_rc;
10032449e17fSsherrym 
10042449e17fSsherrym 	return (rc);
10052449e17fSsherrym }
10062449e17fSsherrym 
10072449e17fSsherrym /*
10082449e17fSsherrym  * Initialize mcpu_ucode_info, and perform microcode update if necessary.
10092449e17fSsherrym  * This is the entry point from boot path where pointer to CPU structure
10102449e17fSsherrym  * is available.
10112449e17fSsherrym  *
10122449e17fSsherrym  * cpuid_info must be initialized before ucode_check can be called.
10132449e17fSsherrym  */
10142449e17fSsherrym void
10152449e17fSsherrym ucode_check(cpu_t *cp)
10162449e17fSsherrym {
1017*adc586deSMark Johnson 	cpu_ucode_info_t *uinfop;
10182449e17fSsherrym 	ucode_errno_t rc = EM_OK;
1019*adc586deSMark Johnson 	uint32_t new_rev = 0;
10202449e17fSsherrym 
10212449e17fSsherrym 	ASSERT(cp);
10222449e17fSsherrym 	if (cp->cpu_id == 0)
10232449e17fSsherrym 		cp->cpu_m.mcpu_ucode_info = &cpu_ucode_info0;
10242449e17fSsherrym 
10252449e17fSsherrym 	uinfop = cp->cpu_m.mcpu_ucode_info;
10262449e17fSsherrym 	ASSERT(uinfop);
10272449e17fSsherrym 
1028*adc586deSMark Johnson 	/* set up function pointers if not already done */
1029*adc586deSMark Johnson 	if (!ucode)
1030*adc586deSMark Johnson 		switch (cpuid_getvendor(cp)) {
1031*adc586deSMark Johnson 		case X86_VENDOR_AMD:
1032*adc586deSMark Johnson 			ucode = &ucode_amd;
1033*adc586deSMark Johnson 			break;
1034*adc586deSMark Johnson 		case X86_VENDOR_Intel:
1035*adc586deSMark Johnson 			ucode = &ucode_intel;
1036*adc586deSMark Johnson 			break;
1037*adc586deSMark Johnson 		default:
1038*adc586deSMark Johnson 			return;
1039*adc586deSMark Johnson 		}
1040*adc586deSMark Johnson 
1041*adc586deSMark Johnson 	if (!ucode->capable(cp))
10422449e17fSsherrym 		return;
10432449e17fSsherrym 
10442449e17fSsherrym 	/*
10452449e17fSsherrym 	 * The MSR_INTC_PLATFORM_ID is supported in Celeron and Xeon
10462449e17fSsherrym 	 * (Family 6, model 5 and above) and all processors after.
10472449e17fSsherrym 	 */
1048*adc586deSMark Johnson 	if ((cpuid_getvendor(cp) == X86_VENDOR_Intel) &&
1049*adc586deSMark Johnson 	    ((cpuid_getmodel(cp) >= 5) || (cpuid_getfamily(cp) > 6))) {
10502449e17fSsherrym 		uinfop->cui_platid = 1 << ((rdmsr(MSR_INTC_PLATFORM_ID) >>
10512449e17fSsherrym 		    INTC_PLATFORM_ID_SHIFT) & INTC_PLATFORM_ID_MASK);
10522449e17fSsherrym 	}
10532449e17fSsherrym 
1054*adc586deSMark Johnson 	ucode->read_rev(uinfop);
10552449e17fSsherrym 
1056c9b5d7d2SMark Johnson #ifdef	__xpv
1057c9b5d7d2SMark Johnson 	/*
1058c9b5d7d2SMark Johnson 	 * for i86xpv, the hypervisor will update all the CPUs. We only need
1059c9b5d7d2SMark Johnson 	 * do do this on one of the CPUs (and there always is a CPU 0).
1060c9b5d7d2SMark Johnson 	 */
1061c9b5d7d2SMark Johnson 	if (cp->cpu_id != 0) {
1062c9b5d7d2SMark Johnson 		return;
1063c9b5d7d2SMark Johnson 	}
1064c9b5d7d2SMark Johnson #endif
1065c9b5d7d2SMark Johnson 
10662449e17fSsherrym 	/*
10672449e17fSsherrym 	 * Check to see if we need ucode update
10682449e17fSsherrym 	 */
1069*adc586deSMark Johnson 	if ((rc = ucode->locate(cp, uinfop, &ucodefile)) == EM_OK) {
1070*adc586deSMark Johnson 		new_rev = ucode->load(&ucodefile, uinfop, cp);
1071882a7af5SMark Johnson 
1072*adc586deSMark Johnson 		if (uinfop->cui_rev != new_rev)
10732449e17fSsherrym 			cmn_err(CE_WARN, ucode_failure_fmt, cp->cpu_id,
1074*adc586deSMark Johnson 			    uinfop->cui_rev, new_rev);
10752449e17fSsherrym 	}
10762449e17fSsherrym 
10772449e17fSsherrym 	/*
10782449e17fSsherrym 	 * If we fail to find a match for any reason, free the file structure
10792449e17fSsherrym 	 * just in case we have read in a partial file.
10802449e17fSsherrym 	 *
10812449e17fSsherrym 	 * Since the scratch memory for holding the microcode for the boot CPU
10822449e17fSsherrym 	 * came from BOP_ALLOC, we will reset the data structure as if we
10832449e17fSsherrym 	 * never did the allocation so we don't have to keep track of this
10842449e17fSsherrym 	 * special chunk of memory.  We free the memory used for the rest
10852449e17fSsherrym 	 * of the CPUs in start_other_cpus().
10862449e17fSsherrym 	 */
10872449e17fSsherrym 	if (rc != EM_OK || cp->cpu_id == 0)
1088*adc586deSMark Johnson 		ucode->file_reset(&ucodefile, cp->cpu_id);
10892449e17fSsherrym }
10902449e17fSsherrym 
10912449e17fSsherrym /*
10922449e17fSsherrym  * Returns microcode revision from the machcpu structure.
10932449e17fSsherrym  */
10942449e17fSsherrym ucode_errno_t
10952449e17fSsherrym ucode_get_rev(uint32_t *revp)
10962449e17fSsherrym {
10972449e17fSsherrym 	int i;
10982449e17fSsherrym 
1099*adc586deSMark Johnson 	ASSERT(ucode);
11002449e17fSsherrym 	ASSERT(revp);
11012449e17fSsherrym 
1102*adc586deSMark Johnson 	if (!ucode->capable(CPU))
11032449e17fSsherrym 		return (EM_NOTSUP);
11042449e17fSsherrym 
11052449e17fSsherrym 	mutex_enter(&cpu_lock);
11062449e17fSsherrym 	for (i = 0; i < max_ncpus; i++) {
11072449e17fSsherrym 		cpu_t *cpu;
11082449e17fSsherrym 
11092449e17fSsherrym 		if ((cpu = cpu_get(i)) == NULL)
11102449e17fSsherrym 			continue;
11112449e17fSsherrym 
11122449e17fSsherrym 		revp[i] = cpu->cpu_m.mcpu_ucode_info->cui_rev;
11132449e17fSsherrym 	}
11142449e17fSsherrym 	mutex_exit(&cpu_lock);
11152449e17fSsherrym 
11162449e17fSsherrym 	return (EM_OK);
11172449e17fSsherrym }
1118