summaryrefslogtreecommitdiff
path: root/util/nvmutil
diff options
context:
space:
mode:
Diffstat (limited to 'util/nvmutil')
-rw-r--r--util/nvmutil/.gitignore3
-rw-r--r--util/nvmutil/ChangeLog.md8
-rw-r--r--util/nvmutil/Makefile2
-rw-r--r--util/nvmutil/README.md4
-rw-r--r--util/nvmutil/nvmutil.c375
5 files changed, 258 insertions, 134 deletions
diff --git a/util/nvmutil/.gitignore b/util/nvmutil/.gitignore
new file mode 100644
index 00000000..802202a4
--- /dev/null
+++ b/util/nvmutil/.gitignore
@@ -0,0 +1,3 @@
+/nvm
+/nvmutil
+*.bin
diff --git a/util/nvmutil/ChangeLog.md b/util/nvmutil/ChangeLog.md
deleted file mode 100644
index e1ed5754..00000000
--- a/util/nvmutil/ChangeLog.md
+++ /dev/null
@@ -1,8 +0,0 @@
-This change log has moved. Please refer here for historical pre-osboot-merge
-changes:
-
-<https://libreboot.org/docs/install/nvmutilimport.html>
-
-Osboot merged with Libreboot on November 17th, 2022. For nvmutil changes after
-this date, please check regular Libreboot release announcements which shall
-now specify any such changes.
diff --git a/util/nvmutil/Makefile b/util/nvmutil/Makefile
index 9bc0e381..22376c70 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 -std=c99 -D_POSIX_C_SOURCE=200809L
+CFLAGS?=-Os -Wall -Wextra -Werror -pedantic -std=c99
DESTDIR?=
PREFIX?=/usr/local
INSTALL?=install
diff --git a/util/nvmutil/README.md b/util/nvmutil/README.md
deleted file mode 100644
index 03a25bc4..00000000
--- a/util/nvmutil/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-
-This documentation has become part of lbwww. See:
-
-<https://libreboot.org/docs/install/nvmutil.html>
diff --git a/util/nvmutil/nvmutil.c b/util/nvmutil/nvmutil.c
index b8ea2376..67b58865 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.
+ *
+ * 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:
*
- * Use these CFLAGS:
- * -Os -Wall -Wextra -Werror -pedantic -std=c99 -D_POSIX_C_SOURCE=200809L
+ * -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,12 +112,19 @@ 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);
static void hexdump(size_t partnum);
+static void cmd_helper_cat(void);
+static void cmd_helper_cat16(void);
+static void cmd_helper_cat128(void);
+static void gbe_catn(size_t n);
+static void gbe_cat(uint8_t *b);
static void write_gbe_file(void);
static void override_part_modified(void);
static void set_checksum(size_t part);
@@ -171,6 +201,7 @@ static const char *rname = NULL;
* The code will handle this properly.
*/
static uint8_t buf[GBE_FILE_SIZE];
+static uint8_t pad[GBE_PART_SIZE];
static uint16_t mac_buf[3];
static off_t gbe_file_size;
@@ -211,6 +242,9 @@ enum {
CMD_SETMAC,
CMD_SWAP,
CMD_COPY,
+ CMD_CAT,
+ CMD_CAT16,
+ CMD_CAT128
};
/*
@@ -257,14 +291,10 @@ struct commands {
* Command table, for nvmutil commands
*/
static const struct commands command[] = {
- /*
- * 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,
+ SKIP_CHECKSUM_READ, SKIP_CHECKSUM_WRITE,
NVM_SIZE },
{ CMD_SETMAC, "setmac", cmd_helper_setmac, ARGC_3,
@@ -291,6 +321,24 @@ static const struct commands command[] = {
ARG_PART,
CHECKSUM_READ, SKIP_CHECKSUM_WRITE,
GBE_PART_SIZE },
+
+ { CMD_CAT, "cat", cmd_helper_cat, ARGC_3,
+ NO_INVERT, SET_MOD_OFF,
+ ARG_NOPART,
+ SKIP_CHECKSUM_READ, SKIP_CHECKSUM_WRITE,
+ GBE_PART_SIZE },
+
+ { CMD_CAT16, "cat16", cmd_helper_cat16, ARGC_3,
+ NO_INVERT, SET_MOD_OFF,
+ ARG_NOPART,
+ SKIP_CHECKSUM_READ, SKIP_CHECKSUM_WRITE,
+ GBE_PART_SIZE },
+
+ { CMD_CAT128, "cat128", cmd_helper_cat128, ARGC_3,
+ NO_INVERT, SET_MOD_OFF,
+ ARG_NOPART,
+ SKIP_CHECKSUM_READ, SKIP_CHECKSUM_WRITE,
+ GBE_PART_SIZE },
};
#define MAX_CMD_LEN 50
@@ -333,14 +381,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)
@@ -370,13 +418,18 @@ main(int argc, char *argv[])
err(ECANCELED, "pledge stdio (main)");
#endif
+ /*
+ * Used by CMD_CAT, for padding
+ */
+ memset(pad, 0xff, sizeof(pad));
+
read_gbe_file();
read_checksums();
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 +601,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 +711,48 @@ 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);
+}
+
+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);
+
+ 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);
- printf("Read %zu bytes from part %zu: %s\n",
- gbe_rw_size, p, fname);
+ return -1;
}
static void
@@ -744,7 +799,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 +829,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 +994,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)
@@ -997,8 +1062,12 @@ cmd_helper_dump(void)
{
size_t partnum;
- for (partnum = 0; partnum < 2; partnum++) {
+ int errval = good_checksum(0);
+ errval |= good_checksum(1);
+ if (errval)
+ errno = 0;
+ for (partnum = 0; partnum < 2; partnum++) {
printf("MAC (part %zu): ", partnum);
print_mac_from_nvm(partnum);
hexdump(partnum);
@@ -1028,7 +1097,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)
@@ -1040,6 +1109,63 @@ hexdump(size_t partnum)
}
static void
+cmd_helper_cat(void)
+{
+ gbe_catn(8);
+}
+
+static void
+cmd_helper_cat16(void)
+{
+ gbe_catn(16);
+}
+
+static void
+cmd_helper_cat128(void)
+{
+ gbe_catn(128);
+}
+
+static void
+gbe_catn(size_t n)
+{
+ size_t p;
+ size_t ff;
+
+ switch (n) {
+ case 8:
+ case 16:
+ case 128:
+ break;
+ default:
+ err(ECANCELED, "gbe_catn: Invalid GBE KB size: %zu", n);
+ }
+
+ fflush(NULL);
+
+ n -= 8;
+ n >>= 3;
+
+ for (p = 0; p < 2; p++) {
+ gbe_cat(buf + (p * GBE_PART_SIZE));
+
+ for (ff = 0; ff < n; ff++)
+ gbe_cat(pad);
+ }
+}
+
+static void
+gbe_cat(uint8_t *b)
+{
+ size_t wc = 0;
+ ssize_t w;
+
+ for (wc = 0; wc < GBE_PART_SIZE; wc += w)
+ if ((w = write(STDOUT_FILENO, b + wc, GBE_PART_SIZE - wc)) < 1)
+ err(EIO, "%s: stdout", fname);
+}
+
+static void
write_gbe_file(void)
{
size_t p;
@@ -1104,12 +1230,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 +1254,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 +1309,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 +1319,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 +1379,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 +1415,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
}
@@ -1341,8 +1470,12 @@ usage(uint8_t usage_exit)
"\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);
+ "\t%s FILE copy 0|1\n"
+ "\t%s FILE cat\n"
+ "\t%s FILE cat16\n"
+ "\t%s FILE cat128\n",
+ util, util, util, util,
+ util, util, util);
if (usage_exit)
err(EINVAL, "Too few arguments");