Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
"Reboot to {OS}" scripts for rEFInd Next Boot selection

Reboot to {OS}

This a collection of notes and files used in my quest to create "Reboot to Windows" and "Reboot to Linux" scripts (and desktop shortcuts) for Linux and Windows respectively that automatically reboot my system and instruct rEFInd to auto-select the appropriate OS entry.

General Information

The key for achieving this is to modify the EFI Variable PreviousBoot with GUID 36d08fa7-cf0b-42f5-8f14-68df73ed3740, which rEFInd uses to store the last entry selected in the menu and, if using the + default entry, will be used to select the default OS. By doing this, we trick rEFInd into booting the OS we choose without having to be physically there to press the keyboard.

This variable seems to use the following format:

  • 4 bytes, 07 00 00 00 (although Windows ignores this)
  • The text string of the entry, in UTF-16 Little Endian (no BOM)
  • 4 bytes, 20 00 00 00 (effectively: a space and a NUL character)

The variable doesn't need to contain the full text of the entry, either: Any substring will match. I don't know what rEFInd does in case of multiple matches; I believe it stops after the first. It's up to you to put everything in there or just a substring.

Select Next Boot OS from Linux

Linux exposes all EFI variables via efivarfs in the directory /sys/firmware/efi/efivars/, with file names {NAME}-{GUID}. Specifically, the relevant variable is at /sys/firmware/efi/efivars/PreviousBoot-36d08fa7-cf0b-42f5-8f14-68df73ed3740. These files contain the value of the variable in NVRAM and can be modified (by root only). Most of them will have the immutable flag set, to prevent errors, so you must call chattr -i /path/to/efivar before attempting to modify them.

This is enough to edit the default rEFInd entry: Write to the efivar file with the format specified in the previous point and the name of the entry you want selected and you're done. See the file below for a ready to use script that will set the value to this variable to its first command line argument with the appropriate format.

If you place that script (renamed to refind-next-boot) in your $PATH and give it the appropriate file permissions, you can just run:

sudo refind-next-boot 'Microsoft'
systemctl reboot

Those two commands can be conviniently placed in a script or desktop launcher so that you can reboot to Windows directly. You might want to add yourself to the sudoers file so that you can run that command with no password, in wich case remember to adequately secure the script: Set root as its owner and group and set permissions to 0755 or more restrictive.

And this is it. That was the easy part.

Select Next Boot OS from Windows

Ok, this is where it gets tricky. Windows has no way of giving you access to the EFI variables other than using the Windows API, specifically via GetFirmwareEnvironmentVariable/SetFirmwareEnvironmentVariable. These functions bot receive the name of the variable, its GUID surrounded by curly braces, a buffer to read/write from/to, respectively, and the length of the buffer or the data.

To call those two functions, the running process needs elevated privileges and a modification to the user access token, which aparently is a thing in Windows. All of this is only available via the Windows API, of course, so you'll need to write some C/C++ code.

Below is a script program that works essentially like the python script but for Windows. It needs to be compiled, which I painfully did using Visual Studio, a experience I wouldn't want to repeat. It works the same: Just call it with the name of the entry you want to boot or a substring of that set. Afterwards, you are free to power off or shut down your system using whatever method and rEFInd will just select the correct entry.

Of course, Windows being Windows, creating a desktop shortcut that has an icon and is just double-click-and-forget is a bit more tricky than the Linux equivalent. First, you'll need to place the compiled program someplace and set it to run as administrator (right click, Propertied, Compatibility, check Run as administrator). After that, in that same folder, create a .bat file that calls our program and restarts:

refind-next-boot "linux"
shutdown -t 0 -r

Now create a shortcut to that .bat file, place it in your desktop, give it a proper icon and name and voilà, a "Reboot to Linux" button! It will bother you with a few console windows and a UAC dialog, yes, but it's better than nothing.

#!/usr/bin/env python3
import subprocess
import sys
EFIVAR_NAME = 'PreviousBoot'
EFIVAR_GUID = '36d08fa7-cf0b-42f5-8f14-68df73ed3740'
EFIVAR_PREFIX = '/sys/firmware/efi/efivars'
PREFIX = b'\x07\x00\x00\x00'
SUFFIX = b'\x20\x00\x00\x00'
if len(sys.argv) != 2:
print('error: must pass exactly one argument', file=sys.stderr)
text = sys.argv[1]
filename = '{}/{}-{}'.format(EFIVAR_PREFIX, EFIVAR_NAME, EFIVAR_GUID)
retcode =['chattr', '-i', filename])
if retcode != 0:
sys.exit(42 + retcode)
with open(filename, 'wb') as f:
content = PREFIX + bytes(text, 'utf-16-le') + SUFFIX
#include <windows.h>
#include <strsafe.h>
#include <iostream>
const LPCTSTR STR_VARNAME = L"PreviousBoot";
const LPCTSTR STR_VARGUID = L"{36d08fa7-cf0b-42f5-8f14-68df73ed3740}";
void ErrorExit() {
DWORD error = GetLastError();
LPTSTR errorText = nullptr;
nullptr, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &errorText, 0, nullptr);
std::wcerr << L"Error " << error << L": " << errorText << std::endl;
int main(int argc, char** argv) {
if (argc != 2) {
std::wcerr << L"Error: Must have exactly one command line argument" << std::endl;
/* get the privileges necessary */
HANDLE hToken;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
LookupPrivilegeValue(nullptr, SE_SYSTEM_ENVIRONMENT_NAME, &tkp.Privileges[0].Luid);
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, nullptr, 0);
if (GetLastError() != ERROR_SUCCESS) {
/* construct the efivar content */
char* sStr = argv[1];
DWORD nStrSize = strlen(sStr);
DWORD nVarSize = 4 + (2 * nStrSize);
BYTE* lpVarData = (BYTE*)LocalAlloc(LPTR, nVarSize);
lpVarData[nVarSize - 4] = 0x20;
for (DWORD i = 0; i < nStrSize; i++) {
lpVarData[(2 * i)] = sStr[i];
lpVarData[1 + (2 * i)] = 0x00;
/* write the efivar contents to the efivar */
DWORD dwSetResult = SetFirmwareEnvironmentVariable(STR_VARNAME, STR_VARGUID, lpVarData, nVarSize);
if (!dwSetResult) {
Copy link

Darkhogg commented Jan 22, 2020

@michaeldillabough It should be agnostic to the target OS, yes, as long as the string you use to select it matches with what refind expects to see.

Copy link

johnmarshall515 commented Mar 22, 2020

Any way to get working in mac to boot into windows and on windows to boot into mac? Cannot use bootcamp utility.

Copy link

Darkhogg commented Mar 22, 2020

@johnmarshall515 From Windows to Mac you can use the same method outlined here but changing the target string to one that matches. I have no idea how to set this up on a Mac, my life is completely Apple-free.

Copy link

johnmarshall515 commented Mar 22, 2020

Ok thank you

Copy link

albertvaka commented Apr 6, 2020

To compile it using the command line on Windows (instead of creating a VS project), open the "Developer Command Prompt for VS" then type:

cl /DUNICODE  /D_UNICODE windows.refind-next-boot.cpp Advapi32.lib

Copy link

albertvaka commented Apr 14, 2020

Also thanks a lot @Darkhogg for writing this, you are amazing!

Copy link

diegodorado commented Sep 21, 2020

This is great!
I have ported the python script to bash. This way I can add it to my sudoers binaries and I can reboot without typing my password.
I use this to bind it to a command in i3
Here is the bash script

chattr -i /sys/firmware/efi/efivars/PreviousBoot-36d08fa7-cf0b-42f5-8f14-68df73ed3740
echo -ne "\x07\x00Microsoft\x20\x00" | iconv -f UTF-8 -t UTF-16LE > /sys/firmware/efi/efivars/PreviousBoot-36d08fa7-cf0b-42f5-8f14-68df73ed3740
systemctl reboot

Then add this script to your sudoers.d directory with sudo visudo /etc/sudoers.d/reboot (or wathever name you want) for passwordless sudo execution (you still have to execute with sudo, but you won't get a password prompt )

youruser ALL=(ALL) NOPASSWD: /path/to/reboot-windows-bash-script

Copy link

evan0621 commented Apr 8, 2021

chattr: No such file or directory while trying to stat /sys/firmware/efi/efivars/PreviousBoot-36d08fa7-cf0b-42f5-8f14-68df73ed3740

I got this from Ubuntu 18.04

Copy link

Darkhogg commented Apr 9, 2021

@evan0621 Are you using rEFInd? Ubuntu installs GRUB2 if I'm not mistaken, in which case this solution won't help you at all as it targets rEFInd specifically.

Copy link

Hizoka76 commented Jan 20, 2022

I guess it's simpler with the command efibootmgr:

# Next reboot on Windows
BootNumber=$(efibootmgr | sed -n "/Windows/ s/Boot\(....\).*/\1/p")

# If the number was finded
if [[ ${BootNumber} ]]
    # Update the next reboot and get the return of BootNext
    NextBoot=$(sudo efibootmgr -n ${BootNumber} | sed -n "/BootNext/ s/.* //p")

    # If the BootNext is OK
    if [[ ${BootNumber} != ${NextBoot} ]]
        sudo reboot
        # Or better for KDE : qdbus org.kde.ksmserver /KSMServer org.kde.KSMServerInterface.logout 0 1 1

    # If the BootNext isn't OK
        echo "An error has occurred, the next boot will not be ${BootNumber} !${RAZ}"

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