summaryrefslogtreecommitdiff
path: root/util
diff options
context:
space:
mode:
Diffstat (limited to 'util')
-rw-r--r--util/nvmutil/Makefile2
-rw-r--r--util/nvmutil/nvmutil.c1006
2 files changed, 643 insertions, 365 deletions
diff --git a/util/nvmutil/Makefile b/util/nvmutil/Makefile
index 91b5ba1c..c6611075 100644
--- a/util/nvmutil/Makefile
+++ b/util/nvmutil/Makefile
@@ -3,7 +3,7 @@
# SPDX-FileCopyrightText: 2023 Riku Viitanen <riku.viitanen@protonmail.com>
CC?=cc
-CFLAGS?=-Os -Wall -Wextra -Werror -pedantic
+CFLAGS?=-Os -Wall -Wextra -Werror -pedantic -std=c99 -D_POSIX_C_SOURCE=200809L
DESTDIR?=
PREFIX?=/usr/local
INSTALL?=install
diff --git a/util/nvmutil/nvmutil.c b/util/nvmutil/nvmutil.c
index 9191f2ea..5aa000a2 100644
--- a/util/nvmutil/nvmutil.c
+++ b/util/nvmutil/nvmutil.c
@@ -2,10 +2,16 @@
/* Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org> */
/* Copyright (c) 2023 Riku Viitanen <riku.viitanen@protonmail.com> */
+#define _POSIX_C_SOURCE 200809L
+
+#ifdef __OpenBSD__
+#include <sys/param.h>
+#endif
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
+#include <inttypes.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
@@ -14,33 +20,63 @@
#include <unistd.h>
/*
- * On the platforms below, we will use arc4random
- * for random MAC address generation.
- *
- * Later on, the code has fallbacks for other systems.
+ * 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.
*/
#if defined(__OpenBSD__) || defined(__FreeBSD__) || \
defined(__NetBSD__) || defined(__APPLE__) || \
defined(__DragonFly__)
-#ifndef HAVE_ARC4RANDOM_BUF
-#define HAVE_ARC4RANDOM_BUF
+#ifndef NVMUTIL_ARC4RANDOM_BUF
+#define NVMUTIL_ARC4RANDOM_BUF 1
#endif
#endif
+/*
+ * Older versions of BSD to the early 2000s
+ * could compile nvmutil, but pledge was
+ * added in the 2010s. Therefore, for extra
+ * portability, we will only pledge/unveil
+ * on OpenBSD versions that have it.
+ */
+#if defined(__OpenBSD__) && defined(OpenBSD)
+#if OpenBSD >= 604
+#ifndef NVMUTIL_UNVEIL
+#define NVMUTIL_UNVEIL 1
+#endif
+#endif
+#if OpenBSD >= 509
+#ifndef NVMUTIL_PLEDGE
+#define NVMUTIL_PLEDGE 1
+#endif
+#endif
+#endif
+
+static void sanitize_command_list(void);
+static void sanitize_command_index(size_t c);
+static void check_enum_bin(size_t a, const char *a_name,
+ size_t b, const char *b_name);
static void set_cmd(int argc, char *argv[]);
-static void check_cmd_args(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 run_cmd(ssize_t c);
static void set_io_flags(int argc, char *argv[]);
-static void open_gbe_file(void);
-#ifndef HAVE_ARC4RANDOM_BUF
+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);
#endif
+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 void cmd_setmac(void);
+static void read_checksums(void);
+static int good_checksum(size_t partnum);
+static void cmd_helper_setmac(void);
static void parse_mac_string(void);
+static size_t xstrxlen(const char *scmp, size_t maxlen);
static void set_mac_byte(size_t mac_byte_pos);
static void set_mac_nib(size_t mac_str_pos,
size_t mac_byte_pos, size_t mac_nib_pos);
@@ -48,31 +84,29 @@ static uint16_t hextonum(char ch_s);
static uint16_t rhex(void);
static void read_file_exact(int fd, void *buf, size_t len,
off_t off, const char *path, const char *op);
-static int write_mac_part(size_t partnum);
-static void cmd_dump(void);
+static void write_mac_part(size_t partnum);
+static void cmd_helper_dump(void);
static void print_mac_from_nvm(size_t partnum);
static void hexdump(size_t partnum);
-static void cmd_setchecksum(void);
+static void write_gbe_file(void);
+static void override_part_modified(void);
static void set_checksum(size_t part);
-static void cmd_brick(void);
-static void cmd_copy(void);
-static void cmd_swap(void);
-static int good_checksum(size_t partnum);
-static uint16_t word(size_t pos16, size_t part);
-static void set_word(size_t pos16, size_t part, uint16_t val16);
+static uint16_t calculated_checksum(size_t p);
+static uint16_t nvm_word(size_t pos16, size_t part);
+static void set_nvm_word(size_t pos16, size_t part, uint16_t val16);
+static void set_part_modified(size_t p);
static void check_nvm_bound(size_t pos16, size_t part);
-static void write_gbe_file(void);
+static void check_bin(size_t a, const char *a_name);
static void write_gbe_file_part(size_t part);
static off_t gbe_file_offset(size_t part, const char *f_op);
static void *gbe_mem_offset(size_t part, const char *f_op);
static off_t gbe_x_offset(size_t part, const char *f_op,
const char *d_type, off_t nsize, off_t ncmp);
-static void set_part_modified(size_t p);
-static void check_part_num(size_t p);
-static void usage(void);
static void err(int nvm_errval, const char *msg, ...);
+static void close_files(void);
static const char *getnvmprogname(void);
static void set_err(int errval);
+static void usage(uint8_t usage_exit);
/*
* Sizes in bytes:
@@ -106,7 +140,7 @@ static void set_err(int errval);
* When reading files, we loop on error EINTR
* a maximum number of times as defined, thus:
*/
-#define MAX_RETRY_READ 30
+#define MAX_RETRY_RW 30
/*
* Portable macro based on BSD nitems.
@@ -114,7 +148,7 @@ static void set_err(int errval);
*/
#define items(x) (sizeof((x)) / sizeof((x)[0]))
-#ifndef HAVE_ARC4RANDOM_BUF
+#ifndef NVMUTIL_ARC4RANDOM_BUF
static const char newrandom[] = "/dev/urandom";
static const char oldrandom[] = "/dev/random"; /* fallback on OLD unix */
static const char *rname = NULL;
@@ -135,100 +169,161 @@ static uint16_t mac_buf[3];
static off_t gbe_file_size;
static int gbe_flags;
-#ifndef HAVE_ARC4RANDOM_BUF
+#ifndef NVMUTIL_ARC4RANDOM_BUF
static int urandom_fd = -1;
#endif
static int gbe_fd = -1;
static size_t part;
static uint8_t part_modified[2];
+static uint8_t part_valid[2];
-static const char *mac_str;
static const char rmac[] = "xx:xx:xx:xx:xx:xx";
+static const char *mac_str;
static const char *fname;
static const char *argv0;
+/*
+ * Use these for .invert in command[]:
+ * If set to 1: read/write inverter (p0->p1, p1->p0)
+ */
#define PART_INVERT 1
#define NO_INVERT 0
+
+/*
+ * Use these for .argc in command[]:
+ */
#define ARGC_3 3
#define ARGC_4 4
/*
* Used as indices for command[]
- *
- * MUST be in the same order as entries in
- * command[] - or run_cmd() will detect this,
- * and cause a non-zero exit (err).
+ * MUST be in the same order as entries in command[]
*/
enum {
CMD_DUMP,
CMD_SETMAC,
CMD_SWAP,
CMD_COPY,
- CMD_BRICK,
- CMD_SETCHECKSUM
};
-#define CMD_NULL -1
+
+/*
+ * If set, a given part will always be written.
+ */
+enum {
+ SET_MOD_OFF, /* don't manually set part modified */
+ SET_MOD_0, /* set part 0 modified */
+ SET_MOD_1, /* set part 1 modified */
+ SET_MOD_N, /* set user-specified part modified */
+ /* affected by command[].invert */
+ SET_MOD_BOTH /* set both parts modified */
+};
+
+enum {
+ ARG_NOPART,
+ ARG_PART
+};
+
+enum {
+ SKIP_CHECKSUM_READ,
+ CHECKSUM_READ
+};
+
+enum {
+ SKIP_CHECKSUM_WRITE,
+ CHECKSUM_WRITE
+};
struct commands {
- size_t chk; /* use by in later check on run_cmd,
- against cmd index, to verify correct enum order */
+ size_t chk;
const char *str;
void (*run)(void);
int argc;
uint8_t invert;
+ uint8_t set_modified;
+ uint8_t arg_part;
+ uint8_t chksum_read;
+ uint8_t chksum_write;
+ size_t rw_size; /* within the 4KB GbE part */
};
/*
- * Pointers used for running nvmutil commands
+ * Command table, for nvmutil commands
*/
static const struct commands command[] = {
- { CMD_DUMP, "dump", cmd_dump, ARGC_3, NO_INVERT },
- { CMD_SETMAC, "setmac", cmd_setmac, ARGC_3, NO_INVERT },
- { CMD_SWAP, "swap", cmd_swap, ARGC_3, PART_INVERT },
- { CMD_COPY, "copy", cmd_copy, ARGC_4, PART_INVERT },
- { CMD_BRICK, "brick", cmd_brick, ARGC_4, NO_INVERT },
- { CMD_SETCHECKSUM, "setchecksum", cmd_setchecksum,
- ARGC_4, NO_INVERT },
+ /*
+ * Unlike older versions, we now require
+ * both checksums to be valid for "dump".
+ */
+ { CMD_DUMP, "dump", cmd_helper_dump, ARGC_3,
+ NO_INVERT, SET_MOD_OFF,
+ ARG_NOPART,
+ CHECKSUM_READ, SKIP_CHECKSUM_WRITE,
+ NVM_SIZE },
+
+ { CMD_SETMAC, "setmac", cmd_helper_setmac, ARGC_3,
+ NO_INVERT, SET_MOD_OFF,
+ ARG_NOPART,
+ CHECKSUM_READ, CHECKSUM_WRITE,
+ NVM_SIZE },
+
+ /*
+ * OPTIMISATION: Read inverted, so no copying is needed.
+ */
+ { CMD_SWAP, "swap", NULL, ARGC_3,
+ PART_INVERT, SET_MOD_BOTH,
+ ARG_NOPART,
+ CHECKSUM_READ, SKIP_CHECKSUM_WRITE,
+ GBE_PART_SIZE },
+
+ /*
+ * OPTIMISATION: Read inverted, so no copying is needed.
+ * The non-target part will not be read.
+ */
+ { CMD_COPY, "copy", NULL, ARGC_4,
+ PART_INVERT, SET_MOD_N,
+ ARG_PART,
+ CHECKSUM_READ, SKIP_CHECKSUM_WRITE,
+ GBE_PART_SIZE },
};
+#define MAX_CMD_LEN 50
+#define N_COMMANDS items(command)
+#define CMD_NULL N_COMMANDS
+
/*
* Index in command[], will be set later
*/
-static ssize_t cmd = CMD_NULL;
+static size_t cmd_index = CMD_NULL;
int
main(int argc, char *argv[])
{
argv0 = argv[0];
- if (argc < 2)
- usage();
+ if (argc < 3)
+ usage(1);
fname = argv[1];
-#ifdef __OpenBSD__
+#ifdef NVMUTIL_PLEDGE
+#ifdef NVMUTIL_UNVEIL
if (pledge("stdio rpath wpath unveil", NULL) == -1)
err(ECANCELED, "pledge");
-
- /*
- * For restricted filesystem access on early error.
- *
- * This prevents access to /dev/urandom, which we
- * should never use in OpenBSD (we use arc4random),
- * thus guarding against any future bugs there.
- *
- * This also prevents early reads to the GbE file,
- * while performing other checks; we will later
- * unveil the GbE file, to allow access.
- */
if (unveil("/dev/null", "r") == -1)
err(ECANCELED, "unveil '/dev/null'");
+#else
+ if (pledge("stdio rpath wpath", NULL) == -1)
+ err(ECANCELED, "pledge");
+#endif
#endif
+ sanitize_command_list();
+
set_cmd(argc, argv);
- check_cmd_args(argc, argv);
+ set_cmd_args(argc, argv);
set_io_flags(argc, argv);
-#ifdef __OpenBSD__
+#ifdef NVMUTIL_PLEDGE
+#ifdef NVMUTIL_UNVEIL
if (gbe_flags == O_RDONLY) {
if (unveil(fname, "r") == -1)
err(ECANCELED, "unveil ro '%s'", fname);
@@ -244,112 +339,178 @@ main(int argc, char *argv[])
if (pledge("stdio rpath wpath", NULL) == -1)
err(ECANCELED, "pledge rw (kill unveil)");
}
+#else
+ if (gbe_flags == O_RDONLY) {
+ if (pledge("stdio rpath", NULL) == -1)
+ err(ECANCELED, "pledge ro");
+ }
+#endif
#endif
-#ifdef HAVE_ARC4RANDOM_BUF
- if (cmd == CMD_SETMAC)
- printf("Randomisation method: arc4random_buf\n");
-#else
- if (cmd == CMD_SETMAC)
- open_dev_urandom();
+#ifndef NVMUTIL_ARC4RANDOM_BUF
+#if defined(__OpenBSD__) || defined(__FreeBSD__) || \
+ defined(__NetBSD__) || defined(__APPLE__) || \
+ defined(__DragonFly__)
+ err(ECANCELED, "Maintainer error: arc4random disabled on BSD/MacOS");
+#endif
+ open_dev_urandom();
#endif
+
open_gbe_file();
-#ifdef __OpenBSD__
+#ifdef NVMUTIL_PLEDGE
if (pledge("stdio", NULL) == -1)
err(ECANCELED, "pledge stdio (main)");
#endif
read_gbe_file();
- run_cmd(cmd);
- write_gbe_file();
-
- if (close(gbe_fd) == -1)
- err(ECANCELED, "close '%s'", fname);
-#ifndef HAVE_ARC4RANDOM_BUF
- if (close(urandom_fd) == -1)
- err(ECANCELED, "close '%s'", rname);
-#endif
+ read_checksums();
- /*
- * We still exit with non-zero status if
- * errno is set, but we don't need to print
- * the error on dump commands, because they
- * already print errors.
- *
- * If both parts have bad checksums, then
- * cmd_dump will cause non-zero exit. If at
- * least one part is valid, it resets errno.
- *
- * However, if we're not using cmd_dump, then
- * we have a bug somewhere in the code.
- */
- if (cmd != CMD_DUMP) {
- if (errno)
- err(ECANCELED, "Unhandled error on exit");
- }
+ run_cmd(cmd_index);
if (errno)
- return EXIT_FAILURE;
- else
- return EXIT_SUCCESS;
+ err(errno, "Unhandled error: will not write file: %s", fname);
+ else if (gbe_flags != O_RDONLY)
+ write_gbe_file();
+
+ close_files();
+
+ if (errno)
+ err(ECANCELED, "Unhandled error on exit");
+
+ return EXIT_SUCCESS;
}
+/*
+ * Guard against regressions by maintainers (command table)
+ */
static void
-set_cmd(int argc, char *argv[])
+sanitize_command_list(void)
{
- /*
- * No extra args: ./nvmutil gbe.bin
- * Equivalent: ./nvmutil gbe.bin setmac xx:xx:xx:xx:xx:xx
- */
- if (argc == 2) {
- cmd = CMD_SETMAC;
- return;
+ size_t c;
+
+ for (c = 0; valid_command(c); c++)
+ sanitize_command_index(c);
+}
+
+static void
+sanitize_command_index(size_t c)
+{
+ uint8_t mod_type;
+ size_t gbe_rw_size;
+
+ check_command_num(c);
+
+ if (ARGC_3 != 3)
+ err(ECANCELED, "ARGC_3 is not equal to 3");
+ if (ARGC_4 != 4)
+ err(ECANCELED, "ARGC_4 is not equal to 4");
+
+ if (command[c].argc < 3)
+ err(ECANCELED, "cmd index %zu: argc below 3, %d",
+ c, command[c].argc);
+
+ if (command[c].str == NULL)
+ err(ECANCELED, "cmd index %zu: NULL str", c);
+ if (*command[c].str == '\0')
+ err(ECANCELED, "cmd index %zu: empty str", c);
+
+ if (xstrxlen(command[c].str, MAX_CMD_LEN + 1) >
+ MAX_CMD_LEN) {
+ err(ECANCELED, "cmd index %zu: str too long: %s",
+ c, command[c].str);
}
- /*
- * Three or more args.
- * Example: ./nvmutil gbe.bin copy 0
- */
- for (cmd = 0; cmd < (ssize_t)items(command); cmd++) {
- if (strcmp(argv[2], command[cmd].str) != 0)
+ mod_type = command[c].set_modified;
+ switch (mod_type) {
+ case SET_MOD_0:
+ case SET_MOD_1:
+ case SET_MOD_N:
+ case SET_MOD_BOTH:
+ case SET_MOD_OFF:
+ break;
+ default:
+ err(EINVAL, "Unsupported set_mod type: %u", mod_type);
+ }
+
+ check_bin(command[c].invert, "cmd.invert");
+ check_bin(command[c].arg_part, "cmd.arg_part");
+ check_bin(command[c].chksum_read, "cmd.chksum_read");
+ check_bin(command[c].chksum_write, "cmd.chksum_write");
+
+ check_enum_bin(ARG_NOPART, "ARG_NOPART", ARG_PART, "ARG_PART");
+ check_enum_bin(SKIP_CHECKSUM_READ, "SKIP_CHECKSUM_READ",
+ CHECKSUM_READ, "CHECKSUM_READ");
+ check_enum_bin(SKIP_CHECKSUM_WRITE, "SKIP_CHECKSUM_WRITE",
+ CHECKSUM_WRITE, "CHECKSUM_WRITE");
+
+ gbe_rw_size = command[c].rw_size;
+
+ switch (gbe_rw_size) {
+ case GBE_PART_SIZE:
+ case NVM_SIZE:
+ break;
+ default:
+ err(EINVAL, "Unsupported rw_size: %zu", gbe_rw_size);
+ }
+
+ if (gbe_rw_size > GBE_PART_SIZE)
+ err(EINVAL, "rw_size larger than GbE part: %zu",
+ gbe_rw_size);
+}
+
+static void
+check_enum_bin(size_t a, const char *a_name,
+ size_t b, const char *b_name)
+{
+ if (a)
+ err(ECANCELED, "%s is non-zero", a_name);
+
+ if (b != 1)
+ err(ECANCELED, "%s is a value other than 1", b_name);
+}
+
+static void
+set_cmd(int argc, char *argv[])
+{
+ const char *cmd_str;
+
+ for (cmd_index = 0; valid_command(cmd_index); cmd_index++) {
+ cmd_str = command[cmd_index].str;
+
+ if (xstrxcmp(argv[2], cmd_str, MAX_CMD_LEN) != 0)
continue;
- if (argc >= command[cmd].argc) {
+ else if (argc >= command[cmd_index].argc)
return;
- }
- err(EINVAL, "Too few args: command '%s'", command[cmd].str);
+ err(EINVAL, "Too few args on command '%s'", cmd_str);
}
- cmd = CMD_NULL;
+ cmd_index = CMD_NULL;
}
static void
-check_cmd_args(int argc, char *argv[])
+set_cmd_args(int argc, char *argv[])
{
- if (cmd == CMD_NULL && argc > 2) {
- /*
- * Example: ./nvmutil gbe.bin xx:1f:16:xx:xx:xx
- * Equivalent ./nvmutil gbe.bin setmac xx:1f:16:xx:xx:xx
- */
- mac_str = argv[2];
- cmd = CMD_SETMAC;
- } else if (cmd == CMD_SETMAC) {
- /*
- * Example: ./nvmutil gbe.bin setmac xx:1f:16:xx:xx:xx
- */
- mac_str = rmac; /* random MAC */
- if (argc > 3)
- mac_str = argv[3];
- } else if (cmd != CMD_NULL && argc > 3) { /* user-supplied partnum */
- /*
- * Example: ./nvmutil gbe.bin copy 0
- */
- part = conv_argv_part_num(argv[3]);
- }
+ uint8_t arg_part;
+
+ if (!valid_command(cmd_index) || argc < 3)
+ usage(1);
+
+ arg_part = command[cmd_index].arg_part;
- if (cmd == CMD_NULL)
- err(EINVAL, "Bad command");
+ /* Maintainer bugs */
+ if (arg_part && argc < 4)
+ err(ECANCELED,
+ "arg_part set for command that needs argc4");
+ if (arg_part && cmd_index == CMD_SETMAC)
+ err(ECANCELED,
+ "arg_part set on CMD_SETMAC");
+
+ if (cmd_index == CMD_SETMAC)
+ mac_str = argc >= 4 ? argv[3] : rmac;
+ else if (arg_part)
+ part = conv_argv_part_num(argv[3]);
}
static size_t
@@ -357,16 +518,11 @@ conv_argv_part_num(const char *part_str)
{
unsigned char ch;
- /*
- * Because char signedness is implementation-defined,
- * we cast to unsigned char before arithmetic.
- */
-
if (part_str[0] == '\0' || part_str[1] != '\0')
err(EINVAL, "Partnum string '%s' wrong length", part_str);
+ /* char signedness is implementation-defined */
ch = (unsigned char)part_str[0];
-
if (ch < '0' || ch > '1')
err(EINVAL, "Bad part number (%c)", ch);
@@ -374,57 +530,97 @@ conv_argv_part_num(const char *part_str)
}
static void
-run_cmd(ssize_t c)
+set_io_flags(int argc, char *argv[])
{
- size_t d = (size_t)c;
+ gbe_flags = O_RDWR;
- if (d >= items(command))
- err(ECANCELED, "run_cmd: Invalid run_cmd arg: %zd", c);
+ if (argc < 3)
+ return;
- if (d != command[d].chk)
- err(ECANCELED, "run_cmd: Invalid chk value (%zu) vs arg: %zd",
- command[d].chk, c);
+ if (xstrxcmp(argv[2], "dump", MAX_CMD_LEN) == 0)
+ gbe_flags = O_RDONLY;
+}
- command[d].run();
+static void
+run_cmd(size_t c)
+{
+ check_command_num(c);
+ if (command[c].run)
+ command[c].run();
}
static void
-set_io_flags(int argc, char *argv[])
+check_command_num(size_t c)
{
- gbe_flags = O_RDWR;
+ if (!valid_command(c))
+ err(ECANCELED, "Invalid run_cmd arg: %zu", c);
+}
- if (argc < 3)
- return;
+static uint8_t
+valid_command(size_t c)
+{
+ if (c >= N_COMMANDS)
+ return 0;
- if (strcmp(argv[2], "dump") == 0)
- gbe_flags = O_RDONLY;
+ if (c != command[c].chk)
+ err(ECANCELED, "Invalid cmd chk value (%zu) vs arg: %zu",
+ command[c].chk, c);
+
+ return 1;
}
-#ifndef HAVE_ARC4RANDOM_BUF
-static void
-open_dev_urandom(void)
+/*
+ * Portable strcmp() but blocks NULL/empty/unterminated
+ * strings. Even stricter than strncmp().
+ */
+static int
+xstrxcmp(const char *a, const char *b, size_t maxlen)
{
- struct stat st_urandom_fd;
+ size_t i;
+
+ if (a == NULL || b == NULL)
+ err(EINVAL, "NULL input to xstrxcmp");
+
+ if (*a == '\0' || *b == '\0')
+ err(EINVAL, "Empty string in xstrxcmp");
- printf("Randomisation method: %s\n", newrandom);
+ for (i = 0; i < maxlen; i++) {
+ if (a[i] != b[i])
+ return (unsigned char)a[i] - (unsigned char)b[i];
+
+ if (a[i] == '\0')
+ return 0;
+ }
/*
- * Try /dev/urandom first
+ * We reached maxlen, so assume unterminated string.
*/
+ err(EINVAL, "Unterminated string in xstrxcmp");
+
+ /*
+ * Should never reach here. This keeps compilers happy.
+ */
+ errno = EINVAL;
+ return -1;
+}
+
+#ifndef NVMUTIL_ARC4RANDOM_BUF
+static void
+open_dev_urandom(void)
+{
+ struct stat st_urandom_fd;
+
rname = newrandom;
if ((urandom_fd = open(rname, O_RDONLY)) != -1)
return;
+ /*
+ * Fall back to /dev/random on very old Unix.
+ */
+
fprintf(stderr, "Can't open %s (will use %s instead)\n",
newrandom, oldrandom);
- /*
- * Fall back to /dev/random on old platforms
- * where /dev/urandom does not exist.
- *
- * We must reset the error condition first,
- * to prevent stale error status later.
- */
errno = 0;
rname = oldrandom;
@@ -456,6 +652,7 @@ xopen(int *fd_ptr, const char *path, int flags, struct stat *st)
{
if ((*fd_ptr = open(path, flags)) == -1)
err(ECANCELED, "%s", path);
+
if (fstat(*fd_ptr, st) == -1)
err(ECANCELED, "%s", path);
}
@@ -467,14 +664,10 @@ read_gbe_file(void)
uint8_t do_read[2] = {1, 1};
/*
- * The copy, brick and setchecksum commands need
- * only read data from the user-specified part.
- *
- * We can skip reading the other part, thus:
+ * Commands specifying a partnum only
+ * need the given GbE part to be read.
*/
- if (cmd == CMD_COPY ||
- cmd == CMD_BRICK ||
- cmd == CMD_SETCHECKSUM)
+ if (command[cmd_index].arg_part)
do_read[part ^ 1] = 0;
for (p = 0; p < 2; p++) {
@@ -486,26 +679,107 @@ read_gbe_file(void)
static void
read_gbe_file_part(size_t p)
{
- void *mem_offset = gbe_mem_offset(p ^ command[cmd].invert, "pread");
+ size_t gbe_rw_size = command[cmd_index].rw_size;
+
+ void *mem_offset =
+ gbe_mem_offset(p ^ command[cmd_index].invert, "pread");
read_file_exact(gbe_fd, mem_offset,
- GBE_PART_SIZE, gbe_file_offset(p, "pread"), fname, "pread");
+ gbe_rw_size, gbe_file_offset(p, "pread"), fname, "pread");
+
+ printf("Read %zu bytes from part %zu: %s\n",
+ gbe_rw_size, p, fname);
}
static void
-cmd_setmac(void)
+read_checksums(void)
+{
+ size_t p;
+ size_t skip_part;
+ uint8_t invert;
+ uint8_t arg_part;
+ uint8_t num_invalid;
+ uint8_t max_invalid;
+
+ if (!command[cmd_index].chksum_read)
+ return;
+
+ num_invalid = 0;
+ max_invalid = 2;
+
+ invert = command[cmd_index].invert;
+ arg_part = command[cmd_index].arg_part;
+ if (arg_part)
+ max_invalid = 1;
+
+ /*
+ * Skip verification on this part,
+ * but only when arg_part is set.
+ */
+ skip_part = part ^ 1 ^ invert;
+
+ for (p = 0; p < 2; p++) {
+ /*
+ * Only verify a part if it was *read*
+ */
+ if (arg_part && (p == skip_part))
+ continue;
+
+ if (good_checksum(p))
+ part_valid[p] = 1;
+ else
+ ++num_invalid;
+ }
+
+ if (num_invalid < max_invalid)
+ errno = 0;
+
+ if (num_invalid >= max_invalid)
+ err(ECANCELED, "No valid checksum found in file: %s",
+ fname);
+}
+
+static int
+good_checksum(size_t partnum)
+{
+ uint16_t expected_checksum = calculated_checksum(partnum);
+ uint16_t current_checksum = nvm_word(NVM_CHECKSUM_WORD, partnum);
+
+ size_t real_partnum = partnum ^ command[cmd_index].invert;
+
+ if (current_checksum == expected_checksum)
+ return 1;
+
+ fprintf(stderr,
+ "WARNING: BAD checksum in part %zu\n"
+ "EXPECTED checksum in part %zu: %04x\n"
+ "CURRENT checksum in part %zu: %04x\n",
+ real_partnum,
+ real_partnum,
+ expected_checksum,
+ real_partnum,
+ current_checksum);
+
+ set_err(ECANCELED);
+ return 0;
+}
+
+static void
+cmd_helper_setmac(void)
{
size_t partnum;
- uint8_t mac_updated = 0;
+
+#ifdef NVMUTIL_ARC4RANDOM_BUF
+ printf("Randomisation method: arc4random_buf\n");
+#else
+ printf("Randomisation method: %s\n", rname);
+#endif
printf("MAC address to be written: %s\n", mac_str);
parse_mac_string();
for (partnum = 0; partnum < 2; partnum++)
- mac_updated |= write_mac_part(partnum);
-
- if (mac_updated)
- errno = 0;
+ write_mac_part(partnum);
}
static void
@@ -513,7 +787,7 @@ parse_mac_string(void)
{
size_t mac_byte;
- if (strlen(mac_str) != 17)
+ if (xstrxlen(mac_str, 18) != 17)
err(EINVAL, "MAC address is the wrong length");
memset(mac_buf, 0, sizeof(mac_buf));
@@ -528,6 +802,33 @@ parse_mac_string(void)
err(EINVAL, "Must not specify multicast MAC address");
}
+/*
+ * strnlen() but aborts on NULL input, and empty strings.
+ * Our version also prohibits unterminated strings.
+ * strnlen() was standardized in POSIX.1-2008 and is not
+ * available on some older systems, so we provide our own.
+ */
+static size_t
+xstrxlen(const char *scmp, size_t maxlen)
+{
+ size_t xstr_index;
+
+ if (scmp == NULL)
+ err(EINVAL, "NULL input to xstrxlen");
+
+ if (*scmp == '\0')
+ err(EINVAL, "Empty string in xstrxlen");
+
+ for (xstr_index = 0;
+ xstr_index < maxlen && scmp[xstr_index] != '\0';
+ xstr_index++);
+
+ if (xstr_index == maxlen)
+ err(EINVAL, "Unterminated string in xstrxlen");
+
+ return xstr_index;
+}
+
static void
set_mac_byte(size_t mac_byte_pos)
{
@@ -558,23 +859,18 @@ set_mac_nib(size_t mac_str_pos,
err(EINVAL, "Invalid character '%c'",
mac_str[mac_str_pos + mac_nib_pos]);
- /* If random, ensure that local/unicast bits are set */
+ /*
+ * If random, ensure that local/unicast bits are set.
+ */
if ((mac_byte_pos == 0) && (mac_nib_pos == 1) &&
((mac_ch | 0x20) == 'x' ||
(mac_ch == '?')))
hex_num = (hex_num & 0xE) | 2; /* local, unicast */
/*
- * Words other than the MAC address are stored little
- * endian in the file, and we handle that when reading.
- * However, MAC address words are stored big-endian
- * in that file, so we write each 2-byte word logically
- * in little-endian order, which on little-endian would
- * be stored big-endian in memory, and vice versa.
- *
- * Later code using the MAC string will handle this.
+ * MAC words stored big endian in-file, little-endian
+ * logically, so we reverse the order.
*/
-
mac_buf[mac_byte_pos >> 1] |= hex_num <<
(((mac_byte_pos & 1) << 3) /* left or right byte? */
| ((mac_nib_pos ^ 1) << 2)); /* left or right nib? */
@@ -583,10 +879,6 @@ set_mac_nib(size_t mac_str_pos,
static uint16_t
hextonum(char ch_s)
{
- /*
- * We assume char is signed, hence ch_s.
- * We explicitly cast to unsigned:
- */
unsigned char ch = (unsigned char)ch_s;
if ((unsigned)(ch - '0') <= 9)
@@ -611,7 +903,7 @@ rhex(void)
if (!n) {
n = sizeof(rnum);
-#ifdef HAVE_ARC4RANDOM_BUF
+#ifdef NVMUTIL_ARC4RANDOM_BUF
arc4random_buf(rnum, n);
#else
read_file_exact(urandom_fd, rnum, n, 0, rname, NULL);
@@ -631,7 +923,7 @@ read_file_exact(int fd, void *buf, size_t len,
if (fd == -1)
err(ECANCELED, "Trying to open bad fd: %s", path);
- for (retry = 0; retry < MAX_RETRY_READ; retry++) {
+ for (retry = 0; retry < MAX_RETRY_RW; retry++) {
if (op)
rval = pread(fd, buf, len, off);
else
@@ -657,42 +949,33 @@ read_file_exact(int fd, void *buf, size_t len,
op ? op : "read", path);
}
-static int
+static void
write_mac_part(size_t partnum)
{
size_t w;
- if (!good_checksum(partnum))
- return 0;
+ check_bin(partnum, "part number");
+ if (!part_valid[partnum])
+ return;
for (w = 0; w < 3; w++)
- set_word(w, partnum, mac_buf[w]);
+ set_nvm_word(w, partnum, mac_buf[w]);
printf("Wrote MAC address to part %zu: ", partnum);
print_mac_from_nvm(partnum);
-
- set_checksum(partnum);
-
- return 1;
}
static void
-cmd_dump(void)
+cmd_helper_dump(void)
{
size_t partnum;
- int num_invalid = 0;
for (partnum = 0; partnum < 2; partnum++) {
- if (!good_checksum(partnum))
- ++num_invalid;
printf("MAC (part %zu): ", partnum);
print_mac_from_nvm(partnum);
hexdump(partnum);
}
-
- if (num_invalid < 2)
- errno = 0;
}
static void
@@ -701,7 +984,7 @@ print_mac_from_nvm(size_t partnum)
size_t c;
for (c = 0; c < 3; c++) {
- uint16_t val16 = word(c, partnum);
+ uint16_t val16 = nvm_word(c, partnum);
printf("%02x:%02x", val16 & 0xff, val16 >> 8);
if (c == 2)
printf("\n");
@@ -720,7 +1003,7 @@ hexdump(size_t partnum)
for (row = 0; row < 8; row++) {
printf("%08zx ", row << 4);
for (c = 0; c < 8; c++) {
- val16 = word((row << 3) + c, partnum);
+ val16 = nvm_word((row << 3) + c, partnum);
if (c == 4)
printf(" ");
printf(" %02x %02x", val16 & 0xff, val16 >> 8);
@@ -730,109 +1013,88 @@ hexdump(size_t partnum)
}
static void
-cmd_setchecksum(void)
+write_gbe_file(void)
{
- set_checksum(part);
-}
+ size_t p;
+ size_t partnum;
+ uint8_t update_checksum;
-static void
-set_checksum(size_t p)
-{
- size_t c;
- uint16_t val16 = 0;
+ if (gbe_flags == O_RDONLY)
+ return;
- check_part_num(p);
+ update_checksum = command[cmd_index].chksum_write;
- for (c = 0; c < NVM_CHECKSUM_WORD; c++)
- val16 += word(c, p);
+ override_part_modified();
- set_word(NVM_CHECKSUM_WORD, p, NVM_CHECKSUM - val16);
-}
+ for (p = 0; p < 2; p++) {
+ partnum = p ^ command[cmd_index].invert;
-static void
-cmd_brick(void)
-{
- uint16_t checksum_word;
+ if (!part_modified[partnum])
+ continue;
- if (!good_checksum(part)) {
- err(ECANCELED,
- "Part %zu checksum already invalid in file '%s'",
- part, fname);
- }
+ if (update_checksum)
+ set_checksum(partnum);
- /*
- * We know checksum_word is valid, so we need only
- * flip one bit to invalidate it.
- */
- checksum_word = word(NVM_CHECKSUM_WORD, part);
- set_word(NVM_CHECKSUM_WORD, part, checksum_word ^ 1);
+ write_gbe_file_part(partnum);
+ }
}
static void
-cmd_copy(void)
+override_part_modified(void)
{
- if (!good_checksum(part ^ 1))
- err(ECANCELED, "copy p%zu, file '%s'", part ^ 1, fname);
+ uint8_t mod_type = command[cmd_index].set_modified;
- /*
- * SPEED HACK:
- *
- * read_gbe_file() already performed the copy,
- * by virtue of inverted read. We need
- * only set the other part as changed.
- */
- set_part_modified(part ^ 1);
+ switch (mod_type) {
+ case SET_MOD_0:
+ set_part_modified(0);
+ break;
+ case SET_MOD_1:
+ set_part_modified(1);
+ break;
+ case SET_MOD_N:
+ set_part_modified(part ^ command[cmd_index].invert);
+ break;
+ case SET_MOD_BOTH:
+ set_part_modified(0);
+ set_part_modified(1);
+ break;
+ case SET_MOD_OFF:
+ break;
+ default:
+ err(EINVAL, "Unsupported set_mod type: %u",
+ mod_type);
+ }
}
static void
-cmd_swap(void)
+set_checksum(size_t p)
{
- if (!(good_checksum(0) || good_checksum(1)))
- err(ECANCELED, "swap parts, file '%s'", fname);
-
- /*
- * good_checksum() can set errno, if one
- * of the parts is bad. We will reset it.
- */
- errno = 0;
-
- /*
- * SPEED HACK:
- *
- * read_gbe_file() already performed the swap,
- * by virtue of inverted read. We need
- * only set both parts as changed.
- */
- set_part_modified(0);
- set_part_modified(1);
+ check_bin(p, "part number");
+ set_nvm_word(NVM_CHECKSUM_WORD, p, calculated_checksum(p));
}
-static int
-good_checksum(size_t partnum)
+static uint16_t
+calculated_checksum(size_t p)
{
- size_t w;
- uint16_t total = 0;
-
- for (w = 0; w <= NVM_CHECKSUM_WORD; w++)
- total += word(w, partnum);
-
- if (total == NVM_CHECKSUM)
- return 1;
+ size_t c;
+ uint16_t val16 = 0;
- fprintf(stderr, "WARNING: BAD checksum in part %zu\n",
- partnum ^ command[cmd].invert);
+ for (c = 0; c < NVM_CHECKSUM_WORD; c++)
+ val16 += nvm_word(c, p);
- set_err(ECANCELED);
- return 0;
+ return NVM_CHECKSUM - val16;
}
/*
* GbE NVM files store 16-bit (2-byte) little-endian words.
* We must therefore swap the order when reading or writing.
+ *
+ * NOTE: The MAC address words are stored big-endian in the
+ * file, but we assume otherwise and adapt accordingly.
*/
static uint16_t
-word(size_t pos16, size_t p)
+nvm_word(size_t pos16, size_t p)
{
size_t pos;
@@ -843,7 +1105,7 @@ word(size_t pos16, size_t p)
}
static void
-set_word(size_t pos16, size_t p, uint16_t val16)
+set_nvm_word(size_t pos16, size_t p, uint16_t val16)
{
size_t pos;
@@ -857,55 +1119,68 @@ set_word(size_t pos16, size_t p, uint16_t val16)
}
static void
+set_part_modified(size_t p)
+{
+ check_bin(p, "part number");
+ part_modified[p] = 1;
+}
+
+static void
check_nvm_bound(size_t c, size_t p)
{
/*
- * NVM_SIZE assumed as the limit, because the
+ * NVM_SIZE assumed as the limit, because this
* current design assumes that we will only
* ever modified the NVM area.
- *
- * The only exception is copy/swap, but these
- * do not use word/set_word and therefore do
- * not cause check_nvm_bound() to be called.
- *
- * TODO:
- * This should be adjusted in the future, if
- * we ever wish to work on the extented area.
*/
- check_part_num(p);
+ check_bin(p, "part number");
if (c >= NVM_WORDS)
err(EINVAL, "check_nvm_bound: out of bounds %zu", c);
}
static void
-write_gbe_file(void)
+check_bin(size_t a, const char *a_name)
{
- size_t p;
-
- if (gbe_flags == O_RDONLY)
- return;
-
- for (p = 0; p < 2; p++) {
- if (part_modified[p])
- write_gbe_file_part(p);
- }
+ if (a > 1)
+ err(ECANCELED, "%s must be 0 or 1, but is %zu", a_name, a);
}
static void
write_gbe_file_part(size_t p)
{
- ssize_t rval = pwrite(gbe_fd, gbe_mem_offset(p, "pwrite"),
- GBE_PART_SIZE, gbe_file_offset(p, "pwrite"));
+ int retry;
+ ssize_t rval;
+ size_t gbe_rw_size;
+
+ if (gbe_fd == -1)
+ err(ECANCELED, "Trying to write bad gbe_fd: %s", fname);
+
+ gbe_rw_size = command[cmd_index].rw_size;
+
+ for (retry = 0; retry < MAX_RETRY_RW; retry++) {
+ rval = pwrite(gbe_fd, gbe_mem_offset(p, "pwrite"),
+ gbe_rw_size, gbe_file_offset(p, "pwrite"));
+
+ if (rval == (ssize_t)gbe_rw_size) {
+ errno = 0;
+ printf("Wrote %zu bytes to part %zu: %s\n",
+ gbe_rw_size, p, fname);
+ return;
+ }
+
+ if (rval != -1)
+ err(ECANCELED,
+ "Short pwrite, %zd bytes, on file: %s",
+ rval, fname);
- if (rval == -1)
- err(ECANCELED, "Can't write %zu b to '%s' p%zu",
- GBE_PART_SIZE, fname, p);
+ if (errno != EINTR)
+ err(ECANCELED,
+ "Could not pwrite file: '%s'", fname);
+ }
- if (rval != GBE_PART_SIZE)
- err(ECANCELED, "CORRUPTED WRITE (%zd b) to file '%s' p%zu",
- rval, fname, p);
+ err(EINTR, "pwrite: max retries exceeded on file: %s", fname);
}
/*
@@ -945,7 +1220,7 @@ gbe_x_offset(size_t p, const char *f_op, const char *d_type,
{
off_t off;
- check_part_num(p);
+ check_bin(p, "part number");
off = (off_t)p * nsize;
@@ -961,46 +1236,11 @@ gbe_x_offset(size_t p, const char *f_op, const char *d_type,
}
static void
-set_part_modified(size_t p)
-{
- check_part_num(p);
- part_modified[p] = 1;
-}
-
-static void
-check_part_num(size_t p)
-{
- if (p > 1)
- err(EINVAL, "Bad part number (%zu)", p);
-}
-
-static void
-usage(void)
-{
- const char *util = getnvmprogname();
-
-#ifdef __OpenBSD__
- if (pledge("stdio", NULL) == -1)
- err(ECANCELED, "pledge");
-#endif
- fprintf(stderr,
- "Modify Intel GbE NVM images e.g. set MAC\n"
- "USAGE:\n"
- "\t%s FILE dump\n"
- "\t%s FILE # same as setmac without [MAC]\n"
- "\t%s FILE setmac [MAC]\n"
- "\t%s FILE swap\n"
- "\t%s FILE copy 0|1\n"
- "\t%s FILE brick 0|1\n"
- "\t%s FILE setchecksum 0|1\n",
- util, util, util, util, util, util, util);
-
- err(ECANCELED, "Too few arguments");
-}
-
-static void
err(int nvm_errval, const char *msg, ...)
{
+ if (nvm_errval != -1)
+ close_files();
+
va_list args;
fprintf(stderr, "%s: ", getnvmprogname());
@@ -1016,6 +1256,22 @@ err(int nvm_errval, const char *msg, ...)
exit(EXIT_FAILURE);
}
+static void
+close_files(void)
+{
+ if (gbe_fd > -1) {
+ if (close(gbe_fd) == -1)
+ err(-1, "close '%s'", fname);
+ }
+
+#ifndef NVMUTIL_ARC4RANDOM_BUF
+ if (urandom_fd > -1) {
+ if (close(urandom_fd) == -1)
+ err(-1, "close '%s'", rname);
+ }
+#endif
+}
+
static const char *
getnvmprogname(void)
{
@@ -1037,8 +1293,30 @@ set_err(int x)
{
if (errno)
return;
- if (x)
+ if (x > 0)
errno = x;
else
errno = ECANCELED;
}
+
+static void
+usage(uint8_t usage_exit)
+{
+ const char *util = getnvmprogname();
+
+#ifdef NVMUTIL_PLEDGE
+ if (pledge("stdio", NULL) == -1)
+ err(ECANCELED, "pledge");
+#endif
+ fprintf(stderr,
+ "Modify Intel GbE NVM images e.g. set MAC\n"
+ "USAGE:\n"
+ "\t%s FILE dump\n"
+ "\t%s FILE setmac [MAC]\n"
+ "\t%s FILE swap\n"
+ "\t%s FILE copy 0|1\n",
+ util, util, util, util);
+
+ if (usage_exit)
+ err(EINVAL, "Too few arguments");
+}