ucode.c revision 337715
1/*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2018 The FreeBSD Foundation 5 * 6 * This software was developed by Mark Johnston under sponsorship from 7 * the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31#include <sys/cdefs.h> 32__FBSDID("$FreeBSD: head/sys/x86/x86/ucode.c 337715 2018-08-13 17:13:09Z markj $"); 33 34#include <sys/param.h> 35#include <sys/cpuset.h> 36#include <sys/kernel.h> 37#include <sys/linker.h> 38#include <sys/malloc.h> 39#include <sys/pcpu.h> 40#include <sys/smp.h> 41#include <sys/systm.h> 42 43#include <machine/atomic.h> 44#include <machine/cpufunc.h> 45#include <x86/specialreg.h> 46#include <machine/stdarg.h> 47#include <x86/ucode.h> 48#include <x86/x86_smp.h> 49 50#include <vm/vm.h> 51#include <vm/pmap.h> 52#include <vm/vm_extern.h> 53#include <vm/vm_kern.h> 54#include <vm/vm_param.h> 55 56static void *ucode_intel_match(uint8_t *data, size_t *len); 57static int ucode_intel_verify(struct ucode_intel_header *hdr, 58 size_t resid); 59 60static struct ucode_ops { 61 const char *vendor; 62 int (*load)(void *, bool); 63 void *(*match)(uint8_t *, size_t *); 64} loaders[] = { 65 { 66 .vendor = INTEL_VENDOR_ID, 67 .load = ucode_intel_load, 68 .match = ucode_intel_match, 69 }, 70}; 71 72/* Selected microcode update data. */ 73static void *early_ucode_data; 74static void *ucode_data; 75 76static char errbuf[128]; 77 78static void __printflike(1, 2) 79log_err(const char *fmt, ...) 80{ 81 va_list ap; 82 83 va_start(ap, fmt); 84 vsnprintf(errbuf, sizeof(errbuf), fmt, ap); 85 va_end(ap); 86} 87 88static void 89print_err(void *arg __unused) 90{ 91 92 if (errbuf[0] != '\0') 93 printf("microcode load error: %s\n", errbuf); 94} 95SYSINIT(ucode_print_err, SI_SUB_CPU, SI_ORDER_FIRST, print_err, NULL); 96 97int 98ucode_intel_load(void *data, bool unsafe) 99{ 100 uint64_t rev0, rev1; 101 uint32_t cpuid[4]; 102 103 rev0 = rdmsr(MSR_BIOS_SIGN); 104 105 /* 106 * Perform update. Flush caches first to work around seemingly 107 * undocumented errata applying to some Broadwell CPUs. 108 */ 109 wbinvd(); 110 if (unsafe) 111 wrmsr_safe(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data); 112 else 113 wrmsr(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data); 114 wrmsr(MSR_BIOS_SIGN, 0); 115 116 /* 117 * Serialize instruction flow. 118 */ 119 do_cpuid(0, cpuid); 120 121 rev1 = rdmsr(MSR_BIOS_SIGN); 122 if (rev1 <= rev0) 123 return (EEXIST); 124 return (0); 125} 126 127static int 128ucode_intel_verify(struct ucode_intel_header *hdr, size_t resid) 129{ 130 uint32_t cksum, *data, size; 131 int i; 132 133 if (resid < sizeof(struct ucode_intel_header)) { 134 log_err("truncated update header"); 135 return (1); 136 } 137 size = hdr->total_size; 138 if (size == 0) 139 size = UCODE_INTEL_DEFAULT_DATA_SIZE + 140 sizeof(struct ucode_intel_header); 141 142 if (hdr->header_version != 1) { 143 log_err("unexpected header version %u", hdr->header_version); 144 return (1); 145 } 146 if (size % 16 != 0) { 147 log_err("unexpected update size %u", hdr->total_size); 148 return (1); 149 } 150 if (resid < size) { 151 log_err("truncated update"); 152 return (1); 153 } 154 155 cksum = 0; 156 data = (uint32_t *)hdr; 157 for (i = 0; i < size / sizeof(uint32_t); i++) 158 cksum += data[i]; 159 if (cksum != 0) { 160 log_err("checksum failed"); 161 return (1); 162 } 163 return (0); 164} 165 166static void * 167ucode_intel_match(uint8_t *data, size_t *len) 168{ 169 struct ucode_intel_header *hdr; 170 struct ucode_intel_extsig_table *table; 171 struct ucode_intel_extsig *entry; 172 uint64_t platformid; 173 size_t resid; 174 uint32_t data_size, flags, regs[4], sig, total_size; 175 int i; 176 177 do_cpuid(1, regs); 178 sig = regs[0]; 179 180 platformid = rdmsr(MSR_IA32_PLATFORM_ID); 181 flags = 1 << ((platformid >> 50) & 0x7); 182 183 for (resid = *len; resid > 0; data += total_size, resid -= total_size) { 184 hdr = (struct ucode_intel_header *)data; 185 if (ucode_intel_verify(hdr, resid) != 0) 186 break; 187 188 data_size = hdr->data_size; 189 total_size = hdr->total_size; 190 if (data_size == 0) 191 data_size = UCODE_INTEL_DEFAULT_DATA_SIZE; 192 if (total_size == 0) 193 total_size = UCODE_INTEL_DEFAULT_DATA_SIZE + 194 sizeof(struct ucode_intel_header); 195 if (data_size > total_size + sizeof(struct ucode_intel_header)) 196 table = (struct ucode_intel_extsig_table *) 197 ((uint8_t *)(hdr + 1) + data_size); 198 else 199 table = NULL; 200 201 if (hdr->processor_signature == sig) { 202 if ((hdr->processor_flags & flags) != 0) { 203 *len = data_size; 204 return (hdr + 1); 205 } 206 } else if (table != NULL) { 207 for (i = 0; i < table->signature_count; i++) { 208 entry = &table->entries[i]; 209 if (entry->processor_signature == sig && 210 (entry->processor_flags & flags) != 0) { 211 *len = data_size; 212 return (hdr + 1); 213 } 214 } 215 } 216 } 217 return (NULL); 218} 219 220/* 221 * Release any memory backing unused microcode blobs back to the system. 222 * We copy the selected update and free the entire microcode file. 223 */ 224static void 225ucode_release(void *arg __unused) 226{ 227 char *name, *type; 228 caddr_t file; 229 int release; 230 231 if (early_ucode_data == NULL) 232 return; 233 release = 1; 234 TUNABLE_INT_FETCH("debug.ucode.release", &release); 235 if (!release) 236 return; 237 238restart: 239 file = 0; 240 for (;;) { 241 file = preload_search_next_name(file); 242 if (file == 0) 243 break; 244 type = (char *)preload_search_info(file, MODINFO_TYPE); 245 if (type == NULL || strcmp(type, "cpu_microcode") != 0) 246 continue; 247 248 name = preload_search_info(file, MODINFO_NAME); 249 preload_delete_name(name); 250 goto restart; 251 } 252} 253SYSINIT(ucode_release, SI_SUB_KMEM + 1, SI_ORDER_ANY, ucode_release, NULL); 254 255void 256ucode_load_ap(int cpu) 257{ 258 259 KASSERT(cpu_info[cpu_apic_ids[cpu]].cpu_present, 260 ("cpu %d not present", cpu)); 261 262 if (ucode_data != NULL && !cpu_info[cpu_apic_ids[cpu]].cpu_hyperthread) 263 (void)ucode_intel_load(ucode_data, false); 264} 265 266static void * 267map_ucode(vm_paddr_t free, size_t len) 268{ 269 270#ifdef __i386__ 271 for (vm_paddr_t pa = free; pa < free + len; pa += PAGE_SIZE) 272 pmap_kenter(pa, pa); 273#else 274 (void)len; 275#endif 276 return ((void *)free); 277} 278 279static void 280unmap_ucode(vm_paddr_t free, size_t len) 281{ 282 283#ifdef __i386__ 284 for (vm_paddr_t pa = free; pa < free + len; pa += PAGE_SIZE) 285 pmap_kremove((vm_offset_t)pa); 286#else 287 (void)free; 288 (void)len; 289#endif 290} 291 292/* 293 * Search for an applicable microcode update, and load it. APs will load the 294 * selected update once they come online. 295 * 296 * "free" is the address of the next free physical page. If a microcode update 297 * is selected, it will be copied to this region prior to loading in order to 298 * satisfy alignment requirements. 299 */ 300size_t 301ucode_load_bsp(uintptr_t free) 302{ 303 union { 304 uint32_t regs[4]; 305 char vendor[13]; 306 } cpuid; 307 struct ucode_ops *loader; 308 uint8_t *addr, *fileaddr, *match; 309 char *type; 310 caddr_t file; 311 size_t len, ucode_len; 312 int i; 313 314 KASSERT(free % PAGE_SIZE == 0, ("unaligned boundary %p", (void *)free)); 315 316 do_cpuid(0, cpuid.regs); 317 cpuid.regs[0] = cpuid.regs[1]; 318 cpuid.regs[1] = cpuid.regs[3]; 319 cpuid.vendor[12] = '\0'; 320 for (i = 0, loader = NULL; i < nitems(loaders); i++) 321 if (strcmp(cpuid.vendor, loaders[i].vendor) == 0) { 322 loader = &loaders[i]; 323 break; 324 } 325 if (loader == NULL) 326 return (0); 327 328 file = 0; 329 fileaddr = match = NULL; 330 ucode_len = 0; 331 for (;;) { 332 file = preload_search_next_name(file); 333 if (file == 0) 334 break; 335 type = (char *)preload_search_info(file, MODINFO_TYPE); 336 if (type == NULL || strcmp(type, "cpu_microcode") != 0) 337 continue; 338 339 fileaddr = preload_fetch_addr(file); 340 len = preload_fetch_size(file); 341 match = loader->match(fileaddr, &len); 342 if (match != NULL) { 343 addr = map_ucode(free, len); 344 memcpy(addr, match, len); 345 match = addr; 346 347 if (loader->load(match, false) == 0) { 348 ucode_data = match; 349 ucode_len = len; 350 early_ucode_data = ucode_data; 351 break; 352 } 353 unmap_ucode(free, len); 354 } 355 } 356 if (fileaddr != NULL && ucode_data == NULL) 357 log_err("no matching update found"); 358 return (ucode_len); 359} 360 361/* 362 * Reload microcode following an ACPI resume. 363 */ 364void 365ucode_reload(void) 366{ 367 368 ucode_load_ap(PCPU_GET(cpuid)); 369} 370 371/* 372 * Replace an existing microcode update. 373 */ 374void * 375ucode_update(void *newdata) 376{ 377 378 newdata = (void *)atomic_swap_ptr((void *)&ucode_data, 379 (uintptr_t)newdata); 380 if (newdata == early_ucode_data) 381 newdata = NULL; 382 return (newdata); 383} 384