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 /* 238647SMark.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 *); 808647SMark.Johnson@Sun.COM static void ucode_chipset_amd(uint8_t *, int); 817605SMark.Johnson@Sun.COM #endif 827605SMark.Johnson@Sun.COM 838647SMark.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 908647SMark.Johnson@Sun.COM #ifndef __xpv 918647SMark.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); 938647SMark.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 { 206*9000SStuart.Maybee@Sun.COM int hwenv = get_hwenv(); 207*9000SStuart.Maybee@Sun.COM 208*9000SStuart.Maybee@Sun.COM if (hwenv == HW_XEN_HVM || (hwenv == HW_XEN_PV && !is_controldom())) { 2097605SMark.Johnson@Sun.COM return (0); 2107605SMark.Johnson@Sun.COM } 2118647SMark.Johnson@Sun.COM return (cpuid_getfamily(cp) >= 0x10); 2128647SMark.Johnson@Sun.COM } 2137605SMark.Johnson@Sun.COM 2147605SMark.Johnson@Sun.COM static int 2157605SMark.Johnson@Sun.COM ucode_capable_intel(cpu_t *cp) 2167605SMark.Johnson@Sun.COM { 217*9000SStuart.Maybee@Sun.COM int hwenv = get_hwenv(); 218*9000SStuart.Maybee@Sun.COM 219*9000SStuart.Maybee@Sun.COM if (hwenv == HW_XEN_HVM || (hwenv == HW_XEN_PV && !is_controldom())) { 2207347SMark.Johnson@Sun.COM return (0); 2217347SMark.Johnson@Sun.COM } 2227605SMark.Johnson@Sun.COM return (cpuid_getfamily(cp) >= 6); 2234581Ssherrym } 2244581Ssherrym 2254581Ssherrym /* 2264581Ssherrym * Called when it is no longer necessary to keep the microcode around, 2274581Ssherrym * or when the cached microcode doesn't match the CPU being processed. 2284581Ssherrym */ 2294581Ssherrym static void 2307605SMark.Johnson@Sun.COM ucode_file_reset_amd(ucode_file_t *ufp, processorid_t id) 2314581Ssherrym { 2327605SMark.Johnson@Sun.COM ucode_file_amd_t *ucodefp = ufp->amd; 2334581Ssherrym 2344581Ssherrym if (ucodefp == NULL) 2354581Ssherrym return; 2364581Ssherrym 2377605SMark.Johnson@Sun.COM ucode_free(id, ucodefp, sizeof (ucode_file_amd_t)); 2387605SMark.Johnson@Sun.COM ufp->amd = NULL; 2397605SMark.Johnson@Sun.COM } 2407605SMark.Johnson@Sun.COM 2417605SMark.Johnson@Sun.COM static void 2427605SMark.Johnson@Sun.COM ucode_file_reset_intel(ucode_file_t *ufp, processorid_t id) 2437605SMark.Johnson@Sun.COM { 2447605SMark.Johnson@Sun.COM ucode_file_intel_t *ucodefp = &ufp->intel; 2457605SMark.Johnson@Sun.COM int total_size, body_size; 2467605SMark.Johnson@Sun.COM 2477605SMark.Johnson@Sun.COM if (ucodefp == NULL || ucodefp->uf_header == NULL) 2487605SMark.Johnson@Sun.COM return; 2497605SMark.Johnson@Sun.COM 2507605SMark.Johnson@Sun.COM total_size = UCODE_TOTAL_SIZE_INTEL(ucodefp->uf_header->uh_total_size); 2517605SMark.Johnson@Sun.COM body_size = UCODE_BODY_SIZE_INTEL(ucodefp->uf_header->uh_body_size); 2524581Ssherrym if (ucodefp->uf_body) { 2537605SMark.Johnson@Sun.COM ucode_free(id, ucodefp->uf_body, body_size); 2544581Ssherrym ucodefp->uf_body = NULL; 2554581Ssherrym } 2564581Ssherrym 2574581Ssherrym if (ucodefp->uf_ext_table) { 2587605SMark.Johnson@Sun.COM int size = total_size - body_size - UCODE_HEADER_SIZE_INTEL; 2597605SMark.Johnson@Sun.COM 2607605SMark.Johnson@Sun.COM ucode_free(id, ucodefp->uf_ext_table, size); 2614581Ssherrym ucodefp->uf_ext_table = NULL; 2624581Ssherrym } 2634581Ssherrym 2647605SMark.Johnson@Sun.COM ucode_free(id, ucodefp->uf_header, UCODE_HEADER_SIZE_INTEL); 2657605SMark.Johnson@Sun.COM ucodefp->uf_header = NULL; 2667605SMark.Johnson@Sun.COM } 2677605SMark.Johnson@Sun.COM 2687605SMark.Johnson@Sun.COM /* 2697605SMark.Johnson@Sun.COM * Find the equivalent CPU id in the equivalence table. 2707605SMark.Johnson@Sun.COM */ 2717605SMark.Johnson@Sun.COM static int 2728647SMark.Johnson@Sun.COM ucode_equiv_cpu_amd(cpu_t *cp, uint16_t *eq_sig) 2737605SMark.Johnson@Sun.COM { 2747605SMark.Johnson@Sun.COM char name[MAXPATHLEN]; 2757605SMark.Johnson@Sun.COM intptr_t fd; 2767605SMark.Johnson@Sun.COM int count; 2777605SMark.Johnson@Sun.COM int offset = 0, cpi_sig = cpuid_getsig(cp); 2787605SMark.Johnson@Sun.COM ucode_eqtbl_amd_t *eqtbl = ucode_eqtbl_amd; 2797605SMark.Johnson@Sun.COM 2807605SMark.Johnson@Sun.COM (void) snprintf(name, MAXPATHLEN, "/%s/%s/equivalence-table", 2817605SMark.Johnson@Sun.COM UCODE_INSTALL_PATH, cpuid_getvendorstr(cp)); 2827605SMark.Johnson@Sun.COM 2837605SMark.Johnson@Sun.COM /* 2847605SMark.Johnson@Sun.COM * No kmem_zalloc() etc. available on boot cpu. 2857605SMark.Johnson@Sun.COM */ 2867605SMark.Johnson@Sun.COM if (cp->cpu_id == 0) { 2877605SMark.Johnson@Sun.COM if ((fd = kobj_open(name)) == -1) 2887605SMark.Johnson@Sun.COM return (EM_OPENFILE); 2897605SMark.Johnson@Sun.COM /* ucode_zalloc() cannot fail on boot cpu */ 2907605SMark.Johnson@Sun.COM eqtbl = ucode_zalloc(cp->cpu_id, sizeof (*eqtbl)); 2917605SMark.Johnson@Sun.COM ASSERT(eqtbl); 2927605SMark.Johnson@Sun.COM do { 2937605SMark.Johnson@Sun.COM count = kobj_read(fd, (int8_t *)eqtbl, 2947605SMark.Johnson@Sun.COM sizeof (*eqtbl), offset); 2957605SMark.Johnson@Sun.COM if (count != sizeof (*eqtbl)) { 2967605SMark.Johnson@Sun.COM (void) kobj_close(fd); 2977605SMark.Johnson@Sun.COM return (EM_HIGHERREV); 2987605SMark.Johnson@Sun.COM } 2997605SMark.Johnson@Sun.COM offset += count; 3007605SMark.Johnson@Sun.COM } while (eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != cpi_sig); 3017605SMark.Johnson@Sun.COM (void) kobj_close(fd); 3027605SMark.Johnson@Sun.COM } 3037605SMark.Johnson@Sun.COM 3047605SMark.Johnson@Sun.COM /* 3057605SMark.Johnson@Sun.COM * If not already done, load the equivalence table. 3067605SMark.Johnson@Sun.COM * Not done on boot CPU. 3077605SMark.Johnson@Sun.COM */ 3087605SMark.Johnson@Sun.COM if (eqtbl == NULL) { 3097605SMark.Johnson@Sun.COM struct _buf *eq; 3107605SMark.Johnson@Sun.COM uint64_t size; 3117605SMark.Johnson@Sun.COM 3127605SMark.Johnson@Sun.COM if ((eq = kobj_open_file(name)) == (struct _buf *)-1) 3137605SMark.Johnson@Sun.COM return (EM_OPENFILE); 3147605SMark.Johnson@Sun.COM 3157605SMark.Johnson@Sun.COM if (kobj_get_filesize(eq, &size) < 0) { 3167605SMark.Johnson@Sun.COM kobj_close_file(eq); 3177605SMark.Johnson@Sun.COM return (EM_OPENFILE); 3187605SMark.Johnson@Sun.COM } 3197605SMark.Johnson@Sun.COM 3207605SMark.Johnson@Sun.COM ucode_eqtbl_amd = kmem_zalloc(size, KM_NOSLEEP); 3217605SMark.Johnson@Sun.COM if (ucode_eqtbl_amd == NULL) { 3227605SMark.Johnson@Sun.COM kobj_close_file(eq); 3237605SMark.Johnson@Sun.COM return (EM_NOMEM); 3247605SMark.Johnson@Sun.COM } 3257605SMark.Johnson@Sun.COM 3267605SMark.Johnson@Sun.COM count = kobj_read_file(eq, (char *)ucode_eqtbl_amd, size, 0); 3277605SMark.Johnson@Sun.COM kobj_close_file(eq); 3287605SMark.Johnson@Sun.COM 3297605SMark.Johnson@Sun.COM if (count != size) 3307605SMark.Johnson@Sun.COM return (EM_FILESIZE); 3317605SMark.Johnson@Sun.COM } 3327605SMark.Johnson@Sun.COM 3337605SMark.Johnson@Sun.COM /* Get the equivalent CPU id. */ 3347605SMark.Johnson@Sun.COM if (cp->cpu_id) 3357605SMark.Johnson@Sun.COM for (eqtbl = ucode_eqtbl_amd; 3367605SMark.Johnson@Sun.COM eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != cpi_sig; 3377605SMark.Johnson@Sun.COM eqtbl++) 3387605SMark.Johnson@Sun.COM ; 3397605SMark.Johnson@Sun.COM 3407605SMark.Johnson@Sun.COM *eq_sig = eqtbl->ue_equiv_cpu; 3417605SMark.Johnson@Sun.COM 3427605SMark.Johnson@Sun.COM /* No equivalent CPU id found, assume outdated microcode file. */ 3437605SMark.Johnson@Sun.COM if (*eq_sig == 0) 3447605SMark.Johnson@Sun.COM return (EM_HIGHERREV); 3457605SMark.Johnson@Sun.COM 3467605SMark.Johnson@Sun.COM return (EM_OK); 3474581Ssherrym } 3484581Ssherrym 3494581Ssherrym /* 3508647SMark.Johnson@Sun.COM * xVM cannot check for the presence of PCI devices. Look for chipset- 3518647SMark.Johnson@Sun.COM * specific microcode patches in the container file and disable them 3528647SMark.Johnson@Sun.COM * by setting their CPU revision to an invalid value. 3538647SMark.Johnson@Sun.COM */ 3548647SMark.Johnson@Sun.COM #ifdef __xpv 3558647SMark.Johnson@Sun.COM static void 3568647SMark.Johnson@Sun.COM ucode_chipset_amd(uint8_t *buf, int size) 3578647SMark.Johnson@Sun.COM { 3588647SMark.Johnson@Sun.COM ucode_header_amd_t *uh; 3598647SMark.Johnson@Sun.COM uint32_t *ptr = (uint32_t *)buf; 3608647SMark.Johnson@Sun.COM int len = 0; 3618647SMark.Johnson@Sun.COM 3628647SMark.Johnson@Sun.COM /* skip to first microcode patch */ 3638647SMark.Johnson@Sun.COM ptr += 2; len = *ptr++; ptr += len >> 2; size -= len; 3648647SMark.Johnson@Sun.COM 3658647SMark.Johnson@Sun.COM while (size >= sizeof (ucode_header_amd_t) + 8) { 3668647SMark.Johnson@Sun.COM ptr++; len = *ptr++; 3678647SMark.Johnson@Sun.COM uh = (ucode_header_amd_t *)ptr; 3688647SMark.Johnson@Sun.COM ptr += len >> 2; size -= len; 3698647SMark.Johnson@Sun.COM 3708647SMark.Johnson@Sun.COM if (uh->uh_nb_id) { 3718647SMark.Johnson@Sun.COM cmn_err(CE_WARN, "ignoring northbridge-specific ucode: " 3728647SMark.Johnson@Sun.COM "chipset id %x, revision %x", 3738647SMark.Johnson@Sun.COM uh->uh_nb_id, uh->uh_nb_rev); 3748647SMark.Johnson@Sun.COM uh->uh_cpu_rev = 0xffff; 3758647SMark.Johnson@Sun.COM } 3768647SMark.Johnson@Sun.COM 3778647SMark.Johnson@Sun.COM if (uh->uh_sb_id) { 3788647SMark.Johnson@Sun.COM cmn_err(CE_WARN, "ignoring southbridge-specific ucode: " 3798647SMark.Johnson@Sun.COM "chipset id %x, revision %x", 3808647SMark.Johnson@Sun.COM uh->uh_sb_id, uh->uh_sb_rev); 3818647SMark.Johnson@Sun.COM uh->uh_cpu_rev = 0xffff; 3828647SMark.Johnson@Sun.COM } 3838647SMark.Johnson@Sun.COM } 3848647SMark.Johnson@Sun.COM } 3858647SMark.Johnson@Sun.COM #endif 3868647SMark.Johnson@Sun.COM 3878647SMark.Johnson@Sun.COM /* 3884581Ssherrym * Populate the ucode file structure from microcode file corresponding to 3894581Ssherrym * this CPU, if exists. 3904581Ssherrym * 3914581Ssherrym * Return EM_OK on success, corresponding error code on failure. 3924581Ssherrym */ 3938647SMark.Johnson@Sun.COM /*ARGSUSED*/ 3944581Ssherrym static ucode_errno_t 3957605SMark.Johnson@Sun.COM ucode_locate_amd(cpu_t *cp, cpu_ucode_info_t *uinfop, ucode_file_t *ufp) 3967605SMark.Johnson@Sun.COM { 3977605SMark.Johnson@Sun.COM char name[MAXPATHLEN]; 3987605SMark.Johnson@Sun.COM intptr_t fd; 3998647SMark.Johnson@Sun.COM int count, rc; 4007605SMark.Johnson@Sun.COM ucode_file_amd_t *ucodefp = ufp->amd; 4017605SMark.Johnson@Sun.COM 4028647SMark.Johnson@Sun.COM #ifndef __xpv 4038647SMark.Johnson@Sun.COM uint16_t eq_sig = 0; 4048647SMark.Johnson@Sun.COM int i; 4058647SMark.Johnson@Sun.COM 4067605SMark.Johnson@Sun.COM /* get equivalent CPU id */ 4077605SMark.Johnson@Sun.COM if ((rc = ucode_equiv_cpu_amd(cp, &eq_sig)) != EM_OK) 4087605SMark.Johnson@Sun.COM return (rc); 4097605SMark.Johnson@Sun.COM 4107605SMark.Johnson@Sun.COM /* 4117605SMark.Johnson@Sun.COM * Allocate a buffer for the microcode patch. If the buffer has been 4127605SMark.Johnson@Sun.COM * allocated before, check for a matching microcode to avoid loading 4137605SMark.Johnson@Sun.COM * the file again. 4147605SMark.Johnson@Sun.COM */ 4157605SMark.Johnson@Sun.COM if (ucodefp == NULL) 4167605SMark.Johnson@Sun.COM ucodefp = ucode_zalloc(cp->cpu_id, sizeof (*ucodefp)); 4177605SMark.Johnson@Sun.COM else if (ucode_match_amd(eq_sig, uinfop, ucodefp, sizeof (*ucodefp)) 4187605SMark.Johnson@Sun.COM == EM_OK) 4197605SMark.Johnson@Sun.COM return (EM_OK); 4207605SMark.Johnson@Sun.COM 4217605SMark.Johnson@Sun.COM if (ucodefp == NULL) 4227605SMark.Johnson@Sun.COM return (EM_NOMEM); 4237605SMark.Johnson@Sun.COM 4247605SMark.Johnson@Sun.COM ufp->amd = ucodefp; 4257605SMark.Johnson@Sun.COM 4267605SMark.Johnson@Sun.COM /* 4277605SMark.Johnson@Sun.COM * Find the patch for this CPU. The patch files are named XXXX-YY, where 4287605SMark.Johnson@Sun.COM * XXXX is the equivalent CPU id and YY is the running patch number. 4297605SMark.Johnson@Sun.COM * Patches specific to certain chipsets are guaranteed to have lower 4307605SMark.Johnson@Sun.COM * numbers than less specific patches, so we can just load the first 4317605SMark.Johnson@Sun.COM * patch that matches. 4327605SMark.Johnson@Sun.COM */ 4337605SMark.Johnson@Sun.COM 4347605SMark.Johnson@Sun.COM for (i = 0; i < 0xff; i++) { 4357605SMark.Johnson@Sun.COM (void) snprintf(name, MAXPATHLEN, "/%s/%s/%04X-%02X", 4367605SMark.Johnson@Sun.COM UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), eq_sig, i); 4377605SMark.Johnson@Sun.COM if ((fd = kobj_open(name)) == -1) 4387605SMark.Johnson@Sun.COM return (EM_NOMATCH); 4397605SMark.Johnson@Sun.COM count = kobj_read(fd, (char *)ucodefp, sizeof (*ucodefp), 0); 4407605SMark.Johnson@Sun.COM (void) kobj_close(fd); 4417605SMark.Johnson@Sun.COM 4427605SMark.Johnson@Sun.COM if (ucode_match_amd(eq_sig, uinfop, ucodefp, count) == EM_OK) 4437605SMark.Johnson@Sun.COM return (EM_OK); 4447605SMark.Johnson@Sun.COM } 4457605SMark.Johnson@Sun.COM return (EM_NOMATCH); 4468647SMark.Johnson@Sun.COM #else 4478647SMark.Johnson@Sun.COM int size = 0; 4488647SMark.Johnson@Sun.COM char c; 4498647SMark.Johnson@Sun.COM 4508647SMark.Johnson@Sun.COM /* 4518647SMark.Johnson@Sun.COM * The xVM case is special. To support mixed-revision systems, the 4528647SMark.Johnson@Sun.COM * hypervisor will choose which patch to load for which CPU, so the 4538647SMark.Johnson@Sun.COM * whole microcode patch container file will have to be loaded. 4548647SMark.Johnson@Sun.COM * 4558647SMark.Johnson@Sun.COM * Since this code is only run on the boot cpu, we don't have to care 4568647SMark.Johnson@Sun.COM * about failing ucode_zalloc() or freeing allocated memory. 4578647SMark.Johnson@Sun.COM */ 4588647SMark.Johnson@Sun.COM if (cp->cpu_id != 0) 4598647SMark.Johnson@Sun.COM return (EM_INVALIDARG); 4608647SMark.Johnson@Sun.COM 4618647SMark.Johnson@Sun.COM (void) snprintf(name, MAXPATHLEN, "/%s/%s/container", 4628647SMark.Johnson@Sun.COM UCODE_INSTALL_PATH, cpuid_getvendorstr(cp)); 4638647SMark.Johnson@Sun.COM 4648647SMark.Johnson@Sun.COM if ((fd = kobj_open(name)) == -1) 4658647SMark.Johnson@Sun.COM return (EM_OPENFILE); 4668647SMark.Johnson@Sun.COM 4678647SMark.Johnson@Sun.COM /* get the file size by counting bytes */ 4688647SMark.Johnson@Sun.COM do { 4698647SMark.Johnson@Sun.COM count = kobj_read(fd, &c, 1, size); 4708647SMark.Johnson@Sun.COM size += count; 4718647SMark.Johnson@Sun.COM } while (count); 4728647SMark.Johnson@Sun.COM 4738647SMark.Johnson@Sun.COM ucodefp = ucode_zalloc(cp->cpu_id, sizeof (*ucodefp)); 4748647SMark.Johnson@Sun.COM ASSERT(ucodefp); 4758647SMark.Johnson@Sun.COM ufp->amd = ucodefp; 4768647SMark.Johnson@Sun.COM 4778647SMark.Johnson@Sun.COM ucodefp->usize = size; 4788647SMark.Johnson@Sun.COM ucodefp->ucodep = ucode_zalloc(cp->cpu_id, size); 4798647SMark.Johnson@Sun.COM ASSERT(ucodefp->ucodep); 4808647SMark.Johnson@Sun.COM 4818647SMark.Johnson@Sun.COM /* load the microcode patch container file */ 4828647SMark.Johnson@Sun.COM count = kobj_read(fd, (char *)ucodefp->ucodep, size, 0); 4838647SMark.Johnson@Sun.COM (void) kobj_close(fd); 4848647SMark.Johnson@Sun.COM 4858647SMark.Johnson@Sun.COM if (count != size) 4868647SMark.Johnson@Sun.COM return (EM_FILESIZE); 4878647SMark.Johnson@Sun.COM 4888647SMark.Johnson@Sun.COM /* make sure the container file is valid */ 4898647SMark.Johnson@Sun.COM rc = ucode->validate(ucodefp->ucodep, ucodefp->usize); 4908647SMark.Johnson@Sun.COM 4918647SMark.Johnson@Sun.COM if (rc != EM_OK) 4928647SMark.Johnson@Sun.COM return (rc); 4938647SMark.Johnson@Sun.COM 4948647SMark.Johnson@Sun.COM /* disable chipset-specific patches */ 4958647SMark.Johnson@Sun.COM ucode_chipset_amd(ucodefp->ucodep, ucodefp->usize); 4968647SMark.Johnson@Sun.COM 4978647SMark.Johnson@Sun.COM return (EM_OK); 4988647SMark.Johnson@Sun.COM #endif 4997605SMark.Johnson@Sun.COM } 5007605SMark.Johnson@Sun.COM 5017605SMark.Johnson@Sun.COM static ucode_errno_t 5027605SMark.Johnson@Sun.COM ucode_locate_intel(cpu_t *cp, cpu_ucode_info_t *uinfop, ucode_file_t *ufp) 5034581Ssherrym { 5044581Ssherrym char name[MAXPATHLEN]; 5054581Ssherrym intptr_t fd; 5064581Ssherrym int count; 5077605SMark.Johnson@Sun.COM int header_size = UCODE_HEADER_SIZE_INTEL; 5084581Ssherrym int cpi_sig = cpuid_getsig(cp); 5094581Ssherrym ucode_errno_t rc = EM_OK; 5107605SMark.Johnson@Sun.COM ucode_file_intel_t *ucodefp = &ufp->intel; 5117605SMark.Johnson@Sun.COM 5127605SMark.Johnson@Sun.COM ASSERT(ucode); 5134581Ssherrym 5144581Ssherrym /* 5154581Ssherrym * If the microcode matches the CPU we are processing, use it. 5164581Ssherrym */ 5177605SMark.Johnson@Sun.COM if (ucode_match_intel(cpi_sig, uinfop, ucodefp->uf_header, 5184581Ssherrym ucodefp->uf_ext_table) == EM_OK && ucodefp->uf_body != NULL) { 5194581Ssherrym return (EM_OK); 5204581Ssherrym } 5214581Ssherrym 5224581Ssherrym /* 5234581Ssherrym * Look for microcode file with the right name. 5244581Ssherrym */ 5254581Ssherrym (void) snprintf(name, MAXPATHLEN, "/%s/%s/%08X-%02X", 5264581Ssherrym UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), cpi_sig, 5274581Ssherrym uinfop->cui_platid); 5284581Ssherrym if ((fd = kobj_open(name)) == -1) { 5294581Ssherrym return (EM_OPENFILE); 5304581Ssherrym } 5314581Ssherrym 5324581Ssherrym /* 5334581Ssherrym * We found a microcode file for the CPU we are processing, 5344581Ssherrym * reset the microcode data structure and read in the new 5354581Ssherrym * file. 5364581Ssherrym */ 5377605SMark.Johnson@Sun.COM ucode->file_reset(ufp, cp->cpu_id); 5384581Ssherrym 5397605SMark.Johnson@Sun.COM ucodefp->uf_header = ucode_zalloc(cp->cpu_id, header_size); 5407605SMark.Johnson@Sun.COM if (ucodefp->uf_header == NULL) 5417605SMark.Johnson@Sun.COM return (EM_NOMEM); 5427605SMark.Johnson@Sun.COM 5437605SMark.Johnson@Sun.COM count = kobj_read(fd, (char *)ucodefp->uf_header, header_size, 0); 5444581Ssherrym 5454581Ssherrym switch (count) { 5467605SMark.Johnson@Sun.COM case UCODE_HEADER_SIZE_INTEL: { 5474581Ssherrym 5487605SMark.Johnson@Sun.COM ucode_header_intel_t *uhp = ucodefp->uf_header; 5494581Ssherrym uint32_t offset = header_size; 5504581Ssherrym int total_size, body_size, ext_size; 5514581Ssherrym uint32_t sum = 0; 5524581Ssherrym 5534581Ssherrym /* 5544581Ssherrym * Make sure that the header contains valid fields. 5554581Ssherrym */ 5567605SMark.Johnson@Sun.COM if ((rc = ucode_header_validate_intel(uhp)) == EM_OK) { 5577605SMark.Johnson@Sun.COM total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size); 5587605SMark.Johnson@Sun.COM body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size); 5597605SMark.Johnson@Sun.COM ucodefp->uf_body = ucode_zalloc(cp->cpu_id, body_size); 5607605SMark.Johnson@Sun.COM if (ucodefp->uf_body == NULL) { 5617605SMark.Johnson@Sun.COM rc = EM_NOMEM; 5627605SMark.Johnson@Sun.COM break; 5634581Ssherrym } 5644581Ssherrym 5654581Ssherrym if (kobj_read(fd, (char *)ucodefp->uf_body, 5664581Ssherrym body_size, offset) != body_size) 5674581Ssherrym rc = EM_FILESIZE; 5684581Ssherrym } 5694581Ssherrym 5704581Ssherrym if (rc) 5714581Ssherrym break; 5724581Ssherrym 5737605SMark.Johnson@Sun.COM sum = ucode_checksum_intel(0, header_size, 5747605SMark.Johnson@Sun.COM (uint8_t *)ucodefp->uf_header); 5757605SMark.Johnson@Sun.COM if (ucode_checksum_intel(sum, body_size, ucodefp->uf_body)) { 5764581Ssherrym rc = EM_CHECKSUM; 5774581Ssherrym break; 5784581Ssherrym } 5794581Ssherrym 5804581Ssherrym /* 5814581Ssherrym * Check to see if there is extended signature table. 5824581Ssherrym */ 5834581Ssherrym offset = body_size + header_size; 5844581Ssherrym ext_size = total_size - offset; 5854581Ssherrym 5864581Ssherrym if (ext_size <= 0) 5874581Ssherrym break; 5884581Ssherrym 5897605SMark.Johnson@Sun.COM ucodefp->uf_ext_table = ucode_zalloc(cp->cpu_id, ext_size); 5907605SMark.Johnson@Sun.COM if (ucodefp->uf_ext_table == NULL) { 5917605SMark.Johnson@Sun.COM rc = EM_NOMEM; 5927605SMark.Johnson@Sun.COM break; 5934581Ssherrym } 5944581Ssherrym 5954581Ssherrym if (kobj_read(fd, (char *)ucodefp->uf_ext_table, 5964581Ssherrym ext_size, offset) != ext_size) { 5974581Ssherrym rc = EM_FILESIZE; 5987605SMark.Johnson@Sun.COM } else if (ucode_checksum_intel(0, ext_size, 5994581Ssherrym (uint8_t *)(ucodefp->uf_ext_table))) { 6004581Ssherrym rc = EM_CHECKSUM; 6014581Ssherrym } else { 6024581Ssherrym int i; 6034581Ssherrym 6047605SMark.Johnson@Sun.COM ext_size -= UCODE_EXT_TABLE_SIZE_INTEL; 6054581Ssherrym for (i = 0; i < ucodefp->uf_ext_table->uet_count; 6064581Ssherrym i++) { 6077605SMark.Johnson@Sun.COM if (ucode_checksum_intel(0, 6087605SMark.Johnson@Sun.COM UCODE_EXT_SIG_SIZE_INTEL, 6094581Ssherrym (uint8_t *)(&(ucodefp->uf_ext_table-> 6104581Ssherrym uet_ext_sig[i])))) { 6114581Ssherrym rc = EM_CHECKSUM; 6124581Ssherrym break; 6134581Ssherrym } 6144581Ssherrym } 6154581Ssherrym } 6164581Ssherrym break; 6174581Ssherrym } 6184581Ssherrym 6194581Ssherrym default: 6204581Ssherrym rc = EM_FILESIZE; 6214581Ssherrym break; 6224581Ssherrym } 6234581Ssherrym 6244581Ssherrym kobj_close(fd); 6254581Ssherrym 6264581Ssherrym if (rc != EM_OK) 6274581Ssherrym return (rc); 6284581Ssherrym 6297605SMark.Johnson@Sun.COM rc = ucode_match_intel(cpi_sig, uinfop, ucodefp->uf_header, 6304581Ssherrym ucodefp->uf_ext_table); 6314581Ssherrym 6324581Ssherrym return (rc); 6334581Ssherrym } 6344581Ssherrym 6358647SMark.Johnson@Sun.COM #ifndef __xpv 6367605SMark.Johnson@Sun.COM static ucode_errno_t 6378647SMark.Johnson@Sun.COM ucode_match_amd(uint16_t eq_sig, cpu_ucode_info_t *uinfop, 6388647SMark.Johnson@Sun.COM ucode_file_amd_t *ucodefp, int size) 6397605SMark.Johnson@Sun.COM { 6407605SMark.Johnson@Sun.COM ucode_header_amd_t *uh; 6417605SMark.Johnson@Sun.COM 6427605SMark.Johnson@Sun.COM if (ucodefp == NULL || size < sizeof (ucode_header_amd_t)) 6437605SMark.Johnson@Sun.COM return (EM_NOMATCH); 6447605SMark.Johnson@Sun.COM 6457605SMark.Johnson@Sun.COM /* 6467605SMark.Johnson@Sun.COM * Don't even think about loading patches that would require code 6477605SMark.Johnson@Sun.COM * execution. 6487605SMark.Johnson@Sun.COM */ 6497605SMark.Johnson@Sun.COM if (size > offsetof(ucode_file_amd_t, uf_code_present) && 6507605SMark.Johnson@Sun.COM ucodefp->uf_code_present) 6517605SMark.Johnson@Sun.COM return (EM_NOMATCH); 6527605SMark.Johnson@Sun.COM 6537605SMark.Johnson@Sun.COM uh = &ucodefp->uf_header; 6547605SMark.Johnson@Sun.COM 6557605SMark.Johnson@Sun.COM if (eq_sig != uh->uh_cpu_rev) 6567605SMark.Johnson@Sun.COM return (EM_NOMATCH); 6577605SMark.Johnson@Sun.COM 6587605SMark.Johnson@Sun.COM if (uh->uh_nb_id) { 6597605SMark.Johnson@Sun.COM cmn_err(CE_WARN, "ignoring northbridge-specific ucode: " 6607605SMark.Johnson@Sun.COM "chipset id %x, revision %x", uh->uh_nb_id, uh->uh_nb_rev); 6617605SMark.Johnson@Sun.COM return (EM_NOMATCH); 6627605SMark.Johnson@Sun.COM } 6637605SMark.Johnson@Sun.COM 6647605SMark.Johnson@Sun.COM if (uh->uh_sb_id) { 6657605SMark.Johnson@Sun.COM cmn_err(CE_WARN, "ignoring southbridge-specific ucode: " 6667605SMark.Johnson@Sun.COM "chipset id %x, revision %x", uh->uh_sb_id, uh->uh_sb_rev); 6677605SMark.Johnson@Sun.COM return (EM_NOMATCH); 6687605SMark.Johnson@Sun.COM } 6697605SMark.Johnson@Sun.COM 6707605SMark.Johnson@Sun.COM if (uh->uh_patch_id <= uinfop->cui_rev) 6717605SMark.Johnson@Sun.COM return (EM_HIGHERREV); 6727605SMark.Johnson@Sun.COM 6737605SMark.Johnson@Sun.COM return (EM_OK); 6747605SMark.Johnson@Sun.COM } 6758647SMark.Johnson@Sun.COM #endif 6764581Ssherrym 6774581Ssherrym /* 6784581Ssherrym * Returns 1 if the microcode is for this processor; 0 otherwise. 6794581Ssherrym */ 6804581Ssherrym static ucode_errno_t 6817605SMark.Johnson@Sun.COM ucode_match_intel(int cpi_sig, cpu_ucode_info_t *uinfop, 6827605SMark.Johnson@Sun.COM ucode_header_intel_t *uhp, ucode_ext_table_intel_t *uetp) 6834581Ssherrym { 6847605SMark.Johnson@Sun.COM if (uhp == NULL) 6857605SMark.Johnson@Sun.COM return (EM_NOMATCH); 6864581Ssherrym 6877605SMark.Johnson@Sun.COM if (UCODE_MATCH_INTEL(cpi_sig, uhp->uh_signature, 6884581Ssherrym uinfop->cui_platid, uhp->uh_proc_flags)) { 6894581Ssherrym 6904581Ssherrym if (uinfop->cui_rev >= uhp->uh_rev && !ucode_force_update) 6914581Ssherrym return (EM_HIGHERREV); 6924581Ssherrym 6934581Ssherrym return (EM_OK); 6944581Ssherrym } 6954581Ssherrym 6964581Ssherrym if (uetp != NULL) { 6974581Ssherrym int i; 6984581Ssherrym 6994581Ssherrym for (i = 0; i < uetp->uet_count; i++) { 7007605SMark.Johnson@Sun.COM ucode_ext_sig_intel_t *uesp; 7014581Ssherrym 7024581Ssherrym uesp = &uetp->uet_ext_sig[i]; 7034581Ssherrym 7047605SMark.Johnson@Sun.COM if (UCODE_MATCH_INTEL(cpi_sig, uesp->ues_signature, 7054581Ssherrym uinfop->cui_platid, uesp->ues_proc_flags)) { 7064581Ssherrym 7074581Ssherrym if (uinfop->cui_rev >= uhp->uh_rev && 7084581Ssherrym !ucode_force_update) 7094581Ssherrym return (EM_HIGHERREV); 7104581Ssherrym 7114581Ssherrym return (EM_OK); 7124581Ssherrym } 7134581Ssherrym } 7144581Ssherrym } 7154581Ssherrym 7164581Ssherrym return (EM_NOMATCH); 7174581Ssherrym } 7184581Ssherrym 7194581Ssherrym /*ARGSUSED*/ 7204581Ssherrym static int 7214581Ssherrym ucode_write(xc_arg_t arg1, xc_arg_t unused2, xc_arg_t unused3) 7224581Ssherrym { 7237605SMark.Johnson@Sun.COM ucode_update_t *uusp = (ucode_update_t *)arg1; 7247605SMark.Johnson@Sun.COM cpu_ucode_info_t *uinfop = CPU->cpu_m.mcpu_ucode_info; 7254581Ssherrym 7267605SMark.Johnson@Sun.COM ASSERT(ucode); 7274581Ssherrym ASSERT(uusp->ucodep); 7284581Ssherrym 7297347SMark.Johnson@Sun.COM #ifndef __xpv 7304581Ssherrym /* 7314581Ssherrym * Check one more time to see if it is really necessary to update 7324581Ssherrym * microcode just in case this is a hyperthreaded processor where 7334581Ssherrym * the threads share the same microcode. 7344581Ssherrym */ 7354581Ssherrym if (!ucode_force_update) { 7367605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 7374581Ssherrym uusp->new_rev = uinfop->cui_rev; 7384581Ssherrym if (uinfop->cui_rev >= uusp->expected_rev) 7394581Ssherrym return (0); 7404581Ssherrym } 7414581Ssherrym 7427605SMark.Johnson@Sun.COM wrmsr(ucode->write_msr, (uintptr_t)uusp->ucodep); 7437347SMark.Johnson@Sun.COM #endif 7447605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 7454581Ssherrym uusp->new_rev = uinfop->cui_rev; 7464581Ssherrym 7474581Ssherrym return (0); 7484581Ssherrym } 7494581Ssherrym 7507605SMark.Johnson@Sun.COM /*ARGSUSED*/ 7517605SMark.Johnson@Sun.COM static uint32_t 7527605SMark.Johnson@Sun.COM ucode_load_amd(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp) 7537605SMark.Johnson@Sun.COM { 7547605SMark.Johnson@Sun.COM ucode_file_amd_t *ucodefp = ufp->amd; 7557605SMark.Johnson@Sun.COM #ifdef __xpv 7567605SMark.Johnson@Sun.COM ucode_update_t uus; 7577605SMark.Johnson@Sun.COM #endif 7584581Ssherrym 7597605SMark.Johnson@Sun.COM ASSERT(ucode); 7607605SMark.Johnson@Sun.COM ASSERT(ucodefp); 7617605SMark.Johnson@Sun.COM 7627605SMark.Johnson@Sun.COM #ifndef __xpv 7637605SMark.Johnson@Sun.COM kpreempt_disable(); 7647605SMark.Johnson@Sun.COM wrmsr(ucode->write_msr, (uintptr_t)ucodefp); 7657605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 7667605SMark.Johnson@Sun.COM kpreempt_enable(); 7678647SMark.Johnson@Sun.COM 7688647SMark.Johnson@Sun.COM return (ucodefp->uf_header.uh_patch_id); 7697605SMark.Johnson@Sun.COM #else 7708647SMark.Johnson@Sun.COM uus.ucodep = ucodefp->ucodep; 7718647SMark.Johnson@Sun.COM uus.usize = ucodefp->usize; 7727605SMark.Johnson@Sun.COM ucode_load_xpv(&uus); 7737605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 7747605SMark.Johnson@Sun.COM uus.new_rev = uinfop->cui_rev; 7758647SMark.Johnson@Sun.COM 7768647SMark.Johnson@Sun.COM return (uus.new_rev); 7777605SMark.Johnson@Sun.COM #endif 7787605SMark.Johnson@Sun.COM } 7797605SMark.Johnson@Sun.COM 7807605SMark.Johnson@Sun.COM /*ARGSUSED2*/ 7817605SMark.Johnson@Sun.COM static uint32_t 7827605SMark.Johnson@Sun.COM ucode_load_intel(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp) 7834581Ssherrym { 7847605SMark.Johnson@Sun.COM ucode_file_intel_t *ucodefp = &ufp->intel; 7857605SMark.Johnson@Sun.COM #ifdef __xpv 7867605SMark.Johnson@Sun.COM uint32_t ext_offset; 7877605SMark.Johnson@Sun.COM uint32_t body_size; 7887605SMark.Johnson@Sun.COM uint32_t ext_size; 7897605SMark.Johnson@Sun.COM uint8_t *ustart; 7907605SMark.Johnson@Sun.COM uint32_t usize; 7917605SMark.Johnson@Sun.COM ucode_update_t uus; 7927605SMark.Johnson@Sun.COM #endif 7937605SMark.Johnson@Sun.COM 7947605SMark.Johnson@Sun.COM ASSERT(ucode); 7957605SMark.Johnson@Sun.COM 7967605SMark.Johnson@Sun.COM #ifdef __xpv 7977605SMark.Johnson@Sun.COM /* 7987605SMark.Johnson@Sun.COM * the hypervisor wants the header, data, and extended 7997605SMark.Johnson@Sun.COM * signature tables. We can only get here from the boot 8007605SMark.Johnson@Sun.COM * CPU (cpu #0), we don't need to free as ucode_zalloc() will 8017605SMark.Johnson@Sun.COM * use BOP_ALLOC(). 8027605SMark.Johnson@Sun.COM */ 8037605SMark.Johnson@Sun.COM usize = UCODE_TOTAL_SIZE_INTEL(ucodefp->uf_header->uh_total_size); 8047605SMark.Johnson@Sun.COM ustart = ucode_zalloc(cp->cpu_id, usize); 8057605SMark.Johnson@Sun.COM ASSERT(ustart); 8067605SMark.Johnson@Sun.COM 8077605SMark.Johnson@Sun.COM body_size = UCODE_BODY_SIZE_INTEL(ucodefp->uf_header->uh_body_size); 8087605SMark.Johnson@Sun.COM ext_offset = body_size + UCODE_HEADER_SIZE_INTEL; 8097605SMark.Johnson@Sun.COM ext_size = usize - ext_offset; 8107605SMark.Johnson@Sun.COM ASSERT(ext_size >= 0); 8117605SMark.Johnson@Sun.COM 8127605SMark.Johnson@Sun.COM (void) memcpy(ustart, ucodefp->uf_header, UCODE_HEADER_SIZE_INTEL); 8137605SMark.Johnson@Sun.COM (void) memcpy(&ustart[UCODE_HEADER_SIZE_INTEL], ucodefp->uf_body, 8147605SMark.Johnson@Sun.COM body_size); 8157605SMark.Johnson@Sun.COM if (ext_size > 0) { 8167605SMark.Johnson@Sun.COM (void) memcpy(&ustart[ext_offset], 8177605SMark.Johnson@Sun.COM ucodefp->uf_ext_table, ext_size); 8187605SMark.Johnson@Sun.COM } 8197605SMark.Johnson@Sun.COM uus.ucodep = ustart; 8207605SMark.Johnson@Sun.COM uus.usize = usize; 8217605SMark.Johnson@Sun.COM ucode_load_xpv(&uus); 8227605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 8237605SMark.Johnson@Sun.COM uus.new_rev = uinfop->cui_rev; 8247605SMark.Johnson@Sun.COM #else 8254581Ssherrym kpreempt_disable(); 8267605SMark.Johnson@Sun.COM wrmsr(ucode->write_msr, (uintptr_t)ucodefp->uf_body); 8277605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 8284581Ssherrym kpreempt_enable(); 8297605SMark.Johnson@Sun.COM #endif 8307605SMark.Johnson@Sun.COM 8317605SMark.Johnson@Sun.COM return (ucodefp->uf_header->uh_rev); 8324581Ssherrym } 8334581Ssherrym 8347347SMark.Johnson@Sun.COM 8357347SMark.Johnson@Sun.COM #ifdef __xpv 8367347SMark.Johnson@Sun.COM static void 8377605SMark.Johnson@Sun.COM ucode_load_xpv(ucode_update_t *uusp) 8387347SMark.Johnson@Sun.COM { 8397347SMark.Johnson@Sun.COM xen_platform_op_t op; 8407347SMark.Johnson@Sun.COM int e; 8417347SMark.Johnson@Sun.COM 8427347SMark.Johnson@Sun.COM ASSERT(DOMAIN_IS_INITDOMAIN(xen_info)); 8437347SMark.Johnson@Sun.COM 8447347SMark.Johnson@Sun.COM kpreempt_disable(); 8457347SMark.Johnson@Sun.COM op.cmd = XENPF_microcode_update; 8467347SMark.Johnson@Sun.COM op.interface_version = XENPF_INTERFACE_VERSION; 8477347SMark.Johnson@Sun.COM /*LINTED: constant in conditional context*/ 8487605SMark.Johnson@Sun.COM set_xen_guest_handle(op.u.microcode.data, uusp->ucodep); 8497605SMark.Johnson@Sun.COM op.u.microcode.length = uusp->usize; 8507347SMark.Johnson@Sun.COM e = HYPERVISOR_platform_op(&op); 8517347SMark.Johnson@Sun.COM if (e != 0) { 8527347SMark.Johnson@Sun.COM cmn_err(CE_WARN, "hypervisor failed to accept uCode update"); 8537347SMark.Johnson@Sun.COM } 8547347SMark.Johnson@Sun.COM kpreempt_enable(); 8557347SMark.Johnson@Sun.COM } 8567347SMark.Johnson@Sun.COM #endif /* __xpv */ 8577347SMark.Johnson@Sun.COM 8587605SMark.Johnson@Sun.COM static void 8597605SMark.Johnson@Sun.COM ucode_read_rev_amd(cpu_ucode_info_t *uinfop) 8607605SMark.Johnson@Sun.COM { 8617605SMark.Johnson@Sun.COM uinfop->cui_rev = rdmsr(MSR_AMD_PATCHLEVEL); 8627605SMark.Johnson@Sun.COM } 8637347SMark.Johnson@Sun.COM 8644581Ssherrym static void 8657605SMark.Johnson@Sun.COM ucode_read_rev_intel(cpu_ucode_info_t *uinfop) 8664581Ssherrym { 8674581Ssherrym struct cpuid_regs crs; 8684581Ssherrym 8694581Ssherrym /* 8704581Ssherrym * The Intel 64 and IA-32 Architecture Software Developer's Manual 8714581Ssherrym * recommends that MSR_INTC_UCODE_REV be loaded with 0 first, then 8724581Ssherrym * execute cpuid to guarantee the correct reading of this register. 8734581Ssherrym */ 8744581Ssherrym wrmsr(MSR_INTC_UCODE_REV, 0); 8754581Ssherrym (void) __cpuid_insn(&crs); 8764581Ssherrym uinfop->cui_rev = (rdmsr(MSR_INTC_UCODE_REV) >> INTC_UCODE_REV_SHIFT); 8774581Ssherrym } 8784581Ssherrym 8797605SMark.Johnson@Sun.COM static ucode_errno_t 8807605SMark.Johnson@Sun.COM ucode_extract_amd(ucode_update_t *uusp, uint8_t *ucodep, int size) 8817605SMark.Johnson@Sun.COM { 8828647SMark.Johnson@Sun.COM #ifndef __xpv 8837605SMark.Johnson@Sun.COM uint32_t *ptr = (uint32_t *)ucodep; 8847605SMark.Johnson@Sun.COM ucode_eqtbl_amd_t *eqtbl; 8857605SMark.Johnson@Sun.COM ucode_file_amd_t *ufp; 8868647SMark.Johnson@Sun.COM int count; 8878647SMark.Johnson@Sun.COM int higher = 0; 8888647SMark.Johnson@Sun.COM ucode_errno_t rc = EM_NOMATCH; 8898647SMark.Johnson@Sun.COM uint16_t eq_sig; 8907605SMark.Johnson@Sun.COM 8917605SMark.Johnson@Sun.COM /* skip over magic number & equivalence table header */ 8927605SMark.Johnson@Sun.COM ptr += 2; size -= 8; 8937605SMark.Johnson@Sun.COM 8947605SMark.Johnson@Sun.COM count = *ptr++; size -= 4; 8957605SMark.Johnson@Sun.COM for (eqtbl = (ucode_eqtbl_amd_t *)ptr; 8967605SMark.Johnson@Sun.COM eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != uusp->sig; 8977605SMark.Johnson@Sun.COM eqtbl++) 8987605SMark.Johnson@Sun.COM ; 8997605SMark.Johnson@Sun.COM 9007605SMark.Johnson@Sun.COM eq_sig = eqtbl->ue_equiv_cpu; 9017605SMark.Johnson@Sun.COM 9027605SMark.Johnson@Sun.COM /* No equivalent CPU id found, assume outdated microcode file. */ 9037605SMark.Johnson@Sun.COM if (eq_sig == 0) 9047605SMark.Johnson@Sun.COM return (EM_HIGHERREV); 9057605SMark.Johnson@Sun.COM 9067605SMark.Johnson@Sun.COM /* Use the first microcode patch that matches. */ 9077605SMark.Johnson@Sun.COM do { 9087605SMark.Johnson@Sun.COM ptr += count >> 2; size -= count; 9097605SMark.Johnson@Sun.COM 9107605SMark.Johnson@Sun.COM if (!size) 9118647SMark.Johnson@Sun.COM return (higher ? EM_HIGHERREV : EM_NOMATCH); 9127605SMark.Johnson@Sun.COM 9137605SMark.Johnson@Sun.COM ptr++; size -= 4; 9147605SMark.Johnson@Sun.COM count = *ptr++; size -= 4; 9157605SMark.Johnson@Sun.COM ufp = (ucode_file_amd_t *)ptr; 9168647SMark.Johnson@Sun.COM 9178647SMark.Johnson@Sun.COM rc = ucode_match_amd(eq_sig, &uusp->info, ufp, count); 9188647SMark.Johnson@Sun.COM if (rc == EM_HIGHERREV) 9198647SMark.Johnson@Sun.COM higher = 1; 9208647SMark.Johnson@Sun.COM } while (rc != EM_OK); 9217605SMark.Johnson@Sun.COM 9227605SMark.Johnson@Sun.COM uusp->ucodep = (uint8_t *)ufp; 9237605SMark.Johnson@Sun.COM uusp->usize = count; 9247605SMark.Johnson@Sun.COM uusp->expected_rev = ufp->uf_header.uh_patch_id; 9258647SMark.Johnson@Sun.COM #else 9268647SMark.Johnson@Sun.COM /* 9278647SMark.Johnson@Sun.COM * The hypervisor will choose the patch to load, so there is no way to 9288647SMark.Johnson@Sun.COM * know the "expected revision" in advance. This is especially true on 9298647SMark.Johnson@Sun.COM * mixed-revision systems where more than one patch will be loaded. 9308647SMark.Johnson@Sun.COM */ 9318647SMark.Johnson@Sun.COM uusp->expected_rev = 0; 9328647SMark.Johnson@Sun.COM uusp->ucodep = ucodep; 9338647SMark.Johnson@Sun.COM uusp->usize = size; 9348647SMark.Johnson@Sun.COM 9358647SMark.Johnson@Sun.COM ucode_chipset_amd(ucodep, size); 9368647SMark.Johnson@Sun.COM #endif 9377605SMark.Johnson@Sun.COM 9387605SMark.Johnson@Sun.COM return (EM_OK); 9397605SMark.Johnson@Sun.COM } 9407605SMark.Johnson@Sun.COM 9417605SMark.Johnson@Sun.COM static ucode_errno_t 9427605SMark.Johnson@Sun.COM ucode_extract_intel(ucode_update_t *uusp, uint8_t *ucodep, int size) 9437605SMark.Johnson@Sun.COM { 9447605SMark.Johnson@Sun.COM uint32_t header_size = UCODE_HEADER_SIZE_INTEL; 9457605SMark.Johnson@Sun.COM int remaining; 9467605SMark.Johnson@Sun.COM int found = 0; 9477605SMark.Johnson@Sun.COM ucode_errno_t search_rc = EM_NOMATCH; /* search result */ 9487605SMark.Johnson@Sun.COM 9497605SMark.Johnson@Sun.COM /* 9507605SMark.Johnson@Sun.COM * Go through the whole buffer in case there are 9517605SMark.Johnson@Sun.COM * multiple versions of matching microcode for this 9527605SMark.Johnson@Sun.COM * processor. 9537605SMark.Johnson@Sun.COM */ 9547605SMark.Johnson@Sun.COM for (remaining = size; remaining > 0; ) { 9557605SMark.Johnson@Sun.COM int total_size, body_size, ext_size; 9567605SMark.Johnson@Sun.COM uint8_t *curbuf = &ucodep[size - remaining]; 9577605SMark.Johnson@Sun.COM ucode_header_intel_t *uhp = (ucode_header_intel_t *)curbuf; 9587605SMark.Johnson@Sun.COM ucode_ext_table_intel_t *uetp = NULL; 9597605SMark.Johnson@Sun.COM ucode_errno_t tmprc; 9607605SMark.Johnson@Sun.COM 9617605SMark.Johnson@Sun.COM total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size); 9627605SMark.Johnson@Sun.COM body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size); 9637605SMark.Johnson@Sun.COM ext_size = total_size - (header_size + body_size); 9647605SMark.Johnson@Sun.COM 9657605SMark.Johnson@Sun.COM if (ext_size > 0) 9667605SMark.Johnson@Sun.COM uetp = (ucode_ext_table_intel_t *) 9677605SMark.Johnson@Sun.COM &curbuf[header_size + body_size]; 9687605SMark.Johnson@Sun.COM 9697605SMark.Johnson@Sun.COM tmprc = ucode_match_intel(uusp->sig, &uusp->info, uhp, uetp); 9707605SMark.Johnson@Sun.COM 9717605SMark.Johnson@Sun.COM /* 9727605SMark.Johnson@Sun.COM * Since we are searching through a big file 9737605SMark.Johnson@Sun.COM * containing microcode for pretty much all the 9747605SMark.Johnson@Sun.COM * processors, we are bound to get EM_NOMATCH 9757605SMark.Johnson@Sun.COM * at one point. However, if we return 9767605SMark.Johnson@Sun.COM * EM_NOMATCH to users, it will really confuse 9777605SMark.Johnson@Sun.COM * them. Therefore, if we ever find a match of 9787605SMark.Johnson@Sun.COM * a lower rev, we will set return code to 9797605SMark.Johnson@Sun.COM * EM_HIGHERREV. 9807605SMark.Johnson@Sun.COM */ 9817605SMark.Johnson@Sun.COM if (tmprc == EM_HIGHERREV) 9827605SMark.Johnson@Sun.COM search_rc = EM_HIGHERREV; 9837605SMark.Johnson@Sun.COM 9847605SMark.Johnson@Sun.COM if (tmprc == EM_OK && 9857605SMark.Johnson@Sun.COM uusp->expected_rev < uhp->uh_rev) { 9867605SMark.Johnson@Sun.COM #ifndef __xpv 9877605SMark.Johnson@Sun.COM uusp->ucodep = (uint8_t *)&curbuf[header_size]; 9887605SMark.Johnson@Sun.COM #else 9897605SMark.Johnson@Sun.COM uusp->ucodep = (uint8_t *)curbuf; 9907605SMark.Johnson@Sun.COM #endif 9917605SMark.Johnson@Sun.COM uusp->usize = 9927605SMark.Johnson@Sun.COM UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size); 9937605SMark.Johnson@Sun.COM uusp->expected_rev = uhp->uh_rev; 9947605SMark.Johnson@Sun.COM found = 1; 9957605SMark.Johnson@Sun.COM } 9967605SMark.Johnson@Sun.COM 9977605SMark.Johnson@Sun.COM remaining -= total_size; 9987605SMark.Johnson@Sun.COM } 9997605SMark.Johnson@Sun.COM 10007605SMark.Johnson@Sun.COM if (!found) 10017605SMark.Johnson@Sun.COM return (search_rc); 10027605SMark.Johnson@Sun.COM 10037605SMark.Johnson@Sun.COM return (EM_OK); 10047605SMark.Johnson@Sun.COM } 10054581Ssherrym /* 10064581Ssherrym * Entry point to microcode update from the ucode_drv driver. 10074581Ssherrym * 10084581Ssherrym * Returns EM_OK on success, corresponding error code on failure. 10094581Ssherrym */ 10104581Ssherrym ucode_errno_t 10114581Ssherrym ucode_update(uint8_t *ucodep, int size) 10124581Ssherrym { 10134581Ssherrym int found = 0; 10144581Ssherrym processorid_t id; 10157605SMark.Johnson@Sun.COM ucode_update_t cached = { 0 }; 10167605SMark.Johnson@Sun.COM ucode_update_t *cachedp = NULL; 10174581Ssherrym ucode_errno_t rc = EM_OK; 10184581Ssherrym ucode_errno_t search_rc = EM_NOMATCH; /* search result */ 10194581Ssherrym cpuset_t cpuset; 10204581Ssherrym 10217605SMark.Johnson@Sun.COM ASSERT(ucode); 10224581Ssherrym ASSERT(ucodep); 10234581Ssherrym CPUSET_ZERO(cpuset); 10244581Ssherrym 10257605SMark.Johnson@Sun.COM if (!ucode->capable(CPU)) 10264581Ssherrym return (EM_NOTSUP); 10274581Ssherrym 10284581Ssherrym mutex_enter(&cpu_lock); 10294581Ssherrym 10304581Ssherrym for (id = 0; id < max_ncpus; id++) { 10314581Ssherrym cpu_t *cpu; 10327605SMark.Johnson@Sun.COM ucode_update_t uus = { 0 }; 10337605SMark.Johnson@Sun.COM ucode_update_t *uusp = &uus; 10344581Ssherrym 10354581Ssherrym /* 10364581Ssherrym * If there is no such CPU or it is not xcall ready, skip it. 10374581Ssherrym */ 10384581Ssherrym if ((cpu = cpu_get(id)) == NULL || 10394581Ssherrym !(cpu->cpu_flags & CPU_READY)) 10404581Ssherrym continue; 10414581Ssherrym 10424581Ssherrym uusp->sig = cpuid_getsig(cpu); 10434581Ssherrym bcopy(cpu->cpu_m.mcpu_ucode_info, &uusp->info, 10444581Ssherrym sizeof (uusp->info)); 10454581Ssherrym 10464581Ssherrym /* 10474581Ssherrym * If the current CPU has the same signature and platform 10484581Ssherrym * id as the previous one we processed, reuse the information. 10494581Ssherrym */ 10504581Ssherrym if (cachedp && cachedp->sig == cpuid_getsig(cpu) && 10514581Ssherrym cachedp->info.cui_platid == uusp->info.cui_platid) { 10524581Ssherrym uusp->ucodep = cachedp->ucodep; 10534581Ssherrym uusp->expected_rev = cachedp->expected_rev; 10544581Ssherrym /* 10554581Ssherrym * Intuitively we should check here to see whether the 10564581Ssherrym * running microcode rev is >= the expected rev, and 10574581Ssherrym * quit if it is. But we choose to proceed with the 10584581Ssherrym * xcall regardless of the running version so that 10594581Ssherrym * the other threads in an HT processor can update 10604581Ssherrym * the cpu_ucode_info structure in machcpu. 10614581Ssherrym */ 10627605SMark.Johnson@Sun.COM } else if ((search_rc = ucode->extract(uusp, ucodep, size)) 10637605SMark.Johnson@Sun.COM == EM_OK) { 10647605SMark.Johnson@Sun.COM bcopy(uusp, &cached, sizeof (cached)); 10657605SMark.Johnson@Sun.COM cachedp = &cached; 10667605SMark.Johnson@Sun.COM found = 1; 10674581Ssherrym } 10684581Ssherrym 10694581Ssherrym /* Nothing to do */ 10704581Ssherrym if (uusp->ucodep == NULL) 10714581Ssherrym continue; 10724581Ssherrym 10737347SMark.Johnson@Sun.COM #ifdef __xpv 10747347SMark.Johnson@Sun.COM /* 10757347SMark.Johnson@Sun.COM * for i86xpv, the hypervisor will update all the CPUs. 10767347SMark.Johnson@Sun.COM * the hypervisor wants the header, data, and extended 10777347SMark.Johnson@Sun.COM * signature tables. ucode_write will just read in the 10787347SMark.Johnson@Sun.COM * updated version on all the CPUs after the update has 10797347SMark.Johnson@Sun.COM * completed. 10807347SMark.Johnson@Sun.COM */ 10817402SMark.Johnson@Sun.COM if (id == 0) { 10827605SMark.Johnson@Sun.COM ucode_load_xpv(uusp); 10837402SMark.Johnson@Sun.COM } 10847347SMark.Johnson@Sun.COM #endif 10857347SMark.Johnson@Sun.COM 10864581Ssherrym CPUSET_ADD(cpuset, id); 10874581Ssherrym kpreempt_disable(); 10884581Ssherrym xc_sync((xc_arg_t)uusp, 0, 0, X_CALL_HIPRI, cpuset, 10894581Ssherrym ucode_write); 10904581Ssherrym kpreempt_enable(); 10914581Ssherrym CPUSET_DEL(cpuset, id); 10924581Ssherrym 10938647SMark.Johnson@Sun.COM if (uusp->new_rev != 0 && uusp->info.cui_rev == uusp->new_rev) { 10948647SMark.Johnson@Sun.COM rc = EM_HIGHERREV; 10958647SMark.Johnson@Sun.COM } else if ((uusp->new_rev == 0) || (uusp->expected_rev != 0 && 10968647SMark.Johnson@Sun.COM uusp->expected_rev != uusp->new_rev)) { 10974581Ssherrym cmn_err(CE_WARN, ucode_failure_fmt, 10984581Ssherrym id, uusp->info.cui_rev, uusp->expected_rev); 10994581Ssherrym rc = EM_UPDATE; 11008647SMark.Johnson@Sun.COM } else { 11018647SMark.Johnson@Sun.COM cmn_err(CE_CONT, ucode_success_fmt, 11028647SMark.Johnson@Sun.COM id, uusp->info.cui_rev, uusp->new_rev); 11034581Ssherrym } 11044581Ssherrym } 11054581Ssherrym 11064581Ssherrym mutex_exit(&cpu_lock); 11074581Ssherrym 11084581Ssherrym if (!found) 11094581Ssherrym rc = search_rc; 11104581Ssherrym 11114581Ssherrym return (rc); 11124581Ssherrym } 11134581Ssherrym 11144581Ssherrym /* 11154581Ssherrym * Initialize mcpu_ucode_info, and perform microcode update if necessary. 11164581Ssherrym * This is the entry point from boot path where pointer to CPU structure 11174581Ssherrym * is available. 11184581Ssherrym * 11194581Ssherrym * cpuid_info must be initialized before ucode_check can be called. 11204581Ssherrym */ 11214581Ssherrym void 11224581Ssherrym ucode_check(cpu_t *cp) 11234581Ssherrym { 11247605SMark.Johnson@Sun.COM cpu_ucode_info_t *uinfop; 11254581Ssherrym ucode_errno_t rc = EM_OK; 11267605SMark.Johnson@Sun.COM uint32_t new_rev = 0; 11274581Ssherrym 11284581Ssherrym ASSERT(cp); 11294581Ssherrym if (cp->cpu_id == 0) 11304581Ssherrym cp->cpu_m.mcpu_ucode_info = &cpu_ucode_info0; 11314581Ssherrym 11324581Ssherrym uinfop = cp->cpu_m.mcpu_ucode_info; 11334581Ssherrym ASSERT(uinfop); 11344581Ssherrym 11357605SMark.Johnson@Sun.COM /* set up function pointers if not already done */ 11367605SMark.Johnson@Sun.COM if (!ucode) 11377605SMark.Johnson@Sun.COM switch (cpuid_getvendor(cp)) { 11387605SMark.Johnson@Sun.COM case X86_VENDOR_AMD: 11397605SMark.Johnson@Sun.COM ucode = &ucode_amd; 11407605SMark.Johnson@Sun.COM break; 11417605SMark.Johnson@Sun.COM case X86_VENDOR_Intel: 11427605SMark.Johnson@Sun.COM ucode = &ucode_intel; 11437605SMark.Johnson@Sun.COM break; 11447605SMark.Johnson@Sun.COM default: 11457605SMark.Johnson@Sun.COM return; 11467605SMark.Johnson@Sun.COM } 11477605SMark.Johnson@Sun.COM 11487605SMark.Johnson@Sun.COM if (!ucode->capable(cp)) 11494581Ssherrym return; 11504581Ssherrym 11514581Ssherrym /* 11524581Ssherrym * The MSR_INTC_PLATFORM_ID is supported in Celeron and Xeon 11534581Ssherrym * (Family 6, model 5 and above) and all processors after. 11544581Ssherrym */ 11557605SMark.Johnson@Sun.COM if ((cpuid_getvendor(cp) == X86_VENDOR_Intel) && 11567605SMark.Johnson@Sun.COM ((cpuid_getmodel(cp) >= 5) || (cpuid_getfamily(cp) > 6))) { 11574581Ssherrym uinfop->cui_platid = 1 << ((rdmsr(MSR_INTC_PLATFORM_ID) >> 11584581Ssherrym INTC_PLATFORM_ID_SHIFT) & INTC_PLATFORM_ID_MASK); 11594581Ssherrym } 11604581Ssherrym 11617605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 11624581Ssherrym 11637402SMark.Johnson@Sun.COM #ifdef __xpv 11647402SMark.Johnson@Sun.COM /* 11657402SMark.Johnson@Sun.COM * for i86xpv, the hypervisor will update all the CPUs. We only need 11667402SMark.Johnson@Sun.COM * do do this on one of the CPUs (and there always is a CPU 0). 11677402SMark.Johnson@Sun.COM */ 11687402SMark.Johnson@Sun.COM if (cp->cpu_id != 0) { 11697402SMark.Johnson@Sun.COM return; 11707402SMark.Johnson@Sun.COM } 11717402SMark.Johnson@Sun.COM #endif 11727402SMark.Johnson@Sun.COM 11734581Ssherrym /* 11744581Ssherrym * Check to see if we need ucode update 11754581Ssherrym */ 11767605SMark.Johnson@Sun.COM if ((rc = ucode->locate(cp, uinfop, &ucodefile)) == EM_OK) { 11777605SMark.Johnson@Sun.COM new_rev = ucode->load(&ucodefile, uinfop, cp); 11787347SMark.Johnson@Sun.COM 11797605SMark.Johnson@Sun.COM if (uinfop->cui_rev != new_rev) 11804581Ssherrym cmn_err(CE_WARN, ucode_failure_fmt, cp->cpu_id, 11817605SMark.Johnson@Sun.COM uinfop->cui_rev, new_rev); 11824581Ssherrym } 11834581Ssherrym 11844581Ssherrym /* 11854581Ssherrym * If we fail to find a match for any reason, free the file structure 11864581Ssherrym * just in case we have read in a partial file. 11874581Ssherrym * 11884581Ssherrym * Since the scratch memory for holding the microcode for the boot CPU 11894581Ssherrym * came from BOP_ALLOC, we will reset the data structure as if we 11904581Ssherrym * never did the allocation so we don't have to keep track of this 11914581Ssherrym * special chunk of memory. We free the memory used for the rest 11924581Ssherrym * of the CPUs in start_other_cpus(). 11934581Ssherrym */ 11944581Ssherrym if (rc != EM_OK || cp->cpu_id == 0) 11957605SMark.Johnson@Sun.COM ucode->file_reset(&ucodefile, cp->cpu_id); 11964581Ssherrym } 11974581Ssherrym 11984581Ssherrym /* 11994581Ssherrym * Returns microcode revision from the machcpu structure. 12004581Ssherrym */ 12014581Ssherrym ucode_errno_t 12024581Ssherrym ucode_get_rev(uint32_t *revp) 12034581Ssherrym { 12044581Ssherrym int i; 12054581Ssherrym 12067605SMark.Johnson@Sun.COM ASSERT(ucode); 12074581Ssherrym ASSERT(revp); 12084581Ssherrym 12097605SMark.Johnson@Sun.COM if (!ucode->capable(CPU)) 12104581Ssherrym return (EM_NOTSUP); 12114581Ssherrym 12124581Ssherrym mutex_enter(&cpu_lock); 12134581Ssherrym for (i = 0; i < max_ncpus; i++) { 12144581Ssherrym cpu_t *cpu; 12154581Ssherrym 12164581Ssherrym if ((cpu = cpu_get(i)) == NULL) 12174581Ssherrym continue; 12184581Ssherrym 12194581Ssherrym revp[i] = cpu->cpu_m.mcpu_ucode_info->cui_rev; 12204581Ssherrym } 12214581Ssherrym mutex_exit(&cpu_lock); 12224581Ssherrym 12234581Ssherrym return (EM_OK); 12244581Ssherrym } 1225