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 /* 2388699bddSsherrym * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 242449e17fSsherrym * Use is subject to license terms. 252449e17fSsherrym */ 262449e17fSsherrym 272449e17fSsherrym #include <sys/asm_linkage.h> 282449e17fSsherrym #include <sys/bootconf.h> 292449e17fSsherrym #include <sys/cpuvar.h> 302449e17fSsherrym #include <sys/cmn_err.h> 312449e17fSsherrym #include <sys/controlregs.h> 322449e17fSsherrym #include <sys/debug.h> 332449e17fSsherrym #include <sys/kobj.h> 342449e17fSsherrym #include <sys/kobj_impl.h> 352449e17fSsherrym #include <sys/machsystm.h> 362449e17fSsherrym #include <sys/param.h> 372449e17fSsherrym #include <sys/machparam.h> 382449e17fSsherrym #include <sys/promif.h> 392449e17fSsherrym #include <sys/sysmacros.h> 402449e17fSsherrym #include <sys/systm.h> 412449e17fSsherrym #include <sys/types.h> 422449e17fSsherrym #include <sys/thread.h> 432449e17fSsherrym #include <sys/ucode.h> 442449e17fSsherrym #include <sys/x86_archext.h> 452449e17fSsherrym #include <sys/x_call.h> 46843e1988Sjohnlev #ifdef __xpv 47843e1988Sjohnlev #include <sys/hypervisor.h> 48843e1988Sjohnlev #endif 492449e17fSsherrym 502449e17fSsherrym /* 512449e17fSsherrym * Microcode specific information per core 522449e17fSsherrym */ 532449e17fSsherrym struct cpu_ucode_info { 542449e17fSsherrym uint32_t cui_platid; /* platform id */ 552449e17fSsherrym uint32_t cui_rev; /* microcode revision */ 562449e17fSsherrym }; 572449e17fSsherrym 582449e17fSsherrym /* 592449e17fSsherrym * Data structure used for xcall 602449e17fSsherrym */ 612449e17fSsherrym struct ucode_update_struct { 622449e17fSsherrym uint32_t sig; /* signature */ 632449e17fSsherrym struct cpu_ucode_info info; /* ucode info */ 642449e17fSsherrym uint32_t expected_rev; 652449e17fSsherrym uint32_t new_rev; 662449e17fSsherrym uint8_t *ucodep; /* pointer to ucode body */ 672449e17fSsherrym }; 682449e17fSsherrym 692449e17fSsherrym /* 702449e17fSsherrym * mcpu_ucode_info for the boot CPU. Statically allocated. 712449e17fSsherrym */ 722449e17fSsherrym static struct cpu_ucode_info cpu_ucode_info0; 732449e17fSsherrym 742449e17fSsherrym static ucode_file_t ucodefile = { 0 }; 752449e17fSsherrym 762449e17fSsherrym static int ucode_capable(cpu_t *); 772449e17fSsherrym static void ucode_file_reset(ucode_file_t *, processorid_t); 782449e17fSsherrym static ucode_errno_t ucode_match(int, struct cpu_ucode_info *, 792449e17fSsherrym ucode_header_t *, ucode_ext_table_t *); 802449e17fSsherrym static ucode_errno_t ucode_locate(cpu_t *, struct cpu_ucode_info *, 812449e17fSsherrym ucode_file_t *); 822449e17fSsherrym static void ucode_update_intel(uint8_t *, struct cpu_ucode_info *); 832449e17fSsherrym static void ucode_read_rev(struct cpu_ucode_info *); 84882a7af5SMark Johnson #ifdef __xpv 85882a7af5SMark Johnson static void ucode_update_xpv(struct ucode_update_struct *, uint8_t *, uint32_t); 86882a7af5SMark Johnson #endif 872449e17fSsherrym 882449e17fSsherrym static const char ucode_failure_fmt[] = 8988699bddSsherrym "cpu%d: failed to update microcode from version 0x%x to 0x%x\n"; 902449e17fSsherrym static const char ucode_success_fmt[] = 9188699bddSsherrym "?cpu%d: microcode has been updated from version 0x%x to 0x%x\n"; 922449e17fSsherrym 932449e17fSsherrym /* 942449e17fSsherrym * Force flag. If set, the first microcode binary that matches 952449e17fSsherrym * signature and platform id will be used for microcode update, 962449e17fSsherrym * regardless of version. Should only be used for debugging. 972449e17fSsherrym */ 982449e17fSsherrym int ucode_force_update = 0; 992449e17fSsherrym 1002449e17fSsherrym /* 1012449e17fSsherrym * Allocate space for mcpu_ucode_info in the machcpu structure 1022449e17fSsherrym * for all non-boot CPUs. 1032449e17fSsherrym */ 1042449e17fSsherrym void 1052449e17fSsherrym ucode_alloc_space(cpu_t *cp) 1062449e17fSsherrym { 1072449e17fSsherrym ASSERT(cp->cpu_id != 0); 1082449e17fSsherrym cp->cpu_m.mcpu_ucode_info = 1092449e17fSsherrym kmem_zalloc(sizeof (*cp->cpu_m.mcpu_ucode_info), KM_SLEEP); 1102449e17fSsherrym } 1112449e17fSsherrym 1122449e17fSsherrym void 1132449e17fSsherrym ucode_free_space(cpu_t *cp) 1142449e17fSsherrym { 1152449e17fSsherrym ASSERT(cp->cpu_id != 0); 1162449e17fSsherrym kmem_free(cp->cpu_m.mcpu_ucode_info, 1172449e17fSsherrym sizeof (*cp->cpu_m.mcpu_ucode_info)); 1182449e17fSsherrym } 1192449e17fSsherrym 1202449e17fSsherrym /* 1212449e17fSsherrym * Called when we are done with microcode update on all processors to free up 1222449e17fSsherrym * space allocated for the microcode file. 1232449e17fSsherrym */ 1242449e17fSsherrym void 1252449e17fSsherrym ucode_free() 1262449e17fSsherrym { 1272449e17fSsherrym ucode_file_reset(&ucodefile, -1); 1282449e17fSsherrym } 1292449e17fSsherrym 1302449e17fSsherrym /* 1312449e17fSsherrym * Check whether or not a processor is capable of microcode operations 1322449e17fSsherrym * Returns 1 if it is capable, 0 if not. 1332449e17fSsherrym */ 1342449e17fSsherrym static int 1352449e17fSsherrym ucode_capable(cpu_t *cp) 1362449e17fSsherrym { 137843e1988Sjohnlev /* i86xpv guest domain can't update microcode */ 138882a7af5SMark Johnson #ifndef __xpv 139882a7af5SMark Johnson extern int xpv_is_hvm; 140882a7af5SMark Johnson if (xpv_is_hvm) { 141882a7af5SMark Johnson return (0); 142882a7af5SMark Johnson } 143882a7af5SMark Johnson #else 144843e1988Sjohnlev if (!DOMAIN_IS_INITDOMAIN(xen_info)) { 145843e1988Sjohnlev return (0); 146843e1988Sjohnlev } 147843e1988Sjohnlev #endif 148843e1988Sjohnlev 1492449e17fSsherrym /* 1502449e17fSsherrym * At this point we only support microcode update for Intel 1512449e17fSsherrym * processors family 6 and above. 1522449e17fSsherrym * 1532449e17fSsherrym * We also assume that we don't support a mix of Intel and 1542449e17fSsherrym * AMD processors in the same box. 1552449e17fSsherrym */ 1562449e17fSsherrym if (cpuid_getvendor(cp) != X86_VENDOR_Intel || 1572449e17fSsherrym cpuid_getfamily(cp) < 6) 1582449e17fSsherrym return (0); 1592449e17fSsherrym else 1602449e17fSsherrym return (1); 1612449e17fSsherrym } 1622449e17fSsherrym 1632449e17fSsherrym /* 1642449e17fSsherrym * Called when it is no longer necessary to keep the microcode around, 1652449e17fSsherrym * or when the cached microcode doesn't match the CPU being processed. 1662449e17fSsherrym */ 1672449e17fSsherrym static void 1682449e17fSsherrym ucode_file_reset(ucode_file_t *ucodefp, processorid_t id) 1692449e17fSsherrym { 1702449e17fSsherrym int total_size, body_size; 1712449e17fSsherrym 1722449e17fSsherrym if (ucodefp == NULL) 1732449e17fSsherrym return; 1742449e17fSsherrym 1752449e17fSsherrym total_size = UCODE_TOTAL_SIZE(ucodefp->uf_header.uh_total_size); 1762449e17fSsherrym body_size = UCODE_BODY_SIZE(ucodefp->uf_header.uh_body_size); 1772449e17fSsherrym if (ucodefp->uf_body) { 1782449e17fSsherrym /* 1792449e17fSsherrym * Space for the boot CPU is allocated with BOP_ALLOC() 1802449e17fSsherrym * and does not require a free. 1812449e17fSsherrym */ 1822449e17fSsherrym if (id != 0) 1832449e17fSsherrym kmem_free(ucodefp->uf_body, body_size); 1842449e17fSsherrym ucodefp->uf_body = NULL; 1852449e17fSsherrym } 1862449e17fSsherrym 1872449e17fSsherrym if (ucodefp->uf_ext_table) { 1882449e17fSsherrym int size = total_size - body_size - UCODE_HEADER_SIZE; 1892449e17fSsherrym /* 1902449e17fSsherrym * Space for the boot CPU is allocated with BOP_ALLOC() 1912449e17fSsherrym * and does not require a free. 1922449e17fSsherrym */ 1932449e17fSsherrym if (id != 0) 1942449e17fSsherrym kmem_free(ucodefp->uf_ext_table, size); 1952449e17fSsherrym ucodefp->uf_ext_table = NULL; 1962449e17fSsherrym } 1972449e17fSsherrym 1982449e17fSsherrym bzero(&ucodefp->uf_header, UCODE_HEADER_SIZE); 1992449e17fSsherrym } 2002449e17fSsherrym 2012449e17fSsherrym /* 2022449e17fSsherrym * Populate the ucode file structure from microcode file corresponding to 2032449e17fSsherrym * this CPU, if exists. 2042449e17fSsherrym * 2052449e17fSsherrym * Return EM_OK on success, corresponding error code on failure. 2062449e17fSsherrym */ 2072449e17fSsherrym static ucode_errno_t 2082449e17fSsherrym ucode_locate(cpu_t *cp, struct cpu_ucode_info *uinfop, ucode_file_t *ucodefp) 2092449e17fSsherrym { 2102449e17fSsherrym char name[MAXPATHLEN]; 2112449e17fSsherrym intptr_t fd; 2122449e17fSsherrym int count; 2132449e17fSsherrym int header_size = UCODE_HEADER_SIZE; 2142449e17fSsherrym int cpi_sig = cpuid_getsig(cp); 2152449e17fSsherrym ucode_errno_t rc = EM_OK; 2162449e17fSsherrym 2172449e17fSsherrym /* 2182449e17fSsherrym * If the microcode matches the CPU we are processing, use it. 2192449e17fSsherrym */ 2202449e17fSsherrym if (ucode_match(cpi_sig, uinfop, &ucodefp->uf_header, 2212449e17fSsherrym ucodefp->uf_ext_table) == EM_OK && ucodefp->uf_body != NULL) { 2222449e17fSsherrym return (EM_OK); 2232449e17fSsherrym } 2242449e17fSsherrym 2252449e17fSsherrym /* 2262449e17fSsherrym * Look for microcode file with the right name. 2272449e17fSsherrym */ 2282449e17fSsherrym (void) snprintf(name, MAXPATHLEN, "/%s/%s/%08X-%02X", 2292449e17fSsherrym UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), cpi_sig, 2302449e17fSsherrym uinfop->cui_platid); 2312449e17fSsherrym if ((fd = kobj_open(name)) == -1) { 2322449e17fSsherrym return (EM_OPENFILE); 2332449e17fSsherrym } 2342449e17fSsherrym 2352449e17fSsherrym /* 2362449e17fSsherrym * We found a microcode file for the CPU we are processing, 2372449e17fSsherrym * reset the microcode data structure and read in the new 2382449e17fSsherrym * file. 2392449e17fSsherrym */ 2402449e17fSsherrym ucode_file_reset(ucodefp, cp->cpu_id); 2412449e17fSsherrym 2422449e17fSsherrym count = kobj_read(fd, (char *)&ucodefp->uf_header, header_size, 0); 2432449e17fSsherrym 2442449e17fSsherrym switch (count) { 2452449e17fSsherrym case UCODE_HEADER_SIZE: { 2462449e17fSsherrym 2472449e17fSsherrym ucode_header_t *uhp = &ucodefp->uf_header; 2482449e17fSsherrym uint32_t offset = header_size; 2492449e17fSsherrym int total_size, body_size, ext_size; 2502449e17fSsherrym uint32_t sum = 0; 2512449e17fSsherrym 2522449e17fSsherrym /* 2532449e17fSsherrym * Make sure that the header contains valid fields. 2542449e17fSsherrym */ 2552449e17fSsherrym if ((rc = ucode_header_validate(uhp)) == EM_OK) { 2562449e17fSsherrym total_size = UCODE_TOTAL_SIZE(uhp->uh_total_size); 2572449e17fSsherrym body_size = UCODE_BODY_SIZE(uhp->uh_body_size); 2582449e17fSsherrym if (cp->cpu_id != 0) { 2592449e17fSsherrym if ((ucodefp->uf_body = kmem_zalloc(body_size, 2602449e17fSsherrym KM_NOSLEEP)) == NULL) { 2612449e17fSsherrym rc = EM_NOMEM; 2622449e17fSsherrym break; 2632449e17fSsherrym } 2642449e17fSsherrym } else { 2652449e17fSsherrym /* 2662449e17fSsherrym * BOP_ALLOC() failure results in panic so we 2672449e17fSsherrym * don't have to check for NULL return. 2682449e17fSsherrym */ 2692449e17fSsherrym ucodefp->uf_body = 2702449e17fSsherrym (uint8_t *)BOP_ALLOC(bootops, 2712449e17fSsherrym NULL, body_size, MMU_PAGESIZE); 2722449e17fSsherrym } 2732449e17fSsherrym 2742449e17fSsherrym if (kobj_read(fd, (char *)ucodefp->uf_body, 2752449e17fSsherrym body_size, offset) != body_size) 2762449e17fSsherrym rc = EM_FILESIZE; 2772449e17fSsherrym } 2782449e17fSsherrym 2792449e17fSsherrym if (rc) 2802449e17fSsherrym break; 2812449e17fSsherrym 2822449e17fSsherrym sum = ucode_checksum(0, header_size, 2832449e17fSsherrym (uint8_t *)&ucodefp->uf_header); 2842449e17fSsherrym if (ucode_checksum(sum, body_size, ucodefp->uf_body)) { 2852449e17fSsherrym rc = EM_CHECKSUM; 2862449e17fSsherrym break; 2872449e17fSsherrym } 2882449e17fSsherrym 2892449e17fSsherrym /* 2902449e17fSsherrym * Check to see if there is extended signature table. 2912449e17fSsherrym */ 2922449e17fSsherrym offset = body_size + header_size; 2932449e17fSsherrym ext_size = total_size - offset; 2942449e17fSsherrym 2952449e17fSsherrym if (ext_size <= 0) 2962449e17fSsherrym break; 2972449e17fSsherrym 2982449e17fSsherrym if (cp->cpu_id != 0) { 2992449e17fSsherrym if ((ucodefp->uf_ext_table = kmem_zalloc(ext_size, 3002449e17fSsherrym KM_NOSLEEP)) == NULL) { 3012449e17fSsherrym rc = EM_NOMEM; 3022449e17fSsherrym break; 3032449e17fSsherrym } 3042449e17fSsherrym } else { 3052449e17fSsherrym /* 3062449e17fSsherrym * BOP_ALLOC() failure results in panic so we 3072449e17fSsherrym * don't have to check for NULL return. 3082449e17fSsherrym */ 3092449e17fSsherrym ucodefp->uf_ext_table = 3102449e17fSsherrym (ucode_ext_table_t *)BOP_ALLOC(bootops, NULL, 3112449e17fSsherrym ext_size, MMU_PAGESIZE); 3122449e17fSsherrym } 3132449e17fSsherrym 3142449e17fSsherrym if (kobj_read(fd, (char *)ucodefp->uf_ext_table, 3152449e17fSsherrym ext_size, offset) != ext_size) { 3162449e17fSsherrym rc = EM_FILESIZE; 3172449e17fSsherrym } else if (ucode_checksum(0, ext_size, 3182449e17fSsherrym (uint8_t *)(ucodefp->uf_ext_table))) { 3192449e17fSsherrym rc = EM_CHECKSUM; 3202449e17fSsherrym } else { 3212449e17fSsherrym int i; 3222449e17fSsherrym 3232449e17fSsherrym ext_size -= UCODE_EXT_TABLE_SIZE; 3242449e17fSsherrym for (i = 0; i < ucodefp->uf_ext_table->uet_count; 3252449e17fSsherrym i++) { 3262449e17fSsherrym if (ucode_checksum(0, UCODE_EXT_SIG_SIZE, 3272449e17fSsherrym (uint8_t *)(&(ucodefp->uf_ext_table-> 3282449e17fSsherrym uet_ext_sig[i])))) { 3292449e17fSsherrym rc = EM_CHECKSUM; 3302449e17fSsherrym break; 3312449e17fSsherrym } 3322449e17fSsherrym } 3332449e17fSsherrym } 3342449e17fSsherrym break; 3352449e17fSsherrym } 3362449e17fSsherrym 3372449e17fSsherrym default: 3382449e17fSsherrym rc = EM_FILESIZE; 3392449e17fSsherrym break; 3402449e17fSsherrym } 3412449e17fSsherrym 3422449e17fSsherrym kobj_close(fd); 3432449e17fSsherrym 3442449e17fSsherrym if (rc != EM_OK) 3452449e17fSsherrym return (rc); 3462449e17fSsherrym 3472449e17fSsherrym rc = ucode_match(cpi_sig, uinfop, &ucodefp->uf_header, 3482449e17fSsherrym ucodefp->uf_ext_table); 3492449e17fSsherrym 3502449e17fSsherrym return (rc); 3512449e17fSsherrym } 3522449e17fSsherrym 3532449e17fSsherrym 3542449e17fSsherrym /* 3552449e17fSsherrym * Returns 1 if the microcode is for this processor; 0 otherwise. 3562449e17fSsherrym */ 3572449e17fSsherrym static ucode_errno_t 3582449e17fSsherrym ucode_match(int cpi_sig, struct cpu_ucode_info *uinfop, 3592449e17fSsherrym ucode_header_t *uhp, ucode_ext_table_t *uetp) 3602449e17fSsherrym { 3612449e17fSsherrym ASSERT(uhp); 3622449e17fSsherrym 3632449e17fSsherrym if (UCODE_MATCH(cpi_sig, uhp->uh_signature, 3642449e17fSsherrym uinfop->cui_platid, uhp->uh_proc_flags)) { 3652449e17fSsherrym 3662449e17fSsherrym if (uinfop->cui_rev >= uhp->uh_rev && !ucode_force_update) 3672449e17fSsherrym return (EM_HIGHERREV); 3682449e17fSsherrym 3692449e17fSsherrym return (EM_OK); 3702449e17fSsherrym } 3712449e17fSsherrym 3722449e17fSsherrym if (uetp != NULL) { 3732449e17fSsherrym int i; 3742449e17fSsherrym 3752449e17fSsherrym for (i = 0; i < uetp->uet_count; i++) { 3762449e17fSsherrym ucode_ext_sig_t *uesp; 3772449e17fSsherrym 3782449e17fSsherrym uesp = &uetp->uet_ext_sig[i]; 3792449e17fSsherrym 3802449e17fSsherrym if (UCODE_MATCH(cpi_sig, uesp->ues_signature, 3812449e17fSsherrym uinfop->cui_platid, uesp->ues_proc_flags)) { 3822449e17fSsherrym 3832449e17fSsherrym if (uinfop->cui_rev >= uhp->uh_rev && 3842449e17fSsherrym !ucode_force_update) 3852449e17fSsherrym return (EM_HIGHERREV); 3862449e17fSsherrym 3872449e17fSsherrym return (EM_OK); 3882449e17fSsherrym } 3892449e17fSsherrym } 3902449e17fSsherrym } 3912449e17fSsherrym 3922449e17fSsherrym return (EM_NOMATCH); 3932449e17fSsherrym } 3942449e17fSsherrym 3952449e17fSsherrym /*ARGSUSED*/ 3962449e17fSsherrym static int 3972449e17fSsherrym ucode_write(xc_arg_t arg1, xc_arg_t unused2, xc_arg_t unused3) 3982449e17fSsherrym { 3992449e17fSsherrym struct ucode_update_struct *uusp = (struct ucode_update_struct *)arg1; 4002449e17fSsherrym struct cpu_ucode_info *uinfop = CPU->cpu_m.mcpu_ucode_info; 4012449e17fSsherrym 4022449e17fSsherrym ASSERT(uusp->ucodep); 4032449e17fSsherrym 404882a7af5SMark Johnson #ifndef __xpv 4052449e17fSsherrym /* 4062449e17fSsherrym * Check one more time to see if it is really necessary to update 4072449e17fSsherrym * microcode just in case this is a hyperthreaded processor where 4082449e17fSsherrym * the threads share the same microcode. 4092449e17fSsherrym */ 4102449e17fSsherrym if (!ucode_force_update) { 4112449e17fSsherrym ucode_read_rev(uinfop); 4122449e17fSsherrym uusp->new_rev = uinfop->cui_rev; 4132449e17fSsherrym if (uinfop->cui_rev >= uusp->expected_rev) 4142449e17fSsherrym return (0); 4152449e17fSsherrym } 4162449e17fSsherrym 4172449e17fSsherrym wrmsr(MSR_INTC_UCODE_WRITE, 4182449e17fSsherrym (uint64_t)(intptr_t)(uusp->ucodep)); 419882a7af5SMark Johnson #endif 4202449e17fSsherrym ucode_read_rev(uinfop); 4212449e17fSsherrym uusp->new_rev = uinfop->cui_rev; 4222449e17fSsherrym 4232449e17fSsherrym return (0); 4242449e17fSsherrym } 4252449e17fSsherrym 4262449e17fSsherrym 4272449e17fSsherrym static void 4282449e17fSsherrym ucode_update_intel(uint8_t *ucode_body, struct cpu_ucode_info *uinfop) 4292449e17fSsherrym { 4302449e17fSsherrym kpreempt_disable(); 4312449e17fSsherrym wrmsr(MSR_INTC_UCODE_WRITE, (uint64_t)(uintptr_t)ucode_body); 4322449e17fSsherrym ucode_read_rev(uinfop); 4332449e17fSsherrym kpreempt_enable(); 4342449e17fSsherrym } 4352449e17fSsherrym 436882a7af5SMark Johnson 437882a7af5SMark Johnson #ifdef __xpv 438882a7af5SMark Johnson static void 439882a7af5SMark Johnson ucode_update_xpv(struct ucode_update_struct *uusp, uint8_t *ucode, 440882a7af5SMark Johnson uint32_t size) 441882a7af5SMark Johnson { 442882a7af5SMark Johnson struct cpu_ucode_info *uinfop; 443882a7af5SMark Johnson xen_platform_op_t op; 444882a7af5SMark Johnson int e; 445882a7af5SMark Johnson 446882a7af5SMark Johnson ASSERT(DOMAIN_IS_INITDOMAIN(xen_info)); 447882a7af5SMark Johnson 448882a7af5SMark Johnson kpreempt_disable(); 449882a7af5SMark Johnson uinfop = CPU->cpu_m.mcpu_ucode_info; 450882a7af5SMark Johnson op.cmd = XENPF_microcode_update; 451882a7af5SMark Johnson op.interface_version = XENPF_INTERFACE_VERSION; 452882a7af5SMark Johnson /*LINTED: constant in conditional context*/ 453882a7af5SMark Johnson set_xen_guest_handle(op.u.microcode.data, ucode); 454882a7af5SMark Johnson op.u.microcode.length = size; 455882a7af5SMark Johnson e = HYPERVISOR_platform_op(&op); 456882a7af5SMark Johnson if (e != 0) { 457882a7af5SMark Johnson cmn_err(CE_WARN, "hypervisor failed to accept uCode update"); 458882a7af5SMark Johnson } 459882a7af5SMark Johnson ucode_read_rev(uinfop); 460882a7af5SMark Johnson if (uusp != NULL) { 461882a7af5SMark Johnson uusp->new_rev = uinfop->cui_rev; 462882a7af5SMark Johnson } 463882a7af5SMark Johnson kpreempt_enable(); 464882a7af5SMark Johnson } 465882a7af5SMark Johnson #endif /* __xpv */ 466882a7af5SMark Johnson 467882a7af5SMark Johnson 4682449e17fSsherrym static void 4692449e17fSsherrym ucode_read_rev(struct cpu_ucode_info *uinfop) 4702449e17fSsherrym { 4712449e17fSsherrym struct cpuid_regs crs; 4722449e17fSsherrym 4732449e17fSsherrym /* 4742449e17fSsherrym * The Intel 64 and IA-32 Architecture Software Developer's Manual 4752449e17fSsherrym * recommends that MSR_INTC_UCODE_REV be loaded with 0 first, then 4762449e17fSsherrym * execute cpuid to guarantee the correct reading of this register. 4772449e17fSsherrym */ 4782449e17fSsherrym wrmsr(MSR_INTC_UCODE_REV, 0); 4792449e17fSsherrym (void) __cpuid_insn(&crs); 4802449e17fSsherrym uinfop->cui_rev = (rdmsr(MSR_INTC_UCODE_REV) >> INTC_UCODE_REV_SHIFT); 4812449e17fSsherrym } 4822449e17fSsherrym 4832449e17fSsherrym /* 4842449e17fSsherrym * Entry point to microcode update from the ucode_drv driver. 4852449e17fSsherrym * 4862449e17fSsherrym * Returns EM_OK on success, corresponding error code on failure. 4872449e17fSsherrym */ 4882449e17fSsherrym ucode_errno_t 4892449e17fSsherrym ucode_update(uint8_t *ucodep, int size) 4902449e17fSsherrym { 4912449e17fSsherrym uint32_t header_size = UCODE_HEADER_SIZE; 4922449e17fSsherrym int remaining; 4932449e17fSsherrym int found = 0; 4942449e17fSsherrym processorid_t id; 4952449e17fSsherrym struct ucode_update_struct cached = { 0 }; 4962449e17fSsherrym struct ucode_update_struct *cachedp = NULL; 4972449e17fSsherrym ucode_errno_t rc = EM_OK; 4982449e17fSsherrym ucode_errno_t search_rc = EM_NOMATCH; /* search result */ 4992449e17fSsherrym cpuset_t cpuset; 500882a7af5SMark Johnson #ifdef __xpv 501882a7af5SMark Johnson uint8_t *ustart; 502882a7af5SMark Johnson uint32_t usize; 503882a7af5SMark Johnson #endif 5042449e17fSsherrym 5052449e17fSsherrym ASSERT(ucodep); 5062449e17fSsherrym CPUSET_ZERO(cpuset); 5072449e17fSsherrym 5082449e17fSsherrym if (!ucode_capable(CPU)) 5092449e17fSsherrym return (EM_NOTSUP); 5102449e17fSsherrym 5112449e17fSsherrym mutex_enter(&cpu_lock); 5122449e17fSsherrym 5132449e17fSsherrym for (id = 0; id < max_ncpus; id++) { 5142449e17fSsherrym cpu_t *cpu; 5152449e17fSsherrym struct ucode_update_struct uus = { 0 }; 5162449e17fSsherrym struct ucode_update_struct *uusp = &uus; 5172449e17fSsherrym 5182449e17fSsherrym /* 5192449e17fSsherrym * If there is no such CPU or it is not xcall ready, skip it. 5202449e17fSsherrym */ 5212449e17fSsherrym if ((cpu = cpu_get(id)) == NULL || 5222449e17fSsherrym !(cpu->cpu_flags & CPU_READY)) 5232449e17fSsherrym continue; 5242449e17fSsherrym 5252449e17fSsherrym uusp->sig = cpuid_getsig(cpu); 5262449e17fSsherrym bcopy(cpu->cpu_m.mcpu_ucode_info, &uusp->info, 5272449e17fSsherrym sizeof (uusp->info)); 5282449e17fSsherrym 5292449e17fSsherrym /* 5302449e17fSsherrym * If the current CPU has the same signature and platform 5312449e17fSsherrym * id as the previous one we processed, reuse the information. 5322449e17fSsherrym */ 5332449e17fSsherrym if (cachedp && cachedp->sig == cpuid_getsig(cpu) && 5342449e17fSsherrym cachedp->info.cui_platid == uusp->info.cui_platid) { 5352449e17fSsherrym uusp->ucodep = cachedp->ucodep; 5362449e17fSsherrym uusp->expected_rev = cachedp->expected_rev; 5372449e17fSsherrym /* 5382449e17fSsherrym * Intuitively we should check here to see whether the 5392449e17fSsherrym * running microcode rev is >= the expected rev, and 5402449e17fSsherrym * quit if it is. But we choose to proceed with the 5412449e17fSsherrym * xcall regardless of the running version so that 5422449e17fSsherrym * the other threads in an HT processor can update 5432449e17fSsherrym * the cpu_ucode_info structure in machcpu. 5442449e17fSsherrym */ 5452449e17fSsherrym } else { 5462449e17fSsherrym /* 5472449e17fSsherrym * Go through the whole buffer in case there are 5482449e17fSsherrym * multiple versions of matching microcode for this 5492449e17fSsherrym * processor. 5502449e17fSsherrym */ 5512449e17fSsherrym for (remaining = size; remaining > 0; ) { 5522449e17fSsherrym int total_size, body_size, ext_size; 5532449e17fSsherrym uint8_t *curbuf = &ucodep[size - remaining]; 5542449e17fSsherrym ucode_header_t *uhp = (ucode_header_t *)curbuf; 5552449e17fSsherrym ucode_ext_table_t *uetp = NULL; 5562449e17fSsherrym ucode_errno_t tmprc; 5572449e17fSsherrym 5582449e17fSsherrym total_size = 5592449e17fSsherrym UCODE_TOTAL_SIZE(uhp->uh_total_size); 5602449e17fSsherrym body_size = UCODE_BODY_SIZE(uhp->uh_body_size); 5612449e17fSsherrym ext_size = total_size - 5622449e17fSsherrym (header_size + body_size); 5632449e17fSsherrym 5642449e17fSsherrym if (ext_size > 0) 5652449e17fSsherrym uetp = (ucode_ext_table_t *) 5662449e17fSsherrym &curbuf[header_size + body_size]; 5672449e17fSsherrym 5682449e17fSsherrym tmprc = ucode_match(uusp->sig, &uusp->info, 5692449e17fSsherrym uhp, uetp); 5702449e17fSsherrym 5712449e17fSsherrym /* 5722449e17fSsherrym * Since we are searching through a big file 5732449e17fSsherrym * containing microcode for pretty much all the 5742449e17fSsherrym * processors, we are bound to get EM_NOMATCH 5752449e17fSsherrym * at one point. However, if we return 5762449e17fSsherrym * EM_NOMATCH to users, it will really confuse 5772449e17fSsherrym * them. Therefore, if we ever find a match of 5782449e17fSsherrym * a lower rev, we will set return code to 5792449e17fSsherrym * EM_HIGHERREV. 5802449e17fSsherrym */ 5812449e17fSsherrym if (tmprc == EM_HIGHERREV) 5822449e17fSsherrym search_rc = EM_HIGHERREV; 5832449e17fSsherrym 5842449e17fSsherrym if (tmprc == EM_OK && 5852449e17fSsherrym uusp->expected_rev < uhp->uh_rev) { 5862449e17fSsherrym uusp->ucodep = &curbuf[header_size]; 587882a7af5SMark Johnson #ifdef __xpv 588882a7af5SMark Johnson ustart = (uint8_t *)curbuf; 589882a7af5SMark Johnson usize = UCODE_TOTAL_SIZE( 590882a7af5SMark Johnson uhp->uh_total_size); 591882a7af5SMark Johnson #endif 5922449e17fSsherrym uusp->expected_rev = uhp->uh_rev; 5932449e17fSsherrym bcopy(uusp, &cached, sizeof (cached)); 5942449e17fSsherrym cachedp = &cached; 5952449e17fSsherrym found = 1; 5962449e17fSsherrym } 5972449e17fSsherrym 5982449e17fSsherrym remaining -= total_size; 5992449e17fSsherrym } 6002449e17fSsherrym } 6012449e17fSsherrym 6022449e17fSsherrym /* Nothing to do */ 6032449e17fSsherrym if (uusp->ucodep == NULL) 6042449e17fSsherrym continue; 6052449e17fSsherrym 606882a7af5SMark Johnson #ifdef __xpv 607882a7af5SMark Johnson /* 608882a7af5SMark Johnson * for i86xpv, the hypervisor will update all the CPUs. 609882a7af5SMark Johnson * the hypervisor wants the header, data, and extended 610882a7af5SMark Johnson * signature tables. ucode_write will just read in the 611882a7af5SMark Johnson * updated version on all the CPUs after the update has 612882a7af5SMark Johnson * completed. 613882a7af5SMark Johnson */ 614*c9b5d7d2SMark Johnson if (id == 0) { 615882a7af5SMark Johnson ucode_update_xpv(uusp, ustart, usize); 616*c9b5d7d2SMark Johnson } 617882a7af5SMark Johnson #endif 618882a7af5SMark Johnson 6192449e17fSsherrym CPUSET_ADD(cpuset, id); 6202449e17fSsherrym kpreempt_disable(); 6212449e17fSsherrym xc_sync((xc_arg_t)uusp, 0, 0, X_CALL_HIPRI, cpuset, 6222449e17fSsherrym ucode_write); 6232449e17fSsherrym kpreempt_enable(); 6242449e17fSsherrym CPUSET_DEL(cpuset, id); 6252449e17fSsherrym 6262449e17fSsherrym if (uusp->expected_rev == uusp->new_rev) { 6272449e17fSsherrym cmn_err(CE_CONT, ucode_success_fmt, 6282449e17fSsherrym id, uusp->info.cui_rev, uusp->expected_rev); 6292449e17fSsherrym } else { 6302449e17fSsherrym cmn_err(CE_WARN, ucode_failure_fmt, 6312449e17fSsherrym id, uusp->info.cui_rev, uusp->expected_rev); 6322449e17fSsherrym rc = EM_UPDATE; 6332449e17fSsherrym } 6342449e17fSsherrym } 6352449e17fSsherrym 6362449e17fSsherrym mutex_exit(&cpu_lock); 6372449e17fSsherrym 6382449e17fSsherrym if (!found) 6392449e17fSsherrym rc = search_rc; 6402449e17fSsherrym 6412449e17fSsherrym return (rc); 6422449e17fSsherrym } 6432449e17fSsherrym 6442449e17fSsherrym /* 6452449e17fSsherrym * Initialize mcpu_ucode_info, and perform microcode update if necessary. 6462449e17fSsherrym * This is the entry point from boot path where pointer to CPU structure 6472449e17fSsherrym * is available. 6482449e17fSsherrym * 6492449e17fSsherrym * cpuid_info must be initialized before ucode_check can be called. 6502449e17fSsherrym */ 6512449e17fSsherrym void 6522449e17fSsherrym ucode_check(cpu_t *cp) 6532449e17fSsherrym { 6542449e17fSsherrym struct cpu_ucode_info *uinfop; 6552449e17fSsherrym ucode_errno_t rc = EM_OK; 656882a7af5SMark Johnson #ifdef __xpv 657882a7af5SMark Johnson uint32_t ext_offset; 658882a7af5SMark Johnson uint32_t body_size; 659882a7af5SMark Johnson uint32_t ext_size; 660882a7af5SMark Johnson uint8_t *ustart; 661882a7af5SMark Johnson uint32_t usize; 662882a7af5SMark Johnson #endif 6632449e17fSsherrym 6642449e17fSsherrym ASSERT(cp); 6652449e17fSsherrym if (cp->cpu_id == 0) 6662449e17fSsherrym cp->cpu_m.mcpu_ucode_info = &cpu_ucode_info0; 6672449e17fSsherrym 6682449e17fSsherrym uinfop = cp->cpu_m.mcpu_ucode_info; 6692449e17fSsherrym ASSERT(uinfop); 6702449e17fSsherrym 6712449e17fSsherrym if (!ucode_capable(cp)) 6722449e17fSsherrym return; 6732449e17fSsherrym 6742449e17fSsherrym /* 6752449e17fSsherrym * The MSR_INTC_PLATFORM_ID is supported in Celeron and Xeon 6762449e17fSsherrym * (Family 6, model 5 and above) and all processors after. 6772449e17fSsherrym */ 6782449e17fSsherrym if ((cpuid_getmodel(cp) >= 5) || (cpuid_getfamily(cp) > 6)) { 6792449e17fSsherrym uinfop->cui_platid = 1 << ((rdmsr(MSR_INTC_PLATFORM_ID) >> 6802449e17fSsherrym INTC_PLATFORM_ID_SHIFT) & INTC_PLATFORM_ID_MASK); 6812449e17fSsherrym } 6822449e17fSsherrym 6832449e17fSsherrym ucode_read_rev(uinfop); 6842449e17fSsherrym 685*c9b5d7d2SMark Johnson #ifdef __xpv 686*c9b5d7d2SMark Johnson /* 687*c9b5d7d2SMark Johnson * for i86xpv, the hypervisor will update all the CPUs. We only need 688*c9b5d7d2SMark Johnson * do do this on one of the CPUs (and there always is a CPU 0). 689*c9b5d7d2SMark Johnson */ 690*c9b5d7d2SMark Johnson if (cp->cpu_id != 0) { 691*c9b5d7d2SMark Johnson return; 692*c9b5d7d2SMark Johnson } 693*c9b5d7d2SMark Johnson #endif 694*c9b5d7d2SMark Johnson 6952449e17fSsherrym /* 6962449e17fSsherrym * Check to see if we need ucode update 6972449e17fSsherrym */ 6982449e17fSsherrym if ((rc = ucode_locate(cp, uinfop, &ucodefile)) == EM_OK) { 699882a7af5SMark Johnson #ifndef __xpv 7002449e17fSsherrym ucode_update_intel(ucodefile.uf_body, uinfop); 701882a7af5SMark Johnson #else 702882a7af5SMark Johnson /* 703882a7af5SMark Johnson * the hypervisor wants the header, data, and extended 704882a7af5SMark Johnson * signature tables. We can only get here from the boot 705882a7af5SMark Johnson * CPU (cpu #0), so use BOP_ALLOC. Since we're using BOP_ALLOC, 706882a7af5SMark Johnson * We don't need to free. 707882a7af5SMark Johnson */ 708882a7af5SMark Johnson usize = UCODE_TOTAL_SIZE(ucodefile.uf_header.uh_total_size); 709882a7af5SMark Johnson ustart = (uint8_t *)BOP_ALLOC(bootops, NULL, usize, 710882a7af5SMark Johnson MMU_PAGESIZE); 711882a7af5SMark Johnson 712882a7af5SMark Johnson body_size = UCODE_BODY_SIZE(ucodefile.uf_header.uh_body_size); 713882a7af5SMark Johnson ext_offset = body_size + UCODE_HEADER_SIZE; 714882a7af5SMark Johnson ext_size = usize - ext_offset; 715882a7af5SMark Johnson ASSERT(ext_size >= 0); 716882a7af5SMark Johnson 717882a7af5SMark Johnson (void) memcpy(ustart, &ucodefile.uf_header, UCODE_HEADER_SIZE); 718882a7af5SMark Johnson (void) memcpy(&ustart[UCODE_HEADER_SIZE], ucodefile.uf_body, 719882a7af5SMark Johnson body_size); 720882a7af5SMark Johnson if (ext_size > 0) { 721882a7af5SMark Johnson (void) memcpy(&ustart[ext_offset], 722882a7af5SMark Johnson ucodefile.uf_ext_table, ext_size); 723882a7af5SMark Johnson } 724882a7af5SMark Johnson ucode_update_xpv(NULL, ustart, usize); 725882a7af5SMark Johnson #endif 7262449e17fSsherrym 7272449e17fSsherrym if (uinfop->cui_rev != ucodefile.uf_header.uh_rev) 7282449e17fSsherrym cmn_err(CE_WARN, ucode_failure_fmt, cp->cpu_id, 7292449e17fSsherrym uinfop->cui_rev, ucodefile.uf_header.uh_rev); 7302449e17fSsherrym } 7312449e17fSsherrym 7322449e17fSsherrym /* 7332449e17fSsherrym * If we fail to find a match for any reason, free the file structure 7342449e17fSsherrym * just in case we have read in a partial file. 7352449e17fSsherrym * 7362449e17fSsherrym * Since the scratch memory for holding the microcode for the boot CPU 7372449e17fSsherrym * came from BOP_ALLOC, we will reset the data structure as if we 7382449e17fSsherrym * never did the allocation so we don't have to keep track of this 7392449e17fSsherrym * special chunk of memory. We free the memory used for the rest 7402449e17fSsherrym * of the CPUs in start_other_cpus(). 7412449e17fSsherrym */ 7422449e17fSsherrym if (rc != EM_OK || cp->cpu_id == 0) 7432449e17fSsherrym ucode_file_reset(&ucodefile, cp->cpu_id); 7442449e17fSsherrym } 7452449e17fSsherrym 7462449e17fSsherrym /* 7472449e17fSsherrym * Returns microcode revision from the machcpu structure. 7482449e17fSsherrym */ 7492449e17fSsherrym ucode_errno_t 7502449e17fSsherrym ucode_get_rev(uint32_t *revp) 7512449e17fSsherrym { 7522449e17fSsherrym int i; 7532449e17fSsherrym 7542449e17fSsherrym ASSERT(revp); 7552449e17fSsherrym 7562449e17fSsherrym if (!ucode_capable(CPU)) 7572449e17fSsherrym return (EM_NOTSUP); 7582449e17fSsherrym 7592449e17fSsherrym mutex_enter(&cpu_lock); 7602449e17fSsherrym for (i = 0; i < max_ncpus; i++) { 7612449e17fSsherrym cpu_t *cpu; 7622449e17fSsherrym 7632449e17fSsherrym if ((cpu = cpu_get(i)) == NULL) 7642449e17fSsherrym continue; 7652449e17fSsherrym 7662449e17fSsherrym revp[i] = cpu->cpu_m.mcpu_ucode_info->cui_rev; 7672449e17fSsherrym } 7682449e17fSsherrym mutex_exit(&cpu_lock); 7692449e17fSsherrym 7702449e17fSsherrym return (EM_OK); 7712449e17fSsherrym } 772