Control: tags -1 + patch
Control: found -1 0~R106-15054.B+dfsg-0.1
Independently hit this on real hardware; confirming the analysis in the
original report and attaching a tested patch.
On ARM Chromebooks running Debian, crossystem cannot write any vboot
NV flag. Every write fails like this (observed on a Lenovo IdeaPad
Slim 3 Chrome 14M868, ChromeOS board "magneton", device tree
compatible google,steelix-sku393218 / mediatek,mt8186, "corsola"
family, running Debian testing):
root@trotamar3:~# crossystem dev_boot_usb=1
Flashrom invocation failed (exit status 1): flashrom -p host -r -i
RW_NVRAM:/tmp/vb2_flashrom.MVAiey
Failed to set parameter dev_boot_usb
The same failure affects "crossystem disable_dev_request=1" and every
other NV flag write, which breaks developer-mode workflows for Debian
on these machines.
Root cause
----------
On this platform the vboot NV data lives in the SPI flash FMAP region
RW_NVRAM. crossystem (host/lib/crossystem.c via host/lib/flashrom.c)
executes the flashrom binary using the conventions of the ChromiumOS
flashrom fork:
* programmer alias "-p host", which does not exist in upstream
flashrom (the fork's cros_alias.c dispatches it to its internal
programmer, which tries the Linux MTD interface first);
* an implicit FMAP layout for "-i RW_NVRAM": upstream flashrom only
honours "-i" together with an explicit layout source, e.g. --fmap.
Debian ships upstream flashrom, so the invocation fails before it
even touches the hardware. The following manual invocation was
verified to work on the machine above (the AP SPI-NOR is exposed as
/dev/mtd0, and the region contains the expected 16-byte vb2 nvdata
ring):
flashrom -p linux_mtd:dev=0 --fmap -r -i RW_NVRAM:/tmp/nvram.bin
This is the same root cause as reported in #1089611; the present
report adds analysis and a tested patch for the crossystem NV data
path.
Fix
---
The patch below makes the flashrom invocations in
host/lib/flashrom.c compatible with upstream flashrom:
* translate exactly the "host" programmer alias to "linux_mtd" at
invocation time; any other programmer string is passed through
untouched;
* pass --fmap whenever a region is accessed with -i.
The translation is deliberately narrow. In this source tree the
flash-backed NV data path is only ever reached from the ARM backend
(host/arch/arm/lib/crossystem_arch.c), and only when the firmware
device tree explicitly declares nonvolatile-context-storage = "flash";
the x86 backends keep NV data in CMOS/ACPI and never call this code.
On the machines that do reach it, the AP flash is exposed as an MTD
device, which is what upstream's linux_mtd programmer drives (it
defaults to mtd0 and refuses devices that are not NOR flash). There
is intentionally no fallback to upstream's "internal" programmer and
no runtime guessing between flash access methods: if no suitable MTD
device exists, flashrom fails cleanly instead of picking another
flash chip to write to.
The mocked flashrom argument parser in the unit test
(tests/vb2_host_flashrom_tests.c) has to learn the new --fmap
argument, since debian/rules runs the test suite during build; the
patch extends the mock (and terminates its getopt_long() option
array, which was missing the required all-zero sentinel) and asserts
that region accesses now request the FMAP layout.
With this change, crossystem NV flag reads and writes work on the
machine above with flashrom 1.6.0. Note that writing a region
without supplying a full chip image ("-w --fmap -i REGION:file")
requires the optional -r/-w/-v filename support introduced in
upstream flashrom 1.5, so the vboot-utils dependency on flashrom
should probably become versioned, flashrom (>= 1.5~); the flashrom
version in trixie (1.4.0) still requires a full image for -w and
would only gain working reads from this patch.
Long term, the proper fix is syncing the package to a newer upstream
snapshot: current vboot_reference no longer executes the flashrom
binary at all, it uses libflashrom (host/lib/flashrom_drv.c) and
reads the FMAP from flash, so the fork-specific conventions are gone.
The patch below is a minimal interim fix for the packaged snapshot.
The patch applies on top of the current debian/patches series; the
affected unit test (vb2_host_flashrom_tests) passes with it.
Regards,
Daniel
Description: make crossystem flashrom calls work with upstream flashrom
crossystem reads and writes the vboot NV data by executing the flashrom
binary, using conventions of the ChromiumOS flashrom fork: the "host"
programmer alias and an implicit FMAP layout for -i REGION accesses.
Debian ships upstream flashrom, which has no "host" programmer and
requires --fmap (or an explicit layout) for region access, so every
NV flag write, e.g. "crossystem dev_boot_usb=1", fails with
"Flashrom invocation failed".
.
Translate exactly the "host" programmer alias to "linux_mtd" at
invocation time; any other programmer string is passed through
untouched. The NV-data-in-flash path is only ever taken by the ARM
backend (host/arch/arm/lib/crossystem_arch.c), and only when the
firmware device tree explicitly declares nonvolatile-context-storage =
"flash"; on those machines the AP flash is exposed as an MTD device.
Deliberately no fallback to upstream's "internal" programmer: if no
suitable MTD device exists, upstream flashrom fails cleanly instead of
guessing at a flash target. Pass --fmap whenever a region is accessed.
Teach the mocked flashrom argument parser in the unit test about the
new --fmap argument (and terminate its getopt_long() option array).
.
Verified on a Lenovo IdeaPad Slim 3 Chrome 14M868
(google,steelix-sku393218, mediatek,mt8186) running Debian testing.
Newer upstream vboot_reference no longer executes the flashrom binary
but uses libflashrom (host/lib/flashrom_drv.c) and reads the FMAP from
flash, so this patch can be dropped when the package moves to a newer
upstream snapshot.
Author: Daniel Golle <[email protected]>
Bug-Debian: https://bugs.debian.org/1089611
Forwarded: no
Last-Update: 2026-07-20
---
--- a/host/lib/flashrom.c
+++ b/host/lib/flashrom.c
@@ -85,6 +85,14 @@
return rv;
}
+static const char *resolve_programmer(const char *programmer)
+{
+ if (strcmp(programmer, FLASHROM_PROGRAMMER_INTERNAL_AP) != 0)
+ return programmer;
+
+ return "linux_mtd";
+}
+
static vb2_error_t run_flashrom(const char *const argv[])
{
int status = subprocess_run(argv, &subprocess_null, &subprocess_null,
@@ -121,9 +129,10 @@
const char *const argv[] = {
FLASHROM_EXEC_NAME,
"-p",
- image->programmer,
+ resolve_programmer(image->programmer),
"-r",
- region ? "-i" : tmpfile,
+ region ? "--fmap" : tmpfile,
+ region ? "-i" : NULL,
region ? region_param : NULL,
NULL,
};
@@ -152,10 +161,11 @@
const char *const argv[] = {
FLASHROM_EXEC_NAME,
"-p",
- image->programmer,
+ resolve_programmer(image->programmer),
"--noverify-all",
"-w",
- region ? "-i" : tmpfile,
+ region ? "--fmap" : tmpfile,
+ region ? "-i" : NULL,
region ? region_param : NULL,
NULL,
};
--- a/tests/vb2_host_flashrom_tests.c
+++ b/tests/vb2_host_flashrom_tests.c
@@ -35,6 +35,7 @@
static const char *captured_op_filename;
static const char *captured_region_param;
static const char *captured_programmer;
+static int captured_fmap;
static uint8_t *captured_rom_contents;
static uint32_t captured_rom_size;
@@ -63,6 +64,13 @@
.flag = &captured_verify_int,
.val = FLASHROM_VERIFY_FAST,
},
+ {
+ .name = "fmap",
+ .has_arg = no_argument,
+ .flag = &captured_fmap,
+ .val = 1,
+ },
+ { 0 },
};
/* Reset static variables to their defaults. */
@@ -71,6 +79,7 @@
captured_op_filename = NULL;
captured_region_param = NULL;
captured_programmer = NULL;
+ captured_fmap = 0;
captured_rom_contents = NULL;
captured_rom_size = 0;
optind = 0;
@@ -183,6 +192,7 @@
"Not doing a read of the whole ROM");
TEST_STR_EQ(captured_region_param, "SOME_REGION:" MOCK_TMPFILE_NAME,
"Reading to correct file and from correct region");
+ TEST_EQ(captured_fmap, 1, "Using FMAP layout");
TEST_EQ(image.size, strlen(MOCK_ROM_CONTENTS), "Contents correct size");
TEST_SUCC(memcmp(image.data, MOCK_ROM_CONTENTS, image.size),
"Buffer has correct contents");
@@ -251,6 +261,7 @@
"Not doing a write of the whole ROM");
TEST_STR_EQ(captured_region_param, "SOME_REGION:" MOCK_TMPFILE_NAME,
"Writing to correct file and from correct region");
+ TEST_EQ(captured_fmap, 1, "Using FMAP layout");
TEST_EQ(captured_rom_size, strlen(MOCK_ROM_CONTENTS),
"Contents correct size");
TEST_SUCC(memcmp(captured_rom_contents, MOCK_ROM_CONTENTS,