In this document I describe my setup about how to add a menu shortcut that will hibernate and reboot the system into a different OS (in my case, Windows), and then restore linux on the next reboot.
For this, I'll be using Arch Linux with systemd-boot as my boot manager, but in practice any bootloader that handles the LoaderEntryOneShot (a.k.a. BootNext) efivar should work.
NOTE It is advisable to not do this with window's fast boot feature active, since alternating the hibernation of two systems can cause shared partitions to get corrupted. However, if additional steps are taken in order to ensure the shared partition are unmounted before reboot, or no partitions are shared between the two OSs, this can be made to work with fastboot enabled which should give quite a boost in Windows' startup time.
First, we want to set this efi variable in order to tell the system that a different OS should be loaded on next boot, and only that next boot. In systemd-boot this means that not even the OS selection screen will be shown and the selected OS will be loaded directly.
This can be accomplished using efibootmgr
To show the current boot entries:
# efibootmgr
Example output:
BootCurrent: 0002
Timeout: 1 seconds
BootOrder: 0002,0000,0005,0006
Boot0000* Windows Boot Manager
Boot0002* Linux Boot Manager
Boot0005* UEFI: IP4 Qualcomm Atheros PCIe Network Controller
Boot0006* UEFI: IP6 Qualcomm Atheros PCIe Network Controller
Here, we get the hex code of the OS we want to boot, in our case, 0000. Then, we can use efibootmgr to set the LoaderEntryOneShot efivar with --bootnext
efibootmgr --bootnext 0000
Now, upon reboot, entry 0000 will be loaded automatically, and the variable will be reset on the next boot.
The s2disk
command, available as part of the uswsusp
(AUR) package can hibernate the system, and has a flag which allows us to set the shutdown method to reboot just on a single hibernation. However, s2disk
for some reason didn't work on my machine, so I made an equivalent (although less robust) systemd-based configuration.
First, a script enable-hibernate-reboot
stored in my $PATH
#!/bin/bash
if [ -f /etc/systemd/sleep.conf ]; then
sudo mv /etc/systemd/sleep.conf /etc/systemd/sleep.conf.bak
fi
sudo printf "[Sleep]\nHibernateMode=reboot" > /etc/systemd/sleep.conf
And also, a disable-hibernate-reboot
stored at the special folder /usr/lib/systemd/system-sleep
:
#!/bin/bash
if [ -f /etc/systemd/sleep.conf.bak ] && [ $1 == "post" ] && [ $2 == "hibernate" ]; then
mv /etc/systemd/sleep.conf.bak /etc/systemd/sleep.conf
fi
The script in this folder will be run after hibernation with $1="post" and $2="hibernate" (see sleep.conf.d manpage) when resuming from hibernation, thus restoring the old sleep.conf. Note that for the hibernation that will reboot to windows, any configuration you had previously set up in /etc/systemd/sleep.conf
(if any) will be disabled.
Now, after runing enable-hibernate-reboot
the computer will reboot after hibernation, but only once.
Finally, we can set up a wrapper script to glue everything we've done so far:
#!/bin/bash
sudo /path/to/enable-hibernate-reboot
sudo efibootmgr --bootnext 0000
sudo systemctl hibernate
Note that the script needs to be run as root. You can work around this by not requiring sudo password for your user just for this three commands.
- https://www.freedesktop.org/wiki/Software/systemd/systemd-boot/
- https://wiki.archlinux.org/index.php/Unified_Extensible_Firmware_Interface#Requirements_for_UEFI_variable_support
- http://www.johnhawthorn.com/2011/03/hibernate-with-reboot/
- https://www.freedesktop.org/software/systemd/man/sleep.conf.d.html
https://gist.github.com/frebib/4d8eff2528237b86c7108dc5cbe6f54b
Thanks for your work here, it has been very useful!