14581Ssherrym /* 24581Ssherrym * CDDL HEADER START 34581Ssherrym * 44581Ssherrym * The contents of this file are subject to the terms of the 54581Ssherrym * Common Development and Distribution License (the "License"). 64581Ssherrym * You may not use this file except in compliance with the License. 74581Ssherrym * 84581Ssherrym * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 94581Ssherrym * or http://www.opensolaris.org/os/licensing. 104581Ssherrym * See the License for the specific language governing permissions 114581Ssherrym * and limitations under the License. 124581Ssherrym * 134581Ssherrym * When distributing Covered Code, include this CDDL HEADER in each 144581Ssherrym * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 154581Ssherrym * If applicable, add the following below this CDDL HEADER, with the 164581Ssherrym * fields enclosed by brackets "[]" replaced with your own identifying 174581Ssherrym * information: Portions Copyright [yyyy] [name of copyright owner] 184581Ssherrym * 194581Ssherrym * CDDL HEADER END 204581Ssherrym */ 214581Ssherrym 224581Ssherrym /* 23*8647SMark.Johnson@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 244581Ssherrym * Use is subject to license terms. 254581Ssherrym */ 264581Ssherrym 274581Ssherrym #include <sys/asm_linkage.h> 284581Ssherrym #include <sys/bootconf.h> 294581Ssherrym #include <sys/cpuvar.h> 304581Ssherrym #include <sys/cmn_err.h> 314581Ssherrym #include <sys/controlregs.h> 324581Ssherrym #include <sys/debug.h> 334581Ssherrym #include <sys/kobj.h> 344581Ssherrym #include <sys/kobj_impl.h> 354581Ssherrym #include <sys/machsystm.h> 364581Ssherrym #include <sys/param.h> 374581Ssherrym #include <sys/machparam.h> 384581Ssherrym #include <sys/promif.h> 394581Ssherrym #include <sys/sysmacros.h> 404581Ssherrym #include <sys/systm.h> 414581Ssherrym #include <sys/types.h> 424581Ssherrym #include <sys/thread.h> 434581Ssherrym #include <sys/ucode.h> 444581Ssherrym #include <sys/x86_archext.h> 454581Ssherrym #include <sys/x_call.h> 465084Sjohnlev #ifdef __xpv 475084Sjohnlev #include <sys/hypervisor.h> 485084Sjohnlev #endif 494581Ssherrym 504581Ssherrym /* 517605SMark.Johnson@Sun.COM * AMD-specific equivalence table 524581Ssherrym */ 537605SMark.Johnson@Sun.COM static ucode_eqtbl_amd_t *ucode_eqtbl_amd; 544581Ssherrym 554581Ssherrym /* 564581Ssherrym * mcpu_ucode_info for the boot CPU. Statically allocated. 574581Ssherrym */ 584581Ssherrym static struct cpu_ucode_info cpu_ucode_info0; 594581Ssherrym 607605SMark.Johnson@Sun.COM static ucode_file_t ucodefile; 617605SMark.Johnson@Sun.COM 627605SMark.Johnson@Sun.COM static void* ucode_zalloc(processorid_t, size_t); 637605SMark.Johnson@Sun.COM static void ucode_free(processorid_t, void *, size_t); 647605SMark.Johnson@Sun.COM 657605SMark.Johnson@Sun.COM static int ucode_capable_amd(cpu_t *); 667605SMark.Johnson@Sun.COM static int ucode_capable_intel(cpu_t *); 677605SMark.Johnson@Sun.COM 687605SMark.Johnson@Sun.COM static ucode_errno_t ucode_extract_amd(ucode_update_t *, uint8_t *, int); 697605SMark.Johnson@Sun.COM static ucode_errno_t ucode_extract_intel(ucode_update_t *, uint8_t *, 707605SMark.Johnson@Sun.COM int); 717605SMark.Johnson@Sun.COM 727605SMark.Johnson@Sun.COM static void ucode_file_reset_amd(ucode_file_t *, processorid_t); 737605SMark.Johnson@Sun.COM static void ucode_file_reset_intel(ucode_file_t *, processorid_t); 747605SMark.Johnson@Sun.COM 757605SMark.Johnson@Sun.COM static uint32_t ucode_load_amd(ucode_file_t *, cpu_ucode_info_t *, cpu_t *); 767605SMark.Johnson@Sun.COM static uint32_t ucode_load_intel(ucode_file_t *, cpu_ucode_info_t *, cpu_t *); 777605SMark.Johnson@Sun.COM 787605SMark.Johnson@Sun.COM #ifdef __xpv 797605SMark.Johnson@Sun.COM static void ucode_load_xpv(ucode_update_t *); 80*8647SMark.Johnson@Sun.COM static void ucode_chipset_amd(uint8_t *, int); 817605SMark.Johnson@Sun.COM #endif 827605SMark.Johnson@Sun.COM 83*8647SMark.Johnson@Sun.COM static int ucode_equiv_cpu_amd(cpu_t *, uint16_t *); 847605SMark.Johnson@Sun.COM 857605SMark.Johnson@Sun.COM static ucode_errno_t ucode_locate_amd(cpu_t *, cpu_ucode_info_t *, 867605SMark.Johnson@Sun.COM ucode_file_t *); 877605SMark.Johnson@Sun.COM static ucode_errno_t ucode_locate_intel(cpu_t *, cpu_ucode_info_t *, 887605SMark.Johnson@Sun.COM ucode_file_t *); 894581Ssherrym 90*8647SMark.Johnson@Sun.COM #ifndef __xpv 91*8647SMark.Johnson@Sun.COM static ucode_errno_t ucode_match_amd(uint16_t, cpu_ucode_info_t *, 927605SMark.Johnson@Sun.COM ucode_file_amd_t *, int); 93*8647SMark.Johnson@Sun.COM #endif 947605SMark.Johnson@Sun.COM static ucode_errno_t ucode_match_intel(int, cpu_ucode_info_t *, 957605SMark.Johnson@Sun.COM ucode_header_intel_t *, ucode_ext_table_intel_t *); 967605SMark.Johnson@Sun.COM 977605SMark.Johnson@Sun.COM static void ucode_read_rev_amd(cpu_ucode_info_t *); 987605SMark.Johnson@Sun.COM static void ucode_read_rev_intel(cpu_ucode_info_t *); 997605SMark.Johnson@Sun.COM 1007605SMark.Johnson@Sun.COM static const struct ucode_ops ucode_amd = { 1017605SMark.Johnson@Sun.COM MSR_AMD_PATCHLOADER, 1027605SMark.Johnson@Sun.COM ucode_capable_amd, 1037605SMark.Johnson@Sun.COM ucode_file_reset_amd, 1047605SMark.Johnson@Sun.COM ucode_read_rev_amd, 1057605SMark.Johnson@Sun.COM ucode_load_amd, 1067605SMark.Johnson@Sun.COM ucode_validate_amd, 1077605SMark.Johnson@Sun.COM ucode_extract_amd, 1087605SMark.Johnson@Sun.COM ucode_locate_amd 1097605SMark.Johnson@Sun.COM }; 1107605SMark.Johnson@Sun.COM 1117605SMark.Johnson@Sun.COM static const struct ucode_ops ucode_intel = { 1127605SMark.Johnson@Sun.COM MSR_INTC_UCODE_WRITE, 1137605SMark.Johnson@Sun.COM ucode_capable_intel, 1147605SMark.Johnson@Sun.COM ucode_file_reset_intel, 1157605SMark.Johnson@Sun.COM ucode_read_rev_intel, 1167605SMark.Johnson@Sun.COM ucode_load_intel, 1177605SMark.Johnson@Sun.COM ucode_validate_intel, 1187605SMark.Johnson@Sun.COM ucode_extract_intel, 1197605SMark.Johnson@Sun.COM ucode_locate_intel 1207605SMark.Johnson@Sun.COM }; 1217605SMark.Johnson@Sun.COM 1227605SMark.Johnson@Sun.COM const struct ucode_ops *ucode; 1234581Ssherrym 1244581Ssherrym static const char ucode_failure_fmt[] = 1256519Ssherrym "cpu%d: failed to update microcode from version 0x%x to 0x%x\n"; 1264581Ssherrym static const char ucode_success_fmt[] = 1276519Ssherrym "?cpu%d: microcode has been updated from version 0x%x to 0x%x\n"; 1284581Ssherrym 1294581Ssherrym /* 1304581Ssherrym * Force flag. If set, the first microcode binary that matches 1314581Ssherrym * signature and platform id will be used for microcode update, 1324581Ssherrym * regardless of version. Should only be used for debugging. 1334581Ssherrym */ 1344581Ssherrym int ucode_force_update = 0; 1354581Ssherrym 1364581Ssherrym /* 1374581Ssherrym * Allocate space for mcpu_ucode_info in the machcpu structure 1384581Ssherrym * for all non-boot CPUs. 1394581Ssherrym */ 1404581Ssherrym void 1414581Ssherrym ucode_alloc_space(cpu_t *cp) 1424581Ssherrym { 1434581Ssherrym ASSERT(cp->cpu_id != 0); 1444581Ssherrym cp->cpu_m.mcpu_ucode_info = 1454581Ssherrym kmem_zalloc(sizeof (*cp->cpu_m.mcpu_ucode_info), KM_SLEEP); 1464581Ssherrym } 1474581Ssherrym 1484581Ssherrym void 1494581Ssherrym ucode_free_space(cpu_t *cp) 1504581Ssherrym { 1514581Ssherrym ASSERT(cp->cpu_id != 0); 1524581Ssherrym kmem_free(cp->cpu_m.mcpu_ucode_info, 1534581Ssherrym sizeof (*cp->cpu_m.mcpu_ucode_info)); 1544581Ssherrym } 1554581Ssherrym 1564581Ssherrym /* 1574581Ssherrym * Called when we are done with microcode update on all processors to free up 1584581Ssherrym * space allocated for the microcode file. 1594581Ssherrym */ 1604581Ssherrym void 1617605SMark.Johnson@Sun.COM ucode_cleanup() 1624581Ssherrym { 1637605SMark.Johnson@Sun.COM ASSERT(ucode); 1647605SMark.Johnson@Sun.COM 1657605SMark.Johnson@Sun.COM ucode->file_reset(&ucodefile, -1); 1667605SMark.Johnson@Sun.COM } 1677605SMark.Johnson@Sun.COM 1687605SMark.Johnson@Sun.COM /* 1697605SMark.Johnson@Sun.COM * Allocate/free a buffer used to hold ucode data. Space for the boot CPU is 1707605SMark.Johnson@Sun.COM * allocated with BOP_ALLOC() and does not require a free. 1717605SMark.Johnson@Sun.COM */ 1727605SMark.Johnson@Sun.COM static void* 1737605SMark.Johnson@Sun.COM ucode_zalloc(processorid_t id, size_t size) 1747605SMark.Johnson@Sun.COM { 1757605SMark.Johnson@Sun.COM if (id) 1767605SMark.Johnson@Sun.COM return (kmem_zalloc(size, KM_NOSLEEP)); 1777605SMark.Johnson@Sun.COM 1787605SMark.Johnson@Sun.COM /* BOP_ALLOC() failure results in panic */ 1797605SMark.Johnson@Sun.COM return (BOP_ALLOC(bootops, NULL, size, MMU_PAGESIZE)); 1807605SMark.Johnson@Sun.COM } 1817605SMark.Johnson@Sun.COM 1827605SMark.Johnson@Sun.COM static void 1837605SMark.Johnson@Sun.COM ucode_free(processorid_t id, void* buf, size_t size) 1847605SMark.Johnson@Sun.COM { 1857605SMark.Johnson@Sun.COM if (id) 1867605SMark.Johnson@Sun.COM kmem_free(buf, size); 1874581Ssherrym } 1884581Ssherrym 1894581Ssherrym /* 1904581Ssherrym * Check whether or not a processor is capable of microcode operations 1914581Ssherrym * Returns 1 if it is capable, 0 if not. 1927605SMark.Johnson@Sun.COM * 1937605SMark.Johnson@Sun.COM * At this point we only support microcode update for: 1947605SMark.Johnson@Sun.COM * - Intel processors family 6 and above, and 1957605SMark.Johnson@Sun.COM * - AMD processors family 0x10 and above. 1967605SMark.Johnson@Sun.COM * 1977605SMark.Johnson@Sun.COM * We also assume that we don't support a mix of Intel and 1987605SMark.Johnson@Sun.COM * AMD processors in the same box. 1997605SMark.Johnson@Sun.COM * 2007605SMark.Johnson@Sun.COM * An i86xpv guest domain can't update the microcode. 2014581Ssherrym */ 2027605SMark.Johnson@Sun.COM /*ARGSUSED*/ 2034581Ssherrym static int 2047605SMark.Johnson@Sun.COM ucode_capable_amd(cpu_t *cp) 2054581Ssherrym { 2067605SMark.Johnson@Sun.COM #ifndef __xpv 2077605SMark.Johnson@Sun.COM extern int xpv_is_hvm; 2087605SMark.Johnson@Sun.COM if (xpv_is_hvm) { 2097605SMark.Johnson@Sun.COM return (0); 2107605SMark.Johnson@Sun.COM } 2117605SMark.Johnson@Sun.COM #else 2127605SMark.Johnson@Sun.COM if (!DOMAIN_IS_INITDOMAIN(xen_info)) { 2137605SMark.Johnson@Sun.COM return (0); 2147605SMark.Johnson@Sun.COM } 215*8647SMark.Johnson@Sun.COM #endif 216*8647SMark.Johnson@Sun.COM return (cpuid_getfamily(cp) >= 0x10); 217*8647SMark.Johnson@Sun.COM } 2187605SMark.Johnson@Sun.COM 2197605SMark.Johnson@Sun.COM static int 2207605SMark.Johnson@Sun.COM ucode_capable_intel(cpu_t *cp) 2217605SMark.Johnson@Sun.COM { 2227347SMark.Johnson@Sun.COM #ifndef __xpv 2237347SMark.Johnson@Sun.COM extern int xpv_is_hvm; 2247347SMark.Johnson@Sun.COM if (xpv_is_hvm) { 2257347SMark.Johnson@Sun.COM return (0); 2267347SMark.Johnson@Sun.COM } 2277347SMark.Johnson@Sun.COM #else 2285084Sjohnlev if (!DOMAIN_IS_INITDOMAIN(xen_info)) { 2295084Sjohnlev return (0); 2305084Sjohnlev } 2315084Sjohnlev #endif 2327605SMark.Johnson@Sun.COM return (cpuid_getfamily(cp) >= 6); 2334581Ssherrym } 2344581Ssherrym 2354581Ssherrym /* 2364581Ssherrym * Called when it is no longer necessary to keep the microcode around, 2374581Ssherrym * or when the cached microcode doesn't match the CPU being processed. 2384581Ssherrym */ 2394581Ssherrym static void 2407605SMark.Johnson@Sun.COM ucode_file_reset_amd(ucode_file_t *ufp, processorid_t id) 2414581Ssherrym { 2427605SMark.Johnson@Sun.COM ucode_file_amd_t *ucodefp = ufp->amd; 2434581Ssherrym 2444581Ssherrym if (ucodefp == NULL) 2454581Ssherrym return; 2464581Ssherrym 2477605SMark.Johnson@Sun.COM ucode_free(id, ucodefp, sizeof (ucode_file_amd_t)); 2487605SMark.Johnson@Sun.COM ufp->amd = NULL; 2497605SMark.Johnson@Sun.COM } 2507605SMark.Johnson@Sun.COM 2517605SMark.Johnson@Sun.COM static void 2527605SMark.Johnson@Sun.COM ucode_file_reset_intel(ucode_file_t *ufp, processorid_t id) 2537605SMark.Johnson@Sun.COM { 2547605SMark.Johnson@Sun.COM ucode_file_intel_t *ucodefp = &ufp->intel; 2557605SMark.Johnson@Sun.COM int total_size, body_size; 2567605SMark.Johnson@Sun.COM 2577605SMark.Johnson@Sun.COM if (ucodefp == NULL || ucodefp->uf_header == NULL) 2587605SMark.Johnson@Sun.COM return; 2597605SMark.Johnson@Sun.COM 2607605SMark.Johnson@Sun.COM total_size = UCODE_TOTAL_SIZE_INTEL(ucodefp->uf_header->uh_total_size); 2617605SMark.Johnson@Sun.COM body_size = UCODE_BODY_SIZE_INTEL(ucodefp->uf_header->uh_body_size); 2624581Ssherrym if (ucodefp->uf_body) { 2637605SMark.Johnson@Sun.COM ucode_free(id, ucodefp->uf_body, body_size); 2644581Ssherrym ucodefp->uf_body = NULL; 2654581Ssherrym } 2664581Ssherrym 2674581Ssherrym if (ucodefp->uf_ext_table) { 2687605SMark.Johnson@Sun.COM int size = total_size - body_size - UCODE_HEADER_SIZE_INTEL; 2697605SMark.Johnson@Sun.COM 2707605SMark.Johnson@Sun.COM ucode_free(id, ucodefp->uf_ext_table, size); 2714581Ssherrym ucodefp->uf_ext_table = NULL; 2724581Ssherrym } 2734581Ssherrym 2747605SMark.Johnson@Sun.COM ucode_free(id, ucodefp->uf_header, UCODE_HEADER_SIZE_INTEL); 2757605SMark.Johnson@Sun.COM ucodefp->uf_header = NULL; 2767605SMark.Johnson@Sun.COM } 2777605SMark.Johnson@Sun.COM 2787605SMark.Johnson@Sun.COM /* 2797605SMark.Johnson@Sun.COM * Find the equivalent CPU id in the equivalence table. 2807605SMark.Johnson@Sun.COM */ 2817605SMark.Johnson@Sun.COM static int 282*8647SMark.Johnson@Sun.COM ucode_equiv_cpu_amd(cpu_t *cp, uint16_t *eq_sig) 2837605SMark.Johnson@Sun.COM { 2847605SMark.Johnson@Sun.COM char name[MAXPATHLEN]; 2857605SMark.Johnson@Sun.COM intptr_t fd; 2867605SMark.Johnson@Sun.COM int count; 2877605SMark.Johnson@Sun.COM int offset = 0, cpi_sig = cpuid_getsig(cp); 2887605SMark.Johnson@Sun.COM ucode_eqtbl_amd_t *eqtbl = ucode_eqtbl_amd; 2897605SMark.Johnson@Sun.COM 2907605SMark.Johnson@Sun.COM (void) snprintf(name, MAXPATHLEN, "/%s/%s/equivalence-table", 2917605SMark.Johnson@Sun.COM UCODE_INSTALL_PATH, cpuid_getvendorstr(cp)); 2927605SMark.Johnson@Sun.COM 2937605SMark.Johnson@Sun.COM /* 2947605SMark.Johnson@Sun.COM * No kmem_zalloc() etc. available on boot cpu. 2957605SMark.Johnson@Sun.COM */ 2967605SMark.Johnson@Sun.COM if (cp->cpu_id == 0) { 2977605SMark.Johnson@Sun.COM if ((fd = kobj_open(name)) == -1) 2987605SMark.Johnson@Sun.COM return (EM_OPENFILE); 2997605SMark.Johnson@Sun.COM /* ucode_zalloc() cannot fail on boot cpu */ 3007605SMark.Johnson@Sun.COM eqtbl = ucode_zalloc(cp->cpu_id, sizeof (*eqtbl)); 3017605SMark.Johnson@Sun.COM ASSERT(eqtbl); 3027605SMark.Johnson@Sun.COM do { 3037605SMark.Johnson@Sun.COM count = kobj_read(fd, (int8_t *)eqtbl, 3047605SMark.Johnson@Sun.COM sizeof (*eqtbl), offset); 3057605SMark.Johnson@Sun.COM if (count != sizeof (*eqtbl)) { 3067605SMark.Johnson@Sun.COM (void) kobj_close(fd); 3077605SMark.Johnson@Sun.COM return (EM_HIGHERREV); 3087605SMark.Johnson@Sun.COM } 3097605SMark.Johnson@Sun.COM offset += count; 3107605SMark.Johnson@Sun.COM } while (eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != cpi_sig); 3117605SMark.Johnson@Sun.COM (void) kobj_close(fd); 3127605SMark.Johnson@Sun.COM } 3137605SMark.Johnson@Sun.COM 3147605SMark.Johnson@Sun.COM /* 3157605SMark.Johnson@Sun.COM * If not already done, load the equivalence table. 3167605SMark.Johnson@Sun.COM * Not done on boot CPU. 3177605SMark.Johnson@Sun.COM */ 3187605SMark.Johnson@Sun.COM if (eqtbl == NULL) { 3197605SMark.Johnson@Sun.COM struct _buf *eq; 3207605SMark.Johnson@Sun.COM uint64_t size; 3217605SMark.Johnson@Sun.COM 3227605SMark.Johnson@Sun.COM if ((eq = kobj_open_file(name)) == (struct _buf *)-1) 3237605SMark.Johnson@Sun.COM return (EM_OPENFILE); 3247605SMark.Johnson@Sun.COM 3257605SMark.Johnson@Sun.COM if (kobj_get_filesize(eq, &size) < 0) { 3267605SMark.Johnson@Sun.COM kobj_close_file(eq); 3277605SMark.Johnson@Sun.COM return (EM_OPENFILE); 3287605SMark.Johnson@Sun.COM } 3297605SMark.Johnson@Sun.COM 3307605SMark.Johnson@Sun.COM ucode_eqtbl_amd = kmem_zalloc(size, KM_NOSLEEP); 3317605SMark.Johnson@Sun.COM if (ucode_eqtbl_amd == NULL) { 3327605SMark.Johnson@Sun.COM kobj_close_file(eq); 3337605SMark.Johnson@Sun.COM return (EM_NOMEM); 3347605SMark.Johnson@Sun.COM } 3357605SMark.Johnson@Sun.COM 3367605SMark.Johnson@Sun.COM count = kobj_read_file(eq, (char *)ucode_eqtbl_amd, size, 0); 3377605SMark.Johnson@Sun.COM kobj_close_file(eq); 3387605SMark.Johnson@Sun.COM 3397605SMark.Johnson@Sun.COM if (count != size) 3407605SMark.Johnson@Sun.COM return (EM_FILESIZE); 3417605SMark.Johnson@Sun.COM } 3427605SMark.Johnson@Sun.COM 3437605SMark.Johnson@Sun.COM /* Get the equivalent CPU id. */ 3447605SMark.Johnson@Sun.COM if (cp->cpu_id) 3457605SMark.Johnson@Sun.COM for (eqtbl = ucode_eqtbl_amd; 3467605SMark.Johnson@Sun.COM eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != cpi_sig; 3477605SMark.Johnson@Sun.COM eqtbl++) 3487605SMark.Johnson@Sun.COM ; 3497605SMark.Johnson@Sun.COM 3507605SMark.Johnson@Sun.COM *eq_sig = eqtbl->ue_equiv_cpu; 3517605SMark.Johnson@Sun.COM 3527605SMark.Johnson@Sun.COM /* No equivalent CPU id found, assume outdated microcode file. */ 3537605SMark.Johnson@Sun.COM if (*eq_sig == 0) 3547605SMark.Johnson@Sun.COM return (EM_HIGHERREV); 3557605SMark.Johnson@Sun.COM 3567605SMark.Johnson@Sun.COM return (EM_OK); 3574581Ssherrym } 3584581Ssherrym 3594581Ssherrym /* 360*8647SMark.Johnson@Sun.COM * xVM cannot check for the presence of PCI devices. Look for chipset- 361*8647SMark.Johnson@Sun.COM * specific microcode patches in the container file and disable them 362*8647SMark.Johnson@Sun.COM * by setting their CPU revision to an invalid value. 363*8647SMark.Johnson@Sun.COM */ 364*8647SMark.Johnson@Sun.COM #ifdef __xpv 365*8647SMark.Johnson@Sun.COM static void 366*8647SMark.Johnson@Sun.COM ucode_chipset_amd(uint8_t *buf, int size) 367*8647SMark.Johnson@Sun.COM { 368*8647SMark.Johnson@Sun.COM ucode_header_amd_t *uh; 369*8647SMark.Johnson@Sun.COM uint32_t *ptr = (uint32_t *)buf; 370*8647SMark.Johnson@Sun.COM int len = 0; 371*8647SMark.Johnson@Sun.COM 372*8647SMark.Johnson@Sun.COM /* skip to first microcode patch */ 373*8647SMark.Johnson@Sun.COM ptr += 2; len = *ptr++; ptr += len >> 2; size -= len; 374*8647SMark.Johnson@Sun.COM 375*8647SMark.Johnson@Sun.COM while (size >= sizeof (ucode_header_amd_t) + 8) { 376*8647SMark.Johnson@Sun.COM ptr++; len = *ptr++; 377*8647SMark.Johnson@Sun.COM uh = (ucode_header_amd_t *)ptr; 378*8647SMark.Johnson@Sun.COM ptr += len >> 2; size -= len; 379*8647SMark.Johnson@Sun.COM 380*8647SMark.Johnson@Sun.COM if (uh->uh_nb_id) { 381*8647SMark.Johnson@Sun.COM cmn_err(CE_WARN, "ignoring northbridge-specific ucode: " 382*8647SMark.Johnson@Sun.COM "chipset id %x, revision %x", 383*8647SMark.Johnson@Sun.COM uh->uh_nb_id, uh->uh_nb_rev); 384*8647SMark.Johnson@Sun.COM uh->uh_cpu_rev = 0xffff; 385*8647SMark.Johnson@Sun.COM } 386*8647SMark.Johnson@Sun.COM 387*8647SMark.Johnson@Sun.COM if (uh->uh_sb_id) { 388*8647SMark.Johnson@Sun.COM cmn_err(CE_WARN, "ignoring southbridge-specific ucode: " 389*8647SMark.Johnson@Sun.COM "chipset id %x, revision %x", 390*8647SMark.Johnson@Sun.COM uh->uh_sb_id, uh->uh_sb_rev); 391*8647SMark.Johnson@Sun.COM uh->uh_cpu_rev = 0xffff; 392*8647SMark.Johnson@Sun.COM } 393*8647SMark.Johnson@Sun.COM } 394*8647SMark.Johnson@Sun.COM } 395*8647SMark.Johnson@Sun.COM #endif 396*8647SMark.Johnson@Sun.COM 397*8647SMark.Johnson@Sun.COM /* 3984581Ssherrym * Populate the ucode file structure from microcode file corresponding to 3994581Ssherrym * this CPU, if exists. 4004581Ssherrym * 4014581Ssherrym * Return EM_OK on success, corresponding error code on failure. 4024581Ssherrym */ 403*8647SMark.Johnson@Sun.COM /*ARGSUSED*/ 4044581Ssherrym static ucode_errno_t 4057605SMark.Johnson@Sun.COM ucode_locate_amd(cpu_t *cp, cpu_ucode_info_t *uinfop, ucode_file_t *ufp) 4067605SMark.Johnson@Sun.COM { 4077605SMark.Johnson@Sun.COM char name[MAXPATHLEN]; 4087605SMark.Johnson@Sun.COM intptr_t fd; 409*8647SMark.Johnson@Sun.COM int count, rc; 4107605SMark.Johnson@Sun.COM ucode_file_amd_t *ucodefp = ufp->amd; 4117605SMark.Johnson@Sun.COM 412*8647SMark.Johnson@Sun.COM #ifndef __xpv 413*8647SMark.Johnson@Sun.COM uint16_t eq_sig = 0; 414*8647SMark.Johnson@Sun.COM int i; 415*8647SMark.Johnson@Sun.COM 4167605SMark.Johnson@Sun.COM /* get equivalent CPU id */ 4177605SMark.Johnson@Sun.COM if ((rc = ucode_equiv_cpu_amd(cp, &eq_sig)) != EM_OK) 4187605SMark.Johnson@Sun.COM return (rc); 4197605SMark.Johnson@Sun.COM 4207605SMark.Johnson@Sun.COM /* 4217605SMark.Johnson@Sun.COM * Allocate a buffer for the microcode patch. If the buffer has been 4227605SMark.Johnson@Sun.COM * allocated before, check for a matching microcode to avoid loading 4237605SMark.Johnson@Sun.COM * the file again. 4247605SMark.Johnson@Sun.COM */ 4257605SMark.Johnson@Sun.COM if (ucodefp == NULL) 4267605SMark.Johnson@Sun.COM ucodefp = ucode_zalloc(cp->cpu_id, sizeof (*ucodefp)); 4277605SMark.Johnson@Sun.COM else if (ucode_match_amd(eq_sig, uinfop, ucodefp, sizeof (*ucodefp)) 4287605SMark.Johnson@Sun.COM == EM_OK) 4297605SMark.Johnson@Sun.COM return (EM_OK); 4307605SMark.Johnson@Sun.COM 4317605SMark.Johnson@Sun.COM if (ucodefp == NULL) 4327605SMark.Johnson@Sun.COM return (EM_NOMEM); 4337605SMark.Johnson@Sun.COM 4347605SMark.Johnson@Sun.COM ufp->amd = ucodefp; 4357605SMark.Johnson@Sun.COM 4367605SMark.Johnson@Sun.COM /* 4377605SMark.Johnson@Sun.COM * Find the patch for this CPU. The patch files are named XXXX-YY, where 4387605SMark.Johnson@Sun.COM * XXXX is the equivalent CPU id and YY is the running patch number. 4397605SMark.Johnson@Sun.COM * Patches specific to certain chipsets are guaranteed to have lower 4407605SMark.Johnson@Sun.COM * numbers than less specific patches, so we can just load the first 4417605SMark.Johnson@Sun.COM * patch that matches. 4427605SMark.Johnson@Sun.COM */ 4437605SMark.Johnson@Sun.COM 4447605SMark.Johnson@Sun.COM for (i = 0; i < 0xff; i++) { 4457605SMark.Johnson@Sun.COM (void) snprintf(name, MAXPATHLEN, "/%s/%s/%04X-%02X", 4467605SMark.Johnson@Sun.COM UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), eq_sig, i); 4477605SMark.Johnson@Sun.COM if ((fd = kobj_open(name)) == -1) 4487605SMark.Johnson@Sun.COM return (EM_NOMATCH); 4497605SMark.Johnson@Sun.COM count = kobj_read(fd, (char *)ucodefp, sizeof (*ucodefp), 0); 4507605SMark.Johnson@Sun.COM (void) kobj_close(fd); 4517605SMark.Johnson@Sun.COM 4527605SMark.Johnson@Sun.COM if (ucode_match_amd(eq_sig, uinfop, ucodefp, count) == EM_OK) 4537605SMark.Johnson@Sun.COM return (EM_OK); 4547605SMark.Johnson@Sun.COM } 4557605SMark.Johnson@Sun.COM return (EM_NOMATCH); 456*8647SMark.Johnson@Sun.COM #else 457*8647SMark.Johnson@Sun.COM int size = 0; 458*8647SMark.Johnson@Sun.COM char c; 459*8647SMark.Johnson@Sun.COM 460*8647SMark.Johnson@Sun.COM /* 461*8647SMark.Johnson@Sun.COM * The xVM case is special. To support mixed-revision systems, the 462*8647SMark.Johnson@Sun.COM * hypervisor will choose which patch to load for which CPU, so the 463*8647SMark.Johnson@Sun.COM * whole microcode patch container file will have to be loaded. 464*8647SMark.Johnson@Sun.COM * 465*8647SMark.Johnson@Sun.COM * Since this code is only run on the boot cpu, we don't have to care 466*8647SMark.Johnson@Sun.COM * about failing ucode_zalloc() or freeing allocated memory. 467*8647SMark.Johnson@Sun.COM */ 468*8647SMark.Johnson@Sun.COM if (cp->cpu_id != 0) 469*8647SMark.Johnson@Sun.COM return (EM_INVALIDARG); 470*8647SMark.Johnson@Sun.COM 471*8647SMark.Johnson@Sun.COM (void) snprintf(name, MAXPATHLEN, "/%s/%s/container", 472*8647SMark.Johnson@Sun.COM UCODE_INSTALL_PATH, cpuid_getvendorstr(cp)); 473*8647SMark.Johnson@Sun.COM 474*8647SMark.Johnson@Sun.COM if ((fd = kobj_open(name)) == -1) 475*8647SMark.Johnson@Sun.COM return (EM_OPENFILE); 476*8647SMark.Johnson@Sun.COM 477*8647SMark.Johnson@Sun.COM /* get the file size by counting bytes */ 478*8647SMark.Johnson@Sun.COM do { 479*8647SMark.Johnson@Sun.COM count = kobj_read(fd, &c, 1, size); 480*8647SMark.Johnson@Sun.COM size += count; 481*8647SMark.Johnson@Sun.COM } while (count); 482*8647SMark.Johnson@Sun.COM 483*8647SMark.Johnson@Sun.COM ucodefp = ucode_zalloc(cp->cpu_id, sizeof (*ucodefp)); 484*8647SMark.Johnson@Sun.COM ASSERT(ucodefp); 485*8647SMark.Johnson@Sun.COM ufp->amd = ucodefp; 486*8647SMark.Johnson@Sun.COM 487*8647SMark.Johnson@Sun.COM ucodefp->usize = size; 488*8647SMark.Johnson@Sun.COM ucodefp->ucodep = ucode_zalloc(cp->cpu_id, size); 489*8647SMark.Johnson@Sun.COM ASSERT(ucodefp->ucodep); 490*8647SMark.Johnson@Sun.COM 491*8647SMark.Johnson@Sun.COM /* load the microcode patch container file */ 492*8647SMark.Johnson@Sun.COM count = kobj_read(fd, (char *)ucodefp->ucodep, size, 0); 493*8647SMark.Johnson@Sun.COM (void) kobj_close(fd); 494*8647SMark.Johnson@Sun.COM 495*8647SMark.Johnson@Sun.COM if (count != size) 496*8647SMark.Johnson@Sun.COM return (EM_FILESIZE); 497*8647SMark.Johnson@Sun.COM 498*8647SMark.Johnson@Sun.COM /* make sure the container file is valid */ 499*8647SMark.Johnson@Sun.COM rc = ucode->validate(ucodefp->ucodep, ucodefp->usize); 500*8647SMark.Johnson@Sun.COM 501*8647SMark.Johnson@Sun.COM if (rc != EM_OK) 502*8647SMark.Johnson@Sun.COM return (rc); 503*8647SMark.Johnson@Sun.COM 504*8647SMark.Johnson@Sun.COM /* disable chipset-specific patches */ 505*8647SMark.Johnson@Sun.COM ucode_chipset_amd(ucodefp->ucodep, ucodefp->usize); 506*8647SMark.Johnson@Sun.COM 507*8647SMark.Johnson@Sun.COM return (EM_OK); 508*8647SMark.Johnson@Sun.COM #endif 5097605SMark.Johnson@Sun.COM } 5107605SMark.Johnson@Sun.COM 5117605SMark.Johnson@Sun.COM static ucode_errno_t 5127605SMark.Johnson@Sun.COM ucode_locate_intel(cpu_t *cp, cpu_ucode_info_t *uinfop, ucode_file_t *ufp) 5134581Ssherrym { 5144581Ssherrym char name[MAXPATHLEN]; 5154581Ssherrym intptr_t fd; 5164581Ssherrym int count; 5177605SMark.Johnson@Sun.COM int header_size = UCODE_HEADER_SIZE_INTEL; 5184581Ssherrym int cpi_sig = cpuid_getsig(cp); 5194581Ssherrym ucode_errno_t rc = EM_OK; 5207605SMark.Johnson@Sun.COM ucode_file_intel_t *ucodefp = &ufp->intel; 5217605SMark.Johnson@Sun.COM 5227605SMark.Johnson@Sun.COM ASSERT(ucode); 5234581Ssherrym 5244581Ssherrym /* 5254581Ssherrym * If the microcode matches the CPU we are processing, use it. 5264581Ssherrym */ 5277605SMark.Johnson@Sun.COM if (ucode_match_intel(cpi_sig, uinfop, ucodefp->uf_header, 5284581Ssherrym ucodefp->uf_ext_table) == EM_OK && ucodefp->uf_body != NULL) { 5294581Ssherrym return (EM_OK); 5304581Ssherrym } 5314581Ssherrym 5324581Ssherrym /* 5334581Ssherrym * Look for microcode file with the right name. 5344581Ssherrym */ 5354581Ssherrym (void) snprintf(name, MAXPATHLEN, "/%s/%s/%08X-%02X", 5364581Ssherrym UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), cpi_sig, 5374581Ssherrym uinfop->cui_platid); 5384581Ssherrym if ((fd = kobj_open(name)) == -1) { 5394581Ssherrym return (EM_OPENFILE); 5404581Ssherrym } 5414581Ssherrym 5424581Ssherrym /* 5434581Ssherrym * We found a microcode file for the CPU we are processing, 5444581Ssherrym * reset the microcode data structure and read in the new 5454581Ssherrym * file. 5464581Ssherrym */ 5477605SMark.Johnson@Sun.COM ucode->file_reset(ufp, cp->cpu_id); 5484581Ssherrym 5497605SMark.Johnson@Sun.COM ucodefp->uf_header = ucode_zalloc(cp->cpu_id, header_size); 5507605SMark.Johnson@Sun.COM if (ucodefp->uf_header == NULL) 5517605SMark.Johnson@Sun.COM return (EM_NOMEM); 5527605SMark.Johnson@Sun.COM 5537605SMark.Johnson@Sun.COM count = kobj_read(fd, (char *)ucodefp->uf_header, header_size, 0); 5544581Ssherrym 5554581Ssherrym switch (count) { 5567605SMark.Johnson@Sun.COM case UCODE_HEADER_SIZE_INTEL: { 5574581Ssherrym 5587605SMark.Johnson@Sun.COM ucode_header_intel_t *uhp = ucodefp->uf_header; 5594581Ssherrym uint32_t offset = header_size; 5604581Ssherrym int total_size, body_size, ext_size; 5614581Ssherrym uint32_t sum = 0; 5624581Ssherrym 5634581Ssherrym /* 5644581Ssherrym * Make sure that the header contains valid fields. 5654581Ssherrym */ 5667605SMark.Johnson@Sun.COM if ((rc = ucode_header_validate_intel(uhp)) == EM_OK) { 5677605SMark.Johnson@Sun.COM total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size); 5687605SMark.Johnson@Sun.COM body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size); 5697605SMark.Johnson@Sun.COM ucodefp->uf_body = ucode_zalloc(cp->cpu_id, body_size); 5707605SMark.Johnson@Sun.COM if (ucodefp->uf_body == NULL) { 5717605SMark.Johnson@Sun.COM rc = EM_NOMEM; 5727605SMark.Johnson@Sun.COM break; 5734581Ssherrym } 5744581Ssherrym 5754581Ssherrym if (kobj_read(fd, (char *)ucodefp->uf_body, 5764581Ssherrym body_size, offset) != body_size) 5774581Ssherrym rc = EM_FILESIZE; 5784581Ssherrym } 5794581Ssherrym 5804581Ssherrym if (rc) 5814581Ssherrym break; 5824581Ssherrym 5837605SMark.Johnson@Sun.COM sum = ucode_checksum_intel(0, header_size, 5847605SMark.Johnson@Sun.COM (uint8_t *)ucodefp->uf_header); 5857605SMark.Johnson@Sun.COM if (ucode_checksum_intel(sum, body_size, ucodefp->uf_body)) { 5864581Ssherrym rc = EM_CHECKSUM; 5874581Ssherrym break; 5884581Ssherrym } 5894581Ssherrym 5904581Ssherrym /* 5914581Ssherrym * Check to see if there is extended signature table. 5924581Ssherrym */ 5934581Ssherrym offset = body_size + header_size; 5944581Ssherrym ext_size = total_size - offset; 5954581Ssherrym 5964581Ssherrym if (ext_size <= 0) 5974581Ssherrym break; 5984581Ssherrym 5997605SMark.Johnson@Sun.COM ucodefp->uf_ext_table = ucode_zalloc(cp->cpu_id, ext_size); 6007605SMark.Johnson@Sun.COM if (ucodefp->uf_ext_table == NULL) { 6017605SMark.Johnson@Sun.COM rc = EM_NOMEM; 6027605SMark.Johnson@Sun.COM break; 6034581Ssherrym } 6044581Ssherrym 6054581Ssherrym if (kobj_read(fd, (char *)ucodefp->uf_ext_table, 6064581Ssherrym ext_size, offset) != ext_size) { 6074581Ssherrym rc = EM_FILESIZE; 6087605SMark.Johnson@Sun.COM } else if (ucode_checksum_intel(0, ext_size, 6094581Ssherrym (uint8_t *)(ucodefp->uf_ext_table))) { 6104581Ssherrym rc = EM_CHECKSUM; 6114581Ssherrym } else { 6124581Ssherrym int i; 6134581Ssherrym 6147605SMark.Johnson@Sun.COM ext_size -= UCODE_EXT_TABLE_SIZE_INTEL; 6154581Ssherrym for (i = 0; i < ucodefp->uf_ext_table->uet_count; 6164581Ssherrym i++) { 6177605SMark.Johnson@Sun.COM if (ucode_checksum_intel(0, 6187605SMark.Johnson@Sun.COM UCODE_EXT_SIG_SIZE_INTEL, 6194581Ssherrym (uint8_t *)(&(ucodefp->uf_ext_table-> 6204581Ssherrym uet_ext_sig[i])))) { 6214581Ssherrym rc = EM_CHECKSUM; 6224581Ssherrym break; 6234581Ssherrym } 6244581Ssherrym } 6254581Ssherrym } 6264581Ssherrym break; 6274581Ssherrym } 6284581Ssherrym 6294581Ssherrym default: 6304581Ssherrym rc = EM_FILESIZE; 6314581Ssherrym break; 6324581Ssherrym } 6334581Ssherrym 6344581Ssherrym kobj_close(fd); 6354581Ssherrym 6364581Ssherrym if (rc != EM_OK) 6374581Ssherrym return (rc); 6384581Ssherrym 6397605SMark.Johnson@Sun.COM rc = ucode_match_intel(cpi_sig, uinfop, ucodefp->uf_header, 6404581Ssherrym ucodefp->uf_ext_table); 6414581Ssherrym 6424581Ssherrym return (rc); 6434581Ssherrym } 6444581Ssherrym 645*8647SMark.Johnson@Sun.COM #ifndef __xpv 6467605SMark.Johnson@Sun.COM static ucode_errno_t 647*8647SMark.Johnson@Sun.COM ucode_match_amd(uint16_t eq_sig, cpu_ucode_info_t *uinfop, 648*8647SMark.Johnson@Sun.COM ucode_file_amd_t *ucodefp, int size) 6497605SMark.Johnson@Sun.COM { 6507605SMark.Johnson@Sun.COM ucode_header_amd_t *uh; 6517605SMark.Johnson@Sun.COM 6527605SMark.Johnson@Sun.COM if (ucodefp == NULL || size < sizeof (ucode_header_amd_t)) 6537605SMark.Johnson@Sun.COM return (EM_NOMATCH); 6547605SMark.Johnson@Sun.COM 6557605SMark.Johnson@Sun.COM /* 6567605SMark.Johnson@Sun.COM * Don't even think about loading patches that would require code 6577605SMark.Johnson@Sun.COM * execution. 6587605SMark.Johnson@Sun.COM */ 6597605SMark.Johnson@Sun.COM if (size > offsetof(ucode_file_amd_t, uf_code_present) && 6607605SMark.Johnson@Sun.COM ucodefp->uf_code_present) 6617605SMark.Johnson@Sun.COM return (EM_NOMATCH); 6627605SMark.Johnson@Sun.COM 6637605SMark.Johnson@Sun.COM uh = &ucodefp->uf_header; 6647605SMark.Johnson@Sun.COM 6657605SMark.Johnson@Sun.COM if (eq_sig != uh->uh_cpu_rev) 6667605SMark.Johnson@Sun.COM return (EM_NOMATCH); 6677605SMark.Johnson@Sun.COM 6687605SMark.Johnson@Sun.COM if (uh->uh_nb_id) { 6697605SMark.Johnson@Sun.COM cmn_err(CE_WARN, "ignoring northbridge-specific ucode: " 6707605SMark.Johnson@Sun.COM "chipset id %x, revision %x", uh->uh_nb_id, uh->uh_nb_rev); 6717605SMark.Johnson@Sun.COM return (EM_NOMATCH); 6727605SMark.Johnson@Sun.COM } 6737605SMark.Johnson@Sun.COM 6747605SMark.Johnson@Sun.COM if (uh->uh_sb_id) { 6757605SMark.Johnson@Sun.COM cmn_err(CE_WARN, "ignoring southbridge-specific ucode: " 6767605SMark.Johnson@Sun.COM "chipset id %x, revision %x", uh->uh_sb_id, uh->uh_sb_rev); 6777605SMark.Johnson@Sun.COM return (EM_NOMATCH); 6787605SMark.Johnson@Sun.COM } 6797605SMark.Johnson@Sun.COM 6807605SMark.Johnson@Sun.COM if (uh->uh_patch_id <= uinfop->cui_rev) 6817605SMark.Johnson@Sun.COM return (EM_HIGHERREV); 6827605SMark.Johnson@Sun.COM 6837605SMark.Johnson@Sun.COM return (EM_OK); 6847605SMark.Johnson@Sun.COM } 685*8647SMark.Johnson@Sun.COM #endif 6864581Ssherrym 6874581Ssherrym /* 6884581Ssherrym * Returns 1 if the microcode is for this processor; 0 otherwise. 6894581Ssherrym */ 6904581Ssherrym static ucode_errno_t 6917605SMark.Johnson@Sun.COM ucode_match_intel(int cpi_sig, cpu_ucode_info_t *uinfop, 6927605SMark.Johnson@Sun.COM ucode_header_intel_t *uhp, ucode_ext_table_intel_t *uetp) 6934581Ssherrym { 6947605SMark.Johnson@Sun.COM if (uhp == NULL) 6957605SMark.Johnson@Sun.COM return (EM_NOMATCH); 6964581Ssherrym 6977605SMark.Johnson@Sun.COM if (UCODE_MATCH_INTEL(cpi_sig, uhp->uh_signature, 6984581Ssherrym uinfop->cui_platid, uhp->uh_proc_flags)) { 6994581Ssherrym 7004581Ssherrym if (uinfop->cui_rev >= uhp->uh_rev && !ucode_force_update) 7014581Ssherrym return (EM_HIGHERREV); 7024581Ssherrym 7034581Ssherrym return (EM_OK); 7044581Ssherrym } 7054581Ssherrym 7064581Ssherrym if (uetp != NULL) { 7074581Ssherrym int i; 7084581Ssherrym 7094581Ssherrym for (i = 0; i < uetp->uet_count; i++) { 7107605SMark.Johnson@Sun.COM ucode_ext_sig_intel_t *uesp; 7114581Ssherrym 7124581Ssherrym uesp = &uetp->uet_ext_sig[i]; 7134581Ssherrym 7147605SMark.Johnson@Sun.COM if (UCODE_MATCH_INTEL(cpi_sig, uesp->ues_signature, 7154581Ssherrym uinfop->cui_platid, uesp->ues_proc_flags)) { 7164581Ssherrym 7174581Ssherrym if (uinfop->cui_rev >= uhp->uh_rev && 7184581Ssherrym !ucode_force_update) 7194581Ssherrym return (EM_HIGHERREV); 7204581Ssherrym 7214581Ssherrym return (EM_OK); 7224581Ssherrym } 7234581Ssherrym } 7244581Ssherrym } 7254581Ssherrym 7264581Ssherrym return (EM_NOMATCH); 7274581Ssherrym } 7284581Ssherrym 7294581Ssherrym /*ARGSUSED*/ 7304581Ssherrym static int 7314581Ssherrym ucode_write(xc_arg_t arg1, xc_arg_t unused2, xc_arg_t unused3) 7324581Ssherrym { 7337605SMark.Johnson@Sun.COM ucode_update_t *uusp = (ucode_update_t *)arg1; 7347605SMark.Johnson@Sun.COM cpu_ucode_info_t *uinfop = CPU->cpu_m.mcpu_ucode_info; 7354581Ssherrym 7367605SMark.Johnson@Sun.COM ASSERT(ucode); 7374581Ssherrym ASSERT(uusp->ucodep); 7384581Ssherrym 7397347SMark.Johnson@Sun.COM #ifndef __xpv 7404581Ssherrym /* 7414581Ssherrym * Check one more time to see if it is really necessary to update 7424581Ssherrym * microcode just in case this is a hyperthreaded processor where 7434581Ssherrym * the threads share the same microcode. 7444581Ssherrym */ 7454581Ssherrym if (!ucode_force_update) { 7467605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 7474581Ssherrym uusp->new_rev = uinfop->cui_rev; 7484581Ssherrym if (uinfop->cui_rev >= uusp->expected_rev) 7494581Ssherrym return (0); 7504581Ssherrym } 7514581Ssherrym 7527605SMark.Johnson@Sun.COM wrmsr(ucode->write_msr, (uintptr_t)uusp->ucodep); 7537347SMark.Johnson@Sun.COM #endif 7547605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 7554581Ssherrym uusp->new_rev = uinfop->cui_rev; 7564581Ssherrym 7574581Ssherrym return (0); 7584581Ssherrym } 7594581Ssherrym 7607605SMark.Johnson@Sun.COM /*ARGSUSED*/ 7617605SMark.Johnson@Sun.COM static uint32_t 7627605SMark.Johnson@Sun.COM ucode_load_amd(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp) 7637605SMark.Johnson@Sun.COM { 7647605SMark.Johnson@Sun.COM ucode_file_amd_t *ucodefp = ufp->amd; 7657605SMark.Johnson@Sun.COM #ifdef __xpv 7667605SMark.Johnson@Sun.COM ucode_update_t uus; 7677605SMark.Johnson@Sun.COM #endif 7684581Ssherrym 7697605SMark.Johnson@Sun.COM ASSERT(ucode); 7707605SMark.Johnson@Sun.COM ASSERT(ucodefp); 7717605SMark.Johnson@Sun.COM 7727605SMark.Johnson@Sun.COM #ifndef __xpv 7737605SMark.Johnson@Sun.COM kpreempt_disable(); 7747605SMark.Johnson@Sun.COM wrmsr(ucode->write_msr, (uintptr_t)ucodefp); 7757605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 7767605SMark.Johnson@Sun.COM kpreempt_enable(); 777*8647SMark.Johnson@Sun.COM 778*8647SMark.Johnson@Sun.COM return (ucodefp->uf_header.uh_patch_id); 7797605SMark.Johnson@Sun.COM #else 780*8647SMark.Johnson@Sun.COM uus.ucodep = ucodefp->ucodep; 781*8647SMark.Johnson@Sun.COM uus.usize = ucodefp->usize; 7827605SMark.Johnson@Sun.COM ucode_load_xpv(&uus); 7837605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 7847605SMark.Johnson@Sun.COM uus.new_rev = uinfop->cui_rev; 785*8647SMark.Johnson@Sun.COM 786*8647SMark.Johnson@Sun.COM return (uus.new_rev); 7877605SMark.Johnson@Sun.COM #endif 7887605SMark.Johnson@Sun.COM } 7897605SMark.Johnson@Sun.COM 7907605SMark.Johnson@Sun.COM /*ARGSUSED2*/ 7917605SMark.Johnson@Sun.COM static uint32_t 7927605SMark.Johnson@Sun.COM ucode_load_intel(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp) 7934581Ssherrym { 7947605SMark.Johnson@Sun.COM ucode_file_intel_t *ucodefp = &ufp->intel; 7957605SMark.Johnson@Sun.COM #ifdef __xpv 7967605SMark.Johnson@Sun.COM uint32_t ext_offset; 7977605SMark.Johnson@Sun.COM uint32_t body_size; 7987605SMark.Johnson@Sun.COM uint32_t ext_size; 7997605SMark.Johnson@Sun.COM uint8_t *ustart; 8007605SMark.Johnson@Sun.COM uint32_t usize; 8017605SMark.Johnson@Sun.COM ucode_update_t uus; 8027605SMark.Johnson@Sun.COM #endif 8037605SMark.Johnson@Sun.COM 8047605SMark.Johnson@Sun.COM ASSERT(ucode); 8057605SMark.Johnson@Sun.COM 8067605SMark.Johnson@Sun.COM #ifdef __xpv 8077605SMark.Johnson@Sun.COM /* 8087605SMark.Johnson@Sun.COM * the hypervisor wants the header, data, and extended 8097605SMark.Johnson@Sun.COM * signature tables. We can only get here from the boot 8107605SMark.Johnson@Sun.COM * CPU (cpu #0), we don't need to free as ucode_zalloc() will 8117605SMark.Johnson@Sun.COM * use BOP_ALLOC(). 8127605SMark.Johnson@Sun.COM */ 8137605SMark.Johnson@Sun.COM usize = UCODE_TOTAL_SIZE_INTEL(ucodefp->uf_header->uh_total_size); 8147605SMark.Johnson@Sun.COM ustart = ucode_zalloc(cp->cpu_id, usize); 8157605SMark.Johnson@Sun.COM ASSERT(ustart); 8167605SMark.Johnson@Sun.COM 8177605SMark.Johnson@Sun.COM body_size = UCODE_BODY_SIZE_INTEL(ucodefp->uf_header->uh_body_size); 8187605SMark.Johnson@Sun.COM ext_offset = body_size + UCODE_HEADER_SIZE_INTEL; 8197605SMark.Johnson@Sun.COM ext_size = usize - ext_offset; 8207605SMark.Johnson@Sun.COM ASSERT(ext_size >= 0); 8217605SMark.Johnson@Sun.COM 8227605SMark.Johnson@Sun.COM (void) memcpy(ustart, ucodefp->uf_header, UCODE_HEADER_SIZE_INTEL); 8237605SMark.Johnson@Sun.COM (void) memcpy(&ustart[UCODE_HEADER_SIZE_INTEL], ucodefp->uf_body, 8247605SMark.Johnson@Sun.COM body_size); 8257605SMark.Johnson@Sun.COM if (ext_size > 0) { 8267605SMark.Johnson@Sun.COM (void) memcpy(&ustart[ext_offset], 8277605SMark.Johnson@Sun.COM ucodefp->uf_ext_table, ext_size); 8287605SMark.Johnson@Sun.COM } 8297605SMark.Johnson@Sun.COM uus.ucodep = ustart; 8307605SMark.Johnson@Sun.COM uus.usize = usize; 8317605SMark.Johnson@Sun.COM ucode_load_xpv(&uus); 8327605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 8337605SMark.Johnson@Sun.COM uus.new_rev = uinfop->cui_rev; 8347605SMark.Johnson@Sun.COM #else 8354581Ssherrym kpreempt_disable(); 8367605SMark.Johnson@Sun.COM wrmsr(ucode->write_msr, (uintptr_t)ucodefp->uf_body); 8377605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 8384581Ssherrym kpreempt_enable(); 8397605SMark.Johnson@Sun.COM #endif 8407605SMark.Johnson@Sun.COM 8417605SMark.Johnson@Sun.COM return (ucodefp->uf_header->uh_rev); 8424581Ssherrym } 8434581Ssherrym 8447347SMark.Johnson@Sun.COM 8457347SMark.Johnson@Sun.COM #ifdef __xpv 8467347SMark.Johnson@Sun.COM static void 8477605SMark.Johnson@Sun.COM ucode_load_xpv(ucode_update_t *uusp) 8487347SMark.Johnson@Sun.COM { 8497347SMark.Johnson@Sun.COM xen_platform_op_t op; 8507347SMark.Johnson@Sun.COM int e; 8517347SMark.Johnson@Sun.COM 8527347SMark.Johnson@Sun.COM ASSERT(DOMAIN_IS_INITDOMAIN(xen_info)); 8537347SMark.Johnson@Sun.COM 8547347SMark.Johnson@Sun.COM kpreempt_disable(); 8557347SMark.Johnson@Sun.COM op.cmd = XENPF_microcode_update; 8567347SMark.Johnson@Sun.COM op.interface_version = XENPF_INTERFACE_VERSION; 8577347SMark.Johnson@Sun.COM /*LINTED: constant in conditional context*/ 8587605SMark.Johnson@Sun.COM set_xen_guest_handle(op.u.microcode.data, uusp->ucodep); 8597605SMark.Johnson@Sun.COM op.u.microcode.length = uusp->usize; 8607347SMark.Johnson@Sun.COM e = HYPERVISOR_platform_op(&op); 8617347SMark.Johnson@Sun.COM if (e != 0) { 8627347SMark.Johnson@Sun.COM cmn_err(CE_WARN, "hypervisor failed to accept uCode update"); 8637347SMark.Johnson@Sun.COM } 8647347SMark.Johnson@Sun.COM kpreempt_enable(); 8657347SMark.Johnson@Sun.COM } 8667347SMark.Johnson@Sun.COM #endif /* __xpv */ 8677347SMark.Johnson@Sun.COM 8687605SMark.Johnson@Sun.COM static void 8697605SMark.Johnson@Sun.COM ucode_read_rev_amd(cpu_ucode_info_t *uinfop) 8707605SMark.Johnson@Sun.COM { 8717605SMark.Johnson@Sun.COM uinfop->cui_rev = rdmsr(MSR_AMD_PATCHLEVEL); 8727605SMark.Johnson@Sun.COM } 8737347SMark.Johnson@Sun.COM 8744581Ssherrym static void 8757605SMark.Johnson@Sun.COM ucode_read_rev_intel(cpu_ucode_info_t *uinfop) 8764581Ssherrym { 8774581Ssherrym struct cpuid_regs crs; 8784581Ssherrym 8794581Ssherrym /* 8804581Ssherrym * The Intel 64 and IA-32 Architecture Software Developer's Manual 8814581Ssherrym * recommends that MSR_INTC_UCODE_REV be loaded with 0 first, then 8824581Ssherrym * execute cpuid to guarantee the correct reading of this register. 8834581Ssherrym */ 8844581Ssherrym wrmsr(MSR_INTC_UCODE_REV, 0); 8854581Ssherrym (void) __cpuid_insn(&crs); 8864581Ssherrym uinfop->cui_rev = (rdmsr(MSR_INTC_UCODE_REV) >> INTC_UCODE_REV_SHIFT); 8874581Ssherrym } 8884581Ssherrym 8897605SMark.Johnson@Sun.COM static ucode_errno_t 8907605SMark.Johnson@Sun.COM ucode_extract_amd(ucode_update_t *uusp, uint8_t *ucodep, int size) 8917605SMark.Johnson@Sun.COM { 892*8647SMark.Johnson@Sun.COM #ifndef __xpv 8937605SMark.Johnson@Sun.COM uint32_t *ptr = (uint32_t *)ucodep; 8947605SMark.Johnson@Sun.COM ucode_eqtbl_amd_t *eqtbl; 8957605SMark.Johnson@Sun.COM ucode_file_amd_t *ufp; 896*8647SMark.Johnson@Sun.COM int count; 897*8647SMark.Johnson@Sun.COM int higher = 0; 898*8647SMark.Johnson@Sun.COM ucode_errno_t rc = EM_NOMATCH; 899*8647SMark.Johnson@Sun.COM uint16_t eq_sig; 9007605SMark.Johnson@Sun.COM 9017605SMark.Johnson@Sun.COM /* skip over magic number & equivalence table header */ 9027605SMark.Johnson@Sun.COM ptr += 2; size -= 8; 9037605SMark.Johnson@Sun.COM 9047605SMark.Johnson@Sun.COM count = *ptr++; size -= 4; 9057605SMark.Johnson@Sun.COM for (eqtbl = (ucode_eqtbl_amd_t *)ptr; 9067605SMark.Johnson@Sun.COM eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != uusp->sig; 9077605SMark.Johnson@Sun.COM eqtbl++) 9087605SMark.Johnson@Sun.COM ; 9097605SMark.Johnson@Sun.COM 9107605SMark.Johnson@Sun.COM eq_sig = eqtbl->ue_equiv_cpu; 9117605SMark.Johnson@Sun.COM 9127605SMark.Johnson@Sun.COM /* No equivalent CPU id found, assume outdated microcode file. */ 9137605SMark.Johnson@Sun.COM if (eq_sig == 0) 9147605SMark.Johnson@Sun.COM return (EM_HIGHERREV); 9157605SMark.Johnson@Sun.COM 9167605SMark.Johnson@Sun.COM /* Use the first microcode patch that matches. */ 9177605SMark.Johnson@Sun.COM do { 9187605SMark.Johnson@Sun.COM ptr += count >> 2; size -= count; 9197605SMark.Johnson@Sun.COM 9207605SMark.Johnson@Sun.COM if (!size) 921*8647SMark.Johnson@Sun.COM return (higher ? EM_HIGHERREV : EM_NOMATCH); 9227605SMark.Johnson@Sun.COM 9237605SMark.Johnson@Sun.COM ptr++; size -= 4; 9247605SMark.Johnson@Sun.COM count = *ptr++; size -= 4; 9257605SMark.Johnson@Sun.COM ufp = (ucode_file_amd_t *)ptr; 926*8647SMark.Johnson@Sun.COM 927*8647SMark.Johnson@Sun.COM rc = ucode_match_amd(eq_sig, &uusp->info, ufp, count); 928*8647SMark.Johnson@Sun.COM if (rc == EM_HIGHERREV) 929*8647SMark.Johnson@Sun.COM higher = 1; 930*8647SMark.Johnson@Sun.COM } while (rc != EM_OK); 9317605SMark.Johnson@Sun.COM 9327605SMark.Johnson@Sun.COM uusp->ucodep = (uint8_t *)ufp; 9337605SMark.Johnson@Sun.COM uusp->usize = count; 9347605SMark.Johnson@Sun.COM uusp->expected_rev = ufp->uf_header.uh_patch_id; 935*8647SMark.Johnson@Sun.COM #else 936*8647SMark.Johnson@Sun.COM /* 937*8647SMark.Johnson@Sun.COM * The hypervisor will choose the patch to load, so there is no way to 938*8647SMark.Johnson@Sun.COM * know the "expected revision" in advance. This is especially true on 939*8647SMark.Johnson@Sun.COM * mixed-revision systems where more than one patch will be loaded. 940*8647SMark.Johnson@Sun.COM */ 941*8647SMark.Johnson@Sun.COM uusp->expected_rev = 0; 942*8647SMark.Johnson@Sun.COM uusp->ucodep = ucodep; 943*8647SMark.Johnson@Sun.COM uusp->usize = size; 944*8647SMark.Johnson@Sun.COM 945*8647SMark.Johnson@Sun.COM ucode_chipset_amd(ucodep, size); 946*8647SMark.Johnson@Sun.COM #endif 9477605SMark.Johnson@Sun.COM 9487605SMark.Johnson@Sun.COM return (EM_OK); 9497605SMark.Johnson@Sun.COM } 9507605SMark.Johnson@Sun.COM 9517605SMark.Johnson@Sun.COM static ucode_errno_t 9527605SMark.Johnson@Sun.COM ucode_extract_intel(ucode_update_t *uusp, uint8_t *ucodep, int size) 9537605SMark.Johnson@Sun.COM { 9547605SMark.Johnson@Sun.COM uint32_t header_size = UCODE_HEADER_SIZE_INTEL; 9557605SMark.Johnson@Sun.COM int remaining; 9567605SMark.Johnson@Sun.COM int found = 0; 9577605SMark.Johnson@Sun.COM ucode_errno_t search_rc = EM_NOMATCH; /* search result */ 9587605SMark.Johnson@Sun.COM 9597605SMark.Johnson@Sun.COM /* 9607605SMark.Johnson@Sun.COM * Go through the whole buffer in case there are 9617605SMark.Johnson@Sun.COM * multiple versions of matching microcode for this 9627605SMark.Johnson@Sun.COM * processor. 9637605SMark.Johnson@Sun.COM */ 9647605SMark.Johnson@Sun.COM for (remaining = size; remaining > 0; ) { 9657605SMark.Johnson@Sun.COM int total_size, body_size, ext_size; 9667605SMark.Johnson@Sun.COM uint8_t *curbuf = &ucodep[size - remaining]; 9677605SMark.Johnson@Sun.COM ucode_header_intel_t *uhp = (ucode_header_intel_t *)curbuf; 9687605SMark.Johnson@Sun.COM ucode_ext_table_intel_t *uetp = NULL; 9697605SMark.Johnson@Sun.COM ucode_errno_t tmprc; 9707605SMark.Johnson@Sun.COM 9717605SMark.Johnson@Sun.COM total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size); 9727605SMark.Johnson@Sun.COM body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size); 9737605SMark.Johnson@Sun.COM ext_size = total_size - (header_size + body_size); 9747605SMark.Johnson@Sun.COM 9757605SMark.Johnson@Sun.COM if (ext_size > 0) 9767605SMark.Johnson@Sun.COM uetp = (ucode_ext_table_intel_t *) 9777605SMark.Johnson@Sun.COM &curbuf[header_size + body_size]; 9787605SMark.Johnson@Sun.COM 9797605SMark.Johnson@Sun.COM tmprc = ucode_match_intel(uusp->sig, &uusp->info, uhp, uetp); 9807605SMark.Johnson@Sun.COM 9817605SMark.Johnson@Sun.COM /* 9827605SMark.Johnson@Sun.COM * Since we are searching through a big file 9837605SMark.Johnson@Sun.COM * containing microcode for pretty much all the 9847605SMark.Johnson@Sun.COM * processors, we are bound to get EM_NOMATCH 9857605SMark.Johnson@Sun.COM * at one point. However, if we return 9867605SMark.Johnson@Sun.COM * EM_NOMATCH to users, it will really confuse 9877605SMark.Johnson@Sun.COM * them. Therefore, if we ever find a match of 9887605SMark.Johnson@Sun.COM * a lower rev, we will set return code to 9897605SMark.Johnson@Sun.COM * EM_HIGHERREV. 9907605SMark.Johnson@Sun.COM */ 9917605SMark.Johnson@Sun.COM if (tmprc == EM_HIGHERREV) 9927605SMark.Johnson@Sun.COM search_rc = EM_HIGHERREV; 9937605SMark.Johnson@Sun.COM 9947605SMark.Johnson@Sun.COM if (tmprc == EM_OK && 9957605SMark.Johnson@Sun.COM uusp->expected_rev < uhp->uh_rev) { 9967605SMark.Johnson@Sun.COM #ifndef __xpv 9977605SMark.Johnson@Sun.COM uusp->ucodep = (uint8_t *)&curbuf[header_size]; 9987605SMark.Johnson@Sun.COM #else 9997605SMark.Johnson@Sun.COM uusp->ucodep = (uint8_t *)curbuf; 10007605SMark.Johnson@Sun.COM #endif 10017605SMark.Johnson@Sun.COM uusp->usize = 10027605SMark.Johnson@Sun.COM UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size); 10037605SMark.Johnson@Sun.COM uusp->expected_rev = uhp->uh_rev; 10047605SMark.Johnson@Sun.COM found = 1; 10057605SMark.Johnson@Sun.COM } 10067605SMark.Johnson@Sun.COM 10077605SMark.Johnson@Sun.COM remaining -= total_size; 10087605SMark.Johnson@Sun.COM } 10097605SMark.Johnson@Sun.COM 10107605SMark.Johnson@Sun.COM if (!found) 10117605SMark.Johnson@Sun.COM return (search_rc); 10127605SMark.Johnson@Sun.COM 10137605SMark.Johnson@Sun.COM return (EM_OK); 10147605SMark.Johnson@Sun.COM } 10154581Ssherrym /* 10164581Ssherrym * Entry point to microcode update from the ucode_drv driver. 10174581Ssherrym * 10184581Ssherrym * Returns EM_OK on success, corresponding error code on failure. 10194581Ssherrym */ 10204581Ssherrym ucode_errno_t 10214581Ssherrym ucode_update(uint8_t *ucodep, int size) 10224581Ssherrym { 10234581Ssherrym int found = 0; 10244581Ssherrym processorid_t id; 10257605SMark.Johnson@Sun.COM ucode_update_t cached = { 0 }; 10267605SMark.Johnson@Sun.COM ucode_update_t *cachedp = NULL; 10274581Ssherrym ucode_errno_t rc = EM_OK; 10284581Ssherrym ucode_errno_t search_rc = EM_NOMATCH; /* search result */ 10294581Ssherrym cpuset_t cpuset; 10304581Ssherrym 10317605SMark.Johnson@Sun.COM ASSERT(ucode); 10324581Ssherrym ASSERT(ucodep); 10334581Ssherrym CPUSET_ZERO(cpuset); 10344581Ssherrym 10357605SMark.Johnson@Sun.COM if (!ucode->capable(CPU)) 10364581Ssherrym return (EM_NOTSUP); 10374581Ssherrym 10384581Ssherrym mutex_enter(&cpu_lock); 10394581Ssherrym 10404581Ssherrym for (id = 0; id < max_ncpus; id++) { 10414581Ssherrym cpu_t *cpu; 10427605SMark.Johnson@Sun.COM ucode_update_t uus = { 0 }; 10437605SMark.Johnson@Sun.COM ucode_update_t *uusp = &uus; 10444581Ssherrym 10454581Ssherrym /* 10464581Ssherrym * If there is no such CPU or it is not xcall ready, skip it. 10474581Ssherrym */ 10484581Ssherrym if ((cpu = cpu_get(id)) == NULL || 10494581Ssherrym !(cpu->cpu_flags & CPU_READY)) 10504581Ssherrym continue; 10514581Ssherrym 10524581Ssherrym uusp->sig = cpuid_getsig(cpu); 10534581Ssherrym bcopy(cpu->cpu_m.mcpu_ucode_info, &uusp->info, 10544581Ssherrym sizeof (uusp->info)); 10554581Ssherrym 10564581Ssherrym /* 10574581Ssherrym * If the current CPU has the same signature and platform 10584581Ssherrym * id as the previous one we processed, reuse the information. 10594581Ssherrym */ 10604581Ssherrym if (cachedp && cachedp->sig == cpuid_getsig(cpu) && 10614581Ssherrym cachedp->info.cui_platid == uusp->info.cui_platid) { 10624581Ssherrym uusp->ucodep = cachedp->ucodep; 10634581Ssherrym uusp->expected_rev = cachedp->expected_rev; 10644581Ssherrym /* 10654581Ssherrym * Intuitively we should check here to see whether the 10664581Ssherrym * running microcode rev is >= the expected rev, and 10674581Ssherrym * quit if it is. But we choose to proceed with the 10684581Ssherrym * xcall regardless of the running version so that 10694581Ssherrym * the other threads in an HT processor can update 10704581Ssherrym * the cpu_ucode_info structure in machcpu. 10714581Ssherrym */ 10727605SMark.Johnson@Sun.COM } else if ((search_rc = ucode->extract(uusp, ucodep, size)) 10737605SMark.Johnson@Sun.COM == EM_OK) { 10747605SMark.Johnson@Sun.COM bcopy(uusp, &cached, sizeof (cached)); 10757605SMark.Johnson@Sun.COM cachedp = &cached; 10767605SMark.Johnson@Sun.COM found = 1; 10774581Ssherrym } 10784581Ssherrym 10794581Ssherrym /* Nothing to do */ 10804581Ssherrym if (uusp->ucodep == NULL) 10814581Ssherrym continue; 10824581Ssherrym 10837347SMark.Johnson@Sun.COM #ifdef __xpv 10847347SMark.Johnson@Sun.COM /* 10857347SMark.Johnson@Sun.COM * for i86xpv, the hypervisor will update all the CPUs. 10867347SMark.Johnson@Sun.COM * the hypervisor wants the header, data, and extended 10877347SMark.Johnson@Sun.COM * signature tables. ucode_write will just read in the 10887347SMark.Johnson@Sun.COM * updated version on all the CPUs after the update has 10897347SMark.Johnson@Sun.COM * completed. 10907347SMark.Johnson@Sun.COM */ 10917402SMark.Johnson@Sun.COM if (id == 0) { 10927605SMark.Johnson@Sun.COM ucode_load_xpv(uusp); 10937402SMark.Johnson@Sun.COM } 10947347SMark.Johnson@Sun.COM #endif 10957347SMark.Johnson@Sun.COM 10964581Ssherrym CPUSET_ADD(cpuset, id); 10974581Ssherrym kpreempt_disable(); 10984581Ssherrym xc_sync((xc_arg_t)uusp, 0, 0, X_CALL_HIPRI, cpuset, 10994581Ssherrym ucode_write); 11004581Ssherrym kpreempt_enable(); 11014581Ssherrym CPUSET_DEL(cpuset, id); 11024581Ssherrym 1103*8647SMark.Johnson@Sun.COM if (uusp->new_rev != 0 && uusp->info.cui_rev == uusp->new_rev) { 1104*8647SMark.Johnson@Sun.COM rc = EM_HIGHERREV; 1105*8647SMark.Johnson@Sun.COM } else if ((uusp->new_rev == 0) || (uusp->expected_rev != 0 && 1106*8647SMark.Johnson@Sun.COM uusp->expected_rev != uusp->new_rev)) { 11074581Ssherrym cmn_err(CE_WARN, ucode_failure_fmt, 11084581Ssherrym id, uusp->info.cui_rev, uusp->expected_rev); 11094581Ssherrym rc = EM_UPDATE; 1110*8647SMark.Johnson@Sun.COM } else { 1111*8647SMark.Johnson@Sun.COM cmn_err(CE_CONT, ucode_success_fmt, 1112*8647SMark.Johnson@Sun.COM id, uusp->info.cui_rev, uusp->new_rev); 11134581Ssherrym } 11144581Ssherrym } 11154581Ssherrym 11164581Ssherrym mutex_exit(&cpu_lock); 11174581Ssherrym 11184581Ssherrym if (!found) 11194581Ssherrym rc = search_rc; 11204581Ssherrym 11214581Ssherrym return (rc); 11224581Ssherrym } 11234581Ssherrym 11244581Ssherrym /* 11254581Ssherrym * Initialize mcpu_ucode_info, and perform microcode update if necessary. 11264581Ssherrym * This is the entry point from boot path where pointer to CPU structure 11274581Ssherrym * is available. 11284581Ssherrym * 11294581Ssherrym * cpuid_info must be initialized before ucode_check can be called. 11304581Ssherrym */ 11314581Ssherrym void 11324581Ssherrym ucode_check(cpu_t *cp) 11334581Ssherrym { 11347605SMark.Johnson@Sun.COM cpu_ucode_info_t *uinfop; 11354581Ssherrym ucode_errno_t rc = EM_OK; 11367605SMark.Johnson@Sun.COM uint32_t new_rev = 0; 11374581Ssherrym 11384581Ssherrym ASSERT(cp); 11394581Ssherrym if (cp->cpu_id == 0) 11404581Ssherrym cp->cpu_m.mcpu_ucode_info = &cpu_ucode_info0; 11414581Ssherrym 11424581Ssherrym uinfop = cp->cpu_m.mcpu_ucode_info; 11434581Ssherrym ASSERT(uinfop); 11444581Ssherrym 11457605SMark.Johnson@Sun.COM /* set up function pointers if not already done */ 11467605SMark.Johnson@Sun.COM if (!ucode) 11477605SMark.Johnson@Sun.COM switch (cpuid_getvendor(cp)) { 11487605SMark.Johnson@Sun.COM case X86_VENDOR_AMD: 11497605SMark.Johnson@Sun.COM ucode = &ucode_amd; 11507605SMark.Johnson@Sun.COM break; 11517605SMark.Johnson@Sun.COM case X86_VENDOR_Intel: 11527605SMark.Johnson@Sun.COM ucode = &ucode_intel; 11537605SMark.Johnson@Sun.COM break; 11547605SMark.Johnson@Sun.COM default: 11557605SMark.Johnson@Sun.COM return; 11567605SMark.Johnson@Sun.COM } 11577605SMark.Johnson@Sun.COM 11587605SMark.Johnson@Sun.COM if (!ucode->capable(cp)) 11594581Ssherrym return; 11604581Ssherrym 11614581Ssherrym /* 11624581Ssherrym * The MSR_INTC_PLATFORM_ID is supported in Celeron and Xeon 11634581Ssherrym * (Family 6, model 5 and above) and all processors after. 11644581Ssherrym */ 11657605SMark.Johnson@Sun.COM if ((cpuid_getvendor(cp) == X86_VENDOR_Intel) && 11667605SMark.Johnson@Sun.COM ((cpuid_getmodel(cp) >= 5) || (cpuid_getfamily(cp) > 6))) { 11674581Ssherrym uinfop->cui_platid = 1 << ((rdmsr(MSR_INTC_PLATFORM_ID) >> 11684581Ssherrym INTC_PLATFORM_ID_SHIFT) & INTC_PLATFORM_ID_MASK); 11694581Ssherrym } 11704581Ssherrym 11717605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 11724581Ssherrym 11737402SMark.Johnson@Sun.COM #ifdef __xpv 11747402SMark.Johnson@Sun.COM /* 11757402SMark.Johnson@Sun.COM * for i86xpv, the hypervisor will update all the CPUs. We only need 11767402SMark.Johnson@Sun.COM * do do this on one of the CPUs (and there always is a CPU 0). 11777402SMark.Johnson@Sun.COM */ 11787402SMark.Johnson@Sun.COM if (cp->cpu_id != 0) { 11797402SMark.Johnson@Sun.COM return; 11807402SMark.Johnson@Sun.COM } 11817402SMark.Johnson@Sun.COM #endif 11827402SMark.Johnson@Sun.COM 11834581Ssherrym /* 11844581Ssherrym * Check to see if we need ucode update 11854581Ssherrym */ 11867605SMark.Johnson@Sun.COM if ((rc = ucode->locate(cp, uinfop, &ucodefile)) == EM_OK) { 11877605SMark.Johnson@Sun.COM new_rev = ucode->load(&ucodefile, uinfop, cp); 11887347SMark.Johnson@Sun.COM 11897605SMark.Johnson@Sun.COM if (uinfop->cui_rev != new_rev) 11904581Ssherrym cmn_err(CE_WARN, ucode_failure_fmt, cp->cpu_id, 11917605SMark.Johnson@Sun.COM uinfop->cui_rev, new_rev); 11924581Ssherrym } 11934581Ssherrym 11944581Ssherrym /* 11954581Ssherrym * If we fail to find a match for any reason, free the file structure 11964581Ssherrym * just in case we have read in a partial file. 11974581Ssherrym * 11984581Ssherrym * Since the scratch memory for holding the microcode for the boot CPU 11994581Ssherrym * came from BOP_ALLOC, we will reset the data structure as if we 12004581Ssherrym * never did the allocation so we don't have to keep track of this 12014581Ssherrym * special chunk of memory. We free the memory used for the rest 12024581Ssherrym * of the CPUs in start_other_cpus(). 12034581Ssherrym */ 12044581Ssherrym if (rc != EM_OK || cp->cpu_id == 0) 12057605SMark.Johnson@Sun.COM ucode->file_reset(&ucodefile, cp->cpu_id); 12064581Ssherrym } 12074581Ssherrym 12084581Ssherrym /* 12094581Ssherrym * Returns microcode revision from the machcpu structure. 12104581Ssherrym */ 12114581Ssherrym ucode_errno_t 12124581Ssherrym ucode_get_rev(uint32_t *revp) 12134581Ssherrym { 12144581Ssherrym int i; 12154581Ssherrym 12167605SMark.Johnson@Sun.COM ASSERT(ucode); 12174581Ssherrym ASSERT(revp); 12184581Ssherrym 12197605SMark.Johnson@Sun.COM if (!ucode->capable(CPU)) 12204581Ssherrym return (EM_NOTSUP); 12214581Ssherrym 12224581Ssherrym mutex_enter(&cpu_lock); 12234581Ssherrym for (i = 0; i < max_ncpus; i++) { 12244581Ssherrym cpu_t *cpu; 12254581Ssherrym 12264581Ssherrym if ((cpu = cpu_get(i)) == NULL) 12274581Ssherrym continue; 12284581Ssherrym 12294581Ssherrym revp[i] = cpu->cpu_m.mcpu_ucode_info->cui_rev; 12304581Ssherrym } 12314581Ssherrym mutex_exit(&cpu_lock); 12324581Ssherrym 12334581Ssherrym return (EM_OK); 12344581Ssherrym } 1235