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 /* 514581Ssherrym * Microcode specific information per core 524581Ssherrym */ 534581Ssherrym struct cpu_ucode_info { 544581Ssherrym uint32_t cui_platid; /* platform id */ 554581Ssherrym uint32_t cui_rev; /* microcode revision */ 564581Ssherrym }; 574581Ssherrym 584581Ssherrym /* 594581Ssherrym * Data structure used for xcall 604581Ssherrym */ 614581Ssherrym struct ucode_update_struct { 624581Ssherrym uint32_t sig; /* signature */ 634581Ssherrym struct cpu_ucode_info info; /* ucode info */ 644581Ssherrym uint32_t expected_rev; 654581Ssherrym uint32_t new_rev; 664581Ssherrym uint8_t *ucodep; /* pointer to ucode body */ 674581Ssherrym }; 684581Ssherrym 694581Ssherrym /* 704581Ssherrym * mcpu_ucode_info for the boot CPU. Statically allocated. 714581Ssherrym */ 724581Ssherrym static struct cpu_ucode_info cpu_ucode_info0; 734581Ssherrym 744581Ssherrym static ucode_file_t ucodefile = { 0 }; 754581Ssherrym 764581Ssherrym static int ucode_capable(cpu_t *); 774581Ssherrym static void ucode_file_reset(ucode_file_t *, processorid_t); 784581Ssherrym static ucode_errno_t ucode_match(int, struct cpu_ucode_info *, 794581Ssherrym ucode_header_t *, ucode_ext_table_t *); 804581Ssherrym static ucode_errno_t ucode_locate(cpu_t *, struct cpu_ucode_info *, 814581Ssherrym ucode_file_t *); 824581Ssherrym static void ucode_update_intel(uint8_t *, struct cpu_ucode_info *); 834581Ssherrym static void ucode_read_rev(struct cpu_ucode_info *); 84*7347SMark.Johnson@Sun.COM #ifdef __xpv 85*7347SMark.Johnson@Sun.COM static void ucode_update_xpv(struct ucode_update_struct *, uint8_t *, uint32_t); 86*7347SMark.Johnson@Sun.COM #endif 874581Ssherrym 884581Ssherrym static const char ucode_failure_fmt[] = 896519Ssherrym "cpu%d: failed to update microcode from version 0x%x to 0x%x\n"; 904581Ssherrym static const char ucode_success_fmt[] = 916519Ssherrym "?cpu%d: microcode has been updated from version 0x%x to 0x%x\n"; 924581Ssherrym 934581Ssherrym /* 944581Ssherrym * Force flag. If set, the first microcode binary that matches 954581Ssherrym * signature and platform id will be used for microcode update, 964581Ssherrym * regardless of version. Should only be used for debugging. 974581Ssherrym */ 984581Ssherrym int ucode_force_update = 0; 994581Ssherrym 1004581Ssherrym /* 1014581Ssherrym * Allocate space for mcpu_ucode_info in the machcpu structure 1024581Ssherrym * for all non-boot CPUs. 1034581Ssherrym */ 1044581Ssherrym void 1054581Ssherrym ucode_alloc_space(cpu_t *cp) 1064581Ssherrym { 1074581Ssherrym ASSERT(cp->cpu_id != 0); 1084581Ssherrym cp->cpu_m.mcpu_ucode_info = 1094581Ssherrym kmem_zalloc(sizeof (*cp->cpu_m.mcpu_ucode_info), KM_SLEEP); 1104581Ssherrym } 1114581Ssherrym 1124581Ssherrym void 1134581Ssherrym ucode_free_space(cpu_t *cp) 1144581Ssherrym { 1154581Ssherrym ASSERT(cp->cpu_id != 0); 1164581Ssherrym kmem_free(cp->cpu_m.mcpu_ucode_info, 1174581Ssherrym sizeof (*cp->cpu_m.mcpu_ucode_info)); 1184581Ssherrym } 1194581Ssherrym 1204581Ssherrym /* 1214581Ssherrym * Called when we are done with microcode update on all processors to free up 1224581Ssherrym * space allocated for the microcode file. 1234581Ssherrym */ 1244581Ssherrym void 1254581Ssherrym ucode_free() 1264581Ssherrym { 1274581Ssherrym ucode_file_reset(&ucodefile, -1); 1284581Ssherrym } 1294581Ssherrym 1304581Ssherrym /* 1314581Ssherrym * Check whether or not a processor is capable of microcode operations 1324581Ssherrym * Returns 1 if it is capable, 0 if not. 1334581Ssherrym */ 1344581Ssherrym static int 1354581Ssherrym ucode_capable(cpu_t *cp) 1364581Ssherrym { 1375084Sjohnlev /* i86xpv guest domain can't update microcode */ 138*7347SMark.Johnson@Sun.COM #ifndef __xpv 139*7347SMark.Johnson@Sun.COM extern int xpv_is_hvm; 140*7347SMark.Johnson@Sun.COM if (xpv_is_hvm) { 141*7347SMark.Johnson@Sun.COM return (0); 142*7347SMark.Johnson@Sun.COM } 143*7347SMark.Johnson@Sun.COM #else 1445084Sjohnlev if (!DOMAIN_IS_INITDOMAIN(xen_info)) { 1455084Sjohnlev return (0); 1465084Sjohnlev } 1475084Sjohnlev #endif 1485084Sjohnlev 1494581Ssherrym /* 1504581Ssherrym * At this point we only support microcode update for Intel 1514581Ssherrym * processors family 6 and above. 1524581Ssherrym * 1534581Ssherrym * We also assume that we don't support a mix of Intel and 1544581Ssherrym * AMD processors in the same box. 1554581Ssherrym */ 1564581Ssherrym if (cpuid_getvendor(cp) != X86_VENDOR_Intel || 1574581Ssherrym cpuid_getfamily(cp) < 6) 1584581Ssherrym return (0); 1594581Ssherrym else 1604581Ssherrym return (1); 1614581Ssherrym } 1624581Ssherrym 1634581Ssherrym /* 1644581Ssherrym * Called when it is no longer necessary to keep the microcode around, 1654581Ssherrym * or when the cached microcode doesn't match the CPU being processed. 1664581Ssherrym */ 1674581Ssherrym static void 1684581Ssherrym ucode_file_reset(ucode_file_t *ucodefp, processorid_t id) 1694581Ssherrym { 1704581Ssherrym int total_size, body_size; 1714581Ssherrym 1724581Ssherrym if (ucodefp == NULL) 1734581Ssherrym return; 1744581Ssherrym 1754581Ssherrym total_size = UCODE_TOTAL_SIZE(ucodefp->uf_header.uh_total_size); 1764581Ssherrym body_size = UCODE_BODY_SIZE(ucodefp->uf_header.uh_body_size); 1774581Ssherrym if (ucodefp->uf_body) { 1784581Ssherrym /* 1794581Ssherrym * Space for the boot CPU is allocated with BOP_ALLOC() 1804581Ssherrym * and does not require a free. 1814581Ssherrym */ 1824581Ssherrym if (id != 0) 1834581Ssherrym kmem_free(ucodefp->uf_body, body_size); 1844581Ssherrym ucodefp->uf_body = NULL; 1854581Ssherrym } 1864581Ssherrym 1874581Ssherrym if (ucodefp->uf_ext_table) { 1884581Ssherrym int size = total_size - body_size - UCODE_HEADER_SIZE; 1894581Ssherrym /* 1904581Ssherrym * Space for the boot CPU is allocated with BOP_ALLOC() 1914581Ssherrym * and does not require a free. 1924581Ssherrym */ 1934581Ssherrym if (id != 0) 1944581Ssherrym kmem_free(ucodefp->uf_ext_table, size); 1954581Ssherrym ucodefp->uf_ext_table = NULL; 1964581Ssherrym } 1974581Ssherrym 1984581Ssherrym bzero(&ucodefp->uf_header, UCODE_HEADER_SIZE); 1994581Ssherrym } 2004581Ssherrym 2014581Ssherrym /* 2024581Ssherrym * Populate the ucode file structure from microcode file corresponding to 2034581Ssherrym * this CPU, if exists. 2044581Ssherrym * 2054581Ssherrym * Return EM_OK on success, corresponding error code on failure. 2064581Ssherrym */ 2074581Ssherrym static ucode_errno_t 2084581Ssherrym ucode_locate(cpu_t *cp, struct cpu_ucode_info *uinfop, ucode_file_t *ucodefp) 2094581Ssherrym { 2104581Ssherrym char name[MAXPATHLEN]; 2114581Ssherrym intptr_t fd; 2124581Ssherrym int count; 2134581Ssherrym int header_size = UCODE_HEADER_SIZE; 2144581Ssherrym int cpi_sig = cpuid_getsig(cp); 2154581Ssherrym ucode_errno_t rc = EM_OK; 2164581Ssherrym 2174581Ssherrym /* 2184581Ssherrym * If the microcode matches the CPU we are processing, use it. 2194581Ssherrym */ 2204581Ssherrym if (ucode_match(cpi_sig, uinfop, &ucodefp->uf_header, 2214581Ssherrym ucodefp->uf_ext_table) == EM_OK && ucodefp->uf_body != NULL) { 2224581Ssherrym return (EM_OK); 2234581Ssherrym } 2244581Ssherrym 2254581Ssherrym /* 2264581Ssherrym * Look for microcode file with the right name. 2274581Ssherrym */ 2284581Ssherrym (void) snprintf(name, MAXPATHLEN, "/%s/%s/%08X-%02X", 2294581Ssherrym UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), cpi_sig, 2304581Ssherrym uinfop->cui_platid); 2314581Ssherrym if ((fd = kobj_open(name)) == -1) { 2324581Ssherrym return (EM_OPENFILE); 2334581Ssherrym } 2344581Ssherrym 2354581Ssherrym /* 2364581Ssherrym * We found a microcode file for the CPU we are processing, 2374581Ssherrym * reset the microcode data structure and read in the new 2384581Ssherrym * file. 2394581Ssherrym */ 2404581Ssherrym ucode_file_reset(ucodefp, cp->cpu_id); 2414581Ssherrym 2424581Ssherrym count = kobj_read(fd, (char *)&ucodefp->uf_header, header_size, 0); 2434581Ssherrym 2444581Ssherrym switch (count) { 2454581Ssherrym case UCODE_HEADER_SIZE: { 2464581Ssherrym 2474581Ssherrym ucode_header_t *uhp = &ucodefp->uf_header; 2484581Ssherrym uint32_t offset = header_size; 2494581Ssherrym int total_size, body_size, ext_size; 2504581Ssherrym uint32_t sum = 0; 2514581Ssherrym 2524581Ssherrym /* 2534581Ssherrym * Make sure that the header contains valid fields. 2544581Ssherrym */ 2554581Ssherrym if ((rc = ucode_header_validate(uhp)) == EM_OK) { 2564581Ssherrym total_size = UCODE_TOTAL_SIZE(uhp->uh_total_size); 2574581Ssherrym body_size = UCODE_BODY_SIZE(uhp->uh_body_size); 2584581Ssherrym if (cp->cpu_id != 0) { 2594581Ssherrym if ((ucodefp->uf_body = kmem_zalloc(body_size, 2604581Ssherrym KM_NOSLEEP)) == NULL) { 2614581Ssherrym rc = EM_NOMEM; 2624581Ssherrym break; 2634581Ssherrym } 2644581Ssherrym } else { 2654581Ssherrym /* 2664581Ssherrym * BOP_ALLOC() failure results in panic so we 2674581Ssherrym * don't have to check for NULL return. 2684581Ssherrym */ 2694581Ssherrym ucodefp->uf_body = 2704581Ssherrym (uint8_t *)BOP_ALLOC(bootops, 2714581Ssherrym NULL, body_size, MMU_PAGESIZE); 2724581Ssherrym } 2734581Ssherrym 2744581Ssherrym if (kobj_read(fd, (char *)ucodefp->uf_body, 2754581Ssherrym body_size, offset) != body_size) 2764581Ssherrym rc = EM_FILESIZE; 2774581Ssherrym } 2784581Ssherrym 2794581Ssherrym if (rc) 2804581Ssherrym break; 2814581Ssherrym 2824581Ssherrym sum = ucode_checksum(0, header_size, 2834581Ssherrym (uint8_t *)&ucodefp->uf_header); 2844581Ssherrym if (ucode_checksum(sum, body_size, ucodefp->uf_body)) { 2854581Ssherrym rc = EM_CHECKSUM; 2864581Ssherrym break; 2874581Ssherrym } 2884581Ssherrym 2894581Ssherrym /* 2904581Ssherrym * Check to see if there is extended signature table. 2914581Ssherrym */ 2924581Ssherrym offset = body_size + header_size; 2934581Ssherrym ext_size = total_size - offset; 2944581Ssherrym 2954581Ssherrym if (ext_size <= 0) 2964581Ssherrym break; 2974581Ssherrym 2984581Ssherrym if (cp->cpu_id != 0) { 2994581Ssherrym if ((ucodefp->uf_ext_table = kmem_zalloc(ext_size, 3004581Ssherrym KM_NOSLEEP)) == NULL) { 3014581Ssherrym rc = EM_NOMEM; 3024581Ssherrym break; 3034581Ssherrym } 3044581Ssherrym } else { 3054581Ssherrym /* 3064581Ssherrym * BOP_ALLOC() failure results in panic so we 3074581Ssherrym * don't have to check for NULL return. 3084581Ssherrym */ 3094581Ssherrym ucodefp->uf_ext_table = 3104581Ssherrym (ucode_ext_table_t *)BOP_ALLOC(bootops, NULL, 3114581Ssherrym ext_size, MMU_PAGESIZE); 3124581Ssherrym } 3134581Ssherrym 3144581Ssherrym if (kobj_read(fd, (char *)ucodefp->uf_ext_table, 3154581Ssherrym ext_size, offset) != ext_size) { 3164581Ssherrym rc = EM_FILESIZE; 3174581Ssherrym } else if (ucode_checksum(0, ext_size, 3184581Ssherrym (uint8_t *)(ucodefp->uf_ext_table))) { 3194581Ssherrym rc = EM_CHECKSUM; 3204581Ssherrym } else { 3214581Ssherrym int i; 3224581Ssherrym 3234581Ssherrym ext_size -= UCODE_EXT_TABLE_SIZE; 3244581Ssherrym for (i = 0; i < ucodefp->uf_ext_table->uet_count; 3254581Ssherrym i++) { 3264581Ssherrym if (ucode_checksum(0, UCODE_EXT_SIG_SIZE, 3274581Ssherrym (uint8_t *)(&(ucodefp->uf_ext_table-> 3284581Ssherrym uet_ext_sig[i])))) { 3294581Ssherrym rc = EM_CHECKSUM; 3304581Ssherrym break; 3314581Ssherrym } 3324581Ssherrym } 3334581Ssherrym } 3344581Ssherrym break; 3354581Ssherrym } 3364581Ssherrym 3374581Ssherrym default: 3384581Ssherrym rc = EM_FILESIZE; 3394581Ssherrym break; 3404581Ssherrym } 3414581Ssherrym 3424581Ssherrym kobj_close(fd); 3434581Ssherrym 3444581Ssherrym if (rc != EM_OK) 3454581Ssherrym return (rc); 3464581Ssherrym 3474581Ssherrym rc = ucode_match(cpi_sig, uinfop, &ucodefp->uf_header, 3484581Ssherrym ucodefp->uf_ext_table); 3494581Ssherrym 3504581Ssherrym return (rc); 3514581Ssherrym } 3524581Ssherrym 3534581Ssherrym 3544581Ssherrym /* 3554581Ssherrym * Returns 1 if the microcode is for this processor; 0 otherwise. 3564581Ssherrym */ 3574581Ssherrym static ucode_errno_t 3584581Ssherrym ucode_match(int cpi_sig, struct cpu_ucode_info *uinfop, 3594581Ssherrym ucode_header_t *uhp, ucode_ext_table_t *uetp) 3604581Ssherrym { 3614581Ssherrym ASSERT(uhp); 3624581Ssherrym 3634581Ssherrym if (UCODE_MATCH(cpi_sig, uhp->uh_signature, 3644581Ssherrym uinfop->cui_platid, uhp->uh_proc_flags)) { 3654581Ssherrym 3664581Ssherrym if (uinfop->cui_rev >= uhp->uh_rev && !ucode_force_update) 3674581Ssherrym return (EM_HIGHERREV); 3684581Ssherrym 3694581Ssherrym return (EM_OK); 3704581Ssherrym } 3714581Ssherrym 3724581Ssherrym if (uetp != NULL) { 3734581Ssherrym int i; 3744581Ssherrym 3754581Ssherrym for (i = 0; i < uetp->uet_count; i++) { 3764581Ssherrym ucode_ext_sig_t *uesp; 3774581Ssherrym 3784581Ssherrym uesp = &uetp->uet_ext_sig[i]; 3794581Ssherrym 3804581Ssherrym if (UCODE_MATCH(cpi_sig, uesp->ues_signature, 3814581Ssherrym uinfop->cui_platid, uesp->ues_proc_flags)) { 3824581Ssherrym 3834581Ssherrym if (uinfop->cui_rev >= uhp->uh_rev && 3844581Ssherrym !ucode_force_update) 3854581Ssherrym return (EM_HIGHERREV); 3864581Ssherrym 3874581Ssherrym return (EM_OK); 3884581Ssherrym } 3894581Ssherrym } 3904581Ssherrym } 3914581Ssherrym 3924581Ssherrym return (EM_NOMATCH); 3934581Ssherrym } 3944581Ssherrym 3954581Ssherrym /*ARGSUSED*/ 3964581Ssherrym static int 3974581Ssherrym ucode_write(xc_arg_t arg1, xc_arg_t unused2, xc_arg_t unused3) 3984581Ssherrym { 3994581Ssherrym struct ucode_update_struct *uusp = (struct ucode_update_struct *)arg1; 4004581Ssherrym struct cpu_ucode_info *uinfop = CPU->cpu_m.mcpu_ucode_info; 4014581Ssherrym 4024581Ssherrym ASSERT(uusp->ucodep); 4034581Ssherrym 404*7347SMark.Johnson@Sun.COM #ifndef __xpv 4054581Ssherrym /* 4064581Ssherrym * Check one more time to see if it is really necessary to update 4074581Ssherrym * microcode just in case this is a hyperthreaded processor where 4084581Ssherrym * the threads share the same microcode. 4094581Ssherrym */ 4104581Ssherrym if (!ucode_force_update) { 4114581Ssherrym ucode_read_rev(uinfop); 4124581Ssherrym uusp->new_rev = uinfop->cui_rev; 4134581Ssherrym if (uinfop->cui_rev >= uusp->expected_rev) 4144581Ssherrym return (0); 4154581Ssherrym } 4164581Ssherrym 4174581Ssherrym wrmsr(MSR_INTC_UCODE_WRITE, 4184581Ssherrym (uint64_t)(intptr_t)(uusp->ucodep)); 419*7347SMark.Johnson@Sun.COM #endif 4204581Ssherrym ucode_read_rev(uinfop); 4214581Ssherrym uusp->new_rev = uinfop->cui_rev; 4224581Ssherrym 4234581Ssherrym return (0); 4244581Ssherrym } 4254581Ssherrym 4264581Ssherrym 4274581Ssherrym static void 4284581Ssherrym ucode_update_intel(uint8_t *ucode_body, struct cpu_ucode_info *uinfop) 4294581Ssherrym { 4304581Ssherrym kpreempt_disable(); 4314581Ssherrym wrmsr(MSR_INTC_UCODE_WRITE, (uint64_t)(uintptr_t)ucode_body); 4324581Ssherrym ucode_read_rev(uinfop); 4334581Ssherrym kpreempt_enable(); 4344581Ssherrym } 4354581Ssherrym 436*7347SMark.Johnson@Sun.COM 437*7347SMark.Johnson@Sun.COM #ifdef __xpv 438*7347SMark.Johnson@Sun.COM static void 439*7347SMark.Johnson@Sun.COM ucode_update_xpv(struct ucode_update_struct *uusp, uint8_t *ucode, 440*7347SMark.Johnson@Sun.COM uint32_t size) 441*7347SMark.Johnson@Sun.COM { 442*7347SMark.Johnson@Sun.COM struct cpu_ucode_info *uinfop; 443*7347SMark.Johnson@Sun.COM xen_platform_op_t op; 444*7347SMark.Johnson@Sun.COM int e; 445*7347SMark.Johnson@Sun.COM 446*7347SMark.Johnson@Sun.COM ASSERT(DOMAIN_IS_INITDOMAIN(xen_info)); 447*7347SMark.Johnson@Sun.COM 448*7347SMark.Johnson@Sun.COM kpreempt_disable(); 449*7347SMark.Johnson@Sun.COM uinfop = CPU->cpu_m.mcpu_ucode_info; 450*7347SMark.Johnson@Sun.COM op.cmd = XENPF_microcode_update; 451*7347SMark.Johnson@Sun.COM op.interface_version = XENPF_INTERFACE_VERSION; 452*7347SMark.Johnson@Sun.COM /*LINTED: constant in conditional context*/ 453*7347SMark.Johnson@Sun.COM set_xen_guest_handle(op.u.microcode.data, ucode); 454*7347SMark.Johnson@Sun.COM op.u.microcode.length = size; 455*7347SMark.Johnson@Sun.COM e = HYPERVISOR_platform_op(&op); 456*7347SMark.Johnson@Sun.COM if (e != 0) { 457*7347SMark.Johnson@Sun.COM cmn_err(CE_WARN, "hypervisor failed to accept uCode update"); 458*7347SMark.Johnson@Sun.COM } 459*7347SMark.Johnson@Sun.COM ucode_read_rev(uinfop); 460*7347SMark.Johnson@Sun.COM if (uusp != NULL) { 461*7347SMark.Johnson@Sun.COM uusp->new_rev = uinfop->cui_rev; 462*7347SMark.Johnson@Sun.COM } 463*7347SMark.Johnson@Sun.COM kpreempt_enable(); 464*7347SMark.Johnson@Sun.COM } 465*7347SMark.Johnson@Sun.COM #endif /* __xpv */ 466*7347SMark.Johnson@Sun.COM 467*7347SMark.Johnson@Sun.COM 4684581Ssherrym static void 4694581Ssherrym ucode_read_rev(struct cpu_ucode_info *uinfop) 4704581Ssherrym { 4714581Ssherrym struct cpuid_regs crs; 4724581Ssherrym 4734581Ssherrym /* 4744581Ssherrym * The Intel 64 and IA-32 Architecture Software Developer's Manual 4754581Ssherrym * recommends that MSR_INTC_UCODE_REV be loaded with 0 first, then 4764581Ssherrym * execute cpuid to guarantee the correct reading of this register. 4774581Ssherrym */ 4784581Ssherrym wrmsr(MSR_INTC_UCODE_REV, 0); 4794581Ssherrym (void) __cpuid_insn(&crs); 4804581Ssherrym uinfop->cui_rev = (rdmsr(MSR_INTC_UCODE_REV) >> INTC_UCODE_REV_SHIFT); 4814581Ssherrym } 4824581Ssherrym 4834581Ssherrym /* 4844581Ssherrym * Entry point to microcode update from the ucode_drv driver. 4854581Ssherrym * 4864581Ssherrym * Returns EM_OK on success, corresponding error code on failure. 4874581Ssherrym */ 4884581Ssherrym ucode_errno_t 4894581Ssherrym ucode_update(uint8_t *ucodep, int size) 4904581Ssherrym { 4914581Ssherrym uint32_t header_size = UCODE_HEADER_SIZE; 4924581Ssherrym int remaining; 4934581Ssherrym int found = 0; 4944581Ssherrym processorid_t id; 4954581Ssherrym struct ucode_update_struct cached = { 0 }; 4964581Ssherrym struct ucode_update_struct *cachedp = NULL; 4974581Ssherrym ucode_errno_t rc = EM_OK; 4984581Ssherrym ucode_errno_t search_rc = EM_NOMATCH; /* search result */ 4994581Ssherrym cpuset_t cpuset; 500*7347SMark.Johnson@Sun.COM #ifdef __xpv 501*7347SMark.Johnson@Sun.COM uint8_t *ustart; 502*7347SMark.Johnson@Sun.COM uint32_t usize; 503*7347SMark.Johnson@Sun.COM #endif 5044581Ssherrym 5054581Ssherrym ASSERT(ucodep); 5064581Ssherrym CPUSET_ZERO(cpuset); 5074581Ssherrym 5084581Ssherrym if (!ucode_capable(CPU)) 5094581Ssherrym return (EM_NOTSUP); 5104581Ssherrym 5114581Ssherrym mutex_enter(&cpu_lock); 5124581Ssherrym 5134581Ssherrym for (id = 0; id < max_ncpus; id++) { 5144581Ssherrym cpu_t *cpu; 5154581Ssherrym struct ucode_update_struct uus = { 0 }; 5164581Ssherrym struct ucode_update_struct *uusp = &uus; 5174581Ssherrym 5184581Ssherrym /* 5194581Ssherrym * If there is no such CPU or it is not xcall ready, skip it. 5204581Ssherrym */ 5214581Ssherrym if ((cpu = cpu_get(id)) == NULL || 5224581Ssherrym !(cpu->cpu_flags & CPU_READY)) 5234581Ssherrym continue; 5244581Ssherrym 5254581Ssherrym uusp->sig = cpuid_getsig(cpu); 5264581Ssherrym bcopy(cpu->cpu_m.mcpu_ucode_info, &uusp->info, 5274581Ssherrym sizeof (uusp->info)); 5284581Ssherrym 5294581Ssherrym /* 5304581Ssherrym * If the current CPU has the same signature and platform 5314581Ssherrym * id as the previous one we processed, reuse the information. 5324581Ssherrym */ 5334581Ssherrym if (cachedp && cachedp->sig == cpuid_getsig(cpu) && 5344581Ssherrym cachedp->info.cui_platid == uusp->info.cui_platid) { 5354581Ssherrym uusp->ucodep = cachedp->ucodep; 5364581Ssherrym uusp->expected_rev = cachedp->expected_rev; 5374581Ssherrym /* 5384581Ssherrym * Intuitively we should check here to see whether the 5394581Ssherrym * running microcode rev is >= the expected rev, and 5404581Ssherrym * quit if it is. But we choose to proceed with the 5414581Ssherrym * xcall regardless of the running version so that 5424581Ssherrym * the other threads in an HT processor can update 5434581Ssherrym * the cpu_ucode_info structure in machcpu. 5444581Ssherrym */ 5454581Ssherrym } else { 5464581Ssherrym /* 5474581Ssherrym * Go through the whole buffer in case there are 5484581Ssherrym * multiple versions of matching microcode for this 5494581Ssherrym * processor. 5504581Ssherrym */ 5514581Ssherrym for (remaining = size; remaining > 0; ) { 5524581Ssherrym int total_size, body_size, ext_size; 5534581Ssherrym uint8_t *curbuf = &ucodep[size - remaining]; 5544581Ssherrym ucode_header_t *uhp = (ucode_header_t *)curbuf; 5554581Ssherrym ucode_ext_table_t *uetp = NULL; 5564581Ssherrym ucode_errno_t tmprc; 5574581Ssherrym 5584581Ssherrym total_size = 5594581Ssherrym UCODE_TOTAL_SIZE(uhp->uh_total_size); 5604581Ssherrym body_size = UCODE_BODY_SIZE(uhp->uh_body_size); 5614581Ssherrym ext_size = total_size - 5624581Ssherrym (header_size + body_size); 5634581Ssherrym 5644581Ssherrym if (ext_size > 0) 5654581Ssherrym uetp = (ucode_ext_table_t *) 5664581Ssherrym &curbuf[header_size + body_size]; 5674581Ssherrym 5684581Ssherrym tmprc = ucode_match(uusp->sig, &uusp->info, 5694581Ssherrym uhp, uetp); 5704581Ssherrym 5714581Ssherrym /* 5724581Ssherrym * Since we are searching through a big file 5734581Ssherrym * containing microcode for pretty much all the 5744581Ssherrym * processors, we are bound to get EM_NOMATCH 5754581Ssherrym * at one point. However, if we return 5764581Ssherrym * EM_NOMATCH to users, it will really confuse 5774581Ssherrym * them. Therefore, if we ever find a match of 5784581Ssherrym * a lower rev, we will set return code to 5794581Ssherrym * EM_HIGHERREV. 5804581Ssherrym */ 5814581Ssherrym if (tmprc == EM_HIGHERREV) 5824581Ssherrym search_rc = EM_HIGHERREV; 5834581Ssherrym 5844581Ssherrym if (tmprc == EM_OK && 5854581Ssherrym uusp->expected_rev < uhp->uh_rev) { 5864581Ssherrym uusp->ucodep = &curbuf[header_size]; 587*7347SMark.Johnson@Sun.COM #ifdef __xpv 588*7347SMark.Johnson@Sun.COM ustart = (uint8_t *)curbuf; 589*7347SMark.Johnson@Sun.COM usize = UCODE_TOTAL_SIZE( 590*7347SMark.Johnson@Sun.COM uhp->uh_total_size); 591*7347SMark.Johnson@Sun.COM #endif 5924581Ssherrym uusp->expected_rev = uhp->uh_rev; 5934581Ssherrym bcopy(uusp, &cached, sizeof (cached)); 5944581Ssherrym cachedp = &cached; 5954581Ssherrym found = 1; 5964581Ssherrym } 5974581Ssherrym 5984581Ssherrym remaining -= total_size; 5994581Ssherrym } 6004581Ssherrym } 6014581Ssherrym 6024581Ssherrym /* Nothing to do */ 6034581Ssherrym if (uusp->ucodep == NULL) 6044581Ssherrym continue; 6054581Ssherrym 606*7347SMark.Johnson@Sun.COM #ifdef __xpv 607*7347SMark.Johnson@Sun.COM /* 608*7347SMark.Johnson@Sun.COM * for i86xpv, the hypervisor will update all the CPUs. 609*7347SMark.Johnson@Sun.COM * the hypervisor wants the header, data, and extended 610*7347SMark.Johnson@Sun.COM * signature tables. ucode_write will just read in the 611*7347SMark.Johnson@Sun.COM * updated version on all the CPUs after the update has 612*7347SMark.Johnson@Sun.COM * completed. 613*7347SMark.Johnson@Sun.COM */ 614*7347SMark.Johnson@Sun.COM ucode_update_xpv(uusp, ustart, usize); 615*7347SMark.Johnson@Sun.COM #endif 616*7347SMark.Johnson@Sun.COM 6174581Ssherrym CPUSET_ADD(cpuset, id); 6184581Ssherrym kpreempt_disable(); 6194581Ssherrym xc_sync((xc_arg_t)uusp, 0, 0, X_CALL_HIPRI, cpuset, 6204581Ssherrym ucode_write); 6214581Ssherrym kpreempt_enable(); 6224581Ssherrym CPUSET_DEL(cpuset, id); 6234581Ssherrym 6244581Ssherrym if (uusp->expected_rev == uusp->new_rev) { 6254581Ssherrym cmn_err(CE_CONT, ucode_success_fmt, 6264581Ssherrym id, uusp->info.cui_rev, uusp->expected_rev); 6274581Ssherrym } else { 6284581Ssherrym cmn_err(CE_WARN, ucode_failure_fmt, 6294581Ssherrym id, uusp->info.cui_rev, uusp->expected_rev); 6304581Ssherrym rc = EM_UPDATE; 6314581Ssherrym } 6324581Ssherrym } 6334581Ssherrym 6344581Ssherrym mutex_exit(&cpu_lock); 6354581Ssherrym 6364581Ssherrym if (!found) 6374581Ssherrym rc = search_rc; 6384581Ssherrym 6394581Ssherrym return (rc); 6404581Ssherrym } 6414581Ssherrym 6424581Ssherrym /* 6434581Ssherrym * Initialize mcpu_ucode_info, and perform microcode update if necessary. 6444581Ssherrym * This is the entry point from boot path where pointer to CPU structure 6454581Ssherrym * is available. 6464581Ssherrym * 6474581Ssherrym * cpuid_info must be initialized before ucode_check can be called. 6484581Ssherrym */ 6494581Ssherrym void 6504581Ssherrym ucode_check(cpu_t *cp) 6514581Ssherrym { 6524581Ssherrym struct cpu_ucode_info *uinfop; 6534581Ssherrym ucode_errno_t rc = EM_OK; 654*7347SMark.Johnson@Sun.COM #ifdef __xpv 655*7347SMark.Johnson@Sun.COM uint32_t ext_offset; 656*7347SMark.Johnson@Sun.COM uint32_t body_size; 657*7347SMark.Johnson@Sun.COM uint32_t ext_size; 658*7347SMark.Johnson@Sun.COM uint8_t *ustart; 659*7347SMark.Johnson@Sun.COM uint32_t usize; 660*7347SMark.Johnson@Sun.COM #endif 6614581Ssherrym 6624581Ssherrym ASSERT(cp); 6634581Ssherrym if (cp->cpu_id == 0) 6644581Ssherrym cp->cpu_m.mcpu_ucode_info = &cpu_ucode_info0; 6654581Ssherrym 6664581Ssherrym uinfop = cp->cpu_m.mcpu_ucode_info; 6674581Ssherrym ASSERT(uinfop); 6684581Ssherrym 6694581Ssherrym if (!ucode_capable(cp)) 6704581Ssherrym return; 6714581Ssherrym 672*7347SMark.Johnson@Sun.COM #ifdef __xpv 673*7347SMark.Johnson@Sun.COM /* 674*7347SMark.Johnson@Sun.COM * for i86xpv, the hypervisor will update all the CPUs. We only need 675*7347SMark.Johnson@Sun.COM * do do this on one of the CPUs (and there always is a CPU 0). We do 676*7347SMark.Johnson@Sun.COM * need to update the CPU version though. Do that before returning. 677*7347SMark.Johnson@Sun.COM */ 678*7347SMark.Johnson@Sun.COM if (cp->cpu_id != 0) { 679*7347SMark.Johnson@Sun.COM ucode_read_rev(uinfop); 680*7347SMark.Johnson@Sun.COM return; 681*7347SMark.Johnson@Sun.COM } 682*7347SMark.Johnson@Sun.COM #endif 683*7347SMark.Johnson@Sun.COM 6844581Ssherrym /* 6854581Ssherrym * The MSR_INTC_PLATFORM_ID is supported in Celeron and Xeon 6864581Ssherrym * (Family 6, model 5 and above) and all processors after. 6874581Ssherrym */ 6884581Ssherrym if ((cpuid_getmodel(cp) >= 5) || (cpuid_getfamily(cp) > 6)) { 6894581Ssherrym uinfop->cui_platid = 1 << ((rdmsr(MSR_INTC_PLATFORM_ID) >> 6904581Ssherrym INTC_PLATFORM_ID_SHIFT) & INTC_PLATFORM_ID_MASK); 6914581Ssherrym } 6924581Ssherrym 6934581Ssherrym ucode_read_rev(uinfop); 6944581Ssherrym 6954581Ssherrym /* 6964581Ssherrym * Check to see if we need ucode update 6974581Ssherrym */ 6984581Ssherrym if ((rc = ucode_locate(cp, uinfop, &ucodefile)) == EM_OK) { 699*7347SMark.Johnson@Sun.COM #ifndef __xpv 7004581Ssherrym ucode_update_intel(ucodefile.uf_body, uinfop); 701*7347SMark.Johnson@Sun.COM #else 702*7347SMark.Johnson@Sun.COM /* 703*7347SMark.Johnson@Sun.COM * the hypervisor wants the header, data, and extended 704*7347SMark.Johnson@Sun.COM * signature tables. We can only get here from the boot 705*7347SMark.Johnson@Sun.COM * CPU (cpu #0), so use BOP_ALLOC. Since we're using BOP_ALLOC, 706*7347SMark.Johnson@Sun.COM * We don't need to free. 707*7347SMark.Johnson@Sun.COM */ 708*7347SMark.Johnson@Sun.COM usize = UCODE_TOTAL_SIZE(ucodefile.uf_header.uh_total_size); 709*7347SMark.Johnson@Sun.COM ustart = (uint8_t *)BOP_ALLOC(bootops, NULL, usize, 710*7347SMark.Johnson@Sun.COM MMU_PAGESIZE); 711*7347SMark.Johnson@Sun.COM 712*7347SMark.Johnson@Sun.COM body_size = UCODE_BODY_SIZE(ucodefile.uf_header.uh_body_size); 713*7347SMark.Johnson@Sun.COM ext_offset = body_size + UCODE_HEADER_SIZE; 714*7347SMark.Johnson@Sun.COM ext_size = usize - ext_offset; 715*7347SMark.Johnson@Sun.COM ASSERT(ext_size >= 0); 716*7347SMark.Johnson@Sun.COM 717*7347SMark.Johnson@Sun.COM (void) memcpy(ustart, &ucodefile.uf_header, UCODE_HEADER_SIZE); 718*7347SMark.Johnson@Sun.COM (void) memcpy(&ustart[UCODE_HEADER_SIZE], ucodefile.uf_body, 719*7347SMark.Johnson@Sun.COM body_size); 720*7347SMark.Johnson@Sun.COM if (ext_size > 0) { 721*7347SMark.Johnson@Sun.COM (void) memcpy(&ustart[ext_offset], 722*7347SMark.Johnson@Sun.COM ucodefile.uf_ext_table, ext_size); 723*7347SMark.Johnson@Sun.COM } 724*7347SMark.Johnson@Sun.COM ucode_update_xpv(NULL, ustart, usize); 725*7347SMark.Johnson@Sun.COM #endif 7264581Ssherrym 7274581Ssherrym if (uinfop->cui_rev != ucodefile.uf_header.uh_rev) 7284581Ssherrym cmn_err(CE_WARN, ucode_failure_fmt, cp->cpu_id, 7294581Ssherrym uinfop->cui_rev, ucodefile.uf_header.uh_rev); 7304581Ssherrym } 7314581Ssherrym 7324581Ssherrym /* 7334581Ssherrym * If we fail to find a match for any reason, free the file structure 7344581Ssherrym * just in case we have read in a partial file. 7354581Ssherrym * 7364581Ssherrym * Since the scratch memory for holding the microcode for the boot CPU 7374581Ssherrym * came from BOP_ALLOC, we will reset the data structure as if we 7384581Ssherrym * never did the allocation so we don't have to keep track of this 7394581Ssherrym * special chunk of memory. We free the memory used for the rest 7404581Ssherrym * of the CPUs in start_other_cpus(). 7414581Ssherrym */ 7424581Ssherrym if (rc != EM_OK || cp->cpu_id == 0) 7434581Ssherrym ucode_file_reset(&ucodefile, cp->cpu_id); 7444581Ssherrym } 7454581Ssherrym 7464581Ssherrym /* 7474581Ssherrym * Returns microcode revision from the machcpu structure. 7484581Ssherrym */ 7494581Ssherrym ucode_errno_t 7504581Ssherrym ucode_get_rev(uint32_t *revp) 7514581Ssherrym { 7524581Ssherrym int i; 7534581Ssherrym 7544581Ssherrym ASSERT(revp); 7554581Ssherrym 7564581Ssherrym if (!ucode_capable(CPU)) 7574581Ssherrym return (EM_NOTSUP); 7584581Ssherrym 7594581Ssherrym mutex_enter(&cpu_lock); 7604581Ssherrym for (i = 0; i < max_ncpus; i++) { 7614581Ssherrym cpu_t *cpu; 7624581Ssherrym 7634581Ssherrym if ((cpu = cpu_get(i)) == NULL) 7644581Ssherrym continue; 7654581Ssherrym 7664581Ssherrym revp[i] = cpu->cpu_m.mcpu_ucode_info->cui_rev; 7674581Ssherrym } 7684581Ssherrym mutex_exit(&cpu_lock); 7694581Ssherrym 7704581Ssherrym return (EM_OK); 7714581Ssherrym } 772