Friday, April 16, 2010

Creating a bootable USB-stick

This is a short explanation on how to create a bootable USB-stick with GRUB. This stick can be used to boot an installer, rescue-mode or an on-stick installed Linux distribution.

I mainly use my USB-stick to install different versions (and architectures) of RHEL. Booting from this USB-stick and starting and starting a kickstart installation is really convenient.

Detect the USB-stick

This is a very important step and the one where most attention is needed. Detecting the device-node where the USB-stick is connected should not go wrong. Picking the wrong drive will not make your system unbootable (as we're creating a bootable disk), but all data will be lost!

To detect which device-node is used for the USB-stick, I tend to check the output from the command 'dmesg'. This commmand displays the contents of the kernel-log-buffer and the last lines contain the last events. Insert the USB-stick and shortly after that checking 'dmesg', results for me in the following output (truncated here, only the last importan messages are shown):
usb 1-2: new high speed USB device using ehci_hcd and address 5
usb 1-2: New USB device found, idVendor=1e3d, idProduct=2093
usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 1-2: Product:              
usb 1-2: Manufacturer: USB 2.0
usb 1-2: SerialNumber: 26073604A307E205
usb 1-2: configuration #1 chosen from 1 choice
scsi4 : SCSI emulation for USB Mass Storage devices
usb-storage: device found at 5
usb-storage: waiting for device to settle before scanning
usb-storage: device scan complete
scsi 4:0:0:0: Direct-Access     USB 2.0                   5.00 PQ: 0 ANSI: 2
sd 4:0:0:0: Attached scsi generic sg2 type 0
sd 4:0:0:0: [sdc] 4067328 512-byte logical blocks: (2.08 GB/1.93 GiB)
sd 4:0:0:0: [sdc] Write Protect is off
sd 4:0:0:0: [sdc] Mode Sense: 0b 00 00 08
sd 4:0:0:0: [sdc] Assuming drive cache: write through
sd 4:0:0:0: [sdc] Assuming drive cache: write through
sdc: sdc1 sdc2
The USB-stick just connected is called 'sdc' and obviously contiains two partitions: sdc1 and sdc2. The matching device-node for this USB-stick is '/dev/sdc'.

Prtitioning the USB-stick

There exist a lot of tools to perform partitioning of a disk. One of the most common tools is 'fdisk' and that is very esy to use, therefore I mostly use this tool too.

'fdisk' takes the device-node as parameter, starting 'fdisk' looks like this:
# fdisk /dev/sdc

The number of cylinders for this disk is set to 32280.
There is nothing wrong with that, but this is larger than 1024,
and could in certain setups cause problems with:
1) software that runs at boot time (e.g., old versions of LILO)
2) booting and partitioning software from other OSs

Command (m for help):
In order to make the USB-stick usable on a lot of systems, it seems to be a good idea to create a MSDOS-partitiontable on it.
Command (m for help): o
Building a new DOS disklabel with disk identifier 0x077c807a.
Changes will remain in memory only, until you decide to write them.
After that, of course, the previous content won't be recoverable.

The number of cylinders for this disk is set to 32280.
There is nothing wrong with that, but this is larger than 1024,
and could in certain setups cause problems with:
1) software that runs at boot time (e.g., old versions of LILO)
2) booting and partitioning software from other OSs
Warning: invalid flag 0x0000 of partition table 4 will be corrected by w(rite)
The only purpose of this stick will be that it is able to boot a range of kernels. Using it to save or transfer files is not important and therefore this stick will contain one VFAT partition. This makes it possible to add files and change the configuration under practically any operating system.

Create one primary partition:
Command (m for help): n
Command action
e   extended
p   primary partition (1-4)
The partition should be the first partition:
Partition number (1-4): 1
The start of the partition should be the first cylinder on the disk:
First cylinder (1-32280, default 1):
Using default value 1
By default the size of the partition will be the whole disk:
Last cylinder, +cylinders or +size{K,M,G} (1-32280, default 32280):
Using default value 32280
Change the type of the partition:
Command (m for help): t
Selected partition 1
VFAT is most portable, so choose 'c' (the hex-value for VFAT):
Hex code (type L to list codes): c
Changed system type of partition 1 to c (W95 FAT32 (LBA))
Mark the newly created partition bootable (probably not needed though):
Command (m for help): a
Partition number (1-4): 1
Check if all has been configured correctly, nothing has been saved yet:
Command (m for help): p

Disk /dev/sdc: 2082 MB, 2082471936 bytes
2 heads, 63 sectors/track, 32280 cylinders
Units = cylinders of 126 * 512 = 64512 bytes
Disk identifier: 0x077c807a

Device Boot      Start         End      Blocks   Id  System
/dev/sdc1   *           1       32280     2033608+   c  W95 FAT32 (LBA)
If all looks okay, write the changes to the partitiontable on the disk:
Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.
Syncing disks.
After this, the kernel got instructed to read the new partitiontable. The result should be that there is a new /dev/sdc1. Before this new partition can be used, it should be formatted:
# mkfs -t vfat -n ndevos /dev/sdc1
With the new filesystem on the USB-stick, it is now possible to mount the stick:
# mount /dev/sdc1 /mnt
and copy the required files for the bootloader:
# mkdir -p /mnt/boot/grub
# cp /boot/grub/* /mnt/boot/grub/
Before the bootlaoder can get installed in the Master Boot Record (MBR), it needs to get mapped, so that GRUB recognises the USB-stick as first bootable disk. This can be done by changing the file (or creating it if missing):
[root@localhost ~]# cat /mnt/boot/grub/
# this device map was generated by ndevos on eeepc
(hd0)     /dev/sdc
This example shows that (hd0) is mapped to /dev/sdc.

Installing GRUB with the updated file is done like this:
# grub --device-map=/mnt/boot/grub/

GNU GRUB  version 0.97  (640K lower / 3072K upper memory)

[ Minimal BASH-like line editing is supported.  For the first word, TAB
lists possible command completions.  Anywhere else TAB lists the possible
completions of a device/filename.]
grub> root (hd0,0)
root (hd0,0)
Filesystem type is fat, partition type 0xc
grub> setup (hd0)
setup (hd0)
Checking if "/boot/grub/stage1" exists... yes
Checking if "/boot/grub/stage2" exists... yes
Checking if "/boot/grub/fat_stage1_5" exists... yes
Running "embed /boot/grub/fat_stage1_5 (hd0)"...  27 sectors are embedded.
Running "install /boot/grub/stage1 (hd0) (hd0)1+27 p (hd0,0)/boot/grub/stage2 /boot/grub/grub.conf"... succeeded
grub> quit
The USB-stick does now contain a working GRUB and can be used to bootstrap an installer (or rescue disk) for any Fedora, RHEL and possibly other Linux distributions. The only two things that needs to be done before this works are
  1. copy the vmlinz and initrd.img to the /mnt/boot directory
  2. update the /mnt/boot/grub/grub.conf with lines to boot the new kernel (with any options you like) with the matching initrd