Skip to content

Conversation

@gportay
Copy link
Contributor

@gportay gportay commented Jan 16, 2025

This adds an initial backend support for the Raspberry Pi firmware.

@gportay

This comment was marked as resolved.

@jluebbe jluebbe added the enhancement Adds new functionality or enhanced handling to RAUC label Jan 16, 2025
@jluebbe
Copy link
Member

jluebbe commented Jan 16, 2025

Very interesting!

I just took a quick look:

  • Would it be possible to use the partition index as RAUC bootname (setting it on the firmware partitions)? In that case, the currently booted partition could be detected by reading ``/chosen/bootloader/partition` in a RPi-sepecific implementation similar to get_custom_bootname(), which would be enabled for with this bootloader backend. That should also remove the limitation of having the three partitions first.
  • Please use Semantic Line Breaks in the docs.
  • Read the DT property directly from sysfs instead of using ftdget, which needs to unflatten the FTD again, which is already done by the kernel. We already do that in other places like https://github.com/rauc/rauc/blob/master/src/context.c#L266

@codecov
Copy link

codecov bot commented Jan 16, 2025

Codecov Report

❌ Patch coverage is 78.75000% with 85 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.37%. Comparing base (d2ec82a) to head (f6b5f54).
⚠️ Report is 4 commits behind head on master.

Files with missing lines Patch % Lines
src/bootloaders/raspberrypi.c 67.45% 69 Missing ⚠️
test/context.c 63.63% 8 Missing ⚠️
test/bootchooser.c 95.65% 6 Missing ⚠️
src/config_file.c 77.77% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1599      +/-   ##
==========================================
- Coverage   84.47%   84.37%   -0.11%     
==========================================
  Files          76       77       +1     
  Lines       22542    22941     +399     
==========================================
+ Hits        19043    19357     +314     
- Misses       3499     3584      +85     
Flag Coverage Δ
service=false 80.93% <78.75%> (-0.05%) ⬇️
service=true 84.35% <78.75%> (-0.11%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@gportay

This comment was marked as resolved.

@gportay

This comment was marked as resolved.

@jluebbe

This comment was marked as resolved.

@jluebbe

This comment was marked as resolved.

@jluebbe

This comment was marked as resolved.

return FALSE;
}

if (g_rename(filename_tmp, filename) == -1) {
Copy link
Contributor

@a3f a3f Jan 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fair to expect that by the time rauc status mark-good returns, the change is actually persistent on disk, so you need at least a call to fsync after this as we can't be sure the FAT is sync mounted. g_file_set_contents_full with G_FILE_SET_CONTENTS_DURABLE does fsync internally, but that's Glib >= 2.66, which is higher than RAUC's current minimum version (2.56).


I have been thinking about this a bit in relation to power-fail safety. The tryboot mechanism looks ok, it's apparently either either a warm-reboot-surviving memory-mapped register or persisted to EEPROM and cleared on next early boot (Generic DT abstractions for that is syscon-reboot-mode and nvmem-reboot-mode respectively).

The mark-good here is more difficult, FAT has no journal and even if it had, bootloaders tend to not implement journal playback anyway, so what happens here if the fsync is interrupted by a power cut?

  1. Can the autoboot.txt file disappear? Apparently, yes.

  2. Can the FAT filesystem itself become corrupt? I am not sure

Now what are the implications of a lost autoboot.txt? Could we deal with this gracefully? Should RAUC recreate this file to recover?

Anyhow, it may make sense to reduce the operations that we expect to be atomic to the bare minimum. Instead of rewriting autoboot.txt and moving it, we could expect an oldboot.txt with the other set of values and use Linux v6.0's support for renameat2(2) RENAME_EXCHANGE on FAT to exchange these files.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fair to expect that by the time rauc status mark-good returns, the change is actually persistent on disk, so you need at least a call to fsync after this as we can't be sure the FAT is sync mounted. g_file_set_contents_full with G_FILE_SET_CONTENTS_DURABLE does fsync internally, but that's Glib >= 2.66, which is higher than RAUC's current minimum version (2.56).

I have tried to address this in 035eb31.

Anyhow, it may make sense to reduce the operations that we expect to be atomic to the bare minimum. Instead of rewriting autoboot.txt and moving it, we could expect an oldboot.txt with the other set of values and use Linux v6.0's support for renameat2(2) RENAME_EXCHANGE on FAT to exchange these files.

Hum, I understand the idea. That file could be generated using autoboot.txt if it does not exist.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now what are the implications of a lost autoboot.txt? Could we deal with this gracefully? Should RAUC recreate this file to recover?

With my observations... the firmware iterates over the FAT filesystems, looking for the file autoboot.txt or config.txt.

  • If the file autoboot.txt is found, it mounts the nth filesystem given by boot_partition to read the file config.txt (or tryboot.txt) and continue booting.
  • If the file config.txt is found, it reads it and continue booting.

My 2 cents... if the file autoboot.txt vanishes on 1st fat filesystem, the firmware mounts the 2nd fat filesystem and reads the config.txt (if it has not vanished as well, or it mounts the 3rd fat filesystem and reads the config.txt... or it continues the romcode boot sequence, maybe it ends in netboot).

So in short, it should boot the system A.

In order to make the system more resilient to autoboot.txt disappearance, a services should regenerate it from the system.conf. I leave this duty to the system, something on top of RAUC, but not an inner thing from the raspberrypi backend.

Copy link
Member

@jluebbe jluebbe Jan 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@a3f Do we really benefit from using RENAME_EXCHANGE here? We don't need the old file anymore, so a normale rename should be enough.

Copy link
Member

@jluebbe jluebbe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't you generate the autoboot.txt file from scratch when writing, as RAUC should have all the required information in memory already?

It's still unclear to me how you generate the root= entry in the command line differently for A/B (see #1599 (comment)).

As far as I can see, you're not reading from /chosen/bootloader/partition in the context setup. How does RAUC detect the boot slot?

Do you handle multiple installations without rebooting correctly?

@gportay
Copy link
Contributor Author

gportay commented Jan 22, 2025

Couldn't you generate the autoboot.txt file from scratch when writing, as RAUC should have all the required information in memory already?

Well, yes, the backend is able to generate the file from its own since I have all the boot_partition= defined with their values in the system.conf. Also, there is that property tryboot_a_b=1 to hardcode in the section [all]; that property is not filled in the system.conf file.

I take the current autoboot.txt and swap the boot_partition= lines only. I think this is more future proof if some properties pop up later or if the user needs to adds extra properties for its own purpose like tryboot_a_b=1.

I have implemented the backend to just do the bare minimal to handle the raspberrypi autoboot.txt+tryboot feature:

  • swap the boot_partition
  • set the tryboot flag

It's still unclear to me how you generate the root= entry in the command line differently for A/B (see #1599 (comment)).

Oh, I jumped your comment. I am sorry.

I do not deal the command line at all.
That file is stored in the second and third boot_partition filesystems (cmdline.txt).
I have preferred to trust the content of those FAT filesystems and to not alter them if it is not necessary.

It adds some complications to modify the cmdline.txt files. The filesystem must be mounted and its path must be set in system.conf to locate file config.txt.

Do you think it's worth it?

As far as I can see, you're not reading from /chosen/bootloader/partition in the context setup. How does RAUC detect the boot slot?

So, I guess I have probably missed something. How does RAUC sets up?

I guess I have missed it by the mark-good systemd service at startup.

Or at least, it is guessed from the kernel command-line.

EDIT; Hum, I think I have understood the point. I will post something today.

Do you handle multiple installations without rebooting correctly?

It depends on the behavior we agree to be correct :) rauc install bundle.raucb always installs the bundle to the same slot(s). Is that the correct behavior? I guess yes as the second run for rauc install would install it on the booted slots, it is like shooting himself in the foot, right?

@gportay

This comment was marked as outdated.

@gportay gportay force-pushed the bootchooser-add-raspberrypi-firmware-initial-support branch from a8dc573 to c437c62 Compare January 22, 2025 17:28
@oli-ben
Copy link

oli-ben commented Jan 23, 2025

Hello Gaël,

thank you for your work on this! I was myself looking at supporting the same use-case.
I had however started supporting this via a bootloader-custom backend.

One particular issue I noticed that I think is not addressed in your PR is maintaining the parental relationship between the rootfs and firmware slots.

Indeed, unless I am mistaken and you have a way to instruct/script the RPi firmware at runtime (in which case disregard this), I think that most likely, that relationship is enforced by the kernel command line, that is specified in cmdline.txt on each of your firmware partitions.
I am now making slight assumptions about your system, please correct me if I'm wrong:

  • the kernel command line config.txt on firmware.0 points it to /dev/mmcblk0p5 (rootfs.0)
  • the kernel command line config.txt on firmware.1 points it to /dev/mmcblk0p6 (rootfs.1)

My point is what happens when you deploy a firmware update which contains a config.txt file:
that file was generated at build time, and must point the kernel to a rootfs in order for the system to be able to boot, but it cannot know at build time which slot RAUC will choose to deploy the update, so there is a 50/50 chance that the parental relationship will be broken because the file will point to rootfs.0 but will be installed on firmware.1.

This would lead to the system booting with an updated kernel from firmware.1 and an old rootfs from rootfs.0.
That means that rootfs.1 could be broken, which would not be detected, and the next update to slot 0 could brick the system if it failed, if it now broke rootfs.0.

All of this to say that I think raspberrypi_set_primary should enforce the parental relationship by making sure that the rootfs device on the kernel command line in cmdline.txt is the device configured for that slot's child in the RAUC configuration.


static RaucSlot *raspberrypi_find_config_slot_by_boot_partition(RaucConfig *config, gint boot_partition)
{
g_autofree gchar *name = g_strdup_printf("%u", boot_partition);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes it mandatory for the name of the slot to be the partition number.

I might have missed it, but I don't think I saw that explicitly documented in docs/integration.rst, and this requirement for other setups.
It might also be useful as a comment in the example configuration files.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See that comment.

The documentation says:

If the slot is bootable, then you also need

  • the bootname which is the name the bootloader uses to refer to this slot device.

That could be interpreted the bootname is how the names/references it.
In a sense, for the raspberrypi firmware, it is the boot_partition index. For barebox, it is the bootchooser/bootstate. For other backend, it is free.

It might also be useful as a comment in the example configuration files.

This should be that point.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It uses the slot's bootname, which is how RAUC knows how the slot is "called" by the bootloader (or firmware in this case).

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My apologies, it looks like I should just read more thoroughly.

@gportay
Copy link
Contributor Author

gportay commented Jan 23, 2025

Hello @oli-ben

Hello Gaël,

thank you for your work on this! I was myself looking at supporting the same use-case. I had however started supporting this via a bootloader-custom backend.

My pleasure. I am learning more about the RAUC internal in doing this.
I have written my own custom backend before that as a PoC.

One particular issue I noticed that I think is not addressed in your PR is maintaining the parental relationship between the rootfs and firmware slots.

I have mentioned this in the integration documentation, maybe it is not clear enough:

Those rootfs slots are parented to their FAT filesystem slot.

And it is part of the example.

So I strongly guess I have to insist on that part. Or...

Indeed, unless I am mistaken and you have a way to instruct/script the RPi firmware at runtime (in which case disregard this), I think that most likely, that relationship is enforced by the kernel command line, that is specified in cmdline.txt on each of your firmware partitions. I am now making slight assumptions about your system, please correct me if I'm wrong:

* the kernel command line `config.txt` on `firmware.0` points it to `/dev/mmcblk0p5` (`rootfs.0`)

* the kernel command line `config.txt` on `firmware.1` points it to `/dev/mmcblk0p6` (`rootfs.1`)

Indeed you assumed it correctly :)

My point is what happens when you deploy a firmware update which contains a config.txt file: that file was generated at build time, and must point the kernel to a rootfs in order for the system to be able to boot, but it cannot know at build time which slot RAUC will choose to deploy the update, so there is a 50/50 chance that the parental relationship will be broken because the file will point to rootfs.0 but will be installed on firmware.1.

This would lead to the system booting with an updated kernel from firmware.1 and an old rootfs from rootfs.0. That means that rootfs.1 could be broken, which would not be detected, and the next update to slot 0 could brick the system if it failed, if it now broke rootfs.0.

... okay, I get the point: I dedicate this to a slot hook.
But I have not mentioned it yet. The integration documentation remains incomplete.

My first intention is to address the rootfs only in a first instance.
This assumes the system.conf matches the two firmware slots (i.e. cmdline.txt).

The update of the firmware slots requires an hook to set the appropriate root= to cmdline.txt once the filesystem is written to the device and mounted.

All of this to say that I think raspberrypi_set_primary should enforce the parental relationship by making sure that the rootfs device on the kernel command line in cmdline.txt is the device configured for that slot's child in the RAUC configuration.

I am not yet convinced to hard-code this to the backend, mostly for this reasons:

  • how to use dm-verity (root= -> systemd.verity_root_data=, systemd.verity_root_hash=, systemd.verity_root_data=...)
  • does the backend force the use of config.txt for tryboot (tryboot.txt, see autoboot.txt's option tryboot_a_b=1)?
  • does the backend force the use of cmdline.txt for the kernel command line (see config.txt's option cmdline)?

I have not yet explore all these possibilities 😭 I cannot tell what is the best solution, and I have preferred to limit the backend to the bare minimum and use the bundle hooks as an exit for now.

I am not saying you are wrong, all of you raise good points, and maybe this should be part of the backend after all...

@jluebbe
Copy link
Member

jluebbe commented Jan 23, 2025

The update of the firmware slots requires an hook to set the appropriate root= to cmdline.txt once the filesystem is written to the device and mounted.

I think that using a (post-install) hook is fine. Compared to the alternatives, it's very simple and easy to implement.

More complex cases (e.g. secure boot) wouldn't allow customizing the cmdline from unsigned config files, so you'd need a signed kernel+initramfs. In that case, I'd probably look at reading /chosen/bootloader/partition from DT and deriving the rootfs partition from that. This way, the contents of the firmware partition image doesn't need to be modified.

@gportay
Copy link
Contributor Author

gportay commented Jan 23, 2025

It's still unclear to me how you generate the root= entry in the command line differently for A/B (see #1599 (comment)).

Do you think it worth to create that function r_boot_get_bootname() and calls it at setup? So I can get the root device from the device-tree on the raspberrypi backend.

@jluebbe It is somehow what you expected?

@oli-ben
Copy link

oli-ben commented Jan 23, 2025

Hello again, thanks for you super quick response

... okay, I get the point: I dedicate this to a slot hook. But I have not mentioned it yet. The integration documentation remains incomplete.

Very fair. I hope my feedback did not come across as criticism.

My first intention is to address the rootfs only in a first instance. This assumes the system.conf matches the two firmware slots (i.e. cmdline.txt).

The update of the firmware slots requires an hook to set the appropriate root= to cmdline.txt once the filesystem is written to the device and mounted.

Also fair = )

I am not yet convinced to hard-code this to the backend, mostly for this reasons:

Jan appears to be with you on this one.

* how to use `dm-verity` (`root=` -> `systemd.verity_root_data=`, `systemd.verity_root_hash=`, `systemd.verity_root_data=`...)

Well, if you're using dm-verity in this setup, my impression is, that you'd then have to update the root hash, which should be co-located with the kernel, on the firmware partitions, because that's where the RPi firmware will expect it.
This only reinforces the fact that someone would have to maintain the parent relationship, as a firmware will only have the hashes of their child rootFS.
From where I stand, that could be either the backend or a post-install hook, but as a naive user, I would expect the backend to be doing that for me, since RAUC is aware of that relationship.

* does the backend force the use of `config.txt` for tryboot (`tryboot.txt`, see `autoboot.txt`'s option [tryboot_a_b=1](https://www.raspberrypi.com/documentation/computers/config_txt.html#tryboot_a_b))?

I wouldn't see a problem with this: this is all assuming that we are doing a A/B setup, which is what this option was intended for.

* does the backend force the use of `cmdline.txt` for the kernel command line (see `config.txt`'s option [cmdline](https://www.raspberrypi.com/documentation/computers/config_txt.html#cmdline))?

I also would not particularly see a problem with this: imho it's fair for RAUC to impose some configuration, especially when it seems to be the "usual" way of using the RPi firmware

I have not yet explore all these possibilities 😭 I cannot tell what is the best solution, and I have preferred to limit the backend to the bare minimum and use the bundle hooks as an exit for now.

I am not saying you are wrong, all of you raise good points, and maybe this should be part of the backend after all...

I'm also not saying you are wrong, just raising a point I thought was potentially interesting.
If you're not interested in looking further into this, I'd be happy to potentially give it a shot after this PR is merged = )

@oli-ben
Copy link

oli-ben commented Jan 23, 2025

The update of the firmware slots requires an hook to set the appropriate root= to cmdline.txt once the filesystem is written to the device and mounted.

I think that using a (post-install) hook is fine. Compared to the alternatives, it's very simple and easy to implement.

More complex cases (e.g. secure boot) wouldn't allow customizing the cmdline from unsigned config files, so you'd need a signed kernel+initramfs. In that case,

That is true, as it seems that secure boot on RPi necessiates a signed ramdisk containing at least the kernel and config.txt

tryboot is pretty explicit in needing partitions and I have not seen anything mention using it with secure boot, so my impression is, that they might be incompatible.

In that case, I'd probably look at reading /chosen/bootloader/partition from DT and deriving the rootfs partition from that.

I think that'd be great, and this is what I did when using UBoot, but when using the RPi firmware directly to load the kernel, the only options we'd have to do this are:

  • within the kernel somehow
  • by having the RPi firmware call essentially a tiny bootloader that would simply read the DTB and load the kernel passing it the correct command line

This seems like a large amount of effort, and while this might solve the secure boot usecase, might be a tad too complicated for the normal one.

This way, the contents of the firmware partition image doesn't need to be modified.

Although it is true that cmdline.txt would no longer need to be modified, I think that might not be true for a dm-verity setup, as it would need to update the firmare partition image to include the correct root hash.

@gportay
Copy link
Contributor Author

gportay commented Jan 31, 2025

Very fair. I hope my feedback did not come across as criticism.

No worries.

I liked your feedback and I had to take the time for reflection. I did that months ago, and I do not recall all the pieces that led me to that implementation.

Well, if you're using dm-verity in this setup, my impression is, that you'd then have to update the root hash, which should be co-located with the kernel, on the firmware partitions, because that's where the RPi firmware will expect it. This only reinforces the fact that someone would have to maintain the parent relationship, as a firmware will only have the hashes of their child rootFS. From where I stand, that could be either the backend or a post-install hook, but as a naive user, I would expect the backend to be doing that for me, since RAUC is aware of that relationship.

I really do understand you opinion; I am still perplex about having an all-in-one backend solution or having some hooks :/

If I am right, the issue lies in the fact that :

  • the raspberry bootloader stores the cmdline and the kernel at the same place, in a FAT filesystem (that is even worth)
  • RAUC works by slots and cannot write files to an existing filesystem (i.e. without wiping the whole partition).

I wonder if the the newly introduced artifact feature may help (with some changes, thinking about porting the new files/tree types a new slot handlers).
This way, the backend can update the rootfs slots, and updates the kernel and the dtb{,o} in the firmware filesystem slots.
Does it look correct?

Also, I do not think the other backends deal with the kernel command line, mostly because they do not need to, the bootloader and kernel files are somehow decorelated, or tools exists to update the pieces separately, am I still correct?

OOS: I guess I will throw an eye to the artifacts once I have free time. I have plan to test it to update the bootcode.bin of the former Raspberry Pis (< 4) that should be a good candidate.

* does the backend force the use of `cmdline.txt` for the kernel command line (see `config.txt`'s option [cmdline](https://www.raspberrypi.com/documentation/computers/config_txt.html#cmdline))?

I also would not particularly see a problem with this: imho it's fair for RAUC to impose some configuration, especially when it seems to be the "usual" way of using the RPi firmware

Indeed it is fair to impose few things in RAUC. I am trying to find a flexible solution first.

I'm also not saying you are wrong, just raising a point I thought was potentially interesting. If you're not interested in looking further into this, I'd be happy to potentially give it a shot after this PR is merged = )

It's great to share our own solutions. And it is not I am not interested, in my head, the wind blows in the direction of not rewriting the kernel cmdline in the backend.

And I do not mind if you do it course ;)
Think about a way to deactive it; for example, do not rewrite the cmdline.txt if raspberrypi-config-txt is unset in system.conf.

@oli-ben
Copy link

oli-ben commented Feb 4, 2025

Very fair. I hope my feedback did not come across as criticism.

No worries.

I liked your feedback and I had to take the time for reflection. I did that months ago, and I do not recall all the pieces that led me to that implementation.

Good to hear! = D

I really do understand you opinion; I am still perplex about having an all-in-one backend solution or having some hooks :/

Fair. My main argument is, that I think a user having a RAUC configuration specifying:

  • to use a backend specific for the RPi bootloader
  • a parent/child relationship between boot and root FS
  • redundant A/B slots

is more likely to expect RAUC to maintain the parent/child relationship between the boot and root FS automatically than actively wanting RAUC to leave the cmdline.txt completely unchanged, down to the init argument.

That second usecase should probably be supported, but I think we might not want it to be the default.

If I am right, the issue lies in the fact that :

* the raspberry bootloader stores the cmdline and the kernel at the same place, in a FAT filesystem (that is even worth)

* RAUC works by slots and cannot write files to an existing filesystem (i.e. without wiping the whole partition).

From what I can tell, that is correct. And definitely regrettable. ><

I wonder if the the newly introduced artifact feature may help (with some changes, thinking about porting the new files/tree types a new slot handlers). This way, the backend can update the rootfs slots, and updates the kernel and the dtb{,o} in the firmware filesystem slots. Does it look correct?

I haven't used that feature, but it might be the right way to handle this. Especially if the overall decision is, that having the backend rewrite cmdline.txt is not ok.

Also, I do not think the other backends deal with the kernel command line, mostly because they do not need to, the bootloader and kernel files are somehow decorelated, or tools exists to update the pieces separately, am I still correct?

Yes. The core issue here, in my opinion, is that the RPi bootloader can only load the kernel from the same partition as cmdline.txt.
That means, that if we want to be able to update the kernel using a "normal"m slot-based approach, we're going to have to update cmdline.txt as well, even though it's related to the bootloader. Some of the configuration in that file (the init argument) is slot-specific, but we can't know when building the bundle which slot it will be installed on.

UBoot could work around this, because it has scripting capabilities, and could therefore dynamically determine the partition it was started from by parsing the DTB, and adjust the command line accordingly.

OOS: I guess I will throw an eye to the artifacts once I have free time. I have plan to test it to update the bootcode.bin of the former Raspberry Pis (< 4) that should be a good candidate.

sounds like it to me

It's great to share our own solutions. And it is not I am not interested, in my head, the wind blows in the direction of not rewriting the kernel cmdline in the backend.

Yes, it sound like we mostly have a different image of what the "default"/"normal"/"common" (or whatever we want to call it) usecase looks like.

And I do not mind if you do it course ;) Think about a way to deactive it; for example, do not rewrite the cmdline.txt if raspberrypi-config-txt is unset in system.conf.

Yes, I agree that it should definitely be optional. I think I'd just default to it being opt-out rather than opt-in, because not wanting it seems to me like the more exotic usecase.

@jluebbe
Copy link
Member

jluebbe commented Feb 5, 2025

@gportay and I talked about this at FOSDEM. Some points from our discussion:

  • Artifact updates won't help here. It is for SW components used by both A/B rootfs slots, which do not depend on a specific rootfs version (e.g. containers or statically linked applications).
  • src/bootchooser.c should be split up, but that's independent of this PR.
  • Introducing r_boot_get_bootname() is a good approach, but we don't need the identical "not implemented" functions. Instead only add the supported cases to the if/else cascade. The refactoring could be merged early in a separate PR to reduce the size of this one.
  • The proper way to have a fixed kernel boot image seems to be an initramfs which determines the root device based on /chosen/bootloader/partition in the device tree (as in Raspberry Pi 5 /chosen/bootloader/partition is set to 0 raspberrypi/rpi-eeprom#575). This will be compatible with signed boot images (for secure boot) as well.
  • The autoboot.txt file should probably be generated from scratch instead of parsing and updating it. That would avoid a broken system if it gets damaged or removed in some way. If there is a need for custom properties in autoboot.txt, support for that could be added later with the appropriate justification.

@bryceschober
Copy link

Yeah, I came to that conclusion myself, and opened a case to help support non-initramfs A/B tryboot integrations over at raspberrypi/firmware#1940. I don't expect them to care about the use-case of A/B updates without an initramfs, though.

@gportay
Copy link
Contributor Author

gportay commented Jul 25, 2025

I'm able to reproduce this issue such that I end up with my autoboot.txt looking like this:

# cat /autoboot/autoboot.txt 
[all]
tryboot_a_b=1
boot_partition=2
[tryboot]
boot_partition=2

@bradfa, thanks, this is origin of the problem. There is an issue about detecting the current primary slot. Let me investigate this.


If I understand correctly, this happens if you install a bundle from the primary slot, reboot to other slot and install a bundle again from the other slot without rebooting (i.e. still tryboot), am I right?

What happen if you install a second bundle, do you have:

[all]
tryboot_a_b=1
boot_partition=2
[tryboot]
boot_partition=3

or

[all]
tryboot_a_b=1
boot_partition=3
[tryboot]
boot_partition=3

or something else?

return FALSE;
}

if ((slot != primary && good) || (slot == primary && !good)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bradfa, the issue comes from that line.

-	if ((slot != primary && good) || (slot == primary && !good)) {
+	if (slot != primary && good) {

This change should fix your issue.

I need more investigation about why I added the bugged condition || (slot == primary && !good).

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one section change seems to improve things, as now I don't end up with both boot_partition= having the same value. But a rauc install, reboot, rauc install, reboot sequence now doesn't seem to be setting tryboot on the 2nd reboot.

@gportay
Copy link
Contributor Author

gportay commented Jul 26, 2025

For these nuanced issues that @berkutta and I have noted, would it be better or easier to simply avoid using the tryboot flag entirely and instead rely on PARTITION_WALK=1 being set as a requirement in the firmware in order to use RAUC?

Thanks, I was not aware about that PARTITION_WALK=1, and I figured out that the bootloader is actually walking through the vFAT partition to search for either autoboot.txt or config.txt, and I have planned to test for adding a recovery (A/B+R) if autoboot.txt is corrupted. My initial thought is to add it in second partition, having 4 vFAT partitions (autoboot.txt, config.txt for recovery, config.txt for A, and config.txt for B).

Then autoboot.txt would just list a single [all] section, like:

[all]
boot_partition=2

And if that boot_partition fails to boot, then PARTITION_WALK will automatically find the other slot's boot partition and boot it? RAUC can then see that the system is booted into the incorrect slot and note that the just-installed slot is bad and update autoboot.txt?

With your solution, do you suggest to switch the boot_partition in the autoboot.txt at rauc install? I see no other solution as the tryboot is unused.

The tryboot is a "volatile" flag, that is cleared after a poweroff, as said in the documentation, it is likely a boot counter of 1.

I think I missed something. How would you recover if your installation is booting but the system is in bad state? For example the kernel panics. The bootloader would reboot on the same system? And thus it would reboot in loop, am I right?

This would also make RAUC determining which slot is currently desired to be used for the next boot much easier, as autoboot.txt would always be correct.

May I ask more details?

If autoboot.txt gets corrupted or deleted entirely on accident, then since PARTITION_WALK=1 is set, the firmware will simply boot the first boot partition it can find. I have verified that at least Raspi 5 firmware 2025-03-10 will do this:

  6.60 [nvme] autoboot.txt not found
  6.62 Select partition rsts 0 C(boot_partition) 0 EEPROM config 0 result 1
  6.68 Trying partition: 1
  6.71 GPT 0000000000000000000000004d9b9ef0 000000001 0e8e088af num-partitions 128 entry-size 128 pentry 2 first 0
  6.81 GPT partition: 0 ebd0a0a2-b9e5-4433-87c0-68b6b72699c7 000002000..000021fff
  6.88 Matched GUID bootable-part-idx 0 want partition 0
  6.93 type: 32 lba: 8192 'mkfs.fat' ' autoboot   ' clusters 129022 (1)
  6.00 rsc 32 fat-sectors 1009 root dir cluster 2 sectors 0 entries 0
  6.06 FAT32 clusters 129022
  6.08 secure-boot
  6.10 Loading boot.img ...
  6.13 [nvme] boot.img not found
  6.16 Error 6 loading boot.img
  6.19 Select partition rsts 0 C(boot_partition) 0 EEPROM config 0 result 1
  6.25 Partition walk: 1
  6.28 Trying partition: 1
  6.30 GPT 0000000000000000000000004d9b9ef0 000000001 0e8e088af num-partitions 128 entry-size 128 pentry 2 first 0
  6.40 GPT partition: 0 ebd0a0a2-b9e5-4433-87c0-68b6b72699c7 000002000..000021fff
  6.47 Matched GUID bootable-part-idx 0 want partition 0
  6.53 type: 32 lba: 8192 'mkfs.fat' ' autoboot   ' clusters 129022 (1)
  6.59 rsc 32 fat-sectors 1009 root dir cluster 2 sectors 0 entries 0
  6.65 FAT32 clusters 129022
  6.68 secure-boot
  6.69 Loading boot.img ...
  6.72 [nvme] boot.img not found
  6.75 Error 6 loading boot.img
  6.78 Trying partition: 2
  6.80 GPT 0000000000000000000000004d9b9ef0 000000001 0e8e088af num-partitions 128 entry-size 128 pentry 2 first 0
  6.90 GPT partition: 0 ebd0a0a2-b9e5-4433-87c0-68b6b72699c7 000002000..000021fff
  6.98 Matched GUID bootable-part-idx 0 want partition 1
  7.03 GPT partition: 1 ebd0a0a2-b9e5-4433-87c0-68b6b72699c7 000022000..000061fff
  7.10 Matched GUID bootable-part-idx 1 want partition 1
  7.15 type: 32 lba: 139264 'mkfs.fat' ' KERNEL_0   ' clusters 258078 (1)
  7.22 rsc 32 fat-sectors 2017 root dir cluster 2 sectors 0 entries 0
  7.28 FAT32 clusters 258078
  7.30 secure-boot
  7.32 Loading boot.img ...
  7.36 boot.sig
  7.36 hash: 7f82770ab4b9f83712da116333a8b7fff9159c7fa31eefe0db40e0f5a49933d7
  7.43 ts: 1733479805
  7.45 rsa2048: a2df95cfe819658adafdee1909f8fb40195e835c3dad11292d2e21a2624872f3b8a822161418c51f38baa2b3a2eae0783163730509cdfd8e17b1e12877cbb56cb078446280294f19cafb14ea3ce2a02a5c3cc99ab7b70ff8e90dd6b30c76ccbb14ccbd31aea1d723a47db63f233e28a32386668e4f0ee86320f88d9a274aaab330e35771ec2406d948f896bf9ca19f859f5f18d383c069b87815a1b1c63a6b34cbbde792607e720d255baaa88ac8bd94efe73e4a0a2a81a151e5c90452863cd0f7d29d3f99052e74d69e7033b579bb23466c65e975b8320065d8e80168381ac93d1365c4ad4b0e7ba81108495787087b535c976ec7c125146e5b660808fcdd17
  9.89 Verifying
 16.89 RSA verify
 16.01 rsa-verify pass (0x0)

This feature is interesting indeed, and it could be used to implement a recovery in the situation of the terrible happen (autoboot.txt is empty, or bad?).

I would prefer to run a recovery as it is impossible to know which was the last correct system (i.e. A or B).

Actually, the bootloader already walks through the vFAT filesystem and I think the recovery should be the second partition (an initramfs that would rewrite the A partitions). But I do not think it needs to set the PARTITION_WALK to 1.

@bradfa
Copy link

bradfa commented Jul 29, 2025

My understanding is you would like more information from RAUC about the current status? Information specific to the backend? For now, you can do this on your own with shell commands. Also I can add more traces if you really think this is important.

Yes, I think so, but this is just my opinion. If the direction is to use tryboot then I would like if rauc status showed the full status of what is going to happen next such that it's clear that a reboot is needed and that during such reboot that tryboot will be used.

On other RAUC targets, if you do a rauc install and then power off the board, then on the next boot the newly installed slot will be booted. But on Raspi if you do a rauc install and power off the board, then it will boot again into the old slot. The Raspi using tryboot mechanism requires a reboot to effectively finish the rauc install operation.

@bradfa
Copy link

bradfa commented Jul 29, 2025

For these nuanced issues that @berkutta and I have noted, would it be better or easier to simply avoid using the tryboot flag entirely and instead rely on PARTITION_WALK=1 being set as a requirement in the firmware in order to use RAUC?

Thanks, I was not aware about that PARTITION_WALK=1, and I figured out that the bootloader is actually walking through the vFAT partition to search for either autoboot.txt or config.txt, and I have planned to test for adding a recovery (A/B+R) if autoboot.txt is corrupted. My initial thought is to add it in second partition, having 4 vFAT partitions (autoboot.txt, config.txt for recovery, config.txt for A, and config.txt for B).

OK, I can understand that concept. My desire is just for 2 slots, an A and a B, without any recovery. This setup with RAUC has worked well for me in other non-Raspi projects.

Then autoboot.txt would just list a single [all] section, like:

[all]
boot_partition=2

And if that boot_partition fails to boot, then PARTITION_WALK will automatically find the other slot's boot partition and boot it? RAUC can then see that the system is booted into the incorrect slot and note that the just-installed slot is bad and update autoboot.txt?

With your solution, do you suggest to switch the boot_partition in the autoboot.txt at rauc install? I see no other solution as the tryboot is unused.

Yes.

The tryboot is a "volatile" flag, that is cleared after a poweroff, as said in the documentation, it is likely a boot counter of 1.

I think I missed something. How would you recover if your installation is booting but the system is in bad state? For example the kernel panics. The bootloader would reboot on the same system? And thus it would reboot in loop, am I right?

This is a good point which I had not fully considered. Yes, there would be no direct fallback with this method of operation as by default there is no watchdog or boot counter to catch this kind of problem.

RAUC with u-boot having boot counters solves this problem, but I'm not sure something similar to that is possible with the Raspi firmware.

There is mention in the docs about a watchdog mechanism and being able to specify fallbacks, although I have not tried to understand how to use it, yet: https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#PARTITION

This would also make RAUC determining which slot is currently desired to be used for the next boot much easier, as autoboot.txt would always be correct.

May I ask more details?

I just mean in regards to my previous critique of rauc status not showing if tryboot is in effect or not.

If autoboot.txt gets corrupted or deleted entirely on accident, then since PARTITION_WALK=1 is set, the firmware will simply boot the first boot partition it can find. I have verified that at least Raspi 5 firmware 2025-03-10 will do this:

  6.60 [nvme] autoboot.txt not found
  6.62 Select partition rsts 0 C(boot_partition) 0 EEPROM config 0 result 1
  6.68 Trying partition: 1
  6.71 GPT 0000000000000000000000004d9b9ef0 000000001 0e8e088af num-partitions 128 entry-size 128 pentry 2 first 0
  6.81 GPT partition: 0 ebd0a0a2-b9e5-4433-87c0-68b6b72699c7 000002000..000021fff
  6.88 Matched GUID bootable-part-idx 0 want partition 0
  6.93 type: 32 lba: 8192 'mkfs.fat' ' autoboot   ' clusters 129022 (1)
  6.00 rsc 32 fat-sectors 1009 root dir cluster 2 sectors 0 entries 0
  6.06 FAT32 clusters 129022
  6.08 secure-boot
  6.10 Loading boot.img ...
  6.13 [nvme] boot.img not found
  6.16 Error 6 loading boot.img
  6.19 Select partition rsts 0 C(boot_partition) 0 EEPROM config 0 result 1
  6.25 Partition walk: 1
  6.28 Trying partition: 1
  6.30 GPT 0000000000000000000000004d9b9ef0 000000001 0e8e088af num-partitions 128 entry-size 128 pentry 2 first 0
  6.40 GPT partition: 0 ebd0a0a2-b9e5-4433-87c0-68b6b72699c7 000002000..000021fff
  6.47 Matched GUID bootable-part-idx 0 want partition 0
  6.53 type: 32 lba: 8192 'mkfs.fat' ' autoboot   ' clusters 129022 (1)
  6.59 rsc 32 fat-sectors 1009 root dir cluster 2 sectors 0 entries 0
  6.65 FAT32 clusters 129022
  6.68 secure-boot
  6.69 Loading boot.img ...
  6.72 [nvme] boot.img not found
  6.75 Error 6 loading boot.img
  6.78 Trying partition: 2
  6.80 GPT 0000000000000000000000004d9b9ef0 000000001 0e8e088af num-partitions 128 entry-size 128 pentry 2 first 0
  6.90 GPT partition: 0 ebd0a0a2-b9e5-4433-87c0-68b6b72699c7 000002000..000021fff
  6.98 Matched GUID bootable-part-idx 0 want partition 1
  7.03 GPT partition: 1 ebd0a0a2-b9e5-4433-87c0-68b6b72699c7 000022000..000061fff
  7.10 Matched GUID bootable-part-idx 1 want partition 1
  7.15 type: 32 lba: 139264 'mkfs.fat' ' KERNEL_0   ' clusters 258078 (1)
  7.22 rsc 32 fat-sectors 2017 root dir cluster 2 sectors 0 entries 0
  7.28 FAT32 clusters 258078
  7.30 secure-boot
  7.32 Loading boot.img ...
  7.36 boot.sig
  7.36 hash: 7f82770ab4b9f83712da116333a8b7fff9159c7fa31eefe0db40e0f5a49933d7
  7.43 ts: 1733479805
  7.45 rsa2048: a2df95cfe819658adafdee1909f8fb40195e835c3dad11292d2e21a2624872f3b8a822161418c51f38baa2b3a2eae0783163730509cdfd8e17b1e12877cbb56cb078446280294f19cafb14ea3ce2a02a5c3cc99ab7b70ff8e90dd6b30c76ccbb14ccbd31aea1d723a47db63f233e28a32386668e4f0ee86320f88d9a274aaab330e35771ec2406d948f896bf9ca19f859f5f18d383c069b87815a1b1c63a6b34cbbde792607e720d255baaa88ac8bd94efe73e4a0a2a81a151e5c90452863cd0f7d29d3f99052e74d69e7033b579bb23466c65e975b8320065d8e80168381ac93d1365c4ad4b0e7ba81108495787087b535c976ec7c125146e5b660808fcdd17
  9.89 Verifying
 16.89 RSA verify
 16.01 rsa-verify pass (0x0)

This feature is interesting indeed, and it could be used to implement a recovery in the situation of the terrible happen (autoboot.txt is empty, or bad?).

I would prefer to run a recovery as it is impossible to know which was the last correct system (i.e. A or B).

Actually, the bootloader already walks through the vFAT filesystem and I think the recovery should be the second partition (an initramfs that would rewrite the A partitions). But I do not think it needs to set the PARTITION_WALK to 1.

I will think about the concept of using a recovery option. That may end up being required given the limitations of the Raspi firmware.

Comment on lines 165 to 171
if (!raspberrypi_bootloader_get_tryboot(&tryboot, &ierror)) {
g_propagate_prefixed_error(
error,
ierror,
"Failed to get bootloader tryboot property: ");
return NULL;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is obtaining information about if the current boot is a tryboot boot. I think what we really want to know here is if tryboot is set for the next boot based on the logic later in this function where tryboot==TRUE causes a look up to find the "other" slot.

On every boot, regardless of if tryboot is in effect, the rauc-mark-good.service should be causing autoboot.txt to be updated such that the currently booted slot goes in the [all] section's boot_partition=.

If tryboot is not set for the next boot, then autoboot.txt is correct and the sysfs partition value will match autoboot.txt's [all] section value, and this slot is the primary. But if tryboot is set for the next boot, then we need to go find the "other" slot info as that's what is configured to be booted next as the primary.

The state of the current boot's tryboot indicator doesn't seem to matter for this function. Only the value of the next boot's tryboot flag.

We should be able to read via vcmailbox if tryboot is set for the next boot by reading from address 0x00030064 as per https://github.com/raspberrypi/linux/blob/rpi-6.12.y/include/soc/bcm2835/raspberrypi-firmware.h#L98

Comment on lines 289 to 292
/* Set slot as primary boot slot, i.e. either persistently in the static file
* autoboot.txt if it is the boot'ed slot or temporarily via the tryboot reboot
* flag otherwise. */
gboolean r_raspberrypi_set_primary(RaucSlot *slot, GError **error)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that if my change mentioned https://github.com/rauc/rauc/pull/1599/files/fdc22a44866b59eaee8fe830655c16a557b63f79#r2245601881 is implemented, then this this function needs to be also modified.

This function would need to change to have logic like:

  1. If the desired slot is NOT the booted slot, then set tryboot for the next boot.
  2. If the desired slot IS the booted slot AND we are currently in a tryboot, then update autoboot.txt.
  3. If the desired slot IS the booted slot and we are NOT currently in a tryboot, then do nothing. This will be the typical path.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rauc status mark-good will only use choices 2 or 3 above because it should only be called to say that this booted slot is good and so autoboot.txt needs to reflect this in the [all] section's boot_partition= value. 2 happens during the reboot after a rauc install was performed. 3 happens on normal boots.

rauc install will only use choice 1 above, as it will always be installing to the non-booted slot, regardless of if we're currently in a tryboot or not.

This presumes that the rauc-mark-good.service is running during startup to mark the current booted slot as good.

@gportay
Copy link
Contributor Author

gportay commented Aug 11, 2025

Yes, I think so, but this is just my opinion. If the direction is to use tryboot then I would like if rauc status showed the full status of what is going to happen next such that it's clear that a reboot is needed and that during such reboot that tryboot will be used.

You can still propose and develop a PoC not using tryboot, but I guess it will be tricky.

I agree, my implementation remains incomplete since it needs a reboot after validating the new slot (i.e. one reboot after rauc install and one reboot after rauc status mark-good to allow rauc install again). I have to work on that.

On other RAUC targets, if you do a rauc install and then power off the board, then on the next boot the newly installed slot will be booted. But on Raspi if you do a rauc install and power off the board, then it will boot again into the old slot. The Raspi using tryboot mechanism requires a reboot to effectively finish the rauc install operation.

I think it is not necessarily true to every bootloader backends. I have never used the EFI backend myself, but if you use the set use-efi-boot-next to true, that EFI variable is volatile IIRC, and thus, the reboot to new slot is lost on powercut. But, please do not trust my own words ;) And some EFI firmwares act differently...

@bradfa
Copy link

bradfa commented Aug 11, 2025

Yes, I think so, but this is just my opinion. If the direction is to use tryboot then I would like if rauc status showed the full status of what is going to happen next such that it's clear that a reboot is needed and that during such reboot that tryboot will be used.

You can still propose and develop a PoC not using tryboot, but I guess it will be tricky.

After more thought, I think using tryboot is the right direction and I think using PARTITION_WALK is not necessarily a good idea.

I agree, my implementation remains incomplete since it needs a reboot after validating the new slot (i.e. one reboot after rauc install and one reboot after rauc status mark-good to allow rauc install again). I have to work on that.

On other RAUC targets, if you do a rauc install and then power off the board, then on the next boot the newly installed slot will be booted. But on Raspi if you do a rauc install and power off the board, then it will boot again into the old slot. The Raspi using tryboot mechanism requires a reboot to effectively finish the rauc install operation.

I think it is not necessarily true to every bootloader backends. I have never used the EFI backend myself, but if you use the set use-efi-boot-next to true, that EFI variable is volatile IIRC, and thus, the reboot to new slot is lost on powercut. But, please do not trust my own words ;) And some EFI firmwares act differently...

That's true about EFI. And I'm very confident that different EFI implementations will treat the boot-next value as volatile in dramatically different ways. EFI is generally a mess :)

@bradfa
Copy link

bradfa commented Aug 12, 2025

I have a wip branch based off this PR which I believe fixes the issues that I've found. My changes almost certainly need some cleanup, but things do seem to work as I expect them to. My implementation of reading if tryboot is set for the next boot makes me cringe as it's just string comparisons on the vcmailbox output reading a register that appears completely undocumented and I worry that other bits in that register might someday be used.

But for now, it seems to work in my tests: https://github.com/bradfa/rauc/commits/wip-raspberrpi5/

This adds an initial backend support for the Raspberry Pi firmware.

Signed-off-by: Gaël PORTAY <gael.portay@rtone.fr>
@gportay gportay force-pushed the bootchooser-add-raspberrypi-firmware-initial-support branch from fdc22a4 to f6b5f54 Compare September 16, 2025 13:25
@gportay
Copy link
Contributor Author

gportay commented Sep 16, 2025

I have reworked the get_state(), set_state(), get_primary() and set_primary() callbacks, almost entirely.

@bradfa
Copy link

bradfa commented Sep 29, 2025

Some quick testing with with @gportay latest changes seems to be working well for me on Raspi 5 booting from NVMe!

I have not tried my previous failure mode where PARTITION_WALK is enabled as I've decided it's not a good solution to any problem that I have, but the other issues which I had hit previously all do seem to be resolved. Thanks! :)

@2K-ZONE
Copy link

2K-ZONE commented Oct 13, 2025

Hello, I am actually trying this out. I came upon this here while searching for support in rauc for the Raspberry Pi 5.
Well I learned it is work in progress so I could assist in debugging and testing this out if this is appreciated.

I have a Raspberry Pi Compute Module 5 with 32GB eMMC embedded into it.
Software is a yocto scarthgap with meta-raspberrypi and meta-rauc. I replaced the rauc git build with one of these forks being mentioned here. Which is the right, more actual one?

I tested

SRC_URI = "git://github.com/bradfa/rauc.git;protocol=https;branch=wip-raspberrpi5"
SRCREV = "f008ed5b3e9a1bb78a80d687b87c4bd9cec14135"

and

SRC_URI = "git://github.com/Rtone/rauc.git;protocol=https;branch=bootchooser-add-raspberrypi-firmware-initial-support"
SRCREV = "f6b5f54bb717cf8d38a6c4ff82159e7b1e655d61"

Both working the same way so far.
I do not wan't to spam this thread with all my configuration files, will do if this is useful and wanted, of course. I stuck 1:1 to the docs/integration.rst#raspberry-pi-firmware documentation. So I have 5 partitions:

/dev/mmcblk0p1: vfat with autoboot.txt (mounted at /autoboot)

and /etc/rauc/system.conf:

[system]
compatible=revpi5connect
bootloader=raspberrypi
raspberrypi-autoboot-txt=/autoboot/autoboot.txt

[keyring]
path=ca.crt.pem

[slot.firmware.0]
device=/dev/mmcblk0p2
type=vfat
bootname=2

[slot.firmware.1]
device=/dev/mmcblk0p3
type=vfat
bootname=3

[slot.rootfs.0]
device=/dev/mmcblk0p5
type=ext4
parent=firmware.0

[slot.rootfs.1]
device=/dev/mmcblk0p6
type=ext4
parent=firmware.1

What me wonders now is that "rauc status mark-good other" does show no effect:

I am on rootfs.1:

# mount |grep mmc
/dev/mmcblk0p6 on / type ext4 (rw,relatime)
/dev/mmcblk0p1 on /autoboot type vfat (rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,errors=remount-ro)
/dev/mmcblk0p3 on /boot type vfat (rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,errors=remount-ro)

and want to mark the other one as good:

# rauc status mark-good other
rauc status: marked slot(s) firmware.0 as good

But it is not shown:

# rauc status
=== System Info ===
Compatible:  revpi5connect
Variant:
Booted from: firmware.1 (3)

=== Bootloader ===
Activated: firmware.0 (2)

=== Slot States ===
o [firmware.1] (/dev/mmcblk0p3, vfat, booted)
      bootname: 3
      mounted: /boot
      boot status: good
    [rootfs.1] (/dev/mmcblk0p6, ext4, active)
      mounted: /

x [firmware.0] (/dev/mmcblk0p2, vfat, inactive)
      bootname: 2
      boot status: bad
    [rootfs.0] (/dev/mmcblk0p5, ext4, inactive)

Did I understand correctly that the only way for rauc to instruct the bootloader is via autoboot.txt in the first partition?
It looks like this now:

# cat /autoboot/autoboot.txt
[all]
tryboot_a_b=1
boot_partition=2
[tryboot]
boot_partition=3

Is this expected behaviour or did I do something wrong?
Changing rootfs would mean mark current bad and the other healthy so I thought. Or is this functional when flashing bundle files to the other partition?

@bradfa
Copy link

bradfa commented Oct 13, 2025

@2K-ZONE I would recommend using the latest revision of this pull request and not using my fork. This pull request has been updated by gportay to include the functional changes in my fork, but their implementation is likely much more robust than mine was.

I don't think it's reasonable to try to mark the non-running firmware/rootfs combo as good on Raspi 5. Raspi 5 will only automatically "fall back" to the other firmware/rootfs slot combo during a tryboot. This is a limitation of the Raspi bootloader operation. So it's really only good to mark the current running firmware/rootfs combo as good once it has actually booted within a tryboot boot, otherwise you risk getting into a situation where the Raspi firmware consistently picks a not-really-bootable choice.

The way Raspi 5 RAUC works is after installing to the not-booted slots, then tryboot is set (tryboot is a transient flag which will cause the [tryboot] choice in autoboot.txt to be used) only for a single boot. If the boot succeeds, then RAUC mark good will mark the currently booted slot as good. If the boot fails, then the next reset will cause the Raspi 5 firmware to boot from teh [all] section of autoboot.txt, effectively the previously running slot.

There are potential ways to change how this works, such as using the PARTITION_WALK mechanism or enabling the watchdog (on newer Raspi 5 firmware releases) early enough and setting up your config.txt files in such as way as to enable a fallback to occur. But each of these come with additional complexity and caveats.

This is all a bit different from using RAUC with u-boot, where scripting can perform boot counting and automated fallbacks, and where with u-boot marking the other slot as good is totally reasonable to try.

@2K-ZONE
Copy link

2K-ZONE commented Oct 14, 2025

Thank you for the information. I admit I have not yet understand everything and I am still playing around with my setup.

Regarding the version: I should use this: https://github.com/rauc/rauc/tree/f6b5f54bb717cf8d38a6c4ff82159e7b1e655d61
right?

My "rauc --version" says "rauc f6b5f54" So this should be this especially commit version (still trying also to understand how to get the latest version of this commit).

On my system this happens:

# rauc status
=== System Info ===
Compatible:  revpi5connect
Variant:
Booted from: firmware.0 (2)

=== Bootloader ===
Activated: firmware.0 (2)

=== Slot States ===
o [firmware.1] (/dev/mmcblk0p3, vfat, inactive)
      bootname: 3
      boot status: bad
    [rootfs.1] (/dev/mmcblk0p6, ext4, inactive)

x [firmware.0] (/dev/mmcblk0p2, vfat, booted)
      bootname: 2
      mounted: /boot
      boot status: good
    [rootfs.0] (/dev/mmcblk0p5, ext4, active)
      mounted: /
# cat /autoboot/autoboot.txt
[all]
tryboot_a_b=1
boot_partition=2
[tryboot]
boot_partition=3
# rauc install revpi5connect-bundle-raspberrypi5.raucb
installing
  0% Installing
  0% Determining slot states
 10% Determining slot states done.
 10% Checking bundle
 10% Verifying signature
 20% Verifying signature done.
 20% Checking bundle done.
 20% Checking manifest contents
 30% Checking manifest contents done.
 30% Determining target install group
 40% Determining target install group done.
 40% Updating slots
 40% Checking slot rootfs.1
 46% Checking slot rootfs.1 done.
 46% Copying image to rootfs.1
 ...
  99% Copying image to rootfs.1
 99% Copying image to rootfs.1 done.
 99% Updating slots done.
100% Installing done.
Installing `/home/root/revpi5connect-bundle-raspberrypi5.raucb` succeeded

# reboot

# rauc-status
=== System Info ===
Compatible:  revpi5connect
Variant:
Booted from: firmware.0 (2)

=== Bootloader ===
Activated: firmware.0 (2)

=== Slot States ===
o [firmware.1] (/dev/mmcblk0p3, vfat, inactive)
      bootname: 3
      boot status: bad
    [rootfs.1] (/dev/mmcblk0p6, ext4, inactive)

x [firmware.0] (/dev/mmcblk0p2, vfat, booted)
      bootname: 2
      mounted: /boot
      boot status: good
    [rootfs.0] (/dev/mmcblk0p5, ext4, active)
      mounted: /
      

Shouldn't after this reboot the booted system have changed?
May be if I get a checkmark onto this, why this is not normal and should not happen or why this is reasonable I understand this.

Can I check if I booted via tryboot? Can I force it?

What I also learned is I can change the system by giving reboot command a parameter (while being on system 2):

# reboot 3

I was no aware of that, how does the "3" reach which part of the bootloader while rebooting, is this the supposed way to change the system? But this is not tryboot mechanism, right?

@bradfa
Copy link

bradfa commented Oct 14, 2025

@2K-ZONE in the rauc bundle that you installed, did it just have a rootfs? Or did it also have something to put into the "firmware" partition as well?

During a tryboot, the firmware output on the serial port should emit a line like this indicating that it is in a tryboot boot:

  3.01 TRYBOOT

and then later in the boot sequence:

  6.69 TRYBOOT_A_B_MODE

The timestamps aren't going to be consistent, but the text indicating TRYBOOT and TRYBOOT_A_B_MODE should be.

You can also verify if the current boot is a tryboot by reading /sys/firmware/devicetree/base/chosen/bootloader/tryboot (it's a binary file, using hexdump may work best). A 0 in this file indicates the current boot is not a tryboot, a 1 indicates this boot is a tryboot.

You can force tryboot for the next reboot with a vcmailbox command, like:

vcmailbox 0x00038064 4 0 1

You can check if the next reboot will use a tryboot (this is a different setting than if the CURRENT boot is a tryboot) with another vcmailbox command:

vcmailbox 0x00030064 4 0 0

The 6th 32 bit value returned will indicate 0 or 1, if tryboot is set for the next reboot.

@2K-ZONE
Copy link

2K-ZONE commented Oct 14, 2025

Currently my bundle only has a rootfs. It should also contain files for the firmware partition (since kernel modules are in rootfs, kernel comes to firmware!) but since I did no changes to firmware contents I omitted that (also do not know how to do this, yet). Need to implement later...

Thanks for the information about the expected serial output, I have have nothing hooked up to a serial since I do not know where I can access serial interface(s), this device has only an external RS-485 one, may be I need to disassemble and look inside to get access to one another...

But the information about tryboot flag in devicetree and vcmailbox is quite useful, thank you!

After reboot I read 0 (vcmailbox and tryboot flag in devicetree). According to mount and rauc status I am on system 1. This is matching autoboot.txt having the 3 in [all].

Also:

# vcmailbox 0x00030064 4 0 0
0x0000001c 0x80000000 0x00030064 0x00000004 0x80000004 0x00000000 0x00000000

If I do a rauc install ....raucb the vcmailbox output is still the same!
So I force tryboot via vcmailbox 0x00038064 4 0 1, reboot and I am on system 0!
And the flags are set accordingly (tryboot 1, vcmailbox back to 0 for next boot):

# cat /sys/firmware/devicetree/base/chosen/bootloader/tryboot|hexdump -C
00000000  00 00 00 01                                       |....|
00000004
# vcmailbox 0x00030064 4 0 0
0x0000001c 0x80000000 0x00030064 0x00000004 0x80000004 0x00000000 0x00000000

Is forcing tryboot via vcmailbox 0x00038064 4 0 1 and maintaining autoboot.txt (to change active system to the updated one without tryboot) both manually expected?
If active partition is the tryboot one rauc status mark-active could handle that (manipulating autoboot.txt), and otherwise.

@bradfa
Copy link

bradfa commented Oct 14, 2025

Currently my bundle only has a rootfs. It should also contain files for the firmware partition (since kernel modules are in rootfs, kernel comes to firmware!) but since I did no changes to firmware contents I omitted that (also do not know how to do this, yet). Need to implement later...

If you're not installing anything new to the firmware partition, then I think RAUC won't automatically understand that you would want to boot into the other slot so it won't set tryboot for you automatically.

It's not really an advisable configuration to try to end up with potentially mismatched kernel and kernel modules. I suggest that you don't wait till later to figure this part out.

Is forcing tryboot via vcmailbox 0x00038064 4 0 1 and maintaining autoboot.txt (to change active system to the updated one without tryboot) both manually expected? If active partition is the tryboot one rauc status mark-active could handle that (manipulating autoboot.txt), and otherwise.

You should not need to manually force tryboot after a rauc bundle install, rauc should automatically enable tryboot for you.

@2K-ZONE
Copy link

2K-ZONE commented Oct 15, 2025

Yes, that seems indeed to be the case.
I somehow managed to extend my rauc file with the content of the /boot partition (copied it from the WIC file into a vfat image I added that to the yocto bundle file).

I went onto system 0.
When I install the rauc file, I see firmware and rootfs updated:

# rauc install revpi5connect-bundle-raspberrypi5.raucb
installing
  0% Installing
  0% Determining slot states
 10% Determining slot states done.
 10% Checking bundle
 10% Verifying signature
 20% Verifying signature done.
 20% Checking bundle done.
 20% Checking manifest contents
 30% Checking manifest contents done.
 30% Determining target install group
 40% Determining target install group done.
 40% Updating slots
 40% Checking slot firmware.1 (3)
 43% Checking slot firmware.1 (3) done.
 43% Copying image to firmware.1
 ...
 69% Copying image to firmware.1
 70% Copying image to firmware.1 done.
 70% Checking slot rootfs.1
 73% Checking slot rootfs.1 done.
 73% Copying image to rootfs.1
 ...
 99% Copying image to rootfs.1
 99% Copying image to rootfs.1 done.
 99% Updating slots done.
100% Installing done.
idle
Installing `/home/root/revpi5connect-bundle-raspberrypi5.raucb` succeeded

After that I manually adapt cmdline and fstab (that goes into post install hooks later...).

After I rebooted I find myself on system 1!

And rauc status mark-good changes the order in the autoboot.txt:

# cat /autoboot/autoboot.txt
[all]
tryboot_a_b=1
boot_partition=2
[tryboot]
boot_partition=3
# rauc status mark-good
rauc status: marked slot(s) firmware.1 as good
# cat /autoboot/autoboot.txt
[all]
tryboot_a_b=1
boot_partition=3
[tryboot]
boot_partition=2

Heureka, nice Job guys, thank you!

@pschrtt
Copy link

pschrtt commented Oct 16, 2025

After that I manually adapt cmdline and fstab (that goes into post install hooks later...).

Hi @2K-ZONE,
what exactly did you do to those files?
In my case I have a secure boot setup using signed boot partitions and rootfs is LUKS encrypted. Would not editing cmdline break the signature and prevent booting?
Where is your cmdline file located?

@bradfa
Copy link

bradfa commented Oct 16, 2025

After that I manually adapt cmdline and fstab (that goes into post install hooks later...).

Hi @2K-ZONE, what exactly did you do to those files? In my case I have a secure boot setup using signed boot partitions and rootfs is LUKS encrypted. Would not editing cmdline break the signature and prevent booting? Where is your cmdline file located?

If you don't have secure boot enabled, then you can easily modify the cmdline in a post-install step.

But if you have secure boot enabled, then you will need to bake into your secure boot setup a way to automatically determine which rootfs is going to be the actual rootfs. This can be done within the config.txt file that's inside your secure boot signed boot.img file and by also having 2 different text files which each dictate a different kernel command line to use, based on which boot partition the Raspi 5 firmware is using. Something like this would go in your boot.img's config.txt:

# Load the corresponding Linux kernel command line based on boot partition
[boot_partition=2]
cmdline=cmdline-2.txt
[boot_partition=3]
cmdline=cmdline-3.txt

Then you have two other files cmdline-2.txt and cmdline-3.txt which contain just your kernel command line. The firmware will pick the correct one based off the config.txt setup and which boot partition it used.

@pschrtt
Copy link

pschrtt commented Oct 16, 2025

Hi @bradfa,
thank you, this is the part that confused me. I´m aware of raspberrypi/rpi-eeprom@d50b2b3

I´ll give it a try and report back how it works out for my setup. Also rpi-image-gen adds some support for OTA-stuff in the last commits, I think this can become a very streamlined overall approach.

@2K-ZONE
Copy link

2K-ZONE commented Oct 20, 2025

Well I used to put the information about the rootfs into the cmdline.txt file.
So I manually put root=/dev/mmcblk0p5 into the cmdline.txt of /dev/mmcblk0p2 and root=/dev/mmcblk0p6 into the cmdline.txt in /dev/mmcblk0p3.

And yes I was not aware of the config.txt/cmdline-2.txt+cmdline-3.txt mechanism, yet. Which may explain the questions I got. I am just trying this out.

I also mount dev/mmcblk0p1 as autoboot in the rootfs' fstab (each time the same) and the boot partition for each rootfs
(not the same). The latter can be omitted, right? Which means I do not necessarily need to mount /boot (with kernel, bin and config.txt + cmdline) on boot via fstab.

Then both rootfs including firmware can be identical...

@bradfa
Copy link

bradfa commented Oct 20, 2025

I also mount dev/mmcblk0p1 as autoboot in the rootfs' fstab (each time the same) and the boot partition for each rootfs
(not the same). The latter can be omitted, right? Which means I do not necessarily need to mount /boot (with kernel, bin and config.txt + cmdline) on boot via fstab.

Correct, you do not need to keep the boot partition that holds the kernel, config.txt, and cmdline files mounted within Linux. RAUC will mount the correct boot partition when it needs to update it.

If you find that the existing documentation written within this pull request's changes is not answering these questions clearly, please suggest documentation changes so that we can help future users to avoid having the same kinds of questions.

@pschrtt
Copy link

pschrtt commented Oct 20, 2025

Hi @2K-ZONE ,
I also do not use the two separate cmdline.txt files in my current setup.
Instead I have a initramfs (the same in each boot partition) with a small sh-script that unlocks LUKS, determines which bootfs is booted by parsing /proc/device-tree/chosen/bootloader/partition and finally does the switch-root.
There are obviously many ways to get this running. I can successfully switch slots using tryboot, but haven´t implemented the RAUC parts for now, so I guess we keep learning.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Adds new functionality or enhanced handling to RAUC

Projects

None yet

Development

Successfully merging this pull request may close these issues.