Tutorial: Dual boot Linux + Windows 10 BIOS mode with BCDEDIT

 In this tutorial, we will set up a dual boot PC with Linux and Windows 10 for a very specific scenario:

-The PC will boot to the Windows boot menu by default
-GRUB will be chainloaded from the Windows boot menu
-The PC is BIOS only, no EFI at all
-Windows and Linux are installed on separate drives
-BCDEDIT is used to add a Linux entry to the Windows boot menu

In this case I'm using Windows 10 and Linux Mint Debian Edition to do this, but It's totally doable with different versions of Windows and different Linux distributions, of course, with some minor differences, but the overall process would be the same.
I did this installing Linux in a different drive, but you can do it in separate partitions of the same drive. Look out for the big warning at the beginning of the tutorial, in case you want to install both OS in the same drive.
So, let's get started.

First of all, why do this?

SHORT STORY.
Because I found myself in a very specific situation: There was this old PC, the motherboard was 100% BIOS. Windows 10 was installed on the PC and my admin permissions were limited and I wanted to do many things, that required admin privileges.
So, one thing I could sneakily do, was adding an extra HDD and installing a secondary OS there. So I choose LMDE, since the PC is old and Linux is better suited for that case.
Any sane person would choose GRUB as the default boot loader since it's just better, more flexible and easier, but then again, I wasn't supposed to tamper with this PC in any way, so If the PC started to GRUB anyone would notice something weird.
I could set Win 10 as the default system to boot and a super short time to choose, but I didn't want to take any chance, so the default had to be the Windows boot menu everyone is familiar with.
Also, I couldn't install EasyBCD or anything else, I just can't install anything on that PC, I mean in Windows, so it had to be done with BCDEDIT.
So, I made Win 10 the default boot entry and added an extra one, but instead of "Linux" I just labeled the entry as "Recovery Options" :D and it looks 0% suspicious.


So, I found myself in that situation and of course there are a ton of tutorials explaining how to set up a dual boot Win / Linux PC but in the year 2025, you can bet every tutorial out there is for EFI methods. I had to dig a little deeper and I found three main sources from where I could put the pieces of the puzzle together.

SOURCES:
The Arch Linux wiki that has a dual boot article and brief sections dedicated to the BIOS scenario and also the booting from the Windows boot loader scenario.
This excellent tutorial that is recommended by the Arch Linux wiki and is just what I was trying to do, except that it works for the single drive Linux and Windows in different partitions setup.
This blog post that was actually found in the comments of the previous mentioned tutorial and it was the key to make everything work on separate drives.
These tutorials were made for Windows Vista, 7, 8 or even XP, so they're naturally outdated but it all helped me to make it work for Windows 10.
 
IMPORTANT:
This tutorial assumes that you are familiar with the Linux installation process of your distribution, the command line in both Linux and Windows, BIOS booting order, devices and configuration and concepts like MBR.
So, I won't be stopping too much on that kind of details.
The tutorial will be referring to /dev/sdx as the generic drive, but you have to correctly identify and replace the x with the corresponding letter of your Linux installation.
 
WARNING:
If you're attempting to follow this tutorial for dual booting Windows and Linux from different partitions on the same drive, there is one important difference.
You should install Windows first, so all the boot config data is written to the MBR of the drive. Then when you install your Linux distribution you have to be sure that GRUB will be installed to your Linux partition and NOT to the root of your drive.
You have to be sure that GRUB is installed to /dev/sdx1 or /dev/sdx2 (the dive partition corresponding to your Linux system) and no to /dev/sdx (the drive itself). 

TUTORIAL:
 
1-Installing Windows and Linux.

Whether you have one or the two systems already installed, or just one of them, the process will be the same. But if you're going to make a clean installation of any of the systems, I'd recommend plugging in only the drive where you're going to install either Windows or Linux, but it's better not having the two drives plugged at the moment of installing a system, just to avoid any risk of confusion and overwriting the MBR. 

Remember, if you have a dedicated drive just for Linux, it's okay to install GRUB to the MBR of the device. You can install to /dev/sdx.
But if you are attempting dual booting two systems present in the same drive, you have to install it to /dev/sdx1 or sdx2 or whatever is your Linux install partition.
Otherwise, the Windows boot loader will be replaced.

At the end of this step, you should have one drive with Windows installed and one drive with Linux installed and you should be able to boot to any of them, by changing the boot order in your BIOS.
 

2-Creating a copy of the MBR

In order to chainload GRUB to the Windows boot loader, we're going to need a BIN copy of our MBR (a copy of the boot sector in the case when GRUB is installed to a partition).
 
If you aren't aware of where GRUB was installed, it's no problem, we're going to check it out, to be sure of what we're doing. For this, you have to be able to boot into Linux, whether by changing the boot order in the BIOS or with your Linux install media, such as a bootable USB drive.

Once in Linux, open the terminal and run this command:
sudo dd if=/dev/sdx bs=512 count=1 | hexdump -C
 
Of course, you have to change the x in sdx to where you know or think that the boot loader was installed. If it was installed in the MBR it should be in /dev/sdx and if it was installed to a partition, it should be in /dev/sdx1 or sdx2, sdx3...

What this command will do, is read the first sector of the drive and output the data as a readable hex dump with three columns. You should look for the word GRUB in the third column.

You should see something like this, and it means GRUB is there.
 
And if GRUB is not installed on the targeted location, you are probably just going to see a bunch of zeroes.
So, use this command to help you be 100% sure of where GRUB was installed, because we need to make a copy of it.

Let's say you found GRUB installed on the MBR, so /dev/sdx
Now we're going to use basically the same command, but instead of outputting the data as a hex dump, we will copy the data to a BIN file.

Run this command to create the copy:
dd if=/dev/sdx of=/path/linux.bin bs=512 count=1
 
if=/dev/sdx is our input file and of=/path/linux.bin is the output file, you can name it how you want and you have to set the path to where you can easily find it.


3-Taking the BIN file to Windows

Now we have to take the BIN file we just created to your Windows drive. You can use an USB drive, cloud storage or whatever works for you.
In Windows, you can place the linux.bin file in the root or C: or anywhere really, I put it not even in C: but in a system partition where the Windows boot loader is also located.


4-Creating a new entry for Linux in the Windows boot menu

In Windows, we will use the BCDedit command line tool to create a new entry for our Linux system. So open CMD or PowerShell with elevated permissions.

First, we will create a new entry:
bcdedit /create /d "Linux Mint" /application BOOTSECTOR

Of course, instead of "Linux Mint" you can label your entry the way you want.
I labeled it "Recovery Options" so no one has a clue that is actually a different system. 

After running this command, you will be provided with a GUID, which is basically an identifier for your new entry. It should look something like this: {583b454f-e590-4fdd-8899-4f87e61ad619} and you have to copy that GUID, because we're going to work with it.
I will be referencing to it just as {GUID}
 
Note: If you're using PowerShell every time you put the {GUID} in a command, it has to be enclosed by quotation marks or it won't work. It has to be: "{GUID}".

Now, we have to set a partition for the new entry. It has to be the partition where we put the .bin file, so if you put it on C:, it will be C:, but if it's located in a different partition as F:, we have to set it to that one.
bcdedit /set {GUID} device partition=C:

The partition is clear now, but now we have to set the exact path to the .bin file:
bcdedit /set {GUID} path \linux.bin
 
This would mean we put the linux.bin file just in the root of C: but if we put it for example, in a folder named "boot" inside the root of C:, the path should be \boot\linux.bin

The new entry is created and correctly pointing out to the linux.bin file, but for it to appear in the boot menu, we have to add it to the display order:
bcdedit /displayorder {GUID} /addlast

You can also use the options /addfirst or /remove depending on what you want.

And finally, we can set the seconds we want to pass before the Windows boot menu goes with the default entry if you don't interrupt it:
bcdedit /timeout 10

Now, to verify that everything is in order, you can run simply the bcdedit command, without any option and it should display the Windows boot manager, a boot loader entry for Windows and a real-mode boot sector for Linux.

And with this, we have successfully added a new entry.
The next time Windows starts, the boot menu will be displayed, giving us two options: boot Windows or boot Linux.
If you have both systems in different partitions of the same drive, you're done. This configuration should work.
But if you have separate drives for the systems, we have one final step to take.


5-Editing the BIN file so it can find the second stage of GRUB.

Now this part is the tricky one. The piece of the puzzle that was missing to really make it work, to chainload GRUB and the Windows boot loader, when they're in separate drives.
And that piece, was found in this blog post: http://blog.mattrudge.net/2010/08/04/booting-ubuntu-through-ntldr/ 
It's currently offline... But thanks to The Internet Archive's Wayback Machine, I was able to save a copy. You can check it out here: Booting Ubuntu through NTLDR or Windows Boot
It explains well the way GRUB works and why you can't chainload Windows -> Linux unless you do something extra...

The short explanation: the GRUB present in the MBR or the boot sector of a partition, is only a small part or a first stage of GRUB, a small program called bootstrap that, by default, will always try to load the second stage or the rest of GRUB from the same drive it starts on.
So, in order to tell the bootstrap to look for the second stage in a different drive, we will have to edit the BIN file with a hex editor.
If you want to understand why and how this work, I really recommend you to check out the original post.

We will be using XVI32, as advised on the post.
 
 
Open up your BIN file with XVI32.
Press CTR + G, the "go to" shortcut.
Then you have to go to the absolute hexadecimal address $64.
The value of said address should be "FF".
Once located in that address go to Edit -> Overwrite string
Replace the FF value with 81.
And save the changes to the BIN file.
 
 
And that's it!
Now GRUB should be able to load the rest of GRUB from a different drive than the one it started.
So, if anyone is struggling with old BIOS computers and ever want to do something like this, hope they can find this and maybe help them.
 
 
 

Comments