1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * Copyright (c) 2018, Joyent, Inc. 28 * Copyright 2022 OmniOS Community Edition (OmniOSce) Association. 29 * Copyright 2025 Oxide Computer Company 30 */ 31 32 #include <sys/bitext.h> 33 #include <sys/types.h> 34 #include <sys/processor.h> 35 #include <sys/sysmacros.h> 36 #include <sys/ucode.h> 37 #include <sys/ucode_intel.h> 38 #include <sys/ucode_amd.h> 39 #include <sys/utsname.h> 40 #include <sys/ioctl.h> 41 #include <sys/stat.h> 42 #include <unistd.h> 43 #include <dirent.h> 44 #include <fcntl.h> 45 #include <errno.h> 46 #include <stdbool.h> 47 #include <stdint.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <stdarg.h> 51 #include <string.h> 52 #include <strings.h> 53 #include <errno.h> 54 #include <syslog.h> 55 #include <time.h> 56 #include <ctype.h> 57 #include <assert.h> 58 #include <libgen.h> 59 #include <limits.h> 60 #include <locale.h> 61 #include <libintl.h> 62 #include <ucode/ucode_errno.h> 63 #include <ucode/ucode_utils_intel.h> 64 #include <ucode/ucode_utils_amd.h> 65 66 #define UCODE_OPT_INSTALL 0x0001 67 #define UCODE_OPT_UPDATE 0x0002 68 #define UCODE_OPT_VERSION 0x0004 69 #define UCODE_OPT_LIST 0x0008 70 71 static const char ucode_dev[] = "/dev/" UCODE_DRIVER_NAME; 72 73 static char *cmdname; 74 75 #define UCODE_INSTALL_COMMON_PATH ".f" 76 77 /* 78 * The maximum directory path length that can be provided via -R has 79 * to allow for appending the files within the microcode bundles. 80 */ 81 #define UCODE_MAX_PATH_LEN (PATH_MAX - \ 82 MAX(UCODE_MAX_NAME_LEN_INTEL, UCODE_MAX_NAME_LEN_AMD) - 1) 83 84 static bool ucode_debug = false; 85 86 static ucode_errno_t ucode_convert_amd(const char *, uint8_t **, size_t *); 87 static ucode_errno_t ucode_convert_intel(const char *, uint8_t **, size_t *); 88 89 static ucode_errno_t ucode_gen_files_amd(uint8_t *, size_t, const char *); 90 static ucode_errno_t ucode_gen_files_intel(uint8_t *, size_t, const char *); 91 92 static void ucode_list_amd(uint8_t *, size_t); 93 static void ucode_list_intel(uint8_t *, size_t); 94 95 typedef struct ucode_source { 96 const char *us_prefix; 97 const char *us_vendor; 98 ucode_errno_t (*us_convert)(const char *, uint8_t **, size_t *); 99 ucode_errno_t (*us_gen_files)(uint8_t *, size_t, const char *); 100 ucode_errno_t (*us_validate)(uint8_t *, size_t); 101 void (*us_list)(uint8_t *, size_t); 102 } ucode_source_t; 103 104 static const ucode_source_t ucode_sources[] = { 105 { 106 .us_prefix = "intel", 107 .us_vendor = "GenuineIntel", 108 .us_convert = ucode_convert_intel, 109 .us_gen_files = ucode_gen_files_intel, 110 .us_validate = ucode_validate_intel, 111 .us_list = ucode_list_intel, 112 }, 113 { 114 .us_prefix = "amd", 115 .us_vendor = "AuthenticAMD", 116 .us_convert = ucode_convert_amd, 117 .us_gen_files = ucode_gen_files_amd, 118 .us_validate = ucode_validate_amd, 119 .us_list = ucode_list_amd, 120 } 121 }; 122 123 const ucode_source_t *ucode; 124 125 static void 126 dbgprintf(const char *format, ...) 127 { 128 if (ucode_debug) { 129 va_list alist; 130 va_start(alist, format); 131 (void) vfprintf(stderr, format, alist); 132 va_end(alist); 133 } 134 } 135 136 static void 137 usage(bool verbose) 138 { 139 (void) fprintf(stderr, gettext("usage:\n")); 140 (void) fprintf(stderr, "\t%s -v\n", cmdname); 141 if (verbose) { 142 (void) fprintf(stderr, 143 gettext("\t\t Shows running microcode version.\n\n")); 144 } 145 146 (void) fprintf(stderr, "\t%s -u [-t type] microcode-file\n", cmdname); 147 if (verbose) { 148 (void) fprintf(stderr, gettext("\t\t Updates microcode to the " 149 "latest matching version found in\n" 150 "\t\t microcode-file.\n\n")); 151 } 152 153 (void) fprintf(stderr, "\t%s -l [-t type] microcode-file\n", cmdname); 154 if (verbose) { 155 (void) fprintf(stderr, gettext("\t\t Displays details of the " 156 "microcode file's contents.\n\n")); 157 } 158 159 (void) fprintf(stderr, 160 "\t%s -i [-t type] [-R path] microcode-file\n", cmdname); 161 if (verbose) { 162 (void) fprintf(stderr, gettext("\t\t Installs microcode to be " 163 "used for subsequent boots.\n")); 164 } 165 (void) fprintf(stderr, gettext( 166 "\nThe type of the microcode file must either be specified with " 167 "the -t option\nor microcode-file must start with the vendor name " 168 "prefix, either \"intel\"\nor \"amd\", so that the type can be " 169 "inferred from it.\n\n")); 170 } 171 172 static void 173 ucode_perror(const char *str, ucode_errno_t rc) 174 { 175 (void) fprintf(stderr, "%s: %s: %s\n", cmdname, str, 176 errno == 0 ? ucode_strerror(rc) : strerror(errno)); 177 errno = 0; 178 } 179 180 static int 181 bcd_to_int(uint8_t b) 182 { 183 int high = (b >> 4) & 0xf; 184 int low = b & 0xf; 185 186 if (high > 9 || low > 9) 187 return (-1); 188 return (high * 10 + low); 189 } 190 191 /* 192 * Extract the family, model and stepping values from a 32-bit CPU signature. 193 * These bit fields are defined by the Intel Application Note AP-485 194 * "Intel Processor Identification and the CPUID Instruction" 195 */ 196 static void 197 ucode_fms(uint32_t sig, uint8_t *family, uint8_t *model, uint8_t *stepping) 198 { 199 const uint8_t xfamily = bitx32(sig, 27, 20); 200 const uint8_t bfamily = bitx32(sig, 11, 8); 201 const uint8_t xmodel = bitx32(sig, 19, 16); 202 const uint8_t bmodel = bitx32(sig, 7, 4); 203 204 *family = bfamily == 0xf ? bfamily + xfamily : bfamily; 205 *model = bfamily == 0x6 || bfamily == 0xf ? 206 (xmodel << 4) | bmodel : bmodel; 207 *stepping = bitx32(sig, 3, 0); 208 } 209 210 /* 211 * AMD microcode updates use a compressed 16-bit CPU identifier called an 212 * Equivalent Processor ID. The compression is achieved by removing the base 213 * family field, and assuming it is always 0xf, and reducing the size of the 214 * extended family field to 4 bits such that only families up to 0x1e can be 215 * represented. The structure is: 216 * 217 * [15:12] Extended Family (i.e. family - 0xf) 218 * [11:4] Model 219 * [3:0] Stepping 220 * 221 * This function expands an AMD Equivalent Processor ID to a traditional 222 * 32-bit CPU signature. 223 */ 224 static uint32_t 225 amd_equivcpu_to_sig(uint16_t equiv) 226 { 227 uint16_t xfamily, model, stepping; 228 uint32_t sig = 0; 229 230 xfamily = bitx16(equiv, 15, 12); 231 model = bitx16(equiv, 11, 4); 232 stepping = bitx16(equiv, 3, 0); 233 234 sig = bitset32(sig, 27, 20, xfamily); /* ext family */ 235 sig = bitset32(sig, 11, 8, 0xf); /* family */ 236 sig = bitset32(sig, 19, 16, bitx16(model, 7, 4)); /* ext model */ 237 sig = bitset32(sig, 7, 4, bitx16(model, 3, 0)); /* model */ 238 sig = bitset32(sig, 3, 0, stepping); 239 240 return (sig); 241 } 242 243 /* 244 * Load a microcode release which is in AMD's binary container format. If the 245 * provided file appears to be a raw binary update, cons up a container 246 * containing just that patch. 247 */ 248 static ucode_errno_t 249 ucode_convert_amd(const char *infile, uint8_t **bufp, size_t *sizep) 250 { 251 ucode_header_amd_t *patch; 252 ucode_section_amd_t *section; 253 ucode_eqtbl_amd_t *eq; 254 int month, day, yearl, fd; 255 size_t csize; 256 ssize_t rsize; 257 uint8_t *buf = *bufp; 258 size_t size = *sizep; 259 260 if (infile == NULL || buf == NULL || size == 0) 261 return (EM_INVALIDARG); 262 263 if ((fd = open(infile, O_RDONLY)) < 0) 264 return (EM_SYS); 265 266 rsize = read(fd, buf, size); 267 if (rsize < 0) { 268 int _errno = errno; 269 (void) close(fd); 270 errno = _errno; 271 return (EM_SYS); 272 } 273 274 (void) close(fd); 275 276 if (rsize == 0) 277 return (EM_FILEFORMAT); 278 279 size = rsize; 280 281 /* 282 * AMD microcode is distributed in two forms. As container/bundle files 283 * or as individual binary patches. If this looks like a container, 284 * we're done. 285 */ 286 if (ucode->us_validate(buf, size) == EM_OK) { 287 *sizep = size; 288 return (EM_OK); 289 } 290 291 /* 292 * Otherwise, see if this looks like a binary patch. We're limited in 293 * what we can check here but we can look at the date field to see if 294 * it is plausible. That field is encoded as a kind of packed 295 * BCD 0xMMDDYYYY. 296 */ 297 patch = (ucode_header_amd_t *)*bufp; 298 month = bcd_to_int(bitx32(patch->uh_date, 31, 24)); 299 day = bcd_to_int(bitx32(patch->uh_date, 23, 16)); 300 yearl = bcd_to_int(bitx32(patch->uh_date, 7, 0)); 301 if (day < 1 || day > 31 || month < 1 || month > 12 || 302 yearl < 0 || yearl > 99) { 303 dbgprintf("implausible date code: 0x%x\n", patch->uh_date); 304 return (EM_FILEFORMAT); 305 } 306 307 /* It's plausibly a binary patch; cons up a container */ 308 dbgprintf("creating microcode container\n"); 309 csize = 310 sizeof (uint32_t) + /* Magic */ 311 2 * sizeof (ucode_section_amd_t) + /* TLV headers */ 312 2 * sizeof (ucode_eqtbl_amd_t); /* Equivalence table */ 313 if (size > SIZE_MAX - csize) { 314 dbgprintf("container size too large (patch size %zu)\n", size); 315 return (EM_FILEFORMAT); 316 } 317 csize += size; /* Patch payload */ 318 319 buf = realloc(*bufp, csize); 320 if (buf == NULL) 321 return (EM_SYS); 322 323 /* Relocate the patch data */ 324 patch = (ucode_header_amd_t *)(buf + csize - size); 325 bcopy(buf, patch, size); 326 327 /* Build the container */ 328 *(uint32_t *)buf = UCODE_AMD_CONTAINER_MAGIC; 329 330 /* Equivalence table section */ 331 section = (ucode_section_amd_t *)(buf + sizeof (uint32_t)); 332 eq = (ucode_eqtbl_amd_t *)section->usa_data; 333 334 section->usa_type = UCODE_AMD_CONTAINER_TYPE_EQUIV; 335 section->usa_size = 2 * sizeof (*eq); 336 eq->ue_equiv_cpu = patch->uh_cpu_rev; 337 eq->ue_inst_cpu = amd_equivcpu_to_sig(patch->uh_cpu_rev); 338 /* Create the equivalence table terminator record */ 339 bzero(eq + 1, sizeof (*eq)); 340 341 /* Patch section */ 342 section = 343 (ucode_section_amd_t *)(section->usa_data + section->usa_size); 344 section->usa_type = UCODE_AMD_CONTAINER_TYPE_PATCH; 345 section->usa_size = size; 346 347 *bufp = buf; 348 *sizep = csize; 349 350 return (EM_OK); 351 } 352 353 /* 354 * Convert text format microcode release into binary format. 355 */ 356 #define LINESIZE 120 /* copyright line sometimes is longer than 80 */ 357 static ucode_errno_t 358 ucode_convert_intel(const char *infile, uint8_t **bufp, size_t *sizep) 359 { 360 char linebuf[LINESIZE]; 361 FILE *infd = NULL; 362 bool firstline = true; 363 size_t count = 0; 364 uint8_t *buf = *bufp; 365 size_t size = *sizep; 366 uint32_t *intbuf = (uint32_t *)(uintptr_t)buf; 367 368 if (infile == NULL || buf == NULL || size == 0) 369 return (EM_INVALIDARG); 370 371 if ((infd = fopen(infile, "r")) == NULL) 372 return (EM_SYS); 373 374 while (fgets(linebuf, LINESIZE, infd)) { 375 376 /* Check to see if we are processing a binary file */ 377 if (firstline && !isprint(linebuf[0])) { 378 if (fseek(infd, 0, SEEK_SET) == 0) 379 count = fread(buf, 1, size, infd); 380 381 (void) fclose(infd); 382 383 if (count == 0) 384 return (EM_FILEFORMAT); 385 386 *sizep = count; 387 return (EM_OK); 388 } 389 390 firstline = false; 391 392 /* Skip blank lines */ 393 if (strlen(linebuf) == 1) 394 continue; 395 396 /* Skip lines with all spaces or tabs */ 397 if (strcspn(linebuf, " \t") == 0) 398 continue; 399 400 /* Text file. Skip comments. */ 401 if (linebuf[0] == '/') 402 continue; 403 404 if (sscanf(linebuf, "%x, %x, %x, %x", 405 &intbuf[count], &intbuf[count+1], 406 &intbuf[count+2], &intbuf[count+3]) != 4) 407 break; 408 409 count += 4; 410 } 411 412 (void) fclose(infd); 413 414 /* 415 * If we get here, we are processing a text format file 416 * where "count" is used to count the number of integers 417 * read. Convert it to number of characters read. 418 */ 419 *sizep = count * sizeof (int); 420 421 return (EM_OK); 422 } 423 424 /* 425 * Returns 0 if no need to update the link; -1 otherwise 426 */ 427 static int 428 ucode_should_update_intel(char *filename, uint32_t new_rev) 429 { 430 int fd; 431 struct stat statbuf; 432 ucode_header_intel_t header; 433 434 /* 435 * If the file or link already exists, check to see if 436 * it is necessary to update it. 437 */ 438 if (stat(filename, &statbuf) == 0) { 439 if ((fd = open(filename, O_RDONLY)) == -1) 440 return (-1); 441 442 if (read(fd, &header, sizeof (header)) == -1) { 443 (void) close(fd); 444 return (-1); 445 } 446 447 (void) close(fd); 448 449 if (header.uh_rev >= new_rev) 450 return (0); 451 } 452 453 return (-1); 454 } 455 456 /* 457 * Generate microcode binary files. Must be called after ucode_validate(). 458 */ 459 static ucode_errno_t 460 ucode_gen_files_amd(uint8_t *buf, size_t size, const char *path) 461 { 462 char common_path[PATH_MAX]; 463 int fd; 464 uint16_t last_cpu_rev = 0; 465 uint32_t counter = 0; 466 int n; 467 468 /* write container file */ 469 n = snprintf(common_path, sizeof (common_path), "%s/container", path); 470 if (n >= sizeof (common_path)) { 471 dbgprintf("failed to construct container path\n"); 472 return (EM_FILEFORMAT); 473 } 474 475 dbgprintf("path = %s\n", common_path); 476 fd = open(common_path, O_WRONLY | O_CREAT | O_TRUNC, 477 S_IRUSR | S_IRGRP | S_IROTH); 478 479 if (fd == -1) { 480 ucode_perror(common_path, EM_SYS); 481 return (EM_SYS); 482 } 483 484 if (write(fd, buf, size) != size) { 485 (void) close(fd); 486 ucode_perror(common_path, EM_SYS); 487 return (EM_SYS); 488 } 489 490 (void) close(fd); 491 492 /* skip over magic number, the file has already been validated */ 493 buf += sizeof (uint32_t); 494 size -= sizeof (uint32_t); 495 496 while (size > sizeof (ucode_section_amd_t)) { 497 ucode_section_amd_t *section = (ucode_section_amd_t *)buf; 498 int n; 499 500 switch (section->usa_type) { 501 case UCODE_AMD_CONTAINER_TYPE_EQUIV: 502 n = snprintf(common_path, sizeof (common_path), "%s/%s", 503 path, UCODE_AMD_EQUIVALENCE_TABLE_NAME); 504 break; 505 case UCODE_AMD_CONTAINER_TYPE_PATCH: { 506 ucode_header_amd_t *uh = 507 (ucode_header_amd_t *)section->usa_data; 508 509 if (uh->uh_cpu_rev != last_cpu_rev) { 510 last_cpu_rev = uh->uh_cpu_rev; 511 counter = 0; 512 } 513 514 n = snprintf(common_path, sizeof (common_path), 515 "%s/%04X-%02X", path, uh->uh_cpu_rev, counter++); 516 break; 517 } 518 default: 519 /* 520 * Since the container has already been validated, this 521 * should never happen. 522 */ 523 return (EM_FILEFORMAT); 524 } 525 526 if (n >= sizeof (common_path)) { 527 dbgprintf("failed to construct component path\n"); 528 return (EM_FILEFORMAT); 529 } 530 531 dbgprintf("path = %s\n", common_path); 532 fd = open(common_path, O_WRONLY | O_CREAT | O_TRUNC, 533 S_IRUSR | S_IRGRP | S_IROTH); 534 535 if (fd == -1) { 536 ucode_perror(common_path, EM_SYS); 537 return (EM_SYS); 538 } 539 540 if (write(fd, section->usa_data, section->usa_size) != 541 section->usa_size) { 542 (void) close(fd); 543 ucode_perror(common_path, EM_SYS); 544 return (EM_SYS); 545 } 546 547 (void) close(fd); 548 549 size -= section->usa_size + sizeof (ucode_section_amd_t); 550 buf += section->usa_size + sizeof (ucode_section_amd_t); 551 } 552 553 return (EM_OK); 554 } 555 556 static ucode_errno_t 557 ucode_gen_files_intel(uint8_t *buf, size_t size, const char *path) 558 { 559 size_t remaining; 560 char common_path[PATH_MAX]; 561 DIR *dirp; 562 struct dirent *dp; 563 int n; 564 565 n = snprintf(common_path, sizeof (common_path), "%s/%s", path, 566 UCODE_INSTALL_COMMON_PATH); 567 if (n >= sizeof (common_path)) { 568 dbgprintf("failed to construct path for %s\n", 569 UCODE_INSTALL_COMMON_PATH); 570 return (EM_FILEFORMAT); 571 } 572 573 if (mkdirp(common_path, 0755) == -1 && errno != EEXIST) { 574 ucode_perror(common_path, EM_SYS); 575 return (EM_SYS); 576 } 577 578 for (remaining = size; remaining > 0; ) { 579 uint32_t total_size, body_size, offset; 580 char firstname[PATH_MAX]; 581 char name[PATH_MAX]; 582 int i; 583 uint8_t *curbuf = &buf[size - remaining]; 584 ucode_header_intel_t *uhp; 585 ucode_ext_table_intel_t *extp; 586 587 uhp = (ucode_header_intel_t *)(uintptr_t)curbuf; 588 589 total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size); 590 body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size); 591 592 remaining -= total_size; 593 594 n = snprintf(firstname, sizeof (common_path), "%s/%08X-%02X", 595 common_path, uhp->uh_signature, uhp->uh_proc_flags); 596 if (n >= sizeof (common_path)) { 597 dbgprintf("failed to construct component path\n"); 598 return (EM_FILEFORMAT); 599 } 600 dbgprintf("firstname = %s\n", firstname); 601 602 if (ucode_should_update_intel(firstname, uhp->uh_rev) != 0) { 603 int fd; 604 605 /* Remove the existing one first */ 606 (void) unlink(firstname); 607 608 if ((fd = open(firstname, O_WRONLY | O_CREAT | O_TRUNC, 609 S_IRUSR | S_IRGRP | S_IROTH)) == -1) { 610 ucode_perror(firstname, EM_SYS); 611 return (EM_SYS); 612 } 613 614 if (write(fd, curbuf, total_size) != total_size) { 615 (void) close(fd); 616 ucode_perror(firstname, EM_SYS); 617 return (EM_SYS); 618 } 619 620 (void) close(fd); 621 } 622 623 /* 624 * Only 1 byte of the proc_flags field is used, therefore 625 * we only need to match 8 potential platform ids. 626 */ 627 for (i = 0; i < 8; i++) { 628 uint32_t platid = uhp->uh_proc_flags & (1 << i); 629 630 if (platid == 0 && uhp->uh_proc_flags != 0) 631 continue; 632 633 n = snprintf(name, sizeof (common_path), 634 "%s/%08X-%02X", path, uhp->uh_signature, platid); 635 if (n >= sizeof (common_path)) { 636 dbgprintf("failed to construct platid path\n"); 637 return (EM_FILEFORMAT); 638 } 639 640 dbgprintf("proc_flags = %x, platid = %x, name = %s\n", 641 uhp->uh_proc_flags, platid, name); 642 643 if (ucode_should_update_intel(name, 644 uhp->uh_rev) != 0) { 645 /* Remove the existing one first */ 646 (void) unlink(name); 647 if (link(firstname, name) == -1) { 648 ucode_perror(name, EM_SYS); 649 return (EM_SYS); 650 } 651 } 652 653 if (uhp->uh_proc_flags == 0) 654 break; 655 } 656 657 offset = UCODE_HEADER_SIZE_INTEL + body_size; 658 659 /* Check to see if there is extended signature table */ 660 if (total_size == offset) 661 continue; 662 663 /* There is extended signature table. More processing. */ 664 extp = (ucode_ext_table_intel_t *)&curbuf[offset]; 665 666 for (i = 0; i < extp->uet_count; i++) { 667 ucode_ext_sig_intel_t *uesp = &extp->uet_ext_sig[i]; 668 int j; 669 670 for (j = 0; j < 8; j++) { 671 uint32_t id = uesp->ues_proc_flags & (1 << j); 672 673 if (id == 0 && uesp->ues_proc_flags) 674 continue; 675 676 n = snprintf(name, sizeof (common_path), 677 "%s/%08X-%02X", path, 678 uesp->ues_signature, id); 679 if (n >= sizeof (common_path)) { 680 dbgprintf( 681 "failed to construct ext path\n"); 682 return (EM_FILEFORMAT); 683 } 684 685 dbgprintf("extsig: proc_flags = %x, " 686 "platid = %x, name = %s\n", 687 uesp->ues_proc_flags, id, name); 688 689 if (ucode_should_update_intel(name, 690 uhp->uh_rev) != 0) { 691 /* Remove the existing one first */ 692 (void) unlink(name); 693 if (link(firstname, name) == -1) { 694 ucode_perror(name, EM_SYS); 695 return (EM_SYS); 696 } 697 } 698 699 if (uesp->ues_proc_flags == 0) 700 break; 701 } 702 } 703 704 } 705 706 /* 707 * Remove files with no links to them. These are probably 708 * obsolete microcode files. 709 */ 710 if ((dirp = opendir(common_path)) == NULL) { 711 ucode_perror(common_path, EM_SYS); 712 return (EM_SYS); 713 } 714 715 while ((dp = readdir(dirp)) != NULL) { 716 char filename[PATH_MAX]; 717 struct stat statbuf; 718 719 n = snprintf(filename, sizeof (common_path), 720 "%s/%s", common_path, dp->d_name); 721 if (n >= sizeof (common_path) || stat(filename, &statbuf) == -1) 722 continue; 723 724 if ((statbuf.st_mode & S_IFMT) == S_IFREG) { 725 if (statbuf.st_nlink == 1) 726 (void) unlink(filename); 727 } 728 } 729 730 (void) closedir(dirp); 731 732 return (EM_OK); 733 } 734 735 static void 736 ucode_list_intel(uint8_t *buf, size_t size) 737 { 738 size_t remaining; 739 740 printf("Microcode patches:\n"); 741 for (remaining = size; remaining > 0; ) { 742 uint8_t *curbuf = &buf[size - remaining]; 743 uint8_t family, model, stepping; 744 uint32_t total_size, body_size, offset; 745 ucode_header_intel_t *uhp; 746 ucode_ext_table_intel_t *extp; 747 748 uhp = (ucode_header_intel_t *)(uintptr_t)curbuf; 749 750 total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size); 751 body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size); 752 753 remaining -= total_size; 754 755 ucode_fms(uhp->uh_signature, &family, &model, &stepping); 756 757 printf( 758 " %08lX-%02lX -> Family=%02x Model=%02x Stepping=%02x\n", 759 uhp->uh_signature, uhp->uh_proc_flags, 760 family, model, stepping); 761 printf( 762 " %14s Date=%08lX Bytes=%lu\n", "", 763 uhp->uh_date, uhp->uh_body_size); 764 765 offset = UCODE_HEADER_SIZE_INTEL + body_size; 766 767 /* Check to see if there is extended signature table */ 768 if (total_size == offset) 769 continue; 770 771 printf("Extended Signature Table:\n"); 772 773 extp = (ucode_ext_table_intel_t *)&curbuf[offset]; 774 775 for (uint32_t i = 0; i < extp->uet_count; i++) { 776 ucode_ext_sig_intel_t *uesp = &extp->uet_ext_sig[i]; 777 778 ucode_fms(uesp->ues_signature, 779 &family, &model, &stepping); 780 781 printf( 782 " %08lX-%02lX -> Family=%02x Model=%02x " 783 "Stepping=%02x\n", 784 uesp->ues_signature, uesp->ues_proc_flags, 785 family, model, stepping); 786 } 787 } 788 } 789 790 static void 791 ucode_list_amd(uint8_t *buf, size_t size) 792 { 793 uint32_t last_type = UINT32_MAX; 794 795 /* The file has already been validated. Skip over magic number */ 796 buf += sizeof (uint32_t); 797 size -= sizeof (uint32_t); 798 799 while (size > sizeof (ucode_section_amd_t)) { 800 ucode_section_amd_t *section = (ucode_section_amd_t *)buf; 801 802 switch (section->usa_type) { 803 case UCODE_AMD_CONTAINER_TYPE_EQUIV: { 804 ucode_eqtbl_amd_t *eq = 805 (ucode_eqtbl_amd_t *)section->usa_data; 806 807 if (last_type != section->usa_type) { 808 printf("Equivalence table:\n"); 809 last_type = section->usa_type; 810 } 811 for (uint_t i = 0; eq->ue_inst_cpu != 0 && 812 i < section->usa_size / sizeof (*eq); eq++, i++) { 813 uint8_t family, model, stepping; 814 815 ucode_fms(eq->ue_inst_cpu, &family, &model, 816 &stepping); 817 818 printf(" %08lX Family=%02x Model=%02x " 819 "Stepping=%02x -> %04X\n", 820 eq->ue_inst_cpu, family, model, 821 stepping, eq->ue_equiv_cpu); 822 } 823 break; 824 } 825 case UCODE_AMD_CONTAINER_TYPE_PATCH: { 826 ucode_header_amd_t *uh = 827 (ucode_header_amd_t *)section->usa_data; 828 829 if (uh->uh_cpu_rev == 0) 830 break; 831 832 if (last_type != section->usa_type) { 833 printf("Microcode patches:\n"); 834 last_type = section->usa_type; 835 } 836 837 printf(" %4X -> Patch=%08lX Date=%08lX Bytes=%lu\n", 838 uh->uh_cpu_rev, uh->uh_patch_id, uh->uh_date, 839 section->usa_size); 840 841 break; 842 } 843 default: 844 break; 845 } 846 847 size -= section->usa_size + sizeof (ucode_section_amd_t); 848 buf += section->usa_size + sizeof (ucode_section_amd_t); 849 } 850 } 851 852 /* 853 * Returns 0 on success, 2 on usage error, and 3 on operation error. 854 */ 855 int 856 main(int argc, char *argv[]) 857 { 858 int c; 859 int action = 0; 860 int actcount = 0; 861 int typeindex = -1; 862 char *path = NULL; 863 char *filename = NULL; 864 int errflg = 0; 865 int dev_fd = -1; 866 int fd = -1; 867 bool verbose = false; 868 bool needfile = false; 869 uint8_t *buf = NULL; 870 ucode_errno_t rc = EM_OK; 871 processorid_t cpuid_max; 872 struct stat filestat; 873 size_t ucode_size = 0; 874 875 (void) setlocale(LC_ALL, ""); 876 877 #if !defined(TEXT_DOMAIN) 878 #define TEXT_DOMAIN "SYS_TEST" 879 #endif 880 (void) textdomain(TEXT_DOMAIN); 881 882 cmdname = basename(argv[0]); 883 884 while ((c = getopt(argc, argv, "idhluvVR:t:")) != EOF) { 885 switch (c) { 886 887 case 'd': 888 ucode_debug = true; 889 break; 890 891 case 'i': 892 action |= UCODE_OPT_INSTALL; 893 actcount++; 894 needfile = true; 895 break; 896 897 case 'l': 898 action |= UCODE_OPT_LIST; 899 actcount++; 900 needfile = true; 901 break; 902 903 case 't': 904 if (typeindex != -1) { 905 (void) fprintf(stderr, gettext( 906 "-t can only be specified once\n")); 907 errflg++; 908 break; 909 } 910 for (uint_t i = 0; i < ARRAY_SIZE(ucode_sources); i++) { 911 if (strcmp(optarg, 912 ucode_sources[i].us_prefix) == 0) { 913 typeindex = i; 914 break; 915 } 916 } 917 if (typeindex == -1) { 918 (void) fprintf(stderr, 919 gettext("Unknown microcode type, %s\n"), 920 optarg); 921 errflg++; 922 } 923 break; 924 925 case 'u': 926 action |= UCODE_OPT_UPDATE; 927 actcount++; 928 needfile = true; 929 break; 930 931 case 'v': 932 action |= UCODE_OPT_VERSION; 933 actcount++; 934 break; 935 936 case 'R': 937 if (optarg[0] == '-') { 938 errflg++; 939 } else if (strlen(optarg) > UCODE_MAX_PATH_LEN) { 940 (void) fprintf(stderr, 941 gettext("Alternate path too long\n")); 942 errflg++; 943 } else if ((path = strdup(optarg)) == NULL) { 944 errflg++; 945 } 946 947 break; 948 949 case 'V': 950 verbose = true; 951 break; 952 953 case 'h': 954 usage(true); 955 return (0); 956 957 default: 958 usage(verbose); 959 return (2); 960 } 961 } 962 963 if (actcount == 0) { 964 (void) fprintf(stderr, gettext("%s: One of -i, -l, -u or -v " 965 "must be provided.\n"), cmdname); 966 usage(verbose); 967 return (2); 968 } 969 970 if (actcount != 1) { 971 (void) fprintf(stderr, gettext("%s: options -i, -l, -u and -v " 972 "are mutually exclusive.\n"), cmdname); 973 usage(verbose); 974 return (2); 975 } 976 977 if (typeindex != -1 && !needfile) { 978 (void) fprintf(stderr, gettext("%s: option -t requires one of " 979 "-i, -l or -u\n"), cmdname); 980 usage(verbose); 981 return (2); 982 } 983 984 if (optind <= argc - 1) 985 filename = argv[optind]; 986 else if (needfile) 987 errflg++; 988 989 if (errflg || action == 0) { 990 usage(verbose); 991 return (2); 992 } 993 994 /* 995 * Convert from the vendor-shipped format to individual microcode files. 996 */ 997 if (needfile) { 998 if (typeindex != -1) { 999 ucode = &ucode_sources[typeindex]; 1000 } else { 1001 for (uint_t i = 0; i < ARRAY_SIZE(ucode_sources); i++) { 1002 const ucode_source_t *src = &ucode_sources[i]; 1003 1004 dbgprintf("i = %d, filestr = %s, " 1005 "filename = %s\n", 1006 i, src->us_prefix, filename); 1007 if (strncasecmp(src->us_prefix, 1008 basename(filename), 1009 strlen(src->us_prefix)) == 0) { 1010 ucode = src; 1011 break; 1012 } 1013 } 1014 } 1015 1016 if (ucode == NULL) { 1017 rc = EM_NOVENDOR; 1018 (void) fprintf(stderr, "%s: %s.\n\n" 1019 "Either specify the type with the -t option, " 1020 "or rename the file such that\nits name begins " 1021 "with a vendor string.\n", 1022 cmdname, ucode_strerror(rc)); 1023 goto out; 1024 } 1025 1026 dbgprintf("Selected microcode type %s (%s)\n", 1027 ucode->us_prefix, ucode->us_vendor); 1028 1029 if ((stat(filename, &filestat)) < 0) { 1030 rc = EM_SYS; 1031 ucode_perror(filename, rc); 1032 goto out; 1033 } 1034 1035 if ((filestat.st_mode & S_IFMT) != S_IFREG && 1036 (filestat.st_mode & S_IFMT) != S_IFLNK) { 1037 rc = EM_FILEFORMAT; 1038 ucode_perror(filename, rc); 1039 goto out; 1040 } 1041 1042 ucode_size = filestat.st_size; 1043 if ((buf = malloc(ucode_size)) == NULL) { 1044 rc = EM_SYS; 1045 ucode_perror(filename, rc); 1046 goto out; 1047 } 1048 1049 rc = ucode->us_convert(filename, &buf, &ucode_size); 1050 if (rc != EM_OK) { 1051 ucode_perror(filename, rc); 1052 goto out; 1053 } 1054 1055 dbgprintf("ucode_size = %zd\n", ucode_size); 1056 1057 if ((rc = ucode->us_validate(buf, ucode_size)) != EM_OK) { 1058 ucode_perror(filename, rc); 1059 goto out; 1060 } 1061 } 1062 1063 if (action & UCODE_OPT_LIST) { 1064 ucode->us_list(buf, ucode_size); 1065 goto out; 1066 } 1067 1068 if (action & UCODE_OPT_INSTALL) { 1069 /* 1070 * If no path is provided by the -R option, put the files in 1071 * /platform/<arch>/ucode/<ucode_vendor_str>/. 1072 */ 1073 if (path == NULL) { 1074 struct utsname uts; 1075 1076 if (uname(&uts) == -1) { 1077 perror("Unable to retrieve system uname"); 1078 goto out; 1079 } 1080 1081 if ((path = malloc(PATH_MAX)) == NULL) { 1082 rc = EM_SYS; 1083 ucode_perror("malloc", rc); 1084 goto out; 1085 } 1086 1087 (void) snprintf(path, PATH_MAX, "/platform/%s/ucode/%s", 1088 uts.machine, ucode->us_vendor); 1089 } 1090 1091 if (mkdirp(path, 0755) == -1 && errno != EEXIST) { 1092 rc = EM_SYS; 1093 ucode_perror(path, rc); 1094 goto out; 1095 } 1096 1097 rc = ucode->us_gen_files(buf, ucode_size, path); 1098 1099 goto out; 1100 } 1101 1102 if ((dev_fd = open(ucode_dev, O_RDONLY)) == -1) { 1103 rc = EM_SYS; 1104 ucode_perror(ucode_dev, rc); 1105 goto out; 1106 } 1107 1108 if (action & UCODE_OPT_VERSION) { 1109 int tmprc; 1110 uint32_t *revp = NULL; 1111 int i; 1112 struct ucode_get_rev_struct info; 1113 1114 cpuid_max = (processorid_t)sysconf(_SC_CPUID_MAX); 1115 1116 if ((revp = (uint32_t *) 1117 malloc(cpuid_max * sizeof (uint32_t))) == NULL) { 1118 rc = EM_SYS; 1119 ucode_perror("malloc", rc); 1120 goto out; 1121 } 1122 1123 for (i = 0; i < cpuid_max; i++) 1124 revp[i] = (uint32_t)-1; 1125 1126 info.ugv_rev = revp; 1127 info.ugv_size = cpuid_max; 1128 info.ugv_errno = EM_OK; 1129 tmprc = ioctl(dev_fd, UCODE_GET_VERSION, &info); 1130 rc = info.ugv_errno; 1131 1132 if (tmprc && rc == EM_OK) { 1133 rc = EM_SYS; 1134 } 1135 1136 if (rc == EM_OK) { 1137 (void) printf(gettext("CPU\tMicrocode Version\n")); 1138 for (i = 0; i < cpuid_max; i++) { 1139 if (info.ugv_rev[i] == (uint32_t)-1) 1140 continue; 1141 (void) printf("%d\t0x%x\n", i, info.ugv_rev[i]); 1142 } 1143 } else { 1144 ucode_perror(gettext("get microcode version"), rc); 1145 } 1146 1147 if (revp) 1148 free(revp); 1149 } 1150 1151 if (action & UCODE_OPT_UPDATE) { 1152 int tmprc; 1153 struct ucode_write_struct uw_struct; 1154 1155 uw_struct.uw_size = ucode_size; 1156 uw_struct.uw_ucode = buf; 1157 uw_struct.uw_errno = EM_OK; 1158 tmprc = ioctl(dev_fd, UCODE_UPDATE, &uw_struct); 1159 rc = uw_struct.uw_errno; 1160 1161 if (rc == EM_OK) { 1162 if (tmprc) { 1163 rc = EM_SYS; 1164 ucode_perror(ucode_dev, rc); 1165 } 1166 } else if (rc == EM_NOMATCH || rc == EM_HIGHERREV) { 1167 ucode_perror(filename, rc); 1168 } else { 1169 ucode_perror(gettext("microcode update"), rc); 1170 } 1171 } 1172 1173 out: 1174 if (dev_fd != -1) 1175 (void) close(dev_fd); 1176 1177 if (fd != -1) 1178 (void) close(fd); 1179 1180 free(buf); 1181 free(path); 1182 1183 if (rc != EM_OK) 1184 return (3); 1185 1186 return (0); 1187 } 1188