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.c294
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");
+}