The following requires a computer using a BIOS to boot or supporting some form of "legacy" boot mode (Compatibility Support Module) – at least I think it does: I have no experience with EFI systems.
To create a bootable usb stick with syslinux you:
- create the partitions
- initialise the file-systems on the partitions
- install syslinux onto a partition
- mark syslinux partition active
- install syslinux' mbr code
After these steps you should be able to boot from the usb stick and, upon booting from the stick, will be greeted with the syslinux boot prompt; there you can issue commands to load further code and continue the boot process.
In order to not having to type a long commandline on every boot one can create a file syslinux.cfg to hold the commandline, possibly multiple.
Commonly one also installs syslinux modules by copying the appropriate *.c32 files to the syslinux directory of the partition; examples for these modules are: menu.c32, chain.c32.
Example Image
Since I do not have a free usb stick lying around I created an 2GB image for the example by issuing:
dd if=/dev/zero of=example.img bs=1k count=2M
which I then attached to a loop device by running:
losetup --find --show example.img
This command requires the parameter max_part of the loop module to be set to a number at least as big as the number of partitions in the image file:
rmmod loop
modprobe loop max_part=10
If the loop module was built into the kernel you can specify max_part as a boot paramter to the kernel:
loop.max_part=10
Alternativly you may use kpartx
to set the loop devices up:
kpartx -av example.img
Device nodes for the partitions can be found below /dev/mapper/. If you use kpartx
and want to follow the example along you should set the dev_part variable appropriately.
Variables
For the examples I have set the following variables:
# device node
dev=$(losetup --find --show example.img)
# set to e.g. /dev/mapper/loop0 when using kpartx
dev_part=${dev}
# loop devices use the letter 'p' to denote partitions (e.g. /dev/loop0p1)
# drives normally do not (e.g. /dev/sda1)
part_divider=p
# path to syslinux modules and mbr
libpath=/usr/lib/syslinux
# Path to mount point; used when e.g. files need editing
mount_point=/mnt
# The number of the partition that you use to share files with windows
files_part_nr=1
# The number of the boot partition
boot_part_nr=2
# The number of the partition that holds a linux distribution CD's content
dist_part_nr=3
As an example distribution I chose Tiny Core Linux:
# Download Tiny Core's iso and set variable "isofile" to its destination
# filename
isofile=$(curl -# -OJ -w '%{filename_effective}\n' "http://tinycorelinux.net/6.x/x86/release/TinyCore-current.iso")
The curl command does the following:
-#
Show progress with a bar on the terminal
-O
Write file to a from the url derived name
-J
Derive name from the "Content-Disposition" header instead of the url
-w
Write filename to the standard output (which will be stored in the variable "isofile")
Create Partitions
MBR
# Create an empty msdos style partition table
parted -s "${dev}" mktable msdos
# Create two primary partitions (i.e. sdb1, sdb2)
# one holding your files and one for syslinux
parted -s "${dev}" mkpart primary fat32 0MB 800MB
parted -s "${dev}" mkpart primary ext2 800MB 900MB
GPT
# Create an empty gpt partition table
parted -s "${dev}" mktable gpt
# Create two primary partitions (i.e. sdb1, sdb2)
# one holding your files and one for syslinux
parted -s "${dev}" mkpart files fat32 0MB 800MB
parted -s "${dev}" mkpart boot ext2 800MB 900MB
Initialise File-Systems
# DATA
mkfs.vfat "${dev_part}${part_divider}${files_part_nr}"
# BOOT
mkfs.ext2 "${dev_part}${part_divider}${boot_part_nr}"
Install syslinux (extlinux) onto a partition
Mount the boot partition and run extlinux
:
mount "${dev_part}${part_divider}${boot_part_nr}" "${mount_point}"
mkdir "${mount_point}/syslinux"
extlinux -i "${mount_point}/syslinux"
umount "${mount_point}"
You may choose any other directory on the partition to hold syslinux' files: put files under the root directory: extlinux -i "${mount_point}"
; put files under a directory called /boot: extlinux -i "${mount_point}/boot"
.
With syslinux the command would look as follows (You should not have the boot partition mounted):
syslinux -i -d /syslinux "${dev_part}${part_divider}${boot_part_nr}"
Mark syslinux partition active
The default syslinux MBR bootloader code searches for a partition marked active to pass control to find the Volume Boot Record to load.
MBR
# Mark boot partition as active (MBR)
parted "${dev}" set "${boot_part_nr}" boot on
GPT
# Mark boot partition as active (GPT)
parted "${dev}" set "${boot_part_nr}" legacy_boot on
If you use gdisk to partition your drive you need to activate "extra functionality" by hitting x
in order to be able to "set attributes" (a
).
Install syslinux MBR code
dd conv=notrunc bs=440 count=1 if="${libpath}/mbr/mbr.bin" of="${dev}"
For a GPT partitioned drive you use gptmbr.bin instead of mbr.bin.
There also exists an alternative loader altmbr.bin that does not search for a partition marked active, but instead uses a specified partition; use it as per the following:
print '\x2' | cat ${libpath}/mbr/altmbr.bin - | dd conv=notrunc bs=440 count=1 iflag=fullblock of=${dev}
Boot
Add this point you should be able to start from the device and be greeted by the syslinux boot prompt.
qemu-system-i386 -drive file="${dev}",format=raw
Since syslinux is only able to load files from the same partition you will not yet be able to continue the boot process to an OS.
Load a Linux Distribution via Chainloading
Create Partition
Create another partition; either run (for an MBR type partition):
parted "${dev}" mkpart primary ext2 900MB 100%
or use (for GPT):
parted "${dev}" mkpart distribution ext2 900MB 95%
Then initialise the file system:
mkfs.ext2 "${dev_part}${part_divider}${dist_part_nr}"
Chainloading
Syslinux' chain.c32 module allows you to chainload to the Volume Boot Record of another partition which can be used to work around syslinux' limitation of only being able to load files of the partition whence it was loaded; that way one can also reuse the syslinux/isolinux configuration files included in the ISO image.
In order to be able to use the chain.c32 module you need to copy it and its dependencies (libcom32.c32 and libutil.c32) into the syslinux directory on the boot partition:
mount "${dev_part}${part_divider}${boot_part_nr}" ${mount_point}
cp "${libpath}/modules/bios/libcom32.c32" ${mount_point}syslinux/
cp "${libpath}/modules/bios/chain.c32" ${mount_point}/syslinux
cp "${libpath}/modules/bios/libutil.c32" ${mount_point}/syslinux
umount ${mount_point}
Next you unpack the ISO-image and rename isolinux.cfg to the name expected by syslinux: syslinux.cfg
mount "${dev_part}${part_divider}${dist_part_nr}" "${mount_point}"
7z x -o"${mount_point}" "${isofile}"
mv "${mount_point}/boot/isolinux/isolinux.cfg" "${mount_point}/boot/isolinux/syslinux.cfg"
I find in cleaner to also rename the isolinux/ folder to syslinux/, although that is not strictly necessary; I also remove some unneeded files:
mv "${mount_point}/boot/isolinux" "${mount_point}/boot/syslinux"
rm "${mount_point}/boot/syslinux/isolinux.bin"
rm -r "${mount_point}/[BOOT]"
TinyCore's iso seems to miss libutil.c32 (needed for menu.c32):
cp ${libpath}/modules/bios/libutil.c32 "${mount_point}/boot/syslinux"
Install the Bootloader on the partition and unmount:
extlinux -i ${mount_point}/boot/syslinux
umount ${mount_point}
Boot
When you now boot the image
qemu-system-i386 -drive file=${dev},format=raw
and enter the following at the syslinux boot prompt:
chain.c32 hd0 3
you should get greeted by the Tiny Core Linux syslinux menu.
Configuration
Mount the boot partition and create a syslinux.cfg file:
mount ${dev_part}${part_divider}${boot_part_nr} ${mount_point}
cat >${mount_point}/syslinux/syslinux.cfg <<EOF
LABEL tc
MENU LABEL Boot TinyCore
COM32 chain.c32
APPEND hd0 3
umount ${mount_point}
On disk partitioned using the GPT format you can use the partition's GUID or its label passing either guid=
or label=
to chain.c32:
APPEND label=<LABEL>
or
APPEND guid=<GUID>
You can use blkid
to find both label and guid.
Grub2
GPT
For GPT grub2 requires an extra partition to store some of its boot code. With parted
you create one with the following commands:
parted ${dev} grub_boot 95% 100%
parted ${dev} set bios_grub on
With gdisk
you chose ef02 as the type for the partition.
According to the grub info document it should have a minimum size of 31KiB.
Install grub
mount ${dev_part}${part_divider}${boot_part_nr} ${mount_point}
# use --modules="part_gpt" for a gpt partition drive
grub-install -d ${mount_point} --modules="part_msdos ext2"
umount ${mount_point}
I do not know whether --modules
is strictly necessary – at least using a loop device grub was not able to access files without it.
Chainloading
At the grub boot prompt type:
insmod chain
set root=(hd0,3)
chainloader +1
boot
And you should see the Tiny Core Linux syslinux menu again.
Configuration
For the Tiny Core Linux example put the following into /grub/grub.cfg on the boot partition:
menuentry "Tiny Core Linux" {
insmod chain
set root=(hd0,3)
chainloader +1
}
To be independent of the order that the drives get listed in you can set the root by UUID; replace set root
with:
search --set root --fs-uuid <UUID>
Use blkid
to obtain the UUID.
Boot an ISO
Some distributions' live CDs can mount their file-system from within an ISO-image; together with grub's loop
feature you can do the following (as an example I use the Archlinux) Live CD):
isofile=archlinux-2015.11.01-dual.iso
mount ${dev_part}${part_divider}${dist_part_nr} ${mount_point}
cp ${isofile} ${mount_point}
umount ${mount_point}
Then use the following in your grub.cfg:
menuentry "Archlinux LiveCD" {
insmod loopback
set isofile="archlinux-2015.11.01-dual.iso"
search --set isodrive --file /$isofile
probe --fs-uuid --set uuid $isodrive
loopback loop ($isodrive)/$isofile
set root=loop
echo "Loading kernel vmlinuz"
linux /arch/boot/i686/vmlinuz img_loop=$isofile img_dev=/dev/disk/by-uuid/$uuid
echo "Loading initial ramdisk archiso.img"
initrd /arch/boot/i686/archiso.img
}
I found the needed boot parameters for the Archlinux ISO in the archiso git tree.
FAT
is the official/boot
format. It's the only filesystem that is required to recognized by a UEFI firmware. With a modern system, you don't need syslinux or anything like it - just a folder at the root of the tree named BOOT. – mikeserv Nov 26 '15 at 9:24