A reboot mode is a name such as recovery or bootloader that userspace passes to reboot(); the kernel turns it into a magic number the bootloader reads after reset to choose which image to start. If the name does not match one the kernel parsed from your device tree, that write is skipped and the board reboots normally, with no error and no kernel log message. An update that should have restarted into recovery just does not, and nothing on the device records why. Linux 7.1 added a sysfs file listing the reboot modes a board supports, but no longterm kernel carries it, so for now the check has to be made against the device tree.
If your product can reboot into recovery, into fastboot, or into a bootloader download mode, it depends on one of your board’s reboot modes matching by name, and nothing in the system checks that for you. On a fleet device with no console attached, a mismatch surfaces months later as a support ticket saying the update sometimes does not roll back, by which point there is no evidence left to work from. The mechanism is worth understanding whatever kernel you are on, because the device tree contract it rests on is the one your current kernel already uses.
Where reboot modes come from
The framework lives in drivers/power/reset/reboot-mode.c. It carries one small piece of information across a reset: a number written into a register or a flash cell that survives the reboot, which the bootloader reads on the way back up to decide whether to start the normal system, a recovery image, or a firmware download mode.
The names and the numbers come from the device tree. Any property whose name begins with mode- is treated as one of the board’s reboot modes. The text after the prefix is the name userspace passes; the property value is the magic number the bootloader sees. The generic binding’s own example, which uses the Rockchip constants, looks like this:
reboot-mode {
compatible = "syscon-reboot-mode";
offset = <0x40>;
mode-normal = <BOOT_NORMAL>;
mode-recovery = <BOOT_RECOVERY>;
mode-bootloader = <BOOT_FASTBOOT>;
mode-loader = <BOOT_BL_DOWNLOAD>;
};Those constants come from include/dt-bindings/soc/rockchip,boot-mode.h, where a tag of 0x5242C300 occupies the upper 24 bits and a small type value occupies the lower eight: BOOT_NORMAL is the tag plus 0, BOOT_BL_DOWNLOAD is plus 1, BOOT_RECOVERY is plus 3, and BOOT_FASTBOOT is plus 9.
At probe time the driver calls devm_reboot_mode_register(). The core walks the properties, builds a list of name-and-magic pairs, and registers a reboot notifier. On restart that notifier looks up the command string and, if it matches, calls the driver’s write() callback — for syscon a single regmap_update_bits() against the register at offset, masked by mask; the NVMEM driver writes the value into a named cell instead.
Note what that implies. The four reboot modes above are named entirely by the board’s device tree, and another vendor might call the same concept fastboot, or download, or edl. There is no standard list, and until recently no way for a program to ask.
A wrong string is not an error. It is a normal reboot.
This is the part that makes guessing expensive. Here is the notifier, unchanged in substance since the framework was written:
static int reboot_mode_notify(struct notifier_block *this,
unsigned long mode, void *cmd)
{
struct reboot_mode_driver *reboot;
unsigned int magic;
reboot = container_of(this, struct reboot_mode_driver, reboot_notifier);
magic = get_reboot_mode_magic(reboot, cmd);
if (magic)
reboot->write(reboot, magic);
return NOTIFY_DONE;
}get_reboot_mode_magic() walks the list looking for a string match and returns 0 when it finds none, and the guard if (magic) then skips the write. The notifier chain continues, the machine resets, the bootloader reads whatever value the register already held, and the board comes up in the normal system.
So an update agent that runs this:
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/reboot.h>
/* The mode string below is a guess. Nothing checks it. */
syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_RESTART2, "recovery-mode");behaves exactly like a plain reboot when the device tree spells the mode recovery rather than recovery-mode. The raw syscall is needed because the C library’s reboot() wrapper takes only the command and has no parameter for the string. The call does not return on success, so there is no return value to test. The device reboots into the running system and your rollback never happens. Which component is entitled to decide that an update worked, and on what evidence, is the adjacent problem, and we worked through it separately in Boot Confirmation: Who Decides an Update Worked?
The lookup makes one allowance, added in 2025. If the exact match fails, the core retries after replacing spaces, commas and forward slashes with hyphens, because those characters cannot appear in a device tree property name, so recovery,boot still finds mode-recovery-boot. That covers punctuation. It does not cover a word that is simply wrong, which is the case that actually occurs.
The second silent case: a magic value of zero
There is a quieter variant of the same problem, and it is a property of the interface rather than of any board. Because the guard tests the magic value rather than whether a match was found, the code cannot distinguish “no mode of that name” from “a mode of that name whose magic value is zero”. A mode declared as mode-normal = <0> is a legitimate device tree entry, the lookup finds it and returns its value, and the write is skipped anyway.
Vendors avoid this by putting a non-zero tag in the upper bits of every magic value, which is what the Rockchip 0x5242C300 constant is for. If you are choosing magic values for a new board, do the same. Zero is not available to you as a mode value, and nothing in the kernel will say so.
How to read your board’s reboot modes today
The list the framework uses comes from the device tree the kernel actually booted, so that is what to read on any kernel. Start with whether the framework is present:
raghu@techveda.org:~$ zcat /proc/config.gz | grep REBOOT_MODE
CONFIG_REBOOT_MODE=y
CONFIG_SYSCON_REBOOT_MODE=yCONFIG_REBOOT_MODE is a hidden symbol selected by the individual drivers, so you never set it directly. It is a tristate: if it and its driver are built as modules and nothing loaded them, the mechanism is absent on a kernel that nominally supports it.
Then read the names out of the live device tree:
raghu@techveda.org:~$ find /proc/device-tree -name 'mode-*' | sed 's|.*/mode-||' | sort
bootloader
loader
normal
recoveryThree caveats. It depends on /proc/device-tree being mounted, which some minimal images skip. It searches the whole tree rather than the node the driver bound to, so it can match an unrelated mode- property. And it says nothing about whether the driver registered, which is what the configuration check above covers.
For the magic number, read the property itself. Device tree cells are big-endian, so a Rockchip recovery mode reads back as the bytes 52 42 c3 03, which is 0x5242C300 plus 3:
raghu@techveda.org:~$ od -An -tx1 /proc/device-tree/syscon@ff770000/reboot-mode/mode-recovery
52 42 c3 03Substitute your own node path from the find output.
Whatever you find, write it down. The set of reboot modes a board supports is a real part of its interface, and on most products it is undocumented because there was never an obvious place to record it.
What Linux 7.1 added
Commit cfaf0a90789a, by Shivendra Pratap of Qualcomm and applied by Sebastian Reichel in March 2026, creates a device class named reboot-mode and gives every registered driver one read-only attribute under it listing the reboot modes it knows about. For the device tree shown earlier, the result is a file you can read directly:
raghu@techveda.org:~$ ls /sys/class/reboot-mode/
syscon-reboot-mode
raghu@techveda.org:~$ cat /sys/class/reboot-mode/syscon-reboot-mode/reboot_modes
normal recovery bootloader loaderThose are the four names from the mode- properties above. This output is derived from the binding’s example node and the parsing code rather than captured from a board; on your hardware the contents follow your own device tree, in property order, which is not something to depend on.
The directory is named after the driver, not the device, which is why the documentation lists three possible paths — syscon-reboot-mode, nvmem-reboot-mode and qcom-pon — and why you should list the class rather than assume a path.
The show function and its off-by-one
One detail in the implementation will affect anyone who writes a parser.
static ssize_t reboot_modes_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct reboot_mode_sysfs_data *priv;
struct mode_info *sysfs_info;
ssize_t size = 0;
priv = dev_get_drvdata(dev);
if (!priv)
return -ENODATA;
list_for_each_entry(sysfs_info, &priv->head, list)
size += sysfs_emit_at(buf, size, "%s ", sysfs_info->mode);
if (!size)
return -ENODATA;
return size + sysfs_emit_at(buf, size - 1, "\n");
}Each mode is emitted followed by a space, so the last byte written by the loop is a trailing space. The final call writes the newline at index size - 1 rather than after it, overwriting that space — a reasonable way to avoid tracking whether the loop is on its last element.
The return value is where it goes wrong. Trace it for two modes, normal and recovery. The loop writes seven bytes then nine, so size is 16 and the buffer holds normal recovery with a trailing space at index 15. The newline call overwrites index 15 and returns 1, because sysfs_emit_at() returns the number of characters it wrote. The text is now 16 bytes ending in the newline. But the function returns size + 1, which is 17 — the extra byte counted even though the newline replaced a byte instead of adding one.
The sysfs read path commits exactly the length the show function returns, and sysfs_emit_at() places a terminating zero after its output, so the seventeenth byte handed to userspace is that zero. A read gives you the mode list, a newline, and one trailing zero byte, and wc -c reports 17 where the text is 16. This is an off-by-one rather than a convention, so strip trailing whitespace and zero bytes rather than trusting the length you were given. The analysis comes from reading the code and was not reproduced on hardware; confirm it with od -c before relying on it.
The -ENODATA return matters too. If the driver registered with no reboot modes, the file exists but reading it fails, so cat prints an error rather than an empty line. Test the exit status, not the emptiness of the output.
Why a twenty-line patch took twenty-four revisions
The patch reached v24 before it was applied, and for its first seventeen revisions it was one patch inside a larger set adding vendor reset support for PSCI SYSTEM_RESET2, split out on Bjorn Andersson’s suggestion. Two review decisions from that history are visible in the code.
An earlier revision protected the list of reboot modes with a mutex, on the reasonable ground that a read of the sysfs file could race with the list being built or torn down. That mutex was removed, because the class device is created only after the list is fully populated and is unregistered before the list is freed. Device lifetime already provides the ordering, which is why a list exposed to userspace has no locking.
An earlier revision also let driver registration continue when creating the sysfs device failed, treating the interface as optional. Review changed that: reboot_mode_register() now returns the error and probe fails with it. That is the stricter choice, and it has a consequence discussed below.
Which kernels carry the reboot modes file
This decides whether any of the above is available to you, and today it is not. Each tag below was fetched from the stable tree and read directly, not inferred from release dates.
| Series | Tag checked | Status on kernel.org | reboot_modes file |
|---|---|---|---|
| 7.2 | v7.2.6 | Stable, not longterm | Present |
| 7.1 | v7.1.13 | End of life | Present |
| 7.0 | v7.0 | End of life | Absent |
| 6.18 | v6.18.52 | Longterm, EOL Dec 2028 | Absent |
| 6.12 | v6.12.110 | Longterm, EOL Dec 2028 | Absent |
| 6.6 | v6.6.157 | Longterm, EOL Dec 2027 | Absent |
| 6.1 | v6.1.188 | Longterm, EOL Dec 2027 | Absent |
| 5.15 | v5.15.221 | Longterm, EOL Dec 2026 | Absent |
| 5.10 | v5.10.270 | Longterm, EOL Dec 2026 | Absent |
Read the middle column against the last one. The file is present in exactly two series, one already end of life and the other an ordinary stable. Kernel.org states that a stable series receives only a few bugfix releases until the next mainline kernel arrives, unless it is designated a longterm maintenance kernel, and 7.2 has not been. It stops receiving updates when 7.3 ships.
So no supported kernel an embedded product would reasonably ship carries this file. Every longterm series lacks it, and the capability does not vary between series: the file is either there or it is not. Kernel.org does not announce the next longterm release in advance, so nobody can tell you today which series will first carry this. Treat it as something that arrives with a future BSP rather than something to plan around.
What the file does not tell you
Four limits, for when you do have it.
It describes the kernel side only. The file lists the names the kernel will translate into a magic number. Whether your bootloader recognises that number is a separate agreement no kernel interface can verify, so a mode can appear in the list and still do nothing if the bootloader was built without the matching check.
Only drivers that use this framework appear. In drivers/power/reset/, three register reboot modes with it: syscon-reboot-mode, nvmem-reboot-mode and qcom-pon, and the ABI document names the same three. If your board carries its reboot reason another way, such as a PMIC driver writing the register itself or a custom restart handler, the class directory will be empty, and that absence tells you nothing about the board.
The directory name is the driver name. Two device tree nodes bound to the same reboot-mode driver would ask for the same class device name. Those names must be unique, so the second creation would fail, and since review made registration failure fatal, that driver instance would fail to probe. This follows from reading the code and was not reproduced on hardware, but check it if your board has more than one reboot-mode node of the same type.
The interface is still provisional. The documentation lives under Documentation/ABI/testing/, and its KernelVersion field still reads TBD as of 7.3-rc3. That does not mean the file will change, only that stability has not been promised.
What to do now
The useful work here does not wait for a kernel upgrade.
- Recover your board’s reboot modes with the device tree commands above, and record them in the board documentation alongside the bootloader’s side of the agreement.
- Check every mode string your userspace passes to
reboot()against that list by hand, once. A silent no-op will not find itself. - Add a startup assertion to the update agent: read the device tree, confirm the mode it intends to use is present, and fail loudly at startup rather than silently at reboot.
- When you next move kernels, check whether the target carries the sysfs file, and if it does, replace the device tree parsing with a read of it.
Key takeaways
- An unrecognised reboot mode string produces no error, no warning and no log message. The board reboots normally, which during an update rollback is the worst available outcome.
- Because the code tests the magic value rather than whether a match was found, a mode whose value is zero behaves identically to no match. Keep a non-zero tag in the upper bits.
- The names come from
mode-properties; the values are magic numbers the bootloader reads after reset. That contract is the same on every kernel. - Linux 7.1 added
/sys/class/reboot-mode/<driver>/reboot_modes, but 7.1 is end of life, 7.2 is not longterm, and no longterm series carries it. - When you do get the file, its returned length over-reports the text by one, so a read carries a trailing zero byte after the newline.
Frequently asked questions
What happens if I pass a reboot mode string the kernel does not know?
Nothing is reported. The lookup returns zero, the kernel skips the register write, and the board performs an ordinary reboot. There is no warning and no kernel log message, so the failure is only visible as the device coming back into the normal system instead of the mode you asked for.
Can I use the reboot_modes sysfs file on my product?
Almost certainly not yet. It landed in mainline Linux 7.1, which has reached end of life, and it is carried by 7.2, which is an ordinary stable rather than a longterm kernel. No longterm series has it, so on a normal embedded product the answer today is no.
How do I find the supported reboot modes without that file?
Read the live device tree with a command such as find /proc/device-tree -name 'mode-*', and confirm the driver is actually built in by checking for CONFIG_REBOOT_MODE. This requires /proc/device-tree to be mounted and may also match unrelated nodes, so treat the result as a starting point rather than an authoritative list.
Why does a mode with a magic value of zero not work?
The notifier guards the write with a test on the magic value rather than on whether a name matched. A mode whose value is zero is therefore indistinguishable from a name that was not found, and its write is skipped. Vendors avoid this by putting a non-zero tag in the upper bits of every magic value.
References
- power: reset: reboot-mode: Expose sysfs for registered reboot_modes (commit cfaf0a90789a)
- Documentation: ABI: Add sysfs-class-reboot-mode-reboot_modes (commit d3da03025e6d)
- Documentation/ABI/testing/sysfs-class-reboot-mode-reboot_modes at v7.2
- drivers/power/reset/reboot-mode.c at v7.2
- Device tree binding: syscon-reboot-mode
- include/dt-bindings/soc/rockchip,boot-mode.h
- Active kernel releases — kernel.org
- Linux 7.1 changelog — KernelNewbies




