How to boot Linux and Windows 7 via UEFI

Don’t.
It’s a fucking pain in the ass.

Note: This refers to Windows 7 (and probably Vista and Server 2008 and older). Starting with Windows 8, the Windows installer should support UEFI better and things should be easier.

Buy a <= 2TB hard disk for Windows installations (additional Windows partitions can be on larger HDDs using GPT, it’s only painful for the system partitions). However, if you want to use a hard drive with >2TB for your Windows installation, you have to use GPT partitions (instead of the old MBR style which only supports <= 2TB disks - there you can only use the space > 2TB with ugly hacks and can’t have a continuous partition from 2TB) - and Windows can only boot from GPT partitions in UEFI mode.
To make things more challenging, Windows doesn’t offer creating a GPT partition table and partitions in the graphical installer (at least for Win7), so one has to use cmd.exe.
But don’t worry, the Linux part also sucks :-)
I’ll describe how I got Windows to install using GPT partitions on a 3TB harddisk, how to make an existing Linux (Debian Wheezy) installation boot via EFI (using grub-efi) and how I got my Mainboard ASUS Z87-A to boot this and GRUB to chainload (UEFI) Windows.

So my situation was like this: I bought a new PC with one 250GB SSD (Samsung SSD 840 Evo Series), a 3TB hard disk and the aforementioned UEFI-capable Mainboard.
I only use Windows for gaming, so I won’t spend precious SSD space for it, so I installed Linux (or rather copied an existing installation) to the SSD, using normal MBR partitions (probably GPT would have been a better choice for multiple reasons) and afterwards I wanted to install Windows 7 to the first 150GB of the HDD or so (with an additional bigger partition for Windows games later, the rest of the harddisk will be data on Linux and swap).
So, in short:

  • Huge HDD: /dev/sda, 3TB, planning Windows installation, Linux swap + data
  • Small(ish) SSD: /dev/sdb, 250GB, Linux-only, so far two partitions (about 50GB System, rest for /home)

Prerequisites

Enable UEFI boot in BIOS, but make sure to disable SecureBoot! To do that you may have to remove the keys first (if any).
All that is done in the BIOS (On mine, press DEL on boot, in that flashy BIOS-like thingy press F7 to go to “Advanced Mode”, there it’s in the “Boot” section).

You’ll need a 64bit Windows DVD to install Windows.
For the Linux part you’ll need a USB key or CD/DVD with a UEFI-capable live Linux system, I used Grml full amd64 on a 512MB USB flash drive.

Installing Windows 7 using GPT partitions

This describes how to install Windows 7 64bit onto a hard disk in UEFI mode. This only works with the 64bit version of Windows! Basically - follow this great guide: http://www.techpowerup.com/forums/threads/installing-windows-vista-7-on-a-guid-partition-table.167245/ I'll list the relevant steps here and add a bit more explanation. Note that this will erase all contents on the selected disk!
  • Boot from Windows Installation DVD in UEFI mode. On my machine, I did that by pressing F8 during boot to get the Mainboard's boot menu and then selecting the UEFI:$DVD_drive_name entry (not the $DVD_drive_name entry without the "UEFI:" prefix).
  • The Windows Installation DVD's should have booted to a menu letting you select language, keyboard layout etc. Do that and click to the next step
  • ... which lets you choose to "Upgrade" an installation or to do a fresh installation.
  • Choose neither, instead press the [Shift]+[F10] keys to get a Windows command prompt. The next steps are in that command prompt:
  • Execute diskpart to start Windows' commandline partitioning tool
  • Execute list disk to get a list of available hard drives/SSDs (within the diskpart tool, it has its own prompt and generally seems quite decent except for the lack of commandline completion).
  • Execute select disk 2 to select the second drive to partition (or another number for another drive, see the list from the last step).
  • Execute online disk to make sure the disk is running (you can probably ignore if this fails)
  • Execute attributes disk clear readonly to make sure the disk isn't set readonly (like the previous command this is just precautionary and may fail).
  • Execute clean to remove any pre-existing partition tables from this disk (i.e. the one you selected 3 steps before)
  • Execute convert gpt to use GPT partitions on this disk.
  • Execute create partition efi size=100 to create a 100MB EFI system partition.
  • Execute create partition msr size=128 to create a 128MB Microsoft Reserved Partition
  • Execute create partition primary size=120000 to create a 120GB partition for the Windows installation. Change 120000 accordingly if you want another size (should not be below 50GB or so). Note that usually the pagefile.sys needs as much space as your amount of RAM, same for hiberfil.sys, so the more RAM you have to bigger your C: drive needs to be.. These files are actually the reason I don't install Windows on the SSD - it just wastes too much space.
  • Execute format fs=ntfs label="Operating System" to format that partition with NTFS (to install windows on later)
  • Execute assign letter=C to make the last created partition the C: partition. Maybe C was already assigned to some other device, in that case you can find out which one with list volume which one it is, and then use select volume $id to select it, assign letter=X to rename it to X:, select disk 2 to go back to the disk you partitioned and formatted before (if it had the id 2...) and then assign letter=C to really assign the letter C this time, as the original C: has been renamed.
  • Execute exit to leave diskpart (you're done there)
  • Close the command prompt
  • Click "Install Windows" (or similar) and install Windows to C: If something went wrong, in the Windows installer partitioning menu it'll show the free space after the last partition in two pieces: One up to 2TB, the other one after that.
After a few reboots you should have a working Windows installation on a GPT formatted hard disk, booting in UEFI mode. If it doesn't boot from the hard disk, you may have to configure your BIOS to boot it in UEFI mode (see Prerequisites)

Making Linux boot in UEFI using grub2

This is for Debian Wheezy with grub2 1.99-27 - but it's probably the same for other distributions and newer versions of Grub. Furthermore it describes what to do when Linux is already installed, I didn't try a fresh install, no idea if debian installer currently supports GPT or UEFI.

Why do I want to do this, my Linux boots and works just fine? - Because you can only load UEFI Windows from Grub in UEFI mode, so grub needs to be started in UEFI mode as well.

I tried using Windows’ EFI System Partition on the HDD for grub as well (it would just another .efi image in there). While calling grub-install kinda succeeded (the files were where they’re supposed to be and it even listed the entry in its list of EFI boot entries afterwards), I couldn’t get it to work: When calling efibootmgr afterwards to show the EFI boot order list, the entry has vanished again. So I’ll describe how to use a seperate EFI partition for Linux, in my case on the SSD.

If you can still boot your Linux system, do so and install grub-efi, on debian wheezy the corresponding package is called grub-efi-amd64. Installing it will uninstall grub-pc (the PC/BIOS version). Also a small partition (1MB is really enough for grub!), preferably at the beginning of the disk, is needed as EFI System Partition. Because of alignment stuff I had some space there and used it. It ended up being /dev/sdb3 though (because it was created after the others), so I reordered the partitions using sfdisk:

  • sudo sfdisk -d /dev/sdb > sdb.old to save the partition table to an ASCII file
  • cp sdb.old sdb.new, then edit the sdb.new to change the partition order (rename them accordingly and reorder lines - not sure if the latter is necessary)
  • sudo sfdisk /dev/sdb < sdb.new to read in the changed partition table
  • Reordering done. That was easy :-)

Anyway, create a partition to be used as (Linux/Grub) EFI System Partition and format it as FAT (mkfs.vfat /dev/sdb1). Then boot up the live Linux and install grub-efi:

  • Boot Grml in UEFI Mode ([F8] on boot, "UEFI:Generic 500MB USB" or something like that if booting from USB key - it really needs to be UEFI mode!)
  • mount root-fs of your installation (e.g. /dev/sdb2) to /mnt/
  • if they're in separate partitions, mount the /boot/, /usr/, ... partitions of your installation to /mnt/boot, /mnt/usr/, ...
  • mount --bind /dev/ and /sys/ to /mnt/dev and /mnt/sys (maybe also /proc for older versions of grub?), grub will need those
  • Create a directory /boot/efi/: mkdir /boot/efi
  • .. and mount your EFI System Partition there: mount /dev/sdb1 /boot/efi
  • chroot /mnt
  • execute grub-install /dev/sdb (assuming your EFI system partition is on sdb) to install grub to the EFI System Partition If your EFI system partition isn't the first partition on that disk, you also have to add -p 2 to that command (assuming it's the second partition..)
  • update-grub to upgrade the grub menu entries
  • Create a /boot/efi/EFI/BOOT/ directory: mkdir /boot/efi/EFI/BOOT/
  • Copy grub's .efi image into that directory, so the Mainboard can find it: cp /boot/efi/EFI/debian/grubx64.efi /boot/efi/EFI/BOOT/BOOTX64.EFI (yes, a symlink would be the sensible thing to use here, but FAT doesn't support it.)
  • Add a line for /boot/efi to /etc/fstab, e.g.: /dev/sdb1 /boot/efi vfat defaults 0 0
  • If names of partitions changed, don't forget to adjust the /etc/fstab entries for them.
  • Reboot, go into the BIOS, it should now have an EFI entry for your Linux disk, make sure it has highest boot priority
  • Afterwards you should be able to boot to Grub, and to your Linux installation from there on.

I didn’t have to format the SSD as GPT, the Mainboard was able to do an UEFI boot from MBR partitions. This may not work with other Mainboards, in that case convert your MBR to GPT - gdisk should be able to do that without destroying your data, see http://www.rodsbooks.com/gdisk/mbr2gpt.html

Adding UEFI Windows to grub2

So what's missing? Oh right, the reason we suffered through all that pain with UEFI on Linux: Create a Grub entry for UEFI Windows!

For some reason, grub/os-prober didn’t automatically detect the Windows installation (maybe that doesn’t work for UEFI Windows installations, no idea, maybe it’s fixed in newer versions of Grub..) so I did it manually:

Add the following to /etc/grub.d/40_custom (or, if your distro doesn’t have /etc/grub.d/, add it to /etc/grub.conf or something like that):

menuentry "Microsoft Windows 7 x86_64 UEFI-GPT" {
	insmod part_gpt
	insmod fat
	insmod search_fs_uuid
	insmod chain
	search --fs-uuid --set=root 03AF-7491
	chainloader (${root})/EFI/Microsoft/Boot/bootmgfw.efi
}

where 03AF-7491 was the output of sudo grub-probe –target=fs_uuid -d /dev/sda1 (assuming your Windows EFI System Partition is /dev/sda1; the actual UUID will most probably be different for you).

Call sudo update-grub afterwards, so it gets added to /boot/grub/grub.cfg.

On next boot you should be able to select (and boot) Windows in Grub.

What I would have done differently in hindsight

Before copying over my Linux installation to my new SSD, I should have created the partitions in GPT, using gdisk or cgdisk. Except for the thing that I really should have had GPT partitions to boot via UEFI and was just lucky that the Mainboard didn't care, gdisk makes alignment much easier.

The problem is, that my SSD (Samsung SSD 840 Evo (not Pro) Series) ideally is aligned to its “erase block size” of 1536KB (3072 sectors of 512bytes), which is unusual, normally it seems to be 1024 or 2048 sectors, so (c)fdisks default alignment of 2048 sectors is suboptimal for me.
Furthermore the SSD does not tell the operating system that it likes that alignment, so partitioning tools like GNU parted, cfdisk, .. really can’t guess the right alignment automatically - unfortunately they don’t allow to specify a manual alignment (at least I didn’t find the corresponding option), so I specified aligned sectors by hand.. not fun and so 80ies. Anyway, gdisk allows to specify the alignment in sectors, which would have been exactly what I needed. Furthermore, I would have aligned to 6144 sectors (2*3072), just to be sure - that would work for both 3072 and 2048 aligned drives.

And of course I shouldn’t have wasted two days with trying to use Windows’ EFI System Partition, but should have created for Linux/Grub on my SSD in the first place.

Questions, additions, .. are welcome :-)
(Note that I’m far from being an expert on UEFI, I just somehow managed to get my shit to work)