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
dprintf(const char * format,...)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
usage(bool verbose)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
ucode_perror(const char * str,ucode_errno_t rc)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
ucode_convert_amd(const char * infile,uint8_t * buf,size_t size)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
ucode_convert_intel(const char * infile,uint8_t * buf,size_t size)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
ucode_should_update_intel(char * filename,uint32_t new_rev)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
ucode_gen_files_amd(uint8_t * buf,int size,char * path)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
ucode_gen_files_intel(uint8_t * buf,int size,char * path)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
ucode_fms(uint32_t sig,uint8_t * family,uint8_t * model,uint8_t * stepping)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
ucode_list_intel(uint8_t * buf,int size)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
ucode_list_amd(uint8_t * buf,int size)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
main(int argc,char * argv[])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