util/nvmutil: limit rw size on specific commands

i previously had this as a speed optimisation, but
removed it because it wouldn't make any real speed
difference, on most modern file systems / kernels.

however, this also has the dual purpose of ensuring
only what was verified gets written, on operations
that only touch the NVM area, since this relies on
checksum verification.

therefore, i have re-added this feature, but under
the new design of nvmutil. it is done policy-based,
instead of having if/else for specific commands.

Signed-off-by: Leah Rowe <leah@libreboot.org>
This commit is contained in:
Leah Rowe
2026-03-08 23:32:08 +00:00
parent 70da9c3940
commit dbd4d6d84a
+49 -18
View File
@@ -214,6 +214,7 @@ struct commands {
uint8_t arg_part; uint8_t arg_part;
uint8_t chksum_read; uint8_t chksum_read;
uint8_t chksum_write; uint8_t chksum_write;
size_t rw_size; /* within the 4KB GbE part */
}; };
/* /*
@@ -223,12 +224,14 @@ static const struct commands command[] = {
{ CMD_DUMP, "dump", cmd_dump, ARGC_3, { CMD_DUMP, "dump", cmd_dump, ARGC_3,
NO_INVERT, SET_MOD_OFF, NO_INVERT, SET_MOD_OFF,
ARG_NOPART, ARG_NOPART,
SKIP_CHECKSUM_READ, SKIP_CHECKSUM_WRITE }, SKIP_CHECKSUM_READ, SKIP_CHECKSUM_WRITE,
NVM_SIZE },
{ CMD_SETMAC, "setmac", cmd_setmac, ARGC_3, { CMD_SETMAC, "setmac", cmd_setmac, ARGC_3,
NO_INVERT, SET_MOD_OFF, NO_INVERT, SET_MOD_OFF,
ARG_NOPART, ARG_NOPART,
CHECKSUM_READ, CHECKSUM_WRITE }, CHECKSUM_READ, CHECKSUM_WRITE,
NVM_SIZE },
/* /*
* OPTIMISATION: Read inverted, so no copying is needed. * OPTIMISATION: Read inverted, so no copying is needed.
@@ -236,7 +239,8 @@ static const struct commands command[] = {
{ CMD_SWAP, "swap", NULL, ARGC_3, { CMD_SWAP, "swap", NULL, ARGC_3,
PART_INVERT, SET_MOD_BOTH, PART_INVERT, SET_MOD_BOTH,
ARG_NOPART, ARG_NOPART,
CHECKSUM_READ, SKIP_CHECKSUM_WRITE }, CHECKSUM_READ, SKIP_CHECKSUM_WRITE,
GBE_PART_SIZE },
/* /*
* OPTIMISATION: Read inverted, so no copying is needed. * OPTIMISATION: Read inverted, so no copying is needed.
@@ -245,7 +249,8 @@ static const struct commands command[] = {
{ CMD_COPY, "copy", NULL, ARGC_4, { CMD_COPY, "copy", NULL, ARGC_4,
PART_INVERT, SET_MOD_N, PART_INVERT, SET_MOD_N,
ARG_PART, ARG_PART,
CHECKSUM_READ, SKIP_CHECKSUM_WRITE }, CHECKSUM_READ, SKIP_CHECKSUM_WRITE,
GBE_PART_SIZE },
/* /*
* The non-target part will not be read. * The non-target part will not be read.
@@ -253,7 +258,8 @@ static const struct commands command[] = {
{ CMD_BRICK, "brick", cmd_brick, ARGC_4, { CMD_BRICK, "brick", cmd_brick, ARGC_4,
NO_INVERT, SET_MOD_OFF, NO_INVERT, SET_MOD_OFF,
ARG_PART, ARG_PART,
CHECKSUM_READ, SKIP_CHECKSUM_WRITE }, CHECKSUM_READ, SKIP_CHECKSUM_WRITE,
NVM_SIZE },
/* /*
* The non-target part will not be read. * The non-target part will not be read.
@@ -261,7 +267,8 @@ static const struct commands command[] = {
{ CMD_SETCHECKSUM, "setchecksum", cmd_setchecksum, ARGC_4, { CMD_SETCHECKSUM, "setchecksum", cmd_setchecksum, ARGC_4,
NO_INVERT, SET_MOD_OFF, NO_INVERT, SET_MOD_OFF,
ARG_PART, ARG_PART,
SKIP_CHECKSUM_READ, CHECKSUM_WRITE }, SKIP_CHECKSUM_READ, CHECKSUM_WRITE,
NVM_SIZE },
}; };
#define MAX_CMD_LEN 50 #define MAX_CMD_LEN 50
@@ -364,6 +371,7 @@ static void
sanitize_command_index(size_t c) sanitize_command_index(size_t c)
{ {
uint8_t mod_type; uint8_t mod_type;
size_t gbe_rw_size;
check_command_num(c); check_command_num(c);
@@ -384,15 +392,14 @@ sanitize_command_index(size_t c)
mod_type = command[c].set_modified; mod_type = command[c].set_modified;
switch (mod_type) { switch (mod_type) {
case SET_MOD_0: case SET_MOD_0:
case SET_MOD_1: case SET_MOD_1:
case SET_MOD_N: case SET_MOD_N:
case SET_MOD_BOTH: case SET_MOD_BOTH:
case SET_MOD_OFF: case SET_MOD_OFF:
break; break;
default: default:
err(EINVAL, "Unsupported set_mod type: %u", err(EINVAL, "Unsupported set_mod type: %u", mod_type);
mod_type);
} }
check_bin(command[c].invert, "cmd.invert"); check_bin(command[c].invert, "cmd.invert");
@@ -405,6 +412,20 @@ sanitize_command_index(size_t c)
CHECKSUM_READ, "CHECKSUM_READ"); CHECKSUM_READ, "CHECKSUM_READ");
check_enum_bin(SKIP_CHECKSUM_WRITE, "SKIP_CHECKSUM_WRITE", check_enum_bin(SKIP_CHECKSUM_WRITE, "SKIP_CHECKSUM_WRITE",
CHECKSUM_WRITE, "CHECKSUM_WRITE"); CHECKSUM_WRITE, "CHECKSUM_WRITE");
gbe_rw_size = command[c].rw_size;
switch (gbe_rw_size) {
case GBE_PART_SIZE:
case NVM_SIZE:
break;
default:
err(EINVAL, "Unsupported rw_size: %zu", gbe_rw_size);
}
if (gbe_rw_size > GBE_PART_SIZE)
err(EINVAL, "rw_size larger than GbE part: %zu",
gbe_rw_size);
} }
static void static void
@@ -614,11 +635,16 @@ read_gbe_file(void)
static void static void
read_gbe_file_part(size_t p) read_gbe_file_part(size_t p)
{ {
size_t gbe_rw_size = command[cmd_index].rw_size;
void *mem_offset = void *mem_offset =
gbe_mem_offset(p ^ command[cmd_index].invert, "pread"); gbe_mem_offset(p ^ command[cmd_index].invert, "pread");
read_file_exact(gbe_fd, mem_offset, read_file_exact(gbe_fd, mem_offset,
GBE_PART_SIZE, gbe_file_offset(p, "pread"), fname, "pread"); gbe_rw_size, gbe_file_offset(p, "pread"), fname, "pread");
printf("Read %zu bytes from part %zu: %s\n",
gbe_rw_size, p, fname);
} }
static void static void
@@ -1038,16 +1064,21 @@ write_gbe_file_part(size_t p)
{ {
int retry; int retry;
ssize_t rval; ssize_t rval;
size_t gbe_rw_size;
if (gbe_fd == -1) if (gbe_fd == -1)
err(ECANCELED, "Trying to write bad gbe_fd: %s", fname); err(ECANCELED, "Trying to write bad gbe_fd: %s", fname);
gbe_rw_size = command[cmd_index].rw_size;
for (retry = 0; retry < MAX_RETRY_RW; retry++) { for (retry = 0; retry < MAX_RETRY_RW; retry++) {
rval = pwrite(gbe_fd, gbe_mem_offset(p, "pwrite"), rval = pwrite(gbe_fd, gbe_mem_offset(p, "pwrite"),
GBE_PART_SIZE, gbe_file_offset(p, "pwrite")); gbe_rw_size, gbe_file_offset(p, "pwrite"));
if (rval == GBE_PART_SIZE) { if (rval == (ssize_t)gbe_rw_size) {
errno = 0; errno = 0;
printf("Wrote %zu bytes to part %zu: %s\n",
gbe_rw_size, p, fname);
return; return;
} }