rflasher reads, writes, and erases SPI flash chips from Rust. It is heavily inspired by flashprog and supports 480+ chips (rflasher list-chips) and most common programmers: CH341A/CH347, Dediprog, FTDI (MPSSE and FT4222H), serprog, Raiden debug hardware, internal Intel/AMD chipset controllers, Allwinner FEL, and Linux spidev/MTD/GPIO. The same codebase also runs in the browser via WebSerial/WebUSB, and the core crates are no_std for reuse in firmware.
Only SPI NOR flash is in scope.
⚠️ Alpha softwarerflasher is not ready for production use. For anything critical, use flashprog instead.
Before writing: make sure the programmer voltage matches the chip (3.3 V for most SPI flash), check write protection status, and always read a backup first. Corrupting the wrong region (e.g. the Intel ME region) can brick the device permanently.
Build from source (Rust 1.85+):
git clone https://github.com/ArthurHeymans/rflasher
cd rflasher
cargo build --release # default programmers
cargo build --release --features all-programmers # everything
cargo build --release --no-default-features --features ch341a,serprog # pick your own
cargo install --path .USB programmers (CH341A, CH347, FTDI, Dediprog, Raiden) need udev rules for non-root access, e.g.:
# CH341A
SUBSYSTEM=="usb", ATTR{idVendor}=="1a86", ATTR{idProduct}=="5512", MODE="0660", GROUP="plugdev"
# Raiden Debug SPI / Cr50 (Google debug hardware)
# The backend matches on vendor ID alone, so this rule covers all Google
# devices; add ATTR{idProduct} matches if you want it narrower.
SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", MODE="0660", GROUP="plugdev"
Install to /etc/udev/rules.d/, run udevadm control --reload-rules && udevadm trigger, and add your user to the plugdev group (create the group first if your distro doesn't have it).
The man page is generated from the CLI definitions:
cargo run --bin gen-manpage # writes man/rflasher.1
man -l man/rflasher.1rflasher probe -p ch341a # detect the flash chip
rflasher info -p ch341a # detailed chip information
rflasher read -p ch341a backup.bin # read flash to a file
rflasher write -p ch341a firmware.bin # erase, write and verify
rflasher erase -p ch341a
rflasher verify -p ch341a firmware.bin
rflasher list-chips # supported chips
rflasher list-programmers # available programmersShort aliases exist (r/w/v, E for erase), the programmer can be set with the RFLASHER_PROGRAMMER environment variable, and -v/-vv increase log verbosity. rflasher write -p ch341a firmware.bin --no-verify skips verification.
When several connected USB programmers match the -p specification, rflasher lists them (with USB bus/address and serial number) and asks which one to use. Selector options narrow the list, e.g. -p dediprog:id=SF123456 or -p ftdi:serial=FT123456. With --non-interactive, or when stdin or stderr is not a terminal, several matches are an error instead.
| Programmer | Parameters | Example |
|---|---|---|
ch341a |
— | rflasher probe -p ch341a |
ch347 |
— | rflasher probe -p ch347 |
serprog |
dev=, baud rate, or ip= |
rflasher probe -p serprog:dev=/dev/ttyUSB0:115200rflasher probe -p serprog:ip=192.168.1.100:5000 |
dediprog |
spispeed= |
rflasher probe -p dediprog:spispeed=12M |
ftdi |
type=, port=, divisor= |
rflasher probe -p ftdi:type=2232h,port=B,divisor=10 |
ft4222 |
spispeed=, cs= |
rflasher probe -p ft4222:spispeed=20000,cs=0 |
raiden |
— | rflasher probe -p raiden |
internal |
— | rflasher probe -p internal |
ati_spi |
pci=DDDD:BB:DD.F |
Experimental GPU VBIOS access; read the preparation instructions before probing |
linux_spi |
dev=, spispeed= |
rflasher probe -p linux_spi:dev=/dev/spidev0.0,spispeed=4000 |
linux_gpio_spi |
gpiochip=, cs=, sck=, mosi=, miso=, spispeed= |
rflasher probe -p linux_gpio_spi:gpiochip=0,cs=25,sck=11,mosi=10,miso=9 |
linux_mtd |
dev= |
rflasher read -p linux_mtd:dev=0 backup.bin |
sunxi_fel |
— | rflasher probe -p sunxi_fel (Allwinner SoC in FEL mode) |
dummy |
— | in-memory flash emulator for testing |
Run rflasher <command> --help or see the man page for the full option list.
The internal programmer uses the chipset's SPI controller on Linux via PCI sysfs and /dev/mem, so it usually requires root. On Intel PCH100 and newer, the SPI PCI function must be visible and accessible through sysfs, with a validated device ID and platform mapping. If firmware hides it, or kernel permissions deny access, rflasher refuses initialization (including reads); it does not bypass the kernel with direct PCI I/O or write to the LPC bridge instead. Where PCI reads are allowed but write access is denied, read-only operation may still be possible. Its controller code is no_std and can be reused in firmware; see crates/rflasher-internal.
Chips are defined as RON files in crates/rflasher-chips/data/vendors/ and compiled into the CLI by default, so installed binaries work without the source tree or separate data files. --chip-db <path> replaces the bundled database with a RON file or a directory of RON files.
JEDEC IDs are not unique, so a probed chip can match several definitions. Identical operational definitions are interchangeable. Otherwise validated SFDP data narrows the candidates, and unresolved aliases use only their common erase/program/read capabilities. Contradictory sizes or essential program/addressing procedures still require explicit selection with -c/--chip NAME. A synthesized common profile disables write protection when candidates disagree on its register layout. Names use flashprog's pattern syntax but, unlike flashprog, match regardless of case, so type the part number printed on the chip: database names may contain alternatives (A/B), optional parts (GD25Q64(B)) and . wildcards (W25Q128.V matches W25Q128FV):
rflasher probe -p ch341a -c EN25P40If the selected definition contradicts the chip's SFDP data on size, or on a page size larger than the chip's, rflasher refuses to operate on it. Pick the right definition with --chip, or override with --force if the SFDP data is known to be wrong.
Authorizations are independent:
--allow-dangerous-regionsacknowledges modifying ME/TXE/IE, the flash descriptor, or PTT. The shared core preflight detects IFD regions on the device even without--ifdor a supplied layout; if the on-device descriptor is unusable (damaged, or describing a second chip), only this flag lets the operation proceed. Prefer selecting only BIOS instead.--forceoverrides contradictory chip/SFDP size or page geometry; it does not authorize dangerous regions or execute disputed erase opcodes. Opaque hwseq/MTD devices reject irrelevant--chip/--forceoptions.
Erase blocks can straddle selected region boundaries. The neighboring data is then read first, restored after the erase and verified; erasing into a dangerous or read-only region is refused as above. --recovery-backup FILE additionally writes and synchronizes a full-device image before any mutation (it refuses to overwrite an existing file), which covers power loss in the middle of such an operation. To recover, use it as the input image with dangerous-region authorization if required. Library users supply a RecoveryBackup through MutationPolicy.
The web UI offers chip selection and separate SFDP/region acknowledgements, with one-operation dangerous-region authorization. SFDP-only chips have no documented protection-register layout, so WP mutation is unavailable rather than guessed.
Intel Flash Descriptor (IFD) and FMAP layouts let you operate on specific flash regions (BIOS, ME, GbE, ...).
--region/--include accept NAME[:FILE]. With a FILE, each region reads to / writes from its own file instead of a full chip image, and the positional file can be omitted. Per-region files must not exceed their region's size; a smaller file covers only the start of the region. When a positional file is combined with per-region files, it must be a full chip image and supplies the data for regions without their own file.
Read destinations are opened and checked before reading flash, without truncating existing files. If validation or the flash read fails, newly created empty destination files remain in place. Existing regular files are truncated only after the read succeeds; devices and pipes can also be used as destinations.
# Extract a layout from an image
rflasher layout ifd flash.bin -o layout.toml
rflasher layout fmap chromebook.bin -o layout.toml
rflasher layout show layout.toml
rflasher layout create custom.toml --size "16 MiB"
# Read the BIOS region into its own file (IFD parsed from the chip)
rflasher read -p ch341a --ifd -r bios:bios.bin
# Read several regions, each to its own file
rflasher read -p ch341a --ifd --include bios:bios.bin,me:me.bin
# Write a region from its own file
rflasher write -p ch341a --layout layout.toml -r bios:bios_update.bin
# Or write a region out of a full chip image
rflasher write -p ch341a --ifd -r bios full_image.binrflasher wp status -p ch341a # current protection status
rflasher wp list -p ch341a # available protection ranges
rflasher wp enable -p ch341a # hardware protection (WP# pin)
rflasher wp disable -p ch341a
rflasher wp range -p ch341a 0,0x100000 # protect start,length
rflasher wp region -p ch341a --ifd bios # protect a named region
rflasher wp disable -p ch341a --temporary # until the next power cycle onlyChanges are persistent by default. --temporary uses the chip's volatile status register write (EWSR, 0x50) and fails on chips without one.
Erase/write operations temporarily remove documented software protection and restore it on success or failure, including Dediprog bulk writes. Hardware and irreversible locks are not bypassed. Unknown/SFDP-only chips never receive guessed status-register writes, and erase verification remains mandatory. Do not interrupt a mutation: power loss or cancellation can prevent restoration.
Automatic unlocking does not imply support for wp configuration. Those commands require an explicitly audited register map, range decoder and write procedure; other chips report unsupported rather than using Winbond's layout. See chip protection procedures for coverage and limitations.
A browser-based UI (egui) can drive a programmer straight from the browser: serprog over WebSerial, and CH341A, CH347, FTDI, FT4222H, Dediprog, and Raiden over WebUSB. Both APIs require Chrome/Edge (or Opera); Firefox and Safari support neither, and a secure context (HTTPS or localhost) is mandatory.
cargo install trunk
rustup target add wasm32-unknown-unknown
cd crates/rflasher-wasm
trunk serve # dev server with auto-reload on http://localhost:8080
trunk build --releaseSee crates/rflasher-wasm/README.md for details.
An experimental Steel Scheme REPL can script raw SPI commands (build with --features repl):
cargo build --release --features repl
rflasher repl -p serprog:dev=/dev/ttyACM0λ > (read-jedec-id)
=> (239 16389)
λ > (bytes->hex (spi-read READ 0 16))
=> "ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff"
λ > (define data (make-bytes 256 #xAA))
λ > (write-enable)
=> #t
λ > (spi-write PP #x1000 data)
=> #t
λ > (wait-ready)
=> #tType (rflasher-help) in the REPL for the full command list.
| Crate | Purpose |
|---|---|
rflasher-chip-types |
no_std SPI NOR chip data model and provider trait |
rflasher-core |
no_std SPI protocol, probing, and flash operations (async, runtime-neutral) |
rflasher-chips |
RON loading and compiled chip database provider |
rflasher-chips-codegen |
build-time generator for the compiled chip database |
rflasher-programmers |
feature-gated programmer backends and registry |
rflasher-internal |
internal chipset SPI controllers, no_std-capable |
rflasher-pci |
no_std PCI configuration-space abstraction |
rflasher-repl |
Steel Scheme scripting |
rflasher-wasm |
browser UI (egui, WebSerial, WebUSB) |
All operational APIs are async and executor-independent on every target: the CLI blocks once in main, the browser event loop drives the same code over WebUSB/WebSerial. See docs/architecture.md for the design details.
- Find the datasheet for your chip.
- Create or update the vendor file under
crates/rflasher-chips/data/vendors/. - Add the chip definition with JEDEC ID, size, erase blocks, and features.
- Submit a pull request.
(
name: "W25Q128.V",
device_id: 0x4018,
total_size: MiB(16),
features: (
wrsr_wren: true,
fast_read: true,
quad_io: true,
),
voltage: (min: 2700, max: 3600),
erase_blocks: [
(opcode: 0x20, size: KiB(4)),
(opcode: 0xD8, size: KiB(64)),
(opcode: 0xC7, size: MiB(16)),
],
tested: (probe: Ok, read: Ok, erase: Ok, write: Ok, wp: Ok),
)- Add a module under
crates/rflasher-programmers/src/backends/. - Implement
SpiMasterfromrflasher-core(overridingread_flash/program_flashif the hardware has a faster flash path), orFlashDeviceif it has no raw SPI access. - Add a feature and optional dependencies in
crates/rflasher-programmers/Cargo.toml. - Register the programmer in
rflasher-programmers/src/registry.rs. - Update the CLI/WASM feature forwarding.
The largest remaining gap is the SPI programmers not yet ported from flashprog.
Licensed under either the MIT license or the Apache License, Version 2.0, at your option.
Third-party components retain their own licenses and notices, including the MIT-licensed xfel payloads.
rflasher is heavily inspired by flashprog, itself a fork of flashrom, and much of its chip database and programmer support derives from them. Thanks to all contributors of those projects.

