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