summaryrefslogtreecommitdiff
path: root/util/nvmutil/nvmutil.c
diff options
context:
space:
mode:
Diffstat (limited to 'util/nvmutil/nvmutil.c')
-rw-r--r--util/nvmutil/nvmutil.c273
1 files changed, 158 insertions, 115 deletions
diff --git a/util/nvmutil/nvmutil.c b/util/nvmutil/nvmutil.c
index b8ea2376..37959bd2 100644
--- a/util/nvmutil/nvmutil.c
+++ b/util/nvmutil/nvmutil.c
@@ -1,24 +1,36 @@
-/* SPDX-License-Identifier: MIT */
-/* Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org> */
-/* Copyright (c) 2023 Riku Viitanen <riku.viitanen@protonmail.com> */
-
-/*
- * Written for portability among the Unices (Linux, BSD etc)
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org>
+ * Copyright (c) 2023 Riku Viitanen <riku.viitanen@protonmail.com>
+ *
+ * This tool lets you modify Intel GbE NVM (Gigabit Ethernet
+ * Non-Volatile Memory) images, e.g. change the MAC address.
+ * These images configure your Intel Gigabit Ethernet adapter.
*
- * Use these CFLAGS:
- * -Os -Wall -Wextra -Werror -pedantic -std=c99 -D_POSIX_C_SOURCE=200809L
+ * This code is designed to be portable, running on as many
+ * Unix and Unix-like systems as possible (mainly BSD/Linux).
+ *
+ * Recommended CFLAGS for Clang/GCC:
+ *
+ * -Os -Wall -Wextra -Werror -pedantic -std=c99
*/
-#define _POSIX_C_SOURCE 200809L
+#ifndef _XOPEN_SOURCE
+#define _XOPEN_SOURCE 500
+#endif
+
+#ifndef _FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 64
+#endif
#ifdef __OpenBSD__
#include <sys/param.h>
#endif
+#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
-#include <inttypes.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
@@ -26,11 +38,20 @@
#include <string.h>
#include <unistd.h>
+#if __STDC_VERSION__ >= 201112L
+_Static_assert(sizeof(uint16_t) == 2, "uint16_t must be 16 bits");
+#else
+typedef char static_assert_uint16_t_is_2[(sizeof(uint16_t) == 2) ? 1 : -1];
+#endif
+
/*
* The BSD versions that could realistically build
* nvmutil almost certainly have arc4random (first
- * introduced in 1990s or early 2000s in most of
- * them - you can just patch as needed, on old BSD.
+ * introduced in 1990s to early 2000s).
+ *
+ * If you want it on another platform, e.g. Linux,
+ * just patch this accordingly. Or patch it to remove
+ * arc4random on old/weird Unix systems.
*/
#if defined(__OpenBSD__) || defined(__FreeBSD__) || \
defined(__NetBSD__) || defined(__APPLE__) || \
@@ -68,9 +89,6 @@ static void set_cmd(int argc, char *argv[]);
static void set_cmd_args(int argc, char *argv[]);
static size_t conv_argv_part_num(const char *part_str);
static void set_io_flags(int argc, char *argv[]);
-static void run_cmd(size_t c);
-static void check_command_num(size_t c);
-static uint8_t valid_command(size_t c);
static int xstrxcmp(const char *a, const char *b, size_t maxlen);
#ifndef NVMUTIL_ARC4RANDOM_BUF
static void open_dev_urandom(void);
@@ -79,8 +97,13 @@ static void open_gbe_file(void);
static void xopen(int *fd, const char *path, int flags, struct stat *st);
static void read_gbe_file(void);
static void read_gbe_file_part(size_t part);
+static ssize_t read_gbe_file_exact(int fd, void *buf, size_t len,
+ off_t off);
static void read_checksums(void);
static int good_checksum(size_t partnum);
+static void run_cmd(size_t c);
+static void check_command_num(size_t c);
+static uint8_t valid_command(size_t c);
static void cmd_helper_setmac(void);
static void parse_mac_string(void);
static size_t xstrxlen(const char *scmp, size_t maxlen);
@@ -89,8 +112,10 @@ static void set_mac_nib(size_t mac_str_pos,
size_t mac_byte_pos, size_t mac_nib_pos);
static uint16_t hextonum(char ch_s);
static uint16_t rhex(void);
-static ssize_t read_gbe_file_exact(int fd, void *buf, size_t len,
- off_t off, const char *path, const char *op);
+#ifndef NVMUTIL_ARC4RANDOM_BUF
+static ssize_t read_dev_urandom(int fd, void *buf,
+ size_t len);
+#endif
static void write_mac_part(size_t partnum);
static void cmd_helper_dump(void);
static void print_mac_from_nvm(size_t partnum);
@@ -258,8 +283,8 @@ struct commands {
*/
static const struct commands command[] = {
/*
- * Unlike older versions, we now require
- * both checksums to be valid for "dump".
+ * Unlike older versions, we require at least
+ * one checksum to be valid when running dump.
*/
{ CMD_DUMP, "dump", cmd_helper_dump, ARGC_3,
NO_INVERT, SET_MOD_OFF,
@@ -333,14 +358,14 @@ main(int argc, char *argv[])
#ifdef NVMUTIL_UNVEIL
if (gbe_flags == O_RDONLY) {
if (unveil(fname, "r") == -1)
- err(ECANCELED, "unveil ro '%s'", fname);
+ err(ECANCELED, "%s: unveil ro", fname);
if (unveil(NULL, NULL) == -1)
err(ECANCELED, "unveil block (ro)");
if (pledge("stdio rpath", NULL) == -1)
err(ECANCELED, "pledge ro (kill unveil)");
} else {
if (unveil(fname, "rw") == -1)
- err(ECANCELED, "unveil rw '%s'", fname);
+ err(ECANCELED, "%s: unveil rw", fname);
if (unveil(NULL, NULL) == -1)
err(ECANCELED, "unveil block (rw)");
if (pledge("stdio rpath wpath", NULL) == -1)
@@ -376,7 +401,7 @@ main(int argc, char *argv[])
run_cmd(cmd_index);
if (errno)
- err(errno, "Unhandled error: will not write file: %s", fname);
+ err(errno, "%s: Unhandled error (WRITE SKIPPED)", fname);
else if (gbe_flags != O_RDONLY)
write_gbe_file();
@@ -548,34 +573,6 @@ set_io_flags(int argc, char *argv[])
gbe_flags = O_RDONLY;
}
-static void
-run_cmd(size_t c)
-{
- check_command_num(c);
- if (command[c].run)
- command[c].run();
-}
-
-static void
-check_command_num(size_t c)
-{
- if (!valid_command(c))
- err(ECANCELED, "Invalid run_cmd arg: %zu", c);
-}
-
-static uint8_t
-valid_command(size_t c)
-{
- if (c >= N_COMMANDS)
- return 0;
-
- if (c != command[c].chk)
- err(ECANCELED, "Invalid cmd chk value (%zu) vs arg: %zu",
- command[c].chk, c);
-
- return 1;
-}
-
/*
* Portable strcmp() but blocks NULL/empty/unterminated
* strings. Even stricter than strncmp().
@@ -686,18 +683,51 @@ read_gbe_file(void)
static void
read_gbe_file_part(size_t p)
{
- size_t gbe_rw_size = command[cmd_index].rw_size;
+ ssize_t rc;
+ size_t gbe_rw_size = command[cmd_index].rw_size;
void *mem_offset =
gbe_mem_offset(p ^ command[cmd_index].invert, "pread");
- if ((size_t)read_gbe_file_exact(gbe_fd, mem_offset,
- gbe_rw_size, gbe_file_offset(p, "pread"), fname, "pread") !=
- gbe_rw_size)
- err(ECANCELED, "Partial read p%zu, file %s", p, fname);
+ rc = read_gbe_file_exact(gbe_fd, mem_offset,
+ gbe_rw_size, gbe_file_offset(p, "pread"));
+
+ if (rc != (ssize_t)gbe_rw_size)
+ err(ECANCELED, "%s: Partial read from p%zu", fname, p);
+
+ printf("%s: Read %zu bytes from p%zu\n",
+ fname, gbe_rw_size, p);
+}
+
+static ssize_t
+read_gbe_file_exact(int fd,
+ void *buf, size_t len, off_t off)
+{
+ int retry;
+ ssize_t rval;
+
+ if (fd == -1)
+ err(ECANCELED, "Trying to open bad fd: %s", fname);
+
+ for (retry = 0; retry < MAX_RETRY_RW; retry++) {
+ rval = pread(fd, buf, len, off);
- printf("Read %zu bytes from part %zu: %s\n",
- gbe_rw_size, p, fname);
+ if (rval == (ssize_t)len) {
+ errno = 0;
+ return rval;
+ } else if (rval != -1) {
+ err(ECANCELED,
+ "%s: Short pread of %zd bytes",
+ fname, rval);
+ } else if (errno != EINTR) {
+ err(ECANCELED,
+ "%s: Could not pread", fname);
+ }
+ }
+
+ err(EINTR, "%s: pread: max retries exceeded", fname);
+
+ return -1;
}
static void
@@ -744,7 +774,7 @@ read_checksums(void)
errno = 0;
if (num_invalid >= max_invalid)
- err(ECANCELED, "No valid checksum found in file: %s",
+ err(ECANCELED, "%s: No valid checksum found in file",
fname);
}
@@ -774,6 +804,34 @@ good_checksum(size_t partnum)
}
static void
+run_cmd(size_t c)
+{
+ check_command_num(c);
+ if (command[c].run)
+ command[c].run();
+}
+
+static void
+check_command_num(size_t c)
+{
+ if (!valid_command(c))
+ err(ECANCELED, "Invalid run_cmd arg: %zu", c);
+}
+
+static uint8_t
+valid_command(size_t c)
+{
+ if (c >= N_COMMANDS)
+ return 0;
+
+ if (c != command[c].chk)
+ err(ECANCELED, "Invalid cmd chk value (%zu) vs arg: %zu",
+ command[c].chk, c);
+
+ return 1;
+}
+
+static void
cmd_helper_setmac(void)
{
size_t partnum;
@@ -911,70 +969,52 @@ rhex(void)
static uint8_t rnum[12];
if (!n) {
- n = sizeof(rnum);
#ifdef NVMUTIL_ARC4RANDOM_BUF
+ n = sizeof(rnum);
arc4random_buf(rnum, n);
#else
- n = (size_t)read_gbe_file_exact(urandom_fd,
- rnum, n, 0, rname, NULL);
+ n = (size_t)read_dev_urandom(
+ urandom_fd, rnum, sizeof(rnum));
+
+ if (!n || n > sizeof(rnum))
+ err(ECANCELED, "Randomisation failure");
#endif
}
return (uint16_t)(rnum[--n] & 0xf);
}
+#ifndef NVMUTIL_ARC4RANDOM_BUF
static ssize_t
-read_gbe_file_exact(int fd, void *buf, size_t len,
- off_t off, const char *path, const char *op)
+read_dev_urandom(int fd, void *buf, size_t len)
{
int retry;
ssize_t rval;
if (fd == -1)
- err(ECANCELED, "Trying to open bad fd: %s", path);
+ err(ECANCELED, "Trying to open bad fd: %s", rname);
for (retry = 0; retry < MAX_RETRY_RW; retry++) {
- if (op)
- rval = pread(fd, buf, len, off);
- else
- rval = read(fd, buf, len);
+ rval = read(fd, buf, len);
- if (rval == (ssize_t)len) {
- errno = 0;
- return rval;
+ if (rval == -1) {
+ if (errno == EINTR)
+ continue;
+ err(errno, "%s", rname);
}
- if (rval != -1) {
- if (fd == urandom_fd) {
- /*
- * /dev/[u]random reads can still return
- * partial reads legally, on some weird
- * Unix systems (especially older ones).
- *
- * We use a circular buffer for random
- * bytes in rhex(), so we can just use
- * the smaller amount of bytes and call
- * read_gbe_file_exact again if necessary.
- */
- if (rval > 0)
- return rval;
- }
-
- err(ECANCELED,
- "Short %s, %zd bytes, on file: %s",
- op ? op : "read", rval, path);
- }
+ if (!rval || (size_t)rval > len)
+ continue;
- if (errno != EINTR)
- err(ECANCELED,
- "Could not %s file: '%s'",
- op ? op : "read", path);
+ errno = 0;
+ return rval;
}
- err(EINTR, "%s: max retries exceeded on file: %s",
- op ? op : "read", path);
+ err(EINTR, "%s: read: max retries exceeded: %s", rname);
+
return -1;
}
+#endif
static void
write_mac_part(size_t partnum)
@@ -1028,7 +1068,7 @@ hexdump(size_t partnum)
uint16_t val16;
for (row = 0; row < 8; row++) {
- printf("%08zx ", row << 4);
+ printf("%08zx ", (size_t)row << 4);
for (c = 0; c < 8; c++) {
val16 = nvm_word((row << 3) + c, partnum);
if (c == 4)
@@ -1104,12 +1144,12 @@ static uint16_t
calculated_checksum(size_t p)
{
size_t c;
- uint16_t val16 = 0;
+ uint32_t val16 = 0;
for (c = 0; c < NVM_CHECKSUM_WORD; c++)
- val16 += nvm_word(c, p);
+ val16 += (uint32_t)nvm_word(c, p);
- return NVM_CHECKSUM - val16;
+ return (uint16_t)((NVM_CHECKSUM - val16) & 0xffff);
}
/*
@@ -1128,7 +1168,8 @@ nvm_word(size_t pos16, size_t p)
check_nvm_bound(pos16, p);
pos = (pos16 << 1) + (p * GBE_PART_SIZE);
- return (uint16_t)buf[pos] | (uint16_t)(buf[pos + 1] << 8);
+ return (uint16_t)buf[pos] |
+ ((uint16_t)buf[pos + 1] << 8);
}
static void
@@ -1182,7 +1223,7 @@ write_gbe_file_part(size_t p)
size_t gbe_rw_size;
if (gbe_fd == -1)
- err(ECANCELED, "Trying to write bad gbe_fd: %s", fname);
+ err(ECANCELED, "%s: Trying to write bad gbe_fd", fname);
gbe_rw_size = command[cmd_index].rw_size;
@@ -1192,22 +1233,22 @@ write_gbe_file_part(size_t p)
if (rval == (ssize_t)gbe_rw_size) {
errno = 0;
- printf("Wrote %zu bytes to part %zu: %s\n",
- gbe_rw_size, p, fname);
+ printf("%s: Wrote %zu bytes to part %zu\n",
+ fname, gbe_rw_size, p);
return;
}
if (rval != -1)
err(ECANCELED,
- "Short pwrite, %zd bytes, on file: %s",
- rval, fname);
+ "%s: Short pwrite of %zd bytes",
+ fname, rval);
if (errno != EINTR)
err(ECANCELED,
- "Could not pwrite file: '%s'", fname);
+ "%s: pwrite failed on p%zu", fname, p);
}
- err(EINTR, "pwrite: max retries exceeded on file: %s", fname);
+ err(EINTR, "%s: pwrite: max retries exceeded on p%zu", fname, p);
}
/*
@@ -1252,12 +1293,12 @@ gbe_x_offset(size_t p, const char *f_op, const char *d_type,
off = (off_t)p * nsize;
if (off + GBE_PART_SIZE > ncmp)
- err(ECANCELED, "GbE %s %s out of bounds: %s",
- d_type, f_op, fname);
+ err(ECANCELED, "%s: GbE %s %s out of bounds",
+ fname, d_type, f_op);
if (off != 0 && off != ncmp >> 1)
- err(ECANCELED, "GbE %s %s at bad offset: %s",
- d_type, f_op, fname);
+ err(ECANCELED, "%s: GbE %s %s at bad offset",
+ fname, d_type, f_op);
return off;
}
@@ -1288,13 +1329,15 @@ close_files(void)
{
if (gbe_fd > -1) {
if (close(gbe_fd) == -1)
- err(-1, "close '%s'", fname);
+ err(-1, "%s: close failed", fname);
+ gbe_fd = -1;
}
#ifndef NVMUTIL_ARC4RANDOM_BUF
if (urandom_fd > -1) {
if (close(urandom_fd) == -1)
- err(-1, "close '%s'", rname);
+ err(-1, "%s: close failed", rname);
+ urandom_fd = -1;
}
#endif
}