Close
0%
0%

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

  • Simplified hardware boot switch

    Stephen Holdaway5 days ago 0 comments

    If you liked the idea in the original post but found the microcontroller and firmware part a bit much, I've got good news! You can make your own equally effective dual-boot selection device using as little as a single USB flash drive.

    Old USB Flash Drive and a toggle switch with the text "Any USB Drive" overlaid
    An old 1GB flash drive and a well-loved toggle switch I had lying around.

    Booting based on drive presence

    Out of the box, GRUB supports scanning for a filesystem by its UUID using the search command. This is typically used to locate your operating system drive independent of the order hardware is connected, but it conveniently reveals a secondary bit of information: if the drive is missing.

    If you only need to choose between two boot options, connecting or disconnecting a flash drive with a known filesystem ID gives a signal that can be easily read in the boot script to change the default selection:

    # Detect if drive exists to determine OS to boot
    search --fs-uuid --set switch_drive C539-84FE
    
    if [ "${switch_drive}" ] ; then
        # Boot Windows
        set default="2"
    else
        # Boot Linux
        set default="0"
    fi

    Searching for a filesystem is a reasonably generic operation, so if you're using a different bootloader there's probably something similar available that you could leverage.

    To get the UUID for a drive under Linux, you can use blkid. If you don't know the device name, running blkid with no arguments will print information for all mounted filesystems.

    Linux blkid command line output example

    See the readme from the original build for instructions on integrating this snippet into your GRUB boot script.

    A more sophisticated variant of this plain-old-flash-drive solution is used by a nicely documented project called The Grub Switch, which also has a microcontroller implementations for Teensy, ATMega32u4 and some other common maker boards.

    Switching power to the flash drive

    While you could physically plug and unplug the flash drive from a USB port as a functional boot selection method, it's not quite as satisfying or easy as flicking a big clunking toggle switch.

    Off-the-shelf USB power switches are available from many suppliers, so it's possible to rig up a boot switch without touching a soldering iron at all, if that's more your style:

    Product search results page for USB power switch

    For demonstration purposes, I'm modifying an old 1GB flash drive to add a custom toggle switch. This isn't required, but it's one way to switch the drive's power if you're comfortable soldering small things.

    I initially wanted to take the USB connector off the flash drive's PCB, but it was fairly solidly connected so I changed tack to splicing onto the 5V trace as it comes off the USB connector. All flash drives will be different, so if you're doing this, confirm which pin is 5V with a multimeter and inspect the traces to find a suitable place to hack onto your particular PCB.

    Once the trace to splice is identified, scratch away the solder mask, cut the trace with a sharp blade, and solder small wires to each side of the cut.

    With a toggle switch connected to the other end of the spliced in wires, this old flash drive is now ready to toggle boot selection:

    Simple USB dual boot selection switch Linux and Windows

    Happy hacking! Try to repurpose an old flash drive that's too small for general use, rather than buying a brand new one ⭐

    Trade-offs vs a microcontroller solution

    Limited window for selection

    As this method relies on the BIOS / UEFI to detect if the mass storage device is turned on, you likely can't reliably change the boot selection once power-on / reset has begun. With the microcontroller solution, the switch position can change right up to the moment the GRUB script is executed.

    A little messier to add more switch positions

    One flash drive provides two states. If you wanted to select between three or more options, you'd need to add more flash drives and wire up a multi-position switch to power the right drive(s) for each position.

  • Hardware OS selection switch

    Stephen Holdaway05/03/2021 at 20:41 8 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 all 2 project logs

Enjoy this project?

Share

Discussions

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:

https://github.com/joric/lufa/tree/promicro/Bootloaders/MassStorage

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 06/19/2021 at 20:45 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:

https://github.com/rw-hsma-fpga/grub-switch/

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. https://bit.ly/2Tbt0Kx

  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:

https://discussions.apple.com/thread/7740130

  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. https://youtu.be/E1EBh4cCbCQ

  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 https://codeandlife.com/2019/06/10/power-up-your-computer-wirelessly-with-wemos-d1-mini/ (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