Hardware boot selection switch

A physical switch for my computer to choose between Linux and Windows

Similar projects worth following
After a decade of dual-booting Linux, I got tired of waiting around to change the GRUB boot selection any time I wanted to boot Windows. With a little tinkering, here's a physical switch that determines the OS to boot each time the computer is turned on.

This uses an STM32 microcontroller to act as a USB mass-storage device, serving up a dynamic file. This file can be loaded by the system's boot config to change its boot behaviour based on the physical input:

A full write-up of the process to arrive at this solution is in the project log.

Source code is available on GitHub.

Making one yourself

The entire solution is in code, so all you need is a toggle switch, a USB-capable STM32 microcontroller and an ST-Link programmer. The classic "blue pill" STM32 dev board is already supported (STM32F103). The "black pill" and Adafruit Feather (both STM32F4xx) are capable of this, and support is on the way for those.

Follow the instructions in the GitHub repository to build and flash your chip.

  • 1 × Toggle switch Any kind of electrical switch you like
  • 1 × STM32 microcontroller with USB support F0 and F1 parts with USB support should work without modification,. Other STM32 series will require minor modification to the code to support

  • Hardware OS selection switch

    Stephen Holdaway05/03/2021 at 20:41 7 comments

    Dual-booting Linux and Windows is a great way to get the best of both worlds, but there's one thing that's always bothered me. To boot into Linux, I simply press the power button and walk away. To boot into Windows on the other hand requires a tactical, precision-timed strike on the keyboard to change the selection when GRUB briefly reveals itself:

    Now I could just increase the GRUB selection timeout, or remove it entirely, but I'd still need to wait around to make an operating system selection. I could use the mode in GRUB that remembers the last OS selection, but I'd still need to be around to change it half the time. I could make a "reboot into Windows" action in Linux, but I'm just as often booting from a powered-off state as I am rebooting from Linux.

    Since I always know which operating system I want ahead of time, why not make a physical switch to select between Linux and Windows?

    Scripting in the boot loader

    GNU GRUB is a popular Linux boot loader - a program that runs before any operating system is loaded to decide what to boot and how to boot it. You'll normally interact with a menu like the one pictured above, but under the hood GRUB is configured by a simplified scripting language. Underneath that it's a system of modules written in C.

    My only hands-on experience with GRUB script to date has been those occasional times where a system fails to boot and you find yourself plunged into a shell with the prompt "grub>". You fairly quickly discover that while this looks like typical Linux shell with ls, cat and tab completion, it is very much not. Running outside of an operating system, GRUB has to supply all of its own tools, so the functionality available is fairly bare-bones.

    Arbitrary USB device access in GRUB?

    My first thought was creating a USB device with a custom ("vendor-specific") interface to read out the switch position, which in non-USB terms is kind of like a bare serial connection. This is straight forward enough to access from an operating system, but I wasn't sure if GRUB could handle it.

    GRUB does implement native USB support in its ehci, uhci and ohci modules, but there's a catch - loading any of these disables the normal mechanism used to access disks through the BIOS to avoid conflicts, leaving you with no disk access. There is a nativedisk module for accessing disks independently of the BIOS, but slowness aside, using this module critically means that GRUB can't chain-load Windows (explanation), making this approach a write-off.

    In short, native access to arbitrary USB devices from GRUB isn't practical for this project, but USB isn't off the table entirely...

    Pretending to be a USB mass-storage device

    Instead of making a custom USB interface, we can leverage the fact that the BIOS already provides GRUB with access to all attached storage devices. All we would need to do is present our device as storage, containing a file whose contents indicate the switch position.

    This is conceptually simple, but there are a few layers to it:

    1. Provide the mass-storage class descriptor, indicating one of several storage protocols to use (SCSI, ATA).
    2. Implement the chosen storage protocol. This is a set of commands to interrogate the storage device's capabilities, capacity, layout and other metadata in addition to standard requests to read and write sectors.
    3. Emulate a valid filesystem when read from, without actually having any storage medium.

    Using the USB-capable STM32 boards and code I already have from USB Status Light, changing a vendor class device to a mass-storage class device was a matter of changing a few bytes in the existing USB descriptors.

    For the storage protocol layer, I was happy to find that libopencm3 has a built-in SCSI mass-storage implementation with simple read_block(address) and write_block(address) callbacks, hiding the complexity of the storage protocol:

    Thanks to this, I was up and running fairly quickly with a recognisable storage device, even...

    Read more »

View project log

Enjoy this project?



Rakinuzzaman wrote 06/06/2021 at 09:18 point

hi! I would like to know if anyone knows if  this is possible to implement using Arduino pro micro ( Atmega 32u4, that supports usb-HID) ?

  Are you sure? yes | no

Stephen Holdaway wrote 06/06/2021 at 09:40 point

It should be possible to combine an existing atmega32u4 mass-storage class implementation with the fake filesystem part of my firmware. The complexity is configuring the specific USB peripheral in that device. Here's something you could start with:

You can also do something equivalent with any flash drive and a toggle switch (or even just plugging and unplugging the flash drive). I haven't had a chance to write this up yet, but that's an option if you don't want to deal with the USB device part.

  Are you sure? yes | no

Ruediger Willenberg wrote 2 days ago point

Hi Stephen, great work!

A coworker sharing your post finally gave me a kick in the butt to finish my own project, which had been languishing on my shelf for almost 2 years...

@Rakinuzzaman, I think it might be what your looking for... it supports a bunch of ATmega32u4 devices, including Arduino Micro and Pro Micro, and is pretty easy to adapt to other 32u4 boards:

I'm hopefully not being rude by advertising my own project here - love your project, Stephen!

  Are you sure? yes | no

Madrajib wrote 05/22/2021 at 10:41 point

Great project Stephen. I managed to port the same changes in Raspberry Pi Pico.

  Are you sure? yes | no

kaustubh wrote 05/24/2021 at 17:52 point

Thanks a lot man...gave you a star on github

  Are you sure? yes | no

Mars wrote 05/18/2021 at 08:28 point

Any chance this could be used on an Apple machine to triple boot Windows, OS X and Linux?

  Are you sure? yes | no

Stephen Holdaway wrote 05/19/2021 at 05:07 point

If you're using startup manager (ie. holding alt when booting), there's probably not a whole lot of clean options. First thing that comes to mind is somehow detecting boot and sending the appropriate keypresses automatically.

If you don't mind picking your next boot from OSX though, It looks like that can be done from the command line:

  Are you sure? yes | no

Mars wrote 05/19/2021 at 08:16 point

Don't know if you've ever heard of the OpenCore bootloader, but if you have, could it support such a solution?

  Are you sure? yes | no

Andreas Puhl wrote 05/13/2021 at 13:00 point

I absolutely love the idea behind the project as it solves a problem (or rather just an annoyance) I have with my machine in a simple and elegant way. And having a physical switch is a beautiful icing on the cake.

I would very much love to build this, but I find the instructions a bit overwhelming. I have never done anything with STM32 or development boards in general, this is my first attempt. I bought a Blue Pill and had a true "uh.. now what?" moment. I read up on the basics of STM32 and gather that I need some sort of an interface to program it. Like an FTDI or ST-Link to even begin with the code, correct? Because the on-board usb port is only for power. But then... once I have your code on the chip, is it the on-board usb port I use to connect it to the pc anyway?

My apologies if that is a very noob question, but I am just that. :)

If it not too much to ask, could you maybe broaden the guide a little bit in such a way to help people get started that have no experience with dev boards? It wouldn't have to be overly detailed, just giving the necessary key words for me to look up and research.

  Are you sure? yes | no

Stephen Holdaway wrote 05/19/2021 at 04:57 point

Yeah, I'm planning to add more details to make this more approachable. Hopefully will get some time to write that up this weekend.

You're correct that you'll need an ST-Link to program the board. To give a really rough run-down:

1. Aquire STM32F103 board

2. Connect a programmer (eg. ST-Link, J-Link, BusPirate, etc) to the SWD header on the board. Many boards will have a 3.3V pin in this header which can be left unplugged if your programmer doesn't have a 3.3V source or a Vref pin.

3. Connect a USB cable to the board (provides power and USB and data lines for the microcontroller to act as a USB device)

4. Run the build and flash instructions from the GitHub repository

As I've mentioned in a few comments here, it's also possible to do something similar with just a toggle switch and a regular flash drive, but I'm happy to help people get their feet wet with STM32 / ARM Cortex M development if they're interested!

  Are you sure? yes | no

Frank Dana wrote 05/13/2021 at 03:03 point

Hah! That's clever, nicely done.

Though, TBH what I _really_ want — my holy grail of dual-boot tooling — is a 100% **software** solution that's also fully integrated into both OSes' native UI tools.  IOW, I want the Windows Start menu to show a "Reboot to Linux" option directly beneath/alongside the "Reboot" option. And vice versa, in GNOME Shell's user menu.

Bonus points if:

1. There's a way to indicate up front that it's only a one-time switch, not a change of default... but in a way that special-cases post-upgrade/install reboots so that they return to the same OS regardless, to avoid breaking the housekeeping tools.

2. Both OS's "Hibernate" options temporarily override the Grub menu entirely, so that booting back to the same OS that generated the hibernation image is the only available option for a resume boot.

For the longest time, one of the biggest sticking points to implement any of these sort of features was the question of how Grub could safely/correctly be configured from the Windows side of things. But now that every UEFI system has a FAT32-formatted boot partition with a known directory structure that's accessible from every installed OS, there's a realistic path that doesn't require jumping through too many crazy hoops.

(I briefly experimented with using a Linux /boot partition that was formatted NTFS instead of ext4, specifically to make it safely writable from both Windows and Linux. Grub2 was perfectly happy with it, but twice a year the NTFS-formatted /boot would reliably break my attempt to upgrade that machine to a new Fedora release. The tools just aren't equipped to handle the unexpected filesystem properties of an NTFS (or FAT) filesystem mounted at /boot. ...Unrelated, but I also ended up removing a gid=10 mount option I'd hoped to set on the /boot/EFI mount, as that caused me similar problems.)

  Are you sure? yes | no

Michael O'Brien wrote 05/12/2021 at 22:48 point

I was hoping this was [much] more hardware vs software. I was expecting something more along the lines of switching device enumeration order on SATA ports by the switch toggling which piece of hardware is “plugged” into which SATA port. Then again, I assume that this type of solution was [also] avoided since UEFI would still preference the boot volume via UUID vs device enumeration.

  Are you sure? yes | no

Stephen Holdaway wrote 05/12/2021 at 23:24 point

You can achieve this pretty simply by switching power to a flash drive and checking for the presence of that volume UUID using a similar grub script modification. This ended up partly being an exercise in making a USB device, but the same principles I ended up using can be leveraged in other ways!

  Are you sure? yes | no

Michael O'Brien wrote 05/12/2021 at 23:49 point

Sounds good. Granted, you do have to have some intermediate, as you pointed out, else it’d be just turning a drive off while turning another on, thus loosing access to that data.

I recently learned about cloning/copying a grub-based MBR for dual booting a non-UEFI, Win10 system between Win 10 and linux. Yeah, I *could* use grub for that, but since Windows thinks it’s the only one that matters, I wanted to figure out how to use that bootloader.

  Are you sure? yes | no

Superpomme wrote 05/12/2021 at 18:05 point

Nice, I made a thing a while back for launching different things on booting to windows (using AHK) which was really useful for arcade stuff, but it couldn't change anything at a bootloader level. Yours looks great.

  Are you sure? yes | no

Dan Julio wrote 05/12/2021 at 16:50 point

Very cool.  Shades of the IBM5100 "Portable Computer" from the 1970s.   Some models had a switch that selected between APL and BASIC.

  Are you sure? yes | no

João Pedro Hegedus Vellenich wrote 05/12/2021 at 15:34 point

Someone has the link to buy the STM32 microcontroller with USB support??

  Are you sure? yes | no

Stephen Holdaway wrote 05/12/2021 at 18:35 point

Note that you can also achieve this by switching the 5V line on and off to a flash drive, then checking if that filesystem can be found to infer the switch position. I'll do a small write up in the next few days on this, since it's much simpler than the approach I took, with only minor drawbacks!

If you do want to experiment with STM32 microcontrollers, any STM32F103 board with an 8MHz crystal will work out of the box with the current code. You'll also need an ST-Link to program the chip. Search on your preferred site for "STM32F103" and "ST-Link".

Other STM32 chip and crystal combinations will work with additional code for clock configuration, but that's quite advanced, so I would recommend sticking to known combinations if you're new to STM32.

  Are you sure? yes | no

PMercier wrote 05/08/2021 at 17:50 point

sweet :) it was in my todolist because i never found one already made. Thank you my todo stack got one entry deleted. Great idea to use the stm as a dynamic mass storage. 

  Are you sure? yes | no

Joonas Pihlajamaa wrote 05/08/2021 at 10:46 point

Very cool! Always wanted to do this, but thought a custom grub module would be needed. You could even do "Windows power button" and "Linux button" with ATX power control. I had the power side covered in (still using this, thankfully not currently dual booting)

  Are you sure? yes | no

Stephen Holdaway wrote 05/09/2021 at 04:26 point

Nice, that's a cool project. I used to use Wake-on-LAN quite a lot, but it definitely gets fiddly requiring the PC to be in a specific state. Did you ever figure out your up-time issue with the Wemos D1?

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates