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 */ 29 30 #include <sys/types.h> 31 #include <sys/processor.h> 32 #include <sys/ucode.h> 33 #include <sys/ioctl.h> 34 #include <sys/stat.h> 35 #include <unistd.h> 36 #include <dirent.h> 37 #include <fcntl.h> 38 #include <errno.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <stdarg.h> 42 #include <string.h> 43 #include <errno.h> 44 #include <syslog.h> 45 #include <time.h> 46 #include <ctype.h> 47 #include <assert.h> 48 #include <libgen.h> 49 #include <locale.h> 50 #include <libintl.h> 51 52 #define UCODE_OPT_INSTALL 0x0001 53 #define UCODE_OPT_UPDATE 0x0002 54 #define UCODE_OPT_VERSION 0x0004 55 56 static const char ucode_dev[] = "/dev/" UCODE_DRIVER_NAME; 57 58 static char *cmdname; 59 60 static char ucode_vendor_str[UCODE_MAX_VENDORS_NAME_LEN]; 61 static char ucode_install_path[] = UCODE_INSTALL_PATH; 62 63 static int ucode_debug = 0; 64 65 static int ucode_convert_amd(const char *, uint8_t *, size_t); 66 static int ucode_convert_intel(const char *, uint8_t *, size_t); 67 68 static ucode_errno_t ucode_gen_files_amd(uint8_t *, int, char *); 69 static ucode_errno_t ucode_gen_files_intel(uint8_t *, int, char *); 70 71 static const struct ucode_ops ucode_ops[] = { 72 { ucode_convert_intel, ucode_gen_files_intel, ucode_validate_intel }, 73 { ucode_convert_amd, ucode_gen_files_amd, ucode_validate_amd }, 74 }; 75 76 const struct ucode_ops *ucode; 77 78 static void 79 dprintf(const char *format, ...) 80 { 81 if (ucode_debug) { 82 va_list alist; 83 va_start(alist, format); 84 (void) vfprintf(stderr, format, alist); 85 va_end(alist); 86 } 87 } 88 89 static void 90 usage(int verbose) 91 { 92 (void) fprintf(stderr, gettext("usage:\n")); 93 (void) fprintf(stderr, "\t%s -v\n", cmdname); 94 if (verbose) { 95 (void) fprintf(stderr, 96 gettext("\t\t Shows running microcode version.\n\n")); 97 } 98 99 (void) fprintf(stderr, "\t%s -u microcode-file\n", cmdname); 100 if (verbose) { 101 (void) fprintf(stderr, gettext("\t\t Updates microcode to the " 102 "latest matching version found in\n" 103 "\t\t microcode-file.\n\n")); 104 } 105 106 (void) fprintf(stderr, "\t%s -i [-R path] microcode-file\n", cmdname); 107 if (verbose) { 108 (void) fprintf(stderr, gettext("\t\t Installs microcode to be " 109 "used for subsequent boots.\n\n")); 110 (void) fprintf(stderr, gettext("Microcode file name must start " 111 "with vendor name, such as \"intel\" or \"amd\".\n\n")); 112 } 113 } 114 115 static void 116 ucode_perror(const char *str, ucode_errno_t rc) 117 { 118 (void) fprintf(stderr, "%s: %s: %s\n", cmdname, str, 119 errno == 0 ? ucode_strerror(rc) : strerror(errno)); 120 errno = 0; 121 } 122 123 #define LINESIZE 120 /* copyright line sometimes is longer than 80 */ 124 125 /* 126 * Convert text format microcode release into binary format. 127 * Return the number of characters read. 128 */ 129 static int 130 ucode_convert_amd(const char *infile, uint8_t *buf, size_t size) 131 { 132 int fd; 133 134 if (infile == NULL || buf == NULL || size == 0) 135 return (0); 136 137 if ((fd = open(infile, O_RDONLY)) < 0) 138 return (0); 139 140 size = read(fd, buf, size); 141 142 (void) close(fd); 143 144 return (size); 145 } 146 147 static int 148 ucode_convert_intel(const char *infile, uint8_t *buf, size_t size) 149 { 150 char linebuf[LINESIZE]; 151 FILE *infd = NULL; 152 int count = 0, firstline = 1; 153 uint32_t *intbuf = (uint32_t *)(intptr_t)buf; 154 155 if (infile == NULL || buf == NULL || size == 0) 156 return (0); 157 158 if ((infd = fopen(infile, "r")) == NULL) 159 return (0); 160 161 while (fgets(linebuf, LINESIZE, infd)) { 162 163 /* Check to see if we are processing a binary file */ 164 if (firstline && !isprint(linebuf[0])) { 165 if (fseek(infd, 0, SEEK_SET) == 0) 166 count = fread(buf, 1, size, infd); 167 168 (void) fclose(infd); 169 return (count); 170 } 171 172 firstline = 0; 173 174 /* Skip blank lines */ 175 if (strlen(linebuf) == 1) 176 continue; 177 178 /* Skip lines with all spaces or tabs */ 179 if (strcspn(linebuf, " \t") == 0) 180 continue; 181 182 /* Text file. Skip comments. */ 183 if (linebuf[0] == '/') 184 continue; 185 186 if (sscanf(linebuf, "%x, %x, %x, %x", 187 &intbuf[count], &intbuf[count+1], 188 &intbuf[count+2], &intbuf[count+3]) != 4) 189 break; 190 191 count += 4; 192 } 193 194 (void) fclose(infd); 195 196 /* 197 * If we get here, we are processing a text format file 198 * where "count" is used to count the number of integers 199 * read. Convert it to number of characters read. 200 */ 201 return (count * sizeof (int)); 202 } 203 204 /* 205 * Returns 0 if no need to update the link; -1 otherwise 206 */ 207 static int 208 ucode_should_update_intel(char *filename, uint32_t new_rev) 209 { 210 int fd; 211 struct stat statbuf; 212 ucode_header_intel_t header; 213 214 /* 215 * If the file or link already exists, check to see if 216 * it is necessary to update it. 217 */ 218 if (stat(filename, &statbuf) == 0) { 219 if ((fd = open(filename, O_RDONLY)) == -1) 220 return (-1); 221 222 if (read(fd, &header, sizeof (header)) == -1) { 223 (void) close(fd); 224 return (-1); 225 } 226 227 (void) close(fd); 228 229 if (header.uh_rev >= new_rev) 230 return (0); 231 } 232 233 return (-1); 234 } 235 236 /* 237 * Generate microcode binary files. Must be called after ucode_validate(). 238 */ 239 static ucode_errno_t 240 ucode_gen_files_amd(uint8_t *buf, int size, char *path) 241 { 242 /* LINTED: pointer alignment */ 243 uint32_t *ptr = (uint32_t *)buf; 244 char common_path[PATH_MAX]; 245 int fd, count, counter; 246 ucode_header_amd_t *uh; 247 int last_cpu_rev = 0; 248 249 250 /* write container file */ 251 (void) snprintf(common_path, PATH_MAX, "%s/%s", path, "container"); 252 253 dprintf("path = %s\n", common_path); 254 fd = open(common_path, O_WRONLY | O_CREAT | O_TRUNC, 255 S_IRUSR | S_IRGRP | S_IROTH); 256 257 if (fd == -1) { 258 ucode_perror(common_path, EM_SYS); 259 return (EM_SYS); 260 } 261 262 if (write(fd, buf, size) != size) { 263 (void) close(fd); 264 ucode_perror(common_path, EM_SYS); 265 return (EM_SYS); 266 } 267 268 (void) close(fd); 269 270 /* skip over magic number & equivalence table header */ 271 ptr += 2; size -= 8; 272 273 count = *ptr++; size -= 4; 274 275 /* equivalence table uses special name */ 276 (void) snprintf(common_path, PATH_MAX, "%s/%s", path, 277 "equivalence-table"); 278 279 for (;;) { 280 dprintf("path = %s\n", common_path); 281 fd = open(common_path, O_WRONLY | O_CREAT | O_TRUNC, 282 S_IRUSR | S_IRGRP | S_IROTH); 283 284 if (fd == -1) { 285 ucode_perror(common_path, EM_SYS); 286 return (EM_SYS); 287 } 288 289 if (write(fd, ptr, count) != count) { 290 (void) close(fd); 291 ucode_perror(common_path, EM_SYS); 292 return (EM_SYS); 293 } 294 295 (void) close(fd); 296 ptr += count >> 2; size -= count; 297 298 if (!size) 299 return (EM_OK); 300 301 ptr++; size -= 4; 302 count = *ptr++; size -= 4; 303 304 /* construct name from header information */ 305 uh = (ucode_header_amd_t *)ptr; 306 307 if (uh->uh_cpu_rev != last_cpu_rev) { 308 last_cpu_rev = uh->uh_cpu_rev; 309 counter = 0; 310 } 311 312 (void) snprintf(common_path, PATH_MAX, "%s/%04X-%02X", path, 313 uh->uh_cpu_rev, counter++); 314 } 315 } 316 317 static ucode_errno_t 318 ucode_gen_files_intel(uint8_t *buf, int size, char *path) 319 { 320 int remaining; 321 char common_path[PATH_MAX]; 322 DIR *dirp; 323 struct dirent *dp; 324 325 (void) snprintf(common_path, PATH_MAX, "%s/%s", path, 326 UCODE_INSTALL_COMMON_PATH); 327 328 if (mkdirp(common_path, 0755) == -1 && errno != EEXIST) { 329 ucode_perror(common_path, EM_SYS); 330 return (EM_SYS); 331 } 332 333 for (remaining = size; remaining > 0; ) { 334 uint32_t total_size, body_size, offset; 335 char firstname[PATH_MAX]; 336 char name[PATH_MAX]; 337 int i; 338 uint8_t *curbuf = &buf[size - remaining]; 339 ucode_header_intel_t *uhp; 340 ucode_ext_table_intel_t *extp; 341 342 uhp = (ucode_header_intel_t *)(intptr_t)curbuf; 343 344 total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size); 345 body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size); 346 347 remaining -= total_size; 348 349 (void) snprintf(firstname, PATH_MAX, "%s/%08X-%02X", 350 common_path, uhp->uh_signature, uhp->uh_proc_flags); 351 dprintf("firstname = %s\n", firstname); 352 353 if (ucode_should_update_intel(firstname, uhp->uh_rev) != 0) { 354 int fd; 355 356 /* Remove the existing one first */ 357 (void) unlink(firstname); 358 359 if ((fd = open(firstname, O_WRONLY | O_CREAT | O_TRUNC, 360 S_IRUSR | S_IRGRP | S_IROTH)) == -1) { 361 ucode_perror(firstname, EM_SYS); 362 return (EM_SYS); 363 } 364 365 if (write(fd, curbuf, total_size) != total_size) { 366 (void) close(fd); 367 ucode_perror(firstname, EM_SYS); 368 return (EM_SYS); 369 } 370 371 (void) close(fd); 372 } 373 374 /* 375 * Only 1 byte of the proc_flags field is used, therefore 376 * we only need to match 8 potential platform ids. 377 */ 378 for (i = 0; i < 8; i++) { 379 uint32_t platid = uhp->uh_proc_flags & (1 << i); 380 381 if (platid == 0 && uhp->uh_proc_flags != 0) 382 continue; 383 384 (void) snprintf(name, PATH_MAX, 385 "%s/%08X-%02X", path, uhp->uh_signature, platid); 386 387 dprintf("proc_flags = %x, platid = %x, name = %s\n", 388 uhp->uh_proc_flags, platid, name); 389 390 if (ucode_should_update_intel(name, uhp->uh_rev) != 0) { 391 392 /* Remove the existing one first */ 393 (void) unlink(name); 394 395 if (link(firstname, name) == -1) { 396 ucode_perror(name, EM_SYS); 397 return (EM_SYS); 398 } 399 } 400 401 if (uhp->uh_proc_flags == 0) 402 break; 403 } 404 405 offset = UCODE_HEADER_SIZE_INTEL + body_size; 406 407 /* Check to see if there is extended signature table */ 408 if (total_size == offset) 409 continue; 410 411 /* There is extended signature table. More processing. */ 412 extp = (ucode_ext_table_intel_t *)(uintptr_t)&curbuf[offset]; 413 414 for (i = 0; i < extp->uet_count; i++) { 415 ucode_ext_sig_intel_t *uesp = &extp->uet_ext_sig[i]; 416 int j; 417 418 for (j = 0; j < 8; j++) { 419 uint32_t id = uesp->ues_proc_flags & (1 << j); 420 421 if (id == 0 && uesp->ues_proc_flags) 422 continue; 423 424 (void) snprintf(name, PATH_MAX, 425 "%s/%08X-%02X", path, extp->uet_ext_sig[i], 426 id); 427 428 if (ucode_should_update_intel(name, uhp->uh_rev) 429 != 0) { 430 431 /* Remove the existing one first */ 432 (void) unlink(name); 433 if (link(firstname, name) == -1) { 434 ucode_perror(name, EM_SYS); 435 return (EM_SYS); 436 } 437 } 438 439 if (uesp->ues_proc_flags == 0) 440 break; 441 } 442 } 443 444 } 445 446 /* 447 * Remove files with no links to them. These are probably 448 * obsolete microcode files. 449 */ 450 if ((dirp = opendir(common_path)) == NULL) { 451 ucode_perror(common_path, EM_SYS); 452 return (EM_SYS); 453 } 454 455 while ((dp = readdir(dirp)) != NULL) { 456 char filename[PATH_MAX]; 457 struct stat statbuf; 458 459 (void) snprintf(filename, PATH_MAX, 460 "%s/%s", common_path, dp->d_name); 461 if (stat(filename, &statbuf) == -1) 462 continue; 463 464 if ((statbuf.st_mode & S_IFMT) == S_IFREG) { 465 if (statbuf.st_nlink == 1) 466 (void) unlink(filename); 467 } 468 } 469 470 (void) closedir(dirp); 471 472 return (EM_OK); 473 } 474 475 /* 476 * Returns 0 on success, 2 on usage error, and 3 on operation error. 477 */ 478 int 479 main(int argc, char *argv[]) 480 { 481 int c; 482 int action = 0; 483 int actcount = 0; 484 char *path = NULL; 485 char *filename = NULL; 486 int errflg = 0; 487 int dev_fd = -1; 488 int fd = -1; 489 int verbose = 0; 490 uint8_t *buf = NULL; 491 ucode_errno_t rc = EM_OK; 492 processorid_t cpuid_max; 493 struct stat filestat; 494 uint32_t ucode_size; 495 496 (void) setlocale(LC_ALL, ""); 497 498 #if !defined(TEXT_DOMAIN) 499 #define TEXT_DOMAIN "SYS_TEST" 500 #endif 501 (void) textdomain(TEXT_DOMAIN); 502 503 cmdname = basename(argv[0]); 504 505 while ((c = getopt(argc, argv, "idhuvVR:")) != EOF) { 506 switch (c) { 507 508 case 'i': 509 action |= UCODE_OPT_INSTALL; 510 actcount++; 511 break; 512 513 case 'u': 514 action |= UCODE_OPT_UPDATE; 515 actcount++; 516 break; 517 518 case 'v': 519 action |= UCODE_OPT_VERSION; 520 actcount++; 521 break; 522 523 case 'd': 524 ucode_debug = 1; 525 break; 526 527 case 'R': 528 if (optarg[0] == '-') 529 errflg++; 530 else if (strlen(optarg) > UCODE_MAX_PATH_LEN) { 531 (void) fprintf(stderr, 532 gettext("Alternate path too long\n")); 533 errflg++; 534 } else if ((path = strdup(optarg)) == NULL) { 535 errflg++; 536 } 537 538 break; 539 540 case 'V': 541 verbose = 1; 542 break; 543 544 case 'h': 545 usage(1); 546 return (0); 547 548 default: 549 usage(verbose); 550 return (2); 551 } 552 } 553 554 if (actcount != 1) { 555 (void) fprintf(stderr, gettext("%s: options -v, -i and -u " 556 "are mutually exclusive.\n"), cmdname); 557 usage(verbose); 558 return (2); 559 } 560 561 if (optind <= argc - 1) 562 filename = argv[optind]; 563 else if (!(action & UCODE_OPT_VERSION)) 564 errflg++; 565 566 if (errflg || action == 0) { 567 usage(verbose); 568 return (2); 569 } 570 571 /* 572 * Convert from text format to binary format 573 */ 574 if ((action & UCODE_OPT_INSTALL) || (action & UCODE_OPT_UPDATE)) { 575 int i; 576 UCODE_VENDORS; 577 578 for (i = 0; ucode_vendors[i].filestr != NULL; i++) { 579 dprintf("i = %d, filestr = %s, filename = %s\n", 580 i, ucode_vendors[i].filestr, filename); 581 if (strncasecmp(ucode_vendors[i].filestr, 582 basename(filename), 583 strlen(ucode_vendors[i].filestr)) == 0) { 584 ucode = &ucode_ops[i]; 585 (void) strncpy(ucode_vendor_str, 586 ucode_vendors[i].vendorstr, 587 sizeof (ucode_vendor_str)); 588 break; 589 } 590 } 591 592 if (ucode_vendors[i].filestr == NULL) { 593 rc = EM_NOVENDOR; 594 ucode_perror(basename(filename), rc); 595 goto err_out; 596 } 597 598 if ((stat(filename, &filestat)) < 0) { 599 rc = EM_SYS; 600 ucode_perror(filename, rc); 601 goto err_out; 602 } 603 604 if ((filestat.st_mode & S_IFMT) != S_IFREG && 605 (filestat.st_mode & S_IFMT) != S_IFLNK) { 606 rc = EM_FILEFORMAT; 607 ucode_perror(filename, rc); 608 goto err_out; 609 } 610 611 if ((buf = malloc(filestat.st_size)) == NULL) { 612 rc = EM_SYS; 613 ucode_perror(filename, rc); 614 goto err_out; 615 } 616 617 ucode_size = ucode->convert(filename, buf, filestat.st_size); 618 619 dprintf("ucode_size = %d\n", ucode_size); 620 621 if (ucode_size == 0) { 622 rc = EM_FILEFORMAT; 623 ucode_perror(filename, rc); 624 goto err_out; 625 } 626 627 if ((rc = ucode->validate(buf, ucode_size)) != EM_OK) { 628 ucode_perror(filename, rc); 629 goto err_out; 630 } 631 } 632 633 /* 634 * For the install option, the microcode file must start with 635 * "intel" for Intel microcode, and "amd" for AMD microcode. 636 */ 637 if (action & UCODE_OPT_INSTALL) { 638 /* 639 * If no path is provided by the -R option, put the files in 640 * /ucode_install_path/ucode_vendor_str/. 641 */ 642 if (path == NULL) { 643 if ((path = malloc(PATH_MAX)) == NULL) { 644 rc = EM_SYS; 645 ucode_perror("malloc", rc); 646 goto err_out; 647 } 648 649 (void) snprintf(path, PATH_MAX, "/%s/%s", 650 ucode_install_path, ucode_vendor_str); 651 } 652 653 if (mkdirp(path, 0755) == -1 && errno != EEXIST) { 654 rc = EM_SYS; 655 ucode_perror(path, rc); 656 goto err_out; 657 } 658 659 rc = ucode->gen_files(buf, ucode_size, path); 660 661 goto err_out; 662 } 663 664 if ((dev_fd = open(ucode_dev, O_RDONLY)) == -1) { 665 rc = EM_SYS; 666 ucode_perror(ucode_dev, rc); 667 goto err_out; 668 } 669 670 if (action & UCODE_OPT_VERSION) { 671 int tmprc; 672 uint32_t *revp = NULL; 673 int i; 674 #if defined(_SYSCALL32_IMPL) 675 struct ucode_get_rev_struct32 inf32; 676 #else 677 struct ucode_get_rev_struct info; 678 #endif 679 680 cpuid_max = (processorid_t)sysconf(_SC_CPUID_MAX); 681 682 if ((revp = (uint32_t *) 683 malloc(cpuid_max * sizeof (uint32_t))) == NULL) { 684 rc = EM_SYS; 685 ucode_perror("malloc", rc); 686 goto err_out; 687 } 688 689 for (i = 0; i < cpuid_max; i++) 690 revp[i] = (uint32_t)-1; 691 692 #if defined(_SYSCALL32_IMPL) 693 info32.ugv_rev = (caddr32_t)revp; 694 info32.ugv_size = cpuid_max; 695 info32.ugv_errno = EM_OK; 696 tmprc = ioctl(dev_fd, UCODE_GET_VERSION, &info32); 697 rc = info32.ugv_errno; 698 #else 699 info.ugv_rev = revp; 700 info.ugv_size = cpuid_max; 701 info.ugv_errno = EM_OK; 702 tmprc = ioctl(dev_fd, UCODE_GET_VERSION, &info); 703 rc = info.ugv_errno; 704 #endif 705 706 if (tmprc && rc == EM_OK) { 707 rc = EM_SYS; 708 } 709 710 if (rc == EM_OK) { 711 (void) printf(gettext("CPU\tMicrocode Version\n")); 712 for (i = 0; i < cpuid_max; i++) { 713 if (info.ugv_rev[i] == (uint32_t)-1) 714 continue; 715 (void) printf("%d\t0x%x\n", i, info.ugv_rev[i]); 716 } 717 } else { 718 ucode_perror(gettext("get microcode version"), rc); 719 } 720 721 if (revp) 722 free(revp); 723 } 724 725 if (action & UCODE_OPT_UPDATE) { 726 int tmprc; 727 #if defined(_SYSCALL32_IMPL) 728 struct ucode_write_struct32 uw_struct32; 729 #else 730 struct ucode_write_struct uw_struct; 731 #endif 732 733 #if defined(_SYSCALL32_IMPL) 734 uw_struct32.uw_size = ucode_size; 735 uw_struct32.uw_ucode = (caddr32_t)buf; 736 uw_struct32.uw_errno = EM_OK; 737 tmprc = ioctl(dev_fd, UCODE_UPDATE, &uw_struct32); 738 rc = uw_struct32.uw_errno; 739 740 #else 741 uw_struct.uw_size = ucode_size; 742 uw_struct.uw_ucode = buf; 743 uw_struct.uw_errno = EM_OK; 744 tmprc = ioctl(dev_fd, UCODE_UPDATE, &uw_struct); 745 rc = uw_struct.uw_errno; 746 #endif 747 748 if (rc == EM_OK) { 749 if (tmprc) { 750 rc = EM_SYS; 751 ucode_perror(ucode_dev, rc); 752 } 753 } else if (rc == EM_NOMATCH || rc == EM_HIGHERREV) { 754 ucode_perror(filename, rc); 755 } else { 756 ucode_perror(gettext("microcode update"), rc); 757 } 758 } 759 760 err_out: 761 if (dev_fd != -1) 762 (void) close(dev_fd); 763 764 if (fd != -1) 765 (void) close(fd); 766 767 free(buf); 768 free(path); 769 770 if (rc != EM_OK) 771 return (3); 772 773 return (0); 774 } 775