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 { 163*9627SMark.Johnson@Sun.COM if (ucode == NULL) 164*9627SMark.Johnson@Sun.COM return; 1657605SMark.Johnson@Sun.COM 1667605SMark.Johnson@Sun.COM ucode->file_reset(&ucodefile, -1); 1677605SMark.Johnson@Sun.COM } 1687605SMark.Johnson@Sun.COM 1697605SMark.Johnson@Sun.COM /* 1707605SMark.Johnson@Sun.COM * Allocate/free a buffer used to hold ucode data. Space for the boot CPU is 1717605SMark.Johnson@Sun.COM * allocated with BOP_ALLOC() and does not require a free. 1727605SMark.Johnson@Sun.COM */ 1737605SMark.Johnson@Sun.COM static void* 1747605SMark.Johnson@Sun.COM ucode_zalloc(processorid_t id, size_t size) 1757605SMark.Johnson@Sun.COM { 1767605SMark.Johnson@Sun.COM if (id) 1777605SMark.Johnson@Sun.COM return (kmem_zalloc(size, KM_NOSLEEP)); 1787605SMark.Johnson@Sun.COM 1797605SMark.Johnson@Sun.COM /* BOP_ALLOC() failure results in panic */ 1807605SMark.Johnson@Sun.COM return (BOP_ALLOC(bootops, NULL, size, MMU_PAGESIZE)); 1817605SMark.Johnson@Sun.COM } 1827605SMark.Johnson@Sun.COM 1837605SMark.Johnson@Sun.COM static void 1847605SMark.Johnson@Sun.COM ucode_free(processorid_t id, void* buf, size_t size) 1857605SMark.Johnson@Sun.COM { 1867605SMark.Johnson@Sun.COM if (id) 1877605SMark.Johnson@Sun.COM kmem_free(buf, size); 1884581Ssherrym } 1894581Ssherrym 1904581Ssherrym /* 1914581Ssherrym * Check whether or not a processor is capable of microcode operations 1924581Ssherrym * Returns 1 if it is capable, 0 if not. 1937605SMark.Johnson@Sun.COM * 1947605SMark.Johnson@Sun.COM * At this point we only support microcode update for: 1957605SMark.Johnson@Sun.COM * - Intel processors family 6 and above, and 1967605SMark.Johnson@Sun.COM * - AMD processors family 0x10 and above. 1977605SMark.Johnson@Sun.COM * 1987605SMark.Johnson@Sun.COM * We also assume that we don't support a mix of Intel and 1997605SMark.Johnson@Sun.COM * AMD processors in the same box. 2007605SMark.Johnson@Sun.COM * 2017605SMark.Johnson@Sun.COM * An i86xpv guest domain can't update the microcode. 2024581Ssherrym */ 2037605SMark.Johnson@Sun.COM /*ARGSUSED*/ 2044581Ssherrym static int 2057605SMark.Johnson@Sun.COM ucode_capable_amd(cpu_t *cp) 2064581Ssherrym { 2079000SStuart.Maybee@Sun.COM int hwenv = get_hwenv(); 2089000SStuart.Maybee@Sun.COM 2099000SStuart.Maybee@Sun.COM if (hwenv == HW_XEN_HVM || (hwenv == HW_XEN_PV && !is_controldom())) { 2107605SMark.Johnson@Sun.COM return (0); 2117605SMark.Johnson@Sun.COM } 2128647SMark.Johnson@Sun.COM return (cpuid_getfamily(cp) >= 0x10); 2138647SMark.Johnson@Sun.COM } 2147605SMark.Johnson@Sun.COM 2157605SMark.Johnson@Sun.COM static int 2167605SMark.Johnson@Sun.COM ucode_capable_intel(cpu_t *cp) 2177605SMark.Johnson@Sun.COM { 2189000SStuart.Maybee@Sun.COM int hwenv = get_hwenv(); 2199000SStuart.Maybee@Sun.COM 2209000SStuart.Maybee@Sun.COM if (hwenv == HW_XEN_HVM || (hwenv == HW_XEN_PV && !is_controldom())) { 2217347SMark.Johnson@Sun.COM return (0); 2227347SMark.Johnson@Sun.COM } 2237605SMark.Johnson@Sun.COM return (cpuid_getfamily(cp) >= 6); 2244581Ssherrym } 2254581Ssherrym 2264581Ssherrym /* 2274581Ssherrym * Called when it is no longer necessary to keep the microcode around, 2284581Ssherrym * or when the cached microcode doesn't match the CPU being processed. 2294581Ssherrym */ 2304581Ssherrym static void 2317605SMark.Johnson@Sun.COM ucode_file_reset_amd(ucode_file_t *ufp, processorid_t id) 2324581Ssherrym { 2337605SMark.Johnson@Sun.COM ucode_file_amd_t *ucodefp = ufp->amd; 2344581Ssherrym 2354581Ssherrym if (ucodefp == NULL) 2364581Ssherrym return; 2374581Ssherrym 2387605SMark.Johnson@Sun.COM ucode_free(id, ucodefp, sizeof (ucode_file_amd_t)); 2397605SMark.Johnson@Sun.COM ufp->amd = NULL; 2407605SMark.Johnson@Sun.COM } 2417605SMark.Johnson@Sun.COM 2427605SMark.Johnson@Sun.COM static void 2437605SMark.Johnson@Sun.COM ucode_file_reset_intel(ucode_file_t *ufp, processorid_t id) 2447605SMark.Johnson@Sun.COM { 2457605SMark.Johnson@Sun.COM ucode_file_intel_t *ucodefp = &ufp->intel; 2467605SMark.Johnson@Sun.COM int total_size, body_size; 2477605SMark.Johnson@Sun.COM 2487605SMark.Johnson@Sun.COM if (ucodefp == NULL || ucodefp->uf_header == NULL) 2497605SMark.Johnson@Sun.COM return; 2507605SMark.Johnson@Sun.COM 2517605SMark.Johnson@Sun.COM total_size = UCODE_TOTAL_SIZE_INTEL(ucodefp->uf_header->uh_total_size); 2527605SMark.Johnson@Sun.COM body_size = UCODE_BODY_SIZE_INTEL(ucodefp->uf_header->uh_body_size); 2534581Ssherrym if (ucodefp->uf_body) { 2547605SMark.Johnson@Sun.COM ucode_free(id, ucodefp->uf_body, body_size); 2554581Ssherrym ucodefp->uf_body = NULL; 2564581Ssherrym } 2574581Ssherrym 2584581Ssherrym if (ucodefp->uf_ext_table) { 2597605SMark.Johnson@Sun.COM int size = total_size - body_size - UCODE_HEADER_SIZE_INTEL; 2607605SMark.Johnson@Sun.COM 2617605SMark.Johnson@Sun.COM ucode_free(id, ucodefp->uf_ext_table, size); 2624581Ssherrym ucodefp->uf_ext_table = NULL; 2634581Ssherrym } 2644581Ssherrym 2657605SMark.Johnson@Sun.COM ucode_free(id, ucodefp->uf_header, UCODE_HEADER_SIZE_INTEL); 2667605SMark.Johnson@Sun.COM ucodefp->uf_header = NULL; 2677605SMark.Johnson@Sun.COM } 2687605SMark.Johnson@Sun.COM 2697605SMark.Johnson@Sun.COM /* 2707605SMark.Johnson@Sun.COM * Find the equivalent CPU id in the equivalence table. 2717605SMark.Johnson@Sun.COM */ 2727605SMark.Johnson@Sun.COM static int 2738647SMark.Johnson@Sun.COM ucode_equiv_cpu_amd(cpu_t *cp, uint16_t *eq_sig) 2747605SMark.Johnson@Sun.COM { 2757605SMark.Johnson@Sun.COM char name[MAXPATHLEN]; 2767605SMark.Johnson@Sun.COM intptr_t fd; 2777605SMark.Johnson@Sun.COM int count; 2787605SMark.Johnson@Sun.COM int offset = 0, cpi_sig = cpuid_getsig(cp); 2797605SMark.Johnson@Sun.COM ucode_eqtbl_amd_t *eqtbl = ucode_eqtbl_amd; 2807605SMark.Johnson@Sun.COM 2817605SMark.Johnson@Sun.COM (void) snprintf(name, MAXPATHLEN, "/%s/%s/equivalence-table", 2827605SMark.Johnson@Sun.COM UCODE_INSTALL_PATH, cpuid_getvendorstr(cp)); 2837605SMark.Johnson@Sun.COM 2847605SMark.Johnson@Sun.COM /* 2857605SMark.Johnson@Sun.COM * No kmem_zalloc() etc. available on boot cpu. 2867605SMark.Johnson@Sun.COM */ 2877605SMark.Johnson@Sun.COM if (cp->cpu_id == 0) { 2887605SMark.Johnson@Sun.COM if ((fd = kobj_open(name)) == -1) 2897605SMark.Johnson@Sun.COM return (EM_OPENFILE); 2907605SMark.Johnson@Sun.COM /* ucode_zalloc() cannot fail on boot cpu */ 2917605SMark.Johnson@Sun.COM eqtbl = ucode_zalloc(cp->cpu_id, sizeof (*eqtbl)); 2927605SMark.Johnson@Sun.COM ASSERT(eqtbl); 2937605SMark.Johnson@Sun.COM do { 2947605SMark.Johnson@Sun.COM count = kobj_read(fd, (int8_t *)eqtbl, 2957605SMark.Johnson@Sun.COM sizeof (*eqtbl), offset); 2967605SMark.Johnson@Sun.COM if (count != sizeof (*eqtbl)) { 2977605SMark.Johnson@Sun.COM (void) kobj_close(fd); 2987605SMark.Johnson@Sun.COM return (EM_HIGHERREV); 2997605SMark.Johnson@Sun.COM } 3007605SMark.Johnson@Sun.COM offset += count; 3017605SMark.Johnson@Sun.COM } while (eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != cpi_sig); 3027605SMark.Johnson@Sun.COM (void) kobj_close(fd); 3037605SMark.Johnson@Sun.COM } 3047605SMark.Johnson@Sun.COM 3057605SMark.Johnson@Sun.COM /* 3067605SMark.Johnson@Sun.COM * If not already done, load the equivalence table. 3077605SMark.Johnson@Sun.COM * Not done on boot CPU. 3087605SMark.Johnson@Sun.COM */ 3097605SMark.Johnson@Sun.COM if (eqtbl == NULL) { 3107605SMark.Johnson@Sun.COM struct _buf *eq; 3117605SMark.Johnson@Sun.COM uint64_t size; 3127605SMark.Johnson@Sun.COM 3137605SMark.Johnson@Sun.COM if ((eq = kobj_open_file(name)) == (struct _buf *)-1) 3147605SMark.Johnson@Sun.COM return (EM_OPENFILE); 3157605SMark.Johnson@Sun.COM 3167605SMark.Johnson@Sun.COM if (kobj_get_filesize(eq, &size) < 0) { 3177605SMark.Johnson@Sun.COM kobj_close_file(eq); 3187605SMark.Johnson@Sun.COM return (EM_OPENFILE); 3197605SMark.Johnson@Sun.COM } 3207605SMark.Johnson@Sun.COM 3217605SMark.Johnson@Sun.COM ucode_eqtbl_amd = kmem_zalloc(size, KM_NOSLEEP); 3227605SMark.Johnson@Sun.COM if (ucode_eqtbl_amd == NULL) { 3237605SMark.Johnson@Sun.COM kobj_close_file(eq); 3247605SMark.Johnson@Sun.COM return (EM_NOMEM); 3257605SMark.Johnson@Sun.COM } 3267605SMark.Johnson@Sun.COM 3277605SMark.Johnson@Sun.COM count = kobj_read_file(eq, (char *)ucode_eqtbl_amd, size, 0); 3287605SMark.Johnson@Sun.COM kobj_close_file(eq); 3297605SMark.Johnson@Sun.COM 3307605SMark.Johnson@Sun.COM if (count != size) 3317605SMark.Johnson@Sun.COM return (EM_FILESIZE); 3327605SMark.Johnson@Sun.COM } 3337605SMark.Johnson@Sun.COM 3347605SMark.Johnson@Sun.COM /* Get the equivalent CPU id. */ 3357605SMark.Johnson@Sun.COM if (cp->cpu_id) 3367605SMark.Johnson@Sun.COM for (eqtbl = ucode_eqtbl_amd; 3377605SMark.Johnson@Sun.COM eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != cpi_sig; 3387605SMark.Johnson@Sun.COM eqtbl++) 3397605SMark.Johnson@Sun.COM ; 3407605SMark.Johnson@Sun.COM 3417605SMark.Johnson@Sun.COM *eq_sig = eqtbl->ue_equiv_cpu; 3427605SMark.Johnson@Sun.COM 3437605SMark.Johnson@Sun.COM /* No equivalent CPU id found, assume outdated microcode file. */ 3447605SMark.Johnson@Sun.COM if (*eq_sig == 0) 3457605SMark.Johnson@Sun.COM return (EM_HIGHERREV); 3467605SMark.Johnson@Sun.COM 3477605SMark.Johnson@Sun.COM return (EM_OK); 3484581Ssherrym } 3494581Ssherrym 3504581Ssherrym /* 3518647SMark.Johnson@Sun.COM * xVM cannot check for the presence of PCI devices. Look for chipset- 3528647SMark.Johnson@Sun.COM * specific microcode patches in the container file and disable them 3538647SMark.Johnson@Sun.COM * by setting their CPU revision to an invalid value. 3548647SMark.Johnson@Sun.COM */ 3558647SMark.Johnson@Sun.COM #ifdef __xpv 3568647SMark.Johnson@Sun.COM static void 3578647SMark.Johnson@Sun.COM ucode_chipset_amd(uint8_t *buf, int size) 3588647SMark.Johnson@Sun.COM { 3598647SMark.Johnson@Sun.COM ucode_header_amd_t *uh; 3608647SMark.Johnson@Sun.COM uint32_t *ptr = (uint32_t *)buf; 3618647SMark.Johnson@Sun.COM int len = 0; 3628647SMark.Johnson@Sun.COM 3638647SMark.Johnson@Sun.COM /* skip to first microcode patch */ 3648647SMark.Johnson@Sun.COM ptr += 2; len = *ptr++; ptr += len >> 2; size -= len; 3658647SMark.Johnson@Sun.COM 3668647SMark.Johnson@Sun.COM while (size >= sizeof (ucode_header_amd_t) + 8) { 3678647SMark.Johnson@Sun.COM ptr++; len = *ptr++; 3688647SMark.Johnson@Sun.COM uh = (ucode_header_amd_t *)ptr; 3698647SMark.Johnson@Sun.COM ptr += len >> 2; size -= len; 3708647SMark.Johnson@Sun.COM 3718647SMark.Johnson@Sun.COM if (uh->uh_nb_id) { 3728647SMark.Johnson@Sun.COM cmn_err(CE_WARN, "ignoring northbridge-specific ucode: " 3738647SMark.Johnson@Sun.COM "chipset id %x, revision %x", 3748647SMark.Johnson@Sun.COM uh->uh_nb_id, uh->uh_nb_rev); 3758647SMark.Johnson@Sun.COM uh->uh_cpu_rev = 0xffff; 3768647SMark.Johnson@Sun.COM } 3778647SMark.Johnson@Sun.COM 3788647SMark.Johnson@Sun.COM if (uh->uh_sb_id) { 3798647SMark.Johnson@Sun.COM cmn_err(CE_WARN, "ignoring southbridge-specific ucode: " 3808647SMark.Johnson@Sun.COM "chipset id %x, revision %x", 3818647SMark.Johnson@Sun.COM uh->uh_sb_id, uh->uh_sb_rev); 3828647SMark.Johnson@Sun.COM uh->uh_cpu_rev = 0xffff; 3838647SMark.Johnson@Sun.COM } 3848647SMark.Johnson@Sun.COM } 3858647SMark.Johnson@Sun.COM } 3868647SMark.Johnson@Sun.COM #endif 3878647SMark.Johnson@Sun.COM 3888647SMark.Johnson@Sun.COM /* 3894581Ssherrym * Populate the ucode file structure from microcode file corresponding to 3904581Ssherrym * this CPU, if exists. 3914581Ssherrym * 3924581Ssherrym * Return EM_OK on success, corresponding error code on failure. 3934581Ssherrym */ 3948647SMark.Johnson@Sun.COM /*ARGSUSED*/ 3954581Ssherrym static ucode_errno_t 3967605SMark.Johnson@Sun.COM ucode_locate_amd(cpu_t *cp, cpu_ucode_info_t *uinfop, ucode_file_t *ufp) 3977605SMark.Johnson@Sun.COM { 3987605SMark.Johnson@Sun.COM char name[MAXPATHLEN]; 3997605SMark.Johnson@Sun.COM intptr_t fd; 4008647SMark.Johnson@Sun.COM int count, rc; 4017605SMark.Johnson@Sun.COM ucode_file_amd_t *ucodefp = ufp->amd; 4027605SMark.Johnson@Sun.COM 4038647SMark.Johnson@Sun.COM #ifndef __xpv 4048647SMark.Johnson@Sun.COM uint16_t eq_sig = 0; 4058647SMark.Johnson@Sun.COM int i; 4068647SMark.Johnson@Sun.COM 4077605SMark.Johnson@Sun.COM /* get equivalent CPU id */ 4087605SMark.Johnson@Sun.COM if ((rc = ucode_equiv_cpu_amd(cp, &eq_sig)) != EM_OK) 4097605SMark.Johnson@Sun.COM return (rc); 4107605SMark.Johnson@Sun.COM 4117605SMark.Johnson@Sun.COM /* 4127605SMark.Johnson@Sun.COM * Allocate a buffer for the microcode patch. If the buffer has been 4137605SMark.Johnson@Sun.COM * allocated before, check for a matching microcode to avoid loading 4147605SMark.Johnson@Sun.COM * the file again. 4157605SMark.Johnson@Sun.COM */ 4167605SMark.Johnson@Sun.COM if (ucodefp == NULL) 4177605SMark.Johnson@Sun.COM ucodefp = ucode_zalloc(cp->cpu_id, sizeof (*ucodefp)); 4187605SMark.Johnson@Sun.COM else if (ucode_match_amd(eq_sig, uinfop, ucodefp, sizeof (*ucodefp)) 4197605SMark.Johnson@Sun.COM == EM_OK) 4207605SMark.Johnson@Sun.COM return (EM_OK); 4217605SMark.Johnson@Sun.COM 4227605SMark.Johnson@Sun.COM if (ucodefp == NULL) 4237605SMark.Johnson@Sun.COM return (EM_NOMEM); 4247605SMark.Johnson@Sun.COM 4257605SMark.Johnson@Sun.COM ufp->amd = ucodefp; 4267605SMark.Johnson@Sun.COM 4277605SMark.Johnson@Sun.COM /* 4287605SMark.Johnson@Sun.COM * Find the patch for this CPU. The patch files are named XXXX-YY, where 4297605SMark.Johnson@Sun.COM * XXXX is the equivalent CPU id and YY is the running patch number. 4307605SMark.Johnson@Sun.COM * Patches specific to certain chipsets are guaranteed to have lower 4317605SMark.Johnson@Sun.COM * numbers than less specific patches, so we can just load the first 4327605SMark.Johnson@Sun.COM * patch that matches. 4337605SMark.Johnson@Sun.COM */ 4347605SMark.Johnson@Sun.COM 4357605SMark.Johnson@Sun.COM for (i = 0; i < 0xff; i++) { 4367605SMark.Johnson@Sun.COM (void) snprintf(name, MAXPATHLEN, "/%s/%s/%04X-%02X", 4377605SMark.Johnson@Sun.COM UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), eq_sig, i); 4387605SMark.Johnson@Sun.COM if ((fd = kobj_open(name)) == -1) 4397605SMark.Johnson@Sun.COM return (EM_NOMATCH); 4407605SMark.Johnson@Sun.COM count = kobj_read(fd, (char *)ucodefp, sizeof (*ucodefp), 0); 4417605SMark.Johnson@Sun.COM (void) kobj_close(fd); 4427605SMark.Johnson@Sun.COM 4437605SMark.Johnson@Sun.COM if (ucode_match_amd(eq_sig, uinfop, ucodefp, count) == EM_OK) 4447605SMark.Johnson@Sun.COM return (EM_OK); 4457605SMark.Johnson@Sun.COM } 4467605SMark.Johnson@Sun.COM return (EM_NOMATCH); 4478647SMark.Johnson@Sun.COM #else 4488647SMark.Johnson@Sun.COM int size = 0; 4498647SMark.Johnson@Sun.COM char c; 4508647SMark.Johnson@Sun.COM 4518647SMark.Johnson@Sun.COM /* 4528647SMark.Johnson@Sun.COM * The xVM case is special. To support mixed-revision systems, the 4538647SMark.Johnson@Sun.COM * hypervisor will choose which patch to load for which CPU, so the 4548647SMark.Johnson@Sun.COM * whole microcode patch container file will have to be loaded. 4558647SMark.Johnson@Sun.COM * 4568647SMark.Johnson@Sun.COM * Since this code is only run on the boot cpu, we don't have to care 4578647SMark.Johnson@Sun.COM * about failing ucode_zalloc() or freeing allocated memory. 4588647SMark.Johnson@Sun.COM */ 4598647SMark.Johnson@Sun.COM if (cp->cpu_id != 0) 4608647SMark.Johnson@Sun.COM return (EM_INVALIDARG); 4618647SMark.Johnson@Sun.COM 4628647SMark.Johnson@Sun.COM (void) snprintf(name, MAXPATHLEN, "/%s/%s/container", 4638647SMark.Johnson@Sun.COM UCODE_INSTALL_PATH, cpuid_getvendorstr(cp)); 4648647SMark.Johnson@Sun.COM 4658647SMark.Johnson@Sun.COM if ((fd = kobj_open(name)) == -1) 4668647SMark.Johnson@Sun.COM return (EM_OPENFILE); 4678647SMark.Johnson@Sun.COM 4688647SMark.Johnson@Sun.COM /* get the file size by counting bytes */ 4698647SMark.Johnson@Sun.COM do { 4708647SMark.Johnson@Sun.COM count = kobj_read(fd, &c, 1, size); 4718647SMark.Johnson@Sun.COM size += count; 4728647SMark.Johnson@Sun.COM } while (count); 4738647SMark.Johnson@Sun.COM 4748647SMark.Johnson@Sun.COM ucodefp = ucode_zalloc(cp->cpu_id, sizeof (*ucodefp)); 4758647SMark.Johnson@Sun.COM ASSERT(ucodefp); 4768647SMark.Johnson@Sun.COM ufp->amd = ucodefp; 4778647SMark.Johnson@Sun.COM 4788647SMark.Johnson@Sun.COM ucodefp->usize = size; 4798647SMark.Johnson@Sun.COM ucodefp->ucodep = ucode_zalloc(cp->cpu_id, size); 4808647SMark.Johnson@Sun.COM ASSERT(ucodefp->ucodep); 4818647SMark.Johnson@Sun.COM 4828647SMark.Johnson@Sun.COM /* load the microcode patch container file */ 4838647SMark.Johnson@Sun.COM count = kobj_read(fd, (char *)ucodefp->ucodep, size, 0); 4848647SMark.Johnson@Sun.COM (void) kobj_close(fd); 4858647SMark.Johnson@Sun.COM 4868647SMark.Johnson@Sun.COM if (count != size) 4878647SMark.Johnson@Sun.COM return (EM_FILESIZE); 4888647SMark.Johnson@Sun.COM 4898647SMark.Johnson@Sun.COM /* make sure the container file is valid */ 4908647SMark.Johnson@Sun.COM rc = ucode->validate(ucodefp->ucodep, ucodefp->usize); 4918647SMark.Johnson@Sun.COM 4928647SMark.Johnson@Sun.COM if (rc != EM_OK) 4938647SMark.Johnson@Sun.COM return (rc); 4948647SMark.Johnson@Sun.COM 4958647SMark.Johnson@Sun.COM /* disable chipset-specific patches */ 4968647SMark.Johnson@Sun.COM ucode_chipset_amd(ucodefp->ucodep, ucodefp->usize); 4978647SMark.Johnson@Sun.COM 4988647SMark.Johnson@Sun.COM return (EM_OK); 4998647SMark.Johnson@Sun.COM #endif 5007605SMark.Johnson@Sun.COM } 5017605SMark.Johnson@Sun.COM 5027605SMark.Johnson@Sun.COM static ucode_errno_t 5037605SMark.Johnson@Sun.COM ucode_locate_intel(cpu_t *cp, cpu_ucode_info_t *uinfop, ucode_file_t *ufp) 5044581Ssherrym { 5054581Ssherrym char name[MAXPATHLEN]; 5064581Ssherrym intptr_t fd; 5074581Ssherrym int count; 5087605SMark.Johnson@Sun.COM int header_size = UCODE_HEADER_SIZE_INTEL; 5094581Ssherrym int cpi_sig = cpuid_getsig(cp); 5104581Ssherrym ucode_errno_t rc = EM_OK; 5117605SMark.Johnson@Sun.COM ucode_file_intel_t *ucodefp = &ufp->intel; 5127605SMark.Johnson@Sun.COM 5137605SMark.Johnson@Sun.COM ASSERT(ucode); 5144581Ssherrym 5154581Ssherrym /* 5164581Ssherrym * If the microcode matches the CPU we are processing, use it. 5174581Ssherrym */ 5187605SMark.Johnson@Sun.COM if (ucode_match_intel(cpi_sig, uinfop, ucodefp->uf_header, 5194581Ssherrym ucodefp->uf_ext_table) == EM_OK && ucodefp->uf_body != NULL) { 5204581Ssherrym return (EM_OK); 5214581Ssherrym } 5224581Ssherrym 5234581Ssherrym /* 5244581Ssherrym * Look for microcode file with the right name. 5254581Ssherrym */ 5264581Ssherrym (void) snprintf(name, MAXPATHLEN, "/%s/%s/%08X-%02X", 5274581Ssherrym UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), cpi_sig, 5284581Ssherrym uinfop->cui_platid); 5294581Ssherrym if ((fd = kobj_open(name)) == -1) { 5304581Ssherrym return (EM_OPENFILE); 5314581Ssherrym } 5324581Ssherrym 5334581Ssherrym /* 5344581Ssherrym * We found a microcode file for the CPU we are processing, 5354581Ssherrym * reset the microcode data structure and read in the new 5364581Ssherrym * file. 5374581Ssherrym */ 5387605SMark.Johnson@Sun.COM ucode->file_reset(ufp, cp->cpu_id); 5394581Ssherrym 5407605SMark.Johnson@Sun.COM ucodefp->uf_header = ucode_zalloc(cp->cpu_id, header_size); 5417605SMark.Johnson@Sun.COM if (ucodefp->uf_header == NULL) 5427605SMark.Johnson@Sun.COM return (EM_NOMEM); 5437605SMark.Johnson@Sun.COM 5447605SMark.Johnson@Sun.COM count = kobj_read(fd, (char *)ucodefp->uf_header, header_size, 0); 5454581Ssherrym 5464581Ssherrym switch (count) { 5477605SMark.Johnson@Sun.COM case UCODE_HEADER_SIZE_INTEL: { 5484581Ssherrym 5497605SMark.Johnson@Sun.COM ucode_header_intel_t *uhp = ucodefp->uf_header; 5504581Ssherrym uint32_t offset = header_size; 5514581Ssherrym int total_size, body_size, ext_size; 5524581Ssherrym uint32_t sum = 0; 5534581Ssherrym 5544581Ssherrym /* 5554581Ssherrym * Make sure that the header contains valid fields. 5564581Ssherrym */ 5577605SMark.Johnson@Sun.COM if ((rc = ucode_header_validate_intel(uhp)) == EM_OK) { 5587605SMark.Johnson@Sun.COM total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size); 5597605SMark.Johnson@Sun.COM body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size); 5607605SMark.Johnson@Sun.COM ucodefp->uf_body = ucode_zalloc(cp->cpu_id, body_size); 5617605SMark.Johnson@Sun.COM if (ucodefp->uf_body == NULL) { 5627605SMark.Johnson@Sun.COM rc = EM_NOMEM; 5637605SMark.Johnson@Sun.COM break; 5644581Ssherrym } 5654581Ssherrym 5664581Ssherrym if (kobj_read(fd, (char *)ucodefp->uf_body, 5674581Ssherrym body_size, offset) != body_size) 5684581Ssherrym rc = EM_FILESIZE; 5694581Ssherrym } 5704581Ssherrym 5714581Ssherrym if (rc) 5724581Ssherrym break; 5734581Ssherrym 5747605SMark.Johnson@Sun.COM sum = ucode_checksum_intel(0, header_size, 5757605SMark.Johnson@Sun.COM (uint8_t *)ucodefp->uf_header); 5767605SMark.Johnson@Sun.COM if (ucode_checksum_intel(sum, body_size, ucodefp->uf_body)) { 5774581Ssherrym rc = EM_CHECKSUM; 5784581Ssherrym break; 5794581Ssherrym } 5804581Ssherrym 5814581Ssherrym /* 5824581Ssherrym * Check to see if there is extended signature table. 5834581Ssherrym */ 5844581Ssherrym offset = body_size + header_size; 5854581Ssherrym ext_size = total_size - offset; 5864581Ssherrym 5874581Ssherrym if (ext_size <= 0) 5884581Ssherrym break; 5894581Ssherrym 5907605SMark.Johnson@Sun.COM ucodefp->uf_ext_table = ucode_zalloc(cp->cpu_id, ext_size); 5917605SMark.Johnson@Sun.COM if (ucodefp->uf_ext_table == NULL) { 5927605SMark.Johnson@Sun.COM rc = EM_NOMEM; 5937605SMark.Johnson@Sun.COM break; 5944581Ssherrym } 5954581Ssherrym 5964581Ssherrym if (kobj_read(fd, (char *)ucodefp->uf_ext_table, 5974581Ssherrym ext_size, offset) != ext_size) { 5984581Ssherrym rc = EM_FILESIZE; 5997605SMark.Johnson@Sun.COM } else if (ucode_checksum_intel(0, ext_size, 6004581Ssherrym (uint8_t *)(ucodefp->uf_ext_table))) { 6014581Ssherrym rc = EM_CHECKSUM; 6024581Ssherrym } else { 6034581Ssherrym int i; 6044581Ssherrym 6057605SMark.Johnson@Sun.COM ext_size -= UCODE_EXT_TABLE_SIZE_INTEL; 6064581Ssherrym for (i = 0; i < ucodefp->uf_ext_table->uet_count; 6074581Ssherrym i++) { 6087605SMark.Johnson@Sun.COM if (ucode_checksum_intel(0, 6097605SMark.Johnson@Sun.COM UCODE_EXT_SIG_SIZE_INTEL, 6104581Ssherrym (uint8_t *)(&(ucodefp->uf_ext_table-> 6114581Ssherrym uet_ext_sig[i])))) { 6124581Ssherrym rc = EM_CHECKSUM; 6134581Ssherrym break; 6144581Ssherrym } 6154581Ssherrym } 6164581Ssherrym } 6174581Ssherrym break; 6184581Ssherrym } 6194581Ssherrym 6204581Ssherrym default: 6214581Ssherrym rc = EM_FILESIZE; 6224581Ssherrym break; 6234581Ssherrym } 6244581Ssherrym 6254581Ssherrym kobj_close(fd); 6264581Ssherrym 6274581Ssherrym if (rc != EM_OK) 6284581Ssherrym return (rc); 6294581Ssherrym 6307605SMark.Johnson@Sun.COM rc = ucode_match_intel(cpi_sig, uinfop, ucodefp->uf_header, 6314581Ssherrym ucodefp->uf_ext_table); 6324581Ssherrym 6334581Ssherrym return (rc); 6344581Ssherrym } 6354581Ssherrym 6368647SMark.Johnson@Sun.COM #ifndef __xpv 6377605SMark.Johnson@Sun.COM static ucode_errno_t 6388647SMark.Johnson@Sun.COM ucode_match_amd(uint16_t eq_sig, cpu_ucode_info_t *uinfop, 6398647SMark.Johnson@Sun.COM ucode_file_amd_t *ucodefp, int size) 6407605SMark.Johnson@Sun.COM { 6417605SMark.Johnson@Sun.COM ucode_header_amd_t *uh; 6427605SMark.Johnson@Sun.COM 6437605SMark.Johnson@Sun.COM if (ucodefp == NULL || size < sizeof (ucode_header_amd_t)) 6447605SMark.Johnson@Sun.COM return (EM_NOMATCH); 6457605SMark.Johnson@Sun.COM 6467605SMark.Johnson@Sun.COM /* 6477605SMark.Johnson@Sun.COM * Don't even think about loading patches that would require code 6487605SMark.Johnson@Sun.COM * execution. 6497605SMark.Johnson@Sun.COM */ 6507605SMark.Johnson@Sun.COM if (size > offsetof(ucode_file_amd_t, uf_code_present) && 6517605SMark.Johnson@Sun.COM ucodefp->uf_code_present) 6527605SMark.Johnson@Sun.COM return (EM_NOMATCH); 6537605SMark.Johnson@Sun.COM 6547605SMark.Johnson@Sun.COM uh = &ucodefp->uf_header; 6557605SMark.Johnson@Sun.COM 6567605SMark.Johnson@Sun.COM if (eq_sig != uh->uh_cpu_rev) 6577605SMark.Johnson@Sun.COM return (EM_NOMATCH); 6587605SMark.Johnson@Sun.COM 6597605SMark.Johnson@Sun.COM if (uh->uh_nb_id) { 6607605SMark.Johnson@Sun.COM cmn_err(CE_WARN, "ignoring northbridge-specific ucode: " 6617605SMark.Johnson@Sun.COM "chipset id %x, revision %x", uh->uh_nb_id, uh->uh_nb_rev); 6627605SMark.Johnson@Sun.COM return (EM_NOMATCH); 6637605SMark.Johnson@Sun.COM } 6647605SMark.Johnson@Sun.COM 6657605SMark.Johnson@Sun.COM if (uh->uh_sb_id) { 6667605SMark.Johnson@Sun.COM cmn_err(CE_WARN, "ignoring southbridge-specific ucode: " 6677605SMark.Johnson@Sun.COM "chipset id %x, revision %x", uh->uh_sb_id, uh->uh_sb_rev); 6687605SMark.Johnson@Sun.COM return (EM_NOMATCH); 6697605SMark.Johnson@Sun.COM } 6707605SMark.Johnson@Sun.COM 6717605SMark.Johnson@Sun.COM if (uh->uh_patch_id <= uinfop->cui_rev) 6727605SMark.Johnson@Sun.COM return (EM_HIGHERREV); 6737605SMark.Johnson@Sun.COM 6747605SMark.Johnson@Sun.COM return (EM_OK); 6757605SMark.Johnson@Sun.COM } 6768647SMark.Johnson@Sun.COM #endif 6774581Ssherrym 6784581Ssherrym /* 6794581Ssherrym * Returns 1 if the microcode is for this processor; 0 otherwise. 6804581Ssherrym */ 6814581Ssherrym static ucode_errno_t 6827605SMark.Johnson@Sun.COM ucode_match_intel(int cpi_sig, cpu_ucode_info_t *uinfop, 6837605SMark.Johnson@Sun.COM ucode_header_intel_t *uhp, ucode_ext_table_intel_t *uetp) 6844581Ssherrym { 6857605SMark.Johnson@Sun.COM if (uhp == NULL) 6867605SMark.Johnson@Sun.COM return (EM_NOMATCH); 6874581Ssherrym 6887605SMark.Johnson@Sun.COM if (UCODE_MATCH_INTEL(cpi_sig, uhp->uh_signature, 6894581Ssherrym uinfop->cui_platid, uhp->uh_proc_flags)) { 6904581Ssherrym 6914581Ssherrym if (uinfop->cui_rev >= uhp->uh_rev && !ucode_force_update) 6924581Ssherrym return (EM_HIGHERREV); 6934581Ssherrym 6944581Ssherrym return (EM_OK); 6954581Ssherrym } 6964581Ssherrym 6974581Ssherrym if (uetp != NULL) { 6984581Ssherrym int i; 6994581Ssherrym 7004581Ssherrym for (i = 0; i < uetp->uet_count; i++) { 7017605SMark.Johnson@Sun.COM ucode_ext_sig_intel_t *uesp; 7024581Ssherrym 7034581Ssherrym uesp = &uetp->uet_ext_sig[i]; 7044581Ssherrym 7057605SMark.Johnson@Sun.COM if (UCODE_MATCH_INTEL(cpi_sig, uesp->ues_signature, 7064581Ssherrym uinfop->cui_platid, uesp->ues_proc_flags)) { 7074581Ssherrym 7084581Ssherrym if (uinfop->cui_rev >= uhp->uh_rev && 7094581Ssherrym !ucode_force_update) 7104581Ssherrym return (EM_HIGHERREV); 7114581Ssherrym 7124581Ssherrym return (EM_OK); 7134581Ssherrym } 7144581Ssherrym } 7154581Ssherrym } 7164581Ssherrym 7174581Ssherrym return (EM_NOMATCH); 7184581Ssherrym } 7194581Ssherrym 7204581Ssherrym /*ARGSUSED*/ 7214581Ssherrym static int 7224581Ssherrym ucode_write(xc_arg_t arg1, xc_arg_t unused2, xc_arg_t unused3) 7234581Ssherrym { 7247605SMark.Johnson@Sun.COM ucode_update_t *uusp = (ucode_update_t *)arg1; 7257605SMark.Johnson@Sun.COM cpu_ucode_info_t *uinfop = CPU->cpu_m.mcpu_ucode_info; 7264581Ssherrym 7277605SMark.Johnson@Sun.COM ASSERT(ucode); 7284581Ssherrym ASSERT(uusp->ucodep); 7294581Ssherrym 7307347SMark.Johnson@Sun.COM #ifndef __xpv 7314581Ssherrym /* 7324581Ssherrym * Check one more time to see if it is really necessary to update 7334581Ssherrym * microcode just in case this is a hyperthreaded processor where 7344581Ssherrym * the threads share the same microcode. 7354581Ssherrym */ 7364581Ssherrym if (!ucode_force_update) { 7377605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 7384581Ssherrym uusp->new_rev = uinfop->cui_rev; 7394581Ssherrym if (uinfop->cui_rev >= uusp->expected_rev) 7404581Ssherrym return (0); 7414581Ssherrym } 7424581Ssherrym 7437605SMark.Johnson@Sun.COM wrmsr(ucode->write_msr, (uintptr_t)uusp->ucodep); 7447347SMark.Johnson@Sun.COM #endif 7457605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 7464581Ssherrym uusp->new_rev = uinfop->cui_rev; 7474581Ssherrym 7484581Ssherrym return (0); 7494581Ssherrym } 7504581Ssherrym 7517605SMark.Johnson@Sun.COM /*ARGSUSED*/ 7527605SMark.Johnson@Sun.COM static uint32_t 7537605SMark.Johnson@Sun.COM ucode_load_amd(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp) 7547605SMark.Johnson@Sun.COM { 7557605SMark.Johnson@Sun.COM ucode_file_amd_t *ucodefp = ufp->amd; 7567605SMark.Johnson@Sun.COM #ifdef __xpv 7577605SMark.Johnson@Sun.COM ucode_update_t uus; 7587605SMark.Johnson@Sun.COM #endif 7594581Ssherrym 7607605SMark.Johnson@Sun.COM ASSERT(ucode); 7617605SMark.Johnson@Sun.COM ASSERT(ucodefp); 7627605SMark.Johnson@Sun.COM 7637605SMark.Johnson@Sun.COM #ifndef __xpv 7647605SMark.Johnson@Sun.COM kpreempt_disable(); 7657605SMark.Johnson@Sun.COM wrmsr(ucode->write_msr, (uintptr_t)ucodefp); 7667605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 7677605SMark.Johnson@Sun.COM kpreempt_enable(); 7688647SMark.Johnson@Sun.COM 7698647SMark.Johnson@Sun.COM return (ucodefp->uf_header.uh_patch_id); 7707605SMark.Johnson@Sun.COM #else 7718647SMark.Johnson@Sun.COM uus.ucodep = ucodefp->ucodep; 7728647SMark.Johnson@Sun.COM uus.usize = ucodefp->usize; 7737605SMark.Johnson@Sun.COM ucode_load_xpv(&uus); 7747605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 7757605SMark.Johnson@Sun.COM uus.new_rev = uinfop->cui_rev; 7768647SMark.Johnson@Sun.COM 7778647SMark.Johnson@Sun.COM return (uus.new_rev); 7787605SMark.Johnson@Sun.COM #endif 7797605SMark.Johnson@Sun.COM } 7807605SMark.Johnson@Sun.COM 7817605SMark.Johnson@Sun.COM /*ARGSUSED2*/ 7827605SMark.Johnson@Sun.COM static uint32_t 7837605SMark.Johnson@Sun.COM ucode_load_intel(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp) 7844581Ssherrym { 7857605SMark.Johnson@Sun.COM ucode_file_intel_t *ucodefp = &ufp->intel; 7867605SMark.Johnson@Sun.COM #ifdef __xpv 7877605SMark.Johnson@Sun.COM uint32_t ext_offset; 7887605SMark.Johnson@Sun.COM uint32_t body_size; 7897605SMark.Johnson@Sun.COM uint32_t ext_size; 7907605SMark.Johnson@Sun.COM uint8_t *ustart; 7917605SMark.Johnson@Sun.COM uint32_t usize; 7927605SMark.Johnson@Sun.COM ucode_update_t uus; 7937605SMark.Johnson@Sun.COM #endif 7947605SMark.Johnson@Sun.COM 7957605SMark.Johnson@Sun.COM ASSERT(ucode); 7967605SMark.Johnson@Sun.COM 7977605SMark.Johnson@Sun.COM #ifdef __xpv 7987605SMark.Johnson@Sun.COM /* 7997605SMark.Johnson@Sun.COM * the hypervisor wants the header, data, and extended 8007605SMark.Johnson@Sun.COM * signature tables. We can only get here from the boot 8017605SMark.Johnson@Sun.COM * CPU (cpu #0), we don't need to free as ucode_zalloc() will 8027605SMark.Johnson@Sun.COM * use BOP_ALLOC(). 8037605SMark.Johnson@Sun.COM */ 8047605SMark.Johnson@Sun.COM usize = UCODE_TOTAL_SIZE_INTEL(ucodefp->uf_header->uh_total_size); 8057605SMark.Johnson@Sun.COM ustart = ucode_zalloc(cp->cpu_id, usize); 8067605SMark.Johnson@Sun.COM ASSERT(ustart); 8077605SMark.Johnson@Sun.COM 8087605SMark.Johnson@Sun.COM body_size = UCODE_BODY_SIZE_INTEL(ucodefp->uf_header->uh_body_size); 8097605SMark.Johnson@Sun.COM ext_offset = body_size + UCODE_HEADER_SIZE_INTEL; 8107605SMark.Johnson@Sun.COM ext_size = usize - ext_offset; 8117605SMark.Johnson@Sun.COM ASSERT(ext_size >= 0); 8127605SMark.Johnson@Sun.COM 8137605SMark.Johnson@Sun.COM (void) memcpy(ustart, ucodefp->uf_header, UCODE_HEADER_SIZE_INTEL); 8147605SMark.Johnson@Sun.COM (void) memcpy(&ustart[UCODE_HEADER_SIZE_INTEL], ucodefp->uf_body, 8157605SMark.Johnson@Sun.COM body_size); 8167605SMark.Johnson@Sun.COM if (ext_size > 0) { 8177605SMark.Johnson@Sun.COM (void) memcpy(&ustart[ext_offset], 8187605SMark.Johnson@Sun.COM ucodefp->uf_ext_table, ext_size); 8197605SMark.Johnson@Sun.COM } 8207605SMark.Johnson@Sun.COM uus.ucodep = ustart; 8217605SMark.Johnson@Sun.COM uus.usize = usize; 8227605SMark.Johnson@Sun.COM ucode_load_xpv(&uus); 8237605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 8247605SMark.Johnson@Sun.COM uus.new_rev = uinfop->cui_rev; 8257605SMark.Johnson@Sun.COM #else 8264581Ssherrym kpreempt_disable(); 8277605SMark.Johnson@Sun.COM wrmsr(ucode->write_msr, (uintptr_t)ucodefp->uf_body); 8287605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 8294581Ssherrym kpreempt_enable(); 8307605SMark.Johnson@Sun.COM #endif 8317605SMark.Johnson@Sun.COM 8327605SMark.Johnson@Sun.COM return (ucodefp->uf_header->uh_rev); 8334581Ssherrym } 8344581Ssherrym 8357347SMark.Johnson@Sun.COM 8367347SMark.Johnson@Sun.COM #ifdef __xpv 8377347SMark.Johnson@Sun.COM static void 8387605SMark.Johnson@Sun.COM ucode_load_xpv(ucode_update_t *uusp) 8397347SMark.Johnson@Sun.COM { 8407347SMark.Johnson@Sun.COM xen_platform_op_t op; 8417347SMark.Johnson@Sun.COM int e; 8427347SMark.Johnson@Sun.COM 8437347SMark.Johnson@Sun.COM ASSERT(DOMAIN_IS_INITDOMAIN(xen_info)); 8447347SMark.Johnson@Sun.COM 8457347SMark.Johnson@Sun.COM kpreempt_disable(); 8467347SMark.Johnson@Sun.COM op.cmd = XENPF_microcode_update; 8477347SMark.Johnson@Sun.COM op.interface_version = XENPF_INTERFACE_VERSION; 8487347SMark.Johnson@Sun.COM /*LINTED: constant in conditional context*/ 8497605SMark.Johnson@Sun.COM set_xen_guest_handle(op.u.microcode.data, uusp->ucodep); 8507605SMark.Johnson@Sun.COM op.u.microcode.length = uusp->usize; 8517347SMark.Johnson@Sun.COM e = HYPERVISOR_platform_op(&op); 8527347SMark.Johnson@Sun.COM if (e != 0) { 8537347SMark.Johnson@Sun.COM cmn_err(CE_WARN, "hypervisor failed to accept uCode update"); 8547347SMark.Johnson@Sun.COM } 8557347SMark.Johnson@Sun.COM kpreempt_enable(); 8567347SMark.Johnson@Sun.COM } 8577347SMark.Johnson@Sun.COM #endif /* __xpv */ 8587347SMark.Johnson@Sun.COM 8597605SMark.Johnson@Sun.COM static void 8607605SMark.Johnson@Sun.COM ucode_read_rev_amd(cpu_ucode_info_t *uinfop) 8617605SMark.Johnson@Sun.COM { 8627605SMark.Johnson@Sun.COM uinfop->cui_rev = rdmsr(MSR_AMD_PATCHLEVEL); 8637605SMark.Johnson@Sun.COM } 8647347SMark.Johnson@Sun.COM 8654581Ssherrym static void 8667605SMark.Johnson@Sun.COM ucode_read_rev_intel(cpu_ucode_info_t *uinfop) 8674581Ssherrym { 8684581Ssherrym struct cpuid_regs crs; 8694581Ssherrym 8704581Ssherrym /* 8714581Ssherrym * The Intel 64 and IA-32 Architecture Software Developer's Manual 8724581Ssherrym * recommends that MSR_INTC_UCODE_REV be loaded with 0 first, then 8734581Ssherrym * execute cpuid to guarantee the correct reading of this register. 8744581Ssherrym */ 8754581Ssherrym wrmsr(MSR_INTC_UCODE_REV, 0); 8764581Ssherrym (void) __cpuid_insn(&crs); 8774581Ssherrym uinfop->cui_rev = (rdmsr(MSR_INTC_UCODE_REV) >> INTC_UCODE_REV_SHIFT); 8784581Ssherrym } 8794581Ssherrym 8807605SMark.Johnson@Sun.COM static ucode_errno_t 8817605SMark.Johnson@Sun.COM ucode_extract_amd(ucode_update_t *uusp, uint8_t *ucodep, int size) 8827605SMark.Johnson@Sun.COM { 8838647SMark.Johnson@Sun.COM #ifndef __xpv 8847605SMark.Johnson@Sun.COM uint32_t *ptr = (uint32_t *)ucodep; 8857605SMark.Johnson@Sun.COM ucode_eqtbl_amd_t *eqtbl; 8867605SMark.Johnson@Sun.COM ucode_file_amd_t *ufp; 8878647SMark.Johnson@Sun.COM int count; 8888647SMark.Johnson@Sun.COM int higher = 0; 8898647SMark.Johnson@Sun.COM ucode_errno_t rc = EM_NOMATCH; 8908647SMark.Johnson@Sun.COM uint16_t eq_sig; 8917605SMark.Johnson@Sun.COM 8927605SMark.Johnson@Sun.COM /* skip over magic number & equivalence table header */ 8937605SMark.Johnson@Sun.COM ptr += 2; size -= 8; 8947605SMark.Johnson@Sun.COM 8957605SMark.Johnson@Sun.COM count = *ptr++; size -= 4; 8967605SMark.Johnson@Sun.COM for (eqtbl = (ucode_eqtbl_amd_t *)ptr; 8977605SMark.Johnson@Sun.COM eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != uusp->sig; 8987605SMark.Johnson@Sun.COM eqtbl++) 8997605SMark.Johnson@Sun.COM ; 9007605SMark.Johnson@Sun.COM 9017605SMark.Johnson@Sun.COM eq_sig = eqtbl->ue_equiv_cpu; 9027605SMark.Johnson@Sun.COM 9037605SMark.Johnson@Sun.COM /* No equivalent CPU id found, assume outdated microcode file. */ 9047605SMark.Johnson@Sun.COM if (eq_sig == 0) 9057605SMark.Johnson@Sun.COM return (EM_HIGHERREV); 9067605SMark.Johnson@Sun.COM 9077605SMark.Johnson@Sun.COM /* Use the first microcode patch that matches. */ 9087605SMark.Johnson@Sun.COM do { 9097605SMark.Johnson@Sun.COM ptr += count >> 2; size -= count; 9107605SMark.Johnson@Sun.COM 9117605SMark.Johnson@Sun.COM if (!size) 9128647SMark.Johnson@Sun.COM return (higher ? EM_HIGHERREV : EM_NOMATCH); 9137605SMark.Johnson@Sun.COM 9147605SMark.Johnson@Sun.COM ptr++; size -= 4; 9157605SMark.Johnson@Sun.COM count = *ptr++; size -= 4; 9167605SMark.Johnson@Sun.COM ufp = (ucode_file_amd_t *)ptr; 9178647SMark.Johnson@Sun.COM 9188647SMark.Johnson@Sun.COM rc = ucode_match_amd(eq_sig, &uusp->info, ufp, count); 9198647SMark.Johnson@Sun.COM if (rc == EM_HIGHERREV) 9208647SMark.Johnson@Sun.COM higher = 1; 9218647SMark.Johnson@Sun.COM } while (rc != EM_OK); 9227605SMark.Johnson@Sun.COM 9237605SMark.Johnson@Sun.COM uusp->ucodep = (uint8_t *)ufp; 9247605SMark.Johnson@Sun.COM uusp->usize = count; 9257605SMark.Johnson@Sun.COM uusp->expected_rev = ufp->uf_header.uh_patch_id; 9268647SMark.Johnson@Sun.COM #else 9278647SMark.Johnson@Sun.COM /* 9288647SMark.Johnson@Sun.COM * The hypervisor will choose the patch to load, so there is no way to 9298647SMark.Johnson@Sun.COM * know the "expected revision" in advance. This is especially true on 9308647SMark.Johnson@Sun.COM * mixed-revision systems where more than one patch will be loaded. 9318647SMark.Johnson@Sun.COM */ 9328647SMark.Johnson@Sun.COM uusp->expected_rev = 0; 9338647SMark.Johnson@Sun.COM uusp->ucodep = ucodep; 9348647SMark.Johnson@Sun.COM uusp->usize = size; 9358647SMark.Johnson@Sun.COM 9368647SMark.Johnson@Sun.COM ucode_chipset_amd(ucodep, size); 9378647SMark.Johnson@Sun.COM #endif 9387605SMark.Johnson@Sun.COM 9397605SMark.Johnson@Sun.COM return (EM_OK); 9407605SMark.Johnson@Sun.COM } 9417605SMark.Johnson@Sun.COM 9427605SMark.Johnson@Sun.COM static ucode_errno_t 9437605SMark.Johnson@Sun.COM ucode_extract_intel(ucode_update_t *uusp, uint8_t *ucodep, int size) 9447605SMark.Johnson@Sun.COM { 9457605SMark.Johnson@Sun.COM uint32_t header_size = UCODE_HEADER_SIZE_INTEL; 9467605SMark.Johnson@Sun.COM int remaining; 9477605SMark.Johnson@Sun.COM int found = 0; 9487605SMark.Johnson@Sun.COM ucode_errno_t search_rc = EM_NOMATCH; /* search result */ 9497605SMark.Johnson@Sun.COM 9507605SMark.Johnson@Sun.COM /* 9517605SMark.Johnson@Sun.COM * Go through the whole buffer in case there are 9527605SMark.Johnson@Sun.COM * multiple versions of matching microcode for this 9537605SMark.Johnson@Sun.COM * processor. 9547605SMark.Johnson@Sun.COM */ 9557605SMark.Johnson@Sun.COM for (remaining = size; remaining > 0; ) { 9567605SMark.Johnson@Sun.COM int total_size, body_size, ext_size; 9577605SMark.Johnson@Sun.COM uint8_t *curbuf = &ucodep[size - remaining]; 9587605SMark.Johnson@Sun.COM ucode_header_intel_t *uhp = (ucode_header_intel_t *)curbuf; 9597605SMark.Johnson@Sun.COM ucode_ext_table_intel_t *uetp = NULL; 9607605SMark.Johnson@Sun.COM ucode_errno_t tmprc; 9617605SMark.Johnson@Sun.COM 9627605SMark.Johnson@Sun.COM total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size); 9637605SMark.Johnson@Sun.COM body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size); 9647605SMark.Johnson@Sun.COM ext_size = total_size - (header_size + body_size); 9657605SMark.Johnson@Sun.COM 9667605SMark.Johnson@Sun.COM if (ext_size > 0) 9677605SMark.Johnson@Sun.COM uetp = (ucode_ext_table_intel_t *) 9687605SMark.Johnson@Sun.COM &curbuf[header_size + body_size]; 9697605SMark.Johnson@Sun.COM 9707605SMark.Johnson@Sun.COM tmprc = ucode_match_intel(uusp->sig, &uusp->info, uhp, uetp); 9717605SMark.Johnson@Sun.COM 9727605SMark.Johnson@Sun.COM /* 9737605SMark.Johnson@Sun.COM * Since we are searching through a big file 9747605SMark.Johnson@Sun.COM * containing microcode for pretty much all the 9757605SMark.Johnson@Sun.COM * processors, we are bound to get EM_NOMATCH 9767605SMark.Johnson@Sun.COM * at one point. However, if we return 9777605SMark.Johnson@Sun.COM * EM_NOMATCH to users, it will really confuse 9787605SMark.Johnson@Sun.COM * them. Therefore, if we ever find a match of 9797605SMark.Johnson@Sun.COM * a lower rev, we will set return code to 9807605SMark.Johnson@Sun.COM * EM_HIGHERREV. 9817605SMark.Johnson@Sun.COM */ 9827605SMark.Johnson@Sun.COM if (tmprc == EM_HIGHERREV) 9837605SMark.Johnson@Sun.COM search_rc = EM_HIGHERREV; 9847605SMark.Johnson@Sun.COM 9857605SMark.Johnson@Sun.COM if (tmprc == EM_OK && 9867605SMark.Johnson@Sun.COM uusp->expected_rev < uhp->uh_rev) { 9877605SMark.Johnson@Sun.COM #ifndef __xpv 9887605SMark.Johnson@Sun.COM uusp->ucodep = (uint8_t *)&curbuf[header_size]; 9897605SMark.Johnson@Sun.COM #else 9907605SMark.Johnson@Sun.COM uusp->ucodep = (uint8_t *)curbuf; 9917605SMark.Johnson@Sun.COM #endif 9927605SMark.Johnson@Sun.COM uusp->usize = 9937605SMark.Johnson@Sun.COM UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size); 9947605SMark.Johnson@Sun.COM uusp->expected_rev = uhp->uh_rev; 9957605SMark.Johnson@Sun.COM found = 1; 9967605SMark.Johnson@Sun.COM } 9977605SMark.Johnson@Sun.COM 9987605SMark.Johnson@Sun.COM remaining -= total_size; 9997605SMark.Johnson@Sun.COM } 10007605SMark.Johnson@Sun.COM 10017605SMark.Johnson@Sun.COM if (!found) 10027605SMark.Johnson@Sun.COM return (search_rc); 10037605SMark.Johnson@Sun.COM 10047605SMark.Johnson@Sun.COM return (EM_OK); 10057605SMark.Johnson@Sun.COM } 10064581Ssherrym /* 10074581Ssherrym * Entry point to microcode update from the ucode_drv driver. 10084581Ssherrym * 10094581Ssherrym * Returns EM_OK on success, corresponding error code on failure. 10104581Ssherrym */ 10114581Ssherrym ucode_errno_t 10124581Ssherrym ucode_update(uint8_t *ucodep, int size) 10134581Ssherrym { 10144581Ssherrym int found = 0; 10154581Ssherrym processorid_t id; 10167605SMark.Johnson@Sun.COM ucode_update_t cached = { 0 }; 10177605SMark.Johnson@Sun.COM ucode_update_t *cachedp = NULL; 10184581Ssherrym ucode_errno_t rc = EM_OK; 10194581Ssherrym ucode_errno_t search_rc = EM_NOMATCH; /* search result */ 10204581Ssherrym cpuset_t cpuset; 10214581Ssherrym 10227605SMark.Johnson@Sun.COM ASSERT(ucode); 10234581Ssherrym ASSERT(ucodep); 10244581Ssherrym CPUSET_ZERO(cpuset); 10254581Ssherrym 10267605SMark.Johnson@Sun.COM if (!ucode->capable(CPU)) 10274581Ssherrym return (EM_NOTSUP); 10284581Ssherrym 10294581Ssherrym mutex_enter(&cpu_lock); 10304581Ssherrym 10314581Ssherrym for (id = 0; id < max_ncpus; id++) { 10324581Ssherrym cpu_t *cpu; 10337605SMark.Johnson@Sun.COM ucode_update_t uus = { 0 }; 10347605SMark.Johnson@Sun.COM ucode_update_t *uusp = &uus; 10354581Ssherrym 10364581Ssherrym /* 10374581Ssherrym * If there is no such CPU or it is not xcall ready, skip it. 10384581Ssherrym */ 10394581Ssherrym if ((cpu = cpu_get(id)) == NULL || 10404581Ssherrym !(cpu->cpu_flags & CPU_READY)) 10414581Ssherrym continue; 10424581Ssherrym 10434581Ssherrym uusp->sig = cpuid_getsig(cpu); 10444581Ssherrym bcopy(cpu->cpu_m.mcpu_ucode_info, &uusp->info, 10454581Ssherrym sizeof (uusp->info)); 10464581Ssherrym 10474581Ssherrym /* 10484581Ssherrym * If the current CPU has the same signature and platform 10494581Ssherrym * id as the previous one we processed, reuse the information. 10504581Ssherrym */ 10514581Ssherrym if (cachedp && cachedp->sig == cpuid_getsig(cpu) && 10524581Ssherrym cachedp->info.cui_platid == uusp->info.cui_platid) { 10534581Ssherrym uusp->ucodep = cachedp->ucodep; 10544581Ssherrym uusp->expected_rev = cachedp->expected_rev; 10554581Ssherrym /* 10564581Ssherrym * Intuitively we should check here to see whether the 10574581Ssherrym * running microcode rev is >= the expected rev, and 10584581Ssherrym * quit if it is. But we choose to proceed with the 10594581Ssherrym * xcall regardless of the running version so that 10604581Ssherrym * the other threads in an HT processor can update 10614581Ssherrym * the cpu_ucode_info structure in machcpu. 10624581Ssherrym */ 10637605SMark.Johnson@Sun.COM } else if ((search_rc = ucode->extract(uusp, ucodep, size)) 10647605SMark.Johnson@Sun.COM == EM_OK) { 10657605SMark.Johnson@Sun.COM bcopy(uusp, &cached, sizeof (cached)); 10667605SMark.Johnson@Sun.COM cachedp = &cached; 10677605SMark.Johnson@Sun.COM found = 1; 10684581Ssherrym } 10694581Ssherrym 10704581Ssherrym /* Nothing to do */ 10714581Ssherrym if (uusp->ucodep == NULL) 10724581Ssherrym continue; 10734581Ssherrym 10747347SMark.Johnson@Sun.COM #ifdef __xpv 10757347SMark.Johnson@Sun.COM /* 10767347SMark.Johnson@Sun.COM * for i86xpv, the hypervisor will update all the CPUs. 10777347SMark.Johnson@Sun.COM * the hypervisor wants the header, data, and extended 10787347SMark.Johnson@Sun.COM * signature tables. ucode_write will just read in the 10797347SMark.Johnson@Sun.COM * updated version on all the CPUs after the update has 10807347SMark.Johnson@Sun.COM * completed. 10817347SMark.Johnson@Sun.COM */ 10827402SMark.Johnson@Sun.COM if (id == 0) { 10837605SMark.Johnson@Sun.COM ucode_load_xpv(uusp); 10847402SMark.Johnson@Sun.COM } 10857347SMark.Johnson@Sun.COM #endif 10867347SMark.Johnson@Sun.COM 10874581Ssherrym CPUSET_ADD(cpuset, id); 10884581Ssherrym kpreempt_disable(); 10899489SJoe.Bonasera@sun.com xc_sync((xc_arg_t)uusp, 0, 0, CPUSET2BV(cpuset), 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: 1145*9627SMark.Johnson@Sun.COM ucode = NULL; 11467605SMark.Johnson@Sun.COM return; 11477605SMark.Johnson@Sun.COM } 11487605SMark.Johnson@Sun.COM 11497605SMark.Johnson@Sun.COM if (!ucode->capable(cp)) 11504581Ssherrym return; 11514581Ssherrym 11524581Ssherrym /* 11534581Ssherrym * The MSR_INTC_PLATFORM_ID is supported in Celeron and Xeon 11544581Ssherrym * (Family 6, model 5 and above) and all processors after. 11554581Ssherrym */ 11567605SMark.Johnson@Sun.COM if ((cpuid_getvendor(cp) == X86_VENDOR_Intel) && 11577605SMark.Johnson@Sun.COM ((cpuid_getmodel(cp) >= 5) || (cpuid_getfamily(cp) > 6))) { 11584581Ssherrym uinfop->cui_platid = 1 << ((rdmsr(MSR_INTC_PLATFORM_ID) >> 11594581Ssherrym INTC_PLATFORM_ID_SHIFT) & INTC_PLATFORM_ID_MASK); 11604581Ssherrym } 11614581Ssherrym 11627605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 11634581Ssherrym 11647402SMark.Johnson@Sun.COM #ifdef __xpv 11657402SMark.Johnson@Sun.COM /* 11667402SMark.Johnson@Sun.COM * for i86xpv, the hypervisor will update all the CPUs. We only need 11677402SMark.Johnson@Sun.COM * do do this on one of the CPUs (and there always is a CPU 0). 11687402SMark.Johnson@Sun.COM */ 11697402SMark.Johnson@Sun.COM if (cp->cpu_id != 0) { 11707402SMark.Johnson@Sun.COM return; 11717402SMark.Johnson@Sun.COM } 11727402SMark.Johnson@Sun.COM #endif 11737402SMark.Johnson@Sun.COM 11744581Ssherrym /* 11754581Ssherrym * Check to see if we need ucode update 11764581Ssherrym */ 11777605SMark.Johnson@Sun.COM if ((rc = ucode->locate(cp, uinfop, &ucodefile)) == EM_OK) { 11787605SMark.Johnson@Sun.COM new_rev = ucode->load(&ucodefile, uinfop, cp); 11797347SMark.Johnson@Sun.COM 11807605SMark.Johnson@Sun.COM if (uinfop->cui_rev != new_rev) 11814581Ssherrym cmn_err(CE_WARN, ucode_failure_fmt, cp->cpu_id, 11827605SMark.Johnson@Sun.COM uinfop->cui_rev, new_rev); 11834581Ssherrym } 11844581Ssherrym 11854581Ssherrym /* 11864581Ssherrym * If we fail to find a match for any reason, free the file structure 11874581Ssherrym * just in case we have read in a partial file. 11884581Ssherrym * 11894581Ssherrym * Since the scratch memory for holding the microcode for the boot CPU 11904581Ssherrym * came from BOP_ALLOC, we will reset the data structure as if we 11914581Ssherrym * never did the allocation so we don't have to keep track of this 11924581Ssherrym * special chunk of memory. We free the memory used for the rest 11934581Ssherrym * of the CPUs in start_other_cpus(). 11944581Ssherrym */ 11954581Ssherrym if (rc != EM_OK || cp->cpu_id == 0) 11967605SMark.Johnson@Sun.COM ucode->file_reset(&ucodefile, cp->cpu_id); 11974581Ssherrym } 11984581Ssherrym 11994581Ssherrym /* 12004581Ssherrym * Returns microcode revision from the machcpu structure. 12014581Ssherrym */ 12024581Ssherrym ucode_errno_t 12034581Ssherrym ucode_get_rev(uint32_t *revp) 12044581Ssherrym { 12054581Ssherrym int i; 12064581Ssherrym 12077605SMark.Johnson@Sun.COM ASSERT(ucode); 12084581Ssherrym ASSERT(revp); 12094581Ssherrym 12107605SMark.Johnson@Sun.COM if (!ucode->capable(CPU)) 12114581Ssherrym return (EM_NOTSUP); 12124581Ssherrym 12134581Ssherrym mutex_enter(&cpu_lock); 12144581Ssherrym for (i = 0; i < max_ncpus; i++) { 12154581Ssherrym cpu_t *cpu; 12164581Ssherrym 12174581Ssherrym if ((cpu = cpu_get(i)) == NULL) 12184581Ssherrym continue; 12194581Ssherrym 12204581Ssherrym revp[i] = cpu->cpu_m.mcpu_ucode_info->cui_rev; 12214581Ssherrym } 12224581Ssherrym mutex_exit(&cpu_lock); 12234581Ssherrym 12244581Ssherrym return (EM_OK); 12254581Ssherrym } 1226