diff options
| -rw-r--r-- | util/nvmutil/nvmutil.c | 256 |
1 files changed, 181 insertions, 75 deletions
diff --git a/util/nvmutil/nvmutil.c b/util/nvmutil/nvmutil.c index a468fd6a..a2e4ba25 100644 --- a/util/nvmutil/nvmutil.c +++ b/util/nvmutil/nvmutil.c @@ -4,30 +4,32 @@ #include <sys/stat.h> -#include <err.h> #include <errno.h> #include <fcntl.h> +#include <stdarg.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #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 valid_read(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); @@ -43,9 +45,11 @@ static void check_bound(int, int); static void write_gbe(void); static void write_gbe_part(int); static void swap(int); -static void usage(const char *); +static void usage(void); static void err_if(int); -static int set_err(int); +static void err(int, const char *, ...); +static const char *getnvmprogname(void); +static void set_err(int); #define NVM_CHECKSUM 0xBABA #define NVM_CHECKSUM_WORD 0x3F @@ -56,11 +60,13 @@ static int 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; @@ -72,6 +78,7 @@ static int part_modified[2]; static const char *mac = NULL; static const char *rmac = "xx:xx:xx:xx:xx:xx"; static const char *fname = ""; +static const char *argv0; struct op { const char *str; @@ -92,8 +99,10 @@ static void (*cmd)(void) = NULL; int main(int argc, char *argv[]) { + argv0 = argv[0]; if (argc < 2) - usage(argv[0]); + usage(); + reset_global_state(); fname = argv[1]; #ifdef __OpenBSD__ err_if(pledge("stdio rpath wpath unveil", NULL) == -1); @@ -121,33 +130,44 @@ main(int argc, char *argv[]) (*cmd)(); write_gbe(); - err_if(close(fd) == -1); + if (close(fd) == -1) + err(ECANCELED, "Could not close '%s'", fname); + if (close(rfd) == -1) + err(ECANCELED, "Could not close '/dev/urandom'"); err_if((errno != 0) && (cmd != cmd_dump)); return errno ? EXIT_FAILURE : EXIT_SUCCESS; } static void +reset_global_state(void) +{ + mac = NULL; + invert = 0; + part_modified[0] = 0; + part_modified[1] = 0; + fname = ""; + cmd = NULL; +} + +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(set_err(EINVAL), "Too few args on command '%s'", + err(EINVAL, "Too few args on command '%s'", ops[i].str); } } @@ -155,10 +175,8 @@ set_cmd(int argc, char *argv[]) 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 */ @@ -167,21 +185,19 @@ check_cmd_args(int argc, char *argv[]) } else if ((cmd != NULL) && (argc > 3)) { /* user-supplied partnum */ part = argv[3][0] - '0'; if (!((part == 0 || part == 1) && argv[3][1] == '\0')) - err(set_err(EINVAL), "Bad partnum: %s", argv[3]); + err(EINVAL, "Bad partnum: %s", argv[3]); } if (cmd == NULL) - err(set_err(EINVAL), "Bad command"); + err(EINVAL, "Bad command"); } 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; } } @@ -192,9 +208,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); @@ -205,28 +218,18 @@ open_files(void) partsize = st.st_size >> 1; break; default: - err(set_err(ECANCELED), "Invalid file size (not 8/16/128KiB)"); + err(ECANCELED, "Invalid file size (not 8/16/128KiB)"); break; } } static void -checkdir(const char *path) -{ - struct stat st; - if (stat(path, &st) == -1) - err(set_err(ECANCELED), "%s", path); - if (S_ISDIR(st.st_mode)) - err(set_err(EISDIR), "%s", path); -} - -static void xopen(int *f, const char *l, int p, struct stat *st) { if ((*f = open(l, p)) == -1) - err(set_err(ECANCELED), "%s", l); + err(ECANCELED, "%s", l); if (fstat(*f, st) == -1) - err(set_err(ECANCELED), "%s", l); + err(ECANCELED, "%s", l); } static void @@ -255,10 +258,19 @@ 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(set_err(ECANCELED), - "Can't read %d b from '%s' p%d", SIZE_4KB, fname, p); + int retry; + ssize_t rval; + + for (retry = 0; retry < (int) MAX_RETRY_READ; retry++) { + rval = pread(fd, buf + (SIZE_4KB * (p ^ invert)), + SIZE_4KB, ((off_t ) p) * partsize); + + if (valid_read(fname, rval, + (ssize_t) SIZE_4KB, retry, "pread")) { + break; + } + } + swap(p ^ invert); /* handle big-endian host CPU */ } @@ -267,8 +279,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++) @@ -280,29 +292,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(set_err(EINVAL), "Invalid MAC address string length"); + err(EINVAL, "Invalid MAC address string length"); + + (void) 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); + + mac_total = 0; + for (c = 0; c < 3; c++) + mac_total += macbuf[c]; - if (total == 0) - err(set_err(EINVAL), "Invalid MAC (all-zero MAC address)"); + if (mac_total == 0) + err(EINVAL, "Invalid MAC (all-zero MAC address)"); if (macbuf[0] & 1) - err(set_err(EINVAL), "Invalid MAC (multicast bit set)"); + err(EINVAL, "Invalid MAC (multicast bit set)"); } 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); } @@ -316,7 +335,7 @@ check_mac_separator(int mac_pos) if ((separator = mac[mac_pos + 2]) == ':') return; - err(set_err(EINVAL), "Invalid MAC address separator '%c'", separator); + err(EINVAL, "Invalid MAC address separator '%c'", separator); } static void @@ -325,7 +344,7 @@ set_mac_nib(int mac_pos, int nib, uint8_t *h) int byte = mac_pos / 3; if ((*h = hextonum(mac[mac_pos + nib])) > 15) - err(set_err(EINVAL), "Invalid character '%c'", + err(EINVAL, "Invalid character '%c'", mac[mac_pos + nib]); /* If random, ensure that local/unicast bits are set */ @@ -363,12 +382,49 @@ rhex(void) if (!n) { n = sizeof(rnum) - 1; - err_if(read(rfd, (uint8_t *) &rnum, n + 1) == -1); + read_urandom((uint8_t *) 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 < (int) MAX_RETRY_READ; retry++) { + rval = read(rfd, (uint8_t *) rnum, rsize); + if (valid_read("/dev/urandom", rval, rsize, retry, "read")) + break; + } +} + +static int +valid_read(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) { @@ -488,15 +544,21 @@ good_checksum(int partnum) fprintf(stderr, "WARNING: BAD checksum in part %d\n", partnum ^ invert); - (void) set_err(ECANCELED); + set_err(ECANCELED); return 0; } static uint16_t word(int pos16, int p) { + uint16_t rval = 0; check_bound(pos16, p); - return ((uint16_t *) (buf + (SIZE_4KB * p)))[pos16]; + (void) memcpy( + (uint8_t *) &rval, + (uint8_t *) (buf + (SIZE_4KB * p) + (pos16 << 1)), + sizeof(uint16_t) + ); + return rval; } static void @@ -504,16 +566,20 @@ set_word(int pos16, int p, uint16_t val16) { check_bound(pos16, p); part_modified[p] = 1; - ((uint16_t *) (buf + (SIZE_4KB * p)))[pos16] = val16; + (void) memcpy( + (uint8_t *) (buf + (SIZE_4KB * p) + (pos16 << 1)), + (uint8_t *) &val16, + sizeof(uint16_t) + ); } static void check_bound(int c, int p) { if ((p != 0) && (p != 1)) - err(set_err(EINVAL), "check_bound: invalid partnum %d", p); - if ((c < 0) || (c >= (SIZE_4KB >> 1))) - err(set_err(EINVAL), "check_bound: out of bounds %d", c); + err(EINVAL, "check_bound: invalid partnum %d", p); + if ((c < 0) || (c >= (NVM_SIZE >> 1))) + err(EINVAL, "check_bound: out of bounds %d", c); } static void @@ -536,8 +602,8 @@ 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) { - err(set_err(ECANCELED), + SIZE_4KB, ((off_t) p) * partsize) != (ssize_t) SIZE_4KB) { + err(ECANCELED, "Can't write %d b to '%s' p%d", SIZE_4KB, fname, p); } } @@ -567,35 +633,75 @@ swap(int partnum) } static void -usage(const char *util) +usage(void) { + const char *util = getnvmprogname(); + #ifdef __OpenBSD__ - err_if(pledge("stdio", NULL) == -1); + if (pledge("stdio", NULL) == -1) + err(ECANCELED, NULL); #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", + "\t%s FILE dump\n" + "\t%s FILE # same as setmac without [MAC]\n" + "\t%s FILE setmac [MAC]\n" + "\t%s FILE swap\n" + "\t%s FILE copy 0|1\n" + "\t%s FILE brick 0|1\n" + "\t%s FILE setchecksum 0|1\n", util, util, util, util, util, util, util); - err(set_err(ECANCELED), "Too few arguments"); + + err(ECANCELED, "Too few arguments"); } static void err_if(int x) { if (x) - err(set_err(ECANCELED), "%s", fname); + err(ECANCELED, "%s", fname); } -static int +static void +err(int nvm_errval, const char *msg, ...) +{ + va_list args; + + fprintf(stderr, "%s: ", getnvmprogname()); + + va_start(args, msg); + vfprintf(stderr, msg, args); + va_end(args); + + set_err(nvm_errval); + fprintf(stderr, ": %s", strerror(errno)); + + fprintf(stderr, "\n"); + exit(EXIT_FAILURE); +} + +static const char * +getnvmprogname(void) +{ + const char *p; + + if ((argv0 == NULL) || (*argv0 == '\0')) + return ""; + + if ((p = strrchr(argv0, '/'))) + return p + 1; + else + return argv0; +} + +static void set_err(int x) { - errno = errno ? errno : x; - return EXIT_FAILURE; + if (errno) + return; + if (x) + errno = x; + else + errno = ECANCELED; } |
