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 }