diff options
| -rw-r--r-- | util/nvmutil/nvmutil.c | 264 |
1 files changed, 186 insertions, 78 deletions
diff --git a/util/nvmutil/nvmutil.c b/util/nvmutil/nvmutil.c index ca7f328f..d775f116 100644 --- a/util/nvmutil/nvmutil.c +++ b/util/nvmutil/nvmutil.c @@ -13,21 +13,24 @@ #include <string.h> #include <unistd.h> +static void reset_global_state(void); static void set_cmd(int, char **); static void check_cmd_args(int, char **); static void set_io_flags(int, char **); static void open_files(void); -static void checkdir(const char *); static void xopen(int *, const char *, int, struct stat *); static void read_gbe(void); static void read_gbe_part(int, int); static void cmd_setmac(void); static void parse_mac_string(void); -static void set_mac_byte(int, uint64_t *); +static void set_mac_byte(int); static void check_mac_separator(int); static void set_mac_nib(int, int, uint8_t *); static uint8_t hextonum(char); static uint8_t rhex(void); +static void read_urandom(uint8_t *, size_t); +static int check_read_or_die(const char *, + ssize_t, size_t, int, const char *); static int write_mac_part(int); static void cmd_dump(void); static void print_mac_address(int); @@ -44,7 +47,6 @@ static void write_gbe(void); static void write_gbe_part(int); static void swap(int); static void usage(void); -static void err_if(int); static void err(int, const char *, ...); static const char *getnvmprogname(void); static void set_err(int); @@ -58,11 +60,13 @@ static void set_err(int); #define SIZE_16KB 0x4000 #define SIZE_128KB 0x20000 +#define MAX_RETRY_READ 200 + #define items(x) (sizeof((x)) / sizeof((x)[0])) static uint8_t buf[SIZE_8KB]; static uint16_t macbuf[3]; -static size_t partsize; +static off_t partsize; static int flags; static int rfd; @@ -98,72 +102,105 @@ main(int argc, char *argv[]) argv0 = argv[0]; if (argc < 2) usage(); + + reset_global_state(); fname = argv[1]; + #ifdef __OpenBSD__ - err_if(pledge("stdio rpath wpath unveil", NULL) == -1); - err_if(unveil("/dev/urandom", "r") == -1); + if (pledge("stdio rpath wpath unveil", NULL) == -1) + err(ECANCELED, "pledge"); + if (unveil("/dev/urandom", "r") == -1) + err(ECANCELED, "unveil '/dev/urandom'"); #endif + set_cmd(argc, argv); check_cmd_args(argc, argv); set_io_flags(argc, argv); + #ifdef __OpenBSD__ if (flags == O_RDONLY) { - err_if(unveil(fname, "r") == -1); - err_if(unveil(NULL, NULL) == -1); - err_if(pledge("stdio rpath", NULL) == -1); + if (unveil(fname, "r") == -1) + err(ECANCELED, "unveil ro '%s'", fname); + if (unveil(NULL, NULL) == -1) + err(ECANCELED, "unveil block (ro)"); + if (pledge("stdio rpath", NULL) == -1) + err(ECANCELED, "pledge ro (kill unveil)"); } else { - err_if(unveil(fname, "rw") == -1); - err_if(unveil(NULL, NULL) == -1); - err_if(pledge("stdio rpath wpath", NULL) == -1); + if (unveil(fname, "rw") == -1) + err(ECANCELED, "unveil rw '%s'", fname); + if (unveil(NULL, NULL) == -1) + err(ECANCELED, "unveil block (rw)"); + if (pledge("stdio rpath wpath", NULL) == -1) + err(ECANCELED, "pledge rw (kill unveil)"); } #endif + open_files(); + #ifdef __OpenBSD__ - err_if(pledge("stdio", NULL) == -1); + if (pledge("stdio", NULL) == -1) + err(ECANCELED, "pledge stdio (main)"); #endif + read_gbe(); (*cmd)(); write_gbe(); - err_if(close(fd) == -1); - err_if(close(rfd) == -1); + if (close(fd) == -1) + err(ECANCELED, "close '%s'", fname); + if (close(rfd) == -1) + err(ECANCELED, "close '/dev/urandom'"); + + if (cmd != cmd_dump) { + if (errno) + err(ECANCELED, "Unhandled error on exit"); + } - err_if((errno != 0) && (cmd != cmd_dump)); - return errno ? EXIT_FAILURE : EXIT_SUCCESS; + if (errno) + return EXIT_FAILURE; + else + return EXIT_SUCCESS; +} + +static void +reset_global_state(void) +{ + mac = NULL; + invert = 0; + part_modified[0] = 0; + part_modified[1] = 0; + fname = ""; + cmd = NULL; + fd = 0; + part = 0; } static void set_cmd(int argc, char *argv[]) { size_t i; - const char *arg_cmd; if (argc == 2) { cmd = cmd_setmac; return; } - arg_cmd = argv[2]; - for (i = 0; (i < items(ops)) && (cmd == NULL); i++) { - if (strcmp(arg_cmd, ops[i].str) != 0) + if (strcmp(argv[2], ops[i].str) != 0) continue; if (argc >= ops[i].args) { cmd = ops[i].cmd; break; } - err(EINVAL, "Too few args on command '%s'", - ops[i].str); + err(EINVAL, "Too few args: command '%s'", ops[i].str); } } static void check_cmd_args(int argc, char *argv[]) { - const char *arg_cmd = argv[2]; - if ((cmd == NULL) && (argc > 2)) { /* nvm gbe [MAC] */ - mac = arg_cmd; + mac = argv[2]; cmd = cmd_setmac; } else if (cmd == cmd_setmac) { /* nvm gbe setmac [MAC] */ mac = rmac; /* random MAC */ @@ -182,11 +219,9 @@ check_cmd_args(int argc, char *argv[]) static void set_io_flags(int argc, char *argv[]) { - const char *arg_cmd = argv[2]; - flags = O_RDWR; if (argc > 2) { - if (strcmp(arg_cmd, "dump") == 0) + if (strcmp(argv[2], "dump") == 0) flags = O_RDONLY; } } @@ -197,9 +232,6 @@ open_files(void) struct stat st; struct stat st_rfd; - checkdir("/dev/urandom"); - checkdir(fname); - xopen(&rfd, "/dev/urandom", O_RDONLY, &st_rfd); xopen(&fd, fname, flags, &st); @@ -210,22 +242,12 @@ open_files(void) partsize = st.st_size >> 1; break; default: - err(ECANCELED, "Invalid file size (not 8/16/128KiB)"); + err(ECANCELED, "File size must be 8KB, 16KB or 128KB"); break; } } static void -checkdir(const char *path) -{ - struct stat st; - if (stat(path, &st) == -1) - err(ECANCELED, "%s", path); - if (S_ISDIR(st.st_mode)) - err(EISDIR, "%s", path); -} - -static void xopen(int *f, const char *l, int p, struct stat *st) { if ((*f = open(l, p)) == -1) @@ -260,10 +282,17 @@ read_gbe(void) static void read_gbe_part(int p, int invert) { - if (pread(fd, buf + (SIZE_4KB * (p ^ invert)), SIZE_4KB, p * partsize) - != SIZE_4KB) - err(ECANCELED, - "Can't read %d b from '%s' p%d", SIZE_4KB, fname, p); + int retry; + ssize_t rval; + + for (retry = 0; retry < MAX_RETRY_READ; retry++) { + rval = pread(fd, buf + (SIZE_4KB * (p ^ invert)), + SIZE_4KB, ((off_t ) p) * partsize); + + if (check_read_or_die(fname, rval, SIZE_4KB, retry, "pread")) + break; + } + swap(p ^ invert); /* handle big-endian host CPU */ } @@ -272,8 +301,8 @@ cmd_setmac(void) { int partnum; int mac_updated = 0; - parse_mac_string(); + parse_mac_string(); printf("MAC address to be written: %s\n", mac); for (partnum = 0; partnum < 2; partnum++) @@ -285,29 +314,36 @@ cmd_setmac(void) static void parse_mac_string(void) { + size_t c; int mac_pos; - uint64_t total = 0; + uint64_t mac_total; if (strnlen(mac, 20) != 17) - err(EINVAL, "Invalid MAC address string length"); + err(EINVAL, "MAC address is the wrong length"); + + memset(macbuf, 0, sizeof(macbuf)); for (mac_pos = 0; mac_pos < 16; mac_pos += 3) - set_mac_byte(mac_pos, &total); + set_mac_byte(mac_pos); - if (total == 0) - err(EINVAL, "Invalid MAC (all-zero MAC address)"); + mac_total = 0; + for (c = 0; c < 3; c++) + mac_total += macbuf[c]; + + if (mac_total == 0) + err(EINVAL, "Must not specify all-zeroes MAC address"); if (macbuf[0] & 1) - err(EINVAL, "Invalid MAC (multicast bit set)"); + err(EINVAL, "Must not specify multicast MAC address"); } static void -set_mac_byte(int mac_pos, uint64_t *total) +set_mac_byte(int mac_pos) { int nib; uint8_t h = 0; check_mac_separator(mac_pos); - for (nib = 0; nib < 2; nib++, *total += h) + for (nib = 0; nib < 2; nib++) set_mac_nib(mac_pos, nib, &h); } @@ -368,13 +404,50 @@ rhex(void) if (!n) { n = sizeof(rnum) - 1; - err_if(read(rfd, (uint8_t *) &rnum, sizeof(rnum)) - != sizeof(rnum)); + read_urandom(rnum, sizeof(rnum)); } return rnum[n--] & 0xf; } +static void +read_urandom(uint8_t *rnum, size_t rsize) +{ + int retry = 0; + ssize_t rval; + + for (retry = 0; retry < MAX_RETRY_READ; retry++) { + rval = read(rfd, rnum, rsize); + if (check_read_or_die("/dev/urandom", rval, + rsize, retry, "read")) + break; + } +} + +static int +check_read_or_die(const char *rpath, ssize_t rval, size_t rsize, + int retry, const char *readtype) +{ + if (rval == (ssize_t) rsize) + return 1; /* Successful read */ + + if (rval != -1) + err(ECANCELED, "Short %s, %zd bytes, on file: %s", + readtype, rval, rpath); + if (errno != EINTR) + err(ECANCELED, "Could not %s file: '%s'", readtype, rpath); + if (retry == MAX_RETRY_READ - 1) + err(EINTR, "%s: max retries exceeded on file: %s", + readtype, rpath); + + /* + * Bad read, with errno EINTR (syscall interrupted). + * Reset the error state and try again. + */ + errno = 0; + return 0; +} + static int write_mac_part(int partnum) { @@ -460,24 +533,52 @@ cmd_setchecksum(void) static void cmd_brick(void) { - if (good_checksum(part)) - set_word(NVM_CHECKSUM_WORD, part, - ((word(NVM_CHECKSUM_WORD, part)) ^ 0xFF)); + if (!good_checksum(part)) + err(ECANCELED, "brick p%d, file '%s'", part, fname); + + set_word(NVM_CHECKSUM_WORD, part, + ((word(NVM_CHECKSUM_WORD, part)) ^ 0xFF)); } static void cmd_copy(void) { - err_if(!good_checksum(part ^ 1)); + if (!good_checksum(part ^ 1)) + err(ECANCELED, "copy p%d, file '%s'", part ^ 1, fname); + + /* + * SPEED HACK: + * + * read_gbe() already performed the copy, + * by virtue of inverted read. We need + * only set the other part as changed. + * + * THIS IS NOT A BUG! + */ part_modified[part ^ 1] = 1; } static void cmd_swap(void) { - err_if(!(good_checksum(0) || good_checksum(1))); + 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() already performed the swap, + * by virtue of inverted read. We need + * only set both parts as changed. + * + * THIS IS NOT A BUG! + */ part_modified[1] = part_modified[0] = 1; } @@ -494,6 +595,7 @@ good_checksum(int partnum) fprintf(stderr, "WARNING: BAD checksum in part %d\n", partnum ^ invert); + set_err(ECANCELED); return 0; } @@ -501,16 +603,21 @@ good_checksum(int partnum) static uint16_t word(int pos16, int p) { + uint16_t rval = 0; + check_bound(pos16, p); - return ((uint16_t *) (buf + (SIZE_4KB * p)))[pos16]; + memcpy(&rval, buf + (SIZE_4KB * p) + (pos16 << 1), sizeof(uint16_t)); + + return rval; } static void set_word(int pos16, int p, uint16_t val16) { check_bound(pos16, p); + memcpy(buf + (SIZE_4KB * p) + (pos16 << 1), &val16, sizeof(uint16_t)); + part_modified[p] = 1; - ((uint16_t *) (buf + (SIZE_4KB * p)))[pos16] = val16; } static void @@ -518,7 +625,7 @@ check_bound(int c, int p) { if ((p != 0) && (p != 1)) err(EINVAL, "check_bound: invalid partnum %d", p); - if ((c < 0) || (c >= (SIZE_4KB >> 1))) + if ((c < 0) || (c >= (NVM_SIZE >> 1))) err(EINVAL, "check_bound: out of bounds %d", c); } @@ -542,7 +649,7 @@ write_gbe_part(int p) swap(p); /* swap bytes on big-endian host CPUs */ if (pwrite(fd, buf + (SIZE_4KB * p), - SIZE_4KB, p * partsize) != SIZE_4KB) { + SIZE_4KB, ((off_t) p) * partsize) != SIZE_4KB) { err(ECANCELED, "Can't write %d b to '%s' p%d", SIZE_4KB, fname, p); } @@ -576,8 +683,10 @@ static void usage(void) { const char *util = getnvmprogname(); + #ifdef __OpenBSD__ - err_if(pledge("stdio", NULL) == -1); + if (pledge("stdio", NULL) == -1) + err(ECANCELED, "pledge"); #endif fprintf(stderr, "Modify Intel GbE NVM images e.g. set MAC\n" @@ -590,18 +699,12 @@ usage(void) "\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_if(int x) -{ - if (x) - err(ECANCELED, "%s", fname); + err(ECANCELED, "Too few arguments"); } static void -err(int errval, const char *msg, ...) +err(int nvm_errval, const char *msg, ...) { va_list args; @@ -611,7 +714,7 @@ err(int errval, const char *msg, ...) vfprintf(stderr, msg, args); va_end(args); - set_err(errval ? errval : ECANCELED); + set_err(nvm_errval); fprintf(stderr, ": %s", strerror(errno)); fprintf(stderr, "\n"); @@ -635,5 +738,10 @@ getnvmprogname(void) static void set_err(int x) { - errno = errno ? errno : x; + if (errno) + return; + if (x) + errno = x; + else + errno = ECANCELED; } |
