June 09, 2014

Creating UEFI bootable USB flash drive with several Linux distributions

Here I would like to describe the way of creating UEFI bootable USB flash drive with several Linux distributions

All you need is Linux and USB flash drive

Let’s assume you are already in Linux and have just plugged a new USB flash drive which will be mapped as /dev/sdb device We will create necessary partitions and temporarily mount them at /target later

Creating GPT partition table and EFI System Partition

First, I recommend to clean the beginning of the USB flash drive in order to not mess up with different partition tables

dd if=/dev/zero of=/dev/sdb bs=2M count=2
sync ; echo 1 | tee /proc/sys/vm/drop_caches

 

Then, create a new disklabel (partition table) of GPT type and following partitions Note: You may want to start my first partition with offset 2MB (instead of 0%) if you find your USB stick performing the best in this layout (you can use flashbench tool to find what is the best erase block size for you)

parted -a optimal /dev/sdb
mklabel gpt
unit MB
mkpart bootefi fat32 0% 200M
set 1 boot on
mkpart bootiso ext4 200M 20G
mkpart private ext4 20G 100%

unit MB
print
print free
align-check optimal 1
align-check optimal 2
align-check optimal 3
quit

Note that the boot flag parted sets is NOT the same thing as the bootable flag in the old master boot record. It’s confusing that parted choose to use the same name, but they are different. Setting the boot flag by parted in a MBR partition marks that partition bootable, while in a GPT partition it is marked as EFI System Partition (it has EF00 code if you check with gdisk -l /dev/sdb).

The EFI System Partition must be explicitly mounted at /boot/efi to properly load via the GNU GRUB2 bootloader.

  • /dev/sdb1 will be later mounted as /target/boot/efi
  • /dev/sdb2 will be later mounted as /target/boot and we created it large enough to store different Linux distros
  • /dev/sdb3 you can use it as you like :-)

Creating required filesystems

Now create filesystems

mkfs.vfat -F32 -n EFIBOOT /dev/sdb1
mkfs.ext4 -L bootiso /dev/sdb2

Mount them in the following order

mkdir -p /target/boot

mount /dev/sdb2 /target/boot
mkdir /target/boot/efi

mount /dev/sdb1 /target/boot/efi

The most important moment – installing the grub2

grub-install --removable --no-uefi-secure-boot --target=x86_64-efi --efi-directory=/target/boot/efi --boot-directory=/target/boot --bootloader-id=grub --recheck /dev/sdb

This is how the data should look like after above steps have been applied

# ls -latrh /target/boot/grub/
total 48K
drwxr-xr-x 8 root root 4.0K Jun   9 17:50 ..
-rw------- 1 root root 1.3K Jun   9 18:40 grub.cfg
drwxr-xr-x 2 root root 4.0K Jun   9 18:45 fonts
-rw-r--r-- 1 root root 1.0K Jun   9 18:45 grubenv
drwx------ 5 root root 4.0K Jun   9 18:45 .
drwxr-xr-x 2 root root   12K Jun   9 19:18 x86_64-efi
drwxr-xr-x 2 root root 4.0K Jun   9 19:18 locale

# find /target/boot/efi/ -ls
         1       4 drwxr-xr-x     3 root         root                 4096 Jan   1   1970 /target/boot/efi/
         4       4 drwxr-xr-x     3 root         root                 4096 Jun   9 18:45 /target/boot/efi/EFI
         6       4 drwxr-xr-x     2 root         root                 4096 Jun   9 18:45 /target/boot/efi/EFI/BOOT
         9   120 -rwxr-xr-x     1 root         root             119296 Jun   9 19:18 /target/boot/efi/EFI/BOOT/BOOTX64.EFI

Disk layout

# parted /dev/sdb print
Model: SanDisk Extreme (scsi)
Disk /dev/sdb: 62.7GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt

Number  Start   End     Size    File system  Name     Flags
 1      1049kB  200MB   199MB   fat32        bootefi  boot
 2      200MB   20.0GB  19.8GB  ext4         bootiso
 3      20.0GB  62.7GB  42.7GB  ntfs         private

# parted /dev/sdb unit s print free
Model: SanDisk Extreme (scsi)
Disk /dev/sdb: 122544516s
Sector size (logical/physical): 512B/512B
Partition Table: gpt

Number  Start       End         Size       File system  Name     Flags
        34s         2047s       2014s      Free Space
 1      2048s       391167s     389120s    fat32        bootefi  boot
 2      391168s     39061503s   38670336s  ext4         bootiso
 3      39061504s   122544127s  83482624s  ntfs         private
        122544128s  122544482s  355s       Free Space

Creating extra filesystem

You can create any filesystem you wish on /dev/sdb3 partition, e.g.

FAT32

mkfs.vfat -F32 -n shared -v /dev/sdb3

NTFS

mkfs.ntfs -Q -L shared -I -v /dev/sdb3

EXT4

mkfs.ext4 -L shared /dev/sdb3

NOTE: if your shared partition (sdb3) will be used for NTFS, then don’t forget to set “msftdata” flag on it, otherwise Windows won’t mount it

(parted) set 3 msftdata on

Generating a GRUB config file

Do not forget to create /target/boot/grub/grub.cfg file

You can always auto-generate it to look what it has detected and which modules does it use by default

grub-mkconfig -o /target/boot/grub/grub.cfg

But that’s not what we are here for, our goal is to upload several Linux distributions to our USB flash drive so that we can use any of them :-)

My configuration is the following

# cat /target/boot/grub/grub.cfg

set timeout=15
set default=0

# (U)EFI Graphic Protocol
insmod efi_gop
insmod efi_uga
insmod font

if loadfont ${prefix}/fonts/unicode.pf2
then
       insmod gfxterm
       set gfxmode=auto
       set gfxpayload=keep
       terminal_output gfxterm
fi

menuentry "Linux Mint 17 MATE DVD" {
               loopback loop /isos/linuxmint-17-mate-dvd-64bit.iso
               linux (loop)/casper/vmlinuz file=/cdrom/preseed/linuxmint.seed boot=casper iso-scan/filename=/isos/linuxmint-17-mate-dvd-64bit.iso quiet splash --
               initrd (loop)/casper/initrd.lz
}

menuentry "SystemRescueCd 4.2.0" {
               loopback loop /isos/systemrescuecd-x86-4.2.0.iso
               linux (loop)/isolinux/rescue32 setkmap=us isoloop=/isos/systemrescuecd-x86-4.2.0.iso
               initrd (loop)/isolinux/initram.igz
}

menuentry "Tails 1.0" {
               loopback loop /isos/tails-i386-1.0.iso
               linux (loop)/live/vmlinuz fromiso=/dev/disk/by-uuid/5e5ca198-7e43-a0dc-91eb-98ebec1274b5/isos/tails-i386-1.0.iso boot=live config live-media=removable nopersistent noprompt timezone=Etc/UTC block.events_dfl_poll_msecs=1000 splash noautologin module=Tails truecrypt quiet
               initrd (loop)/live/initrd.img
}

menuentry "Kali Linux 1.0.6" {
               loopback loop /isos/kali-linux-1.0.6-i386.iso
               linux (loop)/live/vmlinuz fromiso=/dev/disk/by-uuid/5e5ca198-7e43-a0dc-91eb-98ebec1274b5/isos/kali-linux-1.0.6-i386.iso boot=live noconfig=sudo username=root hostname=kali
               initrd (loop)/live/initrd.img
}

menuentry "Debian Live (amd64)" {
               loopback loop /isos/debian-live-7.5.0-amd64-gnome-desktop.iso
               linux (loop)/live/vmlinuz fromiso=/dev/disk/by-uuid/5e5ca198-7e43-a0dc-91eb-98ebec1274b5/isos/debian-live-7.5.0-amd64-gnome-desktop.iso boot=live config quiet splash
               initrd (loop)/live/initrd.img
}

menuentry "Debian GNU/Linux installer boot menu" {
       insmod part_gpt
       insmod ext2
               loopback loop /isos/debian-7.5.0-amd64-netinst.iso
               linux (loop)/install.amd/vmlinuz fromiso=/dev/disk/by-uuid/5e5ca198-7e43-a0dc-91eb-98ebec1274b5/isos/debian-7.5.0-amd64-netinst.iso boot=live vga=788 -- quiet
##               linux (loop)/install.amd/vmlinuz fromiso=/dev/disk/by-uuid/5e5ca198-7e43-a0dc-91eb-98ebec1274b5/isos/debian-7.5.0-amd64-netinst.iso vga=788 -- quiet
##               linux (loop)/install.amd/vmlinuz fromiso=/dev/disk/by-uuid/5e5ca198-7e43-a0dc-91eb-98ebec1274b5/isos/debian-7.5.0-amd64-netinst.iso boot=live config live-media=removable nopersistent vga=788 -- quiet
               initrd (loop)/install.amd/initrd.gz
}

#menuentry "Debian GNU/Linux installer boot menu (Graphical)" {
#               loopback loop /isos/debian-7.5.0-amd64-netinst.iso
#               linux (loop)/install.amd/vmlinuz fromiso=/dev/disk/by-uuid/5e5ca198-7e43-a0dc-91eb-98ebec1274b5/isos/debian-7.5.0-amd64-netinst.iso video=vesa:ywrap,mtrr vga=788 -- quiet
#               initrd (loop)/install.amd/gtk/initrd.gz
#}

To construct linux & initrd lines I have used the contents of isolinux/menu.cfg file (in some cases it is different, but easy to find using grep) that can be accessed if you simply mount/open the .iso file

The path to /dev/disk/by-uuid/5e5ca198-7e43-a0dc-91eb-98ebec1274b5 is used in regards to the UUID of the /dev/sdb2 partition, in your case it will be different so you should replace it with yours

# ls -la /dev/disk/by-uuid/5e5ca198-7e43-a0dc-91eb-98ebec1274b5
lrwxrwxrwx 1 root root 10 Jun   9 10:36 /dev/disk/by-uuid/5e5ca198-7e43-a0dc-91eb-98ebec1274b5 -> ../../sdb2
# blkid   |grep sdb2
/dev/sdb2: LABEL="boot" UUID="5e5ca198-7e43-a0dc-91eb-98ebec1274b5" TYPE="ext4"

Copying Linux distros onto your USB flash drive

Now you just have to create the directory (where you will put your Linux distros) on your /dev/sdb2 device that is mounted as /target/boot and copy your Linux distros there as follows

mkdir /target/boot/isos
cp ~/Downloads/linuxmint-17-mate-dvd-64bit.iso /target/boot/isos/
cp ~/Downloads/systemrescuecd-x86-4.2.0.iso /target/boot/isos/
cp ~/Downloads/tails-i386-1.0.iso /target/boot/isos/
cp ~/Downloads/kali-linux-1.0.6-i386.iso /target/boot/isos/

 

When you are done copying, safely unplug the USB flash drive

umount /dev/sdb1
umount /dev/sdb2
umount /dev/sdb3

sync ; echo 1 | tee /proc/sys/vm/drop_caches

udisks --detach /dev/sdb

 

Now you can boot any Linux distribution on your choice from your USB flash drive!

  screenshot