cpucontrol(8): De-duplicate common update logic

Every µcode-updater must open the cpucontrol devfs node RDWR, open a
firmware file, validate the FW file has a positive length, mmap it, etc.
De-duplicate that identical logic between every individual platform.

Also, constify references to the readonly-mapped firmware files while here.

Sponsored by:	Dell EMC Isilon
This commit is contained in:
Conrad Meyer 2018-11-14 00:21:49 +00:00
parent 91cf497515
commit dee401e833
6 changed files with 183 additions and 228 deletions

View File

@ -73,16 +73,16 @@ amd_probe(int fd)
}
void
amd_update(const char *dev, const char *path)
amd_update(const struct ucode_update_params *params)
{
int fd, devfd;
int devfd;
unsigned int i;
struct stat st;
uint32_t *fw_image;
amd_fw_header_t *fw_header;
const char *dev, *path;
const uint32_t *fw_image;
const amd_fw_header_t *fw_header;
uint32_t sum;
uint32_t signature;
uint32_t *fw_data;
const uint32_t *fw_data;
size_t fw_size;
cpuctl_cpuid_args_t idargs = {
.level = 1, /* Request signature. */
@ -90,16 +90,14 @@ amd_update(const char *dev, const char *path)
cpuctl_update_args_t args;
int error;
dev = params->dev_path;
path = params->fw_path;
devfd = params->devfd;
fw_image = params->fwimage;
assert(path);
assert(dev);
fd = -1;
fw_image = MAP_FAILED;
devfd = open(dev, O_RDWR);
if (devfd < 0) {
WARN(0, "could not open %s for writing", dev);
return;
}
error = ioctl(devfd, CPUCTL_CPUID, &idargs);
if (error < 0) {
WARN(0, "ioctl()");
@ -115,37 +113,18 @@ amd_update(const char *dev, const char *path)
/*
* Open the firmware file.
*/
fd = open(path, O_RDONLY, 0);
if (fd < 0) {
WARN(0, "open(%s)", path);
goto fail;
}
error = fstat(fd, &st);
if (error != 0) {
WARN(0, "fstat(%s)", path);
goto fail;
}
if (st.st_size < 0 || (unsigned)st.st_size < sizeof(*fw_header)) {
if (params->fwsize < sizeof(*fw_header)) {
WARNX(2, "file too short: %s", path);
goto fail;
}
/*
* mmap the whole image.
*/
fw_image = (uint32_t *)mmap(NULL, st.st_size, PROT_READ,
MAP_PRIVATE, fd, 0);
if (fw_image == MAP_FAILED) {
WARN(0, "mmap(%s)", path);
goto fail;
}
fw_header = (amd_fw_header_t *)fw_image;
fw_header = (const amd_fw_header_t *)fw_image;
if ((fw_header->magic >> 8) != AMD_MAGIC) {
WARNX(2, "%s is not a valid amd firmware: version mismatch",
path);
goto fail;
}
fw_data = (uint32_t *)(fw_header + 1);
fw_size = (st.st_size - sizeof(*fw_header)) / sizeof(uint32_t);
fw_data = (const uint32_t *)(fw_header + 1);
fw_size = (params->fwsize - sizeof(*fw_header)) / sizeof(uint32_t);
/*
* Check the primary checksum.
@ -160,8 +139,8 @@ amd_update(const char *dev, const char *path)
if (signature == fw_header->signature) {
fprintf(stderr, "%s: updating cpu %s... ", path, dev);
args.data = fw_image;
args.size = st.st_size;
args.data = __DECONST(void *, fw_image);
args.size = params->fwsize;
error = ioctl(devfd, CPUCTL_UPDATE, &args);
if (error < 0) {
fprintf(stderr, "failed.\n");
@ -172,12 +151,5 @@ amd_update(const char *dev, const char *path)
}
fail:
if (fd >= 0)
close(fd);
if (devfd >= 0)
close(devfd);
if (fw_image != MAP_FAILED)
if(munmap(fw_image, st.st_size) != 0)
warn("munmap(%s)", path);
return;
}

View File

@ -88,9 +88,8 @@ amd10h_probe(int fd)
* source code.
*/
void
amd10h_update(const char *dev, const char *path)
amd10h_update(const struct ucode_update_params *params)
{
struct stat st;
cpuctl_cpuid_args_t idargs;
cpuctl_msr_args_t msrargs;
cpuctl_update_args_t args;
@ -100,27 +99,27 @@ amd10h_update(const char *dev, const char *path)
const section_header_t *section_header;
const container_header_t *container_header;
const uint8_t *fw_data;
uint8_t *fw_image;
const uint8_t *fw_image;
const char *dev, *path;
size_t fw_size;
size_t selected_size;
uint32_t revision;
uint32_t new_rev;
uint32_t signature;
uint16_t equiv_id;
int fd, devfd;
int devfd;
unsigned int i;
int error;
dev = params->dev_path;
path = params->fw_path;
devfd = params->devfd;
fw_image = params->fwimage;
fw_size = params->fwsize;
assert(path);
assert(dev);
fd = -1;
fw_image = MAP_FAILED;
devfd = open(dev, O_RDWR);
if (devfd < 0) {
WARN(0, "could not open %s for writing", dev);
return;
}
idargs.level = 1;
error = ioctl(devfd, CPUCTL_CPUID, &idargs);
if (error < 0) {
@ -149,33 +148,15 @@ amd10h_update(const char *dev, const char *path)
* Open the firmware file.
*/
WARNX(1, "checking %s for update.", path);
fd = open(path, O_RDONLY, 0);
if (fd < 0) {
WARN(0, "open(%s)", path);
goto done;
}
error = fstat(fd, &st);
if (error != 0) {
WARN(0, "fstat(%s)", path);
goto done;
}
if (st.st_size < 0 || (size_t)st.st_size <
if (fw_size <
(sizeof(*container_header) + sizeof(*section_header))) {
WARNX(2, "file too short: %s", path);
goto done;
}
fw_size = st.st_size;
/*
* mmap the whole image.
*/
fw_image = (uint8_t *)mmap(NULL, st.st_size, PROT_READ,
MAP_PRIVATE, fd, 0);
if (fw_image == MAP_FAILED) {
WARN(0, "mmap(%s)", path);
goto done;
}
fw_data = fw_image;
container_header = (const container_header_t *)fw_data;
if (container_header->magic != AMD_10H_MAGIC) {
@ -306,12 +287,5 @@ amd10h_update(const char *dev, const char *path)
WARNX(0, "revision after update %#x", new_rev);
done:
if (fd >= 0)
close(fd);
if (devfd >= 0)
close(devfd);
if (fw_image != MAP_FAILED)
if (munmap(fw_image, st.st_size) != 0)
warn("munmap(%s)", path);
return;
}

View File

@ -34,18 +34,20 @@
__FBSDID("$FreeBSD$");
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <err.h>
#include <sysexits.h>
#include <dirent.h>
#include <sys/queue.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/cpuctl.h>
@ -74,16 +76,6 @@ int verbosity_level = 0;
#define HIGH(val) (uint32_t)(((val) >> 32) & 0xffffffff)
#define LOW(val) (uint32_t)((val) & 0xffffffff)
/*
* Macros for freeing SLISTs, probably must be in /sys/queue.h
*/
#define SLIST_FREE(head, field, freef) do { \
typeof(SLIST_FIRST(head)) __elm0; \
typeof(SLIST_FIRST(head)) __elm; \
SLIST_FOREACH_SAFE(__elm, (head), field, __elm0) \
(void)(freef)(__elm); \
} while(0);
struct datadir {
const char *path;
SLIST_ENTRY(datadir) next;
@ -102,7 +94,6 @@ static struct ucode_handler {
#define NHANDLERS (sizeof(handlers) / sizeof(*handlers))
static void usage(void);
static int isdir(const char *path);
static int do_cpuid(const char *cmdarg, const char *dev);
static int do_cpuid_count(const char *cmdarg, const char *dev);
static int do_msr(const char *cmdarg, const char *dev);
@ -122,20 +113,6 @@ usage(void)
exit(EX_USAGE);
}
static int
isdir(const char *path)
{
int error;
struct stat st;
error = stat(path, &st);
if (error < 0) {
WARN(0, "stat(%s)", path);
return (error);
}
return (st.st_mode & S_IFDIR);
}
static int
do_cpuid(const char *cmdarg, const char *dev)
{
@ -360,17 +337,78 @@ do_eval_cpu_features(const char *dev)
return (error);
}
static int
try_a_fw_image(const char *dev_path, int devfd, int fwdfd, const char *dpath,
const char *fname, struct ucode_handler *handler)
{
struct ucode_update_params parm;
struct stat st;
char *fw_path;
void *fw_map;
int fwfd, rc;
rc = 0;
fw_path = NULL;
fw_map = MAP_FAILED;
fwfd = openat(fwdfd, fname, O_RDONLY);
if (fwfd < 0) {
WARN(0, "openat(%s, %s)", dpath, fname);
goto out;
}
rc = asprintf(&fw_path, "%s/%s", dpath, fname);
if (rc == -1) {
WARNX(0, "out of memory");
rc = ENOMEM;
goto out;
}
rc = fstat(fwfd, &st);
if (rc != 0) {
WARN(0, "fstat(%s)", fw_path);
rc = 0;
goto out;
}
if (st.st_size <= 0) {
WARN(0, "%s: empty", fw_path);
goto out;
}
fw_map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fwfd, 0);
if (fw_map == MAP_FAILED) {
WARN(0, "mmap(%s)", fw_path);
goto out;
}
memset(&parm, 0, sizeof(parm));
parm.devfd = devfd;
parm.fwimage = fw_map;
parm.fwsize = st.st_size;
parm.dev_path = dev_path;
parm.fw_path = fw_path;
handler->update(&parm);
out:
if (fw_map != MAP_FAILED)
munmap(fw_map, st.st_size);
free(fw_path);
if (fwfd >= 0)
close(fwfd);
return (rc);
}
static int
do_update(const char *dev)
{
int fd;
int fd, fwdfd;
unsigned int i;
int error;
struct ucode_handler *handler;
struct datadir *dir;
DIR *dirp;
struct dirent *direntry;
char buf[MAXPATHLEN];
fd = open(dev, O_RDONLY);
if (fd < 0) {
@ -379,7 +417,7 @@ do_update(const char *dev)
}
/*
* Find the appropriate handler for device.
* Find the appropriate handler for CPU.
*/
for (i = 0; i < NHANDLERS; i++)
if (handlers[i].probe(fd) == 0)
@ -387,39 +425,54 @@ do_update(const char *dev)
if (i < NHANDLERS)
handler = &handlers[i];
else {
WARNX(0, "cannot find the appropriate handler for device");
WARNX(0, "cannot find the appropriate handler for %s", dev);
close(fd);
return (1);
}
close(fd);
fd = open(dev, O_RDWR);
if (fd < 0) {
WARN(0, "error opening %s for writing", dev);
return (1);
}
/*
* Process every image in specified data directories.
*/
SLIST_FOREACH(dir, &datadirs, next) {
dirp = opendir(dir->path);
if (dirp == NULL) {
WARNX(1, "skipping directory %s: not accessible", dir->path);
fwdfd = open(dir->path, O_RDONLY);
if (fwdfd < 0) {
WARN(1, "skipping directory %s: not accessible", dir->path);
continue;
}
dirp = fdopendir(fwdfd);
if (dirp == NULL) {
WARNX(0, "out of memory");
close(fwdfd);
close(fd);
return (1);
}
while ((direntry = readdir(dirp)) != NULL) {
if (direntry->d_namlen == 0)
continue;
error = snprintf(buf, sizeof(buf), "%s/%s", dir->path,
direntry->d_name);
if ((unsigned)error >= sizeof(buf))
WARNX(0, "skipping %s, buffer too short",
direntry->d_name);
if (isdir(buf) != 0) {
WARNX(2, "skipping %s: is a directory", buf);
if (direntry->d_type == DT_DIR)
continue;
error = try_a_fw_image(dev, fd, fwdfd, dir->path,
direntry->d_name, handler);
if (error != 0) {
closedir(dirp);
close(fd);
return (1);
}
handler->update(dev, buf);
}
error = closedir(dirp);
if (error != 0)
WARN(0, "closedir(%s)", dir->path);
}
close(fd);
return (0);
}
@ -441,6 +494,7 @@ datadir_add(const char *path)
int
main(int argc, char *argv[])
{
struct datadir *elm;
int c, flags;
const char *cmdarg;
const char *dev;
@ -511,6 +565,9 @@ main(int argc, char *argv[])
default:
usage(); /* Only one command can be selected. */
}
SLIST_FREE(&datadirs, next, free);
while ((elm = SLIST_FIRST(&datadirs)) != NULL) {
SLIST_REMOVE_HEAD(&datadirs, next);
free(elm);
}
return (error == 0 ? 0 : 1);
}

View File

@ -30,8 +30,17 @@
#ifndef CPUCONTROL_H
#define CPUCONTROL_H
#include <stddef.h>
typedef int ucode_probe_t(int fd);
typedef void ucode_update_t(const char *dev, const char *image);
struct ucode_update_params {
int devfd; /* RDWR handle to cpucontrol device */
const void *fwimage; /* READ mapping of firmware image */
size_t fwsize; /* Non-zero size of firmware image */
const char *dev_path; /* cpucontrol device path, for logging */
const char *fw_path; /* firmware image path, for logging */
};
typedef void ucode_update_t(const struct ucode_update_params *params);
extern int verbosity_level;

View File

@ -76,23 +76,23 @@ intel_probe(int fd)
}
void
intel_update(const char *dev, const char *path)
intel_update(const struct ucode_update_params *params)
{
int fd, devfd;
struct stat st;
uint32_t *fw_image;
int devfd;
const char *dev, *path;
const uint32_t *fw_image;
int have_ext_table;
uint32_t sum;
unsigned int i;
size_t payload_size;
intel_fw_header_t *fw_header;
intel_cpu_signature_t *ext_table;
intel_ext_header_t *ext_header;
const intel_fw_header_t *fw_header;
const intel_cpu_signature_t *ext_table;
const intel_ext_header_t *ext_header;
uint32_t sig, signature, flags;
int32_t revision;
ssize_t ext_size;
size_t ext_table_size;
void *fw_data;
const void *fw_data;
size_t data_size, total_size;
cpuctl_msr_args_t msrargs = {
.msr = MSR_BIOS_SIGN,
@ -104,18 +104,17 @@ intel_update(const char *dev, const char *path)
cpuctl_update_args_t args;
int error;
dev = params->dev_path;
path = params->fw_path;
devfd = params->devfd;
fw_image = params->fwimage;
assert(path);
assert(dev);
fd = -1;
fw_image = MAP_FAILED;
ext_table = NULL;
ext_header = NULL;
devfd = open(dev, O_RDWR);
if (devfd < 0) {
WARN(0, "could not open %s for writing", dev);
return;
}
error = ioctl(devfd, CPUCTL_WRMSR, &msrargs);
if (error < 0) {
WARN(0, "ioctl(%s)", dev);
@ -151,31 +150,12 @@ intel_update(const char *dev, const char *path)
/*
* Open firmware image.
*/
fd = open(path, O_RDONLY, 0);
if (fd < 0) {
WARN(0, "open(%s)", path);
goto fail;
}
error = fstat(fd, &st);
if (error != 0) {
WARN(0, "fstat(%s)", path);
goto fail;
}
if (st.st_size < 0 || (unsigned)st.st_size < sizeof(*fw_header)) {
if (params->fwsize < sizeof(*fw_header)) {
WARNX(2, "file too short: %s", path);
goto fail;
}
/*
* mmap the whole image.
*/
fw_image = (uint32_t *)mmap(NULL, st.st_size, PROT_READ,
MAP_PRIVATE, fd, 0);
if (fw_image == MAP_FAILED) {
WARN(0, "mmap(%s)", path);
goto fail;
}
fw_header = (intel_fw_header_t *)fw_image;
fw_header = (const intel_fw_header_t *)fw_image;
if (fw_header->header_version != INTEL_HEADER_VERSION ||
fw_header->loader_revision != INTEL_LOADER_REVISION) {
WARNX(2, "%s is not a valid intel firmware: version mismatch",
@ -193,7 +173,7 @@ intel_update(const char *dev, const char *path)
total_size = data_size + sizeof(*fw_header);
else
total_size = fw_header->total_size;
if (total_size > (unsigned)st.st_size || st.st_size < 0) {
if (total_size > params->fwsize) {
WARNX(2, "file too short: %s", path);
goto fail;
}
@ -204,7 +184,7 @@ intel_update(const char *dev, const char *path)
*/
sum = 0;
for (i = 0; i < (payload_size / sizeof(uint32_t)); i++)
sum += *((uint32_t *)fw_image + i);
sum += *((const uint32_t *)fw_image + i);
if (sum != 0) {
WARNX(2, "%s: update data checksum invalid", path);
goto fail;
@ -217,9 +197,9 @@ intel_update(const char *dev, const char *path)
have_ext_table = 0;
if (ext_size > (signed)sizeof(*ext_header)) {
ext_header =
(intel_ext_header_t *)((char *)fw_image + payload_size);
ext_table = (intel_cpu_signature_t *)(ext_header + 1);
ext_header = (const intel_ext_header_t *)
((const char *)fw_image + payload_size);
ext_table = (const intel_cpu_signature_t *)(ext_header + 1);
/*
* Check the extended table size.
@ -236,7 +216,7 @@ intel_update(const char *dev, const char *path)
*/
sum = 0;
for (i = 0; i < (ext_table_size / sizeof(uint32_t)); i++)
sum += *((uint32_t *)ext_header + i);
sum += *((const uint32_t *)ext_header + i);
if (sum != 0) {
WARNX(2,
"%s: extended signature table checksum invalid",
@ -273,7 +253,7 @@ intel_update(const char *dev, const char *path)
}
fprintf(stderr, "%s: updating cpu %s from rev %#x to rev %#x... ",
path, dev, revision, fw_header->revision);
args.data = fw_data;
args.data = __DECONST(void *, fw_data);
args.size = data_size;
error = ioctl(devfd, CPUCTL_UPDATE, &args);
if (error < 0) {
@ -286,11 +266,5 @@ intel_update(const char *dev, const char *path)
fprintf(stderr, "done.\n");
fail:
if (fw_image != MAP_FAILED)
if (munmap(fw_image, st.st_size) != 0)
warn("munmap(%s)", path);
if (devfd >= 0)
close(devfd);
if (fd >= 0)
close(fd);
return;
}

View File

@ -76,18 +76,18 @@ via_probe(int fd)
}
void
via_update(const char *dev, const char *path)
via_update(const struct ucode_update_params *params)
{
int fd, devfd;
struct stat st;
uint32_t *fw_image;
int devfd;
const char *dev, *path;
const uint32_t *fw_image;
uint32_t sum;
unsigned int i;
size_t payload_size;
via_fw_header_t *fw_header;
const via_fw_header_t *fw_header;
uint32_t signature;
int32_t revision;
void *fw_data;
const void *fw_data;
size_t data_size, total_size;
cpuctl_msr_args_t msrargs = {
.msr = MSR_IA32_PLATFORM_ID,
@ -98,17 +98,14 @@ via_update(const char *dev, const char *path)
cpuctl_update_args_t args;
int error;
dev = params->dev_path;
path = params->fw_path;
devfd = params->devfd;
fw_image = params->fwimage;
assert(path);
assert(dev);
fd = -1;
devfd = -1;
fw_image = MAP_FAILED;
devfd = open(dev, O_RDWR);
if (devfd < 0) {
WARN(0, "could not open %s for writing", dev);
return;
}
error = ioctl(devfd, CPUCTL_CPUID, &idargs);
if (error < 0) {
WARN(0, "ioctl(%s)", dev);
@ -134,34 +131,13 @@ via_update(const char *dev, const char *path)
WARNX(2, "found cpu type %#x family %#x model %#x stepping %#x.",
(signature >> 12) & 0x03, (signature >> 8) & 0x0f,
(signature >> 4) & 0x0f, (signature >> 0) & 0x0f);
/*
* Open firmware image.
*/
fd = open(path, O_RDONLY, 0);
if (fd < 0) {
WARN(0, "open(%s)", path);
goto fail;
}
error = fstat(fd, &st);
if (error != 0) {
WARN(0, "fstat(%s)", path);
goto fail;
}
if (st.st_size < 0 || (unsigned)st.st_size < sizeof(*fw_header)) {
if (params->fwsize < sizeof(*fw_header)) {
WARNX(2, "file too short: %s", path);
goto fail;
}
/*
* mmap the whole image.
*/
fw_image = (uint32_t *)mmap(NULL, st.st_size, PROT_READ,
MAP_PRIVATE, fd, 0);
if (fw_image == MAP_FAILED) {
WARN(0, "mmap(%s)", path);
goto fail;
}
fw_header = (via_fw_header_t *)fw_image;
fw_header = (const via_fw_header_t *)fw_image;
if (fw_header->signature != VIA_HEADER_SIGNATURE ||
fw_header->loader_revision != VIA_LOADER_REVISION) {
WARNX(2, "%s is not a valid via firmware: version mismatch",
@ -170,7 +146,7 @@ via_update(const char *dev, const char *path)
}
data_size = fw_header->data_size;
total_size = fw_header->total_size;
if (total_size > (unsigned)st.st_size || st.st_size < 0) {
if (total_size > params->fwsize) {
WARNX(2, "file too short: %s", path);
goto fail;
}
@ -181,7 +157,7 @@ via_update(const char *dev, const char *path)
*/
sum = 0;
for (i = 0; i < (payload_size / sizeof(uint32_t)); i++)
sum += *((uint32_t *)fw_image + i);
sum += *((const uint32_t *)fw_image + i);
if (sum != 0) {
WARNX(2, "%s: update data checksum invalid", path);
goto fail;
@ -202,25 +178,18 @@ via_update(const char *dev, const char *path)
}
fprintf(stderr, "%s: updating cpu %s from rev %#x to rev %#x... ",
path, dev, revision, fw_header->revision);
args.data = fw_data;
args.data = __DECONST(void *, fw_data);
args.size = data_size;
error = ioctl(devfd, CPUCTL_UPDATE, &args);
if (error < 0) {
error = errno;
fprintf(stderr, "failed.\n");
fprintf(stderr, "failed.\n");
errno = error;
WARN(0, "ioctl()");
goto fail;
WARN(0, "ioctl()");
goto fail;
}
fprintf(stderr, "done.\n");
fail:
if (fw_image != MAP_FAILED)
if (munmap(fw_image, st.st_size) != 0)
warn("munmap(%s)", path);
if (devfd >= 0)
close(devfd);
if (fd >= 0)
close(fd);
return;
}