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 /* 236519Ssherrym * Copyright 2008 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 /* 51*7605SMark.Johnson@Sun.COM * AMD-specific equivalence table 524581Ssherrym */ 53*7605SMark.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 60*7605SMark.Johnson@Sun.COM static ucode_file_t ucodefile; 61*7605SMark.Johnson@Sun.COM 62*7605SMark.Johnson@Sun.COM static void* ucode_zalloc(processorid_t, size_t); 63*7605SMark.Johnson@Sun.COM static void ucode_free(processorid_t, void *, size_t); 64*7605SMark.Johnson@Sun.COM 65*7605SMark.Johnson@Sun.COM static int ucode_capable_amd(cpu_t *); 66*7605SMark.Johnson@Sun.COM static int ucode_capable_intel(cpu_t *); 67*7605SMark.Johnson@Sun.COM 68*7605SMark.Johnson@Sun.COM static ucode_errno_t ucode_extract_amd(ucode_update_t *, uint8_t *, int); 69*7605SMark.Johnson@Sun.COM static ucode_errno_t ucode_extract_intel(ucode_update_t *, uint8_t *, 70*7605SMark.Johnson@Sun.COM int); 71*7605SMark.Johnson@Sun.COM 72*7605SMark.Johnson@Sun.COM static void ucode_file_reset_amd(ucode_file_t *, processorid_t); 73*7605SMark.Johnson@Sun.COM static void ucode_file_reset_intel(ucode_file_t *, processorid_t); 74*7605SMark.Johnson@Sun.COM 75*7605SMark.Johnson@Sun.COM static uint32_t ucode_load_amd(ucode_file_t *, cpu_ucode_info_t *, cpu_t *); 76*7605SMark.Johnson@Sun.COM static uint32_t ucode_load_intel(ucode_file_t *, cpu_ucode_info_t *, cpu_t *); 77*7605SMark.Johnson@Sun.COM 78*7605SMark.Johnson@Sun.COM #ifdef __xpv 79*7605SMark.Johnson@Sun.COM static void ucode_load_xpv(ucode_update_t *); 80*7605SMark.Johnson@Sun.COM #endif 81*7605SMark.Johnson@Sun.COM 82*7605SMark.Johnson@Sun.COM static int ucode_equiv_cpu_amd(cpu_t *, int *); 83*7605SMark.Johnson@Sun.COM 84*7605SMark.Johnson@Sun.COM static ucode_errno_t ucode_locate_amd(cpu_t *, cpu_ucode_info_t *, 85*7605SMark.Johnson@Sun.COM ucode_file_t *); 86*7605SMark.Johnson@Sun.COM static ucode_errno_t ucode_locate_intel(cpu_t *, cpu_ucode_info_t *, 87*7605SMark.Johnson@Sun.COM ucode_file_t *); 884581Ssherrym 89*7605SMark.Johnson@Sun.COM static ucode_errno_t ucode_match_amd(int, cpu_ucode_info_t *, 90*7605SMark.Johnson@Sun.COM ucode_file_amd_t *, int); 91*7605SMark.Johnson@Sun.COM static ucode_errno_t ucode_match_intel(int, cpu_ucode_info_t *, 92*7605SMark.Johnson@Sun.COM ucode_header_intel_t *, ucode_ext_table_intel_t *); 93*7605SMark.Johnson@Sun.COM 94*7605SMark.Johnson@Sun.COM static void ucode_read_rev_amd(cpu_ucode_info_t *); 95*7605SMark.Johnson@Sun.COM static void ucode_read_rev_intel(cpu_ucode_info_t *); 96*7605SMark.Johnson@Sun.COM 97*7605SMark.Johnson@Sun.COM static const struct ucode_ops ucode_amd = { 98*7605SMark.Johnson@Sun.COM MSR_AMD_PATCHLOADER, 99*7605SMark.Johnson@Sun.COM ucode_capable_amd, 100*7605SMark.Johnson@Sun.COM ucode_file_reset_amd, 101*7605SMark.Johnson@Sun.COM ucode_read_rev_amd, 102*7605SMark.Johnson@Sun.COM ucode_load_amd, 103*7605SMark.Johnson@Sun.COM ucode_validate_amd, 104*7605SMark.Johnson@Sun.COM ucode_extract_amd, 105*7605SMark.Johnson@Sun.COM ucode_locate_amd 106*7605SMark.Johnson@Sun.COM }; 107*7605SMark.Johnson@Sun.COM 108*7605SMark.Johnson@Sun.COM static const struct ucode_ops ucode_intel = { 109*7605SMark.Johnson@Sun.COM MSR_INTC_UCODE_WRITE, 110*7605SMark.Johnson@Sun.COM ucode_capable_intel, 111*7605SMark.Johnson@Sun.COM ucode_file_reset_intel, 112*7605SMark.Johnson@Sun.COM ucode_read_rev_intel, 113*7605SMark.Johnson@Sun.COM ucode_load_intel, 114*7605SMark.Johnson@Sun.COM ucode_validate_intel, 115*7605SMark.Johnson@Sun.COM ucode_extract_intel, 116*7605SMark.Johnson@Sun.COM ucode_locate_intel 117*7605SMark.Johnson@Sun.COM }; 118*7605SMark.Johnson@Sun.COM 119*7605SMark.Johnson@Sun.COM const struct ucode_ops *ucode; 1204581Ssherrym 1214581Ssherrym static const char ucode_failure_fmt[] = 1226519Ssherrym "cpu%d: failed to update microcode from version 0x%x to 0x%x\n"; 1234581Ssherrym static const char ucode_success_fmt[] = 1246519Ssherrym "?cpu%d: microcode has been updated from version 0x%x to 0x%x\n"; 1254581Ssherrym 1264581Ssherrym /* 1274581Ssherrym * Force flag. If set, the first microcode binary that matches 1284581Ssherrym * signature and platform id will be used for microcode update, 1294581Ssherrym * regardless of version. Should only be used for debugging. 1304581Ssherrym */ 1314581Ssherrym int ucode_force_update = 0; 1324581Ssherrym 1334581Ssherrym /* 1344581Ssherrym * Allocate space for mcpu_ucode_info in the machcpu structure 1354581Ssherrym * for all non-boot CPUs. 1364581Ssherrym */ 1374581Ssherrym void 1384581Ssherrym ucode_alloc_space(cpu_t *cp) 1394581Ssherrym { 1404581Ssherrym ASSERT(cp->cpu_id != 0); 1414581Ssherrym cp->cpu_m.mcpu_ucode_info = 1424581Ssherrym kmem_zalloc(sizeof (*cp->cpu_m.mcpu_ucode_info), KM_SLEEP); 1434581Ssherrym } 1444581Ssherrym 1454581Ssherrym void 1464581Ssherrym ucode_free_space(cpu_t *cp) 1474581Ssherrym { 1484581Ssherrym ASSERT(cp->cpu_id != 0); 1494581Ssherrym kmem_free(cp->cpu_m.mcpu_ucode_info, 1504581Ssherrym sizeof (*cp->cpu_m.mcpu_ucode_info)); 1514581Ssherrym } 1524581Ssherrym 1534581Ssherrym /* 1544581Ssherrym * Called when we are done with microcode update on all processors to free up 1554581Ssherrym * space allocated for the microcode file. 1564581Ssherrym */ 1574581Ssherrym void 158*7605SMark.Johnson@Sun.COM ucode_cleanup() 1594581Ssherrym { 160*7605SMark.Johnson@Sun.COM ASSERT(ucode); 161*7605SMark.Johnson@Sun.COM 162*7605SMark.Johnson@Sun.COM ucode->file_reset(&ucodefile, -1); 163*7605SMark.Johnson@Sun.COM } 164*7605SMark.Johnson@Sun.COM 165*7605SMark.Johnson@Sun.COM /* 166*7605SMark.Johnson@Sun.COM * Allocate/free a buffer used to hold ucode data. Space for the boot CPU is 167*7605SMark.Johnson@Sun.COM * allocated with BOP_ALLOC() and does not require a free. 168*7605SMark.Johnson@Sun.COM */ 169*7605SMark.Johnson@Sun.COM static void* 170*7605SMark.Johnson@Sun.COM ucode_zalloc(processorid_t id, size_t size) 171*7605SMark.Johnson@Sun.COM { 172*7605SMark.Johnson@Sun.COM if (id) 173*7605SMark.Johnson@Sun.COM return (kmem_zalloc(size, KM_NOSLEEP)); 174*7605SMark.Johnson@Sun.COM 175*7605SMark.Johnson@Sun.COM /* BOP_ALLOC() failure results in panic */ 176*7605SMark.Johnson@Sun.COM return (BOP_ALLOC(bootops, NULL, size, MMU_PAGESIZE)); 177*7605SMark.Johnson@Sun.COM } 178*7605SMark.Johnson@Sun.COM 179*7605SMark.Johnson@Sun.COM static void 180*7605SMark.Johnson@Sun.COM ucode_free(processorid_t id, void* buf, size_t size) 181*7605SMark.Johnson@Sun.COM { 182*7605SMark.Johnson@Sun.COM if (id) 183*7605SMark.Johnson@Sun.COM kmem_free(buf, size); 1844581Ssherrym } 1854581Ssherrym 1864581Ssherrym /* 1874581Ssherrym * Check whether or not a processor is capable of microcode operations 1884581Ssherrym * Returns 1 if it is capable, 0 if not. 189*7605SMark.Johnson@Sun.COM * 190*7605SMark.Johnson@Sun.COM * At this point we only support microcode update for: 191*7605SMark.Johnson@Sun.COM * - Intel processors family 6 and above, and 192*7605SMark.Johnson@Sun.COM * - AMD processors family 0x10 and above. 193*7605SMark.Johnson@Sun.COM * 194*7605SMark.Johnson@Sun.COM * We also assume that we don't support a mix of Intel and 195*7605SMark.Johnson@Sun.COM * AMD processors in the same box. 196*7605SMark.Johnson@Sun.COM * 197*7605SMark.Johnson@Sun.COM * An i86xpv guest domain can't update the microcode. 1984581Ssherrym */ 199*7605SMark.Johnson@Sun.COM /*ARGSUSED*/ 2004581Ssherrym static int 201*7605SMark.Johnson@Sun.COM ucode_capable_amd(cpu_t *cp) 2024581Ssherrym { 203*7605SMark.Johnson@Sun.COM #ifndef __xpv 204*7605SMark.Johnson@Sun.COM extern int xpv_is_hvm; 205*7605SMark.Johnson@Sun.COM if (xpv_is_hvm) { 206*7605SMark.Johnson@Sun.COM return (0); 207*7605SMark.Johnson@Sun.COM } 208*7605SMark.Johnson@Sun.COM 209*7605SMark.Johnson@Sun.COM return (cpuid_getfamily(cp) >= 0x10); 210*7605SMark.Johnson@Sun.COM #else 211*7605SMark.Johnson@Sun.COM if (!DOMAIN_IS_INITDOMAIN(xen_info)) { 212*7605SMark.Johnson@Sun.COM return (0); 213*7605SMark.Johnson@Sun.COM } 214*7605SMark.Johnson@Sun.COM 215*7605SMark.Johnson@Sun.COM /* 216*7605SMark.Johnson@Sun.COM * XXPV - change when microcode loading works in dom0. Don't support 217*7605SMark.Johnson@Sun.COM * microcode loading in dom0 right now for AMD. 218*7605SMark.Johnson@Sun.COM */ 219*7605SMark.Johnson@Sun.COM return (0); 220*7605SMark.Johnson@Sun.COM #endif 221*7605SMark.Johnson@Sun.COM } 222*7605SMark.Johnson@Sun.COM static int 223*7605SMark.Johnson@Sun.COM ucode_capable_intel(cpu_t *cp) 224*7605SMark.Johnson@Sun.COM { 2257347SMark.Johnson@Sun.COM #ifndef __xpv 2267347SMark.Johnson@Sun.COM extern int xpv_is_hvm; 2277347SMark.Johnson@Sun.COM if (xpv_is_hvm) { 2287347SMark.Johnson@Sun.COM return (0); 2297347SMark.Johnson@Sun.COM } 2307347SMark.Johnson@Sun.COM #else 2315084Sjohnlev if (!DOMAIN_IS_INITDOMAIN(xen_info)) { 2325084Sjohnlev return (0); 2335084Sjohnlev } 2345084Sjohnlev #endif 235*7605SMark.Johnson@Sun.COM return (cpuid_getfamily(cp) >= 6); 2364581Ssherrym } 2374581Ssherrym 2384581Ssherrym /* 2394581Ssherrym * Called when it is no longer necessary to keep the microcode around, 2404581Ssherrym * or when the cached microcode doesn't match the CPU being processed. 2414581Ssherrym */ 2424581Ssherrym static void 243*7605SMark.Johnson@Sun.COM ucode_file_reset_amd(ucode_file_t *ufp, processorid_t id) 2444581Ssherrym { 245*7605SMark.Johnson@Sun.COM ucode_file_amd_t *ucodefp = ufp->amd; 2464581Ssherrym 2474581Ssherrym if (ucodefp == NULL) 2484581Ssherrym return; 2494581Ssherrym 250*7605SMark.Johnson@Sun.COM ucode_free(id, ucodefp, sizeof (ucode_file_amd_t)); 251*7605SMark.Johnson@Sun.COM ufp->amd = NULL; 252*7605SMark.Johnson@Sun.COM } 253*7605SMark.Johnson@Sun.COM 254*7605SMark.Johnson@Sun.COM static void 255*7605SMark.Johnson@Sun.COM ucode_file_reset_intel(ucode_file_t *ufp, processorid_t id) 256*7605SMark.Johnson@Sun.COM { 257*7605SMark.Johnson@Sun.COM ucode_file_intel_t *ucodefp = &ufp->intel; 258*7605SMark.Johnson@Sun.COM int total_size, body_size; 259*7605SMark.Johnson@Sun.COM 260*7605SMark.Johnson@Sun.COM if (ucodefp == NULL || ucodefp->uf_header == NULL) 261*7605SMark.Johnson@Sun.COM return; 262*7605SMark.Johnson@Sun.COM 263*7605SMark.Johnson@Sun.COM total_size = UCODE_TOTAL_SIZE_INTEL(ucodefp->uf_header->uh_total_size); 264*7605SMark.Johnson@Sun.COM body_size = UCODE_BODY_SIZE_INTEL(ucodefp->uf_header->uh_body_size); 2654581Ssherrym if (ucodefp->uf_body) { 266*7605SMark.Johnson@Sun.COM ucode_free(id, ucodefp->uf_body, body_size); 2674581Ssherrym ucodefp->uf_body = NULL; 2684581Ssherrym } 2694581Ssherrym 2704581Ssherrym if (ucodefp->uf_ext_table) { 271*7605SMark.Johnson@Sun.COM int size = total_size - body_size - UCODE_HEADER_SIZE_INTEL; 272*7605SMark.Johnson@Sun.COM 273*7605SMark.Johnson@Sun.COM ucode_free(id, ucodefp->uf_ext_table, size); 2744581Ssherrym ucodefp->uf_ext_table = NULL; 2754581Ssherrym } 2764581Ssherrym 277*7605SMark.Johnson@Sun.COM ucode_free(id, ucodefp->uf_header, UCODE_HEADER_SIZE_INTEL); 278*7605SMark.Johnson@Sun.COM ucodefp->uf_header = NULL; 279*7605SMark.Johnson@Sun.COM } 280*7605SMark.Johnson@Sun.COM 281*7605SMark.Johnson@Sun.COM /* 282*7605SMark.Johnson@Sun.COM * Find the equivalent CPU id in the equivalence table. 283*7605SMark.Johnson@Sun.COM */ 284*7605SMark.Johnson@Sun.COM static int 285*7605SMark.Johnson@Sun.COM ucode_equiv_cpu_amd(cpu_t *cp, int *eq_sig) 286*7605SMark.Johnson@Sun.COM { 287*7605SMark.Johnson@Sun.COM char name[MAXPATHLEN]; 288*7605SMark.Johnson@Sun.COM intptr_t fd; 289*7605SMark.Johnson@Sun.COM int count; 290*7605SMark.Johnson@Sun.COM int offset = 0, cpi_sig = cpuid_getsig(cp); 291*7605SMark.Johnson@Sun.COM ucode_eqtbl_amd_t *eqtbl = ucode_eqtbl_amd; 292*7605SMark.Johnson@Sun.COM 293*7605SMark.Johnson@Sun.COM (void) snprintf(name, MAXPATHLEN, "/%s/%s/equivalence-table", 294*7605SMark.Johnson@Sun.COM UCODE_INSTALL_PATH, cpuid_getvendorstr(cp)); 295*7605SMark.Johnson@Sun.COM 296*7605SMark.Johnson@Sun.COM /* 297*7605SMark.Johnson@Sun.COM * No kmem_zalloc() etc. available on boot cpu. 298*7605SMark.Johnson@Sun.COM */ 299*7605SMark.Johnson@Sun.COM if (cp->cpu_id == 0) { 300*7605SMark.Johnson@Sun.COM if ((fd = kobj_open(name)) == -1) 301*7605SMark.Johnson@Sun.COM return (EM_OPENFILE); 302*7605SMark.Johnson@Sun.COM /* ucode_zalloc() cannot fail on boot cpu */ 303*7605SMark.Johnson@Sun.COM eqtbl = ucode_zalloc(cp->cpu_id, sizeof (*eqtbl)); 304*7605SMark.Johnson@Sun.COM ASSERT(eqtbl); 305*7605SMark.Johnson@Sun.COM do { 306*7605SMark.Johnson@Sun.COM count = kobj_read(fd, (int8_t *)eqtbl, 307*7605SMark.Johnson@Sun.COM sizeof (*eqtbl), offset); 308*7605SMark.Johnson@Sun.COM if (count != sizeof (*eqtbl)) { 309*7605SMark.Johnson@Sun.COM (void) kobj_close(fd); 310*7605SMark.Johnson@Sun.COM return (EM_HIGHERREV); 311*7605SMark.Johnson@Sun.COM } 312*7605SMark.Johnson@Sun.COM offset += count; 313*7605SMark.Johnson@Sun.COM } while (eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != cpi_sig); 314*7605SMark.Johnson@Sun.COM (void) kobj_close(fd); 315*7605SMark.Johnson@Sun.COM } 316*7605SMark.Johnson@Sun.COM 317*7605SMark.Johnson@Sun.COM /* 318*7605SMark.Johnson@Sun.COM * If not already done, load the equivalence table. 319*7605SMark.Johnson@Sun.COM * Not done on boot CPU. 320*7605SMark.Johnson@Sun.COM */ 321*7605SMark.Johnson@Sun.COM if (eqtbl == NULL) { 322*7605SMark.Johnson@Sun.COM struct _buf *eq; 323*7605SMark.Johnson@Sun.COM uint64_t size; 324*7605SMark.Johnson@Sun.COM 325*7605SMark.Johnson@Sun.COM if ((eq = kobj_open_file(name)) == (struct _buf *)-1) 326*7605SMark.Johnson@Sun.COM return (EM_OPENFILE); 327*7605SMark.Johnson@Sun.COM 328*7605SMark.Johnson@Sun.COM if (kobj_get_filesize(eq, &size) < 0) { 329*7605SMark.Johnson@Sun.COM kobj_close_file(eq); 330*7605SMark.Johnson@Sun.COM return (EM_OPENFILE); 331*7605SMark.Johnson@Sun.COM } 332*7605SMark.Johnson@Sun.COM 333*7605SMark.Johnson@Sun.COM ucode_eqtbl_amd = kmem_zalloc(size, KM_NOSLEEP); 334*7605SMark.Johnson@Sun.COM if (ucode_eqtbl_amd == NULL) { 335*7605SMark.Johnson@Sun.COM kobj_close_file(eq); 336*7605SMark.Johnson@Sun.COM return (EM_NOMEM); 337*7605SMark.Johnson@Sun.COM } 338*7605SMark.Johnson@Sun.COM 339*7605SMark.Johnson@Sun.COM count = kobj_read_file(eq, (char *)ucode_eqtbl_amd, size, 0); 340*7605SMark.Johnson@Sun.COM kobj_close_file(eq); 341*7605SMark.Johnson@Sun.COM 342*7605SMark.Johnson@Sun.COM if (count != size) 343*7605SMark.Johnson@Sun.COM return (EM_FILESIZE); 344*7605SMark.Johnson@Sun.COM } 345*7605SMark.Johnson@Sun.COM 346*7605SMark.Johnson@Sun.COM /* Get the equivalent CPU id. */ 347*7605SMark.Johnson@Sun.COM if (cp->cpu_id) 348*7605SMark.Johnson@Sun.COM for (eqtbl = ucode_eqtbl_amd; 349*7605SMark.Johnson@Sun.COM eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != cpi_sig; 350*7605SMark.Johnson@Sun.COM eqtbl++) 351*7605SMark.Johnson@Sun.COM ; 352*7605SMark.Johnson@Sun.COM 353*7605SMark.Johnson@Sun.COM *eq_sig = eqtbl->ue_equiv_cpu; 354*7605SMark.Johnson@Sun.COM *eq_sig = ((*eq_sig >> 8) & 0xff00) | (*eq_sig & 0xff); 355*7605SMark.Johnson@Sun.COM 356*7605SMark.Johnson@Sun.COM /* No equivalent CPU id found, assume outdated microcode file. */ 357*7605SMark.Johnson@Sun.COM if (*eq_sig == 0) 358*7605SMark.Johnson@Sun.COM return (EM_HIGHERREV); 359*7605SMark.Johnson@Sun.COM 360*7605SMark.Johnson@Sun.COM return (EM_OK); 3614581Ssherrym } 3624581Ssherrym 3634581Ssherrym /* 3644581Ssherrym * Populate the ucode file structure from microcode file corresponding to 3654581Ssherrym * this CPU, if exists. 3664581Ssherrym * 3674581Ssherrym * Return EM_OK on success, corresponding error code on failure. 3684581Ssherrym */ 3694581Ssherrym static ucode_errno_t 370*7605SMark.Johnson@Sun.COM ucode_locate_amd(cpu_t *cp, cpu_ucode_info_t *uinfop, ucode_file_t *ufp) 371*7605SMark.Johnson@Sun.COM { 372*7605SMark.Johnson@Sun.COM char name[MAXPATHLEN]; 373*7605SMark.Johnson@Sun.COM intptr_t fd; 374*7605SMark.Johnson@Sun.COM int count, i, rc; 375*7605SMark.Johnson@Sun.COM int eq_sig = 0; 376*7605SMark.Johnson@Sun.COM ucode_file_amd_t *ucodefp = ufp->amd; 377*7605SMark.Johnson@Sun.COM 378*7605SMark.Johnson@Sun.COM /* get equivalent CPU id */ 379*7605SMark.Johnson@Sun.COM if ((rc = ucode_equiv_cpu_amd(cp, &eq_sig)) != EM_OK) 380*7605SMark.Johnson@Sun.COM return (rc); 381*7605SMark.Johnson@Sun.COM 382*7605SMark.Johnson@Sun.COM /* 383*7605SMark.Johnson@Sun.COM * Allocate a buffer for the microcode patch. If the buffer has been 384*7605SMark.Johnson@Sun.COM * allocated before, check for a matching microcode to avoid loading 385*7605SMark.Johnson@Sun.COM * the file again. 386*7605SMark.Johnson@Sun.COM */ 387*7605SMark.Johnson@Sun.COM if (ucodefp == NULL) 388*7605SMark.Johnson@Sun.COM ucodefp = ucode_zalloc(cp->cpu_id, sizeof (*ucodefp)); 389*7605SMark.Johnson@Sun.COM else if (ucode_match_amd(eq_sig, uinfop, ucodefp, sizeof (*ucodefp)) 390*7605SMark.Johnson@Sun.COM == EM_OK) 391*7605SMark.Johnson@Sun.COM return (EM_OK); 392*7605SMark.Johnson@Sun.COM 393*7605SMark.Johnson@Sun.COM if (ucodefp == NULL) 394*7605SMark.Johnson@Sun.COM return (EM_NOMEM); 395*7605SMark.Johnson@Sun.COM 396*7605SMark.Johnson@Sun.COM ufp->amd = ucodefp; 397*7605SMark.Johnson@Sun.COM 398*7605SMark.Johnson@Sun.COM /* 399*7605SMark.Johnson@Sun.COM * Find the patch for this CPU. The patch files are named XXXX-YY, where 400*7605SMark.Johnson@Sun.COM * XXXX is the equivalent CPU id and YY is the running patch number. 401*7605SMark.Johnson@Sun.COM * Patches specific to certain chipsets are guaranteed to have lower 402*7605SMark.Johnson@Sun.COM * numbers than less specific patches, so we can just load the first 403*7605SMark.Johnson@Sun.COM * patch that matches. 404*7605SMark.Johnson@Sun.COM */ 405*7605SMark.Johnson@Sun.COM 406*7605SMark.Johnson@Sun.COM for (i = 0; i < 0xff; i++) { 407*7605SMark.Johnson@Sun.COM (void) snprintf(name, MAXPATHLEN, "/%s/%s/%04X-%02X", 408*7605SMark.Johnson@Sun.COM UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), eq_sig, i); 409*7605SMark.Johnson@Sun.COM if ((fd = kobj_open(name)) == -1) 410*7605SMark.Johnson@Sun.COM return (EM_NOMATCH); 411*7605SMark.Johnson@Sun.COM count = kobj_read(fd, (char *)ucodefp, sizeof (*ucodefp), 0); 412*7605SMark.Johnson@Sun.COM (void) kobj_close(fd); 413*7605SMark.Johnson@Sun.COM 414*7605SMark.Johnson@Sun.COM if (ucode_match_amd(eq_sig, uinfop, ucodefp, count) == EM_OK) 415*7605SMark.Johnson@Sun.COM return (EM_OK); 416*7605SMark.Johnson@Sun.COM } 417*7605SMark.Johnson@Sun.COM return (EM_NOMATCH); 418*7605SMark.Johnson@Sun.COM } 419*7605SMark.Johnson@Sun.COM 420*7605SMark.Johnson@Sun.COM static ucode_errno_t 421*7605SMark.Johnson@Sun.COM ucode_locate_intel(cpu_t *cp, cpu_ucode_info_t *uinfop, ucode_file_t *ufp) 4224581Ssherrym { 4234581Ssherrym char name[MAXPATHLEN]; 4244581Ssherrym intptr_t fd; 4254581Ssherrym int count; 426*7605SMark.Johnson@Sun.COM int header_size = UCODE_HEADER_SIZE_INTEL; 4274581Ssherrym int cpi_sig = cpuid_getsig(cp); 4284581Ssherrym ucode_errno_t rc = EM_OK; 429*7605SMark.Johnson@Sun.COM ucode_file_intel_t *ucodefp = &ufp->intel; 430*7605SMark.Johnson@Sun.COM 431*7605SMark.Johnson@Sun.COM ASSERT(ucode); 4324581Ssherrym 4334581Ssherrym /* 4344581Ssherrym * If the microcode matches the CPU we are processing, use it. 4354581Ssherrym */ 436*7605SMark.Johnson@Sun.COM if (ucode_match_intel(cpi_sig, uinfop, ucodefp->uf_header, 4374581Ssherrym ucodefp->uf_ext_table) == EM_OK && ucodefp->uf_body != NULL) { 4384581Ssherrym return (EM_OK); 4394581Ssherrym } 4404581Ssherrym 4414581Ssherrym /* 4424581Ssherrym * Look for microcode file with the right name. 4434581Ssherrym */ 4444581Ssherrym (void) snprintf(name, MAXPATHLEN, "/%s/%s/%08X-%02X", 4454581Ssherrym UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), cpi_sig, 4464581Ssherrym uinfop->cui_platid); 4474581Ssherrym if ((fd = kobj_open(name)) == -1) { 4484581Ssherrym return (EM_OPENFILE); 4494581Ssherrym } 4504581Ssherrym 4514581Ssherrym /* 4524581Ssherrym * We found a microcode file for the CPU we are processing, 4534581Ssherrym * reset the microcode data structure and read in the new 4544581Ssherrym * file. 4554581Ssherrym */ 456*7605SMark.Johnson@Sun.COM ucode->file_reset(ufp, cp->cpu_id); 4574581Ssherrym 458*7605SMark.Johnson@Sun.COM ucodefp->uf_header = ucode_zalloc(cp->cpu_id, header_size); 459*7605SMark.Johnson@Sun.COM if (ucodefp->uf_header == NULL) 460*7605SMark.Johnson@Sun.COM return (EM_NOMEM); 461*7605SMark.Johnson@Sun.COM 462*7605SMark.Johnson@Sun.COM count = kobj_read(fd, (char *)ucodefp->uf_header, header_size, 0); 4634581Ssherrym 4644581Ssherrym switch (count) { 465*7605SMark.Johnson@Sun.COM case UCODE_HEADER_SIZE_INTEL: { 4664581Ssherrym 467*7605SMark.Johnson@Sun.COM ucode_header_intel_t *uhp = ucodefp->uf_header; 4684581Ssherrym uint32_t offset = header_size; 4694581Ssherrym int total_size, body_size, ext_size; 4704581Ssherrym uint32_t sum = 0; 4714581Ssherrym 4724581Ssherrym /* 4734581Ssherrym * Make sure that the header contains valid fields. 4744581Ssherrym */ 475*7605SMark.Johnson@Sun.COM if ((rc = ucode_header_validate_intel(uhp)) == EM_OK) { 476*7605SMark.Johnson@Sun.COM total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size); 477*7605SMark.Johnson@Sun.COM body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size); 478*7605SMark.Johnson@Sun.COM ucodefp->uf_body = ucode_zalloc(cp->cpu_id, body_size); 479*7605SMark.Johnson@Sun.COM if (ucodefp->uf_body == NULL) { 480*7605SMark.Johnson@Sun.COM rc = EM_NOMEM; 481*7605SMark.Johnson@Sun.COM break; 4824581Ssherrym } 4834581Ssherrym 4844581Ssherrym if (kobj_read(fd, (char *)ucodefp->uf_body, 4854581Ssherrym body_size, offset) != body_size) 4864581Ssherrym rc = EM_FILESIZE; 4874581Ssherrym } 4884581Ssherrym 4894581Ssherrym if (rc) 4904581Ssherrym break; 4914581Ssherrym 492*7605SMark.Johnson@Sun.COM sum = ucode_checksum_intel(0, header_size, 493*7605SMark.Johnson@Sun.COM (uint8_t *)ucodefp->uf_header); 494*7605SMark.Johnson@Sun.COM if (ucode_checksum_intel(sum, body_size, ucodefp->uf_body)) { 4954581Ssherrym rc = EM_CHECKSUM; 4964581Ssherrym break; 4974581Ssherrym } 4984581Ssherrym 4994581Ssherrym /* 5004581Ssherrym * Check to see if there is extended signature table. 5014581Ssherrym */ 5024581Ssherrym offset = body_size + header_size; 5034581Ssherrym ext_size = total_size - offset; 5044581Ssherrym 5054581Ssherrym if (ext_size <= 0) 5064581Ssherrym break; 5074581Ssherrym 508*7605SMark.Johnson@Sun.COM ucodefp->uf_ext_table = ucode_zalloc(cp->cpu_id, ext_size); 509*7605SMark.Johnson@Sun.COM if (ucodefp->uf_ext_table == NULL) { 510*7605SMark.Johnson@Sun.COM rc = EM_NOMEM; 511*7605SMark.Johnson@Sun.COM break; 5124581Ssherrym } 5134581Ssherrym 5144581Ssherrym if (kobj_read(fd, (char *)ucodefp->uf_ext_table, 5154581Ssherrym ext_size, offset) != ext_size) { 5164581Ssherrym rc = EM_FILESIZE; 517*7605SMark.Johnson@Sun.COM } else if (ucode_checksum_intel(0, ext_size, 5184581Ssherrym (uint8_t *)(ucodefp->uf_ext_table))) { 5194581Ssherrym rc = EM_CHECKSUM; 5204581Ssherrym } else { 5214581Ssherrym int i; 5224581Ssherrym 523*7605SMark.Johnson@Sun.COM ext_size -= UCODE_EXT_TABLE_SIZE_INTEL; 5244581Ssherrym for (i = 0; i < ucodefp->uf_ext_table->uet_count; 5254581Ssherrym i++) { 526*7605SMark.Johnson@Sun.COM if (ucode_checksum_intel(0, 527*7605SMark.Johnson@Sun.COM UCODE_EXT_SIG_SIZE_INTEL, 5284581Ssherrym (uint8_t *)(&(ucodefp->uf_ext_table-> 5294581Ssherrym uet_ext_sig[i])))) { 5304581Ssherrym rc = EM_CHECKSUM; 5314581Ssherrym break; 5324581Ssherrym } 5334581Ssherrym } 5344581Ssherrym } 5354581Ssherrym break; 5364581Ssherrym } 5374581Ssherrym 5384581Ssherrym default: 5394581Ssherrym rc = EM_FILESIZE; 5404581Ssherrym break; 5414581Ssherrym } 5424581Ssherrym 5434581Ssherrym kobj_close(fd); 5444581Ssherrym 5454581Ssherrym if (rc != EM_OK) 5464581Ssherrym return (rc); 5474581Ssherrym 548*7605SMark.Johnson@Sun.COM rc = ucode_match_intel(cpi_sig, uinfop, ucodefp->uf_header, 5494581Ssherrym ucodefp->uf_ext_table); 5504581Ssherrym 5514581Ssherrym return (rc); 5524581Ssherrym } 5534581Ssherrym 554*7605SMark.Johnson@Sun.COM static ucode_errno_t 555*7605SMark.Johnson@Sun.COM ucode_match_amd(int eq_sig, cpu_ucode_info_t *uinfop, ucode_file_amd_t *ucodefp, 556*7605SMark.Johnson@Sun.COM int size) 557*7605SMark.Johnson@Sun.COM { 558*7605SMark.Johnson@Sun.COM ucode_header_amd_t *uh; 559*7605SMark.Johnson@Sun.COM 560*7605SMark.Johnson@Sun.COM if (ucodefp == NULL || size < sizeof (ucode_header_amd_t)) 561*7605SMark.Johnson@Sun.COM return (EM_NOMATCH); 562*7605SMark.Johnson@Sun.COM 563*7605SMark.Johnson@Sun.COM /* 564*7605SMark.Johnson@Sun.COM * Don't even think about loading patches that would require code 565*7605SMark.Johnson@Sun.COM * execution. 566*7605SMark.Johnson@Sun.COM */ 567*7605SMark.Johnson@Sun.COM if (size > offsetof(ucode_file_amd_t, uf_code_present) && 568*7605SMark.Johnson@Sun.COM ucodefp->uf_code_present) 569*7605SMark.Johnson@Sun.COM return (EM_NOMATCH); 570*7605SMark.Johnson@Sun.COM 571*7605SMark.Johnson@Sun.COM uh = &ucodefp->uf_header; 572*7605SMark.Johnson@Sun.COM 573*7605SMark.Johnson@Sun.COM if (eq_sig != uh->uh_cpu_rev) 574*7605SMark.Johnson@Sun.COM return (EM_NOMATCH); 575*7605SMark.Johnson@Sun.COM 576*7605SMark.Johnson@Sun.COM if (uh->uh_nb_id) { 577*7605SMark.Johnson@Sun.COM cmn_err(CE_WARN, "ignoring northbridge-specific ucode: " 578*7605SMark.Johnson@Sun.COM "chipset id %x, revision %x", uh->uh_nb_id, uh->uh_nb_rev); 579*7605SMark.Johnson@Sun.COM return (EM_NOMATCH); 580*7605SMark.Johnson@Sun.COM } 581*7605SMark.Johnson@Sun.COM 582*7605SMark.Johnson@Sun.COM if (uh->uh_sb_id) { 583*7605SMark.Johnson@Sun.COM cmn_err(CE_WARN, "ignoring southbridge-specific ucode: " 584*7605SMark.Johnson@Sun.COM "chipset id %x, revision %x", uh->uh_sb_id, uh->uh_sb_rev); 585*7605SMark.Johnson@Sun.COM return (EM_NOMATCH); 586*7605SMark.Johnson@Sun.COM } 587*7605SMark.Johnson@Sun.COM 588*7605SMark.Johnson@Sun.COM if (uh->uh_patch_id <= uinfop->cui_rev) 589*7605SMark.Johnson@Sun.COM return (EM_HIGHERREV); 590*7605SMark.Johnson@Sun.COM 591*7605SMark.Johnson@Sun.COM return (EM_OK); 592*7605SMark.Johnson@Sun.COM } 5934581Ssherrym 5944581Ssherrym /* 5954581Ssherrym * Returns 1 if the microcode is for this processor; 0 otherwise. 5964581Ssherrym */ 5974581Ssherrym static ucode_errno_t 598*7605SMark.Johnson@Sun.COM ucode_match_intel(int cpi_sig, cpu_ucode_info_t *uinfop, 599*7605SMark.Johnson@Sun.COM ucode_header_intel_t *uhp, ucode_ext_table_intel_t *uetp) 6004581Ssherrym { 601*7605SMark.Johnson@Sun.COM if (uhp == NULL) 602*7605SMark.Johnson@Sun.COM return (EM_NOMATCH); 6034581Ssherrym 604*7605SMark.Johnson@Sun.COM if (UCODE_MATCH_INTEL(cpi_sig, uhp->uh_signature, 6054581Ssherrym uinfop->cui_platid, uhp->uh_proc_flags)) { 6064581Ssherrym 6074581Ssherrym if (uinfop->cui_rev >= uhp->uh_rev && !ucode_force_update) 6084581Ssherrym return (EM_HIGHERREV); 6094581Ssherrym 6104581Ssherrym return (EM_OK); 6114581Ssherrym } 6124581Ssherrym 6134581Ssherrym if (uetp != NULL) { 6144581Ssherrym int i; 6154581Ssherrym 6164581Ssherrym for (i = 0; i < uetp->uet_count; i++) { 617*7605SMark.Johnson@Sun.COM ucode_ext_sig_intel_t *uesp; 6184581Ssherrym 6194581Ssherrym uesp = &uetp->uet_ext_sig[i]; 6204581Ssherrym 621*7605SMark.Johnson@Sun.COM if (UCODE_MATCH_INTEL(cpi_sig, uesp->ues_signature, 6224581Ssherrym uinfop->cui_platid, uesp->ues_proc_flags)) { 6234581Ssherrym 6244581Ssherrym if (uinfop->cui_rev >= uhp->uh_rev && 6254581Ssherrym !ucode_force_update) 6264581Ssherrym return (EM_HIGHERREV); 6274581Ssherrym 6284581Ssherrym return (EM_OK); 6294581Ssherrym } 6304581Ssherrym } 6314581Ssherrym } 6324581Ssherrym 6334581Ssherrym return (EM_NOMATCH); 6344581Ssherrym } 6354581Ssherrym 6364581Ssherrym /*ARGSUSED*/ 6374581Ssherrym static int 6384581Ssherrym ucode_write(xc_arg_t arg1, xc_arg_t unused2, xc_arg_t unused3) 6394581Ssherrym { 640*7605SMark.Johnson@Sun.COM ucode_update_t *uusp = (ucode_update_t *)arg1; 641*7605SMark.Johnson@Sun.COM cpu_ucode_info_t *uinfop = CPU->cpu_m.mcpu_ucode_info; 6424581Ssherrym 643*7605SMark.Johnson@Sun.COM ASSERT(ucode); 6444581Ssherrym ASSERT(uusp->ucodep); 6454581Ssherrym 6467347SMark.Johnson@Sun.COM #ifndef __xpv 6474581Ssherrym /* 6484581Ssherrym * Check one more time to see if it is really necessary to update 6494581Ssherrym * microcode just in case this is a hyperthreaded processor where 6504581Ssherrym * the threads share the same microcode. 6514581Ssherrym */ 6524581Ssherrym if (!ucode_force_update) { 653*7605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 6544581Ssherrym uusp->new_rev = uinfop->cui_rev; 6554581Ssherrym if (uinfop->cui_rev >= uusp->expected_rev) 6564581Ssherrym return (0); 6574581Ssherrym } 6584581Ssherrym 659*7605SMark.Johnson@Sun.COM wrmsr(ucode->write_msr, (uintptr_t)uusp->ucodep); 6607347SMark.Johnson@Sun.COM #endif 661*7605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 6624581Ssherrym uusp->new_rev = uinfop->cui_rev; 6634581Ssherrym 6644581Ssherrym return (0); 6654581Ssherrym } 6664581Ssherrym 667*7605SMark.Johnson@Sun.COM /*ARGSUSED*/ 668*7605SMark.Johnson@Sun.COM static uint32_t 669*7605SMark.Johnson@Sun.COM ucode_load_amd(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp) 670*7605SMark.Johnson@Sun.COM { 671*7605SMark.Johnson@Sun.COM ucode_file_amd_t *ucodefp = ufp->amd; 672*7605SMark.Johnson@Sun.COM #ifdef __xpv 673*7605SMark.Johnson@Sun.COM ucode_update_t uus; 674*7605SMark.Johnson@Sun.COM #endif 6754581Ssherrym 676*7605SMark.Johnson@Sun.COM ASSERT(ucode); 677*7605SMark.Johnson@Sun.COM ASSERT(ucodefp); 678*7605SMark.Johnson@Sun.COM 679*7605SMark.Johnson@Sun.COM #ifndef __xpv 680*7605SMark.Johnson@Sun.COM kpreempt_disable(); 681*7605SMark.Johnson@Sun.COM wrmsr(ucode->write_msr, (uintptr_t)ucodefp); 682*7605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 683*7605SMark.Johnson@Sun.COM kpreempt_enable(); 684*7605SMark.Johnson@Sun.COM #else 685*7605SMark.Johnson@Sun.COM uus.ucodep = (uint8_t *)ucodefp; 686*7605SMark.Johnson@Sun.COM uus.usize = sizeof (*ucodefp); 687*7605SMark.Johnson@Sun.COM ucode_load_xpv(&uus); 688*7605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 689*7605SMark.Johnson@Sun.COM uus.new_rev = uinfop->cui_rev; 690*7605SMark.Johnson@Sun.COM #endif 691*7605SMark.Johnson@Sun.COM 692*7605SMark.Johnson@Sun.COM return (ucodefp->uf_header.uh_patch_id); 693*7605SMark.Johnson@Sun.COM } 694*7605SMark.Johnson@Sun.COM 695*7605SMark.Johnson@Sun.COM /*ARGSUSED2*/ 696*7605SMark.Johnson@Sun.COM static uint32_t 697*7605SMark.Johnson@Sun.COM ucode_load_intel(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp) 6984581Ssherrym { 699*7605SMark.Johnson@Sun.COM ucode_file_intel_t *ucodefp = &ufp->intel; 700*7605SMark.Johnson@Sun.COM #ifdef __xpv 701*7605SMark.Johnson@Sun.COM uint32_t ext_offset; 702*7605SMark.Johnson@Sun.COM uint32_t body_size; 703*7605SMark.Johnson@Sun.COM uint32_t ext_size; 704*7605SMark.Johnson@Sun.COM uint8_t *ustart; 705*7605SMark.Johnson@Sun.COM uint32_t usize; 706*7605SMark.Johnson@Sun.COM ucode_update_t uus; 707*7605SMark.Johnson@Sun.COM #endif 708*7605SMark.Johnson@Sun.COM 709*7605SMark.Johnson@Sun.COM ASSERT(ucode); 710*7605SMark.Johnson@Sun.COM 711*7605SMark.Johnson@Sun.COM #ifdef __xpv 712*7605SMark.Johnson@Sun.COM /* 713*7605SMark.Johnson@Sun.COM * the hypervisor wants the header, data, and extended 714*7605SMark.Johnson@Sun.COM * signature tables. We can only get here from the boot 715*7605SMark.Johnson@Sun.COM * CPU (cpu #0), we don't need to free as ucode_zalloc() will 716*7605SMark.Johnson@Sun.COM * use BOP_ALLOC(). 717*7605SMark.Johnson@Sun.COM */ 718*7605SMark.Johnson@Sun.COM usize = UCODE_TOTAL_SIZE_INTEL(ucodefp->uf_header->uh_total_size); 719*7605SMark.Johnson@Sun.COM ustart = ucode_zalloc(cp->cpu_id, usize); 720*7605SMark.Johnson@Sun.COM ASSERT(ustart); 721*7605SMark.Johnson@Sun.COM 722*7605SMark.Johnson@Sun.COM body_size = UCODE_BODY_SIZE_INTEL(ucodefp->uf_header->uh_body_size); 723*7605SMark.Johnson@Sun.COM ext_offset = body_size + UCODE_HEADER_SIZE_INTEL; 724*7605SMark.Johnson@Sun.COM ext_size = usize - ext_offset; 725*7605SMark.Johnson@Sun.COM ASSERT(ext_size >= 0); 726*7605SMark.Johnson@Sun.COM 727*7605SMark.Johnson@Sun.COM (void) memcpy(ustart, ucodefp->uf_header, UCODE_HEADER_SIZE_INTEL); 728*7605SMark.Johnson@Sun.COM (void) memcpy(&ustart[UCODE_HEADER_SIZE_INTEL], ucodefp->uf_body, 729*7605SMark.Johnson@Sun.COM body_size); 730*7605SMark.Johnson@Sun.COM if (ext_size > 0) { 731*7605SMark.Johnson@Sun.COM (void) memcpy(&ustart[ext_offset], 732*7605SMark.Johnson@Sun.COM ucodefp->uf_ext_table, ext_size); 733*7605SMark.Johnson@Sun.COM } 734*7605SMark.Johnson@Sun.COM uus.ucodep = ustart; 735*7605SMark.Johnson@Sun.COM uus.usize = usize; 736*7605SMark.Johnson@Sun.COM ucode_load_xpv(&uus); 737*7605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 738*7605SMark.Johnson@Sun.COM uus.new_rev = uinfop->cui_rev; 739*7605SMark.Johnson@Sun.COM #else 7404581Ssherrym kpreempt_disable(); 741*7605SMark.Johnson@Sun.COM wrmsr(ucode->write_msr, (uintptr_t)ucodefp->uf_body); 742*7605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 7434581Ssherrym kpreempt_enable(); 744*7605SMark.Johnson@Sun.COM #endif 745*7605SMark.Johnson@Sun.COM 746*7605SMark.Johnson@Sun.COM return (ucodefp->uf_header->uh_rev); 7474581Ssherrym } 7484581Ssherrym 7497347SMark.Johnson@Sun.COM 7507347SMark.Johnson@Sun.COM #ifdef __xpv 7517347SMark.Johnson@Sun.COM static void 752*7605SMark.Johnson@Sun.COM ucode_load_xpv(ucode_update_t *uusp) 7537347SMark.Johnson@Sun.COM { 7547347SMark.Johnson@Sun.COM xen_platform_op_t op; 7557347SMark.Johnson@Sun.COM int e; 7567347SMark.Johnson@Sun.COM 7577347SMark.Johnson@Sun.COM ASSERT(DOMAIN_IS_INITDOMAIN(xen_info)); 7587347SMark.Johnson@Sun.COM 7597347SMark.Johnson@Sun.COM kpreempt_disable(); 7607347SMark.Johnson@Sun.COM op.cmd = XENPF_microcode_update; 7617347SMark.Johnson@Sun.COM op.interface_version = XENPF_INTERFACE_VERSION; 7627347SMark.Johnson@Sun.COM /*LINTED: constant in conditional context*/ 763*7605SMark.Johnson@Sun.COM set_xen_guest_handle(op.u.microcode.data, uusp->ucodep); 764*7605SMark.Johnson@Sun.COM op.u.microcode.length = uusp->usize; 7657347SMark.Johnson@Sun.COM e = HYPERVISOR_platform_op(&op); 7667347SMark.Johnson@Sun.COM if (e != 0) { 7677347SMark.Johnson@Sun.COM cmn_err(CE_WARN, "hypervisor failed to accept uCode update"); 7687347SMark.Johnson@Sun.COM } 7697347SMark.Johnson@Sun.COM kpreempt_enable(); 7707347SMark.Johnson@Sun.COM } 7717347SMark.Johnson@Sun.COM #endif /* __xpv */ 7727347SMark.Johnson@Sun.COM 773*7605SMark.Johnson@Sun.COM static void 774*7605SMark.Johnson@Sun.COM ucode_read_rev_amd(cpu_ucode_info_t *uinfop) 775*7605SMark.Johnson@Sun.COM { 776*7605SMark.Johnson@Sun.COM uinfop->cui_rev = rdmsr(MSR_AMD_PATCHLEVEL); 777*7605SMark.Johnson@Sun.COM } 7787347SMark.Johnson@Sun.COM 7794581Ssherrym static void 780*7605SMark.Johnson@Sun.COM ucode_read_rev_intel(cpu_ucode_info_t *uinfop) 7814581Ssherrym { 7824581Ssherrym struct cpuid_regs crs; 7834581Ssherrym 7844581Ssherrym /* 7854581Ssherrym * The Intel 64 and IA-32 Architecture Software Developer's Manual 7864581Ssherrym * recommends that MSR_INTC_UCODE_REV be loaded with 0 first, then 7874581Ssherrym * execute cpuid to guarantee the correct reading of this register. 7884581Ssherrym */ 7894581Ssherrym wrmsr(MSR_INTC_UCODE_REV, 0); 7904581Ssherrym (void) __cpuid_insn(&crs); 7914581Ssherrym uinfop->cui_rev = (rdmsr(MSR_INTC_UCODE_REV) >> INTC_UCODE_REV_SHIFT); 7924581Ssherrym } 7934581Ssherrym 794*7605SMark.Johnson@Sun.COM static ucode_errno_t 795*7605SMark.Johnson@Sun.COM ucode_extract_amd(ucode_update_t *uusp, uint8_t *ucodep, int size) 796*7605SMark.Johnson@Sun.COM { 797*7605SMark.Johnson@Sun.COM uint32_t *ptr = (uint32_t *)ucodep; 798*7605SMark.Johnson@Sun.COM ucode_eqtbl_amd_t *eqtbl; 799*7605SMark.Johnson@Sun.COM ucode_file_amd_t *ufp; 800*7605SMark.Johnson@Sun.COM int count, eq_sig; 801*7605SMark.Johnson@Sun.COM 802*7605SMark.Johnson@Sun.COM /* skip over magic number & equivalence table header */ 803*7605SMark.Johnson@Sun.COM ptr += 2; size -= 8; 804*7605SMark.Johnson@Sun.COM 805*7605SMark.Johnson@Sun.COM count = *ptr++; size -= 4; 806*7605SMark.Johnson@Sun.COM for (eqtbl = (ucode_eqtbl_amd_t *)ptr; 807*7605SMark.Johnson@Sun.COM eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != uusp->sig; 808*7605SMark.Johnson@Sun.COM eqtbl++) 809*7605SMark.Johnson@Sun.COM ; 810*7605SMark.Johnson@Sun.COM 811*7605SMark.Johnson@Sun.COM eq_sig = eqtbl->ue_equiv_cpu; 812*7605SMark.Johnson@Sun.COM eq_sig = ((eq_sig >> 8) & 0xff00) | (eq_sig & 0xff); 813*7605SMark.Johnson@Sun.COM 814*7605SMark.Johnson@Sun.COM /* No equivalent CPU id found, assume outdated microcode file. */ 815*7605SMark.Johnson@Sun.COM if (eq_sig == 0) 816*7605SMark.Johnson@Sun.COM return (EM_HIGHERREV); 817*7605SMark.Johnson@Sun.COM 818*7605SMark.Johnson@Sun.COM /* Use the first microcode patch that matches. */ 819*7605SMark.Johnson@Sun.COM do { 820*7605SMark.Johnson@Sun.COM ptr += count >> 2; size -= count; 821*7605SMark.Johnson@Sun.COM 822*7605SMark.Johnson@Sun.COM if (!size) 823*7605SMark.Johnson@Sun.COM return (EM_NOMATCH); 824*7605SMark.Johnson@Sun.COM 825*7605SMark.Johnson@Sun.COM ptr++; size -= 4; 826*7605SMark.Johnson@Sun.COM count = *ptr++; size -= 4; 827*7605SMark.Johnson@Sun.COM ufp = (ucode_file_amd_t *)ptr; 828*7605SMark.Johnson@Sun.COM } while (ucode_match_amd(eq_sig, &uusp->info, ufp, count) != EM_OK); 829*7605SMark.Johnson@Sun.COM 830*7605SMark.Johnson@Sun.COM uusp->ucodep = (uint8_t *)ufp; 831*7605SMark.Johnson@Sun.COM uusp->usize = count; 832*7605SMark.Johnson@Sun.COM uusp->expected_rev = ufp->uf_header.uh_patch_id; 833*7605SMark.Johnson@Sun.COM 834*7605SMark.Johnson@Sun.COM return (EM_OK); 835*7605SMark.Johnson@Sun.COM } 836*7605SMark.Johnson@Sun.COM 837*7605SMark.Johnson@Sun.COM static ucode_errno_t 838*7605SMark.Johnson@Sun.COM ucode_extract_intel(ucode_update_t *uusp, uint8_t *ucodep, int size) 839*7605SMark.Johnson@Sun.COM { 840*7605SMark.Johnson@Sun.COM uint32_t header_size = UCODE_HEADER_SIZE_INTEL; 841*7605SMark.Johnson@Sun.COM int remaining; 842*7605SMark.Johnson@Sun.COM int found = 0; 843*7605SMark.Johnson@Sun.COM ucode_errno_t search_rc = EM_NOMATCH; /* search result */ 844*7605SMark.Johnson@Sun.COM 845*7605SMark.Johnson@Sun.COM /* 846*7605SMark.Johnson@Sun.COM * Go through the whole buffer in case there are 847*7605SMark.Johnson@Sun.COM * multiple versions of matching microcode for this 848*7605SMark.Johnson@Sun.COM * processor. 849*7605SMark.Johnson@Sun.COM */ 850*7605SMark.Johnson@Sun.COM for (remaining = size; remaining > 0; ) { 851*7605SMark.Johnson@Sun.COM int total_size, body_size, ext_size; 852*7605SMark.Johnson@Sun.COM uint8_t *curbuf = &ucodep[size - remaining]; 853*7605SMark.Johnson@Sun.COM ucode_header_intel_t *uhp = (ucode_header_intel_t *)curbuf; 854*7605SMark.Johnson@Sun.COM ucode_ext_table_intel_t *uetp = NULL; 855*7605SMark.Johnson@Sun.COM ucode_errno_t tmprc; 856*7605SMark.Johnson@Sun.COM 857*7605SMark.Johnson@Sun.COM total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size); 858*7605SMark.Johnson@Sun.COM body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size); 859*7605SMark.Johnson@Sun.COM ext_size = total_size - (header_size + body_size); 860*7605SMark.Johnson@Sun.COM 861*7605SMark.Johnson@Sun.COM if (ext_size > 0) 862*7605SMark.Johnson@Sun.COM uetp = (ucode_ext_table_intel_t *) 863*7605SMark.Johnson@Sun.COM &curbuf[header_size + body_size]; 864*7605SMark.Johnson@Sun.COM 865*7605SMark.Johnson@Sun.COM tmprc = ucode_match_intel(uusp->sig, &uusp->info, uhp, uetp); 866*7605SMark.Johnson@Sun.COM 867*7605SMark.Johnson@Sun.COM /* 868*7605SMark.Johnson@Sun.COM * Since we are searching through a big file 869*7605SMark.Johnson@Sun.COM * containing microcode for pretty much all the 870*7605SMark.Johnson@Sun.COM * processors, we are bound to get EM_NOMATCH 871*7605SMark.Johnson@Sun.COM * at one point. However, if we return 872*7605SMark.Johnson@Sun.COM * EM_NOMATCH to users, it will really confuse 873*7605SMark.Johnson@Sun.COM * them. Therefore, if we ever find a match of 874*7605SMark.Johnson@Sun.COM * a lower rev, we will set return code to 875*7605SMark.Johnson@Sun.COM * EM_HIGHERREV. 876*7605SMark.Johnson@Sun.COM */ 877*7605SMark.Johnson@Sun.COM if (tmprc == EM_HIGHERREV) 878*7605SMark.Johnson@Sun.COM search_rc = EM_HIGHERREV; 879*7605SMark.Johnson@Sun.COM 880*7605SMark.Johnson@Sun.COM if (tmprc == EM_OK && 881*7605SMark.Johnson@Sun.COM uusp->expected_rev < uhp->uh_rev) { 882*7605SMark.Johnson@Sun.COM #ifndef __xpv 883*7605SMark.Johnson@Sun.COM uusp->ucodep = (uint8_t *)&curbuf[header_size]; 884*7605SMark.Johnson@Sun.COM #else 885*7605SMark.Johnson@Sun.COM uusp->ucodep = (uint8_t *)curbuf; 886*7605SMark.Johnson@Sun.COM #endif 887*7605SMark.Johnson@Sun.COM uusp->usize = 888*7605SMark.Johnson@Sun.COM UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size); 889*7605SMark.Johnson@Sun.COM uusp->expected_rev = uhp->uh_rev; 890*7605SMark.Johnson@Sun.COM found = 1; 891*7605SMark.Johnson@Sun.COM } 892*7605SMark.Johnson@Sun.COM 893*7605SMark.Johnson@Sun.COM remaining -= total_size; 894*7605SMark.Johnson@Sun.COM } 895*7605SMark.Johnson@Sun.COM 896*7605SMark.Johnson@Sun.COM if (!found) 897*7605SMark.Johnson@Sun.COM return (search_rc); 898*7605SMark.Johnson@Sun.COM 899*7605SMark.Johnson@Sun.COM return (EM_OK); 900*7605SMark.Johnson@Sun.COM } 9014581Ssherrym /* 9024581Ssherrym * Entry point to microcode update from the ucode_drv driver. 9034581Ssherrym * 9044581Ssherrym * Returns EM_OK on success, corresponding error code on failure. 9054581Ssherrym */ 9064581Ssherrym ucode_errno_t 9074581Ssherrym ucode_update(uint8_t *ucodep, int size) 9084581Ssherrym { 9094581Ssherrym int found = 0; 9104581Ssherrym processorid_t id; 911*7605SMark.Johnson@Sun.COM ucode_update_t cached = { 0 }; 912*7605SMark.Johnson@Sun.COM ucode_update_t *cachedp = NULL; 9134581Ssherrym ucode_errno_t rc = EM_OK; 9144581Ssherrym ucode_errno_t search_rc = EM_NOMATCH; /* search result */ 9154581Ssherrym cpuset_t cpuset; 9164581Ssherrym 917*7605SMark.Johnson@Sun.COM ASSERT(ucode); 9184581Ssherrym ASSERT(ucodep); 9194581Ssherrym CPUSET_ZERO(cpuset); 9204581Ssherrym 921*7605SMark.Johnson@Sun.COM if (!ucode->capable(CPU)) 9224581Ssherrym return (EM_NOTSUP); 9234581Ssherrym 9244581Ssherrym mutex_enter(&cpu_lock); 9254581Ssherrym 9264581Ssherrym for (id = 0; id < max_ncpus; id++) { 9274581Ssherrym cpu_t *cpu; 928*7605SMark.Johnson@Sun.COM ucode_update_t uus = { 0 }; 929*7605SMark.Johnson@Sun.COM ucode_update_t *uusp = &uus; 9304581Ssherrym 9314581Ssherrym /* 9324581Ssherrym * If there is no such CPU or it is not xcall ready, skip it. 9334581Ssherrym */ 9344581Ssherrym if ((cpu = cpu_get(id)) == NULL || 9354581Ssherrym !(cpu->cpu_flags & CPU_READY)) 9364581Ssherrym continue; 9374581Ssherrym 9384581Ssherrym uusp->sig = cpuid_getsig(cpu); 9394581Ssherrym bcopy(cpu->cpu_m.mcpu_ucode_info, &uusp->info, 9404581Ssherrym sizeof (uusp->info)); 9414581Ssherrym 9424581Ssherrym /* 9434581Ssherrym * If the current CPU has the same signature and platform 9444581Ssherrym * id as the previous one we processed, reuse the information. 9454581Ssherrym */ 9464581Ssherrym if (cachedp && cachedp->sig == cpuid_getsig(cpu) && 9474581Ssherrym cachedp->info.cui_platid == uusp->info.cui_platid) { 9484581Ssherrym uusp->ucodep = cachedp->ucodep; 9494581Ssherrym uusp->expected_rev = cachedp->expected_rev; 9504581Ssherrym /* 9514581Ssherrym * Intuitively we should check here to see whether the 9524581Ssherrym * running microcode rev is >= the expected rev, and 9534581Ssherrym * quit if it is. But we choose to proceed with the 9544581Ssherrym * xcall regardless of the running version so that 9554581Ssherrym * the other threads in an HT processor can update 9564581Ssherrym * the cpu_ucode_info structure in machcpu. 9574581Ssherrym */ 958*7605SMark.Johnson@Sun.COM } else if ((search_rc = ucode->extract(uusp, ucodep, size)) 959*7605SMark.Johnson@Sun.COM == EM_OK) { 960*7605SMark.Johnson@Sun.COM bcopy(uusp, &cached, sizeof (cached)); 961*7605SMark.Johnson@Sun.COM cachedp = &cached; 962*7605SMark.Johnson@Sun.COM found = 1; 9634581Ssherrym } 9644581Ssherrym 9654581Ssherrym /* Nothing to do */ 9664581Ssherrym if (uusp->ucodep == NULL) 9674581Ssherrym continue; 9684581Ssherrym 9697347SMark.Johnson@Sun.COM #ifdef __xpv 9707347SMark.Johnson@Sun.COM /* 9717347SMark.Johnson@Sun.COM * for i86xpv, the hypervisor will update all the CPUs. 9727347SMark.Johnson@Sun.COM * the hypervisor wants the header, data, and extended 9737347SMark.Johnson@Sun.COM * signature tables. ucode_write will just read in the 9747347SMark.Johnson@Sun.COM * updated version on all the CPUs after the update has 9757347SMark.Johnson@Sun.COM * completed. 9767347SMark.Johnson@Sun.COM */ 9777402SMark.Johnson@Sun.COM if (id == 0) { 978*7605SMark.Johnson@Sun.COM ucode_load_xpv(uusp); 9797402SMark.Johnson@Sun.COM } 9807347SMark.Johnson@Sun.COM #endif 9817347SMark.Johnson@Sun.COM 9824581Ssherrym CPUSET_ADD(cpuset, id); 9834581Ssherrym kpreempt_disable(); 9844581Ssherrym xc_sync((xc_arg_t)uusp, 0, 0, X_CALL_HIPRI, cpuset, 9854581Ssherrym ucode_write); 9864581Ssherrym kpreempt_enable(); 9874581Ssherrym CPUSET_DEL(cpuset, id); 9884581Ssherrym 9894581Ssherrym if (uusp->expected_rev == uusp->new_rev) { 9904581Ssherrym cmn_err(CE_CONT, ucode_success_fmt, 9914581Ssherrym id, uusp->info.cui_rev, uusp->expected_rev); 9924581Ssherrym } else { 9934581Ssherrym cmn_err(CE_WARN, ucode_failure_fmt, 9944581Ssherrym id, uusp->info.cui_rev, uusp->expected_rev); 9954581Ssherrym rc = EM_UPDATE; 9964581Ssherrym } 9974581Ssherrym } 9984581Ssherrym 9994581Ssherrym mutex_exit(&cpu_lock); 10004581Ssherrym 10014581Ssherrym if (!found) 10024581Ssherrym rc = search_rc; 10034581Ssherrym 10044581Ssherrym return (rc); 10054581Ssherrym } 10064581Ssherrym 10074581Ssherrym /* 10084581Ssherrym * Initialize mcpu_ucode_info, and perform microcode update if necessary. 10094581Ssherrym * This is the entry point from boot path where pointer to CPU structure 10104581Ssherrym * is available. 10114581Ssherrym * 10124581Ssherrym * cpuid_info must be initialized before ucode_check can be called. 10134581Ssherrym */ 10144581Ssherrym void 10154581Ssherrym ucode_check(cpu_t *cp) 10164581Ssherrym { 1017*7605SMark.Johnson@Sun.COM cpu_ucode_info_t *uinfop; 10184581Ssherrym ucode_errno_t rc = EM_OK; 1019*7605SMark.Johnson@Sun.COM uint32_t new_rev = 0; 10204581Ssherrym 10214581Ssherrym ASSERT(cp); 10224581Ssherrym if (cp->cpu_id == 0) 10234581Ssherrym cp->cpu_m.mcpu_ucode_info = &cpu_ucode_info0; 10244581Ssherrym 10254581Ssherrym uinfop = cp->cpu_m.mcpu_ucode_info; 10264581Ssherrym ASSERT(uinfop); 10274581Ssherrym 1028*7605SMark.Johnson@Sun.COM /* set up function pointers if not already done */ 1029*7605SMark.Johnson@Sun.COM if (!ucode) 1030*7605SMark.Johnson@Sun.COM switch (cpuid_getvendor(cp)) { 1031*7605SMark.Johnson@Sun.COM case X86_VENDOR_AMD: 1032*7605SMark.Johnson@Sun.COM ucode = &ucode_amd; 1033*7605SMark.Johnson@Sun.COM break; 1034*7605SMark.Johnson@Sun.COM case X86_VENDOR_Intel: 1035*7605SMark.Johnson@Sun.COM ucode = &ucode_intel; 1036*7605SMark.Johnson@Sun.COM break; 1037*7605SMark.Johnson@Sun.COM default: 1038*7605SMark.Johnson@Sun.COM return; 1039*7605SMark.Johnson@Sun.COM } 1040*7605SMark.Johnson@Sun.COM 1041*7605SMark.Johnson@Sun.COM if (!ucode->capable(cp)) 10424581Ssherrym return; 10434581Ssherrym 10444581Ssherrym /* 10454581Ssherrym * The MSR_INTC_PLATFORM_ID is supported in Celeron and Xeon 10464581Ssherrym * (Family 6, model 5 and above) and all processors after. 10474581Ssherrym */ 1048*7605SMark.Johnson@Sun.COM if ((cpuid_getvendor(cp) == X86_VENDOR_Intel) && 1049*7605SMark.Johnson@Sun.COM ((cpuid_getmodel(cp) >= 5) || (cpuid_getfamily(cp) > 6))) { 10504581Ssherrym uinfop->cui_platid = 1 << ((rdmsr(MSR_INTC_PLATFORM_ID) >> 10514581Ssherrym INTC_PLATFORM_ID_SHIFT) & INTC_PLATFORM_ID_MASK); 10524581Ssherrym } 10534581Ssherrym 1054*7605SMark.Johnson@Sun.COM ucode->read_rev(uinfop); 10554581Ssherrym 10567402SMark.Johnson@Sun.COM #ifdef __xpv 10577402SMark.Johnson@Sun.COM /* 10587402SMark.Johnson@Sun.COM * for i86xpv, the hypervisor will update all the CPUs. We only need 10597402SMark.Johnson@Sun.COM * do do this on one of the CPUs (and there always is a CPU 0). 10607402SMark.Johnson@Sun.COM */ 10617402SMark.Johnson@Sun.COM if (cp->cpu_id != 0) { 10627402SMark.Johnson@Sun.COM return; 10637402SMark.Johnson@Sun.COM } 10647402SMark.Johnson@Sun.COM #endif 10657402SMark.Johnson@Sun.COM 10664581Ssherrym /* 10674581Ssherrym * Check to see if we need ucode update 10684581Ssherrym */ 1069*7605SMark.Johnson@Sun.COM if ((rc = ucode->locate(cp, uinfop, &ucodefile)) == EM_OK) { 1070*7605SMark.Johnson@Sun.COM new_rev = ucode->load(&ucodefile, uinfop, cp); 10717347SMark.Johnson@Sun.COM 1072*7605SMark.Johnson@Sun.COM if (uinfop->cui_rev != new_rev) 10734581Ssherrym cmn_err(CE_WARN, ucode_failure_fmt, cp->cpu_id, 1074*7605SMark.Johnson@Sun.COM uinfop->cui_rev, new_rev); 10754581Ssherrym } 10764581Ssherrym 10774581Ssherrym /* 10784581Ssherrym * If we fail to find a match for any reason, free the file structure 10794581Ssherrym * just in case we have read in a partial file. 10804581Ssherrym * 10814581Ssherrym * Since the scratch memory for holding the microcode for the boot CPU 10824581Ssherrym * came from BOP_ALLOC, we will reset the data structure as if we 10834581Ssherrym * never did the allocation so we don't have to keep track of this 10844581Ssherrym * special chunk of memory. We free the memory used for the rest 10854581Ssherrym * of the CPUs in start_other_cpus(). 10864581Ssherrym */ 10874581Ssherrym if (rc != EM_OK || cp->cpu_id == 0) 1088*7605SMark.Johnson@Sun.COM ucode->file_reset(&ucodefile, cp->cpu_id); 10894581Ssherrym } 10904581Ssherrym 10914581Ssherrym /* 10924581Ssherrym * Returns microcode revision from the machcpu structure. 10934581Ssherrym */ 10944581Ssherrym ucode_errno_t 10954581Ssherrym ucode_get_rev(uint32_t *revp) 10964581Ssherrym { 10974581Ssherrym int i; 10984581Ssherrym 1099*7605SMark.Johnson@Sun.COM ASSERT(ucode); 11004581Ssherrym ASSERT(revp); 11014581Ssherrym 1102*7605SMark.Johnson@Sun.COM if (!ucode->capable(CPU)) 11034581Ssherrym return (EM_NOTSUP); 11044581Ssherrym 11054581Ssherrym mutex_enter(&cpu_lock); 11064581Ssherrym for (i = 0; i < max_ncpus; i++) { 11074581Ssherrym cpu_t *cpu; 11084581Ssherrym 11094581Ssherrym if ((cpu = cpu_get(i)) == NULL) 11104581Ssherrym continue; 11114581Ssherrym 11124581Ssherrym revp[i] = cpu->cpu_m.mcpu_ucode_info->cui_rev; 11134581Ssherrym } 11144581Ssherrym mutex_exit(&cpu_lock); 11154581Ssherrym 11164581Ssherrym return (EM_OK); 11174581Ssherrym } 1118