d4b93e8846
Intel microcode files can contain an extended signature table after the
main payload, listing additional processor signatures and platform flag
combinations that are covered by the same microcode blob.
ucode-split was only reading the primary header signature when naming
output files and ignored the extended table entirely. As a result,
processors whose signature appeared only in an extended table entry had
no matching split file in ${DATADIR} and therefore no microcode via
cpucontrol(8).
This affected a number of CPU families in the current release, including
Raptor Lake (06-bf-02/05/06/07, Core Gen13/Gen14), Sapphire Rapids
steppings E0-E3 (06-8f-04 through 06-8f-07), Arrow Lake-H (06-c5-02),
Panther Lake (06-cc-02/03), and others.
Extend ucode-split to read the extended signature table and write an
additional output file for each entry.
PR: 295351
Reported by: fastboot <255.255.255.255@web.de>
Reviewed by: markj, sbruno
Sponsored by: The FreeBSD Foundation
Differential Revision: https://reviews.freebsd.org/D57046
284 lines
7.4 KiB
C
284 lines
7.4 KiB
C
/*-
|
|
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
*
|
|
* Copyright (C) 2018 The FreeBSD Foundation.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <err.h>
|
|
#include <fcntl.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
static const size_t bufsize = 65536;
|
|
|
|
/* SDM vol 3 9.11.1 Intel microcode header. */
|
|
struct microcode_update_header {
|
|
uint32_t header_version;
|
|
uint32_t update_revision;
|
|
uint32_t date; /* BCD mmddyyyy */
|
|
uint32_t processor_signature;
|
|
uint32_t checksum; /* Over update data and header */
|
|
uint32_t loader_revision;
|
|
uint32_t processor_flags;
|
|
uint32_t data_size;
|
|
uint32_t total_size;
|
|
uint32_t reserved[3];
|
|
};
|
|
|
|
/* SDM (March 2026) vol 3A 12.11.2 Optional Extended Signature Table. */
|
|
struct ucode_intel_extsig_table {
|
|
uint32_t signature_count;
|
|
uint32_t checksum;
|
|
uint32_t reserved[3];
|
|
};
|
|
|
|
struct ucode_intel_extsig {
|
|
uint32_t processor_signature;
|
|
uint32_t processor_flags;
|
|
uint32_t checksum;
|
|
};
|
|
|
|
/*
|
|
* SDM vol 2A CPUID EAX = 01h Returns Model, Family, Stepping Information.
|
|
* Caller must free the returned string.
|
|
*/
|
|
|
|
static char *
|
|
format_signature(uint32_t signature)
|
|
{
|
|
char *buf;
|
|
unsigned family, model, stepping;
|
|
|
|
family = (signature & 0xf00) >> 8;
|
|
model = (signature & 0xf0) >> 4;
|
|
stepping = signature & 0xf;
|
|
if (family == 0x06 || family == 0x0f)
|
|
model += (signature & 0xf0000) >> 12;
|
|
if (family == 0x0f)
|
|
family += (signature & 0xff00000) >> 20;
|
|
asprintf(&buf, "%02x-%02x-%02x", family, model, stepping);
|
|
if (buf == NULL)
|
|
err(1, "asprintf");
|
|
return (buf);
|
|
}
|
|
|
|
static void
|
|
dump_header(const struct microcode_update_header *hdr)
|
|
{
|
|
char *sig_str;
|
|
int i;
|
|
bool platformid_printed;
|
|
|
|
sig_str = format_signature(hdr->processor_signature);
|
|
printf("header version\t0x%x\n", hdr->header_version);
|
|
printf("revision\t0x%x\n", hdr->update_revision);
|
|
printf("date\t\t0x%x\t%04x-%02x-%02x\n", hdr->date,
|
|
hdr->date & 0xffff, (hdr->date & 0xff000000) >> 24,
|
|
(hdr->date & 0xff0000) >> 16);
|
|
printf("signature\t0x%x\t\t%s\n", hdr->processor_signature, sig_str);
|
|
printf("checksum\t0x%x\n", hdr->checksum);
|
|
printf("loader revision\t0x%x\n", hdr->loader_revision);
|
|
printf("processor flags\t0x%x", hdr->processor_flags);
|
|
platformid_printed = false;
|
|
for (i = 0; i < 8; i++) {
|
|
if (hdr->processor_flags & 1 << i) {
|
|
printf("%s%d", platformid_printed ? ", " : "\t\t", i);
|
|
platformid_printed = true;
|
|
}
|
|
}
|
|
printf("\n");
|
|
printf("datasize\t0x%x\t\t0x%x\n", hdr->data_size,
|
|
hdr->data_size != 0 ? hdr->data_size : 2000);
|
|
printf("size\t\t0x%x\t\t0x%x\n", hdr->total_size,
|
|
hdr->total_size != 0 ? hdr->total_size : 2048);
|
|
free(sig_str);
|
|
}
|
|
|
|
static void
|
|
copy_entry(int ifd, off_t entry_start, uint32_t total_size,
|
|
const char *output_file, char *buf, bool vflag)
|
|
{
|
|
off_t off;
|
|
size_t len, resid;
|
|
ssize_t rv;
|
|
int ofd;
|
|
|
|
ofd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
|
if (ofd < 0)
|
|
err(1, "open");
|
|
|
|
resid = total_size;
|
|
off = entry_start;
|
|
while (resid > 0) {
|
|
len = resid < bufsize ? resid : bufsize;
|
|
rv = pread(ifd, buf, len, off);
|
|
if (rv < 0)
|
|
err(1, "pread");
|
|
else if (rv < (ssize_t)len)
|
|
errx(1, "truncated microcode data");
|
|
if (write(ofd, buf, len) < (ssize_t)len)
|
|
err(1, "write");
|
|
resid -= len;
|
|
off += len;
|
|
}
|
|
if (vflag)
|
|
printf("written to %s\n", output_file);
|
|
close(ofd);
|
|
}
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
|
|
printf("ucode-split [-v] microcode_file\n");
|
|
exit(1);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
struct microcode_update_header hdr;
|
|
struct ucode_intel_extsig_table etbl;
|
|
struct ucode_intel_extsig extsig;
|
|
char *buf, *output_file, *sig_str;
|
|
off_t entry_start;
|
|
uint32_t data_size, total_size, i;
|
|
ssize_t rv;
|
|
int c, ifd;
|
|
bool vflag;
|
|
|
|
vflag = false;
|
|
while ((c = getopt(argc, argv, "v")) != -1) {
|
|
switch (c) {
|
|
case 'v':
|
|
vflag = true;
|
|
break;
|
|
default:
|
|
usage();
|
|
}
|
|
}
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (argc != 1)
|
|
usage();
|
|
|
|
ifd = open(argv[0], O_RDONLY);
|
|
if (ifd < 0)
|
|
err(1, "open");
|
|
|
|
buf = malloc(bufsize);
|
|
if (buf == NULL)
|
|
err(1, "malloc");
|
|
|
|
for (;;) {
|
|
entry_start = lseek(ifd, 0, SEEK_CUR);
|
|
if (entry_start < 0)
|
|
err(1, "lseek");
|
|
|
|
/* Read header. */
|
|
rv = read(ifd, &hdr, sizeof(hdr));
|
|
if (rv < 0) {
|
|
err(1, "read");
|
|
} else if (rv == 0) {
|
|
break;
|
|
} else if (rv < (ssize_t)sizeof(hdr)) {
|
|
errx(1, "invalid microcode header");
|
|
}
|
|
if (hdr.header_version != 1)
|
|
errx(1, "invalid header version");
|
|
|
|
if (vflag)
|
|
dump_header(&hdr);
|
|
|
|
data_size = hdr.data_size != 0 ? hdr.data_size : 2000;
|
|
total_size = hdr.total_size != 0 ? hdr.total_size : 2048;
|
|
|
|
/* Arbitrarily chosen maximum size. */
|
|
if (total_size - sizeof(hdr) > 1 << 24)
|
|
errx(1, "header total_size too large");
|
|
|
|
/* Write primary output file. */
|
|
sig_str = format_signature(hdr.processor_signature);
|
|
asprintf(&output_file, "%s.%02x", sig_str,
|
|
hdr.processor_flags & 0xff);
|
|
free(sig_str);
|
|
if (output_file == NULL)
|
|
err(1, "asprintf");
|
|
copy_entry(ifd, entry_start, total_size, output_file, buf, vflag);
|
|
free(output_file);
|
|
|
|
/*
|
|
* Process the extended signature table, if present. Each
|
|
* entry names an additional processor signature and platform
|
|
* flags combination covered by this microcode blob, so write
|
|
* a copy of the blob for each.
|
|
*/
|
|
if (total_size > data_size + sizeof(hdr)) {
|
|
if (lseek(ifd, entry_start + sizeof(hdr) + data_size,
|
|
SEEK_SET) < 0)
|
|
err(1, "lseek");
|
|
rv = read(ifd, &etbl, sizeof(etbl));
|
|
if (rv < 0)
|
|
err(1, "read");
|
|
else if (rv < (ssize_t)sizeof(etbl))
|
|
errx(1, "truncated extended signature table");
|
|
|
|
for (i = 0; i < etbl.signature_count; i++) {
|
|
rv = read(ifd, &extsig, sizeof(extsig));
|
|
if (rv < 0)
|
|
err(1, "read");
|
|
else if (rv < (ssize_t)sizeof(extsig))
|
|
errx(1, "truncated extended signature "
|
|
"entry %u", i);
|
|
|
|
sig_str = format_signature(
|
|
extsig.processor_signature);
|
|
asprintf(&output_file, "%s.%02x", sig_str,
|
|
extsig.processor_flags & 0xff);
|
|
free(sig_str);
|
|
if (output_file == NULL)
|
|
err(1, "asprintf");
|
|
|
|
copy_entry(ifd, entry_start, total_size,
|
|
output_file, buf, vflag);
|
|
free(output_file);
|
|
}
|
|
}
|
|
|
|
if (vflag)
|
|
printf("\n");
|
|
|
|
/* Advance to the next entry. */
|
|
if (lseek(ifd, entry_start + total_size, SEEK_SET) < 0)
|
|
err(1, "lseek");
|
|
}
|
|
free(buf);
|
|
close(ifd);
|
|
}
|