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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <sys/types.h>
27 #include <sys/processor.h>
28 #include <sys/ucode.h>
29 #include <sys/ioctl.h>
48 #define UCODE_OPT_INSTALL 0x0001
49 #define UCODE_OPT_UPDATE 0x0002
50 #define UCODE_OPT_VERSION 0x0004
52 static const char ucode_dev
[] = "/dev/" UCODE_DRIVER_NAME
;
56 static char ucode_vendor_str
[UCODE_MAX_VENDORS_NAME_LEN
];
57 static char ucode_install_path
[] = UCODE_INSTALL_PATH
;
59 static int ucode_debug
= 0;
61 static int ucode_convert_amd(const char *, uint8_t *, size_t);
62 static int ucode_convert_intel(const char *, uint8_t *, size_t);
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 *);
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
},
72 const struct ucode_ops
*ucode
;
75 dprintf(const char *format
, ...)
79 va_start(alist
, format
);
80 (void) vfprintf(stderr
, format
, alist
);
88 (void) fprintf(stderr
, gettext("usage:\n"));
89 (void) fprintf(stderr
, "\t%s -v\n", cmdname
);
91 (void) fprintf(stderr
,
92 gettext("\t\t Shows running microcode version.\n\n"));
95 (void) fprintf(stderr
, "\t%s -u microcode-file\n", cmdname
);
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"));
102 (void) fprintf(stderr
, "\t%s -i [-R path] microcode-file\n", cmdname
);
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"));
112 ucode_perror(const char *str
, ucode_errno_t rc
)
114 (void) fprintf(stderr
, "%s: %s: %s\n", cmdname
, str
,
115 errno
== 0 ? ucode_strerror(rc
) : strerror(errno
));
119 #define LINESIZE 120 /* copyright line sometimes is longer than 80 */
122 * Convert text format microcode release into binary format.
123 * Return the number of characters read.
126 ucode_convert_amd(const char *infile
, uint8_t *buf
, size_t size
)
130 if (infile
== NULL
|| buf
== NULL
|| size
== 0)
133 if ((fd
= open(infile
, O_RDONLY
)) < 0)
136 size
= read(fd
, buf
, size
);
144 ucode_convert_intel(const char *infile
, uint8_t *buf
, size_t size
)
146 char linebuf
[LINESIZE
];
148 int count
= 0, firstline
= 1;
149 uint32_t *intbuf
= (uint32_t *)(intptr_t)buf
;
151 if (infile
== NULL
|| buf
== NULL
|| size
== 0)
154 if ((infd
= fopen(infile
, "r")) == NULL
)
157 while (fgets(linebuf
, LINESIZE
, infd
)) {
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
);
170 /* Skip blank lines */
171 if (strlen(linebuf
) == 1)
174 /* Skip lines with all spaces or tabs */
175 if (strcspn(linebuf
, " \t") == 0)
178 /* Text file. Skip comments. */
179 if (linebuf
[0] == '/')
182 if (sscanf(linebuf
, "%x, %x, %x, %x",
183 &intbuf
[count
], &intbuf
[count
+1],
184 &intbuf
[count
+2], &intbuf
[count
+3]) != 4)
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.
197 return (count
* sizeof (int));
201 * Returns 0 if no need to update the link; -1 otherwise
204 ucode_should_update_intel(char *filename
, uint32_t new_rev
)
208 ucode_header_intel_t header
;
211 * If the file or link already exists, check to see if
212 * it is necessary to update it.
214 if (stat(filename
, &statbuf
) == 0) {
215 if ((fd
= open(filename
, O_RDONLY
)) == -1)
218 if (read(fd
, &header
, sizeof (header
)) == -1) {
225 if (header
.uh_rev
>= new_rev
)
233 * Generate microcode binary files. Must be called after ucode_validate().
236 ucode_gen_files_amd(uint8_t *buf
, int size
, char *path
)
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;
246 /* write container file */
247 (void) snprintf(common_path
, PATH_MAX
, "%s/%s", path
, "container");
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
);
254 ucode_perror(common_path
, EM_SYS
);
258 if (write(fd
, buf
, size
) != size
) {
260 ucode_perror(common_path
, EM_SYS
);
266 /* skip over magic number & equivalence table header */
269 count
= *ptr
++; size
-= 4;
271 /* equivalence table uses special name */
272 (void) snprintf(common_path
, PATH_MAX
, "%s/%s", path
,
273 "equivalence-table");
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
);
281 ucode_perror(common_path
, EM_SYS
);
285 if (write(fd
, ptr
, count
) != count
) {
287 ucode_perror(common_path
, EM_SYS
);
292 ptr
+= count
>> 2; size
-= count
;
298 count
= *ptr
++; size
-= 4;
300 /* construct name from header information */
301 uh
= (ucode_header_amd_t
*)ptr
;
303 if (uh
->uh_cpu_rev
!= last_cpu_rev
) {
304 last_cpu_rev
= uh
->uh_cpu_rev
;
308 (void) snprintf(common_path
, PATH_MAX
, "%s/%04X-%02X", path
,
309 uh
->uh_cpu_rev
, counter
++);
314 ucode_gen_files_intel(uint8_t *buf
, int size
, char *path
)
317 char common_path
[PATH_MAX
];
321 (void) snprintf(common_path
, PATH_MAX
, "%s/%s", path
,
322 UCODE_INSTALL_COMMON_PATH
);
324 if (mkdirp(common_path
, 0755) == -1 && errno
!= EEXIST
) {
325 ucode_perror(common_path
, EM_SYS
);
329 for (remaining
= size
; remaining
> 0; ) {
330 uint32_t total_size
, body_size
, offset
;
331 char firstname
[PATH_MAX
];
334 uint8_t *curbuf
= &buf
[size
- remaining
];
335 ucode_header_intel_t
*uhp
;
336 ucode_ext_table_intel_t
*extp
;
338 uhp
= (ucode_header_intel_t
*)(intptr_t)curbuf
;
340 total_size
= UCODE_TOTAL_SIZE_INTEL(uhp
->uh_total_size
);
341 body_size
= UCODE_BODY_SIZE_INTEL(uhp
->uh_body_size
);
343 remaining
-= total_size
;
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
);
349 if (ucode_should_update_intel(firstname
, uhp
->uh_rev
) != 0) {
352 /* Remove the existing one first */
353 (void) unlink(firstname
);
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
);
361 if (write(fd
, curbuf
, total_size
) != total_size
) {
363 ucode_perror(firstname
, EM_SYS
);
371 * Only 1 byte of the proc_flags field is used, therefore
372 * we only need to match 8 potential platform ids.
374 for (i
= 0; i
< 8; i
++) {
375 uint32_t platid
= uhp
->uh_proc_flags
& (1 << i
);
377 if (platid
== 0 && uhp
->uh_proc_flags
!= 0)
380 (void) snprintf(name
, PATH_MAX
,
381 "%s/%08X-%02X", path
, uhp
->uh_signature
, platid
);
383 dprintf("proc_flags = %x, platid = %x, name = %s\n",
384 uhp
->uh_proc_flags
, platid
, name
);
386 if (ucode_should_update_intel(name
, uhp
->uh_rev
) != 0) {
388 /* Remove the existing one first */
391 if (link(firstname
, name
) == -1) {
392 ucode_perror(name
, EM_SYS
);
397 if (uhp
->uh_proc_flags
== 0)
401 offset
= UCODE_HEADER_SIZE_INTEL
+ body_size
;
403 /* Check to see if there is extended signature table */
404 if (total_size
== offset
)
407 /* There is extended signature table. More processing. */
408 extp
= (ucode_ext_table_intel_t
*)(uintptr_t)&curbuf
[offset
];
410 for (i
= 0; i
< extp
->uet_count
; i
++) {
411 ucode_ext_sig_intel_t
*uesp
= &extp
->uet_ext_sig
[i
];
414 for (j
= 0; j
< 8; j
++) {
415 uint32_t id
= uesp
->ues_proc_flags
& (1 << j
);
417 if (id
== 0 && uesp
->ues_proc_flags
)
420 (void) snprintf(name
, PATH_MAX
,
421 "%s/%08X-%02X", path
, extp
->uet_ext_sig
[i
],
424 if (ucode_should_update_intel(name
, uhp
->uh_rev
)
427 /* Remove the existing one first */
429 if (link(firstname
, name
) == -1) {
430 ucode_perror(name
, EM_SYS
);
435 if (uesp
->ues_proc_flags
== 0)
443 * Remove files with no links to them. These are probably
444 * obsolete microcode files.
446 if ((dirp
= opendir(common_path
)) == NULL
) {
447 ucode_perror(common_path
, EM_SYS
);
451 while ((dp
= readdir(dirp
)) != NULL
) {
452 char filename
[PATH_MAX
];
455 (void) snprintf(filename
, PATH_MAX
,
456 "%s/%s", common_path
, dp
->d_name
);
457 if (stat(filename
, &statbuf
) == -1)
460 if ((statbuf
.st_mode
& S_IFMT
) == S_IFREG
) {
461 if (statbuf
.st_nlink
== 1)
462 (void) unlink(filename
);
466 (void) closedir(dirp
);
472 * Returns 0 on success, 2 on usage error, and 3 on operation error.
475 main(int argc
, char *argv
[])
481 char *filename
= NULL
;
487 ucode_errno_t rc
= EM_OK
;
488 processorid_t cpuid_max
;
489 struct stat filestat
;
492 (void) setlocale(LC_ALL
, "");
494 #if !defined(TEXT_DOMAIN)
495 #define TEXT_DOMAIN "SYS_TEST"
497 (void) textdomain(TEXT_DOMAIN
);
499 cmdname
= basename(argv
[0]);
501 while ((c
= getopt(argc
, argv
, "idhuvVR:")) != EOF
) {
505 action
|= UCODE_OPT_INSTALL
;
510 action
|= UCODE_OPT_UPDATE
;
515 action
|= UCODE_OPT_VERSION
;
524 if (optarg
[0] == '-')
526 else if (strlen(optarg
) > UCODE_MAX_PATH_LEN
) {
527 (void) fprintf(stderr
,
528 gettext("Alternate path too long\n"));
530 } else if ((path
= strdup(optarg
)) == NULL
) {
551 (void) fprintf(stderr
, gettext("%s: options -v, -i and -u "
552 "are mutually exclusive.\n"), cmdname
);
557 if (optind
<= argc
- 1)
558 filename
= argv
[optind
];
559 else if (!(action
& UCODE_OPT_VERSION
))
562 if (errflg
|| action
== 0) {
568 * Convert from text format to binary format
570 if ((action
& UCODE_OPT_INSTALL
) || (action
& UCODE_OPT_UPDATE
)) {
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
,
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
));
588 if (ucode_vendors
[i
].filestr
== NULL
) {
590 ucode_perror(basename(filename
), rc
);
594 if ((stat(filename
, &filestat
)) < 0) {
596 ucode_perror(filename
, rc
);
600 if ((filestat
.st_mode
& S_IFMT
) != S_IFREG
&&
601 (filestat
.st_mode
& S_IFMT
) != S_IFLNK
) {
603 ucode_perror(filename
, rc
);
607 if ((buf
= malloc(filestat
.st_size
)) == NULL
) {
609 ucode_perror(filename
, rc
);
613 ucode_size
= ucode
->convert(filename
, buf
, filestat
.st_size
);
615 dprintf("ucode_size = %d\n", ucode_size
);
617 if (ucode_size
== 0) {
619 ucode_perror(filename
, rc
);
623 if ((rc
= ucode
->validate(buf
, ucode_size
)) != EM_OK
) {
624 ucode_perror(filename
, rc
);
630 * For the install option, the microcode file must start with
631 * "intel" for Intel microcode, and "amd" for AMD microcode.
633 if (action
& UCODE_OPT_INSTALL
) {
635 * If no path is provided by the -R option, put the files in
636 * /ucode_install_path/ucode_vendor_str/.
639 if ((path
= malloc(PATH_MAX
)) == NULL
) {
641 ucode_perror("malloc", rc
);
645 (void) snprintf(path
, PATH_MAX
, "/%s/%s",
646 ucode_install_path
, ucode_vendor_str
);
649 if (mkdirp(path
, 0755) == -1 && errno
!= EEXIST
) {
651 ucode_perror(path
, rc
);
655 rc
= ucode
->gen_files(buf
, ucode_size
, path
);
660 if ((dev_fd
= open(ucode_dev
, O_RDONLY
)) == -1) {
662 ucode_perror(ucode_dev
, rc
);
666 if (action
& UCODE_OPT_VERSION
) {
668 uint32_t *revp
= NULL
;
670 #if defined(_SYSCALL32_IMPL)
671 struct ucode_get_rev_struct32 inf32
;
673 struct ucode_get_rev_struct info
;
676 cpuid_max
= (processorid_t
)sysconf(_SC_CPUID_MAX
);
678 if ((revp
= (uint32_t *)
679 malloc(cpuid_max
* sizeof (uint32_t))) == NULL
) {
681 ucode_perror("malloc", rc
);
685 for (i
= 0; i
< cpuid_max
; i
++)
686 revp
[i
] = (uint32_t)-1;
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
;
696 info
.ugv_size
= cpuid_max
;
697 info
.ugv_errno
= EM_OK
;
698 tmprc
= ioctl(dev_fd
, UCODE_GET_VERSION
, &info
);
702 if (tmprc
&& 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)
711 (void) printf("%d\t0x%x\n", i
, info
.ugv_rev
[i
]);
714 ucode_perror(gettext("get microcode version"), rc
);
720 if (action
& UCODE_OPT_UPDATE
) {
722 #if defined(_SYSCALL32_IMPL)
723 struct ucode_write_struct32 uw_struct32
;
725 struct ucode_write_struct uw_struct
;
728 #if defined(_SYSCALL32_IMPL)
729 uw_struct32
.uw_size
= ucode_size
;
730 uw_struct32
.uw_ucode
= (caddr32_t
)buf
;
731 uw_struct32
.uw_errno
= EM_OK
;
732 tmprc
= ioctl(dev_fd
, UCODE_UPDATE
, &uw_struct32
);
733 rc
= uw_struct32
.uw_errno
;
736 uw_struct
.uw_size
= ucode_size
;
737 uw_struct
.uw_ucode
= buf
;
738 uw_struct
.uw_errno
= EM_OK
;
739 tmprc
= ioctl(dev_fd
, UCODE_UPDATE
, &uw_struct
);
740 rc
= uw_struct
.uw_errno
;
746 ucode_perror(ucode_dev
, rc
);
748 } else if (rc
== EM_NOMATCH
|| rc
== EM_HIGHERREV
) {
749 ucode_perror(filename
, rc
);
751 ucode_perror(gettext("microcode update"), rc
);
757 (void) close(dev_fd
);