diff options
Diffstat (limited to 'util/nvmutil/nvmutil.c')
| -rw-r--r-- | util/nvmutil/nvmutil.c | 273 |
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 } |
