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 2025 Oxide Computer Company
30 */
31
32 #include <sys/bitext.h>
33 #include <sys/types.h>
34 #include <sys/processor.h>
35 #include <sys/sysmacros.h>
36 #include <sys/ucode.h>
37 #include <sys/ucode_intel.h>
38 #include <sys/ucode_amd.h>
39 #include <sys/utsname.h>
40 #include <sys/ioctl.h>
41 #include <sys/stat.h>
42 #include <unistd.h>
43 #include <dirent.h>
44 #include <fcntl.h>
45 #include <errno.h>
46 #include <stdbool.h>
47 #include <stdint.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <stdarg.h>
51 #include <string.h>
52 #include <strings.h>
53 #include <errno.h>
54 #include <syslog.h>
55 #include <time.h>
56 #include <ctype.h>
57 #include <assert.h>
58 #include <libgen.h>
59 #include <limits.h>
60 #include <locale.h>
61 #include <libintl.h>
62 #include <ucode/ucode_errno.h>
63 #include <ucode/ucode_utils_intel.h>
64 #include <ucode/ucode_utils_amd.h>
65
66 #define UCODE_OPT_INSTALL 0x0001
67 #define UCODE_OPT_UPDATE 0x0002
68 #define UCODE_OPT_VERSION 0x0004
69 #define UCODE_OPT_LIST 0x0008
70
71 static const char ucode_dev[] = "/dev/" UCODE_DRIVER_NAME;
72
73 static char *cmdname;
74
75 #define UCODE_INSTALL_COMMON_PATH ".f"
76
77 /*
78 * The maximum directory path length that can be provided via -R has
79 * to allow for appending the files within the microcode bundles.
80 */
81 #define UCODE_MAX_PATH_LEN (PATH_MAX - \
82 MAX(UCODE_MAX_NAME_LEN_INTEL, UCODE_MAX_NAME_LEN_AMD) - 1)
83
84 static bool ucode_debug = false;
85
86 static ucode_errno_t ucode_convert_amd(const char *, uint8_t **, size_t *);
87 static ucode_errno_t ucode_convert_intel(const char *, uint8_t **, size_t *);
88
89 static ucode_errno_t ucode_gen_files_amd(uint8_t *, size_t, const char *);
90 static ucode_errno_t ucode_gen_files_intel(uint8_t *, size_t, const char *);
91
92 static void ucode_list_amd(uint8_t *, size_t);
93 static void ucode_list_intel(uint8_t *, size_t);
94
95 typedef struct ucode_source {
96 const char *us_prefix;
97 const char *us_vendor;
98 ucode_errno_t (*us_convert)(const char *, uint8_t **, size_t *);
99 ucode_errno_t (*us_gen_files)(uint8_t *, size_t, const char *);
100 ucode_errno_t (*us_validate)(uint8_t *, size_t);
101 void (*us_list)(uint8_t *, size_t);
102 } ucode_source_t;
103
104 static const ucode_source_t ucode_sources[] = {
105 {
106 .us_prefix = "intel",
107 .us_vendor = "GenuineIntel",
108 .us_convert = ucode_convert_intel,
109 .us_gen_files = ucode_gen_files_intel,
110 .us_validate = ucode_validate_intel,
111 .us_list = ucode_list_intel,
112 },
113 {
114 .us_prefix = "amd",
115 .us_vendor = "AuthenticAMD",
116 .us_convert = ucode_convert_amd,
117 .us_gen_files = ucode_gen_files_amd,
118 .us_validate = ucode_validate_amd,
119 .us_list = ucode_list_amd,
120 }
121 };
122
123 const ucode_source_t *ucode;
124
125 static void
dbgprintf(const char * format,...)126 dbgprintf(const char *format, ...)
127 {
128 if (ucode_debug) {
129 va_list alist;
130 va_start(alist, format);
131 (void) vfprintf(stderr, format, alist);
132 va_end(alist);
133 }
134 }
135
136 static void
usage(bool verbose)137 usage(bool verbose)
138 {
139 (void) fprintf(stderr, gettext("usage:\n"));
140 (void) fprintf(stderr, "\t%s -v\n", cmdname);
141 if (verbose) {
142 (void) fprintf(stderr,
143 gettext("\t\t Shows running microcode version.\n\n"));
144 }
145
146 (void) fprintf(stderr, "\t%s -u [-t type] microcode-file\n", cmdname);
147 if (verbose) {
148 (void) fprintf(stderr, gettext("\t\t Updates microcode to the "
149 "latest matching version found in\n"
150 "\t\t microcode-file.\n\n"));
151 }
152
153 (void) fprintf(stderr, "\t%s -l [-t type] microcode-file\n", cmdname);
154 if (verbose) {
155 (void) fprintf(stderr, gettext("\t\t Displays details of the "
156 "microcode file's contents.\n\n"));
157 }
158
159 (void) fprintf(stderr,
160 "\t%s -i [-t type] [-R path] microcode-file\n", cmdname);
161 if (verbose) {
162 (void) fprintf(stderr, gettext("\t\t Installs microcode to be "
163 "used for subsequent boots.\n"));
164 }
165 (void) fprintf(stderr, gettext(
166 "\nThe type of the microcode file must either be specified with "
167 "the -t option\nor microcode-file must start with the vendor name "
168 "prefix, either \"intel\"\nor \"amd\", so that the type can be "
169 "inferred from it.\n\n"));
170 }
171
172 static void
ucode_perror(const char * str,ucode_errno_t rc)173 ucode_perror(const char *str, ucode_errno_t rc)
174 {
175 (void) fprintf(stderr, "%s: %s: %s\n", cmdname, str,
176 errno == 0 ? ucode_strerror(rc) : strerror(errno));
177 errno = 0;
178 }
179
180 static int
bcd_to_int(uint8_t b)181 bcd_to_int(uint8_t b)
182 {
183 int high = (b >> 4) & 0xf;
184 int low = b & 0xf;
185
186 if (high > 9 || low > 9)
187 return (-1);
188 return (high * 10 + low);
189 }
190
191 /*
192 * Extract the family, model and stepping values from a 32-bit CPU signature.
193 * These bit fields are defined by the Intel Application Note AP-485
194 * "Intel Processor Identification and the CPUID Instruction"
195 */
196 static void
ucode_fms(uint32_t sig,uint8_t * family,uint8_t * model,uint8_t * stepping)197 ucode_fms(uint32_t sig, uint8_t *family, uint8_t *model, uint8_t *stepping)
198 {
199 const uint8_t xfamily = bitx32(sig, 27, 20);
200 const uint8_t bfamily = bitx32(sig, 11, 8);
201 const uint8_t xmodel = bitx32(sig, 19, 16);
202 const uint8_t bmodel = bitx32(sig, 7, 4);
203
204 *family = bfamily == 0xf ? bfamily + xfamily : bfamily;
205 *model = bfamily == 0x6 || bfamily == 0xf ?
206 (xmodel << 4) | bmodel : bmodel;
207 *stepping = bitx32(sig, 3, 0);
208 }
209
210 /*
211 * AMD microcode updates use a compressed 16-bit CPU identifier called an
212 * Equivalent Processor ID. The compression is achieved by removing the base
213 * family field, and assuming it is always 0xf, and reducing the size of the
214 * extended family field to 4 bits such that only families up to 0x1e can be
215 * represented. The structure is:
216 *
217 * [15:12] Extended Family (i.e. family - 0xf)
218 * [11:4] Model
219 * [3:0] Stepping
220 *
221 * This function expands an AMD Equivalent Processor ID to a traditional
222 * 32-bit CPU signature.
223 */
224 static uint32_t
amd_equivcpu_to_sig(uint16_t equiv)225 amd_equivcpu_to_sig(uint16_t equiv)
226 {
227 uint16_t xfamily, model, stepping;
228 uint32_t sig = 0;
229
230 xfamily = bitx16(equiv, 15, 12);
231 model = bitx16(equiv, 11, 4);
232 stepping = bitx16(equiv, 3, 0);
233
234 sig = bitset32(sig, 27, 20, xfamily); /* ext family */
235 sig = bitset32(sig, 11, 8, 0xf); /* family */
236 sig = bitset32(sig, 19, 16, bitx16(model, 7, 4)); /* ext model */
237 sig = bitset32(sig, 7, 4, bitx16(model, 3, 0)); /* model */
238 sig = bitset32(sig, 3, 0, stepping);
239
240 return (sig);
241 }
242
243 /*
244 * Load a microcode release which is in AMD's binary container format. If the
245 * provided file appears to be a raw binary update, cons up a container
246 * containing just that patch.
247 */
248 static ucode_errno_t
ucode_convert_amd(const char * infile,uint8_t ** bufp,size_t * sizep)249 ucode_convert_amd(const char *infile, uint8_t **bufp, size_t *sizep)
250 {
251 ucode_header_amd_t *patch;
252 ucode_section_amd_t *section;
253 ucode_eqtbl_amd_t *eq;
254 int month, day, yearl, fd;
255 size_t csize;
256 ssize_t rsize;
257 uint8_t *buf = *bufp;
258 size_t size = *sizep;
259
260 if (infile == NULL || buf == NULL || size == 0)
261 return (EM_INVALIDARG);
262
263 if ((fd = open(infile, O_RDONLY)) < 0)
264 return (EM_SYS);
265
266 rsize = read(fd, buf, size);
267 if (rsize < 0) {
268 int _errno = errno;
269 (void) close(fd);
270 errno = _errno;
271 return (EM_SYS);
272 }
273
274 (void) close(fd);
275
276 if (rsize == 0)
277 return (EM_FILEFORMAT);
278
279 size = rsize;
280
281 /*
282 * AMD microcode is distributed in two forms. As container/bundle files
283 * or as individual binary patches. If this looks like a container,
284 * we're done.
285 */
286 if (ucode->us_validate(buf, size) == EM_OK) {
287 *sizep = size;
288 return (EM_OK);
289 }
290
291 /*
292 * Otherwise, see if this looks like a binary patch. We're limited in
293 * what we can check here but we can look at the date field to see if
294 * it is plausible. That field is encoded as a kind of packed
295 * BCD 0xMMDDYYYY.
296 */
297 patch = (ucode_header_amd_t *)*bufp;
298 month = bcd_to_int(bitx32(patch->uh_date, 31, 24));
299 day = bcd_to_int(bitx32(patch->uh_date, 23, 16));
300 yearl = bcd_to_int(bitx32(patch->uh_date, 7, 0));
301 if (day < 1 || day > 31 || month < 1 || month > 12 ||
302 yearl < 0 || yearl > 99) {
303 dbgprintf("implausible date code: 0x%x\n", patch->uh_date);
304 return (EM_FILEFORMAT);
305 }
306
307 /* It's plausibly a binary patch; cons up a container */
308 dbgprintf("creating microcode container\n");
309 csize =
310 sizeof (uint32_t) + /* Magic */
311 2 * sizeof (ucode_section_amd_t) + /* TLV headers */
312 2 * sizeof (ucode_eqtbl_amd_t); /* Equivalence table */
313 if (size > SIZE_MAX - csize) {
314 dbgprintf("container size too large (patch size %zu)\n", size);
315 return (EM_FILEFORMAT);
316 }
317 csize += size; /* Patch payload */
318
319 buf = realloc(*bufp, csize);
320 if (buf == NULL)
321 return (EM_SYS);
322
323 /* Relocate the patch data */
324 patch = (ucode_header_amd_t *)(buf + csize - size);
325 bcopy(buf, patch, size);
326
327 /* Build the container */
328 *(uint32_t *)buf = UCODE_AMD_CONTAINER_MAGIC;
329
330 /* Equivalence table section */
331 section = (ucode_section_amd_t *)(buf + sizeof (uint32_t));
332 eq = (ucode_eqtbl_amd_t *)section->usa_data;
333
334 section->usa_type = UCODE_AMD_CONTAINER_TYPE_EQUIV;
335 section->usa_size = 2 * sizeof (*eq);
336 eq->ue_equiv_cpu = patch->uh_cpu_rev;
337 eq->ue_inst_cpu = amd_equivcpu_to_sig(patch->uh_cpu_rev);
338 /* Create the equivalence table terminator record */
339 bzero(eq + 1, sizeof (*eq));
340
341 /* Patch section */
342 section =
343 (ucode_section_amd_t *)(section->usa_data + section->usa_size);
344 section->usa_type = UCODE_AMD_CONTAINER_TYPE_PATCH;
345 section->usa_size = size;
346
347 *bufp = buf;
348 *sizep = csize;
349
350 return (EM_OK);
351 }
352
353 /*
354 * Convert text format microcode release into binary format.
355 */
356 #define LINESIZE 120 /* copyright line sometimes is longer than 80 */
357 static ucode_errno_t
ucode_convert_intel(const char * infile,uint8_t ** bufp,size_t * sizep)358 ucode_convert_intel(const char *infile, uint8_t **bufp, size_t *sizep)
359 {
360 char linebuf[LINESIZE];
361 FILE *infd = NULL;
362 bool firstline = true;
363 size_t count = 0;
364 uint8_t *buf = *bufp;
365 size_t size = *sizep;
366 uint32_t *intbuf = (uint32_t *)(uintptr_t)buf;
367
368 if (infile == NULL || buf == NULL || size == 0)
369 return (EM_INVALIDARG);
370
371 if ((infd = fopen(infile, "r")) == NULL)
372 return (EM_SYS);
373
374 while (fgets(linebuf, LINESIZE, infd)) {
375
376 /* Check to see if we are processing a binary file */
377 if (firstline && !isprint(linebuf[0])) {
378 if (fseek(infd, 0, SEEK_SET) == 0)
379 count = fread(buf, 1, size, infd);
380
381 (void) fclose(infd);
382
383 if (count == 0)
384 return (EM_FILEFORMAT);
385
386 *sizep = count;
387 return (EM_OK);
388 }
389
390 firstline = false;
391
392 /* Skip blank lines */
393 if (strlen(linebuf) == 1)
394 continue;
395
396 /* Skip lines with all spaces or tabs */
397 if (strcspn(linebuf, " \t") == 0)
398 continue;
399
400 /* Text file. Skip comments. */
401 if (linebuf[0] == '/')
402 continue;
403
404 if (sscanf(linebuf, "%x, %x, %x, %x",
405 &intbuf[count], &intbuf[count+1],
406 &intbuf[count+2], &intbuf[count+3]) != 4)
407 break;
408
409 count += 4;
410 }
411
412 (void) fclose(infd);
413
414 /*
415 * If we get here, we are processing a text format file
416 * where "count" is used to count the number of integers
417 * read. Convert it to number of characters read.
418 */
419 *sizep = count * sizeof (int);
420
421 return (EM_OK);
422 }
423
424 /*
425 * Returns 0 if no need to update the link; -1 otherwise
426 */
427 static int
ucode_should_update_intel(char * filename,uint32_t new_rev)428 ucode_should_update_intel(char *filename, uint32_t new_rev)
429 {
430 int fd;
431 struct stat statbuf;
432 ucode_header_intel_t header;
433
434 /*
435 * If the file or link already exists, check to see if
436 * it is necessary to update it.
437 */
438 if (stat(filename, &statbuf) == 0) {
439 if ((fd = open(filename, O_RDONLY)) == -1)
440 return (-1);
441
442 if (read(fd, &header, sizeof (header)) == -1) {
443 (void) close(fd);
444 return (-1);
445 }
446
447 (void) close(fd);
448
449 if (header.uh_rev >= new_rev)
450 return (0);
451 }
452
453 return (-1);
454 }
455
456 /*
457 * Generate microcode binary files. Must be called after ucode_validate().
458 */
459 static ucode_errno_t
ucode_gen_files_amd(uint8_t * buf,size_t size,const char * path)460 ucode_gen_files_amd(uint8_t *buf, size_t size, const char *path)
461 {
462 char common_path[PATH_MAX];
463 int fd;
464 uint16_t last_cpu_rev = 0;
465 uint32_t counter = 0;
466 int n;
467
468 /* write container file */
469 n = snprintf(common_path, sizeof (common_path), "%s/container", path);
470 if (n >= sizeof (common_path)) {
471 dbgprintf("failed to construct container path\n");
472 return (EM_FILEFORMAT);
473 }
474
475 dbgprintf("path = %s\n", common_path);
476 fd = open(common_path, O_WRONLY | O_CREAT | O_TRUNC,
477 S_IRUSR | S_IRGRP | S_IROTH);
478
479 if (fd == -1) {
480 ucode_perror(common_path, EM_SYS);
481 return (EM_SYS);
482 }
483
484 if (write(fd, buf, size) != size) {
485 (void) close(fd);
486 ucode_perror(common_path, EM_SYS);
487 return (EM_SYS);
488 }
489
490 (void) close(fd);
491
492 /* skip over magic number, the file has already been validated */
493 buf += sizeof (uint32_t);
494 size -= sizeof (uint32_t);
495
496 while (size > sizeof (ucode_section_amd_t)) {
497 ucode_section_amd_t *section = (ucode_section_amd_t *)buf;
498 int n;
499
500 switch (section->usa_type) {
501 case UCODE_AMD_CONTAINER_TYPE_EQUIV:
502 n = snprintf(common_path, sizeof (common_path), "%s/%s",
503 path, UCODE_AMD_EQUIVALENCE_TABLE_NAME);
504 break;
505 case UCODE_AMD_CONTAINER_TYPE_PATCH: {
506 ucode_header_amd_t *uh =
507 (ucode_header_amd_t *)section->usa_data;
508
509 if (uh->uh_cpu_rev != last_cpu_rev) {
510 last_cpu_rev = uh->uh_cpu_rev;
511 counter = 0;
512 }
513
514 n = snprintf(common_path, sizeof (common_path),
515 "%s/%04X-%02X", path, uh->uh_cpu_rev, counter++);
516 break;
517 }
518 default:
519 /*
520 * Since the container has already been validated, this
521 * should never happen.
522 */
523 return (EM_FILEFORMAT);
524 }
525
526 if (n >= sizeof (common_path)) {
527 dbgprintf("failed to construct component path\n");
528 return (EM_FILEFORMAT);
529 }
530
531 dbgprintf("path = %s\n", common_path);
532 fd = open(common_path, O_WRONLY | O_CREAT | O_TRUNC,
533 S_IRUSR | S_IRGRP | S_IROTH);
534
535 if (fd == -1) {
536 ucode_perror(common_path, EM_SYS);
537 return (EM_SYS);
538 }
539
540 if (write(fd, section->usa_data, section->usa_size) !=
541 section->usa_size) {
542 (void) close(fd);
543 ucode_perror(common_path, EM_SYS);
544 return (EM_SYS);
545 }
546
547 (void) close(fd);
548
549 size -= section->usa_size + sizeof (ucode_section_amd_t);
550 buf += section->usa_size + sizeof (ucode_section_amd_t);
551 }
552
553 return (EM_OK);
554 }
555
556 static ucode_errno_t
ucode_gen_files_intel(uint8_t * buf,size_t size,const char * path)557 ucode_gen_files_intel(uint8_t *buf, size_t size, const char *path)
558 {
559 size_t remaining;
560 char common_path[PATH_MAX];
561 DIR *dirp;
562 struct dirent *dp;
563 int n;
564
565 n = snprintf(common_path, sizeof (common_path), "%s/%s", path,
566 UCODE_INSTALL_COMMON_PATH);
567 if (n >= sizeof (common_path)) {
568 dbgprintf("failed to construct path for %s\n",
569 UCODE_INSTALL_COMMON_PATH);
570 return (EM_FILEFORMAT);
571 }
572
573 if (mkdirp(common_path, 0755) == -1 && errno != EEXIST) {
574 ucode_perror(common_path, EM_SYS);
575 return (EM_SYS);
576 }
577
578 for (remaining = size; remaining > 0; ) {
579 uint32_t total_size, body_size, offset;
580 char firstname[PATH_MAX];
581 char name[PATH_MAX];
582 int i;
583 uint8_t *curbuf = &buf[size - remaining];
584 ucode_header_intel_t *uhp;
585 ucode_ext_table_intel_t *extp;
586
587 uhp = (ucode_header_intel_t *)(uintptr_t)curbuf;
588
589 total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
590 body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size);
591
592 remaining -= total_size;
593
594 n = snprintf(firstname, sizeof (common_path), "%s/%08X-%02X",
595 common_path, uhp->uh_signature, uhp->uh_proc_flags);
596 if (n >= sizeof (common_path)) {
597 dbgprintf("failed to construct component path\n");
598 return (EM_FILEFORMAT);
599 }
600 dbgprintf("firstname = %s\n", firstname);
601
602 if (ucode_should_update_intel(firstname, uhp->uh_rev) != 0) {
603 int fd;
604
605 /* Remove the existing one first */
606 (void) unlink(firstname);
607
608 if ((fd = open(firstname, O_WRONLY | O_CREAT | O_TRUNC,
609 S_IRUSR | S_IRGRP | S_IROTH)) == -1) {
610 ucode_perror(firstname, EM_SYS);
611 return (EM_SYS);
612 }
613
614 if (write(fd, curbuf, total_size) != total_size) {
615 (void) close(fd);
616 ucode_perror(firstname, EM_SYS);
617 return (EM_SYS);
618 }
619
620 (void) close(fd);
621 }
622
623 /*
624 * Only 1 byte of the proc_flags field is used, therefore
625 * we only need to match 8 potential platform ids.
626 */
627 for (i = 0; i < 8; i++) {
628 uint32_t platid = uhp->uh_proc_flags & (1 << i);
629
630 if (platid == 0 && uhp->uh_proc_flags != 0)
631 continue;
632
633 n = snprintf(name, sizeof (common_path),
634 "%s/%08X-%02X", path, uhp->uh_signature, platid);
635 if (n >= sizeof (common_path)) {
636 dbgprintf("failed to construct platid path\n");
637 return (EM_FILEFORMAT);
638 }
639
640 dbgprintf("proc_flags = %x, platid = %x, name = %s\n",
641 uhp->uh_proc_flags, platid, name);
642
643 if (ucode_should_update_intel(name,
644 uhp->uh_rev) != 0) {
645 /* Remove the existing one first */
646 (void) unlink(name);
647 if (link(firstname, name) == -1) {
648 ucode_perror(name, EM_SYS);
649 return (EM_SYS);
650 }
651 }
652
653 if (uhp->uh_proc_flags == 0)
654 break;
655 }
656
657 offset = UCODE_HEADER_SIZE_INTEL + body_size;
658
659 /* Check to see if there is extended signature table */
660 if (total_size == offset)
661 continue;
662
663 /* There is extended signature table. More processing. */
664 extp = (ucode_ext_table_intel_t *)&curbuf[offset];
665
666 for (i = 0; i < extp->uet_count; i++) {
667 ucode_ext_sig_intel_t *uesp = &extp->uet_ext_sig[i];
668 int j;
669
670 for (j = 0; j < 8; j++) {
671 uint32_t id = uesp->ues_proc_flags & (1 << j);
672
673 if (id == 0 && uesp->ues_proc_flags)
674 continue;
675
676 n = snprintf(name, sizeof (common_path),
677 "%s/%08X-%02X", path,
678 uesp->ues_signature, id);
679 if (n >= sizeof (common_path)) {
680 dbgprintf(
681 "failed to construct ext path\n");
682 return (EM_FILEFORMAT);
683 }
684
685 dbgprintf("extsig: proc_flags = %x, "
686 "platid = %x, name = %s\n",
687 uesp->ues_proc_flags, id, name);
688
689 if (ucode_should_update_intel(name,
690 uhp->uh_rev) != 0) {
691 /* Remove the existing one first */
692 (void) unlink(name);
693 if (link(firstname, name) == -1) {
694 ucode_perror(name, EM_SYS);
695 return (EM_SYS);
696 }
697 }
698
699 if (uesp->ues_proc_flags == 0)
700 break;
701 }
702 }
703
704 }
705
706 /*
707 * Remove files with no links to them. These are probably
708 * obsolete microcode files.
709 */
710 if ((dirp = opendir(common_path)) == NULL) {
711 ucode_perror(common_path, EM_SYS);
712 return (EM_SYS);
713 }
714
715 while ((dp = readdir(dirp)) != NULL) {
716 char filename[PATH_MAX];
717 struct stat statbuf;
718
719 n = snprintf(filename, sizeof (common_path),
720 "%s/%s", common_path, dp->d_name);
721 if (n >= sizeof (common_path) || stat(filename, &statbuf) == -1)
722 continue;
723
724 if ((statbuf.st_mode & S_IFMT) == S_IFREG) {
725 if (statbuf.st_nlink == 1)
726 (void) unlink(filename);
727 }
728 }
729
730 (void) closedir(dirp);
731
732 return (EM_OK);
733 }
734
735 static void
ucode_list_intel(uint8_t * buf,size_t size)736 ucode_list_intel(uint8_t *buf, size_t size)
737 {
738 size_t remaining;
739
740 printf("Microcode patches:\n");
741 for (remaining = size; remaining > 0; ) {
742 uint8_t *curbuf = &buf[size - remaining];
743 uint8_t family, model, stepping;
744 uint32_t total_size, body_size, offset;
745 ucode_header_intel_t *uhp;
746 ucode_ext_table_intel_t *extp;
747
748 uhp = (ucode_header_intel_t *)(uintptr_t)curbuf;
749
750 total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
751 body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size);
752
753 remaining -= total_size;
754
755 ucode_fms(uhp->uh_signature, &family, &model, &stepping);
756
757 printf(
758 " %08lX-%02lX -> Family=%02x Model=%02x Stepping=%02x\n",
759 uhp->uh_signature, uhp->uh_proc_flags,
760 family, model, stepping);
761 printf(
762 " %14s Date=%08lX Bytes=%lu\n", "",
763 uhp->uh_date, uhp->uh_body_size);
764
765 offset = UCODE_HEADER_SIZE_INTEL + body_size;
766
767 /* Check to see if there is extended signature table */
768 if (total_size == offset)
769 continue;
770
771 printf("Extended Signature Table:\n");
772
773 extp = (ucode_ext_table_intel_t *)&curbuf[offset];
774
775 for (uint32_t i = 0; i < extp->uet_count; i++) {
776 ucode_ext_sig_intel_t *uesp = &extp->uet_ext_sig[i];
777
778 ucode_fms(uesp->ues_signature,
779 &family, &model, &stepping);
780
781 printf(
782 " %08lX-%02lX -> Family=%02x Model=%02x "
783 "Stepping=%02x\n",
784 uesp->ues_signature, uesp->ues_proc_flags,
785 family, model, stepping);
786 }
787 }
788 }
789
790 static void
ucode_list_amd(uint8_t * buf,size_t size)791 ucode_list_amd(uint8_t *buf, size_t size)
792 {
793 uint32_t last_type = UINT32_MAX;
794
795 /* The file has already been validated. Skip over magic number */
796 buf += sizeof (uint32_t);
797 size -= sizeof (uint32_t);
798
799 while (size > sizeof (ucode_section_amd_t)) {
800 ucode_section_amd_t *section = (ucode_section_amd_t *)buf;
801
802 switch (section->usa_type) {
803 case UCODE_AMD_CONTAINER_TYPE_EQUIV: {
804 ucode_eqtbl_amd_t *eq =
805 (ucode_eqtbl_amd_t *)section->usa_data;
806
807 if (last_type != section->usa_type) {
808 printf("Equivalence table:\n");
809 last_type = section->usa_type;
810 }
811 for (uint_t i = 0; eq->ue_inst_cpu != 0 &&
812 i < section->usa_size / sizeof (*eq); eq++, i++) {
813 uint8_t family, model, stepping;
814
815 ucode_fms(eq->ue_inst_cpu, &family, &model,
816 &stepping);
817
818 printf(" %08lX Family=%02x Model=%02x "
819 "Stepping=%02x -> %04X\n",
820 eq->ue_inst_cpu, family, model,
821 stepping, eq->ue_equiv_cpu);
822 }
823 break;
824 }
825 case UCODE_AMD_CONTAINER_TYPE_PATCH: {
826 ucode_header_amd_t *uh =
827 (ucode_header_amd_t *)section->usa_data;
828
829 if (uh->uh_cpu_rev == 0)
830 break;
831
832 if (last_type != section->usa_type) {
833 printf("Microcode patches:\n");
834 last_type = section->usa_type;
835 }
836
837 printf(" %4X -> Patch=%08lX Date=%08lX Bytes=%lu\n",
838 uh->uh_cpu_rev, uh->uh_patch_id, uh->uh_date,
839 section->usa_size);
840
841 break;
842 }
843 default:
844 break;
845 }
846
847 size -= section->usa_size + sizeof (ucode_section_amd_t);
848 buf += section->usa_size + sizeof (ucode_section_amd_t);
849 }
850 }
851
852 /*
853 * Returns 0 on success, 2 on usage error, and 3 on operation error.
854 */
855 int
main(int argc,char * argv[])856 main(int argc, char *argv[])
857 {
858 int c;
859 int action = 0;
860 int actcount = 0;
861 int typeindex = -1;
862 char *path = NULL;
863 char *filename = NULL;
864 int errflg = 0;
865 int dev_fd = -1;
866 int fd = -1;
867 bool verbose = false;
868 bool needfile = false;
869 uint8_t *buf = NULL;
870 ucode_errno_t rc = EM_OK;
871 processorid_t cpuid_max;
872 struct stat filestat;
873 size_t ucode_size = 0;
874
875 (void) setlocale(LC_ALL, "");
876
877 #if !defined(TEXT_DOMAIN)
878 #define TEXT_DOMAIN "SYS_TEST"
879 #endif
880 (void) textdomain(TEXT_DOMAIN);
881
882 cmdname = basename(argv[0]);
883
884 while ((c = getopt(argc, argv, "idhluvVR:t:")) != EOF) {
885 switch (c) {
886
887 case 'd':
888 ucode_debug = true;
889 break;
890
891 case 'i':
892 action |= UCODE_OPT_INSTALL;
893 actcount++;
894 needfile = true;
895 break;
896
897 case 'l':
898 action |= UCODE_OPT_LIST;
899 actcount++;
900 needfile = true;
901 break;
902
903 case 't':
904 if (typeindex != -1) {
905 (void) fprintf(stderr, gettext(
906 "-t can only be specified once\n"));
907 errflg++;
908 break;
909 }
910 for (uint_t i = 0; i < ARRAY_SIZE(ucode_sources); i++) {
911 if (strcmp(optarg,
912 ucode_sources[i].us_prefix) == 0) {
913 typeindex = i;
914 break;
915 }
916 }
917 if (typeindex == -1) {
918 (void) fprintf(stderr,
919 gettext("Unknown microcode type, %s\n"),
920 optarg);
921 errflg++;
922 }
923 break;
924
925 case 'u':
926 action |= UCODE_OPT_UPDATE;
927 actcount++;
928 needfile = true;
929 break;
930
931 case 'v':
932 action |= UCODE_OPT_VERSION;
933 actcount++;
934 break;
935
936 case 'R':
937 if (optarg[0] == '-') {
938 errflg++;
939 } else if (strlen(optarg) > UCODE_MAX_PATH_LEN) {
940 (void) fprintf(stderr,
941 gettext("Alternate path too long\n"));
942 errflg++;
943 } else if ((path = strdup(optarg)) == NULL) {
944 errflg++;
945 }
946
947 break;
948
949 case 'V':
950 verbose = true;
951 break;
952
953 case 'h':
954 usage(true);
955 return (0);
956
957 default:
958 usage(verbose);
959 return (2);
960 }
961 }
962
963 if (actcount == 0) {
964 (void) fprintf(stderr, gettext("%s: One of -i, -l, -u or -v "
965 "must be provided.\n"), cmdname);
966 usage(verbose);
967 return (2);
968 }
969
970 if (actcount != 1) {
971 (void) fprintf(stderr, gettext("%s: options -i, -l, -u and -v "
972 "are mutually exclusive.\n"), cmdname);
973 usage(verbose);
974 return (2);
975 }
976
977 if (typeindex != -1 && !needfile) {
978 (void) fprintf(stderr, gettext("%s: option -t requires one of "
979 "-i, -l or -u\n"), cmdname);
980 usage(verbose);
981 return (2);
982 }
983
984 if (optind <= argc - 1)
985 filename = argv[optind];
986 else if (needfile)
987 errflg++;
988
989 if (errflg || action == 0) {
990 usage(verbose);
991 return (2);
992 }
993
994 /*
995 * Convert from the vendor-shipped format to individual microcode files.
996 */
997 if (needfile) {
998 if (typeindex != -1) {
999 ucode = &ucode_sources[typeindex];
1000 } else {
1001 for (uint_t i = 0; i < ARRAY_SIZE(ucode_sources); i++) {
1002 const ucode_source_t *src = &ucode_sources[i];
1003
1004 dbgprintf("i = %d, filestr = %s, "
1005 "filename = %s\n",
1006 i, src->us_prefix, filename);
1007 if (strncasecmp(src->us_prefix,
1008 basename(filename),
1009 strlen(src->us_prefix)) == 0) {
1010 ucode = src;
1011 break;
1012 }
1013 }
1014 }
1015
1016 if (ucode == NULL) {
1017 rc = EM_NOVENDOR;
1018 (void) fprintf(stderr, "%s: %s.\n\n"
1019 "Either specify the type with the -t option, "
1020 "or rename the file such that\nits name begins "
1021 "with a vendor string.\n",
1022 cmdname, ucode_strerror(rc));
1023 goto out;
1024 }
1025
1026 dbgprintf("Selected microcode type %s (%s)\n",
1027 ucode->us_prefix, ucode->us_vendor);
1028
1029 if ((stat(filename, &filestat)) < 0) {
1030 rc = EM_SYS;
1031 ucode_perror(filename, rc);
1032 goto out;
1033 }
1034
1035 if ((filestat.st_mode & S_IFMT) != S_IFREG &&
1036 (filestat.st_mode & S_IFMT) != S_IFLNK) {
1037 rc = EM_FILEFORMAT;
1038 ucode_perror(filename, rc);
1039 goto out;
1040 }
1041
1042 ucode_size = filestat.st_size;
1043 if ((buf = malloc(ucode_size)) == NULL) {
1044 rc = EM_SYS;
1045 ucode_perror(filename, rc);
1046 goto out;
1047 }
1048
1049 rc = ucode->us_convert(filename, &buf, &ucode_size);
1050 if (rc != EM_OK) {
1051 ucode_perror(filename, rc);
1052 goto out;
1053 }
1054
1055 dbgprintf("ucode_size = %zd\n", ucode_size);
1056
1057 if ((rc = ucode->us_validate(buf, ucode_size)) != EM_OK) {
1058 ucode_perror(filename, rc);
1059 goto out;
1060 }
1061 }
1062
1063 if (action & UCODE_OPT_LIST) {
1064 ucode->us_list(buf, ucode_size);
1065 goto out;
1066 }
1067
1068 if (action & UCODE_OPT_INSTALL) {
1069 /*
1070 * If no path is provided by the -R option, put the files in
1071 * /platform/<arch>/ucode/<ucode_vendor_str>/.
1072 */
1073 if (path == NULL) {
1074 struct utsname uts;
1075
1076 if (uname(&uts) == -1) {
1077 perror("Unable to retrieve system uname");
1078 goto out;
1079 }
1080
1081 if ((path = malloc(PATH_MAX)) == NULL) {
1082 rc = EM_SYS;
1083 ucode_perror("malloc", rc);
1084 goto out;
1085 }
1086
1087 (void) snprintf(path, PATH_MAX, "/platform/%s/ucode/%s",
1088 uts.machine, ucode->us_vendor);
1089 }
1090
1091 if (mkdirp(path, 0755) == -1 && errno != EEXIST) {
1092 rc = EM_SYS;
1093 ucode_perror(path, rc);
1094 goto out;
1095 }
1096
1097 rc = ucode->us_gen_files(buf, ucode_size, path);
1098
1099 goto out;
1100 }
1101
1102 if ((dev_fd = open(ucode_dev, O_RDONLY)) == -1) {
1103 rc = EM_SYS;
1104 ucode_perror(ucode_dev, rc);
1105 goto out;
1106 }
1107
1108 if (action & UCODE_OPT_VERSION) {
1109 int tmprc;
1110 uint32_t *revp = NULL;
1111 int i;
1112 struct ucode_get_rev_struct info;
1113
1114 cpuid_max = (processorid_t)sysconf(_SC_CPUID_MAX);
1115
1116 if ((revp = (uint32_t *)
1117 malloc(cpuid_max * sizeof (uint32_t))) == NULL) {
1118 rc = EM_SYS;
1119 ucode_perror("malloc", rc);
1120 goto out;
1121 }
1122
1123 for (i = 0; i < cpuid_max; i++)
1124 revp[i] = (uint32_t)-1;
1125
1126 info.ugv_rev = revp;
1127 info.ugv_size = cpuid_max;
1128 info.ugv_errno = EM_OK;
1129 tmprc = ioctl(dev_fd, UCODE_GET_VERSION, &info);
1130 rc = info.ugv_errno;
1131
1132 if (tmprc && rc == EM_OK) {
1133 rc = EM_SYS;
1134 }
1135
1136 if (rc == EM_OK) {
1137 (void) printf(gettext("CPU\tMicrocode Version\n"));
1138 for (i = 0; i < cpuid_max; i++) {
1139 if (info.ugv_rev[i] == (uint32_t)-1)
1140 continue;
1141 (void) printf("%d\t0x%x\n", i, info.ugv_rev[i]);
1142 }
1143 } else {
1144 ucode_perror(gettext("get microcode version"), rc);
1145 }
1146
1147 if (revp)
1148 free(revp);
1149 }
1150
1151 if (action & UCODE_OPT_UPDATE) {
1152 int tmprc;
1153 struct ucode_write_struct uw_struct;
1154
1155 uw_struct.uw_size = ucode_size;
1156 uw_struct.uw_ucode = buf;
1157 uw_struct.uw_errno = EM_OK;
1158 tmprc = ioctl(dev_fd, UCODE_UPDATE, &uw_struct);
1159 rc = uw_struct.uw_errno;
1160
1161 if (rc == EM_OK) {
1162 if (tmprc) {
1163 rc = EM_SYS;
1164 ucode_perror(ucode_dev, rc);
1165 }
1166 } else if (rc == EM_NOMATCH || rc == EM_HIGHERREV) {
1167 ucode_perror(filename, rc);
1168 } else {
1169 ucode_perror(gettext("microcode update"), rc);
1170 }
1171 }
1172
1173 out:
1174 if (dev_fd != -1)
1175 (void) close(dev_fd);
1176
1177 if (fd != -1)
1178 (void) close(fd);
1179
1180 free(buf);
1181 free(path);
1182
1183 if (rc != EM_OK)
1184 return (3);
1185
1186 return (0);
1187 }
1188