MartLugt - How I wake my dual-booted PC from a distance
Obi Wan Kenobi from Star Wars as a swan (Obi Swan Kenobi), smoking a blunt.

How I wake my dual-booted PC from a distance

Lately, I’ve been trying to use an iPad for all my on-the-go computing needs, to avoid having to lug a laptop around as well. The newer iPad's are incredibly powerful devices, and are more than sufficient for 90% of my use cases. Up until now, there has only been one real obstacle, and that has been the disability to code on an iPad. iPadOS, with its locked down architecture and insufficient file management system, has not yet produced an on-device coding environment that is comparable to one you can create with Visual Studio Code or a full-fledged IDE.

Luckily, Microsoft has lately released Visual Studio Code Server, which allows you to remotely connect to a development environment. The best part is that you can run this on your own development machine, and thus code directly on your own machine!

This however of course requires my PC to be on, and running Linux, where my development environment resides. This would pose no problem, except I sometimes need Windows to be running when I have to run particular programs through RDP. I therefore would like to be able to, from a distance, boot my PC to either Linux or Windows.

Implementation
link

To do this, I’m going to implement a solution suggested by user arch1t3cht30 via superuser. The implementation of this solution requires a home server (though it can probably also be implemented using a remote server, with a bit of added hassle), for which I’m using a RPI4 running home assistant. The basic idea is that we serve a file on this server, which our PC can then load during the boot process. This file then contains the boot settings for the OS we want our computer to boot.

For this we need to set up a TFTP server, which is a very simple protocol that allows us to serve files over the network. We also need to set up Grub to be able to load the file we serve on the TFTP server. Lastly, we need to create a script that changes the boot settings file we serve and then boots the computer.

TFTP
link

As mentioned earlier, we need to run a TFTP server. I’m going to do this by creating a new add-on (which is basically a docker container) in home assistant, but if you have a regular Linux home server running it is perhaps easier to install a TFTP server natively. The home assistant Docker set up for the server looks as follows:

./Dockerfile
content_copy
light_mode
Dockerfile
ARG BUILD_FROM
FROM $BUILD_FROM

RUN apk add --no-cache tftp-hpa

# Copy data for add-on
COPY run.sh /
RUN chmod a+x /run.sh

CMD [ "/run.sh" ]
doneCode copied
closeCopying failed

It is important to note that in the add-on setup in home assistant, I mapped the share map to the container, and also exposed the needed port (port 69).

The entry point for the container is the run.sh file, which simply lists the files we're serving and starts the TFTP daemon:

./run.sh
content_copy
light_mode
sh
echo "Starting server."
echo "Sharing files:"
ls /share/srv/tftp

in.tftpd -L --secure /share/srv/tftp
doneCode copied
closeCopying failed

We can now add a demo file to the server's share directory and test the server by running the following command:

$
content_copy
light_mode
tftp <server_ip_address> -c get demo_file && cat demo_file
doneCode copied
closeCopying failed

Grub
link

Next, we need to set up Grub to load our file from the server.

Firstly, it is important to enable the network of your computer during the boot sequence. This is done in the BIOS, and this option is, in most cases, called either Network Stack or Network boot. Enabling this allows Grub to use the network, which is needed for what we are going to do next. Then we can try to retrieve a file from our TFTP server. First trying this in the Grub console allows us to test the commands and also see if the BIOS is set up correctly.

The commands that worked for me are:

/etc/grub.d/40_custom
content_copy
light_mode
# Load necessary modules
insmod net
insmod efinet
insmod tftp
# Boot the network and retrieve IP address using DHCP
net_bootp
# Retrieve and apply the TFTP file
source (tftp,<server_ip_address>)/boot_config
doneCode copied
closeCopying failed

After figuring out what commands are needed, these can be added to the end of the grub config at /etc/grub.d/40_custom. Make sure to rebuild the grub configuration and add some grub commands (such as default="2") to the boot configuration file on the TFTP server. Now, while booting, these commands should be executed, and you should be able to see this reflected in the grub boot process!

Server script
link

The last step is to change this boot configuration based on the OS we want to boot. To do this, we just have to write the default="<index>" command to the configuration file (where index is the index of the item in the grub list we want to boot).

To do this, I created a script in home assistant that sets the default index in the file to 2 (which is the index in Grub for my Linux installation), wakes the computer using a magic packet, waits a certain time to allow grub to boot, and changes the file back to an empty file, which causes Grub to boot the default OS (Windows in my case). As arch1t3cht30 explains, this can also be done with a simple shell script on the server:

./boot_change.sh
content_copy
light_mode
sh
echo 'default="<boot-target-index>"' > /share/srv/tftp/boot_config
wol <PC MAC-address>
sleep 15     # wait for GRUB to boot
echo '' > /share/srv/tftp/boot_config
doneCode copied
closeCopying failed

How I boot
link

In the end, I have set up a home assistant script such that I only have to press one button to boot my computer. Using the home assistant app, I can also add buttons to the home screen of my iPad, to make the booting experience frictionless!

Two buttons: one showing Boot Windows and one showing Boot Linux. In the middle of them is an indicator of the PC status.
The buttons on my home assistant dashboard.