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