diff options
Diffstat (limited to 'util/nvmutil')
| -rw-r--r-- | util/nvmutil/nvmutil.c | 294 |
1 files changed, 160 insertions, 134 deletions
diff --git a/util/nvmutil/nvmutil.c b/util/nvmutil/nvmutil.c index 05459bb7..0d9020ee 100644 --- a/util/nvmutil/nvmutil.c +++ b/util/nvmutil/nvmutil.c @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: MIT */ -/* Copyright (c) 2022-2025 Leah Rowe <leah@libreboot.org> */ +/* Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org> */ /* Copyright (c) 2023 Riku Viitanen <riku.viitanen@protonmail.com> */ #include <sys/stat.h> @@ -15,12 +15,19 @@ #include <unistd.h> void cmd_setchecksum(void), cmd_brick(void), swap(int partnum), writeGbe(void), - cmd_dump(void), cmd_setmac(void), readGbe(void), checkdir(const char *path), - macf(int partnum), hexdump(int partnum), openFiles(const char *path), - cmd_copy(void), parseMacString(const char *strMac, uint16_t *mac), - cmd_swap(void); + cmd_dump(void), cmd_setmac(void), nvmalloc(void), readGbe(void), + checkdir(const char *path), macf(int partnum), hexdump(int partnum), + parseMacString(const char *strMac, uint16_t *mac), cmd_swap(void), + openFiles(const char *path), cmd_copy(void), writeGbe_part(int), + readGbe_part(int), usage(char*), set_io_flags(int, char **), + set_cmd(int, char **), setWord(int, int, uint16_t), check_bounds(int, int); int goodChecksum(int partnum); uint8_t hextonum(char chs), rhex(void); +uint16_t word(int, int); + +#ifdef __OpenBSD__ +void block_unveil(void); +#endif #define COMMAND argv[2] #define MAC_ADDRESS argv[3] @@ -38,9 +45,10 @@ uint16_t mac[3] = {0, 0, 0}; ssize_t nf; size_t partsize, gbe[2]; uint8_t nvmPartChanged[2] = {0, 0}, do_read[2] = {1, 1}; -int flags, rfd, fd, part; +int flags, rfd, fd, part, e = 1; +struct stat st; -const char *strMac = NULL, *strRMac = "xx:xx:xx:xx:xx:xx", *filename = NULL; +const char *strMac = NULL, *strRMac = "xx:xx:xx:xx:xx:xx", *fname = NULL; typedef struct op { char *str; @@ -57,76 +65,49 @@ op_t op[] = { }; void (*cmd)(void) = NULL; -#define ERR() errno = errno ? errno : ECANCELED -#define err_if(x) if (x) err(ERR(), "%s", filename) - -#define xopen(f,l,p) if ((f = open(l, p)) == -1) err(ERR(), "%s", l); \ - if (fstat(f, &st) == -1) err(ERR(), "%s", l) +#define SET_ERR(x) errno = errno ? errno : x +#define err_if(x) if (x) err(SET_ERR(ECANCELED), "%s", fname) -#define word(pos16, partnum) ((uint16_t *) gbe[partnum])[pos16] -#define setWord(pos16, p, val16) if (word(pos16, p) != val16) \ - nvmPartChanged[p] = 1 | (word(pos16, p) = val16) +#define xopen(f,l,p) \ + if ((f = open(l, p)) == -1) \ + err(SET_ERR(ECANCELED), "%s", l); \ + if (fstat(f, &st) == -1) \ + err(SET_ERR(ECANCELED), "%s", l) int main(int argc, char *argv[]) { #ifdef __OpenBSD__ err_if(pledge("stdio rpath wpath unveil", NULL) == -1); + err_if(unveil("/dev/urandom", "r") == -1); #endif + set_cmd(argc, argv); - if (argc < 2) { + fname = argv[1]; + set_io_flags(argc, argv); #ifdef __OpenBSD__ - err_if(pledge("stdio", NULL) == -1); + block_unveil(); #endif - fprintf(stderr, "Modify Intel GbE NVM images e.g. set MAC\n"); - fprintf(stderr, "USAGE:\n"); - fprintf(stderr, " %s FILE dump\n", argv[0]); - fprintf(stderr, " %s FILE\n # same as setmac without arg\n", - argv[0]); - fprintf(stderr, " %s FILE setmac [MAC]\n", argv[0]); - fprintf(stderr, " %s FILE swap\n", argv[0]); - fprintf(stderr, " %s FILE copy 0|1\n", argv[0]); - fprintf(stderr, " %s FILE brick 0|1\n", argv[0]); - fprintf(stderr, " %s FILE setchecksum 0|1\n", argv[0]); - err(errno = ECANCELED, "Too few arguments"); - } - - filename = argv[1]; - - flags = O_RDWR; - - if (argc > 2) { - if (strcmp(COMMAND, "dump") == 0) { - flags = O_RDONLY; + openFiles(fname); #ifdef __OpenBSD__ - err_if(pledge("stdio rpath unveil", NULL) == -1); + err_if(pledge("stdio", NULL) == -1); #endif - } - } - - checkdir("/dev/urandom"); - checkdir(filename); -#ifdef __OpenBSD__ - err_if(unveil("/dev/urandom", "r") == -1); - - if (flags == O_RDONLY) { - err_if(unveil(filename, "r") == -1); - err_if(unveil(NULL, NULL) == -1); - err_if(pledge("stdio rpath", NULL) == -1); - } else { - err_if(unveil(filename, "rw") == -1); - err_if(unveil(NULL, NULL) == -1); - err_if(pledge("stdio rpath wpath", NULL) == -1); - } -#endif + nvmalloc(); + readGbe(); + (*cmd)(); + writeGbe(); - openFiles(filename); -#ifdef __OpenBSD__ - err_if(pledge("stdio", NULL) == -1); -#endif + err_if((errno != 0) && (cmd != cmd_dump)); + return errno; +} - if (argc > 2) { +void +set_cmd(int argc, char *argv[]) +{ + if (argc < 2) { + usage(argv[0]); + } else if (argc > 2) { for (int i = 0; (i < 6) && (cmd == NULL); i++) { if (strcmp(COMMAND, op[i].str) != 0) continue; @@ -134,10 +115,10 @@ main(int argc, char *argv[]) cmd = op[i].cmd; break; } - err(errno = EINVAL, "Too few args on command '%s'", + err(SET_ERR(EINVAL), "Too few args on command '%s'", op[i].str); } - } else { + } else { /* argc == 2 */ cmd = cmd_setmac; } @@ -153,20 +134,22 @@ main(int argc, char *argv[]) || PARTN[1] ? EINVAL : errno)); /* only allow '0' or '1' */ } err_if((errno = (cmd == NULL) ? EINVAL : errno)); +} - readGbe(); - (*cmd)(); - writeGbe(); - - err_if((errno != 0) && (cmd != cmd_dump)); - return errno; +void +set_io_flags(int argc, char *argv[]) +{ + flags = O_RDWR; + if (argc > 2) + if (strcmp(COMMAND, "dump") == 0) + flags = O_RDONLY; } void checkdir(const char *path) { if (opendir(path) != NULL) - err(errno = EISDIR, "%s", path); + err(SET_ERR(EISDIR), "%s", path); if (errno == ENOTDIR) errno = 0; err_if(errno); @@ -175,8 +158,10 @@ checkdir(const char *path) void openFiles(const char *path) { - struct stat st; + checkdir("/dev/urandom"); + checkdir(fname); + xopen(rfd, "/dev/urandom", O_RDONLY); xopen(fd, path, flags); switch(st.st_size) { @@ -186,49 +171,49 @@ openFiles(const char *path) partsize = st.st_size >> 1; break; default: - err(errno = ECANCELED, "Invalid file size (not 8/16/128KiB)"); + err(SET_ERR(ECANCELED), "Invalid file size (not 8/16/128KiB)"); break; } - - xopen(rfd, "/dev/urandom", O_RDONLY); } void -readGbe(void) +nvmalloc(void) { + /* same operations need the full block, others only 128 bytes */ + nf = NVM_SIZE; if ((cmd == cmd_swap) || (cmd == cmd_copy)) nf = SIZE_4KB; - else - nf = NVM_SIZE; + /* only read the part specified, for copy/setchecksum/brick */ if ((cmd == cmd_copy) || (cmd == cmd_setchecksum) || (cmd == cmd_brick)) do_read[part ^ 1] = 0; + /* only allocate one block if only one part being read */ char *buf = malloc(nf << (do_read[0] & do_read[1])); if (buf == NULL) err(errno, NULL); gbe[0] = (size_t) buf; - gbe[1] = gbe[0] + (nf * (do_read[0] & do_read[1])); - ssize_t tnr = 0; - - for (int p = 0; p < 2; p++) { - if (!do_read[p]) - continue; - - ssize_t nr = pread(fd, (uint8_t *) gbe[p], nf, p * partsize); - err_if(nr == -1); - if (nr != nf) - err(errno == ECANCELED, - "%ld bytes read from '%s', expected %ld bytes\n", - nr, filename, nf); + /* speedhack: for cmd copy, both pointers are set the same */ + gbe[1] = gbe[0] + (nf * (do_read[0] & do_read[1])); +} - tnr += nr; - swap(p); /* handle big-endian host CPU */ - } +void +readGbe(void) +{ + for (int p = 0; p < 2; p++) + if (do_read[p]) + readGbe_part(p); +} - printf("%ld bytes read from file '%s'\n", tnr, filename); +void +readGbe_part(int p) +{ + if (pread(fd, (uint8_t *) gbe[p], nf, p * partsize) != nf) + err(SET_ERR(ECANCELED), + "Can't read %ld b from '%s' p%d", nf, fname, p); + swap(p); /* handle big-endian host CPU */ } void @@ -262,12 +247,12 @@ parseMacString(const char *strMac, uint16_t *mac) { uint64_t total = 0; if (strnlen(strMac, 20) != 17) - err(errno = EINVAL, "Invalid MAC address string length"); + err(SET_ERR(EINVAL), "Invalid MAC address string length"); for (uint8_t h, i = 0; i < 16; i += 3) { if (i != 15) if (strMac[i + 2] != ':') - err(errno = EINVAL, + err(SET_ERR(EINVAL), "Invalid MAC address separator '%c'", strMac[i + 2]); @@ -275,7 +260,7 @@ parseMacString(const char *strMac, uint16_t *mac) for (int nib = 0; nib < 2; nib++, total += h) { if ((h = hextonum(strMac[i + nib])) > 15) - err(errno = EINVAL, "Invalid character '%c'", + err(SET_ERR(EINVAL), "Invalid character '%c'", strMac[i + nib]); /* If random, ensure that local/unicast bits are set */ @@ -291,9 +276,9 @@ parseMacString(const char *strMac, uint16_t *mac) } if (total == 0) - err(errno = EINVAL, "Invalid MAC (all-zero MAC address)"); + err(SET_ERR(EINVAL), "Invalid MAC (all-zero MAC address)"); if (mac[0] & 1) - err(errno = EINVAL, "Invalid MAC (multicast bit set)"); + err(SET_ERR(EINVAL), "Invalid MAC (multicast bit set)"); } uint8_t @@ -307,8 +292,7 @@ hextonum(char ch) return ch - 'a' + 10; else if ((ch == '?') || (ch == 'x') || (ch == 'X')) return rhex(); /* random hex value */ - else - return 16; /* error: invalid character */ + return 16; /* error: invalid character */ } uint8_t @@ -415,58 +399,100 @@ goodChecksum(int partnum) return 1; fprintf(stderr, "WARNING: BAD checksum in part %d\n", partnum); - errno = ECANCELED; + SET_ERR(ECANCELED); return 0; } -void -writeGbe(void) +uint16_t +word(int pos16, int p) { - ssize_t tnw = 0; - - for (int p = 0; p < 2; p++) { - if ((!nvmPartChanged[p]) || (flags == O_RDONLY)) - continue; - - swap(p); /* swap bytes on big-endian host CPUs */ - ssize_t nw = pwrite(fd, (uint8_t *) gbe[p], nf, p * partsize); - err_if(nw == -1); - if (nw != nf) - err(errno == ECANCELED, - "%ld bytes written to '%s', expected %ld bytes\n", - nw, filename, nf); + check_bounds(pos16, p); + return ((uint16_t *) gbe[p])[pos16]; +} - tnw += nf; +void +setWord(int pos16, int p, uint16_t val16) +{ + check_bounds(pos16, p); + if (((uint16_t *) gbe[p])[pos16] != val16) { + nvmPartChanged[p] = 1; + ((uint16_t *) gbe[p])[pos16] = val16; } +} - if ((flags != O_RDONLY) && (cmd != cmd_dump)) { - if (nvmPartChanged[0] || nvmPartChanged[1]) - printf("The following nvm words were written:\n"); - cmd_dump(); - } +void +check_bounds(int c, int p) +{ + if ((p != 0) && (p != 1)) + err(SET_ERR(EINVAL), "check_bounds: invalid partnum %d", p); + if ((c < 0) || (c > ((nf >> 1) - 1))) + err(SET_ERR(EINVAL), "check_bounds: out of bounds %d", c); +} - if ((!tnw) && (flags != O_RDONLY) && (!errno)) - fprintf(stderr, "No changes needed on file '%s'\n", filename); - else if (tnw) - printf("%ld bytes written to file '%s'\n", tnw, filename); +void +writeGbe(void) +{ + for (int p = 0; p < 2; p++) + if ((nvmPartChanged[p]) && (flags != O_RDONLY)) + writeGbe_part(p); + err_if(close(fd) == -1); +} - if (tnw) - errno = 0; +void +writeGbe_part(int p) +{ + swap(p); /* swap bytes on big-endian host CPUs */ + if(pwrite(fd, (uint8_t *) gbe[p], nf, p * partsize) != nf) + err(SET_ERR(ECANCELED), + "Can't write %ld b to '%s' p%d", nf, fname, p); - err_if(close(fd) == -1); } void swap(int partnum) { - size_t w, x; uint8_t *n = (uint8_t *) gbe[partnum]; - int e = 1; - for (w = NVM_SIZE * ((uint8_t *) &e)[0], x = 1; w < NVM_SIZE; - w += 2, x += 2) { + for (size_t w = NVM_SIZE * ((uint8_t *) &e)[0], x = 1; + w < NVM_SIZE; w += 2, x += 2) { n[w] ^= n[x]; n[x] ^= n[w]; n[w] ^= n[x]; } } + +#ifdef __OpenBSD__ +void +block_unveil(void) +{ + if (flags == O_RDONLY) { + err_if(unveil(fname, "r") == -1); + err_if(unveil(NULL, NULL) == -1); + err_if(pledge("stdio rpath", NULL) == -1); + } else { + err_if(unveil(fname, "rw") == -1); + err_if(unveil(NULL, NULL) == -1); + err_if(pledge("stdio rpath wpath", NULL) == -1); + } +} +#endif + +void +usage(char *util) +{ +#ifdef __OpenBSD__ + err_if(pledge("stdio", NULL) == -1); +#endif + fprintf(stderr, + "Modify Intel GbE NVM images e.g. set MAC\n" + "USAGE:\n" + "%s FILE dump\n" + " %s FILE\n # same as setmac without arg\n" + " %s FILE setmac [MAC]\n" + " %s FILE swap\n" + " %s FILE copy 0|1\n" + " %s FILE brick 0|1\n" + " %s FILE setchecksum 0|1\n", + util, util, util, util, util, util, util); + err(SET_ERR(ECANCELED), "Too few arguments"); +} |
