12449e17sherrym/*
22449e17sherrym * CDDL HEADER START
32449e17sherrym *
42449e17sherrym * The contents of this file are subject to the terms of the
52449e17sherrym * Common Development and Distribution License (the "License").
62449e17sherrym * You may not use this file except in compliance with the License.
72449e17sherrym *
82449e17sherrym * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92449e17sherrym * or http://www.opensolaris.org/os/licensing.
102449e17sherrym * See the License for the specific language governing permissions
112449e17sherrym * and limitations under the License.
122449e17sherrym *
132449e17sherrym * When distributing Covered Code, include this CDDL HEADER in each
142449e17sherrym * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152449e17sherrym * If applicable, add the following below this CDDL HEADER, with the
162449e17sherrym * fields enclosed by brackets "[]" replaced with your own identifying
172449e17sherrym * information: Portions Copyright [yyyy] [name of copyright owner]
182449e17sherrym *
192449e17sherrym * CDDL HEADER END
202449e17sherrym */
212449e17sherrym
222449e17sherrym/*
230ba6f73Mark Johnson * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
242449e17sherrym * Use is subject to license terms.
2579ec9daYuri Pankov *
2679ec9daYuri Pankov * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
27f226ceeRobert Mustacchi * Copyright (c) 2018, Joyent, Inc.
282449e17sherrym */
292449e17sherrym
302449e17sherrym#include <sys/asm_linkage.h>
312449e17sherrym#include <sys/bootconf.h>
322449e17sherrym#include <sys/cpuvar.h>
332449e17sherrym#include <sys/cmn_err.h>
342449e17sherrym#include <sys/controlregs.h>
352449e17sherrym#include <sys/debug.h>
362449e17sherrym#include <sys/kobj.h>
372449e17sherrym#include <sys/kobj_impl.h>
382449e17sherrym#include <sys/machsystm.h>
395e53ed3Hans Rosenfeld#include <sys/ontrap.h>
402449e17sherrym#include <sys/param.h>
412449e17sherrym#include <sys/machparam.h>
422449e17sherrym#include <sys/promif.h>
432449e17sherrym#include <sys/sysmacros.h>
442449e17sherrym#include <sys/systm.h>
452449e17sherrym#include <sys/types.h>
462449e17sherrym#include <sys/thread.h>
472449e17sherrym#include <sys/ucode.h>
482449e17sherrym#include <sys/x86_archext.h>
492449e17sherrym#include <sys/x_call.h>
50843e198johnlev#ifdef	__xpv
51843e198johnlev#include <sys/hypervisor.h>
52843e198johnlev#endif
532449e17sherrym
542449e17sherrym/*
55adc586dMark Johnson * AMD-specific equivalence table
562449e17sherrym */
57adc586dMark Johnsonstatic ucode_eqtbl_amd_t *ucode_eqtbl_amd;
582449e17sherrym
592449e17sherrym/*
602449e17sherrym * mcpu_ucode_info for the boot CPU.  Statically allocated.
612449e17sherrym */
622449e17sherrymstatic struct cpu_ucode_info cpu_ucode_info0;
632449e17sherrym
64adc586dMark Johnsonstatic ucode_file_t ucodefile;
65adc586dMark Johnson
66adc586dMark Johnsonstatic void* ucode_zalloc(processorid_t, size_t);
67adc586dMark Johnsonstatic void ucode_free(processorid_t, void *, size_t);
68adc586dMark Johnson
69adc586dMark Johnsonstatic int ucode_capable_amd(cpu_t *);
70adc586dMark Johnsonstatic int ucode_capable_intel(cpu_t *);
71adc586dMark Johnson
72adc586dMark Johnsonstatic ucode_errno_t ucode_extract_amd(ucode_update_t *, uint8_t *, int);
73adc586dMark Johnsonstatic ucode_errno_t ucode_extract_intel(ucode_update_t *, uint8_t *,
74adc586dMark Johnson    int);
75adc586dMark Johnson
76adc586dMark Johnsonstatic void ucode_file_reset_amd(ucode_file_t *, processorid_t);
77adc586dMark Johnsonstatic void ucode_file_reset_intel(ucode_file_t *, processorid_t);
78adc586dMark Johnson
79adc586dMark Johnsonstatic uint32_t ucode_load_amd(ucode_file_t *, cpu_ucode_info_t *, cpu_t *);
80adc586dMark Johnsonstatic uint32_t ucode_load_intel(ucode_file_t *, cpu_ucode_info_t *, cpu_t *);
812449e17sherrym
82882a7afMark Johnson#ifdef	__xpv
83adc586dMark Johnsonstatic void ucode_load_xpv(ucode_update_t *);
840ba6f73Mark Johnsonstatic void ucode_chipset_amd(uint8_t *, int);
85882a7afMark Johnson#endif
862449e17sherrym
870ba6f73Mark Johnsonstatic int ucode_equiv_cpu_amd(cpu_t *, uint16_t *);
88adc586dMark Johnson
89adc586dMark Johnsonstatic ucode_errno_t ucode_locate_amd(cpu_t *, cpu_ucode_info_t *,
90adc586dMark Johnson    ucode_file_t *);
91adc586dMark Johnsonstatic ucode_errno_t ucode_locate_intel(cpu_t *, cpu_ucode_info_t *,
92adc586dMark Johnson    ucode_file_t *);
93adc586dMark Johnson
940ba6f73Mark Johnson#ifndef __xpv
950ba6f73Mark Johnsonstatic ucode_errno_t ucode_match_amd(uint16_t, cpu_ucode_info_t *,
96adc586dMark Johnson    ucode_file_amd_t *, int);
970ba6f73Mark Johnson#endif
98adc586dMark Johnsonstatic ucode_errno_t ucode_match_intel(int, cpu_ucode_info_t *,
99adc586dMark Johnson    ucode_header_intel_t *, ucode_ext_table_intel_t *);
100adc586dMark Johnson
101adc586dMark Johnsonstatic void ucode_read_rev_amd(cpu_ucode_info_t *);
102adc586dMark Johnsonstatic void ucode_read_rev_intel(cpu_ucode_info_t *);
103adc586dMark Johnson
104adc586dMark Johnsonstatic const struct ucode_ops ucode_amd = {
105adc586dMark Johnson	MSR_AMD_PATCHLOADER,
106adc586dMark Johnson	ucode_capable_amd,
107adc586dMark Johnson	ucode_file_reset_amd,
108adc586dMark Johnson	ucode_read_rev_amd,
109adc586dMark Johnson	ucode_load_amd,
110adc586dMark Johnson	ucode_validate_amd,
111adc586dMark Johnson	ucode_extract_amd,
112adc586dMark Johnson	ucode_locate_amd
113adc586dMark Johnson};
114adc586dMark Johnson
115adc586dMark Johnsonstatic const struct ucode_ops ucode_intel = {
116adc586dMark Johnson	MSR_INTC_UCODE_WRITE,
117adc586dMark Johnson	ucode_capable_intel,
118adc586dMark Johnson	ucode_file_reset_intel,
119adc586dMark Johnson	ucode_read_rev_intel,
120adc586dMark Johnson	ucode_load_intel,
121adc586dMark Johnson	ucode_validate_intel,
122adc586dMark Johnson	ucode_extract_intel,
123adc586dMark Johnson	ucode_locate_intel
124adc586dMark Johnson};
125adc586dMark Johnson
126adc586dMark Johnsonconst struct ucode_ops *ucode;
127adc586dMark Johnson
1282449e17sherrymstatic const char ucode_failure_fmt[] =
12988699bdsherrym	"cpu%d: failed to update microcode from version 0x%x to 0x%x\n";
1302449e17sherrymstatic const char ucode_success_fmt[] =
13188699bdsherrym	"?cpu%d: microcode has been updated from version 0x%x to 0x%x\n";
1322449e17sherrym
1332449e17sherrym/*
1342449e17sherrym * Force flag.  If set, the first microcode binary that matches
1352449e17sherrym * signature and platform id will be used for microcode update,
1362449e17sherrym * regardless of version.  Should only be used for debugging.
1372449e17sherrym */
1382449e17sherrymint ucode_force_update = 0;
1392449e17sherrym
1402449e17sherrym/*
1412449e17sherrym * Allocate space for mcpu_ucode_info in the machcpu structure
1422449e17sherrym * for all non-boot CPUs.
1432449e17sherrym */
1442449e17sherrymvoid
1452449e17sherrymucode_alloc_space(cpu_t *cp)
1462449e17sherrym{
1472449e17sherrym	ASSERT(cp->cpu_id != 0);
148a311483Gerry Liu	ASSERT(cp->cpu_m.mcpu_ucode_info == NULL);
1492449e17sherrym	cp->cpu_m.mcpu_ucode_info =
1502449e17sherrym	    kmem_zalloc(sizeof (*cp->cpu_m.mcpu_ucode_info), KM_SLEEP);
1512449e17sherrym}
1522449e17sherrym
1532449e17sherrymvoid
1542449e17sherrymucode_free_space(cpu_t *cp)
1552449e17sherrym{
156a311483Gerry Liu	ASSERT(cp->cpu_m.mcpu_ucode_info != NULL);
157a311483Gerry Liu	ASSERT(cp->cpu_m.mcpu_ucode_info != &cpu_ucode_info0);
1582449e17sherrym	kmem_free(cp->cpu_m.mcpu_ucode_info,
1592449e17sherrym	    sizeof (*cp->cpu_m.mcpu_ucode_info));
160a311483Gerry Liu	cp->cpu_m.mcpu_ucode_info = NULL;
1612449e17sherrym}
1622449e17sherrym
1632449e17sherrym/*
1642449e17sherrym * Called when we are done with microcode update on all processors to free up
1652449e17sherrym * space allocated for the microcode file.
1662449e17sherrym */
1672449e17sherrymvoid
168adc586dMark Johnsonucode_cleanup()
169adc586dMark Johnson{
170126b0a7Mark Johnson	if (ucode == NULL)
171126b0a7Mark Johnson		return;
172adc586dMark Johnson
173adc586dMark Johnson	ucode->file_reset(&ucodefile, -1);
174adc586dMark Johnson}
175adc586dMark Johnson
176adc586dMark Johnson/*
177adc586dMark Johnson * Allocate/free a buffer used to hold ucode data. Space for the boot CPU is
178adc586dMark Johnson * allocated with BOP_ALLOC() and does not require a free.
179adc586dMark Johnson */
180adc586dMark Johnsonstatic void*
181adc586dMark Johnsonucode_zalloc(processorid_t id, size_t size)
182adc586dMark Johnson{
183adc586dMark Johnson	if (id)
184adc586dMark Johnson		return (kmem_zalloc(size, KM_NOSLEEP));
185adc586dMark Johnson
186adc586dMark Johnson	/* BOP_ALLOC() failure results in panic */
187adc586dMark Johnson	return (BOP_ALLOC(bootops, NULL, size, MMU_PAGESIZE));
188adc586dMark Johnson}
189adc586dMark Johnson
190adc586dMark Johnsonstatic void
191adc586dMark Johnsonucode_free(processorid_t id, void* buf, size_t size)
1922449e17sherrym{
193adc586dMark Johnson	if (id)
194adc586dMark Johnson		kmem_free(buf, size);
1952449e17sherrym}
1962449e17sherrym
1972449e17sherrym/*
1982449e17sherrym * Check whether or not a processor is capable of microcode operations
1992449e17sherrym * Returns 1 if it is capable, 0 if not.
200adc586dMark Johnson *
201adc586dMark Johnson * At this point we only support microcode update for:
202adc586dMark Johnson * - Intel processors family 6 and above, and
203adc586dMark Johnson * - AMD processors family 0x10 and above.
204adc586dMark Johnson *
205adc586dMark Johnson * We also assume that we don't support a mix of Intel and
206adc586dMark Johnson * AMD processors in the same box.
207adc586dMark Johnson *
20879ec9daYuri Pankov * An i86xpv guest domain or VM can't update the microcode.
2092449e17sherrym */
21079ec9daYuri Pankov
21179ec9daYuri Pankov#define	XPVDOMU_OR_HVM	\
21279ec9daYuri Pankov	((hwenv == HW_XEN_PV && !is_controldom()) || (hwenv & HW_VIRTUAL) != 0)
21379ec9daYuri Pankov
214adc586dMark Johnson/*ARGSUSED*/
2152449e17sherrymstatic int
216adc586dMark Johnsonucode_capable_amd(cpu_t *cp)
2172449e17sherrym{
218b9bfdccStuart Maybee	int hwenv = get_hwenv();
219b9bfdccStuart Maybee
22079ec9daYuri Pankov	if (XPVDOMU_OR_HVM)
221843e198johnlev		return (0);
22279ec9daYuri Pankov
2230ba6f73Mark Johnson	return (cpuid_getfamily(cp) >= 0x10);
224adc586dMark Johnson}
2250ba6f73Mark Johnson
226adc586dMark Johnsonstatic int
227adc586dMark Johnsonucode_capable_intel(cpu_t *cp)
228adc586dMark Johnson{
229b9bfdccStuart Maybee	int hwenv = get_hwenv();
230b9bfdccStuart Maybee
23179ec9daYuri Pankov	if (XPVDOMU_OR_HVM)
232adc586dMark Johnson		return (0);
23379ec9daYuri Pankov
234adc586dMark Johnson	return (cpuid_getfamily(cp) >= 6);
2352449e17sherrym}
2362449e17sherrym
2372449e17sherrym/*
2382449e17sherrym * Called when it is no longer necessary to keep the microcode around,
2392449e17sherrym * or when the cached microcode doesn't match the CPU being processed.
2402449e17sherrym */
2412449e17sherrymstatic void
242adc586dMark Johnsonucode_file_reset_amd(ucode_file_t *ufp, processorid_t id)
2432449e17sherrym{
244adc586dMark Johnson	ucode_file_amd_t *ucodefp = ufp->amd;
2452449e17sherrym
2462449e17sherrym	if (ucodefp == NULL)
2472449e17sherrym		return;
2482449e17sherrym
249adc586dMark Johnson	ucode_free(id, ucodefp, sizeof (ucode_file_amd_t));
250adc586dMark Johnson	ufp->amd = NULL;
251adc586dMark Johnson}
252adc586dMark Johnson
253adc586dMark Johnsonstatic void
254adc586dMark Johnsonucode_file_reset_intel(ucode_file_t *ufp, processorid_t id)
255adc586dMark Johnson{
256adc586dMark Johnson	ucode_file_intel_t *ucodefp = &ufp->intel;
257adc586dMark Johnson	int total_size, body_size;
258adc586dMark Johnson
259adc586dMark Johnson	if (ucodefp == NULL || ucodefp->uf_header == NULL)
260adc586dMark Johnson		return;
261adc586dMark Johnson
262adc586dMark Johnson	total_size = UCODE_TOTAL_SIZE_INTEL(ucodefp->uf_header->uh_total_size);
263adc586dMark Johnson	body_size = UCODE_BODY_SIZE_INTEL(ucodefp->uf_header->uh_body_size);
2642449e17sherrym	if (ucodefp->uf_body) {
265adc586dMark Johnson		ucode_free(id, ucodefp->uf_body, body_size);
2662449e17sherrym		ucodefp->uf_body = NULL;
2672449e17sherrym	}
2682449e17sherrym
2692449e17sherrym	if (ucodefp->uf_ext_table) {
270adc586dMark Johnson		int size = total_size - body_size - UCODE_HEADER_SIZE_INTEL;
271adc586dMark Johnson
272adc586dMark Johnson		ucode_free(id, ucodefp->uf_ext_table, size);
2732449e17sherrym		ucodefp->uf_ext_table = NULL;
2742449e17sherrym	}
2752449e17sherrym
276adc586dMark Johnson	ucode_free(id, ucodefp->uf_header, UCODE_HEADER_SIZE_INTEL);
277adc586dMark Johnson	ucodefp->uf_header = NULL;
278adc586dMark Johnson}
279adc586dMark Johnson
280adc586dMark Johnson/*
281adc586dMark Johnson * Find the equivalent CPU id in the equivalence table.
282adc586dMark Johnson */
283adc586dMark Johnsonstatic int
2840ba6f73Mark Johnsonucode_equiv_cpu_amd(cpu_t *cp, uint16_t *eq_sig)
285adc586dMark Johnson{
286adc586dMark Johnson	char name[MAXPATHLEN];
287adc586dMark Johnson	intptr_t fd;
288adc586dMark Johnson	int count;
289adc586dMark Johnson	int offset = 0, cpi_sig = cpuid_getsig(cp);
290adc586dMark Johnson	ucode_eqtbl_amd_t *eqtbl = ucode_eqtbl_amd;
291adc586dMark Johnson
292adc586dMark Johnson	(void) snprintf(name, MAXPATHLEN, "/%s/%s/equivalence-table",
293adc586dMark Johnson	    UCODE_INSTALL_PATH, cpuid_getvendorstr(cp));
294adc586dMark Johnson
295adc586dMark Johnson	/*
296adc586dMark Johnson	 * No kmem_zalloc() etc. available on boot cpu.
297adc586dMark Johnson	 */
298adc586dMark Johnson	if (cp->cpu_id == 0) {
299adc586dMark Johnson		if ((fd = kobj_open(name)) == -1)
300adc586dMark Johnson			return (EM_OPENFILE);
301adc586dMark Johnson		/* ucode_zalloc() cannot fail on boot cpu */
302adc586dMark Johnson		eqtbl = ucode_zalloc(cp->cpu_id, sizeof (*eqtbl));
303adc586dMark Johnson		ASSERT(eqtbl);
304adc586dMark Johnson		do {
305adc586dMark Johnson			count = kobj_read(fd, (int8_t *)eqtbl,
306adc586dMark Johnson			    sizeof (*eqtbl), offset);
307adc586dMark Johnson			if (count != sizeof (*eqtbl)) {
308adc586dMark Johnson				(void) kobj_close(fd);
309adc586dMark Johnson				return (EM_HIGHERREV);
310adc586dMark Johnson			}
311adc586dMark Johnson			offset += count;
312adc586dMark Johnson		} while (eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != cpi_sig);
313adc586dMark Johnson		(void) kobj_close(fd);
314adc586dMark Johnson	}
315adc586dMark Johnson
316adc586dMark Johnson	/*
317adc586dMark Johnson	 * If not already done, load the equivalence table.
318adc586dMark Johnson	 * Not done on boot CPU.
319adc586dMark Johnson	 */
320adc586dMark Johnson	if (eqtbl == NULL) {
321adc586dMark Johnson		struct _buf *eq;
322adc586dMark Johnson		uint64_t size;
323adc586dMark Johnson
324adc586dMark Johnson		if ((eq = kobj_open_file(name)) == (struct _buf *)-1)
325adc586dMark Johnson			return (EM_OPENFILE);
326adc586dMark Johnson
327adc586dMark Johnson		if (kobj_get_filesize(eq, &size) < 0) {
328adc586dMark Johnson			kobj_close_file(eq);
329adc586dMark Johnson			return (EM_OPENFILE);
330adc586dMark Johnson		}
331adc586dMark Johnson
332adc586dMark Johnson		ucode_eqtbl_amd = kmem_zalloc(size, KM_NOSLEEP);
333adc586dMark Johnson		if (ucode_eqtbl_amd == NULL) {
334adc586dMark Johnson			kobj_close_file(eq);
335adc586dMark Johnson			return (EM_NOMEM);
336adc586dMark Johnson		}
337adc586dMark Johnson
338adc586dMark Johnson		count = kobj_read_file(eq, (char *)ucode_eqtbl_amd, size, 0);
339adc586dMark Johnson		kobj_close_file(eq);
340adc586dMark Johnson
341adc586dMark Johnson		if (count != size)
342adc586dMark Johnson			return (EM_FILESIZE);
343adc586dMark Johnson	}
344adc586dMark Johnson
345adc586dMark Johnson	/* Get the equivalent CPU id. */
346adc586dMark Johnson	if (cp->cpu_id)
347adc586dMark Johnson		for (eqtbl = ucode_eqtbl_amd;
348adc586dMark Johnson		    eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != cpi_sig;
349adc586dMark Johnson		    eqtbl++)
350adc586dMark Johnson			;
351adc586dMark Johnson
352adc586dMark Johnson	*eq_sig = eqtbl->ue_equiv_cpu;
353adc586dMark Johnson
354adc586dMark Johnson	/* No equivalent CPU id found, assume outdated microcode file. */
355adc586dMark Johnson	if (*eq_sig == 0)
356adc586dMark Johnson		return (EM_HIGHERREV);
357adc586dMark Johnson
358adc586dMark Johnson	return (EM_OK);
3592449e17sherrym}
3602449e17sherrym
3612449e17sherrym/*
3620ba6f73Mark Johnson * xVM cannot check for the presence of PCI devices. Look for chipset-
3630ba6f73Mark Johnson * specific microcode patches in the container file and disable them
3640ba6f73Mark Johnson * by setting their CPU revision to an invalid value.
3650ba6f73Mark Johnson */
3660ba6f73Mark Johnson#ifdef __xpv
3670ba6f73Mark Johnsonstatic void
3680ba6f73Mark Johnsonucode_chipset_amd(uint8_t *buf, int size)
3690ba6f73Mark Johnson{
3700ba6f73Mark Johnson	ucode_header_amd_t *uh;
3710ba6f73Mark Johnson	uint32_t *ptr = (uint32_t *)buf;
3720ba6f73Mark Johnson	int len = 0;
3730ba6f73Mark Johnson
3740ba6f73Mark Johnson	/* skip to first microcode patch */
3750ba6f73Mark Johnson	ptr += 2; len = *ptr++; ptr += len >> 2; size -= len;
3760ba6f73Mark Johnson
3770ba6f73Mark Johnson	while (size >= sizeof (ucode_header_amd_t) + 8) {
3780ba6f73Mark Johnson		ptr++; len = *ptr++;
3790ba6f73Mark Johnson		uh = (ucode_header_amd_t *)ptr;
3800ba6f73Mark Johnson		ptr += len >> 2; size -= len;
3810ba6f73Mark Johnson
3820ba6f73Mark Johnson		if (uh->uh_nb_id) {
3830ba6f73Mark Johnson			cmn_err(CE_WARN, "ignoring northbridge-specific ucode: "
3840ba6f73Mark Johnson			    "chipset id %x, revision %x",
3850ba6f73Mark Johnson			    uh->uh_nb_id, uh->uh_nb_rev);
3860ba6f73Mark Johnson			uh->uh_cpu_rev = 0xffff;
3870ba6f73Mark Johnson		}
3880ba6f73Mark Johnson
3890ba6f73Mark Johnson		if (uh->uh_sb_id) {
3900ba6f73Mark Johnson			cmn_err(CE_WARN, "ignoring southbridge-specific ucode: "
3910ba6f73Mark Johnson			    "chipset id %x, revision %x",
3920ba6f73Mark Johnson			    uh->uh_sb_id, uh->uh_sb_rev);
3930ba6f73Mark Johnson			uh->uh_cpu_rev = 0xffff;
3940ba6f73Mark Johnson		}
3950ba6f73Mark Johnson	}
3960ba6f73Mark Johnson}
3970ba6f73Mark Johnson#endif
3980ba6f73Mark Johnson
3990ba6f73Mark Johnson/*
4002449e17sherrym * Populate the ucode file structure from microcode file corresponding to
4012449e17sherrym * this CPU, if exists.
4022449e17sherrym *
4032449e17sherrym * Return EM_OK on success, corresponding error code on failure.
4042449e17sherrym */
4050ba6f73Mark Johnson/*ARGSUSED*/
4062449e17sherrymstatic ucode_errno_t
407adc586dMark Johnsonucode_locate_amd(cpu_t *cp, cpu_ucode_info_t *uinfop, ucode_file_t *ufp)
408adc586dMark Johnson{
409adc586dMark Johnson	char name[MAXPATHLEN];
410adc586dMark Johnson	intptr_t fd;
4110ba6f73Mark Johnson	int count, rc;
412adc586dMark Johnson	ucode_file_amd_t *ucodefp = ufp->amd;
413adc586dMark Johnson
4140ba6f73Mark Johnson#ifndef __xpv
4150ba6f73Mark Johnson	uint16_t eq_sig = 0;
4160ba6f73Mark Johnson	int i;
4170ba6f73Mark Johnson
418adc586dMark Johnson	/* get equivalent CPU id */
419adc586dMark Johnson	if ((rc = ucode_equiv_cpu_amd(cp, &eq_sig)) != EM_OK)
420adc586dMark Johnson		return (rc);
421adc586dMark Johnson
422adc586dMark Johnson	/*
423adc586dMark Johnson	 * Allocate a buffer for the microcode patch. If the buffer has been
424adc586dMark Johnson	 * allocated before, check for a matching microcode to avoid loading
425adc586dMark Johnson	 * the file again.
426adc586dMark Johnson	 */
427adc586dMark Johnson	if (ucodefp == NULL)
428adc586dMark Johnson		ucodefp = ucode_zalloc(cp->cpu_id, sizeof (*ucodefp));
429adc586dMark Johnson	else if (ucode_match_amd(eq_sig, uinfop, ucodefp, sizeof (*ucodefp))
430adc586dMark Johnson	    == EM_OK)
431adc586dMark Johnson		return (EM_OK);
432adc586dMark Johnson
433adc586dMark Johnson	if (ucodefp == NULL)
434adc586dMark Johnson		return (EM_NOMEM);
435adc586dMark Johnson
436adc586dMark Johnson	ufp->amd = ucodefp;
437adc586dMark Johnson
438adc586dMark Johnson	/*
439adc586dMark Johnson	 * Find the patch for this CPU. The patch files are named XXXX-YY, where
440adc586dMark Johnson	 * XXXX is the equivalent CPU id and YY is the running patch number.
441adc586dMark Johnson	 * Patches specific to certain chipsets are guaranteed to have lower
442adc586dMark Johnson	 * numbers than less specific patches, so we can just load the first
443adc586dMark Johnson	 * patch that matches.
444adc586dMark Johnson	 */
445adc586dMark Johnson
446adc586dMark Johnson	for (i = 0; i < 0xff; i++) {
447adc586dMark Johnson		(void) snprintf(name, MAXPATHLEN, "/%s/%s/%04X-%02X",
448adc586dMark Johnson		    UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), eq_sig, i);
449adc586dMark Johnson		if ((fd = kobj_open(name)) == -1)
450adc586dMark Johnson			return (EM_NOMATCH);
451adc586dMark Johnson		count = kobj_read(fd, (char *)ucodefp, sizeof (*ucodefp), 0);
452adc586dMark Johnson		(void) kobj_close(fd);
453adc586dMark Johnson
454adc586dMark Johnson		if (ucode_match_amd(eq_sig, uinfop, ucodefp, count) == EM_OK)
455adc586dMark Johnson			return (EM_OK);
456adc586dMark Johnson	}
457adc586dMark Johnson	return (EM_NOMATCH);
4580ba6f73Mark Johnson#else
4590ba6f73Mark Johnson	int size = 0;
4600ba6f73Mark Johnson	char c;
4610ba6f73Mark Johnson
4620ba6f73Mark Johnson	/*
4630ba6f73Mark Johnson	 * The xVM case is special. To support mixed-revision systems, the
4640ba6f73Mark Johnson	 * hypervisor will choose which patch to load for which CPU, so the
4650ba6f73Mark Johnson	 * whole microcode patch container file will have to be loaded.
4660ba6f73Mark Johnson	 *
4670ba6f73Mark Johnson	 * Since this code is only run on the boot cpu, we don't have to care
4680ba6f73Mark Johnson	 * about failing ucode_zalloc() or freeing allocated memory.
4690ba6f73Mark Johnson	 */
4700ba6f73Mark Johnson	if (cp->cpu_id != 0)
4710ba6f73Mark Johnson		return (EM_INVALIDARG);
4720ba6f73Mark Johnson
4730ba6f73Mark Johnson	(void) snprintf(name, MAXPATHLEN, "/%s/%s/container",
4740ba6f73Mark Johnson	    UCODE_INSTALL_PATH, cpuid_getvendorstr(cp));
4750ba6f73Mark Johnson
4760ba6f73Mark Johnson	if ((fd = kobj_open(name)) == -1)
4770ba6f73Mark Johnson		return (EM_OPENFILE);
4780ba6f73Mark Johnson
4790ba6f73Mark Johnson	/* get the file size by counting bytes */
4800ba6f73Mark Johnson	do {
4810ba6f73Mark Johnson		count = kobj_read(fd, &c, 1, size);
4820ba6f73Mark Johnson		size += count;
4830ba6f73Mark Johnson	} while (count);
4840ba6f73Mark Johnson
4850ba6f73Mark Johnson	ucodefp = ucode_zalloc(cp->cpu_id, sizeof (*ucodefp));
4860ba6f73Mark Johnson	ASSERT(ucodefp);
4870ba6f73Mark Johnson	ufp->amd = ucodefp;
4880ba6f73Mark Johnson
4890ba6f73Mark Johnson	ucodefp->usize = size;
4900ba6f73Mark Johnson	ucodefp->ucodep = ucode_zalloc(cp->cpu_id, size);
4910ba6f73Mark Johnson	ASSERT(ucodefp->ucodep);
4920ba6f73Mark Johnson
4930ba6f73Mark Johnson	/* load the microcode patch container file */
4940ba6f73Mark Johnson	count = kobj_read(fd, (char *)ucodefp->ucodep, size, 0);
4950ba6f73Mark Johnson	(void) kobj_close(fd);
4960ba6f73Mark Johnson
4970ba6f73Mark Johnson	if (count != size)
4980ba6f73Mark Johnson		return (EM_FILESIZE);
4990ba6f73Mark Johnson
5000ba6f73Mark Johnson	/* make sure the container file is valid */
5010ba6f73Mark Johnson	rc = ucode->validate(ucodefp->ucodep, ucodefp->usize);
5020ba6f73Mark Johnson
5030ba6f73Mark Johnson	if (rc != EM_OK)
5040ba6f73Mark Johnson		return (rc);
5050ba6f73Mark Johnson
5060ba6f73Mark Johnson	/* disable chipset-specific patches */
5070ba6f73Mark Johnson	ucode_chipset_amd(ucodefp->ucodep, ucodefp->usize);
5080ba6f73Mark Johnson
5090ba6f73Mark Johnson	return (EM_OK);
5100ba6f73Mark Johnson#endif
511adc586dMark Johnson}
512adc586dMark Johnson
513adc586dMark Johnsonstatic ucode_errno_t
514adc586dMark Johnsonucode_locate_intel(cpu_t *cp, cpu_ucode_info_t *uinfop, ucode_file_t *ufp)
5152449e17sherrym{
5162449e17sherrym	char		name[MAXPATHLEN];
5172449e17sherrym	intptr_t	fd;
5182449e17sherrym	int		count;
519adc586dMark Johnson	int		header_size = UCODE_HEADER_SIZE_INTEL;
5202449e17sherrym	int		cpi_sig = cpuid_getsig(cp);
5212449e17sherrym	ucode_errno_t	rc = EM_OK;
522adc586dMark Johnson	ucode_file_intel_t *ucodefp = &ufp->intel;
523adc586dMark Johnson
524adc586dMark Johnson	ASSERT(ucode);
5252449e17sherrym
5262449e17sherrym	/*
5272449e17sherrym	 * If the microcode matches the CPU we are processing, use it.
5282449e17sherrym	 */
529adc586dMark Johnson	if (ucode_match_intel(cpi_sig, uinfop, ucodefp->uf_header,
5302449e17sherrym	    ucodefp->uf_ext_table) == EM_OK && ucodefp->uf_body != NULL) {
5312449e17sherrym		return (EM_OK);
5322449e17sherrym	}
5332449e17sherrym
5342449e17sherrym	/*
5352449e17sherrym	 * Look for microcode file with the right name.
5362449e17sherrym	 */
5372449e17sherrym	(void) snprintf(name, MAXPATHLEN, "/%s/%s/%08X-%02X",
5382449e17sherrym	    UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), cpi_sig,
5392449e17sherrym	    uinfop->cui_platid);
5402449e17sherrym	if ((fd = kobj_open(name)) == -1) {
5412449e17sherrym		return (EM_OPENFILE);
5422449e17sherrym	}
5432449e17sherrym
5442449e17sherrym	/*
5452449e17sherrym	 * We found a microcode file for the CPU we are processing,
5462449e17sherrym	 * reset the microcode data structure and read in the new
5472449e17sherrym	 * file.
5482449e17sherrym	 */
549adc586dMark Johnson	ucode->file_reset(ufp, cp->cpu_id);
550adc586dMark Johnson
551adc586dMark Johnson	ucodefp->uf_header = ucode_zalloc(cp->cpu_id, header_size);
552adc586dMark Johnson	if (ucodefp->uf_header == NULL)
553adc586dMark Johnson		return (EM_NOMEM);
5542449e17sherrym
555adc586dMark Johnson	count = kobj_read(fd, (char *)ucodefp->uf_header, header_size, 0);
5562449e17sherrym
5572449e17sherrym	switch (count) {
558adc586dMark Johnson	case UCODE_HEADER_SIZE_INTEL: {
5592449e17sherrym
560adc586dMark Johnson		ucode_header_intel_t	*uhp = ucodefp->uf_header;
5612449e17sherrym		uint32_t	offset = header_size;
5622449e17sherrym		int		total_size, body_size, ext_size;
5632449e17sherrym		uint32_t	sum = 0;
5642449e17sherrym
5652449e17sherrym		/*
5662449e17sherrym		 * Make sure that the header contains valid fields.
5672449e17sherrym		 */
568adc586dMark Johnson		if ((rc = ucode_header_validate_intel(uhp)) == EM_OK) {
569adc586dMark Johnson			total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
570adc586dMark Johnson			body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size);
571adc586dMark Johnson			ucodefp->uf_body = ucode_zalloc(cp->cpu_id, body_size);
572adc586dMark Johnson			if (ucodefp->uf_body == NULL) {
573adc586dMark Johnson				rc = EM_NOMEM;
574adc586dMark Johnson				break;
5752449e17sherrym			}
5762449e17sherrym
5772449e17sherrym			if (kobj_read(fd, (char *)ucodefp->uf_body,
5782449e17sherrym			    body_size, offset) != body_size)
5792449e17sherrym				rc = EM_FILESIZE;
5802449e17sherrym		}
5812449e17sherrym
5822449e17sherrym		if (rc)
5832449e17sherrym			break;
5842449e17sherrym
585adc586dMark Johnson		sum = ucode_checksum_intel(0, header_size,
586adc586dMark Johnson		    (uint8_t *)ucodefp->uf_header);
587adc586dMark Johnson		if (ucode_checksum_intel(sum, body_size, ucodefp->uf_body)) {
5882449e17sherrym			rc = EM_CHECKSUM;
5892449e17sherrym			break;
5902449e17sherrym		}
5912449e17sherrym
5922449e17sherrym		/*
5932449e17sherrym		 * Check to see if there is extended signature table.
5942449e17sherrym		 */
5952449e17sherrym		offset = body_size + header_size;
5962449e17sherrym		ext_size = total_size - offset;
5972449e17sherrym
5982449e17sherrym		if (ext_size <= 0)
5992449e17sherrym			break;
6002449e17sherrym
601adc586dMark Johnson		ucodefp->uf_ext_table = ucode_zalloc(cp->cpu_id, ext_size);
602adc586dMark Johnson		if (ucodefp->uf_ext_table == NULL) {
603adc586dMark Johnson			rc = EM_NOMEM;
604adc586dMark Johnson			break;
6052449e17sherrym		}
6062449e17sherrym
6072449e17sherrym		if (kobj_read(fd, (char *)ucodefp->uf_ext_table,
6082449e17sherrym		    ext_size, offset) != ext_size) {
6092449e17sherrym			rc = EM_FILESIZE;
610adc586dMark Johnson		} else if (ucode_checksum_intel(0, ext_size,
6112449e17sherrym		    (uint8_t *)(ucodefp->uf_ext_table))) {
6122449e17sherrym			rc = EM_CHECKSUM;
6132449e17sherrym		} else {
6142449e17sherrym			int i;
6152449e17sherrym
616adc586dMark Johnson			ext_size -= UCODE_EXT_TABLE_SIZE_INTEL;
6172449e17sherrym			for (i = 0; i < ucodefp->uf_ext_table->uet_count;
6182449e17sherrym			    i++) {
619adc586dMark Johnson				if (ucode_checksum_intel(0,
620adc586dMark Johnson				    UCODE_EXT_SIG_SIZE_INTEL,
6212449e17sherrym				    (uint8_t *)(&(ucodefp->uf_ext_table->
6222449e17sherrym				    uet_ext_sig[i])))) {
6232449e17sherrym					rc = EM_CHECKSUM;
6242449e17sherrym					break;
6252449e17sherrym				}
6262449e17sherrym			}
6272449e17sherrym		}
6282449e17sherrym		break;
6292449e17sherrym	}
6302449e17sherrym
6312449e17sherrym	default:
6322449e17sherrym		rc = EM_FILESIZE;
6332449e17sherrym		break;
6342449e17sherrym	}
6352449e17sherrym
6362449e17sherrym	kobj_close(fd);
6372449e17sherrym
6382449e17sherrym	if (rc != EM_OK)
6392449e17sherrym		return (rc);
6402449e17sherrym
641adc586dMark Johnson	rc = ucode_match_intel(cpi_sig, uinfop, ucodefp->uf_header,
6422449e17sherrym	    ucodefp->uf_ext_table);
6432449e17sherrym
6442449e17sherrym	return (rc);
6452449e17sherrym}
6462449e17sherrym
6470ba6f73Mark Johnson#ifndef __xpv
648adc586dMark Johnsonstatic ucode_errno_t
6490ba6f73Mark Johnsonucode_match_amd(uint16_t eq_sig, cpu_ucode_info_t *uinfop,
6500ba6f73Mark Johnson    ucode_file_amd_t *ucodefp, int size)
651adc586dMark Johnson{
652adc586dMark Johnson	ucode_header_amd_t *uh;
653adc586dMark Johnson
654adc586dMark Johnson	if (ucodefp == NULL || size < sizeof (ucode_header_amd_t))
655adc586dMark Johnson		return (EM_NOMATCH);
656adc586dMark Johnson
6575e53ed3Hans Rosenfeld	uh = &ucodefp->uf_header;
6585e53ed3Hans Rosenfeld
659adc586dMark Johnson	/*
660adc586dMark Johnson	 * Don't even think about loading patches that would require code
6615e53ed3Hans Rosenfeld	 * execution. Does not apply to patches for family 0x14 and beyond.
662adc586dMark Johnson	 */
6635e53ed3Hans Rosenfeld	if (uh->uh_cpu_rev < 0x5000 &&
6645e53ed3Hans Rosenfeld	    size > offsetof(ucode_file_amd_t, uf_code_present) &&
665adc586dMark Johnson	    ucodefp->uf_code_present)
666adc586dMark Johnson		return (EM_NOMATCH);
667adc586dMark Johnson
668adc586dMark Johnson	if (eq_sig != uh->uh_cpu_rev)
669adc586dMark Johnson		return (EM_NOMATCH);
670adc586dMark Johnson
671adc586dMark Johnson	if (uh->uh_nb_id) {
672adc586dMark Johnson		cmn_err(CE_WARN, "ignoring northbridge-specific ucode: "
673adc586dMark Johnson		    "chipset id %x, revision %x", uh->uh_nb_id, uh->uh_nb_rev);
674adc586dMark Johnson		return (EM_NOMATCH);
675adc586dMark Johnson	}
676adc586dMark Johnson
677adc586dMark Johnson	if (uh->uh_sb_id) {
678adc586dMark Johnson		cmn_err(CE_WARN, "ignoring southbridge-specific ucode: "
679adc586dMark Johnson		    "chipset id %x, revision %x", uh->uh_sb_id, uh->uh_sb_rev);
680adc586dMark Johnson		return (EM_NOMATCH);
681adc586dMark Johnson	}
682adc586dMark Johnson
6835e53ed3Hans Rosenfeld	if (uh->uh_patch_id <= uinfop->cui_rev && !ucode_force_update)
684adc586dMark Johnson		return (EM_HIGHERREV);
685adc586dMark Johnson
686adc586dMark Johnson	return (EM_OK);
687adc586dMark Johnson}
6880ba6f73Mark Johnson#endif
6892449e17sherrym
6902449e17sherrym/*
6912449e17sherrym * Returns 1 if the microcode is for this processor; 0 otherwise.
6922449e17sherrym */
6932449e17sherrymstatic ucode_errno_t
694adc586dMark Johnsonucode_match_intel(int cpi_sig, cpu_ucode_info_t *uinfop,
695adc586dMark Johnson    ucode_header_intel_t *uhp, ucode_ext_table_intel_t *uetp)
6962449e17sherrym{
697adc586dMark Johnson	if (uhp == NULL)
698adc586dMark Johnson		return (EM_NOMATCH);
6992449e17sherrym
700adc586dMark Johnson	if (UCODE_MATCH_INTEL(cpi_sig, uhp->uh_signature,
7012449e17sherrym	    uinfop->cui_platid, uhp->uh_proc_flags)) {
7022449e17sherrym
7032449e17sherrym		if (uinfop->cui_rev >= uhp->uh_rev && !ucode_force_update)
7042449e17sherrym			return (EM_HIGHERREV);
7052449e17sherrym
7062449e17sherrym		return (EM_OK);
7072449e17sherrym	}
7082449e17sherrym
7092449e17sherrym	if (uetp != NULL) {
7102449e17sherrym		int i;
7112449e17sherrym
7122449e17sherrym		for (i = 0; i < uetp->uet_count; i++) {
713adc586dMark Johnson			ucode_ext_sig_intel_t *uesp;
7142449e17sherrym
7152449e17sherrym			uesp = &uetp->uet_ext_sig[i];
7162449e17sherrym
717adc586dMark Johnson			if (UCODE_MATCH_INTEL(cpi_sig, uesp->ues_signature,
7182449e17sherrym			    uinfop->cui_platid, uesp->ues_proc_flags)) {
7192449e17sherrym
7202449e17sherrym				if (uinfop->cui_rev >= uhp->uh_rev &&
7212449e17sherrym				    !ucode_force_update)
7222449e17sherrym					return (EM_HIGHERREV);
7232449e17sherrym
7242449e17sherrym				return (EM_OK);
7252449e17sherrym			}
7262449e17sherrym		}
7272449e17sherrym	}
7282449e17sherrym
7292449e17sherrym	return (EM_NOMATCH);
7302449e17sherrym}
7312449e17sherrym
7322449e17sherrym/*ARGSUSED*/
7332449e17sherrymstatic int
7342449e17sherrymucode_write(xc_arg_t arg1, xc_arg_t unused2, xc_arg_t unused3)
7352449e17sherrym{
736adc586dMark Johnson	ucode_update_t *uusp = (ucode_update_t *)arg1;
737adc586dMark Johnson	cpu_ucode_info_t *uinfop = CPU->cpu_m.mcpu_ucode_info;
7385e53ed3Hans Rosenfeld#ifndef __xpv
7395e53ed3Hans Rosenfeld	on_trap_data_t otd;
7405e53ed3Hans Rosenfeld#endif
7412449e17sherrym
742adc586dMark Johnson	ASSERT(ucode);
7432449e17sherrym	ASSERT(uusp->ucodep);
7442449e17sherrym
745882a7afMark Johnson#ifndef	__xpv
7462449e17sherrym	/*
7472449e17sherrym	 * Check one more time to see if it is really necessary to update
7482449e17sherrym	 * microcode just in case this is a hyperthreaded processor where
7492449e17sherrym	 * the threads share the same microcode.
7502449e17sherrym	 */
7512449e17sherrym	if (!ucode_force_update) {
752adc586dMark Johnson		ucode->read_rev(uinfop);
7532449e17sherrym		uusp->new_rev = uinfop->cui_rev;
7542449e17sherrym		if (uinfop->cui_rev >= uusp->expected_rev)
7552449e17sherrym			return (0);
7562449e17sherrym	}
7572449e17sherrym
758f226ceeRobert Mustacchi	if (!on_trap(&otd, OT_DATA_ACCESS)) {
759f226ceeRobert Mustacchi		/*
760f226ceeRobert Mustacchi		 * On some platforms a cache invalidation is required for the
761f226ceeRobert Mustacchi		 * ucode update to be successful due to the parts of the
762f226ceeRobert Mustacchi		 * processor that the microcode is updating.
763f226ceeRobert Mustacchi		 */
764f226ceeRobert Mustacchi		invalidate_cache();
7655e53ed3Hans Rosenfeld		wrmsr(ucode->write_msr, (uintptr_t)uusp->ucodep);
766f226ceeRobert Mustacchi	}
7675e53ed3Hans Rosenfeld
7685e53ed3Hans Rosenfeld	no_trap();
769882a7afMark Johnson#endif
770adc586dMark Johnson	ucode->read_rev(uinfop);
7712449e17sherrym	uusp->new_rev = uinfop->cui_rev;
7722449e17sherrym
7732449e17sherrym	return (0);
7742449e17sherrym}
7752449e17sherrym
776adc586dMark Johnson/*ARGSUSED*/
777adc586dMark Johnsonstatic uint32_t
778adc586dMark Johnsonucode_load_amd(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp)
779adc586dMark Johnson{
780adc586dMark Johnson	ucode_file_amd_t *ucodefp = ufp->amd;
781adc586dMark Johnson#ifdef	__xpv
782adc586dMark Johnson	ucode_update_t uus;
7835e53ed3Hans Rosenfeld#else
7845e53ed3Hans Rosenfeld	on_trap_data_t otd;
785adc586dMark Johnson#endif
7862449e17sherrym
787adc586dMark Johnson	ASSERT(ucode);
788adc586dMark Johnson	ASSERT(ucodefp);
789adc586dMark Johnson
790adc586dMark Johnson#ifndef	__xpv
791adc586dMark Johnson	kpreempt_disable();
7925e53ed3Hans Rosenfeld	if (on_trap(&otd, OT_DATA_ACCESS)) {
7935e53ed3Hans Rosenfeld		no_trap();
7945e53ed3Hans Rosenfeld		kpreempt_enable();
7955e53ed3Hans Rosenfeld		return (0);
7965e53ed3Hans Rosenfeld	}
797adc586dMark Johnson	wrmsr(ucode->write_msr, (uintptr_t)ucodefp);
7985e53ed3Hans Rosenfeld	no_trap();
799adc586dMark Johnson	ucode->read_rev(uinfop);
800adc586dMark Johnson	kpreempt_enable();
8010ba6f73Mark Johnson
8020ba6f73Mark Johnson	return (ucodefp->uf_header.uh_patch_id);
803adc586dMark Johnson#else
8040ba6f73Mark Johnson	uus.ucodep = ucodefp->ucodep;
8050ba6f73Mark Johnson	uus.usize = ucodefp->usize;
806adc586dMark Johnson	ucode_load_xpv(&uus);
807adc586dMark Johnson	ucode->read_rev(uinfop);
808adc586dMark Johnson	uus.new_rev = uinfop->cui_rev;
809adc586dMark Johnson
8100ba6f73Mark Johnson	return (uus.new_rev);
8110ba6f73Mark Johnson#endif
812adc586dMark Johnson}
813adc586dMark Johnson
814adc586dMark Johnson/*ARGSUSED2*/
815adc586dMark Johnsonstatic uint32_t
816adc586dMark Johnsonucode_load_intel(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp)
8172449e17sherrym{
818adc586dMark Johnson	ucode_file_intel_t *ucodefp = &ufp->intel;
819adc586dMark Johnson#ifdef __xpv
820adc586dMark Johnson	uint32_t ext_offset;
821adc586dMark Johnson	uint32_t body_size;
822adc586dMark Johnson	uint32_t ext_size;
823adc586dMark Johnson	uint8_t *ustart;
824adc586dMark Johnson	uint32_t usize;
825adc586dMark Johnson	ucode_update_t uus;
826adc586dMark Johnson#endif
827adc586dMark Johnson
828adc586dMark Johnson	ASSERT(ucode);
829adc586dMark Johnson
830adc586dMark Johnson#ifdef __xpv
831adc586dMark Johnson	/*
832adc586dMark Johnson	 * the hypervisor wants the header, data, and extended
833adc586dMark Johnson	 * signature tables. We can only get here from the boot
834adc586dMark Johnson	 * CPU (cpu #0), we don't need to free as ucode_zalloc() will
835adc586dMark Johnson	 * use BOP_ALLOC().
836adc586dMark Johnson	 */
837adc586dMark Johnson	usize = UCODE_TOTAL_SIZE_INTEL(ucodefp->uf_header->uh_total_size);
838adc586dMark Johnson	ustart = ucode_zalloc(cp->cpu_id, usize);
839adc586dMark Johnson	ASSERT(ustart);
840adc586dMark Johnson
841adc586dMark Johnson	body_size = UCODE_BODY_SIZE_INTEL(ucodefp->uf_header->uh_body_size);
842adc586dMark Johnson	ext_offset = body_size + UCODE_HEADER_SIZE_INTEL;
843adc586dMark Johnson	ext_size = usize - ext_offset;
844adc586dMark Johnson	ASSERT(ext_size >= 0);
845adc586dMark Johnson
846adc586dMark Johnson	(void) memcpy(ustart, ucodefp->uf_header, UCODE_HEADER_SIZE_INTEL);
847adc586dMark Johnson	(void) memcpy(&ustart[UCODE_HEADER_SIZE_INTEL], ucodefp->uf_body,
848adc586dMark Johnson	    body_size);
849adc586dMark Johnson	if (ext_size > 0) {
850adc586dMark Johnson		(void) memcpy(&ustart[ext_offset],
851adc586dMark Johnson		    ucodefp->uf_ext_table, ext_size);
852adc586dMark Johnson	}
853adc586dMark Johnson	uus.ucodep = ustart;
854adc586dMark Johnson	uus.usize = usize;
855adc586dMark Johnson	ucode_load_xpv(&uus);
856adc586dMark Johnson	ucode->read_rev(uinfop);
857adc586dMark Johnson	uus.new_rev = uinfop->cui_rev;
858adc586dMark Johnson#else
8592449e17sherrym	kpreempt_disable();
860f226ceeRobert Mustacchi	/*
861f226ceeRobert Mustacchi	 * On some platforms a cache invalidation is required for the
862f226ceeRobert Mustacchi	 * ucode update to be successful due to the parts of the
863f226ceeRobert Mustacchi	 * processor that the microcode is updating.
864f226ceeRobert Mustacchi	 */
865f226ceeRobert Mustacchi	invalidate_cache();
866adc586dMark Johnson	wrmsr(ucode->write_msr, (uintptr_t)ucodefp->uf_body);
867adc586dMark Johnson	ucode->read_rev(uinfop);
8682449e17sherrym	kpreempt_enable();
869adc586dMark Johnson#endif
870adc586dMark Johnson
871adc586dMark Johnson	return (ucodefp->uf_header->uh_rev);
8722449e17sherrym}
8732449e17sherrym
874882a7afMark Johnson
875882a7afMark Johnson#ifdef	__xpv
876882a7afMark Johnsonstatic void
877adc586dMark Johnsonucode_load_xpv(ucode_update_t *uusp)
878882a7afMark Johnson{
879882a7afMark Johnson	xen_platform_op_t op;
880882a7afMark Johnson	int e;
881882a7afMark Johnson
882882a7afMark Johnson	ASSERT(DOMAIN_IS_INITDOMAIN(xen_info));
883882a7afMark Johnson
884882a7afMark Johnson	kpreempt_disable();
885882a7afMark Johnson	op.cmd = XENPF_microcode_update;
886882a7afMark Johnson	op.interface_version = XENPF_INTERFACE_VERSION;
887882a7afMark Johnson	/*LINTED: constant in conditional context*/
888adc586dMark Johnson	set_xen_guest_handle(op.u.microcode.data, uusp->ucodep);
889adc586dMark Johnson	op.u.microcode.length = uusp->usize;
890882a7afMark Johnson	e = HYPERVISOR_platform_op(&op);
891882a7afMark Johnson	if (e != 0) {
892882a7afMark Johnson		cmn_err(CE_WARN, "hypervisor failed to accept uCode update");
893882a7afMark Johnson	}
894882a7afMark Johnson	kpreempt_enable();
895882a7afMark Johnson}
896882a7afMark Johnson#endif /* __xpv */
897882a7afMark Johnson
898adc586dMark Johnsonstatic void
899adc586dMark Johnsonucode_read_rev_amd(cpu_ucode_info_t *uinfop)
900adc586dMark Johnson{
901adc586dMark Johnson	uinfop->cui_rev = rdmsr(MSR_AMD_PATCHLEVEL);
902adc586dMark Johnson}
903882a7afMark Johnson
9042449e17sherrymstatic void
905adc586dMark Johnsonucode_read_rev_intel(cpu_ucode_info_t *uinfop)
9062449e17sherrym{
9072449e17sherrym	struct cpuid_regs crs;
9082449e17sherrym
9092449e17sherrym	/*
9102449e17sherrym	 * The Intel 64 and IA-32 Architecture Software Developer's Manual
9112449e17sherrym	 * recommends that MSR_INTC_UCODE_REV be loaded with 0 first, then
9122449e17sherrym	 * execute cpuid to guarantee the correct reading of this register.
9132449e17sherrym	 */
9142449e17sherrym	wrmsr(MSR_INTC_UCODE_REV, 0);
9152449e17sherrym	(void) __cpuid_insn(&crs);
9162449e17sherrym	uinfop->cui_rev = (rdmsr(MSR_INTC_UCODE_REV) >> INTC_UCODE_REV_SHIFT);
9172449e17sherrym}
9182449e17sherrym
919adc586dMark Johnsonstatic ucode_errno_t
920adc586dMark Johnsonucode_extract_amd(ucode_update_t *uusp, uint8_t *ucodep, int size)
921adc586dMark Johnson{
9220ba6f73Mark Johnson#ifndef __xpv
923adc586dMark Johnson	uint32_t *ptr = (uint32_t *)ucodep;
924adc586dMark Johnson	ucode_eqtbl_amd_t *eqtbl;
925adc586dMark Johnson	ucode_file_amd_t *ufp;
9260ba6f73Mark Johnson	int count;
9270ba6f73Mark Johnson	int higher = 0;
9280ba6f73Mark Johnson	ucode_errno_t rc = EM_NOMATCH;
9290ba6f73Mark Johnson	uint16_t eq_sig;
930adc586dMark Johnson
931adc586dMark Johnson	/* skip over magic number & equivalence table header */
932adc586dMark Johnson	ptr += 2; size -= 8;
933adc586dMark Johnson
934adc586dMark Johnson	count = *ptr++; size -= 4;
935adc586dMark Johnson	for (eqtbl = (ucode_eqtbl_amd_t *)ptr;
936adc586dMark Johnson	    eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != uusp->sig;
937adc586dMark Johnson	    eqtbl++)
938adc586dMark Johnson		;
939adc586dMark Johnson
940adc586dMark Johnson	eq_sig = eqtbl->ue_equiv_cpu;
941adc586dMark Johnson
942adc586dMark Johnson	/* No equivalent CPU id found, assume outdated microcode file. */
943adc586dMark Johnson	if (eq_sig == 0)
944adc586dMark Johnson		return (EM_HIGHERREV);
945adc586dMark Johnson
946adc586dMark Johnson	/* Use the first microcode patch that matches. */
947adc586dMark Johnson	do {
948adc586dMark Johnson		ptr += count >> 2; size -= count;
949adc586dMark Johnson
950adc586dMark Johnson		if (!size)
9510ba6f73Mark Johnson			return (higher ? EM_HIGHERREV : EM_NOMATCH);
952adc586dMark Johnson
953adc586dMark Johnson		ptr++; size -= 4;
954adc586dMark Johnson		count = *ptr++; size -= 4;
955adc586dMark Johnson		ufp = (ucode_file_amd_t *)ptr;
9560ba6f73Mark Johnson
9570ba6f73Mark Johnson		rc = ucode_match_amd(eq_sig, &uusp->info, ufp, count);
9580ba6f73Mark Johnson		if (rc == EM_HIGHERREV)
9590ba6f73Mark Johnson			higher = 1;
9600ba6f73Mark Johnson	} while (rc != EM_OK);
961adc586dMark Johnson
962adc586dMark Johnson	uusp->ucodep = (uint8_t *)ufp;
963adc586dMark Johnson	uusp->usize = count;
964adc586dMark Johnson	uusp->expected_rev = ufp->uf_header.uh_patch_id;
9650ba6f73Mark Johnson#else
9660ba6f73Mark Johnson	/*
9670ba6f73Mark Johnson	 * The hypervisor will choose the patch to load, so there is no way to
9680ba6f73Mark Johnson	 * know the "expected revision" in advance. This is especially true on
9690ba6f73Mark Johnson	 * mixed-revision systems where more than one patch will be loaded.
9700ba6f73Mark Johnson	 */
9710ba6f73Mark Johnson	uusp->expected_rev = 0;
9720ba6f73Mark Johnson	uusp->ucodep = ucodep;
9730ba6f73Mark Johnson	uusp->usize = size;
9740ba6f73Mark Johnson
9750ba6f73Mark Johnson	ucode_chipset_amd(ucodep, size);
9760ba6f73Mark Johnson#endif
977adc586dMark Johnson
978adc586dMark Johnson	return (EM_OK);
979adc586dMark Johnson}
980adc586dMark Johnson
981adc586dMark Johnsonstatic ucode_errno_t
982adc586dMark Johnsonucode_extract_intel(ucode_update_t *uusp, uint8_t *ucodep, int size)
983adc586dMark Johnson{
984adc586dMark Johnson	uint32_t	header_size = UCODE_HEADER_SIZE_INTEL;
985adc586dMark Johnson	int		remaining;
986adc586dMark Johnson	int		found = 0;
987adc586dMark Johnson	ucode_errno_t	search_rc = EM_NOMATCH; /* search result */
988adc586dMark Johnson
989adc586dMark Johnson	/*
990adc586dMark Johnson	 * Go through the whole buffer in case there are
991adc586dMark Johnson	 * multiple versions of matching microcode for this
992adc586dMark Johnson	 * processor.
993adc586dMark Johnson	 */
994adc586dMark Johnson	for (remaining = size; remaining > 0; ) {
995adc586dMark Johnson		int	total_size, body_size, ext_size;
996adc586dMark Johnson		uint8_t	*curbuf = &ucodep[size - remaining];
997adc586dMark Johnson		ucode_header_intel_t *uhp = (ucode_header_intel_t *)curbuf;
998adc586dMark Johnson		ucode_ext_table_intel_t *uetp = NULL;
999adc586dMark Johnson		ucode_errno_t tmprc;
1000adc586dMark Johnson
1001adc586dMark Johnson		total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
1002adc586dMark Johnson		body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size);
1003adc586dMark Johnson		ext_size = total_size - (header_size + body_size);
1004adc586dMark Johnson
1005adc586dMark Johnson		if (ext_size > 0)
1006adc586dMark Johnson			uetp = (ucode_ext_table_intel_t *)
1007adc586dMark Johnson			    &curbuf[header_size + body_size];
1008adc586dMark Johnson
1009adc586dMark Johnson		tmprc = ucode_match_intel(uusp->sig, &uusp->info, uhp, uetp);
1010adc586dMark Johnson
1011adc586dMark Johnson		/*
1012adc586dMark Johnson		 * Since we are searching through a big file
1013adc586dMark Johnson		 * containing microcode for pretty much all the
1014adc586dMark Johnson		 * processors, we are bound to get EM_NOMATCH
1015adc586dMark Johnson		 * at one point.  However, if we return
1016adc586dMark Johnson		 * EM_NOMATCH to users, it will really confuse
1017adc586dMark Johnson		 * them.  Therefore, if we ever find a match of
1018adc586dMark Johnson		 * a lower rev, we will set return code to
1019adc586dMark Johnson		 * EM_HIGHERREV.
1020adc586dMark Johnson		 */
1021adc586dMark Johnson		if (tmprc == EM_HIGHERREV)
1022adc586dMark Johnson			search_rc = EM_HIGHERREV;
1023adc586dMark Johnson
1024adc586dMark Johnson		if (tmprc == EM_OK &&
1025adc586dMark Johnson		    uusp->expected_rev < uhp->uh_rev) {
1026adc586dMark Johnson#ifndef __xpv
1027adc586dMark Johnson			uusp->ucodep = (uint8_t *)&curbuf[header_size];
1028adc586dMark Johnson#else
1029adc586dMark Johnson			uusp->ucodep = (uint8_t *)curbuf;
1030adc586dMark Johnson#endif
1031adc586dMark Johnson			uusp->usize =
1032adc586dMark Johnson			    UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
1033adc586dMark Johnson			uusp->expected_rev = uhp->uh_rev;
1034adc586dMark Johnson			found = 1;
1035adc586dMark Johnson		}
1036adc586dMark Johnson
1037adc586dMark Johnson		remaining -= total_size;
1038adc586dMark Johnson	}
1039adc586dMark Johnson
1040adc586dMark Johnson	if (!found)
1041adc586dMark Johnson		return (search_rc);
1042adc586dMark Johnson
1043adc586dMark Johnson	return (EM_OK);
1044adc586dMark Johnson}
10452449e17sherrym/*
10462449e17sherrym * Entry point to microcode update from the ucode_drv driver.
10472449e17sherrym *
10482449e17sherrym * Returns EM_OK on success, corresponding error code on failure.
10492449e17sherrym */
10502449e17sherrymucode_errno_t
10512449e17sherrymucode_update(uint8_t *ucodep, int size)
10522449e17sherrym{
10532449e17sherrym	int		found = 0;
10542449e17sherrym	processorid_t	id;
1055adc586dMark Johnson	ucode_update_t	cached = { 0 };
1056adc586dMark Johnson	ucode_update_t	*cachedp = NULL;
10572449e17sherrym	ucode_errno_t	rc = EM_OK;
10582449e17sherrym	ucode_errno_t	search_rc = EM_NOMATCH; /* search result */
10592449e17sherrym	cpuset_t cpuset;
10602449e17sherrym
1061adc586dMark Johnson	ASSERT(ucode);
10622449e17sherrym	ASSERT(ucodep);
10632449e17sherrym	CPUSET_ZERO(cpuset);
10642449e17sherrym
1065adc586dMark Johnson	if (!ucode->capable(CPU))
10662449e17sherrym		return (EM_NOTSUP);
10672449e17sherrym
10682449e17sherrym	mutex_enter(&cpu_lock);
10692449e17sherrym
10702449e17sherrym	for (id = 0; id < max_ncpus; id++) {
10712449e17sherrym		cpu_t *cpu;
1072adc586dMark Johnson		ucode_update_t uus = { 0 };
1073adc586dMark Johnson		ucode_update_t *uusp = &uus;
10742449e17sherrym
10752449e17sherrym		/*
10762449e17sherrym		 * If there is no such CPU or it is not xcall ready, skip it.
10772449e17sherrym		 */
10782449e17sherrym		if ((cpu = cpu_get(id)) == NULL ||
10792449e17sherrym		    !(cpu->cpu_flags & CPU_READY))
10802449e17sherrym			continue;
10812449e17sherrym
10822449e17sherrym		uusp->sig = cpuid_getsig(cpu);
10832449e17sherrym		bcopy(cpu->cpu_m.mcpu_ucode_info, &uusp->info,
10842449e17sherrym		    sizeof (uusp->info));
10852449e17sherrym
10862449e17sherrym		/*
10872449e17sherrym		 * If the current CPU has the same signature and platform
10882449e17sherrym		 * id as the previous one we processed, reuse the information.
10892449e17sherrym		 */
10902449e17sherrym		if (cachedp && cachedp->sig == cpuid_getsig(cpu) &&
10912449e17sherrym		    cachedp->info.cui_platid == uusp->info.cui_platid) {
10922449e17sherrym			uusp->ucodep = cachedp->ucodep;
10932449e17sherrym			uusp->expected_rev = cachedp->expected_rev;
10942449e17sherrym			/*
10952449e17sherrym			 * Intuitively we should check here to see whether the
10962449e17sherrym			 * running microcode rev is >= the expected rev, and
10972449e17sherrym			 * quit if it is.  But we choose to proceed with the
10982449e17sherrym			 * xcall regardless of the running version so that
10992449e17sherrym			 * the other threads in an HT processor can update
11002449e17sherrym			 * the cpu_ucode_info structure in machcpu.
11012449e17sherrym			 */
1102adc586dMark Johnson		} else if ((search_rc = ucode->extract(uusp, ucodep, size))
1103adc586dMark Johnson		    == EM_OK) {
1104adc586dMark Johnson			bcopy(uusp, &cached, sizeof (cached));
1105adc586dMark Johnson			cachedp = &cached;
1106adc586dMark Johnson			found = 1;
11072449e17sherrym		}
11082449e17sherrym
11092449e17sherrym		/* Nothing to do */
11102449e17sherrym		if (uusp->ucodep == NULL)
11112449e17sherrym			continue;
11122449e17sherrym
1113882a7afMark Johnson#ifdef	__xpv
1114882a7afMark Johnson		/*
1115882a7afMark Johnson		 * for i86xpv, the hypervisor will update all the CPUs.
1116882a7afMark Johnson		 * the hypervisor wants the header, data, and extended
1117882a7afMark Johnson		 * signature tables. ucode_write will just read in the
1118882a7afMark Johnson		 * updated version on all the CPUs after the update has
1119882a7afMark Johnson		 * completed.
1120882a7afMark Johnson		 */
1121c9b5d7dMark Johnson		if (id == 0) {
1122adc586dMark Johnson			ucode_load_xpv(uusp);
1123c9b5d7dMark Johnson		}
1124882a7afMark Johnson#endif
1125882a7afMark Johnson
11262449e17sherrym		CPUSET_ADD(cpuset, id);
11272449e17sherrym		kpreempt_disable();
1128f34a717Joe Bonasera		xc_sync((xc_arg_t)uusp, 0, 0, CPUSET2BV(cpuset), ucode_write);
11292449e17sherrym		kpreempt_enable();
11302449e17sherrym		CPUSET_DEL(cpuset, id);
11312449e17sherrym
11325e53ed3Hans Rosenfeld		if (uusp->new_rev != 0 && uusp->info.cui_rev == uusp->new_rev &&
11335e53ed3Hans Rosenfeld		    !ucode_force_update) {
11340ba6f73Mark Johnson			rc = EM_HIGHERREV;
11350ba6f73Mark Johnson		} else if ((uusp->new_rev == 0) || (uusp->expected_rev != 0 &&
11360ba6f73Mark Johnson		    uusp->expected_rev != uusp->new_rev)) {
11372449e17sherrym			cmn_err(CE_WARN, ucode_failure_fmt,
11382449e17sherrym			    id, uusp->info.cui_rev, uusp->expected_rev);
11392449e17sherrym			rc = EM_UPDATE;
11400ba6f73Mark Johnson		} else {
11410ba6f73Mark Johnson			cmn_err(CE_CONT, ucode_success_fmt,
11420ba6f73Mark Johnson			    id, uusp->info.cui_rev, uusp->new_rev);
11432449e17sherrym		}
11442449e17sherrym	}
11452449e17sherrym
11462449e17sherrym	mutex_exit(&cpu_lock);
11472449e17sherrym
114801add34Robert Mustacchi	if (!found) {
11492449e17sherrym		rc = search_rc;
115001add34Robert Mustacchi	} else if (rc == EM_OK) {
115101add34Robert Mustacchi		cpuid_post_ucodeadm();
115201add34Robert Mustacchi	}
11532449e17sherrym
11542449e17sherrym	return (rc);
11552449e17sherrym}
11562449e17sherrym
11572449e17sherrym/*
11582449e17sherrym * Initialize mcpu_ucode_info, and perform microcode update if necessary.
11592449e17sherrym * This is the entry point from boot path where pointer to CPU structure
11602449e17sherrym * is available.
11612449e17sherrym *
11622449e17sherrym * cpuid_info must be initialized before ucode_check can be called.
11632449e17sherrym */
11642449e17sherrymvoid
11652449e17sherrymucode_check(cpu_t *cp)
11662449e17sherrym{
1167adc586dMark Johnson	cpu_ucode_info_t *uinfop;
11682449e17sherrym	ucode_errno_t rc = EM_OK;
1169adc586dMark Johnson	uint32_t new_rev = 0;
11702449e17sherrym
11712449e17sherrym	ASSERT(cp);
1172a311483Gerry Liu	/*
1173a311483Gerry Liu	 * Space statically allocated for BSP, ensure pointer is set
1174a311483Gerry Liu	 */
1175a311483Gerry Liu	if (cp->cpu_id == 0 && cp->cpu_m.mcpu_ucode_info == NULL)
11762449e17sherrym		cp->cpu_m.mcpu_ucode_info = &cpu_ucode_info0;
11772449e17sherrym
11782449e17sherrym	uinfop = cp->cpu_m.mcpu_ucode_info;
11792449e17sherrym	ASSERT(uinfop);
11802449e17sherrym
1181adc586dMark Johnson	/* set up function pointers if not already done */
1182adc586dMark Johnson	if (!ucode)
1183adc586dMark Johnson		switch (cpuid_getvendor(cp)) {
1184adc586dMark Johnson		case X86_VENDOR_AMD:
1185adc586dMark Johnson			ucode = &ucode_amd;
1186adc586dMark Johnson			break;
1187adc586dMark Johnson		case X86_VENDOR_Intel:
1188adc586dMark Johnson			ucode = &ucode_intel;
1189adc586dMark Johnson			break;
1190adc586dMark Johnson		default:
1191126b0a7Mark Johnson			ucode = NULL;
1192adc586dMark Johnson			return;
1193adc586dMark Johnson		}
1194adc586dMark Johnson
1195adc586dMark Johnson	if (!ucode->capable(cp))
11962449e17sherrym		return;
11972449e17sherrym
11982449e17sherrym	/*
11992449e17sherrym	 * The MSR_INTC_PLATFORM_ID is supported in Celeron and Xeon
12002449e17sherrym	 * (Family 6, model 5 and above) and all processors after.
12012449e17sherrym	 */
1202adc586dMark Johnson	if ((cpuid_getvendor(cp) == X86_VENDOR_Intel) &&
1203adc586dMark Johnson	    ((cpuid_getmodel(cp) >= 5) || (cpuid_getfamily(cp) > 6))) {
12042449e17sherrym		uinfop->cui_platid = 1 << ((rdmsr(MSR_INTC_PLATFORM_ID) >>
12052449e17sherrym		    INTC_PLATFORM_ID_SHIFT) & INTC_PLATFORM_ID_MASK);
12062449e17sherrym	}
12072449e17sherrym
1208adc586dMark Johnson	ucode->read_rev(uinfop);
12092449e17sherrym
1210c9b5d7dMark Johnson#ifdef	__xpv
1211c9b5d7dMark Johnson	/*
1212c9b5d7dMark Johnson	 * for i86xpv, the hypervisor will update all the CPUs. We only need
1213c9b5d7dMark Johnson	 * do do this on one of the CPUs (and there always is a CPU 0).
1214c9b5d7dMark Johnson	 */
1215c9b5d7dMark Johnson	if (cp->cpu_id != 0) {
1216c9b5d7dMark Johnson		return;
1217c9b5d7dMark Johnson	}
1218c9b5d7dMark Johnson#endif
1219c9b5d7dMark Johnson
12202449e17sherrym	/*
12212449e17sherrym	 * Check to see if we need ucode update
12222449e17sherrym	 */
1223adc586dMark Johnson	if ((rc = ucode->locate(cp, uinfop, &ucodefile)) == EM_OK) {
1224adc586dMark Johnson		new_rev = ucode->load(&ucodefile, uinfop, cp);
12252449e17sherrym
1226adc586dMark Johnson		if (uinfop->cui_rev != new_rev)
12272449e17sherrym			cmn_err(CE_WARN, ucode_failure_fmt, cp->cpu_id,
1228adc586dMark Johnson			    uinfop->cui_rev, new_rev);
12292449e17sherrym	}
12302449e17sherrym
12312449e17sherrym	/*
12322449e17sherrym	 * If we fail to find a match for any reason, free the file structure
12332449e17sherrym	 * just in case we have read in a partial file.
12342449e17sherrym	 *
12352449e17sherrym	 * Since the scratch memory for holding the microcode for the boot CPU
12362449e17sherrym	 * came from BOP_ALLOC, we will reset the data structure as if we
12372449e17sherrym	 * never did the allocation so we don't have to keep track of this
12382449e17sherrym	 * special chunk of memory.  We free the memory used for the rest
12392449e17sherrym	 * of the CPUs in start_other_cpus().
12402449e17sherrym	 */
12412449e17sherrym	if (rc != EM_OK || cp->cpu_id == 0)
1242adc586dMark Johnson		ucode->file_reset(&ucodefile, cp->cpu_id);
12432449e17sherrym}
12442449e17sherrym
12452449e17sherrym/*
12462449e17sherrym * Returns microcode revision from the machcpu structure.
12472449e17sherrym */
12482449e17sherrymucode_errno_t
12492449e17sherrymucode_get_rev(uint32_t *revp)
12502449e17sherrym{
12512449e17sherrym	int i;
12522449e17sherrym
1253adc586dMark Johnson	ASSERT(ucode);
12542449e17sherrym	ASSERT(revp);
12552449e17sherrym
1256adc586dMark Johnson	if (!ucode->capable(CPU))
12572449e17sherrym		return (EM_NOTSUP);
12582449e17sherrym
12592449e17sherrym	mutex_enter(&cpu_lock);
12602449e17sherrym	for (i = 0; i < max_ncpus; i++) {
12612449e17sherrym		cpu_t *cpu;
12622449e17sherrym
12632449e17sherrym		if ((cpu = cpu_get(i)) == NULL)
12642449e17sherrym			continue;
12652449e17sherrym
12662449e17sherrym		revp[i] = cpu->cpu_m.mcpu_ucode_info->cui_rev;
12672449e17sherrym	}
12682449e17sherrym	mutex_exit(&cpu_lock);
12692449e17sherrym
12702449e17sherrym	return (EM_OK);
12712449e17sherrym}
1272