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 char common_path[PATH_MAX]; 241 int fd, count, counter; 242 ucode_header_amd_t *uh; 243 int last_cpu_rev = 0; 244 245 246 /* write container file */ 247 (void) snprintf(common_path, PATH_MAX, "%s/%s", path, "container"); 248 249 dprintf("path = %s\n", common_path); 250 fd = open(common_path, O_WRONLY | O_CREAT | O_TRUNC, 251 S_IRUSR | S_IRGRP | S_IROTH); 252 253 if (fd == -1) { 254 ucode_perror(common_path, EM_SYS); 255 return (EM_SYS); 256 } 257 258 if (write(fd, buf, size) != size) { 259 (void) close(fd); 260 ucode_perror(common_path, EM_SYS); 261 return (EM_SYS); 262 } 263 264 (void) close(fd); 265 266 /* skip over magic number & equivalence table header */ 267 ptr += 2; size -= 8; 268 269 count = *ptr++; size -= 4; 270 271 /* equivalence table uses special name */ 272 (void) snprintf(common_path, PATH_MAX, "%s/%s", path, 273 "equivalence-table"); 274 275 for (;;) { 276 dprintf("path = %s\n", common_path); 277 fd = open(common_path, O_WRONLY | O_CREAT | O_TRUNC, 278 S_IRUSR | S_IRGRP | S_IROTH); 279 280 if (fd == -1) { 281 ucode_perror(common_path, EM_SYS); 282 return (EM_SYS); 283 } 284 285 if (write(fd, ptr, count) != count) { 286 (void) close(fd); 287 ucode_perror(common_path, EM_SYS); 288 return (EM_SYS); 289 } 290 291 (void) close(fd); 292 ptr += count >> 2; size -= count; 293 294 if (!size) 295 return (EM_OK); 296 297 ptr++; size -= 4; 298 count = *ptr++; size -= 4; 299 300 /* construct name from header information */ 301 uh = (ucode_header_amd_t *)ptr; 302 303 if (uh->uh_cpu_rev != last_cpu_rev) { 304 last_cpu_rev = uh->uh_cpu_rev; 305 counter = 0; 306 } 307 308 (void) snprintf(common_path, PATH_MAX, "%s/%04X-%02X", path, 309 uh->uh_cpu_rev, counter++); 310 } 311 } 312 313 static ucode_errno_t 314 ucode_gen_files_intel(uint8_t *buf, int size, char *path) 315 { 316 int remaining; 317 char common_path[PATH_MAX]; 318 DIR *dirp; 319 struct dirent *dp; 320 321 (void) snprintf(common_path, PATH_MAX, "%s/%s", path, 322 UCODE_INSTALL_COMMON_PATH); 323 324 if (mkdirp(common_path, 0755) == -1 && errno != EEXIST) { 325 ucode_perror(common_path, EM_SYS); 326 return (EM_SYS); 327 } 328 329 for (remaining = size; remaining > 0; ) { 330 uint32_t total_size, body_size, offset; 331 char firstname[PATH_MAX]; 332 char name[PATH_MAX]; 333 int i; 334 uint8_t *curbuf = &buf[size - remaining]; 335 ucode_header_intel_t *uhp; 336 ucode_ext_table_intel_t *extp; 337 338 uhp = (ucode_header_intel_t *)(intptr_t)curbuf; 339 340 total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size); 341 body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size); 342 343 remaining -= total_size; 344 345 (void) snprintf(firstname, PATH_MAX, "%s/%08X-%02X", 346 common_path, uhp->uh_signature, uhp->uh_proc_flags); 347 dprintf("firstname = %s\n", firstname); 348 349 if (ucode_should_update_intel(firstname, uhp->uh_rev) != 0) { 350 int fd; 351 352 /* Remove the existing one first */ 353 (void) unlink(firstname); 354 355 if ((fd = open(firstname, O_WRONLY | O_CREAT | O_TRUNC, 356 S_IRUSR | S_IRGRP | S_IROTH)) == -1) { 357 ucode_perror(firstname, EM_SYS); 358 return (EM_SYS); 359 } 360 361 if (write(fd, curbuf, total_size) != total_size) { 362 (void) close(fd); 363 ucode_perror(firstname, EM_SYS); 364 return (EM_SYS); 365 } 366 367 (void) close(fd); 368 } 369 370 /* 371 * Only 1 byte of the proc_flags field is used, therefore 372 * we only need to match 8 potential platform ids. 373 */ 374 for (i = 0; i < 8; i++) { 375 uint32_t platid = uhp->uh_proc_flags & (1 << i); 376 377 if (platid == 0 && uhp->uh_proc_flags != 0) 378 continue; 379 380 (void) snprintf(name, PATH_MAX, 381 "%s/%08X-%02X", path, uhp->uh_signature, platid); 382 383 dprintf("proc_flags = %x, platid = %x, name = %s\n", 384 uhp->uh_proc_flags, platid, name); 385 386 if (ucode_should_update_intel(name, uhp->uh_rev) != 0) { 387 388 /* Remove the existing one first */ 389 (void) unlink(name); 390 391 if (link(firstname, name) == -1) { 392 ucode_perror(name, EM_SYS); 393 return (EM_SYS); 394 } 395 } 396 397 if (uhp->uh_proc_flags == 0) 398 break; 399 } 400 401 offset = UCODE_HEADER_SIZE_INTEL + body_size; 402 403 /* Check to see if there is extended signature table */ 404 if (total_size == offset) 405 continue; 406 407 /* There is extended signature table. More processing. */ 408 extp = (ucode_ext_table_intel_t *)(uintptr_t)&curbuf[offset]; 409 410 for (i = 0; i < extp->uet_count; i++) { 411 ucode_ext_sig_intel_t *uesp = &extp->uet_ext_sig[i]; 412 int j; 413 414 for (j = 0; j < 8; j++) { 415 uint32_t id = uesp->ues_proc_flags & (1 << j); 416 417 if (id == 0 && uesp->ues_proc_flags) 418 continue; 419 420 (void) snprintf(name, PATH_MAX, 421 "%s/%08X-%02X", path, extp->uet_ext_sig[i], 422 id); 423 424 if (ucode_should_update_intel(name, uhp->uh_rev) 425 != 0) { 426 427 /* Remove the existing one first */ 428 (void) unlink(name); 429 if (link(firstname, name) == -1) { 430 ucode_perror(name, EM_SYS); 431 return (EM_SYS); 432 } 433 } 434 435 if (uesp->ues_proc_flags == 0) 436 break; 437 } 438 } 439 440 } 441 442 /* 443 * Remove files with no links to them. These are probably 444 * obsolete microcode files. 445 */ 446 if ((dirp = opendir(common_path)) == NULL) { 447 ucode_perror(common_path, EM_SYS); 448 return (EM_SYS); 449 } 450 451 while ((dp = readdir(dirp)) != NULL) { 452 char filename[PATH_MAX]; 453 struct stat statbuf; 454 455 (void) snprintf(filename, PATH_MAX, 456 "%s/%s", common_path, dp->d_name); 457 if (stat(filename, &statbuf) == -1) 458 continue; 459 460 if ((statbuf.st_mode & S_IFMT) == S_IFREG) { 461 if (statbuf.st_nlink == 1) 462 (void) unlink(filename); 463 } 464 } 465 466 (void) closedir(dirp); 467 468 return (EM_OK); 469 } 470 471 /* 472 * Returns 0 on success, 2 on usage error, and 3 on operation error. 473 */ 474 int 475 main(int argc, char *argv[]) 476 { 477 int c; 478 int action = 0; 479 int actcount = 0; 480 char *path = NULL; 481 char *filename = NULL; 482 int errflg = 0; 483 int dev_fd = -1; 484 int fd = -1; 485 int verbose = 0; 486 uint8_t *buf = NULL; 487 ucode_errno_t rc = EM_OK; 488 processorid_t cpuid_max; 489 struct stat filestat; 490 uint32_t ucode_size; 491 492 (void) setlocale(LC_ALL, ""); 493 494 #if !defined(TEXT_DOMAIN) 495 #define TEXT_DOMAIN "SYS_TEST" 496 #endif 497 (void) textdomain(TEXT_DOMAIN); 498 499 cmdname = basename(argv[0]); 500 501 while ((c = getopt(argc, argv, "idhuvVR:")) != EOF) { 502 switch (c) { 503 504 case 'i': 505 action |= UCODE_OPT_INSTALL; 506 actcount++; 507 break; 508 509 case 'u': 510 action |= UCODE_OPT_UPDATE; 511 actcount++; 512 break; 513 514 case 'v': 515 action |= UCODE_OPT_VERSION; 516 actcount++; 517 break; 518 519 case 'd': 520 ucode_debug = 1; 521 break; 522 523 case 'R': 524 if (optarg[0] == '-') 525 errflg++; 526 else if (strlen(optarg) > UCODE_MAX_PATH_LEN) { 527 (void) fprintf(stderr, 528 gettext("Alternate path too long\n")); 529 errflg++; 530 } else if ((path = strdup(optarg)) == NULL) { 531 errflg++; 532 } 533 534 break; 535 536 case 'V': 537 verbose = 1; 538 break; 539 540 case 'h': 541 usage(1); 542 return (0); 543 544 default: 545 usage(verbose); 546 return (2); 547 } 548 } 549 550 if (actcount != 1) { 551 (void) fprintf(stderr, gettext("%s: options -v, -i and -u " 552 "are mutually exclusive.\n"), cmdname); 553 usage(verbose); 554 return (2); 555 } 556 557 if (optind <= argc - 1) 558 filename = argv[optind]; 559 else if (!(action & UCODE_OPT_VERSION)) 560 errflg++; 561 562 if (errflg || action == 0) { 563 usage(verbose); 564 return (2); 565 } 566 567 /* 568 * Convert from text format to binary format 569 */ 570 if ((action & UCODE_OPT_INSTALL) || (action & UCODE_OPT_UPDATE)) { 571 int i; 572 UCODE_VENDORS; 573 574 for (i = 0; ucode_vendors[i].filestr != NULL; i++) { 575 dprintf("i = %d, filestr = %s, filename = %s\n", 576 i, ucode_vendors[i].filestr, filename); 577 if (strncasecmp(ucode_vendors[i].filestr, 578 basename(filename), 579 strlen(ucode_vendors[i].filestr)) == 0) { 580 ucode = &ucode_ops[i]; 581 (void) strncpy(ucode_vendor_str, 582 ucode_vendors[i].vendorstr, 583 sizeof (ucode_vendor_str)); 584 break; 585 } 586 } 587 588 if (ucode_vendors[i].filestr == NULL) { 589 rc = EM_NOVENDOR; 590 ucode_perror(basename(filename), rc); 591 goto err_out; 592 } 593 594 if ((stat(filename, &filestat)) < 0) { 595 rc = EM_SYS; 596 ucode_perror(filename, rc); 597 goto err_out; 598 } 599 600 if ((filestat.st_mode & S_IFMT) != S_IFREG && 601 (filestat.st_mode & S_IFMT) != S_IFLNK) { 602 rc = EM_FILEFORMAT; 603 ucode_perror(filename, rc); 604 goto err_out; 605 } 606 607 if ((buf = malloc(filestat.st_size)) == NULL) { 608 rc = EM_SYS; 609 ucode_perror(filename, rc); 610 goto err_out; 611 } 612 613 ucode_size = ucode->convert(filename, buf, filestat.st_size); 614 615 dprintf("ucode_size = %d\n", ucode_size); 616 617 if (ucode_size == 0) { 618 rc = EM_FILEFORMAT; 619 ucode_perror(filename, rc); 620 goto err_out; 621 } 622 623 if ((rc = ucode->validate(buf, ucode_size)) != EM_OK) { 624 ucode_perror(filename, rc); 625 goto err_out; 626 } 627 } 628 629 /* 630 * For the install option, the microcode file must start with 631 * "intel" for Intel microcode, and "amd" for AMD microcode. 632 */ 633 if (action & UCODE_OPT_INSTALL) { 634 /* 635 * If no path is provided by the -R option, put the files in 636 * /ucode_install_path/ucode_vendor_str/. 637 */ 638 if (path == NULL) { 639 if ((path = malloc(PATH_MAX)) == NULL) { 640 rc = EM_SYS; 641 ucode_perror("malloc", rc); 642 goto err_out; 643 } 644 645 (void) snprintf(path, PATH_MAX, "/%s/%s", 646 ucode_install_path, ucode_vendor_str); 647 } 648 649 if (mkdirp(path, 0755) == -1 && errno != EEXIST) { 650 rc = EM_SYS; 651 ucode_perror(path, rc); 652 goto err_out; 653 } 654 655 rc = ucode->gen_files(buf, ucode_size, path); 656 657 goto err_out; 658 } 659 660 if ((dev_fd = open(ucode_dev, O_RDONLY)) == -1) { 661 rc = EM_SYS; 662 ucode_perror(ucode_dev, rc); 663 goto err_out; 664 } 665 666 if (action & UCODE_OPT_VERSION) { 667 int tmprc; 668 uint32_t *revp = NULL; 669 int i; 670 #if defined(_SYSCALL32_IMPL) 671 struct ucode_get_rev_struct32 inf32; 672 #else 673 struct ucode_get_rev_struct info; 674 #endif 675 676 cpuid_max = (processorid_t)sysconf(_SC_CPUID_MAX); 677 678 if ((revp = (uint32_t *) 679 malloc(cpuid_max * sizeof (uint32_t))) == NULL) { 680 rc = EM_SYS; 681 ucode_perror("malloc", rc); 682 goto err_out; 683 } 684 685 for (i = 0; i < cpuid_max; i++) 686 revp[i] = (uint32_t)-1; 687 688 #if defined(_SYSCALL32_IMPL) 689 info32.ugv_rev = (caddr32_t)revp; 690 info32.ugv_size = cpuid_max; 691 info32.ugv_errno = EM_OK; 692 tmprc = ioctl(dev_fd, UCODE_GET_VERSION, &info32); 693 rc = info32.ugv_errno; 694 #else 695 info.ugv_rev = revp; 696 info.ugv_size = cpuid_max; 697 info.ugv_errno = EM_OK; 698 tmprc = ioctl(dev_fd, UCODE_GET_VERSION, &info); 699 rc = info.ugv_errno; 700 #endif 701 702 if (tmprc && rc == EM_OK) { 703 rc = EM_SYS; 704 } 705 706 if (rc == EM_OK) { 707 (void) printf(gettext("CPU\tMicrocode Version\n")); 708 for (i = 0; i < cpuid_max; i++) { 709 if (info.ugv_rev[i] == (uint32_t)-1) 710 continue; 711 (void) printf("%d\t0x%x\n", i, info.ugv_rev[i]); 712 } 713 } else { 714 ucode_perror(gettext("get microcode version"), rc); 715 } 716 717 if (revp) 718 free(revp); 719 } 720 721 if (action & UCODE_OPT_UPDATE) { 722 int tmprc; 723 #if defined(_SYSCALL32_IMPL) 724 struct ucode_write_struct32 uw_struct32; 725 #else 726 struct ucode_write_struct uw_struct; 727 #endif 728 729 #if defined(_SYSCALL32_IMPL) 730 uw_struct32.uw_size = ucode_size; 731 uw_struct32.uw_ucode = (caddr32_t)buf; 732 uw_struct32.uw_errno = EM_OK; 733 tmprc = ioctl(dev_fd, UCODE_UPDATE, &uw_struct32); 734 rc = uw_struct32.uw_errno; 735 736 #else 737 uw_struct.uw_size = ucode_size; 738 uw_struct.uw_ucode = buf; 739 uw_struct.uw_errno = EM_OK; 740 tmprc = ioctl(dev_fd, UCODE_UPDATE, &uw_struct); 741 rc = uw_struct.uw_errno; 742 #endif 743 744 if (rc == EM_OK) { 745 if (tmprc) { 746 rc = EM_SYS; 747 ucode_perror(ucode_dev, rc); 748 } 749 } else if (rc == EM_NOMATCH || rc == EM_HIGHERREV) { 750 ucode_perror(filename, rc); 751 } else { 752 ucode_perror(gettext("microcode update"), rc); 753 } 754 } 755 756 err_out: 757 if (dev_fd != -1) 758 (void) close(dev_fd); 759 760 if (fd != -1) 761 (void) close(fd); 762 763 free(buf); 764 free(path); 765 766 if (rc != EM_OK) 767 return (3); 768 769 return (0); 770 } 771