slicer69 / sysvinit

GNU General Public License v2.0
111 stars 20 forks source link

Support `reboot` parameters to support Raspberry Pi `TRYBOOT` functionality #22

Closed srcshelton closed 6 months ago

srcshelton commented 7 months ago

As documented at [1], the Raspberry Pi project has a fail-safe boot solution which involves passing a string to the reboot command, which the firmware interprets on restart to determine the boot partition.

I believe this is using, e.g.:

int syscall(SYS_reboot, ..., LINUX_REBOOT_CMD_RESTART2, "0 tryboot");

Would it be possible to add an optional parameter to halt/reboot/shutdown to accept a command-line string parameter from the user and populate this into the void *arg parameter of the SYS_reboot LINUX_REBOOT_CMD_RESTART2 syscall?

Update: I see there was discussion of this back in 2017, see [2].

[1] https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#fail-safe-os-updates-tryboot [2] https://github.com/raspberrypi/linux/issues/2095

slicer69 commented 7 months ago

I think we could do something like this. Since reboot is mostly just there to call "shutdown -r", we'll probably put the code in shutdown as an optional parameter.

So instead of using "reboot 0 tryboot" the user would probably run something like

  shutdown -r -m '0 tryboot' now

If it is necessary for some reason for reboot to also accept an extra parameter we could probably do that too.

The one stumbling block I potentially see is it looks like systemd's reboot only accepts the one parameter while sysvinit's reboot command can accept multiple flags. So we can't assume the only argument to reboot is a message intended to be passed along.

Thoughts on this? Can we get away with an implementation where calling "reboot -m '0 tryboot'" is acceptable?

srcshelton commented 7 months ago

So long as there's a way to achieve this, I don't think it's very much of an issue at all if it requires an additional flag/option!

slicer69 commented 7 months ago

I've added experimental support for this feature in the halt/reboot command. If the user runs

   reboot -f -m "0 tryboot"

the reboot command will attempt to pass the quoted text to the firmware on next boot. I don't have a Raspberry Pi to test this at the moment so maybe someone else can give it a shot? I can confirm the system reboots as expected on a x86 machine when the above command is used.

If this works as expected I might also extend the shutdown command too, but one thing at a time.

srcshelton commented 7 months ago

maybe someone else can give it a shot?

Unfortunately, I'm away now and for the next week… but a I'll definitely test this once I'm back home!

slicer69 commented 7 months ago

Sounds good to me. Let me know if this works and I'll get it in place for the next release of SysV init.

srcshelton commented 6 months ago

Sorry for taking so long to get back to you on this - I've now had chance to build 3.09 and gave it a test… and it works!

[  195.174692] reboot: Restarting system with command '0 tryboot'
1705920081 serial 81d57ffa boardrev d03140 stc 477186
PM_RSTS: 0x00000020
part 00000000 reset_info 00000001
Initialising SDRAM 'Micron' 32Gb x2 total-size: 64 Gbit 3200
DDR 3200 1 0 64 152
TRYBOOT

TRYBOOT

😁

… could I also enquire whether it would also be possible to add an equivalent -m option to shutdown also, so that the -f flag to reboot isn't required (… only valid with shutdown -r, I guess)?

slicer69 commented 6 months ago

I think I can add that. Will look into it soon. Thanks for testing.

slicer69 commented 6 months ago

I have updated the shutdown command in the 3.09 branch. It now accepts -m followed by a reboot message to pass to the firmware.

Please test when you have time and let me know if it works for you? If it does, then I'll likely publish a new version of SysV init next week.

srcshelton commented 6 months ago

Hmm - I'm not so sure about this latest change, I'm afraid:

Previously, issuing shutdown -r now would cause the system to cleanly step back through its runlevels, shutting down services in the process.

With latest sysvinit-3.09, issuing shutdown -m '0 TRYBOOT' -r now instantly hard-rebooted the host.

I was anticipating that even with the -m option, an clean shutdown would still be possible?

slicer69 commented 6 months ago

"an clean shutdown would still be possible?"

Probably not, at least not the way host distributions are set up. When used normally, shutdown doesn't actually shutdown the system. It just hands control over to whatever the init system uses to handle halting. This could be a program, a script, a service manager, etc. We don't have any way of knowing for sure. It's sort of a "blind handoff".

In order for shutdown to pass a message to the firmware it's got to force a shutdown on its own, which means skipping over all the normal processes. OR handing the message to those normal processes and hoping they'll know how to handle the message to firmware.

Since I don't think any distro does the latter, that leaves us to short-circuit and handle it ourselves. Unless support for handling firmware messages is added to the various distro shutdown processes. Then we could pass the message along.

srcshelton commented 6 months ago

Ah - my reading was that shutdown actually does stuff and then invokes halt or reboot, and so would only need to be changed to accept a -m option and then pass that to reboot when called?

(I note that you can do shutdown -m '…' -h <time>, which perhaps should be a warning or error since I don't believe any string would persist when the firmware is next invoked?)

slicer69 commented 6 months ago

That would be convenient, but no. The shutdown command basically signals that the system is going down and then passes the job off to init (normally). On most systems init then calls whatever process or script is configured to be run at the new runlevel (typically runlevel 0 or 6).

The manual page sums it up: "shutdown does its job by signalling the init process, asking it to change the runlevel. Runlevel 0 is used to halt the system, runlevel 6 is used to reboot the system, and runlevel 1 is used to put to system into a state where administrative tasks can be performed; this is the default if neither the -h or -r flag is given to shutdown. To see which actions are taken on halt or reboot see the appropriate entries for these runlevels in the file /etc/inittab."

You're right, the -h flag probably shouldn't be accepted along with the -m flag since it'll cause a proper halt/poweroff.

srcshelton commented 6 months ago

Fair - it's likely a larger change, then, but could any message provided by the user to shutdown be prodded into (by default) init such that if/when run level 6 is reached, the (effectively cached) value is used in the reboot syscall?

Alternatively, might there be the possibility of looking for a (compile-time/configurable?) 'magic' file somewhere on the root filesystem into which any message has been written, and then using these contents if they exist?

slicer69 commented 6 months ago

In theory, yes, it's possible. But it's a long series of changes and pretty out of scope. This is the sort of thing the distribution's init scripts/service manager should be handling, in my opinion. Upstream is here to trigger a runlevel change, not determine what happens when the runlevel changes if we can avoid it. Passing messages to init/runlevel 6 is not something we're going to implement upstream because there are so many variations on how distributions handle the shutdown/runlevel 6 process. And distributions usually have a way to pass messages or alter behaviour already.

The proper way to get this introduced, in my opinion, would be to have the shutdown/halt/reboot script (or service) check its configuration file or options file for a message to pass to the firmware.

For example, on Debian (and therefore Raspberry Pi OS) the halt/reboot command checks /etc/defaults/halt for options, variables, and messages prior to shutting down. Ideally the distro will have a variable there which can be used to trigger a message to the firmware.

srcshelton commented 6 months ago

In that case, perhaps it's better to revert the changes to shutdown (so as not to surprise users with an unexpected immediate unclean reboot), and independently I'll try to figure out the sequence of scripts involved in a clean shutdown (which I had assumed was much more hard-coded than it sounds as if it actually is!)

slicer69 commented 6 months ago

I'd agree with that. I'll undo the shutdown changes and leave the halt/reboot changes in place in case distributions want to support passing messages to firmware that way.