12449e17fSsherrym /* 22449e17fSsherrym * CDDL HEADER START 32449e17fSsherrym * 42449e17fSsherrym * The contents of this file are subject to the terms of the 52449e17fSsherrym * Common Development and Distribution License (the "License"). 62449e17fSsherrym * You may not use this file except in compliance with the License. 72449e17fSsherrym * 82449e17fSsherrym * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 92449e17fSsherrym * or http://www.opensolaris.org/os/licensing. 102449e17fSsherrym * See the License for the specific language governing permissions 112449e17fSsherrym * and limitations under the License. 122449e17fSsherrym * 132449e17fSsherrym * When distributing Covered Code, include this CDDL HEADER in each 142449e17fSsherrym * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 152449e17fSsherrym * If applicable, add the following below this CDDL HEADER, with the 162449e17fSsherrym * fields enclosed by brackets "[]" replaced with your own identifying 172449e17fSsherrym * information: Portions Copyright [yyyy] [name of copyright owner] 182449e17fSsherrym * 192449e17fSsherrym * CDDL HEADER END 202449e17fSsherrym */ 212449e17fSsherrym 222449e17fSsherrym /* 23*88699bddSsherrym * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 242449e17fSsherrym * Use is subject to license terms. 252449e17fSsherrym */ 262449e17fSsherrym 272449e17fSsherrym #pragma ident "%Z%%M% %I% %E% SMI" 282449e17fSsherrym 292449e17fSsherrym #include <sys/asm_linkage.h> 302449e17fSsherrym #include <sys/bootconf.h> 312449e17fSsherrym #include <sys/cpuvar.h> 322449e17fSsherrym #include <sys/cmn_err.h> 332449e17fSsherrym #include <sys/controlregs.h> 342449e17fSsherrym #include <sys/debug.h> 352449e17fSsherrym #include <sys/kobj.h> 362449e17fSsherrym #include <sys/kobj_impl.h> 372449e17fSsherrym #include <sys/machsystm.h> 382449e17fSsherrym #include <sys/param.h> 392449e17fSsherrym #include <sys/machparam.h> 402449e17fSsherrym #include <sys/promif.h> 412449e17fSsherrym #include <sys/sysmacros.h> 422449e17fSsherrym #include <sys/systm.h> 432449e17fSsherrym #include <sys/types.h> 442449e17fSsherrym #include <sys/thread.h> 452449e17fSsherrym #include <sys/ucode.h> 462449e17fSsherrym #include <sys/x86_archext.h> 472449e17fSsherrym #include <sys/x_call.h> 48843e1988Sjohnlev #ifdef __xpv 49843e1988Sjohnlev #include <sys/hypervisor.h> 50843e1988Sjohnlev #endif 512449e17fSsherrym 522449e17fSsherrym /* 532449e17fSsherrym * Microcode specific information per core 542449e17fSsherrym */ 552449e17fSsherrym struct cpu_ucode_info { 562449e17fSsherrym uint32_t cui_platid; /* platform id */ 572449e17fSsherrym uint32_t cui_rev; /* microcode revision */ 582449e17fSsherrym }; 592449e17fSsherrym 602449e17fSsherrym /* 612449e17fSsherrym * Data structure used for xcall 622449e17fSsherrym */ 632449e17fSsherrym struct ucode_update_struct { 642449e17fSsherrym uint32_t sig; /* signature */ 652449e17fSsherrym struct cpu_ucode_info info; /* ucode info */ 662449e17fSsherrym uint32_t expected_rev; 672449e17fSsherrym uint32_t new_rev; 682449e17fSsherrym uint8_t *ucodep; /* pointer to ucode body */ 692449e17fSsherrym }; 702449e17fSsherrym 712449e17fSsherrym /* 722449e17fSsherrym * mcpu_ucode_info for the boot CPU. Statically allocated. 732449e17fSsherrym */ 742449e17fSsherrym static struct cpu_ucode_info cpu_ucode_info0; 752449e17fSsherrym 762449e17fSsherrym static ucode_file_t ucodefile = { 0 }; 772449e17fSsherrym 782449e17fSsherrym static int ucode_capable(cpu_t *); 792449e17fSsherrym static void ucode_file_reset(ucode_file_t *, processorid_t); 802449e17fSsherrym static ucode_errno_t ucode_match(int, struct cpu_ucode_info *, 812449e17fSsherrym ucode_header_t *, ucode_ext_table_t *); 822449e17fSsherrym static ucode_errno_t ucode_locate(cpu_t *, struct cpu_ucode_info *, 832449e17fSsherrym ucode_file_t *); 842449e17fSsherrym static void ucode_update_intel(uint8_t *, struct cpu_ucode_info *); 852449e17fSsherrym static void ucode_read_rev(struct cpu_ucode_info *); 862449e17fSsherrym 872449e17fSsherrym static const char ucode_failure_fmt[] = 88*88699bddSsherrym "cpu%d: failed to update microcode from version 0x%x to 0x%x\n"; 892449e17fSsherrym static const char ucode_success_fmt[] = 90*88699bddSsherrym "?cpu%d: microcode has been updated from version 0x%x to 0x%x\n"; 912449e17fSsherrym 922449e17fSsherrym /* 932449e17fSsherrym * Force flag. If set, the first microcode binary that matches 942449e17fSsherrym * signature and platform id will be used for microcode update, 952449e17fSsherrym * regardless of version. Should only be used for debugging. 962449e17fSsherrym */ 972449e17fSsherrym int ucode_force_update = 0; 982449e17fSsherrym 992449e17fSsherrym /* 1002449e17fSsherrym * Allocate space for mcpu_ucode_info in the machcpu structure 1012449e17fSsherrym * for all non-boot CPUs. 1022449e17fSsherrym */ 1032449e17fSsherrym void 1042449e17fSsherrym ucode_alloc_space(cpu_t *cp) 1052449e17fSsherrym { 1062449e17fSsherrym ASSERT(cp->cpu_id != 0); 1072449e17fSsherrym cp->cpu_m.mcpu_ucode_info = 1082449e17fSsherrym kmem_zalloc(sizeof (*cp->cpu_m.mcpu_ucode_info), KM_SLEEP); 1092449e17fSsherrym } 1102449e17fSsherrym 1112449e17fSsherrym void 1122449e17fSsherrym ucode_free_space(cpu_t *cp) 1132449e17fSsherrym { 1142449e17fSsherrym ASSERT(cp->cpu_id != 0); 1152449e17fSsherrym kmem_free(cp->cpu_m.mcpu_ucode_info, 1162449e17fSsherrym sizeof (*cp->cpu_m.mcpu_ucode_info)); 1172449e17fSsherrym } 1182449e17fSsherrym 1192449e17fSsherrym /* 1202449e17fSsherrym * Called when we are done with microcode update on all processors to free up 1212449e17fSsherrym * space allocated for the microcode file. 1222449e17fSsherrym */ 1232449e17fSsherrym void 1242449e17fSsherrym ucode_free() 1252449e17fSsherrym { 1262449e17fSsherrym ucode_file_reset(&ucodefile, -1); 1272449e17fSsherrym } 1282449e17fSsherrym 1292449e17fSsherrym /* 1302449e17fSsherrym * Check whether or not a processor is capable of microcode operations 1312449e17fSsherrym * Returns 1 if it is capable, 0 if not. 1322449e17fSsherrym */ 133843e1988Sjohnlev /*ARGSUSED*/ 1342449e17fSsherrym static int 1352449e17fSsherrym ucode_capable(cpu_t *cp) 1362449e17fSsherrym { 137843e1988Sjohnlev /* i86xpv guest domain can't update microcode */ 138843e1988Sjohnlev #ifdef __xpv 139843e1988Sjohnlev if (!DOMAIN_IS_INITDOMAIN(xen_info)) { 140843e1988Sjohnlev return (0); 141843e1988Sjohnlev } 142843e1988Sjohnlev #endif 143843e1988Sjohnlev 144843e1988Sjohnlev #ifndef __xpv 1452449e17fSsherrym /* 1462449e17fSsherrym * At this point we only support microcode update for Intel 1472449e17fSsherrym * processors family 6 and above. 1482449e17fSsherrym * 1492449e17fSsherrym * We also assume that we don't support a mix of Intel and 1502449e17fSsherrym * AMD processors in the same box. 1512449e17fSsherrym */ 1522449e17fSsherrym if (cpuid_getvendor(cp) != X86_VENDOR_Intel || 1532449e17fSsherrym cpuid_getfamily(cp) < 6) 1542449e17fSsherrym return (0); 1552449e17fSsherrym else 1562449e17fSsherrym return (1); 157843e1988Sjohnlev #else 158843e1988Sjohnlev /* 159843e1988Sjohnlev * XXPV - remove when microcode loading works in dom0. Don't support 160843e1988Sjohnlev * microcode loading in dom0 right now. 161843e1988Sjohnlev */ 162843e1988Sjohnlev return (0); 163843e1988Sjohnlev #endif 1642449e17fSsherrym } 1652449e17fSsherrym 1662449e17fSsherrym /* 1672449e17fSsherrym * Called when it is no longer necessary to keep the microcode around, 1682449e17fSsherrym * or when the cached microcode doesn't match the CPU being processed. 1692449e17fSsherrym */ 1702449e17fSsherrym static void 1712449e17fSsherrym ucode_file_reset(ucode_file_t *ucodefp, processorid_t id) 1722449e17fSsherrym { 1732449e17fSsherrym int total_size, body_size; 1742449e17fSsherrym 1752449e17fSsherrym if (ucodefp == NULL) 1762449e17fSsherrym return; 1772449e17fSsherrym 1782449e17fSsherrym total_size = UCODE_TOTAL_SIZE(ucodefp->uf_header.uh_total_size); 1792449e17fSsherrym body_size = UCODE_BODY_SIZE(ucodefp->uf_header.uh_body_size); 1802449e17fSsherrym if (ucodefp->uf_body) { 1812449e17fSsherrym /* 1822449e17fSsherrym * Space for the boot CPU is allocated with BOP_ALLOC() 1832449e17fSsherrym * and does not require a free. 1842449e17fSsherrym */ 1852449e17fSsherrym if (id != 0) 1862449e17fSsherrym kmem_free(ucodefp->uf_body, body_size); 1872449e17fSsherrym ucodefp->uf_body = NULL; 1882449e17fSsherrym } 1892449e17fSsherrym 1902449e17fSsherrym if (ucodefp->uf_ext_table) { 1912449e17fSsherrym int size = total_size - body_size - UCODE_HEADER_SIZE; 1922449e17fSsherrym /* 1932449e17fSsherrym * Space for the boot CPU is allocated with BOP_ALLOC() 1942449e17fSsherrym * and does not require a free. 1952449e17fSsherrym */ 1962449e17fSsherrym if (id != 0) 1972449e17fSsherrym kmem_free(ucodefp->uf_ext_table, size); 1982449e17fSsherrym ucodefp->uf_ext_table = NULL; 1992449e17fSsherrym } 2002449e17fSsherrym 2012449e17fSsherrym bzero(&ucodefp->uf_header, UCODE_HEADER_SIZE); 2022449e17fSsherrym } 2032449e17fSsherrym 2042449e17fSsherrym /* 2052449e17fSsherrym * Populate the ucode file structure from microcode file corresponding to 2062449e17fSsherrym * this CPU, if exists. 2072449e17fSsherrym * 2082449e17fSsherrym * Return EM_OK on success, corresponding error code on failure. 2092449e17fSsherrym */ 2102449e17fSsherrym static ucode_errno_t 2112449e17fSsherrym ucode_locate(cpu_t *cp, struct cpu_ucode_info *uinfop, ucode_file_t *ucodefp) 2122449e17fSsherrym { 2132449e17fSsherrym char name[MAXPATHLEN]; 2142449e17fSsherrym intptr_t fd; 2152449e17fSsherrym int count; 2162449e17fSsherrym int header_size = UCODE_HEADER_SIZE; 2172449e17fSsherrym int cpi_sig = cpuid_getsig(cp); 2182449e17fSsherrym ucode_errno_t rc = EM_OK; 2192449e17fSsherrym 2202449e17fSsherrym /* 2212449e17fSsherrym * If the microcode matches the CPU we are processing, use it. 2222449e17fSsherrym */ 2232449e17fSsherrym if (ucode_match(cpi_sig, uinfop, &ucodefp->uf_header, 2242449e17fSsherrym ucodefp->uf_ext_table) == EM_OK && ucodefp->uf_body != NULL) { 2252449e17fSsherrym return (EM_OK); 2262449e17fSsherrym } 2272449e17fSsherrym 2282449e17fSsherrym /* 2292449e17fSsherrym * Look for microcode file with the right name. 2302449e17fSsherrym */ 2312449e17fSsherrym (void) snprintf(name, MAXPATHLEN, "/%s/%s/%08X-%02X", 2322449e17fSsherrym UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), cpi_sig, 2332449e17fSsherrym uinfop->cui_platid); 2342449e17fSsherrym if ((fd = kobj_open(name)) == -1) { 2352449e17fSsherrym return (EM_OPENFILE); 2362449e17fSsherrym } 2372449e17fSsherrym 2382449e17fSsherrym /* 2392449e17fSsherrym * We found a microcode file for the CPU we are processing, 2402449e17fSsherrym * reset the microcode data structure and read in the new 2412449e17fSsherrym * file. 2422449e17fSsherrym */ 2432449e17fSsherrym ucode_file_reset(ucodefp, cp->cpu_id); 2442449e17fSsherrym 2452449e17fSsherrym count = kobj_read(fd, (char *)&ucodefp->uf_header, header_size, 0); 2462449e17fSsherrym 2472449e17fSsherrym switch (count) { 2482449e17fSsherrym case UCODE_HEADER_SIZE: { 2492449e17fSsherrym 2502449e17fSsherrym ucode_header_t *uhp = &ucodefp->uf_header; 2512449e17fSsherrym uint32_t offset = header_size; 2522449e17fSsherrym int total_size, body_size, ext_size; 2532449e17fSsherrym uint32_t sum = 0; 2542449e17fSsherrym 2552449e17fSsherrym /* 2562449e17fSsherrym * Make sure that the header contains valid fields. 2572449e17fSsherrym */ 2582449e17fSsherrym if ((rc = ucode_header_validate(uhp)) == EM_OK) { 2592449e17fSsherrym total_size = UCODE_TOTAL_SIZE(uhp->uh_total_size); 2602449e17fSsherrym body_size = UCODE_BODY_SIZE(uhp->uh_body_size); 2612449e17fSsherrym if (cp->cpu_id != 0) { 2622449e17fSsherrym if ((ucodefp->uf_body = kmem_zalloc(body_size, 2632449e17fSsherrym KM_NOSLEEP)) == NULL) { 2642449e17fSsherrym rc = EM_NOMEM; 2652449e17fSsherrym break; 2662449e17fSsherrym } 2672449e17fSsherrym } else { 2682449e17fSsherrym /* 2692449e17fSsherrym * BOP_ALLOC() failure results in panic so we 2702449e17fSsherrym * don't have to check for NULL return. 2712449e17fSsherrym */ 2722449e17fSsherrym ucodefp->uf_body = 2732449e17fSsherrym (uint8_t *)BOP_ALLOC(bootops, 2742449e17fSsherrym NULL, body_size, MMU_PAGESIZE); 2752449e17fSsherrym } 2762449e17fSsherrym 2772449e17fSsherrym if (kobj_read(fd, (char *)ucodefp->uf_body, 2782449e17fSsherrym body_size, offset) != body_size) 2792449e17fSsherrym rc = EM_FILESIZE; 2802449e17fSsherrym } 2812449e17fSsherrym 2822449e17fSsherrym if (rc) 2832449e17fSsherrym break; 2842449e17fSsherrym 2852449e17fSsherrym sum = ucode_checksum(0, header_size, 2862449e17fSsherrym (uint8_t *)&ucodefp->uf_header); 2872449e17fSsherrym if (ucode_checksum(sum, body_size, ucodefp->uf_body)) { 2882449e17fSsherrym rc = EM_CHECKSUM; 2892449e17fSsherrym break; 2902449e17fSsherrym } 2912449e17fSsherrym 2922449e17fSsherrym /* 2932449e17fSsherrym * Check to see if there is extended signature table. 2942449e17fSsherrym */ 2952449e17fSsherrym offset = body_size + header_size; 2962449e17fSsherrym ext_size = total_size - offset; 2972449e17fSsherrym 2982449e17fSsherrym if (ext_size <= 0) 2992449e17fSsherrym break; 3002449e17fSsherrym 3012449e17fSsherrym if (cp->cpu_id != 0) { 3022449e17fSsherrym if ((ucodefp->uf_ext_table = kmem_zalloc(ext_size, 3032449e17fSsherrym KM_NOSLEEP)) == NULL) { 3042449e17fSsherrym rc = EM_NOMEM; 3052449e17fSsherrym break; 3062449e17fSsherrym } 3072449e17fSsherrym } else { 3082449e17fSsherrym /* 3092449e17fSsherrym * BOP_ALLOC() failure results in panic so we 3102449e17fSsherrym * don't have to check for NULL return. 3112449e17fSsherrym */ 3122449e17fSsherrym ucodefp->uf_ext_table = 3132449e17fSsherrym (ucode_ext_table_t *)BOP_ALLOC(bootops, NULL, 3142449e17fSsherrym ext_size, MMU_PAGESIZE); 3152449e17fSsherrym } 3162449e17fSsherrym 3172449e17fSsherrym if (kobj_read(fd, (char *)ucodefp->uf_ext_table, 3182449e17fSsherrym ext_size, offset) != ext_size) { 3192449e17fSsherrym rc = EM_FILESIZE; 3202449e17fSsherrym } else if (ucode_checksum(0, ext_size, 3212449e17fSsherrym (uint8_t *)(ucodefp->uf_ext_table))) { 3222449e17fSsherrym rc = EM_CHECKSUM; 3232449e17fSsherrym } else { 3242449e17fSsherrym int i; 3252449e17fSsherrym 3262449e17fSsherrym ext_size -= UCODE_EXT_TABLE_SIZE; 3272449e17fSsherrym for (i = 0; i < ucodefp->uf_ext_table->uet_count; 3282449e17fSsherrym i++) { 3292449e17fSsherrym if (ucode_checksum(0, UCODE_EXT_SIG_SIZE, 3302449e17fSsherrym (uint8_t *)(&(ucodefp->uf_ext_table-> 3312449e17fSsherrym uet_ext_sig[i])))) { 3322449e17fSsherrym rc = EM_CHECKSUM; 3332449e17fSsherrym break; 3342449e17fSsherrym } 3352449e17fSsherrym } 3362449e17fSsherrym } 3372449e17fSsherrym break; 3382449e17fSsherrym } 3392449e17fSsherrym 3402449e17fSsherrym default: 3412449e17fSsherrym rc = EM_FILESIZE; 3422449e17fSsherrym break; 3432449e17fSsherrym } 3442449e17fSsherrym 3452449e17fSsherrym kobj_close(fd); 3462449e17fSsherrym 3472449e17fSsherrym if (rc != EM_OK) 3482449e17fSsherrym return (rc); 3492449e17fSsherrym 3502449e17fSsherrym rc = ucode_match(cpi_sig, uinfop, &ucodefp->uf_header, 3512449e17fSsherrym ucodefp->uf_ext_table); 3522449e17fSsherrym 3532449e17fSsherrym return (rc); 3542449e17fSsherrym } 3552449e17fSsherrym 3562449e17fSsherrym 3572449e17fSsherrym /* 3582449e17fSsherrym * Returns 1 if the microcode is for this processor; 0 otherwise. 3592449e17fSsherrym */ 3602449e17fSsherrym static ucode_errno_t 3612449e17fSsherrym ucode_match(int cpi_sig, struct cpu_ucode_info *uinfop, 3622449e17fSsherrym ucode_header_t *uhp, ucode_ext_table_t *uetp) 3632449e17fSsherrym { 3642449e17fSsherrym ASSERT(uhp); 3652449e17fSsherrym 3662449e17fSsherrym if (UCODE_MATCH(cpi_sig, uhp->uh_signature, 3672449e17fSsherrym uinfop->cui_platid, uhp->uh_proc_flags)) { 3682449e17fSsherrym 3692449e17fSsherrym if (uinfop->cui_rev >= uhp->uh_rev && !ucode_force_update) 3702449e17fSsherrym return (EM_HIGHERREV); 3712449e17fSsherrym 3722449e17fSsherrym return (EM_OK); 3732449e17fSsherrym } 3742449e17fSsherrym 3752449e17fSsherrym if (uetp != NULL) { 3762449e17fSsherrym int i; 3772449e17fSsherrym 3782449e17fSsherrym for (i = 0; i < uetp->uet_count; i++) { 3792449e17fSsherrym ucode_ext_sig_t *uesp; 3802449e17fSsherrym 3812449e17fSsherrym uesp = &uetp->uet_ext_sig[i]; 3822449e17fSsherrym 3832449e17fSsherrym if (UCODE_MATCH(cpi_sig, uesp->ues_signature, 3842449e17fSsherrym uinfop->cui_platid, uesp->ues_proc_flags)) { 3852449e17fSsherrym 3862449e17fSsherrym if (uinfop->cui_rev >= uhp->uh_rev && 3872449e17fSsherrym !ucode_force_update) 3882449e17fSsherrym return (EM_HIGHERREV); 3892449e17fSsherrym 3902449e17fSsherrym return (EM_OK); 3912449e17fSsherrym } 3922449e17fSsherrym } 3932449e17fSsherrym } 3942449e17fSsherrym 3952449e17fSsherrym return (EM_NOMATCH); 3962449e17fSsherrym } 3972449e17fSsherrym 3982449e17fSsherrym /*ARGSUSED*/ 3992449e17fSsherrym static int 4002449e17fSsherrym ucode_write(xc_arg_t arg1, xc_arg_t unused2, xc_arg_t unused3) 4012449e17fSsherrym { 4022449e17fSsherrym struct ucode_update_struct *uusp = (struct ucode_update_struct *)arg1; 4032449e17fSsherrym struct cpu_ucode_info *uinfop = CPU->cpu_m.mcpu_ucode_info; 4042449e17fSsherrym 4052449e17fSsherrym ASSERT(uusp->ucodep); 4062449e17fSsherrym 4072449e17fSsherrym /* 4082449e17fSsherrym * Check one more time to see if it is really necessary to update 4092449e17fSsherrym * microcode just in case this is a hyperthreaded processor where 4102449e17fSsherrym * the threads share the same microcode. 4112449e17fSsherrym */ 4122449e17fSsherrym if (!ucode_force_update) { 4132449e17fSsherrym ucode_read_rev(uinfop); 4142449e17fSsherrym uusp->new_rev = uinfop->cui_rev; 4152449e17fSsherrym if (uinfop->cui_rev >= uusp->expected_rev) 4162449e17fSsherrym return (0); 4172449e17fSsherrym } 4182449e17fSsherrym 4192449e17fSsherrym wrmsr(MSR_INTC_UCODE_WRITE, 4202449e17fSsherrym (uint64_t)(intptr_t)(uusp->ucodep)); 4212449e17fSsherrym ucode_read_rev(uinfop); 4222449e17fSsherrym uusp->new_rev = uinfop->cui_rev; 4232449e17fSsherrym 4242449e17fSsherrym return (0); 4252449e17fSsherrym } 4262449e17fSsherrym 4272449e17fSsherrym 4282449e17fSsherrym static void 4292449e17fSsherrym ucode_update_intel(uint8_t *ucode_body, struct cpu_ucode_info *uinfop) 4302449e17fSsherrym { 4312449e17fSsherrym kpreempt_disable(); 4322449e17fSsherrym wrmsr(MSR_INTC_UCODE_WRITE, (uint64_t)(uintptr_t)ucode_body); 4332449e17fSsherrym ucode_read_rev(uinfop); 4342449e17fSsherrym kpreempt_enable(); 4352449e17fSsherrym } 4362449e17fSsherrym 4372449e17fSsherrym static void 4382449e17fSsherrym ucode_read_rev(struct cpu_ucode_info *uinfop) 4392449e17fSsherrym { 4402449e17fSsherrym struct cpuid_regs crs; 4412449e17fSsherrym 4422449e17fSsherrym /* 4432449e17fSsherrym * The Intel 64 and IA-32 Architecture Software Developer's Manual 4442449e17fSsherrym * recommends that MSR_INTC_UCODE_REV be loaded with 0 first, then 4452449e17fSsherrym * execute cpuid to guarantee the correct reading of this register. 4462449e17fSsherrym */ 4472449e17fSsherrym wrmsr(MSR_INTC_UCODE_REV, 0); 4482449e17fSsherrym (void) __cpuid_insn(&crs); 4492449e17fSsherrym uinfop->cui_rev = (rdmsr(MSR_INTC_UCODE_REV) >> INTC_UCODE_REV_SHIFT); 4502449e17fSsherrym } 4512449e17fSsherrym 4522449e17fSsherrym /* 4532449e17fSsherrym * Entry point to microcode update from the ucode_drv driver. 4542449e17fSsherrym * 4552449e17fSsherrym * Returns EM_OK on success, corresponding error code on failure. 4562449e17fSsherrym */ 4572449e17fSsherrym ucode_errno_t 4582449e17fSsherrym ucode_update(uint8_t *ucodep, int size) 4592449e17fSsherrym { 4602449e17fSsherrym uint32_t header_size = UCODE_HEADER_SIZE; 4612449e17fSsherrym int remaining; 4622449e17fSsherrym int found = 0; 4632449e17fSsherrym processorid_t id; 4642449e17fSsherrym struct ucode_update_struct cached = { 0 }; 4652449e17fSsherrym struct ucode_update_struct *cachedp = NULL; 4662449e17fSsherrym ucode_errno_t rc = EM_OK; 4672449e17fSsherrym ucode_errno_t search_rc = EM_NOMATCH; /* search result */ 4682449e17fSsherrym cpuset_t cpuset; 4692449e17fSsherrym 4702449e17fSsherrym ASSERT(ucodep); 4712449e17fSsherrym 4722449e17fSsherrym CPUSET_ZERO(cpuset); 4732449e17fSsherrym 4742449e17fSsherrym if (!ucode_capable(CPU)) 4752449e17fSsherrym return (EM_NOTSUP); 4762449e17fSsherrym 4772449e17fSsherrym mutex_enter(&cpu_lock); 4782449e17fSsherrym 4792449e17fSsherrym for (id = 0; id < max_ncpus; id++) { 4802449e17fSsherrym cpu_t *cpu; 4812449e17fSsherrym struct ucode_update_struct uus = { 0 }; 4822449e17fSsherrym struct ucode_update_struct *uusp = &uus; 4832449e17fSsherrym 4842449e17fSsherrym /* 4852449e17fSsherrym * If there is no such CPU or it is not xcall ready, skip it. 4862449e17fSsherrym */ 4872449e17fSsherrym if ((cpu = cpu_get(id)) == NULL || 4882449e17fSsherrym !(cpu->cpu_flags & CPU_READY)) 4892449e17fSsherrym continue; 4902449e17fSsherrym 4912449e17fSsherrym uusp->sig = cpuid_getsig(cpu); 4922449e17fSsherrym bcopy(cpu->cpu_m.mcpu_ucode_info, &uusp->info, 4932449e17fSsherrym sizeof (uusp->info)); 4942449e17fSsherrym 4952449e17fSsherrym /* 4962449e17fSsherrym * If the current CPU has the same signature and platform 4972449e17fSsherrym * id as the previous one we processed, reuse the information. 4982449e17fSsherrym */ 4992449e17fSsherrym if (cachedp && cachedp->sig == cpuid_getsig(cpu) && 5002449e17fSsherrym cachedp->info.cui_platid == uusp->info.cui_platid) { 5012449e17fSsherrym uusp->ucodep = cachedp->ucodep; 5022449e17fSsherrym uusp->expected_rev = cachedp->expected_rev; 5032449e17fSsherrym /* 5042449e17fSsherrym * Intuitively we should check here to see whether the 5052449e17fSsherrym * running microcode rev is >= the expected rev, and 5062449e17fSsherrym * quit if it is. But we choose to proceed with the 5072449e17fSsherrym * xcall regardless of the running version so that 5082449e17fSsherrym * the other threads in an HT processor can update 5092449e17fSsherrym * the cpu_ucode_info structure in machcpu. 5102449e17fSsherrym */ 5112449e17fSsherrym } else { 5122449e17fSsherrym /* 5132449e17fSsherrym * Go through the whole buffer in case there are 5142449e17fSsherrym * multiple versions of matching microcode for this 5152449e17fSsherrym * processor. 5162449e17fSsherrym */ 5172449e17fSsherrym for (remaining = size; remaining > 0; ) { 5182449e17fSsherrym int total_size, body_size, ext_size; 5192449e17fSsherrym uint8_t *curbuf = &ucodep[size - remaining]; 5202449e17fSsherrym ucode_header_t *uhp = (ucode_header_t *)curbuf; 5212449e17fSsherrym ucode_ext_table_t *uetp = NULL; 5222449e17fSsherrym ucode_errno_t tmprc; 5232449e17fSsherrym 5242449e17fSsherrym total_size = 5252449e17fSsherrym UCODE_TOTAL_SIZE(uhp->uh_total_size); 5262449e17fSsherrym body_size = UCODE_BODY_SIZE(uhp->uh_body_size); 5272449e17fSsherrym ext_size = total_size - 5282449e17fSsherrym (header_size + body_size); 5292449e17fSsherrym 5302449e17fSsherrym if (ext_size > 0) 5312449e17fSsherrym uetp = (ucode_ext_table_t *) 5322449e17fSsherrym &curbuf[header_size + body_size]; 5332449e17fSsherrym 5342449e17fSsherrym tmprc = ucode_match(uusp->sig, &uusp->info, 5352449e17fSsherrym uhp, uetp); 5362449e17fSsherrym 5372449e17fSsherrym /* 5382449e17fSsherrym * Since we are searching through a big file 5392449e17fSsherrym * containing microcode for pretty much all the 5402449e17fSsherrym * processors, we are bound to get EM_NOMATCH 5412449e17fSsherrym * at one point. However, if we return 5422449e17fSsherrym * EM_NOMATCH to users, it will really confuse 5432449e17fSsherrym * them. Therefore, if we ever find a match of 5442449e17fSsherrym * a lower rev, we will set return code to 5452449e17fSsherrym * EM_HIGHERREV. 5462449e17fSsherrym */ 5472449e17fSsherrym if (tmprc == EM_HIGHERREV) 5482449e17fSsherrym search_rc = EM_HIGHERREV; 5492449e17fSsherrym 5502449e17fSsherrym if (tmprc == EM_OK && 5512449e17fSsherrym uusp->expected_rev < uhp->uh_rev) { 5522449e17fSsherrym uusp->ucodep = &curbuf[header_size]; 5532449e17fSsherrym uusp->expected_rev = uhp->uh_rev; 5542449e17fSsherrym bcopy(uusp, &cached, sizeof (cached)); 5552449e17fSsherrym cachedp = &cached; 5562449e17fSsherrym found = 1; 5572449e17fSsherrym } 5582449e17fSsherrym 5592449e17fSsherrym remaining -= total_size; 5602449e17fSsherrym } 5612449e17fSsherrym } 5622449e17fSsherrym 5632449e17fSsherrym /* Nothing to do */ 5642449e17fSsherrym if (uusp->ucodep == NULL) 5652449e17fSsherrym continue; 5662449e17fSsherrym 5672449e17fSsherrym CPUSET_ADD(cpuset, id); 5682449e17fSsherrym kpreempt_disable(); 5692449e17fSsherrym xc_sync((xc_arg_t)uusp, 0, 0, X_CALL_HIPRI, cpuset, 5702449e17fSsherrym ucode_write); 5712449e17fSsherrym kpreempt_enable(); 5722449e17fSsherrym CPUSET_DEL(cpuset, id); 5732449e17fSsherrym 5742449e17fSsherrym if (uusp->expected_rev == uusp->new_rev) { 5752449e17fSsherrym cmn_err(CE_CONT, ucode_success_fmt, 5762449e17fSsherrym id, uusp->info.cui_rev, uusp->expected_rev); 5772449e17fSsherrym } else { 5782449e17fSsherrym cmn_err(CE_WARN, ucode_failure_fmt, 5792449e17fSsherrym id, uusp->info.cui_rev, uusp->expected_rev); 5802449e17fSsherrym rc = EM_UPDATE; 5812449e17fSsherrym } 5822449e17fSsherrym } 5832449e17fSsherrym 5842449e17fSsherrym mutex_exit(&cpu_lock); 5852449e17fSsherrym 5862449e17fSsherrym if (!found) 5872449e17fSsherrym rc = search_rc; 5882449e17fSsherrym 5892449e17fSsherrym return (rc); 5902449e17fSsherrym } 5912449e17fSsherrym 5922449e17fSsherrym /* 5932449e17fSsherrym * Initialize mcpu_ucode_info, and perform microcode update if necessary. 5942449e17fSsherrym * This is the entry point from boot path where pointer to CPU structure 5952449e17fSsherrym * is available. 5962449e17fSsherrym * 5972449e17fSsherrym * cpuid_info must be initialized before ucode_check can be called. 5982449e17fSsherrym */ 5992449e17fSsherrym void 6002449e17fSsherrym ucode_check(cpu_t *cp) 6012449e17fSsherrym { 6022449e17fSsherrym struct cpu_ucode_info *uinfop; 6032449e17fSsherrym ucode_errno_t rc = EM_OK; 6042449e17fSsherrym 6052449e17fSsherrym ASSERT(cp); 6062449e17fSsherrym if (cp->cpu_id == 0) 6072449e17fSsherrym cp->cpu_m.mcpu_ucode_info = &cpu_ucode_info0; 6082449e17fSsherrym 6092449e17fSsherrym uinfop = cp->cpu_m.mcpu_ucode_info; 6102449e17fSsherrym ASSERT(uinfop); 6112449e17fSsherrym 6122449e17fSsherrym if (!ucode_capable(cp)) 6132449e17fSsherrym return; 6142449e17fSsherrym 6152449e17fSsherrym /* 6162449e17fSsherrym * The MSR_INTC_PLATFORM_ID is supported in Celeron and Xeon 6172449e17fSsherrym * (Family 6, model 5 and above) and all processors after. 6182449e17fSsherrym */ 6192449e17fSsherrym if ((cpuid_getmodel(cp) >= 5) || (cpuid_getfamily(cp) > 6)) { 6202449e17fSsherrym uinfop->cui_platid = 1 << ((rdmsr(MSR_INTC_PLATFORM_ID) >> 6212449e17fSsherrym INTC_PLATFORM_ID_SHIFT) & INTC_PLATFORM_ID_MASK); 6222449e17fSsherrym } 6232449e17fSsherrym 6242449e17fSsherrym ucode_read_rev(uinfop); 6252449e17fSsherrym 6262449e17fSsherrym /* 6272449e17fSsherrym * Check to see if we need ucode update 6282449e17fSsherrym */ 6292449e17fSsherrym if ((rc = ucode_locate(cp, uinfop, &ucodefile)) == EM_OK) { 6302449e17fSsherrym ucode_update_intel(ucodefile.uf_body, uinfop); 6312449e17fSsherrym 6322449e17fSsherrym if (uinfop->cui_rev != ucodefile.uf_header.uh_rev) 6332449e17fSsherrym cmn_err(CE_WARN, ucode_failure_fmt, cp->cpu_id, 6342449e17fSsherrym uinfop->cui_rev, ucodefile.uf_header.uh_rev); 6352449e17fSsherrym } 6362449e17fSsherrym 6372449e17fSsherrym /* 6382449e17fSsherrym * If we fail to find a match for any reason, free the file structure 6392449e17fSsherrym * just in case we have read in a partial file. 6402449e17fSsherrym * 6412449e17fSsherrym * Since the scratch memory for holding the microcode for the boot CPU 6422449e17fSsherrym * came from BOP_ALLOC, we will reset the data structure as if we 6432449e17fSsherrym * never did the allocation so we don't have to keep track of this 6442449e17fSsherrym * special chunk of memory. We free the memory used for the rest 6452449e17fSsherrym * of the CPUs in start_other_cpus(). 6462449e17fSsherrym */ 6472449e17fSsherrym if (rc != EM_OK || cp->cpu_id == 0) 6482449e17fSsherrym ucode_file_reset(&ucodefile, cp->cpu_id); 6492449e17fSsherrym } 6502449e17fSsherrym 6512449e17fSsherrym /* 6522449e17fSsherrym * Returns microcode revision from the machcpu structure. 6532449e17fSsherrym */ 6542449e17fSsherrym ucode_errno_t 6552449e17fSsherrym ucode_get_rev(uint32_t *revp) 6562449e17fSsherrym { 6572449e17fSsherrym int i; 6582449e17fSsherrym 6592449e17fSsherrym ASSERT(revp); 6602449e17fSsherrym 6612449e17fSsherrym if (!ucode_capable(CPU)) 6622449e17fSsherrym return (EM_NOTSUP); 6632449e17fSsherrym 6642449e17fSsherrym mutex_enter(&cpu_lock); 6652449e17fSsherrym for (i = 0; i < max_ncpus; i++) { 6662449e17fSsherrym cpu_t *cpu; 6672449e17fSsherrym 6682449e17fSsherrym if ((cpu = cpu_get(i)) == NULL) 6692449e17fSsherrym continue; 6702449e17fSsherrym 6712449e17fSsherrym revp[i] = cpu->cpu_m.mcpu_ucode_info->cui_rev; 6722449e17fSsherrym } 6732449e17fSsherrym mutex_exit(&cpu_lock); 6742449e17fSsherrym 6752449e17fSsherrym return (EM_OK); 6762449e17fSsherrym } 677