#include <sys/bitext.h>
#include <sys/types.h>
#include <sys/processor.h>
#include <sys/sysmacros.h>
#include <sys/ucode.h>
#include <sys/ucode_intel.h>
#include <sys/ucode_amd.h>
#include <sys/utsname.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <syslog.h>
#include <time.h>
#include <ctype.h>
#include <assert.h>
#include <libgen.h>
#include <limits.h>
#include <locale.h>
#include <libintl.h>
#include <ucode/ucode_errno.h>
#include <ucode/ucode_utils_intel.h>
#include <ucode/ucode_utils_amd.h>
#define UCODE_OPT_INSTALL 0x0001
#define UCODE_OPT_UPDATE 0x0002
#define UCODE_OPT_VERSION 0x0004
#define UCODE_OPT_LIST 0x0008
static const char ucode_dev[] = "/dev/" UCODE_DRIVER_NAME;
static char *cmdname;
#define UCODE_INSTALL_COMMON_PATH ".f"
#define UCODE_MAX_PATH_LEN (PATH_MAX - \
MAX(UCODE_MAX_NAME_LEN_INTEL, UCODE_MAX_NAME_LEN_AMD) - 1)
static bool ucode_debug = false;
static ucode_errno_t ucode_convert_amd(const char *, uint8_t **, size_t *);
static ucode_errno_t ucode_convert_intel(const char *, uint8_t **, size_t *);
static ucode_errno_t ucode_gen_files_amd(uint8_t *, size_t, const char *);
static ucode_errno_t ucode_gen_files_intel(uint8_t *, size_t, const char *);
static void ucode_list_amd(uint8_t *, size_t);
static void ucode_list_intel(uint8_t *, size_t);
typedef struct ucode_source {
const char *us_prefix;
const char *us_vendor;
ucode_errno_t (*us_convert)(const char *, uint8_t **, size_t *);
ucode_errno_t (*us_gen_files)(uint8_t *, size_t, const char *);
ucode_errno_t (*us_validate)(uint8_t *, size_t);
void (*us_list)(uint8_t *, size_t);
} ucode_source_t;
static const ucode_source_t ucode_sources[] = {
{
.us_prefix = "intel",
.us_vendor = "GenuineIntel",
.us_convert = ucode_convert_intel,
.us_gen_files = ucode_gen_files_intel,
.us_validate = ucode_validate_intel,
.us_list = ucode_list_intel,
},
{
.us_prefix = "amd",
.us_vendor = "AuthenticAMD",
.us_convert = ucode_convert_amd,
.us_gen_files = ucode_gen_files_amd,
.us_validate = ucode_validate_amd,
.us_list = ucode_list_amd,
}
};
const ucode_source_t *ucode;
static void
dbgprintf(const char *format, ...)
{
if (ucode_debug) {
va_list alist;
va_start(alist, format);
(void) vfprintf(stderr, format, alist);
va_end(alist);
}
}
static void
usage(bool verbose)
{
(void) fprintf(stderr, gettext("usage:\n"));
(void) fprintf(stderr, "\t%s -v\n", cmdname);
if (verbose) {
(void) fprintf(stderr,
gettext("\t\t Shows running microcode version.\n\n"));
}
(void) fprintf(stderr, "\t%s -u [-t type] microcode-file\n", cmdname);
if (verbose) {
(void) fprintf(stderr, gettext("\t\t Updates microcode to the "
"latest matching version found in\n"
"\t\t microcode-file.\n\n"));
}
(void) fprintf(stderr, "\t%s -l [-t type] microcode-file\n", cmdname);
if (verbose) {
(void) fprintf(stderr, gettext("\t\t Displays details of the "
"microcode file's contents.\n\n"));
}
(void) fprintf(stderr,
"\t%s -i [-t type] [-R path] microcode-file\n", cmdname);
if (verbose) {
(void) fprintf(stderr, gettext("\t\t Installs microcode to be "
"used for subsequent boots.\n"));
}
(void) fprintf(stderr, gettext(
"\nThe type of the microcode file must either be specified with "
"the -t option\nor microcode-file must start with the vendor name "
"prefix, either \"intel\"\nor \"amd\", so that the type can be "
"inferred from it.\n\n"));
}
static void
ucode_perror(const char *str, ucode_errno_t rc)
{
(void) fprintf(stderr, "%s: %s: %s\n", cmdname, str,
errno == 0 ? ucode_strerror(rc) : strerror(errno));
errno = 0;
}
static int
bcd_to_int(uint8_t b)
{
int high = (b >> 4) & 0xf;
int low = b & 0xf;
if (high > 9 || low > 9)
return (-1);
return (high * 10 + low);
}
static void
ucode_fms(uint32_t sig, uint8_t *family, uint8_t *model, uint8_t *stepping)
{
const uint8_t xfamily = bitx32(sig, 27, 20);
const uint8_t bfamily = bitx32(sig, 11, 8);
const uint8_t xmodel = bitx32(sig, 19, 16);
const uint8_t bmodel = bitx32(sig, 7, 4);
*family = bfamily == 0xf ? bfamily + xfamily : bfamily;
*model = bfamily == 0x6 || bfamily == 0xf ?
(xmodel << 4) | bmodel : bmodel;
*stepping = bitx32(sig, 3, 0);
}
static uint32_t
amd_equivcpu_to_sig(uint16_t equiv)
{
uint16_t xfamily, model, stepping;
uint32_t sig = 0;
xfamily = bitx16(equiv, 15, 12);
model = bitx16(equiv, 11, 4);
stepping = bitx16(equiv, 3, 0);
sig = bitset32(sig, 27, 20, xfamily);
sig = bitset32(sig, 11, 8, 0xf);
sig = bitset32(sig, 19, 16, bitx16(model, 7, 4));
sig = bitset32(sig, 7, 4, bitx16(model, 3, 0));
sig = bitset32(sig, 3, 0, stepping);
return (sig);
}
static ucode_errno_t
ucode_convert_amd(const char *infile, uint8_t **bufp, size_t *sizep)
{
ucode_header_amd_t *patch;
ucode_section_amd_t *section;
ucode_eqtbl_amd_t *eq;
int month, day, yearl, fd;
size_t csize;
ssize_t rsize;
uint8_t *buf = *bufp;
size_t size = *sizep;
if (infile == NULL || buf == NULL || size == 0)
return (EM_INVALIDARG);
if ((fd = open(infile, O_RDONLY)) < 0)
return (EM_SYS);
rsize = read(fd, buf, size);
if (rsize < 0) {
int _errno = errno;
(void) close(fd);
errno = _errno;
return (EM_SYS);
}
(void) close(fd);
if (rsize == 0)
return (EM_FILEFORMAT);
size = rsize;
if (ucode->us_validate(buf, size) == EM_OK) {
*sizep = size;
return (EM_OK);
}
patch = (ucode_header_amd_t *)*bufp;
month = bcd_to_int(bitx32(patch->uh_date, 31, 24));
day = bcd_to_int(bitx32(patch->uh_date, 23, 16));
yearl = bcd_to_int(bitx32(patch->uh_date, 7, 0));
if (day < 1 || day > 31 || month < 1 || month > 12 ||
yearl < 0 || yearl > 99) {
dbgprintf("implausible date code: 0x%x\n", patch->uh_date);
return (EM_FILEFORMAT);
}
dbgprintf("creating microcode container\n");
csize =
sizeof (uint32_t) +
2 * sizeof (ucode_section_amd_t) +
2 * sizeof (ucode_eqtbl_amd_t);
if (size > SIZE_MAX - csize) {
dbgprintf("container size too large (patch size %zu)\n", size);
return (EM_FILEFORMAT);
}
csize += size;
buf = realloc(*bufp, csize);
if (buf == NULL)
return (EM_SYS);
patch = (ucode_header_amd_t *)(buf + csize - size);
bcopy(buf, patch, size);
*(uint32_t *)buf = UCODE_AMD_CONTAINER_MAGIC;
section = (ucode_section_amd_t *)(buf + sizeof (uint32_t));
eq = (ucode_eqtbl_amd_t *)section->usa_data;
section->usa_type = UCODE_AMD_CONTAINER_TYPE_EQUIV;
section->usa_size = 2 * sizeof (*eq);
eq->ue_equiv_cpu = patch->uh_cpu_rev;
eq->ue_inst_cpu = amd_equivcpu_to_sig(patch->uh_cpu_rev);
bzero(eq + 1, sizeof (*eq));
section =
(ucode_section_amd_t *)(section->usa_data + section->usa_size);
section->usa_type = UCODE_AMD_CONTAINER_TYPE_PATCH;
section->usa_size = size;
*bufp = buf;
*sizep = csize;
return (EM_OK);
}
#define LINESIZE 120
static ucode_errno_t
ucode_convert_intel(const char *infile, uint8_t **bufp, size_t *sizep)
{
char linebuf[LINESIZE];
FILE *infd = NULL;
bool firstline = true;
size_t count = 0;
uint8_t *buf = *bufp;
size_t size = *sizep;
uint32_t *intbuf = (uint32_t *)(uintptr_t)buf;
if (infile == NULL || buf == NULL || size == 0)
return (EM_INVALIDARG);
if ((infd = fopen(infile, "r")) == NULL)
return (EM_SYS);
while (fgets(linebuf, LINESIZE, infd)) {
if (firstline && !isprint(linebuf[0])) {
if (fseek(infd, 0, SEEK_SET) == 0)
count = fread(buf, 1, size, infd);
(void) fclose(infd);
if (count == 0)
return (EM_FILEFORMAT);
*sizep = count;
return (EM_OK);
}
firstline = false;
if (strlen(linebuf) == 1)
continue;
if (strcspn(linebuf, " \t") == 0)
continue;
if (linebuf[0] == '/')
continue;
if (sscanf(linebuf, "%x, %x, %x, %x",
&intbuf[count], &intbuf[count+1],
&intbuf[count+2], &intbuf[count+3]) != 4)
break;
count += 4;
}
(void) fclose(infd);
*sizep = count * sizeof (int);
return (EM_OK);
}
static int
ucode_should_update_intel(char *filename, uint32_t new_rev)
{
int fd;
struct stat statbuf;
ucode_header_intel_t header;
if (stat(filename, &statbuf) == 0) {
if ((fd = open(filename, O_RDONLY)) == -1)
return (-1);
if (read(fd, &header, sizeof (header)) == -1) {
(void) close(fd);
return (-1);
}
(void) close(fd);
if (header.uh_rev >= new_rev)
return (0);
}
return (-1);
}
static ucode_errno_t
ucode_gen_files_amd(uint8_t *buf, size_t size, const char *path)
{
char common_path[PATH_MAX];
int fd;
uint16_t last_cpu_rev = 0;
uint32_t counter = 0;
int n;
n = snprintf(common_path, sizeof (common_path), "%s/container", path);
if (n >= sizeof (common_path)) {
dbgprintf("failed to construct container path\n");
return (EM_FILEFORMAT);
}
dbgprintf("path = %s\n", common_path);
fd = open(common_path, O_WRONLY | O_CREAT | O_TRUNC,
S_IRUSR | S_IRGRP | S_IROTH);
if (fd == -1) {
ucode_perror(common_path, EM_SYS);
return (EM_SYS);
}
if (write(fd, buf, size) != size) {
(void) close(fd);
ucode_perror(common_path, EM_SYS);
return (EM_SYS);
}
(void) close(fd);
buf += sizeof (uint32_t);
size -= sizeof (uint32_t);
while (size > sizeof (ucode_section_amd_t)) {
ucode_section_amd_t *section = (ucode_section_amd_t *)buf;
int n;
switch (section->usa_type) {
case UCODE_AMD_CONTAINER_TYPE_EQUIV:
n = snprintf(common_path, sizeof (common_path), "%s/%s",
path, UCODE_AMD_EQUIVALENCE_TABLE_NAME);
break;
case UCODE_AMD_CONTAINER_TYPE_PATCH: {
ucode_header_amd_t *uh =
(ucode_header_amd_t *)section->usa_data;
if (uh->uh_cpu_rev != last_cpu_rev) {
last_cpu_rev = uh->uh_cpu_rev;
counter = 0;
}
n = snprintf(common_path, sizeof (common_path),
"%s/%04X-%02X", path, uh->uh_cpu_rev, counter++);
break;
}
default:
return (EM_FILEFORMAT);
}
if (n >= sizeof (common_path)) {
dbgprintf("failed to construct component path\n");
return (EM_FILEFORMAT);
}
dbgprintf("path = %s\n", common_path);
fd = open(common_path, O_WRONLY | O_CREAT | O_TRUNC,
S_IRUSR | S_IRGRP | S_IROTH);
if (fd == -1) {
ucode_perror(common_path, EM_SYS);
return (EM_SYS);
}
if (write(fd, section->usa_data, section->usa_size) !=
section->usa_size) {
(void) close(fd);
ucode_perror(common_path, EM_SYS);
return (EM_SYS);
}
(void) close(fd);
size -= section->usa_size + sizeof (ucode_section_amd_t);
buf += section->usa_size + sizeof (ucode_section_amd_t);
}
return (EM_OK);
}
static ucode_errno_t
ucode_gen_files_intel(uint8_t *buf, size_t size, const char *path)
{
size_t remaining;
char common_path[PATH_MAX];
DIR *dirp;
struct dirent *dp;
int n;
n = snprintf(common_path, sizeof (common_path), "%s/%s", path,
UCODE_INSTALL_COMMON_PATH);
if (n >= sizeof (common_path)) {
dbgprintf("failed to construct path for %s\n",
UCODE_INSTALL_COMMON_PATH);
return (EM_FILEFORMAT);
}
if (mkdirp(common_path, 0755) == -1 && errno != EEXIST) {
ucode_perror(common_path, EM_SYS);
return (EM_SYS);
}
for (remaining = size; remaining > 0; ) {
uint32_t total_size, body_size, offset;
char firstname[PATH_MAX];
char name[PATH_MAX];
int i;
uint8_t *curbuf = &buf[size - remaining];
ucode_header_intel_t *uhp;
ucode_ext_table_intel_t *extp;
uhp = (ucode_header_intel_t *)(uintptr_t)curbuf;
total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size);
remaining -= total_size;
n = snprintf(firstname, sizeof (common_path), "%s/%08X-%02X",
common_path, uhp->uh_signature, uhp->uh_proc_flags);
if (n >= sizeof (common_path)) {
dbgprintf("failed to construct component path\n");
return (EM_FILEFORMAT);
}
dbgprintf("firstname = %s\n", firstname);
if (ucode_should_update_intel(firstname, uhp->uh_rev) != 0) {
int fd;
(void) unlink(firstname);
if ((fd = open(firstname, O_WRONLY | O_CREAT | O_TRUNC,
S_IRUSR | S_IRGRP | S_IROTH)) == -1) {
ucode_perror(firstname, EM_SYS);
return (EM_SYS);
}
if (write(fd, curbuf, total_size) != total_size) {
(void) close(fd);
ucode_perror(firstname, EM_SYS);
return (EM_SYS);
}
(void) close(fd);
}
for (i = 0; i < 8; i++) {
uint32_t platid = uhp->uh_proc_flags & (1 << i);
if (platid == 0 && uhp->uh_proc_flags != 0)
continue;
n = snprintf(name, sizeof (common_path),
"%s/%08X-%02X", path, uhp->uh_signature, platid);
if (n >= sizeof (common_path)) {
dbgprintf("failed to construct platid path\n");
return (EM_FILEFORMAT);
}
dbgprintf("proc_flags = %x, platid = %x, name = %s\n",
uhp->uh_proc_flags, platid, name);
if (ucode_should_update_intel(name,
uhp->uh_rev) != 0) {
(void) unlink(name);
if (link(firstname, name) == -1) {
ucode_perror(name, EM_SYS);
return (EM_SYS);
}
}
if (uhp->uh_proc_flags == 0)
break;
}
offset = UCODE_HEADER_SIZE_INTEL + body_size;
if (total_size == offset)
continue;
extp = (ucode_ext_table_intel_t *)&curbuf[offset];
for (i = 0; i < extp->uet_count; i++) {
ucode_ext_sig_intel_t *uesp = &extp->uet_ext_sig[i];
int j;
for (j = 0; j < 8; j++) {
uint32_t id = uesp->ues_proc_flags & (1 << j);
if (id == 0 && uesp->ues_proc_flags)
continue;
n = snprintf(name, sizeof (common_path),
"%s/%08X-%02X", path,
uesp->ues_signature, id);
if (n >= sizeof (common_path)) {
dbgprintf(
"failed to construct ext path\n");
return (EM_FILEFORMAT);
}
dbgprintf("extsig: proc_flags = %x, "
"platid = %x, name = %s\n",
uesp->ues_proc_flags, id, name);
if (ucode_should_update_intel(name,
uhp->uh_rev) != 0) {
(void) unlink(name);
if (link(firstname, name) == -1) {
ucode_perror(name, EM_SYS);
return (EM_SYS);
}
}
if (uesp->ues_proc_flags == 0)
break;
}
}
}
if ((dirp = opendir(common_path)) == NULL) {
ucode_perror(common_path, EM_SYS);
return (EM_SYS);
}
while ((dp = readdir(dirp)) != NULL) {
char filename[PATH_MAX];
struct stat statbuf;
n = snprintf(filename, sizeof (common_path),
"%s/%s", common_path, dp->d_name);
if (n >= sizeof (common_path) || stat(filename, &statbuf) == -1)
continue;
if ((statbuf.st_mode & S_IFMT) == S_IFREG) {
if (statbuf.st_nlink == 1)
(void) unlink(filename);
}
}
(void) closedir(dirp);
return (EM_OK);
}
static void
ucode_list_intel(uint8_t *buf, size_t size)
{
size_t remaining;
printf("Microcode patches:\n");
for (remaining = size; remaining > 0; ) {
uint8_t *curbuf = &buf[size - remaining];
uint8_t family, model, stepping;
uint32_t total_size, body_size, offset;
ucode_header_intel_t *uhp;
ucode_ext_table_intel_t *extp;
uhp = (ucode_header_intel_t *)(uintptr_t)curbuf;
total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size);
remaining -= total_size;
ucode_fms(uhp->uh_signature, &family, &model, &stepping);
printf(
" %08lX-%02lX -> Family=%02x Model=%02x Stepping=%02x\n",
uhp->uh_signature, uhp->uh_proc_flags,
family, model, stepping);
printf(
" %14s Date=%08lX Bytes=%lu\n", "",
uhp->uh_date, uhp->uh_body_size);
offset = UCODE_HEADER_SIZE_INTEL + body_size;
if (total_size == offset)
continue;
printf("Extended Signature Table:\n");
extp = (ucode_ext_table_intel_t *)&curbuf[offset];
for (uint32_t i = 0; i < extp->uet_count; i++) {
ucode_ext_sig_intel_t *uesp = &extp->uet_ext_sig[i];
ucode_fms(uesp->ues_signature,
&family, &model, &stepping);
printf(
" %08lX-%02lX -> Family=%02x Model=%02x "
"Stepping=%02x\n",
uesp->ues_signature, uesp->ues_proc_flags,
family, model, stepping);
}
}
}
static void
ucode_list_amd(uint8_t *buf, size_t size)
{
uint32_t last_type = UINT32_MAX;
buf += sizeof (uint32_t);
size -= sizeof (uint32_t);
while (size > sizeof (ucode_section_amd_t)) {
ucode_section_amd_t *section = (ucode_section_amd_t *)buf;
switch (section->usa_type) {
case UCODE_AMD_CONTAINER_TYPE_EQUIV: {
ucode_eqtbl_amd_t *eq =
(ucode_eqtbl_amd_t *)section->usa_data;
if (last_type != section->usa_type) {
printf("Equivalence table:\n");
last_type = section->usa_type;
}
for (uint_t i = 0; eq->ue_inst_cpu != 0 &&
i < section->usa_size / sizeof (*eq); eq++, i++) {
uint8_t family, model, stepping;
ucode_fms(eq->ue_inst_cpu, &family, &model,
&stepping);
printf(" %08lX Family=%02x Model=%02x "
"Stepping=%02x -> %04X\n",
eq->ue_inst_cpu, family, model,
stepping, eq->ue_equiv_cpu);
}
break;
}
case UCODE_AMD_CONTAINER_TYPE_PATCH: {
ucode_header_amd_t *uh =
(ucode_header_amd_t *)section->usa_data;
if (uh->uh_cpu_rev == 0)
break;
if (last_type != section->usa_type) {
printf("Microcode patches:\n");
last_type = section->usa_type;
}
printf(" %4X -> Patch=%08lX Date=%08lX Bytes=%lu\n",
uh->uh_cpu_rev, uh->uh_patch_id, uh->uh_date,
section->usa_size);
break;
}
default:
break;
}
size -= section->usa_size + sizeof (ucode_section_amd_t);
buf += section->usa_size + sizeof (ucode_section_amd_t);
}
}
int
main(int argc, char *argv[])
{
int c;
int action = 0;
int actcount = 0;
int typeindex = -1;
char *path = NULL;
char *filename = NULL;
int errflg = 0;
int dev_fd = -1;
int fd = -1;
bool verbose = false;
bool needfile = false;
uint8_t *buf = NULL;
ucode_errno_t rc = EM_OK;
processorid_t cpuid_max;
struct stat filestat;
size_t ucode_size = 0;
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
cmdname = basename(argv[0]);
while ((c = getopt(argc, argv, "idhluvVR:t:")) != EOF) {
switch (c) {
case 'd':
ucode_debug = true;
break;
case 'i':
action |= UCODE_OPT_INSTALL;
actcount++;
needfile = true;
break;
case 'l':
action |= UCODE_OPT_LIST;
actcount++;
needfile = true;
break;
case 't':
if (typeindex != -1) {
(void) fprintf(stderr, gettext(
"-t can only be specified once\n"));
errflg++;
break;
}
for (uint_t i = 0; i < ARRAY_SIZE(ucode_sources); i++) {
if (strcmp(optarg,
ucode_sources[i].us_prefix) == 0) {
typeindex = i;
break;
}
}
if (typeindex == -1) {
(void) fprintf(stderr,
gettext("Unknown microcode type, %s\n"),
optarg);
errflg++;
}
break;
case 'u':
action |= UCODE_OPT_UPDATE;
actcount++;
needfile = true;
break;
case 'v':
action |= UCODE_OPT_VERSION;
actcount++;
break;
case 'R':
if (optarg[0] == '-') {
errflg++;
} else if (strlen(optarg) > UCODE_MAX_PATH_LEN) {
(void) fprintf(stderr,
gettext("Alternate path too long\n"));
errflg++;
} else if ((path = strdup(optarg)) == NULL) {
errflg++;
}
break;
case 'V':
verbose = true;
break;
case 'h':
usage(true);
return (0);
default:
usage(verbose);
return (2);
}
}
if (actcount == 0) {
(void) fprintf(stderr, gettext("%s: One of -i, -l, -u or -v "
"must be provided.\n"), cmdname);
usage(verbose);
return (2);
}
if (actcount != 1) {
(void) fprintf(stderr, gettext("%s: options -i, -l, -u and -v "
"are mutually exclusive.\n"), cmdname);
usage(verbose);
return (2);
}
if (typeindex != -1 && !needfile) {
(void) fprintf(stderr, gettext("%s: option -t requires one of "
"-i, -l or -u\n"), cmdname);
usage(verbose);
return (2);
}
if (optind <= argc - 1)
filename = argv[optind];
else if (needfile)
errflg++;
if (errflg || action == 0) {
usage(verbose);
return (2);
}
if (needfile) {
if (typeindex != -1) {
ucode = &ucode_sources[typeindex];
} else {
for (uint_t i = 0; i < ARRAY_SIZE(ucode_sources); i++) {
const ucode_source_t *src = &ucode_sources[i];
dbgprintf("i = %d, filestr = %s, "
"filename = %s\n",
i, src->us_prefix, filename);
if (strncasecmp(src->us_prefix,
basename(filename),
strlen(src->us_prefix)) == 0) {
ucode = src;
break;
}
}
}
if (ucode == NULL) {
rc = EM_NOVENDOR;
(void) fprintf(stderr, "%s: %s.\n\n"
"Either specify the type with the -t option, "
"or rename the file such that\nits name begins "
"with a vendor string.\n",
cmdname, ucode_strerror(rc));
goto out;
}
dbgprintf("Selected microcode type %s (%s)\n",
ucode->us_prefix, ucode->us_vendor);
if ((stat(filename, &filestat)) < 0) {
rc = EM_SYS;
ucode_perror(filename, rc);
goto out;
}
if ((filestat.st_mode & S_IFMT) != S_IFREG &&
(filestat.st_mode & S_IFMT) != S_IFLNK) {
rc = EM_FILEFORMAT;
ucode_perror(filename, rc);
goto out;
}
ucode_size = filestat.st_size;
if ((buf = malloc(ucode_size)) == NULL) {
rc = EM_SYS;
ucode_perror(filename, rc);
goto out;
}
rc = ucode->us_convert(filename, &buf, &ucode_size);
if (rc != EM_OK) {
ucode_perror(filename, rc);
goto out;
}
dbgprintf("ucode_size = %zd\n", ucode_size);
if ((rc = ucode->us_validate(buf, ucode_size)) != EM_OK) {
ucode_perror(filename, rc);
goto out;
}
}
if (action & UCODE_OPT_LIST) {
ucode->us_list(buf, ucode_size);
goto out;
}
if (action & UCODE_OPT_INSTALL) {
if (path == NULL) {
struct utsname uts;
if (uname(&uts) == -1) {
perror("Unable to retrieve system uname");
goto out;
}
if ((path = malloc(PATH_MAX)) == NULL) {
rc = EM_SYS;
ucode_perror("malloc", rc);
goto out;
}
(void) snprintf(path, PATH_MAX, "/platform/%s/ucode/%s",
uts.machine, ucode->us_vendor);
}
if (mkdirp(path, 0755) == -1 && errno != EEXIST) {
rc = EM_SYS;
ucode_perror(path, rc);
goto out;
}
rc = ucode->us_gen_files(buf, ucode_size, path);
goto out;
}
if ((dev_fd = open(ucode_dev, O_RDONLY)) == -1) {
rc = EM_SYS;
ucode_perror(ucode_dev, rc);
goto out;
}
if (action & UCODE_OPT_VERSION) {
int tmprc;
uint32_t *revp = NULL;
int i;
struct ucode_get_rev_struct info;
cpuid_max = (processorid_t)sysconf(_SC_CPUID_MAX);
if ((revp = (uint32_t *)
malloc(cpuid_max * sizeof (uint32_t))) == NULL) {
rc = EM_SYS;
ucode_perror("malloc", rc);
goto out;
}
for (i = 0; i < cpuid_max; i++)
revp[i] = (uint32_t)-1;
info.ugv_rev = revp;
info.ugv_size = cpuid_max;
info.ugv_errno = EM_OK;
tmprc = ioctl(dev_fd, UCODE_GET_VERSION, &info);
rc = info.ugv_errno;
if (tmprc && rc == EM_OK) {
rc = EM_SYS;
}
if (rc == EM_OK) {
(void) printf(gettext("CPU\tMicrocode Version\n"));
for (i = 0; i < cpuid_max; i++) {
if (info.ugv_rev[i] == (uint32_t)-1)
continue;
(void) printf("%d\t0x%x\n", i, info.ugv_rev[i]);
}
} else {
ucode_perror(gettext("get microcode version"), rc);
}
if (revp)
free(revp);
}
if (action & UCODE_OPT_UPDATE) {
int tmprc;
struct ucode_write_struct uw_struct;
uw_struct.uw_size = ucode_size;
uw_struct.uw_ucode = buf;
uw_struct.uw_errno = EM_OK;
tmprc = ioctl(dev_fd, UCODE_UPDATE, &uw_struct);
rc = uw_struct.uw_errno;
if (rc == EM_OK) {
if (tmprc) {
rc = EM_SYS;
ucode_perror(ucode_dev, rc);
}
} else if (rc == EM_NOMATCH || rc == EM_HIGHERREV) {
ucode_perror(filename, rc);
} else {
ucode_perror(gettext("microcode update"), rc);
}
}
out:
if (dev_fd != -1)
(void) close(dev_fd);
if (fd != -1)
(void) close(fd);
free(buf);
free(path);
if (rc != EM_OK)
return (3);
return (0);
}