Skip to content

Instantly share code, notes, and snippets.

@Darkhogg
Last active July 28, 2025 02:18
Show Gist options
  • Save Darkhogg/82a651f40f835196df3b1bd1362f5b8c to your computer and use it in GitHub Desktop.
Save Darkhogg/82a651f40f835196df3b1bd1362f5b8c to your computer and use it in GitHub Desktop.

Revisions

  1. Darkhogg revised this gist Mar 5, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion !RebootToOs.md
    Original file line number Diff line number Diff line change
    @@ -48,7 +48,7 @@ Below is a ~~script~~ program that works essentially like the python script but
    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:

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

  2. Darkhogg revised this gist Jul 3, 2017. 1 changed file with 8 additions and 1 deletion.
    9 changes: 8 additions & 1 deletion !RebootToOs.md
    Original file line number Diff line number Diff line change
    @@ -45,4 +45,11 @@ To call those two functions, the running process needs elevated privileges *and*

    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 that command with the entry name as the first argument (`linux` in my case) and then call the appropriate shutdown command (`shutdown -t 0 -r` for *restart*). 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 UAC dialog, yes, but it's better than nothing.
    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:

    ```
    sudo 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.
  3. Darkhogg revised this gist Jul 3, 2017. 2 changed files with 63 additions and 2 deletions.
    6 changes: 4 additions & 2 deletions !RebootToOs.md
    Original file line number Diff line number Diff line change
    @@ -10,7 +10,7 @@ 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`
    - 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)

    @@ -43,4 +43,6 @@ Ok, this is where it gets tricky. Windows has *no way* of giving you access to

    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.

    > TO BE CONTINUED
    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 that command with the entry name as the first argument (`linux` in my case) and then call the appropriate shutdown command (`shutdown -t 0 -r` for *restart*). 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 UAC dialog, yes, but it's better than nothing.
    59 changes: 59 additions & 0 deletions windows.refind-next-boot.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,59 @@
    #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;
    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
    nullptr, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &errorText, 0, nullptr);
    std::wcerr << L"Error " << error << L": " << errorText << std::endl;
    LocalFree(errorText);

    ExitProcess(error);
    }


    int main(int argc, char** argv) {
    if (argc != 2) {
    std::wcerr << L"Error: Must have exactly one command line argument" << std::endl;
    ExitProcess(1);
    }

    /* get the privileges necessary */
    HANDLE hToken;
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
    ErrorExit();
    }

    TOKEN_PRIVILEGES tkp;
    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) {
    ErrorExit();
    }

    /* 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) {
    ErrorExit();
    }
    }
  4. Darkhogg revised this gist Jul 3, 2017. No changes.
  5. Darkhogg revised this gist Jul 3, 2017. 1 changed file with 25 additions and 0 deletions.
    25 changes: 25 additions & 0 deletions linux.refind-next-boot.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,25 @@
    #!/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)
    sys.exit(1)

    text = sys.argv[1]
    filename = '{}/{}-{}'.format(EFIVAR_PREFIX, EFIVAR_NAME, EFIVAR_GUID)

    retcode = subprocess.call(['chattr', '-i', filename])
    if retcode != 0:
    sys.exit(42 + retcode)

    with open(filename, 'wb') as f:
    content = PREFIX + bytes(text, 'utf-16-le') + SUFFIX
    f.write(content)
  6. Darkhogg revised this gist Jul 3, 2017. 1 changed file with 41 additions and 1 deletion.
    42 changes: 41 additions & 1 deletion !RebootToOs.md
    Original file line number Diff line number Diff line change
    @@ -3,4 +3,44 @@ 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.

    The key to 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.

    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`
    - 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 `linux.refind-next-boot.py` 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.

    > TO BE CONTINUED
  7. Darkhogg revised this gist Jul 1, 2017. 1 changed file with 3 additions and 2 deletions.
    5 changes: 3 additions & 2 deletions !RebootToOs.md
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,6 @@
    Reboot to OS
    ============
    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.

    The key to 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.
  8. Darkhogg created this gist Jul 1, 2017.
    5 changes: 5 additions & 0 deletions !RebootToOs.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,5 @@
    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.