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 2023 Oxide Computer Company 30 */ 31 32 #include <sys/types.h> 33 #include <sys/processor.h> 34 #include <sys/sysmacros.h> 35 #include <sys/ucode.h> 36 #include <sys/ucode_intel.h> 37 #include <sys/ucode_amd.h> 38 #include <sys/utsname.h> 39 #include <sys/ioctl.h> 40 #include <sys/stat.h> 41 #include <unistd.h> 42 #include <dirent.h> 43 #include <fcntl.h> 44 #include <errno.h> 45 #include <stdbool.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <stdarg.h> 49 #include <string.h> 50 #include <errno.h> 51 #include <syslog.h> 52 #include <time.h> 53 #include <ctype.h> 54 #include <assert.h> 55 #include <libgen.h> 56 #include <limits.h> 57 #include <locale.h> 58 #include <libintl.h> 59 #include <ucode/ucode_errno.h> 60 #include <ucode/ucode_utils_intel.h> 61 #include <ucode/ucode_utils_amd.h> 62 63 #define UCODE_OPT_INSTALL 0x0001 64 #define UCODE_OPT_UPDATE 0x0002 65 #define UCODE_OPT_VERSION 0x0004 66 #define UCODE_OPT_LIST 0x0008 67 68 static const char ucode_dev[] = "/dev/" UCODE_DRIVER_NAME; 69 70 static char *cmdname; 71 72 #define UCODE_INSTALL_COMMON_PATH ".f" 73 74 /* 75 * The maximum directory path length that can be provided via -R has 76 * to allow for appending the files within the microcode bundles. 77 */ 78 #define UCODE_MAX_PATH_LEN (PATH_MAX - \ 79 MAX(UCODE_MAX_NAME_LEN_INTEL, UCODE_MAX_NAME_LEN_AMD) - 1) 80 81 static bool ucode_debug = false; 82 83 static int ucode_convert_amd(const char *, uint8_t *, size_t); 84 static int ucode_convert_intel(const char *, uint8_t *, size_t); 85 86 static ucode_errno_t ucode_gen_files_amd(uint8_t *, int, char *); 87 static ucode_errno_t ucode_gen_files_intel(uint8_t *, int, char *); 88 89 static void ucode_list_amd(uint8_t *, int); 90 static void ucode_list_intel(uint8_t *, int); 91 92 typedef struct ucode_source { 93 const char *us_prefix; 94 const char *us_vendor; 95 int (*us_convert)(const char *, uint8_t *, size_t); 96 ucode_errno_t (*us_gen_files)(uint8_t *, int, char *); 97 ucode_errno_t (*us_validate)(uint8_t *, int); 98 void (*us_list)(uint8_t *, int); 99 } ucode_source_t; 100 101 static const ucode_source_t ucode_sources[] = { 102 { 103 .us_prefix = "intel", 104 .us_vendor = "GenuineIntel", 105 .us_convert = ucode_convert_intel, 106 .us_gen_files = ucode_gen_files_intel, 107 .us_validate = ucode_validate_intel, 108 .us_list = ucode_list_intel, 109 }, 110 { 111 .us_prefix = "amd", 112 .us_vendor = "AuthenticAMD", 113 .us_convert = ucode_convert_amd, 114 .us_gen_files = ucode_gen_files_amd, 115 .us_validate = ucode_validate_amd, 116 .us_list = ucode_list_amd, 117 }, 118 }; 119 120 const ucode_source_t *ucode; 121 122 static void 123 dprintf(const char *format, ...) 124 { 125 if (ucode_debug) { 126 va_list alist; 127 va_start(alist, format); 128 (void) vfprintf(stderr, format, alist); 129 va_end(alist); 130 } 131 } 132 133 static void 134 usage(bool verbose) 135 { 136 (void) fprintf(stderr, gettext("usage:\n")); 137 (void) fprintf(stderr, "\t%s -v\n", cmdname); 138 if (verbose) { 139 (void) fprintf(stderr, 140 gettext("\t\t Shows running microcode version.\n\n")); 141 } 142 143 (void) fprintf(stderr, "\t%s -u [-t type] microcode-file\n", cmdname); 144 if (verbose) { 145 (void) fprintf(stderr, gettext("\t\t Updates microcode to the " 146 "latest matching version found in\n" 147 "\t\t microcode-file.\n\n")); 148 } 149 150 (void) fprintf(stderr, "\t%s -l [-t type] microcode-file\n", cmdname); 151 if (verbose) { 152 (void) fprintf(stderr, gettext("\t\t Displays details of the " 153 "microcode file's contents.\n\n")); 154 } 155 156 (void) fprintf(stderr, 157 "\t%s -i [-t type] [-R path] microcode-file\n", cmdname); 158 if (verbose) { 159 (void) fprintf(stderr, gettext("\t\t Installs microcode to be " 160 "used for subsequent boots.\n")); 161 } 162 (void) fprintf(stderr, gettext( 163 "\nThe type of the microcode file must either be specified with " 164 "the -t option\nor microcode-file must start with the vendor name " 165 "prefix, either \"intel\"\nor \"amd\", so that the type can be " 166 "inferred from it.\n\n")); 167 } 168 169 static void 170 ucode_perror(const char *str, ucode_errno_t rc) 171 { 172 (void) fprintf(stderr, "%s: %s: %s\n", cmdname, str, 173 errno == 0 ? ucode_strerror(rc) : strerror(errno)); 174 errno = 0; 175 } 176 177 #define LINESIZE 120 /* copyright line sometimes is longer than 80 */ 178 179 /* 180 * Convert text format microcode release into binary format. 181 * Return the number of characters read. 182 * 183 * AMD microcode is already in binary format. 184 */ 185 static int 186 ucode_convert_amd(const char *infile, uint8_t *buf, size_t size) 187 { 188 int fd; 189 190 if (infile == NULL || buf == NULL || size == 0) 191 return (0); 192 193 if ((fd = open(infile, O_RDONLY)) < 0) 194 return (0); 195 196 size = read(fd, buf, size); 197 198 (void) close(fd); 199 200 return (size); 201 } 202 203 static int 204 ucode_convert_intel(const char *infile, uint8_t *buf, size_t size) 205 { 206 char linebuf[LINESIZE]; 207 FILE *infd = NULL; 208 int count = 0, firstline = 1; 209 uint32_t *intbuf = (uint32_t *)(uintptr_t)buf; 210 211 if (infile == NULL || buf == NULL || size == 0) 212 return (0); 213 214 if ((infd = fopen(infile, "r")) == NULL) 215 return (0); 216 217 while (fgets(linebuf, LINESIZE, infd)) { 218 219 /* Check to see if we are processing a binary file */ 220 if (firstline && !isprint(linebuf[0])) { 221 if (fseek(infd, 0, SEEK_SET) == 0) 222 count = fread(buf, 1, size, infd); 223 224 (void) fclose(infd); 225 return (count); 226 } 227 228 firstline = 0; 229 230 /* Skip blank lines */ 231 if (strlen(linebuf) == 1) 232 continue; 233 234 /* Skip lines with all spaces or tabs */ 235 if (strcspn(linebuf, " \t") == 0) 236 continue; 237 238 /* Text file. Skip comments. */ 239 if (linebuf[0] == '/') 240 continue; 241 242 if (sscanf(linebuf, "%x, %x, %x, %x", 243 &intbuf[count], &intbuf[count+1], 244 &intbuf[count+2], &intbuf[count+3]) != 4) 245 break; 246 247 count += 4; 248 } 249 250 (void) fclose(infd); 251 252 /* 253 * If we get here, we are processing a text format file 254 * where "count" is used to count the number of integers 255 * read. Convert it to number of characters read. 256 */ 257 return (count * sizeof (int)); 258 } 259 260 /* 261 * Returns 0 if no need to update the link; -1 otherwise 262 */ 263 static int 264 ucode_should_update_intel(char *filename, uint32_t new_rev) 265 { 266 int fd; 267 struct stat statbuf; 268 ucode_header_intel_t header; 269 270 /* 271 * If the file or link already exists, check to see if 272 * it is necessary to update it. 273 */ 274 if (stat(filename, &statbuf) == 0) { 275 if ((fd = open(filename, O_RDONLY)) == -1) 276 return (-1); 277 278 if (read(fd, &header, sizeof (header)) == -1) { 279 (void) close(fd); 280 return (-1); 281 } 282 283 (void) close(fd); 284 285 if (header.uh_rev >= new_rev) 286 return (0); 287 } 288 289 return (-1); 290 } 291 292 /* 293 * Generate microcode binary files. Must be called after ucode_validate(). 294 */ 295 static ucode_errno_t 296 ucode_gen_files_amd(uint8_t *buf, int size, char *path) 297 { 298 uint32_t *ptr = (uint32_t *)buf; 299 char common_path[PATH_MAX]; 300 int fd, count, counter = 0; 301 ucode_header_amd_t *uh; 302 int last_cpu_rev = 0; 303 304 /* write container file */ 305 (void) snprintf(common_path, PATH_MAX, "%s/%s", path, "container"); 306 307 dprintf("path = %s\n", common_path); 308 fd = open(common_path, O_WRONLY | O_CREAT | O_TRUNC, 309 S_IRUSR | S_IRGRP | S_IROTH); 310 311 if (fd == -1) { 312 ucode_perror(common_path, EM_SYS); 313 return (EM_SYS); 314 } 315 316 if (write(fd, buf, size) != size) { 317 (void) close(fd); 318 ucode_perror(common_path, EM_SYS); 319 return (EM_SYS); 320 } 321 322 (void) close(fd); 323 324 /* skip over magic number & equivalence table header */ 325 ptr += 2; size -= 8; 326 327 count = *ptr++; size -= 4; 328 329 /* equivalence table uses special name */ 330 (void) snprintf(common_path, PATH_MAX, "%s/%s", path, 331 UCODE_AMD_EQUIVALENCE_TABLE_NAME); 332 333 for (;;) { 334 dprintf("path = %s\n", common_path); 335 fd = open(common_path, O_WRONLY | O_CREAT | O_TRUNC, 336 S_IRUSR | S_IRGRP | S_IROTH); 337 338 if (fd == -1) { 339 ucode_perror(common_path, EM_SYS); 340 return (EM_SYS); 341 } 342 343 if (write(fd, ptr, count) != count) { 344 (void) close(fd); 345 ucode_perror(common_path, EM_SYS); 346 return (EM_SYS); 347 } 348 349 (void) close(fd); 350 ptr += count >> 2; size -= count; 351 352 if (!size) 353 return (EM_OK); 354 355 ptr++; size -= 4; 356 count = *ptr++; size -= 4; 357 358 /* construct name from header information */ 359 uh = (ucode_header_amd_t *)ptr; 360 361 if (uh->uh_cpu_rev != last_cpu_rev) { 362 last_cpu_rev = uh->uh_cpu_rev; 363 counter = 0; 364 } 365 366 (void) snprintf(common_path, PATH_MAX, "%s/%04X-%02X", path, 367 uh->uh_cpu_rev, counter++); 368 } 369 } 370 371 static ucode_errno_t 372 ucode_gen_files_intel(uint8_t *buf, int size, char *path) 373 { 374 int remaining; 375 char common_path[PATH_MAX]; 376 DIR *dirp; 377 struct dirent *dp; 378 379 (void) snprintf(common_path, PATH_MAX, "%s/%s", path, 380 UCODE_INSTALL_COMMON_PATH); 381 382 if (mkdirp(common_path, 0755) == -1 && errno != EEXIST) { 383 ucode_perror(common_path, EM_SYS); 384 return (EM_SYS); 385 } 386 387 for (remaining = size; remaining > 0; ) { 388 uint32_t total_size, body_size, offset; 389 char firstname[PATH_MAX]; 390 char name[PATH_MAX]; 391 int i; 392 uint8_t *curbuf = &buf[size - remaining]; 393 ucode_header_intel_t *uhp; 394 ucode_ext_table_intel_t *extp; 395 396 uhp = (ucode_header_intel_t *)(uintptr_t)curbuf; 397 398 total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size); 399 body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size); 400 401 remaining -= total_size; 402 403 (void) snprintf(firstname, PATH_MAX, "%s/%08X-%02X", 404 common_path, uhp->uh_signature, uhp->uh_proc_flags); 405 dprintf("firstname = %s\n", firstname); 406 407 if (ucode_should_update_intel(firstname, uhp->uh_rev) != 0) { 408 int fd; 409 410 /* Remove the existing one first */ 411 (void) unlink(firstname); 412 413 if ((fd = open(firstname, O_WRONLY | O_CREAT | O_TRUNC, 414 S_IRUSR | S_IRGRP | S_IROTH)) == -1) { 415 ucode_perror(firstname, EM_SYS); 416 return (EM_SYS); 417 } 418 419 if (write(fd, curbuf, total_size) != total_size) { 420 (void) close(fd); 421 ucode_perror(firstname, EM_SYS); 422 return (EM_SYS); 423 } 424 425 (void) close(fd); 426 } 427 428 /* 429 * Only 1 byte of the proc_flags field is used, therefore 430 * we only need to match 8 potential platform ids. 431 */ 432 for (i = 0; i < 8; i++) { 433 uint32_t platid = uhp->uh_proc_flags & (1 << i); 434 435 if (platid == 0 && uhp->uh_proc_flags != 0) 436 continue; 437 438 (void) snprintf(name, PATH_MAX, 439 "%s/%08X-%02X", path, uhp->uh_signature, platid); 440 441 dprintf("proc_flags = %x, platid = %x, name = %s\n", 442 uhp->uh_proc_flags, platid, name); 443 444 if (ucode_should_update_intel(name, 445 uhp->uh_rev) != 0) { 446 /* Remove the existing one first */ 447 (void) unlink(name); 448 if (link(firstname, name) == -1) { 449 ucode_perror(name, EM_SYS); 450 return (EM_SYS); 451 } 452 } 453 454 if (uhp->uh_proc_flags == 0) 455 break; 456 } 457 458 offset = UCODE_HEADER_SIZE_INTEL + body_size; 459 460 /* Check to see if there is extended signature table */ 461 if (total_size == offset) 462 continue; 463 464 /* There is extended signature table. More processing. */ 465 extp = (ucode_ext_table_intel_t *)&curbuf[offset]; 466 467 for (i = 0; i < extp->uet_count; i++) { 468 ucode_ext_sig_intel_t *uesp = &extp->uet_ext_sig[i]; 469 int j; 470 471 for (j = 0; j < 8; j++) { 472 uint32_t id = uesp->ues_proc_flags & (1 << j); 473 474 if (id == 0 && uesp->ues_proc_flags) 475 continue; 476 477 (void) snprintf(name, PATH_MAX, 478 "%s/%08X-%02X", path, 479 uesp->ues_signature, id); 480 481 dprintf("extsig: proc_flags = %x, " 482 "platid = %x, name = %s\n", 483 uesp->ues_proc_flags, id, name); 484 485 if (ucode_should_update_intel(name, 486 uhp->uh_rev) != 0) { 487 /* Remove the existing one first */ 488 (void) unlink(name); 489 if (link(firstname, name) == -1) { 490 ucode_perror(name, EM_SYS); 491 return (EM_SYS); 492 } 493 } 494 495 if (uesp->ues_proc_flags == 0) 496 break; 497 } 498 } 499 500 } 501 502 /* 503 * Remove files with no links to them. These are probably 504 * obsolete microcode files. 505 */ 506 if ((dirp = opendir(common_path)) == NULL) { 507 ucode_perror(common_path, EM_SYS); 508 return (EM_SYS); 509 } 510 511 while ((dp = readdir(dirp)) != NULL) { 512 char filename[PATH_MAX]; 513 struct stat statbuf; 514 515 (void) snprintf(filename, PATH_MAX, 516 "%s/%s", common_path, dp->d_name); 517 if (stat(filename, &statbuf) == -1) 518 continue; 519 520 if ((statbuf.st_mode & S_IFMT) == S_IFREG) { 521 if (statbuf.st_nlink == 1) 522 (void) unlink(filename); 523 } 524 } 525 526 (void) closedir(dirp); 527 528 return (EM_OK); 529 } 530 531 static void 532 ucode_fms(uint32_t sig, uint8_t *family, uint8_t *model, uint8_t *stepping) 533 { 534 *family = ((sig >> 8) & 0xf) + ((sig >> 20) & 0xff); 535 *model = ((sig >> 4) & 0xf) | ((sig >> 12) & 0xf0); 536 *stepping = sig & 0xf; 537 } 538 539 static void 540 ucode_list_intel(uint8_t *buf, int size) 541 { 542 int remaining; 543 544 printf("Microcode patches:\n"); 545 for (remaining = size; remaining > 0; ) { 546 uint8_t *curbuf = &buf[size - remaining]; 547 uint8_t family, model, stepping; 548 uint32_t total_size, body_size, offset; 549 ucode_header_intel_t *uhp; 550 ucode_ext_table_intel_t *extp; 551 552 uhp = (ucode_header_intel_t *)(uintptr_t)curbuf; 553 554 total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size); 555 body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size); 556 557 remaining -= total_size; 558 559 ucode_fms(uhp->uh_signature, &family, &model, &stepping); 560 561 printf( 562 " %08lX-%02lX -> Family=%02x Model=%02x Stepping=%02x\n", 563 uhp->uh_signature, uhp->uh_proc_flags, 564 family, model, stepping); 565 printf( 566 " %14s Date=%08lX Bytes=%lu\n", "", 567 uhp->uh_date, uhp->uh_body_size); 568 569 offset = UCODE_HEADER_SIZE_INTEL + body_size; 570 571 /* Check to see if there is extended signature table */ 572 if (total_size == offset) 573 continue; 574 575 printf("Extended Signature Table:\n"); 576 577 extp = (ucode_ext_table_intel_t *)&curbuf[offset]; 578 579 for (uint32_t i = 0; i < extp->uet_count; i++) { 580 ucode_ext_sig_intel_t *uesp = &extp->uet_ext_sig[i]; 581 582 ucode_fms(uesp->ues_signature, 583 &family, &model, &stepping); 584 585 printf( 586 " %08lX-%02lX -> Family=%02x Model=%02x " 587 "Stepping=%02x\n", 588 uesp->ues_signature, uesp->ues_proc_flags, 589 family, model, stepping); 590 } 591 } 592 } 593 594 static void 595 ucode_list_amd(uint8_t *buf, int size) 596 { 597 ucode_eqtbl_amd_t *eq; 598 ucode_header_amd_t *uh; 599 uint32_t tsz; 600 601 /* 602 * The file has already been validated so we can skip straight to 603 * the equivalence table. 604 */ 605 tsz = *(uint32_t *)(buf + 8); 606 eq = (ucode_eqtbl_amd_t *)(buf + 12); 607 size -= 12; 608 609 printf("Equivalence table:\n"); 610 while (size >= sizeof (ucode_eqtbl_amd_t) && eq->ue_inst_cpu != 0) { 611 uint8_t family, model, stepping; 612 613 ucode_fms(eq->ue_inst_cpu, &family, &model, &stepping); 614 615 printf( 616 " %08lX Family=%02x Model=%02x Stepping=%02x -> %04X\n", 617 eq->ue_inst_cpu, family, model, stepping, eq->ue_equiv_cpu); 618 eq++; 619 size -= sizeof (*eq); 620 } 621 622 /* Move past the equivalence table terminating record */ 623 eq++; 624 size -= sizeof (*eq); 625 buf = (uint8_t *)eq; 626 627 printf("Microcode patches:\n"); 628 while (size > sizeof (ucode_header_amd_t) + 8) { 629 tsz = *(uint32_t *)(buf + 4); 630 uh = (ucode_header_amd_t *)(buf + 8); 631 632 if (uh->uh_cpu_rev == 0) 633 break; 634 635 printf(" %4X -> Patch=%08lX Date=%08lX Bytes=%lu\n", 636 uh->uh_cpu_rev, uh->uh_patch_id, uh->uh_date, tsz); 637 638 buf += (tsz + 8); 639 size -= (tsz + 8); 640 } 641 } 642 643 /* 644 * Returns 0 on success, 2 on usage error, and 3 on operation error. 645 */ 646 int 647 main(int argc, char *argv[]) 648 { 649 int c; 650 int action = 0; 651 int actcount = 0; 652 int typeindex = -1; 653 char *path = NULL; 654 char *filename = NULL; 655 int errflg = 0; 656 int dev_fd = -1; 657 int fd = -1; 658 bool verbose = false; 659 bool needfile = false; 660 uint8_t *buf = NULL; 661 ucode_errno_t rc = EM_OK; 662 processorid_t cpuid_max; 663 struct stat filestat; 664 int ucode_size = 0; 665 666 (void) setlocale(LC_ALL, ""); 667 668 #if !defined(TEXT_DOMAIN) 669 #define TEXT_DOMAIN "SYS_TEST" 670 #endif 671 (void) textdomain(TEXT_DOMAIN); 672 673 cmdname = basename(argv[0]); 674 675 while ((c = getopt(argc, argv, "idhluvVR:t:")) != EOF) { 676 switch (c) { 677 678 case 'd': 679 ucode_debug = true; 680 break; 681 682 case 'i': 683 action |= UCODE_OPT_INSTALL; 684 actcount++; 685 needfile = true; 686 break; 687 688 case 'l': 689 action |= UCODE_OPT_LIST; 690 actcount++; 691 needfile = true; 692 break; 693 694 case 't': 695 if (typeindex != -1) { 696 (void) fprintf(stderr, gettext( 697 "-t can only be specified once\n")); 698 errflg++; 699 break; 700 } 701 for (uint_t i = 0; i < ARRAY_SIZE(ucode_sources); i++) { 702 if (strcmp(optarg, 703 ucode_sources[i].us_prefix) == 0) { 704 typeindex = i; 705 break; 706 } 707 } 708 if (typeindex == -1) { 709 (void) fprintf(stderr, 710 gettext("Unknown microcode type, %s\n"), 711 optarg); 712 errflg++; 713 } 714 break; 715 716 case 'u': 717 action |= UCODE_OPT_UPDATE; 718 actcount++; 719 needfile = true; 720 break; 721 722 case 'v': 723 action |= UCODE_OPT_VERSION; 724 actcount++; 725 break; 726 727 case 'R': 728 if (optarg[0] == '-') { 729 errflg++; 730 } else if (strlen(optarg) > UCODE_MAX_PATH_LEN) { 731 (void) fprintf(stderr, 732 gettext("Alternate path too long\n")); 733 errflg++; 734 } else if ((path = strdup(optarg)) == NULL) { 735 errflg++; 736 } 737 738 break; 739 740 case 'V': 741 verbose = true; 742 break; 743 744 case 'h': 745 usage(true); 746 return (0); 747 748 default: 749 usage(verbose); 750 return (2); 751 } 752 } 753 754 if (actcount == 0) { 755 (void) fprintf(stderr, gettext("%s: One of -i, -l, -u or -v " 756 "must be provided.\n"), cmdname); 757 usage(verbose); 758 return (2); 759 } 760 761 if (actcount != 1) { 762 (void) fprintf(stderr, gettext("%s: options -i, -l, -u and -v " 763 "are mutually exclusive.\n"), cmdname); 764 usage(verbose); 765 return (2); 766 } 767 768 if (typeindex != -1 && !needfile) { 769 (void) fprintf(stderr, gettext("%s: option -t requires one of " 770 "-i, -l or -u\n"), cmdname); 771 usage(verbose); 772 return (2); 773 } 774 775 if (optind <= argc - 1) 776 filename = argv[optind]; 777 else if (needfile) 778 errflg++; 779 780 if (errflg || action == 0) { 781 usage(verbose); 782 return (2); 783 } 784 785 /* 786 * Convert from the vendor-shipped format to individual microcode files. 787 */ 788 if (needfile) { 789 if (typeindex != -1) { 790 ucode = &ucode_sources[typeindex]; 791 } else { 792 for (uint_t i = 0; i < ARRAY_SIZE(ucode_sources); i++) { 793 const ucode_source_t *src = &ucode_sources[i]; 794 795 dprintf("i = %d, filestr = %s, filename = %s\n", 796 i, src->us_prefix, filename); 797 if (strncasecmp(src->us_prefix, 798 basename(filename), 799 strlen(src->us_prefix)) == 0) { 800 ucode = src; 801 break; 802 } 803 } 804 } 805 806 if (ucode == NULL) { 807 rc = EM_NOVENDOR; 808 (void) fprintf(stderr, "%s: %s.\n\n" 809 "Either specify the type with the -t option, " 810 "or rename the file such that\nits name begins " 811 "with a vendor string.\n", 812 cmdname, ucode_strerror(rc)); 813 goto out; 814 } 815 816 dprintf("Selected microcode type %s (%s)\n", 817 ucode->us_prefix, ucode->us_vendor); 818 819 if ((stat(filename, &filestat)) < 0) { 820 rc = EM_SYS; 821 ucode_perror(filename, rc); 822 goto out; 823 } 824 825 if ((filestat.st_mode & S_IFMT) != S_IFREG && 826 (filestat.st_mode & S_IFMT) != S_IFLNK) { 827 rc = EM_FILEFORMAT; 828 ucode_perror(filename, rc); 829 goto out; 830 } 831 832 if ((buf = malloc(filestat.st_size)) == NULL) { 833 rc = EM_SYS; 834 ucode_perror(filename, rc); 835 goto out; 836 } 837 838 ucode_size = ucode->us_convert(filename, buf, filestat.st_size); 839 840 dprintf("ucode_size = %d\n", ucode_size); 841 842 if (ucode_size == 0) { 843 rc = EM_FILEFORMAT; 844 ucode_perror(filename, rc); 845 goto out; 846 } 847 848 if ((rc = ucode->us_validate(buf, ucode_size)) != EM_OK) { 849 ucode_perror(filename, rc); 850 goto out; 851 } 852 } 853 854 if (action & UCODE_OPT_LIST) { 855 ucode->us_list(buf, ucode_size); 856 goto out; 857 } 858 859 if (action & UCODE_OPT_INSTALL) { 860 /* 861 * If no path is provided by the -R option, put the files in 862 * /platform/<arch>/ucode/<ucode_vendor_str>/. 863 */ 864 if (path == NULL) { 865 struct utsname uts; 866 867 if (uname(&uts) == -1) { 868 perror("Unable to retrieve system uname"); 869 goto out; 870 } 871 872 if ((path = malloc(PATH_MAX)) == NULL) { 873 rc = EM_SYS; 874 ucode_perror("malloc", rc); 875 goto out; 876 } 877 878 (void) snprintf(path, PATH_MAX, "/platform/%s/ucode/%s", 879 uts.machine, ucode->us_vendor); 880 } 881 882 if (mkdirp(path, 0755) == -1 && errno != EEXIST) { 883 rc = EM_SYS; 884 ucode_perror(path, rc); 885 goto out; 886 } 887 888 rc = ucode->us_gen_files(buf, ucode_size, path); 889 890 goto out; 891 } 892 893 if ((dev_fd = open(ucode_dev, O_RDONLY)) == -1) { 894 rc = EM_SYS; 895 ucode_perror(ucode_dev, rc); 896 goto out; 897 } 898 899 if (action & UCODE_OPT_VERSION) { 900 int tmprc; 901 uint32_t *revp = NULL; 902 int i; 903 struct ucode_get_rev_struct info; 904 905 cpuid_max = (processorid_t)sysconf(_SC_CPUID_MAX); 906 907 if ((revp = (uint32_t *) 908 malloc(cpuid_max * sizeof (uint32_t))) == NULL) { 909 rc = EM_SYS; 910 ucode_perror("malloc", rc); 911 goto out; 912 } 913 914 for (i = 0; i < cpuid_max; i++) 915 revp[i] = (uint32_t)-1; 916 917 info.ugv_rev = revp; 918 info.ugv_size = cpuid_max; 919 info.ugv_errno = EM_OK; 920 tmprc = ioctl(dev_fd, UCODE_GET_VERSION, &info); 921 rc = info.ugv_errno; 922 923 if (tmprc && rc == EM_OK) { 924 rc = EM_SYS; 925 } 926 927 if (rc == EM_OK) { 928 (void) printf(gettext("CPU\tMicrocode Version\n")); 929 for (i = 0; i < cpuid_max; i++) { 930 if (info.ugv_rev[i] == (uint32_t)-1) 931 continue; 932 (void) printf("%d\t0x%x\n", i, info.ugv_rev[i]); 933 } 934 } else { 935 ucode_perror(gettext("get microcode version"), rc); 936 } 937 938 if (revp) 939 free(revp); 940 } 941 942 if (action & UCODE_OPT_UPDATE) { 943 int tmprc; 944 struct ucode_write_struct uw_struct; 945 946 uw_struct.uw_size = ucode_size; 947 uw_struct.uw_ucode = buf; 948 uw_struct.uw_errno = EM_OK; 949 tmprc = ioctl(dev_fd, UCODE_UPDATE, &uw_struct); 950 rc = uw_struct.uw_errno; 951 952 if (rc == EM_OK) { 953 if (tmprc) { 954 rc = EM_SYS; 955 ucode_perror(ucode_dev, rc); 956 } 957 } else if (rc == EM_NOMATCH || rc == EM_HIGHERREV) { 958 ucode_perror(filename, rc); 959 } else { 960 ucode_perror(gettext("microcode update"), rc); 961 } 962 } 963 964 out: 965 if (dev_fd != -1) 966 (void) close(dev_fd); 967 968 if (fd != -1) 969 (void) close(fd); 970 971 free(buf); 972 free(path); 973 974 if (rc != EM_OK) 975 return (3); 976 977 return (0); 978 } 979