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: stable/11/sys/x86/x86/ucode.c 347700 2019-05-16 14:42:16Z 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, uint64_t *, uint64_t *); 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; 75static struct ucode_ops *ucode_loader; 76 77/* Variables used for reporting success or failure. */ 78enum { 79 NO_ERROR, 80 NO_MATCH, 81 VERIFICATION_FAILED, 82} ucode_error = NO_ERROR; 83static uint64_t ucode_nrev, ucode_orev; 84 85static void 86log_msg(void *arg __unused) 87{ 88 89 if (ucode_nrev != 0) { 90 printf("CPU microcode: updated from %#jx to %#jx\n", 91 (uintmax_t)ucode_orev, (uintmax_t)ucode_nrev); 92 return; 93 } 94 95 switch (ucode_error) { 96 case NO_MATCH: 97 printf("CPU microcode: no matching update found\n"); 98 break; 99 case VERIFICATION_FAILED: 100 printf("CPU microcode: microcode verification failed\n"); 101 break; 102 default: 103 break; 104 } 105} 106SYSINIT(ucode_log, SI_SUB_CPU, SI_ORDER_FIRST, log_msg, NULL); 107 108int 109ucode_intel_load(void *data, bool unsafe, uint64_t *nrevp, uint64_t *orevp) 110{ 111 uint64_t nrev, orev; 112 uint32_t cpuid[4]; 113 114 orev = rdmsr(MSR_BIOS_SIGN) >> 32; 115 116 /* 117 * Perform update. Flush caches first to work around seemingly 118 * undocumented errata applying to some Broadwell CPUs. 119 */ 120 wbinvd(); 121 if (unsafe) 122 wrmsr_safe(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data); 123 else 124 wrmsr(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data); 125 wrmsr(MSR_BIOS_SIGN, 0); 126 127 /* 128 * Serialize instruction flow. 129 */ 130 do_cpuid(0, cpuid); 131 132 /* 133 * Verify that the microcode revision changed. 134 */ 135 nrev = rdmsr(MSR_BIOS_SIGN) >> 32; 136 if (nrevp != NULL) 137 *nrevp = nrev; 138 if (orevp != NULL) 139 *orevp = orev; 140 if (nrev <= orev) 141 return (EEXIST); 142 return (0); 143} 144 145static int 146ucode_intel_verify(struct ucode_intel_header *hdr, size_t resid) 147{ 148 uint32_t cksum, *data, size; 149 int i; 150 151 if (resid < sizeof(struct ucode_intel_header)) 152 return (1); 153 size = hdr->total_size; 154 if (size == 0) 155 size = UCODE_INTEL_DEFAULT_DATA_SIZE + 156 sizeof(struct ucode_intel_header); 157 158 if (hdr->header_version != 1) 159 return (1); 160 if (size % 16 != 0) 161 return (1); 162 if (resid < size) 163 return (1); 164 165 cksum = 0; 166 data = (uint32_t *)hdr; 167 for (i = 0; i < size / sizeof(uint32_t); i++) 168 cksum += data[i]; 169 if (cksum != 0) 170 return (1); 171 return (0); 172} 173 174static void * 175ucode_intel_match(uint8_t *data, size_t *len) 176{ 177 struct ucode_intel_header *hdr; 178 struct ucode_intel_extsig_table *table; 179 struct ucode_intel_extsig *entry; 180 uint64_t platformid; 181 size_t resid; 182 uint32_t data_size, flags, regs[4], sig, total_size; 183 int i; 184 185 do_cpuid(1, regs); 186 sig = regs[0]; 187 188 platformid = rdmsr(MSR_IA32_PLATFORM_ID); 189 flags = 1 << ((platformid >> 50) & 0x7); 190 191 for (resid = *len; resid > 0; data += total_size, resid -= total_size) { 192 hdr = (struct ucode_intel_header *)data; 193 if (ucode_intel_verify(hdr, resid) != 0) { 194 ucode_error = VERIFICATION_FAILED; 195 break; 196 } 197 198 data_size = hdr->data_size; 199 total_size = hdr->total_size; 200 if (data_size == 0) 201 data_size = UCODE_INTEL_DEFAULT_DATA_SIZE; 202 if (total_size == 0) 203 total_size = UCODE_INTEL_DEFAULT_DATA_SIZE + 204 sizeof(struct ucode_intel_header); 205 if (data_size > total_size + sizeof(struct ucode_intel_header)) 206 table = (struct ucode_intel_extsig_table *) 207 ((uint8_t *)(hdr + 1) + data_size); 208 else 209 table = NULL; 210 211 if (hdr->processor_signature == sig) { 212 if ((hdr->processor_flags & flags) != 0) { 213 *len = data_size; 214 return (hdr + 1); 215 } 216 } else if (table != NULL) { 217 for (i = 0; i < table->signature_count; i++) { 218 entry = &table->entries[i]; 219 if (entry->processor_signature == sig && 220 (entry->processor_flags & flags) != 0) { 221 *len = data_size; 222 return (hdr + 1); 223 } 224 } 225 } 226 } 227 return (NULL); 228} 229 230/* 231 * Release any memory backing unused microcode blobs back to the system. 232 * We copy the selected update and free the entire microcode file. 233 */ 234static void 235ucode_release(void *arg __unused) 236{ 237 char *name, *type; 238 caddr_t file; 239 int release; 240 241 if (early_ucode_data == NULL) 242 return; 243 release = 1; 244 TUNABLE_INT_FETCH("debug.ucode.release", &release); 245 if (!release) 246 return; 247 248restart: 249 file = 0; 250 for (;;) { 251 file = preload_search_next_name(file); 252 if (file == 0) 253 break; 254 type = (char *)preload_search_info(file, MODINFO_TYPE); 255 if (type == NULL || strcmp(type, "cpu_microcode") != 0) 256 continue; 257 258 name = preload_search_info(file, MODINFO_NAME); 259 preload_delete_name(name); 260 goto restart; 261 } 262} 263SYSINIT(ucode_release, SI_SUB_KMEM + 1, SI_ORDER_ANY, ucode_release, NULL); 264 265void 266ucode_load_ap(int cpu) 267{ 268#ifdef SMP 269 KASSERT(cpu_info[cpu_apic_ids[cpu]].cpu_present, 270 ("cpu %d not present", cpu)); 271 272 if (cpu_info[cpu_apic_ids[cpu]].cpu_hyperthread) 273 return; 274#endif 275 276 if (ucode_data != NULL) 277 (void)ucode_loader->load(ucode_data, false, NULL, NULL); 278} 279 280static void * 281map_ucode(uintptr_t free, size_t len) 282{ 283#ifdef __i386__ 284 uintptr_t va; 285 286 for (va = free; va < free + len; va += PAGE_SIZE) 287 pmap_kenter(va, (vm_paddr_t)va); 288#else 289 (void)len; 290#endif 291 return ((void *)free); 292} 293 294static void 295unmap_ucode(uintptr_t free, size_t len) 296{ 297#ifdef __i386__ 298 uintptr_t va; 299 300 for (va = free; va < free + len; va += PAGE_SIZE) 301 pmap_kremove(va); 302#else 303 (void)free; 304 (void)len; 305#endif 306} 307 308/* 309 * Search for an applicable microcode update, and load it. APs will load the 310 * selected update once they come online. 311 * 312 * "free" is the address of the next free physical page. If a microcode update 313 * is selected, it will be copied to this region prior to loading in order to 314 * satisfy alignment requirements. 315 */ 316size_t 317ucode_load_bsp(uintptr_t free) 318{ 319 union { 320 uint32_t regs[4]; 321 char vendor[13]; 322 } cpuid; 323 uint8_t *addr, *fileaddr, *match; 324 char *type; 325 uint64_t nrev, orev; 326 caddr_t file; 327 size_t i, len; 328 int error; 329 330 KASSERT(free % PAGE_SIZE == 0, ("unaligned boundary %p", (void *)free)); 331 332 do_cpuid(0, cpuid.regs); 333 cpuid.regs[0] = cpuid.regs[1]; 334 cpuid.regs[1] = cpuid.regs[3]; 335 cpuid.vendor[12] = '\0'; 336 for (i = 0; i < nitems(loaders); i++) 337 if (strcmp(cpuid.vendor, loaders[i].vendor) == 0) { 338 ucode_loader = &loaders[i]; 339 break; 340 } 341 if (ucode_loader == NULL) 342 return (0); 343 344 file = 0; 345 fileaddr = match = NULL; 346 for (;;) { 347 file = preload_search_next_name(file); 348 if (file == 0) 349 break; 350 type = (char *)preload_search_info(file, MODINFO_TYPE); 351 if (type == NULL || strcmp(type, "cpu_microcode") != 0) 352 continue; 353 354 fileaddr = preload_fetch_addr(file); 355 len = preload_fetch_size(file); 356 match = ucode_loader->match(fileaddr, &len); 357 if (match != NULL) { 358 addr = map_ucode(free, len); 359 /* We can't use memcpy() before ifunc resolution. */ 360 for (i = 0; i < len; i++) 361 addr[i] = ((volatile uint8_t *)match)[i]; 362 match = addr; 363 364 error = ucode_loader->load(match, false, &nrev, &orev); 365 if (error == 0) { 366 ucode_data = early_ucode_data = match; 367 ucode_nrev = nrev; 368 ucode_orev = orev; 369 return (len); 370 } 371 unmap_ucode(free, len); 372 } 373 } 374 if (fileaddr != NULL && ucode_error == NO_ERROR) 375 ucode_error = NO_MATCH; 376 return (0); 377} 378 379/* 380 * Reload microcode following an ACPI resume. 381 */ 382void 383ucode_reload(void) 384{ 385 386 ucode_load_ap(PCPU_GET(cpuid)); 387} 388 389/* 390 * Replace an existing microcode update. 391 */ 392void * 393ucode_update(void *newdata) 394{ 395 396 newdata = (void *)atomic_swap_ptr((void *)&ucode_data, 397 (uintptr_t)newdata); 398 if (newdata == early_ucode_data) 399 newdata = NULL; 400 return (newdata); 401} 402