14581Ssherrym /* 24581Ssherrym * CDDL HEADER START 34581Ssherrym * 44581Ssherrym * The contents of this file are subject to the terms of the 54581Ssherrym * Common Development and Distribution License (the "License"). 64581Ssherrym * You may not use this file except in compliance with the License. 74581Ssherrym * 84581Ssherrym * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 94581Ssherrym * or http://www.opensolaris.org/os/licensing. 104581Ssherrym * See the License for the specific language governing permissions 114581Ssherrym * and limitations under the License. 124581Ssherrym * 134581Ssherrym * When distributing Covered Code, include this CDDL HEADER in each 144581Ssherrym * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 154581Ssherrym * If applicable, add the following below this CDDL HEADER, with the 164581Ssherrym * fields enclosed by brackets "[]" replaced with your own identifying 174581Ssherrym * information: Portions Copyright [yyyy] [name of copyright owner] 184581Ssherrym * 194581Ssherrym * CDDL HEADER END 204581Ssherrym */ 214581Ssherrym 224581Ssherrym /* 23*6519Ssherrym * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 244581Ssherrym * Use is subject to license terms. 254581Ssherrym */ 264581Ssherrym 274581Ssherrym #pragma ident "%Z%%M% %I% %E% SMI" 284581Ssherrym 294581Ssherrym #include <sys/asm_linkage.h> 304581Ssherrym #include <sys/bootconf.h> 314581Ssherrym #include <sys/cpuvar.h> 324581Ssherrym #include <sys/cmn_err.h> 334581Ssherrym #include <sys/controlregs.h> 344581Ssherrym #include <sys/debug.h> 354581Ssherrym #include <sys/kobj.h> 364581Ssherrym #include <sys/kobj_impl.h> 374581Ssherrym #include <sys/machsystm.h> 384581Ssherrym #include <sys/param.h> 394581Ssherrym #include <sys/machparam.h> 404581Ssherrym #include <sys/promif.h> 414581Ssherrym #include <sys/sysmacros.h> 424581Ssherrym #include <sys/systm.h> 434581Ssherrym #include <sys/types.h> 444581Ssherrym #include <sys/thread.h> 454581Ssherrym #include <sys/ucode.h> 464581Ssherrym #include <sys/x86_archext.h> 474581Ssherrym #include <sys/x_call.h> 485084Sjohnlev #ifdef __xpv 495084Sjohnlev #include <sys/hypervisor.h> 505084Sjohnlev #endif 514581Ssherrym 524581Ssherrym /* 534581Ssherrym * Microcode specific information per core 544581Ssherrym */ 554581Ssherrym struct cpu_ucode_info { 564581Ssherrym uint32_t cui_platid; /* platform id */ 574581Ssherrym uint32_t cui_rev; /* microcode revision */ 584581Ssherrym }; 594581Ssherrym 604581Ssherrym /* 614581Ssherrym * Data structure used for xcall 624581Ssherrym */ 634581Ssherrym struct ucode_update_struct { 644581Ssherrym uint32_t sig; /* signature */ 654581Ssherrym struct cpu_ucode_info info; /* ucode info */ 664581Ssherrym uint32_t expected_rev; 674581Ssherrym uint32_t new_rev; 684581Ssherrym uint8_t *ucodep; /* pointer to ucode body */ 694581Ssherrym }; 704581Ssherrym 714581Ssherrym /* 724581Ssherrym * mcpu_ucode_info for the boot CPU. Statically allocated. 734581Ssherrym */ 744581Ssherrym static struct cpu_ucode_info cpu_ucode_info0; 754581Ssherrym 764581Ssherrym static ucode_file_t ucodefile = { 0 }; 774581Ssherrym 784581Ssherrym static int ucode_capable(cpu_t *); 794581Ssherrym static void ucode_file_reset(ucode_file_t *, processorid_t); 804581Ssherrym static ucode_errno_t ucode_match(int, struct cpu_ucode_info *, 814581Ssherrym ucode_header_t *, ucode_ext_table_t *); 824581Ssherrym static ucode_errno_t ucode_locate(cpu_t *, struct cpu_ucode_info *, 834581Ssherrym ucode_file_t *); 844581Ssherrym static void ucode_update_intel(uint8_t *, struct cpu_ucode_info *); 854581Ssherrym static void ucode_read_rev(struct cpu_ucode_info *); 864581Ssherrym 874581Ssherrym static const char ucode_failure_fmt[] = 88*6519Ssherrym "cpu%d: failed to update microcode from version 0x%x to 0x%x\n"; 894581Ssherrym static const char ucode_success_fmt[] = 90*6519Ssherrym "?cpu%d: microcode has been updated from version 0x%x to 0x%x\n"; 914581Ssherrym 924581Ssherrym /* 934581Ssherrym * Force flag. If set, the first microcode binary that matches 944581Ssherrym * signature and platform id will be used for microcode update, 954581Ssherrym * regardless of version. Should only be used for debugging. 964581Ssherrym */ 974581Ssherrym int ucode_force_update = 0; 984581Ssherrym 994581Ssherrym /* 1004581Ssherrym * Allocate space for mcpu_ucode_info in the machcpu structure 1014581Ssherrym * for all non-boot CPUs. 1024581Ssherrym */ 1034581Ssherrym void 1044581Ssherrym ucode_alloc_space(cpu_t *cp) 1054581Ssherrym { 1064581Ssherrym ASSERT(cp->cpu_id != 0); 1074581Ssherrym cp->cpu_m.mcpu_ucode_info = 1084581Ssherrym kmem_zalloc(sizeof (*cp->cpu_m.mcpu_ucode_info), KM_SLEEP); 1094581Ssherrym } 1104581Ssherrym 1114581Ssherrym void 1124581Ssherrym ucode_free_space(cpu_t *cp) 1134581Ssherrym { 1144581Ssherrym ASSERT(cp->cpu_id != 0); 1154581Ssherrym kmem_free(cp->cpu_m.mcpu_ucode_info, 1164581Ssherrym sizeof (*cp->cpu_m.mcpu_ucode_info)); 1174581Ssherrym } 1184581Ssherrym 1194581Ssherrym /* 1204581Ssherrym * Called when we are done with microcode update on all processors to free up 1214581Ssherrym * space allocated for the microcode file. 1224581Ssherrym */ 1234581Ssherrym void 1244581Ssherrym ucode_free() 1254581Ssherrym { 1264581Ssherrym ucode_file_reset(&ucodefile, -1); 1274581Ssherrym } 1284581Ssherrym 1294581Ssherrym /* 1304581Ssherrym * Check whether or not a processor is capable of microcode operations 1314581Ssherrym * Returns 1 if it is capable, 0 if not. 1324581Ssherrym */ 1335084Sjohnlev /*ARGSUSED*/ 1344581Ssherrym static int 1354581Ssherrym ucode_capable(cpu_t *cp) 1364581Ssherrym { 1375084Sjohnlev /* i86xpv guest domain can't update microcode */ 1385084Sjohnlev #ifdef __xpv 1395084Sjohnlev if (!DOMAIN_IS_INITDOMAIN(xen_info)) { 1405084Sjohnlev return (0); 1415084Sjohnlev } 1425084Sjohnlev #endif 1435084Sjohnlev 1445084Sjohnlev #ifndef __xpv 1454581Ssherrym /* 1464581Ssherrym * At this point we only support microcode update for Intel 1474581Ssherrym * processors family 6 and above. 1484581Ssherrym * 1494581Ssherrym * We also assume that we don't support a mix of Intel and 1504581Ssherrym * AMD processors in the same box. 1514581Ssherrym */ 1524581Ssherrym if (cpuid_getvendor(cp) != X86_VENDOR_Intel || 1534581Ssherrym cpuid_getfamily(cp) < 6) 1544581Ssherrym return (0); 1554581Ssherrym else 1564581Ssherrym return (1); 1575084Sjohnlev #else 1585084Sjohnlev /* 1595084Sjohnlev * XXPV - remove when microcode loading works in dom0. Don't support 1605084Sjohnlev * microcode loading in dom0 right now. 1615084Sjohnlev */ 1625084Sjohnlev return (0); 1635084Sjohnlev #endif 1644581Ssherrym } 1654581Ssherrym 1664581Ssherrym /* 1674581Ssherrym * Called when it is no longer necessary to keep the microcode around, 1684581Ssherrym * or when the cached microcode doesn't match the CPU being processed. 1694581Ssherrym */ 1704581Ssherrym static void 1714581Ssherrym ucode_file_reset(ucode_file_t *ucodefp, processorid_t id) 1724581Ssherrym { 1734581Ssherrym int total_size, body_size; 1744581Ssherrym 1754581Ssherrym if (ucodefp == NULL) 1764581Ssherrym return; 1774581Ssherrym 1784581Ssherrym total_size = UCODE_TOTAL_SIZE(ucodefp->uf_header.uh_total_size); 1794581Ssherrym body_size = UCODE_BODY_SIZE(ucodefp->uf_header.uh_body_size); 1804581Ssherrym if (ucodefp->uf_body) { 1814581Ssherrym /* 1824581Ssherrym * Space for the boot CPU is allocated with BOP_ALLOC() 1834581Ssherrym * and does not require a free. 1844581Ssherrym */ 1854581Ssherrym if (id != 0) 1864581Ssherrym kmem_free(ucodefp->uf_body, body_size); 1874581Ssherrym ucodefp->uf_body = NULL; 1884581Ssherrym } 1894581Ssherrym 1904581Ssherrym if (ucodefp->uf_ext_table) { 1914581Ssherrym int size = total_size - body_size - UCODE_HEADER_SIZE; 1924581Ssherrym /* 1934581Ssherrym * Space for the boot CPU is allocated with BOP_ALLOC() 1944581Ssherrym * and does not require a free. 1954581Ssherrym */ 1964581Ssherrym if (id != 0) 1974581Ssherrym kmem_free(ucodefp->uf_ext_table, size); 1984581Ssherrym ucodefp->uf_ext_table = NULL; 1994581Ssherrym } 2004581Ssherrym 2014581Ssherrym bzero(&ucodefp->uf_header, UCODE_HEADER_SIZE); 2024581Ssherrym } 2034581Ssherrym 2044581Ssherrym /* 2054581Ssherrym * Populate the ucode file structure from microcode file corresponding to 2064581Ssherrym * this CPU, if exists. 2074581Ssherrym * 2084581Ssherrym * Return EM_OK on success, corresponding error code on failure. 2094581Ssherrym */ 2104581Ssherrym static ucode_errno_t 2114581Ssherrym ucode_locate(cpu_t *cp, struct cpu_ucode_info *uinfop, ucode_file_t *ucodefp) 2124581Ssherrym { 2134581Ssherrym char name[MAXPATHLEN]; 2144581Ssherrym intptr_t fd; 2154581Ssherrym int count; 2164581Ssherrym int header_size = UCODE_HEADER_SIZE; 2174581Ssherrym int cpi_sig = cpuid_getsig(cp); 2184581Ssherrym ucode_errno_t rc = EM_OK; 2194581Ssherrym 2204581Ssherrym /* 2214581Ssherrym * If the microcode matches the CPU we are processing, use it. 2224581Ssherrym */ 2234581Ssherrym if (ucode_match(cpi_sig, uinfop, &ucodefp->uf_header, 2244581Ssherrym ucodefp->uf_ext_table) == EM_OK && ucodefp->uf_body != NULL) { 2254581Ssherrym return (EM_OK); 2264581Ssherrym } 2274581Ssherrym 2284581Ssherrym /* 2294581Ssherrym * Look for microcode file with the right name. 2304581Ssherrym */ 2314581Ssherrym (void) snprintf(name, MAXPATHLEN, "/%s/%s/%08X-%02X", 2324581Ssherrym UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), cpi_sig, 2334581Ssherrym uinfop->cui_platid); 2344581Ssherrym if ((fd = kobj_open(name)) == -1) { 2354581Ssherrym return (EM_OPENFILE); 2364581Ssherrym } 2374581Ssherrym 2384581Ssherrym /* 2394581Ssherrym * We found a microcode file for the CPU we are processing, 2404581Ssherrym * reset the microcode data structure and read in the new 2414581Ssherrym * file. 2424581Ssherrym */ 2434581Ssherrym ucode_file_reset(ucodefp, cp->cpu_id); 2444581Ssherrym 2454581Ssherrym count = kobj_read(fd, (char *)&ucodefp->uf_header, header_size, 0); 2464581Ssherrym 2474581Ssherrym switch (count) { 2484581Ssherrym case UCODE_HEADER_SIZE: { 2494581Ssherrym 2504581Ssherrym ucode_header_t *uhp = &ucodefp->uf_header; 2514581Ssherrym uint32_t offset = header_size; 2524581Ssherrym int total_size, body_size, ext_size; 2534581Ssherrym uint32_t sum = 0; 2544581Ssherrym 2554581Ssherrym /* 2564581Ssherrym * Make sure that the header contains valid fields. 2574581Ssherrym */ 2584581Ssherrym if ((rc = ucode_header_validate(uhp)) == EM_OK) { 2594581Ssherrym total_size = UCODE_TOTAL_SIZE(uhp->uh_total_size); 2604581Ssherrym body_size = UCODE_BODY_SIZE(uhp->uh_body_size); 2614581Ssherrym if (cp->cpu_id != 0) { 2624581Ssherrym if ((ucodefp->uf_body = kmem_zalloc(body_size, 2634581Ssherrym KM_NOSLEEP)) == NULL) { 2644581Ssherrym rc = EM_NOMEM; 2654581Ssherrym break; 2664581Ssherrym } 2674581Ssherrym } else { 2684581Ssherrym /* 2694581Ssherrym * BOP_ALLOC() failure results in panic so we 2704581Ssherrym * don't have to check for NULL return. 2714581Ssherrym */ 2724581Ssherrym ucodefp->uf_body = 2734581Ssherrym (uint8_t *)BOP_ALLOC(bootops, 2744581Ssherrym NULL, body_size, MMU_PAGESIZE); 2754581Ssherrym } 2764581Ssherrym 2774581Ssherrym if (kobj_read(fd, (char *)ucodefp->uf_body, 2784581Ssherrym body_size, offset) != body_size) 2794581Ssherrym rc = EM_FILESIZE; 2804581Ssherrym } 2814581Ssherrym 2824581Ssherrym if (rc) 2834581Ssherrym break; 2844581Ssherrym 2854581Ssherrym sum = ucode_checksum(0, header_size, 2864581Ssherrym (uint8_t *)&ucodefp->uf_header); 2874581Ssherrym if (ucode_checksum(sum, body_size, ucodefp->uf_body)) { 2884581Ssherrym rc = EM_CHECKSUM; 2894581Ssherrym break; 2904581Ssherrym } 2914581Ssherrym 2924581Ssherrym /* 2934581Ssherrym * Check to see if there is extended signature table. 2944581Ssherrym */ 2954581Ssherrym offset = body_size + header_size; 2964581Ssherrym ext_size = total_size - offset; 2974581Ssherrym 2984581Ssherrym if (ext_size <= 0) 2994581Ssherrym break; 3004581Ssherrym 3014581Ssherrym if (cp->cpu_id != 0) { 3024581Ssherrym if ((ucodefp->uf_ext_table = kmem_zalloc(ext_size, 3034581Ssherrym KM_NOSLEEP)) == NULL) { 3044581Ssherrym rc = EM_NOMEM; 3054581Ssherrym break; 3064581Ssherrym } 3074581Ssherrym } else { 3084581Ssherrym /* 3094581Ssherrym * BOP_ALLOC() failure results in panic so we 3104581Ssherrym * don't have to check for NULL return. 3114581Ssherrym */ 3124581Ssherrym ucodefp->uf_ext_table = 3134581Ssherrym (ucode_ext_table_t *)BOP_ALLOC(bootops, NULL, 3144581Ssherrym ext_size, MMU_PAGESIZE); 3154581Ssherrym } 3164581Ssherrym 3174581Ssherrym if (kobj_read(fd, (char *)ucodefp->uf_ext_table, 3184581Ssherrym ext_size, offset) != ext_size) { 3194581Ssherrym rc = EM_FILESIZE; 3204581Ssherrym } else if (ucode_checksum(0, ext_size, 3214581Ssherrym (uint8_t *)(ucodefp->uf_ext_table))) { 3224581Ssherrym rc = EM_CHECKSUM; 3234581Ssherrym } else { 3244581Ssherrym int i; 3254581Ssherrym 3264581Ssherrym ext_size -= UCODE_EXT_TABLE_SIZE; 3274581Ssherrym for (i = 0; i < ucodefp->uf_ext_table->uet_count; 3284581Ssherrym i++) { 3294581Ssherrym if (ucode_checksum(0, UCODE_EXT_SIG_SIZE, 3304581Ssherrym (uint8_t *)(&(ucodefp->uf_ext_table-> 3314581Ssherrym uet_ext_sig[i])))) { 3324581Ssherrym rc = EM_CHECKSUM; 3334581Ssherrym break; 3344581Ssherrym } 3354581Ssherrym } 3364581Ssherrym } 3374581Ssherrym break; 3384581Ssherrym } 3394581Ssherrym 3404581Ssherrym default: 3414581Ssherrym rc = EM_FILESIZE; 3424581Ssherrym break; 3434581Ssherrym } 3444581Ssherrym 3454581Ssherrym kobj_close(fd); 3464581Ssherrym 3474581Ssherrym if (rc != EM_OK) 3484581Ssherrym return (rc); 3494581Ssherrym 3504581Ssherrym rc = ucode_match(cpi_sig, uinfop, &ucodefp->uf_header, 3514581Ssherrym ucodefp->uf_ext_table); 3524581Ssherrym 3534581Ssherrym return (rc); 3544581Ssherrym } 3554581Ssherrym 3564581Ssherrym 3574581Ssherrym /* 3584581Ssherrym * Returns 1 if the microcode is for this processor; 0 otherwise. 3594581Ssherrym */ 3604581Ssherrym static ucode_errno_t 3614581Ssherrym ucode_match(int cpi_sig, struct cpu_ucode_info *uinfop, 3624581Ssherrym ucode_header_t *uhp, ucode_ext_table_t *uetp) 3634581Ssherrym { 3644581Ssherrym ASSERT(uhp); 3654581Ssherrym 3664581Ssherrym if (UCODE_MATCH(cpi_sig, uhp->uh_signature, 3674581Ssherrym uinfop->cui_platid, uhp->uh_proc_flags)) { 3684581Ssherrym 3694581Ssherrym if (uinfop->cui_rev >= uhp->uh_rev && !ucode_force_update) 3704581Ssherrym return (EM_HIGHERREV); 3714581Ssherrym 3724581Ssherrym return (EM_OK); 3734581Ssherrym } 3744581Ssherrym 3754581Ssherrym if (uetp != NULL) { 3764581Ssherrym int i; 3774581Ssherrym 3784581Ssherrym for (i = 0; i < uetp->uet_count; i++) { 3794581Ssherrym ucode_ext_sig_t *uesp; 3804581Ssherrym 3814581Ssherrym uesp = &uetp->uet_ext_sig[i]; 3824581Ssherrym 3834581Ssherrym if (UCODE_MATCH(cpi_sig, uesp->ues_signature, 3844581Ssherrym uinfop->cui_platid, uesp->ues_proc_flags)) { 3854581Ssherrym 3864581Ssherrym if (uinfop->cui_rev >= uhp->uh_rev && 3874581Ssherrym !ucode_force_update) 3884581Ssherrym return (EM_HIGHERREV); 3894581Ssherrym 3904581Ssherrym return (EM_OK); 3914581Ssherrym } 3924581Ssherrym } 3934581Ssherrym } 3944581Ssherrym 3954581Ssherrym return (EM_NOMATCH); 3964581Ssherrym } 3974581Ssherrym 3984581Ssherrym /*ARGSUSED*/ 3994581Ssherrym static int 4004581Ssherrym ucode_write(xc_arg_t arg1, xc_arg_t unused2, xc_arg_t unused3) 4014581Ssherrym { 4024581Ssherrym struct ucode_update_struct *uusp = (struct ucode_update_struct *)arg1; 4034581Ssherrym struct cpu_ucode_info *uinfop = CPU->cpu_m.mcpu_ucode_info; 4044581Ssherrym 4054581Ssherrym ASSERT(uusp->ucodep); 4064581Ssherrym 4074581Ssherrym /* 4084581Ssherrym * Check one more time to see if it is really necessary to update 4094581Ssherrym * microcode just in case this is a hyperthreaded processor where 4104581Ssherrym * the threads share the same microcode. 4114581Ssherrym */ 4124581Ssherrym if (!ucode_force_update) { 4134581Ssherrym ucode_read_rev(uinfop); 4144581Ssherrym uusp->new_rev = uinfop->cui_rev; 4154581Ssherrym if (uinfop->cui_rev >= uusp->expected_rev) 4164581Ssherrym return (0); 4174581Ssherrym } 4184581Ssherrym 4194581Ssherrym wrmsr(MSR_INTC_UCODE_WRITE, 4204581Ssherrym (uint64_t)(intptr_t)(uusp->ucodep)); 4214581Ssherrym ucode_read_rev(uinfop); 4224581Ssherrym uusp->new_rev = uinfop->cui_rev; 4234581Ssherrym 4244581Ssherrym return (0); 4254581Ssherrym } 4264581Ssherrym 4274581Ssherrym 4284581Ssherrym static void 4294581Ssherrym ucode_update_intel(uint8_t *ucode_body, struct cpu_ucode_info *uinfop) 4304581Ssherrym { 4314581Ssherrym kpreempt_disable(); 4324581Ssherrym wrmsr(MSR_INTC_UCODE_WRITE, (uint64_t)(uintptr_t)ucode_body); 4334581Ssherrym ucode_read_rev(uinfop); 4344581Ssherrym kpreempt_enable(); 4354581Ssherrym } 4364581Ssherrym 4374581Ssherrym static void 4384581Ssherrym ucode_read_rev(struct cpu_ucode_info *uinfop) 4394581Ssherrym { 4404581Ssherrym struct cpuid_regs crs; 4414581Ssherrym 4424581Ssherrym /* 4434581Ssherrym * The Intel 64 and IA-32 Architecture Software Developer's Manual 4444581Ssherrym * recommends that MSR_INTC_UCODE_REV be loaded with 0 first, then 4454581Ssherrym * execute cpuid to guarantee the correct reading of this register. 4464581Ssherrym */ 4474581Ssherrym wrmsr(MSR_INTC_UCODE_REV, 0); 4484581Ssherrym (void) __cpuid_insn(&crs); 4494581Ssherrym uinfop->cui_rev = (rdmsr(MSR_INTC_UCODE_REV) >> INTC_UCODE_REV_SHIFT); 4504581Ssherrym } 4514581Ssherrym 4524581Ssherrym /* 4534581Ssherrym * Entry point to microcode update from the ucode_drv driver. 4544581Ssherrym * 4554581Ssherrym * Returns EM_OK on success, corresponding error code on failure. 4564581Ssherrym */ 4574581Ssherrym ucode_errno_t 4584581Ssherrym ucode_update(uint8_t *ucodep, int size) 4594581Ssherrym { 4604581Ssherrym uint32_t header_size = UCODE_HEADER_SIZE; 4614581Ssherrym int remaining; 4624581Ssherrym int found = 0; 4634581Ssherrym processorid_t id; 4644581Ssherrym struct ucode_update_struct cached = { 0 }; 4654581Ssherrym struct ucode_update_struct *cachedp = NULL; 4664581Ssherrym ucode_errno_t rc = EM_OK; 4674581Ssherrym ucode_errno_t search_rc = EM_NOMATCH; /* search result */ 4684581Ssherrym cpuset_t cpuset; 4694581Ssherrym 4704581Ssherrym ASSERT(ucodep); 4714581Ssherrym 4724581Ssherrym CPUSET_ZERO(cpuset); 4734581Ssherrym 4744581Ssherrym if (!ucode_capable(CPU)) 4754581Ssherrym return (EM_NOTSUP); 4764581Ssherrym 4774581Ssherrym mutex_enter(&cpu_lock); 4784581Ssherrym 4794581Ssherrym for (id = 0; id < max_ncpus; id++) { 4804581Ssherrym cpu_t *cpu; 4814581Ssherrym struct ucode_update_struct uus = { 0 }; 4824581Ssherrym struct ucode_update_struct *uusp = &uus; 4834581Ssherrym 4844581Ssherrym /* 4854581Ssherrym * If there is no such CPU or it is not xcall ready, skip it. 4864581Ssherrym */ 4874581Ssherrym if ((cpu = cpu_get(id)) == NULL || 4884581Ssherrym !(cpu->cpu_flags & CPU_READY)) 4894581Ssherrym continue; 4904581Ssherrym 4914581Ssherrym uusp->sig = cpuid_getsig(cpu); 4924581Ssherrym bcopy(cpu->cpu_m.mcpu_ucode_info, &uusp->info, 4934581Ssherrym sizeof (uusp->info)); 4944581Ssherrym 4954581Ssherrym /* 4964581Ssherrym * If the current CPU has the same signature and platform 4974581Ssherrym * id as the previous one we processed, reuse the information. 4984581Ssherrym */ 4994581Ssherrym if (cachedp && cachedp->sig == cpuid_getsig(cpu) && 5004581Ssherrym cachedp->info.cui_platid == uusp->info.cui_platid) { 5014581Ssherrym uusp->ucodep = cachedp->ucodep; 5024581Ssherrym uusp->expected_rev = cachedp->expected_rev; 5034581Ssherrym /* 5044581Ssherrym * Intuitively we should check here to see whether the 5054581Ssherrym * running microcode rev is >= the expected rev, and 5064581Ssherrym * quit if it is. But we choose to proceed with the 5074581Ssherrym * xcall regardless of the running version so that 5084581Ssherrym * the other threads in an HT processor can update 5094581Ssherrym * the cpu_ucode_info structure in machcpu. 5104581Ssherrym */ 5114581Ssherrym } else { 5124581Ssherrym /* 5134581Ssherrym * Go through the whole buffer in case there are 5144581Ssherrym * multiple versions of matching microcode for this 5154581Ssherrym * processor. 5164581Ssherrym */ 5174581Ssherrym for (remaining = size; remaining > 0; ) { 5184581Ssherrym int total_size, body_size, ext_size; 5194581Ssherrym uint8_t *curbuf = &ucodep[size - remaining]; 5204581Ssherrym ucode_header_t *uhp = (ucode_header_t *)curbuf; 5214581Ssherrym ucode_ext_table_t *uetp = NULL; 5224581Ssherrym ucode_errno_t tmprc; 5234581Ssherrym 5244581Ssherrym total_size = 5254581Ssherrym UCODE_TOTAL_SIZE(uhp->uh_total_size); 5264581Ssherrym body_size = UCODE_BODY_SIZE(uhp->uh_body_size); 5274581Ssherrym ext_size = total_size - 5284581Ssherrym (header_size + body_size); 5294581Ssherrym 5304581Ssherrym if (ext_size > 0) 5314581Ssherrym uetp = (ucode_ext_table_t *) 5324581Ssherrym &curbuf[header_size + body_size]; 5334581Ssherrym 5344581Ssherrym tmprc = ucode_match(uusp->sig, &uusp->info, 5354581Ssherrym uhp, uetp); 5364581Ssherrym 5374581Ssherrym /* 5384581Ssherrym * Since we are searching through a big file 5394581Ssherrym * containing microcode for pretty much all the 5404581Ssherrym * processors, we are bound to get EM_NOMATCH 5414581Ssherrym * at one point. However, if we return 5424581Ssherrym * EM_NOMATCH to users, it will really confuse 5434581Ssherrym * them. Therefore, if we ever find a match of 5444581Ssherrym * a lower rev, we will set return code to 5454581Ssherrym * EM_HIGHERREV. 5464581Ssherrym */ 5474581Ssherrym if (tmprc == EM_HIGHERREV) 5484581Ssherrym search_rc = EM_HIGHERREV; 5494581Ssherrym 5504581Ssherrym if (tmprc == EM_OK && 5514581Ssherrym uusp->expected_rev < uhp->uh_rev) { 5524581Ssherrym uusp->ucodep = &curbuf[header_size]; 5534581Ssherrym uusp->expected_rev = uhp->uh_rev; 5544581Ssherrym bcopy(uusp, &cached, sizeof (cached)); 5554581Ssherrym cachedp = &cached; 5564581Ssherrym found = 1; 5574581Ssherrym } 5584581Ssherrym 5594581Ssherrym remaining -= total_size; 5604581Ssherrym } 5614581Ssherrym } 5624581Ssherrym 5634581Ssherrym /* Nothing to do */ 5644581Ssherrym if (uusp->ucodep == NULL) 5654581Ssherrym continue; 5664581Ssherrym 5674581Ssherrym CPUSET_ADD(cpuset, id); 5684581Ssherrym kpreempt_disable(); 5694581Ssherrym xc_sync((xc_arg_t)uusp, 0, 0, X_CALL_HIPRI, cpuset, 5704581Ssherrym ucode_write); 5714581Ssherrym kpreempt_enable(); 5724581Ssherrym CPUSET_DEL(cpuset, id); 5734581Ssherrym 5744581Ssherrym if (uusp->expected_rev == uusp->new_rev) { 5754581Ssherrym cmn_err(CE_CONT, ucode_success_fmt, 5764581Ssherrym id, uusp->info.cui_rev, uusp->expected_rev); 5774581Ssherrym } else { 5784581Ssherrym cmn_err(CE_WARN, ucode_failure_fmt, 5794581Ssherrym id, uusp->info.cui_rev, uusp->expected_rev); 5804581Ssherrym rc = EM_UPDATE; 5814581Ssherrym } 5824581Ssherrym } 5834581Ssherrym 5844581Ssherrym mutex_exit(&cpu_lock); 5854581Ssherrym 5864581Ssherrym if (!found) 5874581Ssherrym rc = search_rc; 5884581Ssherrym 5894581Ssherrym return (rc); 5904581Ssherrym } 5914581Ssherrym 5924581Ssherrym /* 5934581Ssherrym * Initialize mcpu_ucode_info, and perform microcode update if necessary. 5944581Ssherrym * This is the entry point from boot path where pointer to CPU structure 5954581Ssherrym * is available. 5964581Ssherrym * 5974581Ssherrym * cpuid_info must be initialized before ucode_check can be called. 5984581Ssherrym */ 5994581Ssherrym void 6004581Ssherrym ucode_check(cpu_t *cp) 6014581Ssherrym { 6024581Ssherrym struct cpu_ucode_info *uinfop; 6034581Ssherrym ucode_errno_t rc = EM_OK; 6044581Ssherrym 6054581Ssherrym ASSERT(cp); 6064581Ssherrym if (cp->cpu_id == 0) 6074581Ssherrym cp->cpu_m.mcpu_ucode_info = &cpu_ucode_info0; 6084581Ssherrym 6094581Ssherrym uinfop = cp->cpu_m.mcpu_ucode_info; 6104581Ssherrym ASSERT(uinfop); 6114581Ssherrym 6124581Ssherrym if (!ucode_capable(cp)) 6134581Ssherrym return; 6144581Ssherrym 6154581Ssherrym /* 6164581Ssherrym * The MSR_INTC_PLATFORM_ID is supported in Celeron and Xeon 6174581Ssherrym * (Family 6, model 5 and above) and all processors after. 6184581Ssherrym */ 6194581Ssherrym if ((cpuid_getmodel(cp) >= 5) || (cpuid_getfamily(cp) > 6)) { 6204581Ssherrym uinfop->cui_platid = 1 << ((rdmsr(MSR_INTC_PLATFORM_ID) >> 6214581Ssherrym INTC_PLATFORM_ID_SHIFT) & INTC_PLATFORM_ID_MASK); 6224581Ssherrym } 6234581Ssherrym 6244581Ssherrym ucode_read_rev(uinfop); 6254581Ssherrym 6264581Ssherrym /* 6274581Ssherrym * Check to see if we need ucode update 6284581Ssherrym */ 6294581Ssherrym if ((rc = ucode_locate(cp, uinfop, &ucodefile)) == EM_OK) { 6304581Ssherrym ucode_update_intel(ucodefile.uf_body, uinfop); 6314581Ssherrym 6324581Ssherrym if (uinfop->cui_rev != ucodefile.uf_header.uh_rev) 6334581Ssherrym cmn_err(CE_WARN, ucode_failure_fmt, cp->cpu_id, 6344581Ssherrym uinfop->cui_rev, ucodefile.uf_header.uh_rev); 6354581Ssherrym } 6364581Ssherrym 6374581Ssherrym /* 6384581Ssherrym * If we fail to find a match for any reason, free the file structure 6394581Ssherrym * just in case we have read in a partial file. 6404581Ssherrym * 6414581Ssherrym * Since the scratch memory for holding the microcode for the boot CPU 6424581Ssherrym * came from BOP_ALLOC, we will reset the data structure as if we 6434581Ssherrym * never did the allocation so we don't have to keep track of this 6444581Ssherrym * special chunk of memory. We free the memory used for the rest 6454581Ssherrym * of the CPUs in start_other_cpus(). 6464581Ssherrym */ 6474581Ssherrym if (rc != EM_OK || cp->cpu_id == 0) 6484581Ssherrym ucode_file_reset(&ucodefile, cp->cpu_id); 6494581Ssherrym } 6504581Ssherrym 6514581Ssherrym /* 6524581Ssherrym * Returns microcode revision from the machcpu structure. 6534581Ssherrym */ 6544581Ssherrym ucode_errno_t 6554581Ssherrym ucode_get_rev(uint32_t *revp) 6564581Ssherrym { 6574581Ssherrym int i; 6584581Ssherrym 6594581Ssherrym ASSERT(revp); 6604581Ssherrym 6614581Ssherrym if (!ucode_capable(CPU)) 6624581Ssherrym return (EM_NOTSUP); 6634581Ssherrym 6644581Ssherrym mutex_enter(&cpu_lock); 6654581Ssherrym for (i = 0; i < max_ncpus; i++) { 6664581Ssherrym cpu_t *cpu; 6674581Ssherrym 6684581Ssherrym if ((cpu = cpu_get(i)) == NULL) 6694581Ssherrym continue; 6704581Ssherrym 6714581Ssherrym revp[i] = cpu->cpu_m.mcpu_ucode_info->cui_rev; 6724581Ssherrym } 6734581Ssherrym mutex_exit(&cpu_lock); 6744581Ssherrym 6754581Ssherrym return (EM_OK); 6764581Ssherrym } 677