What iPXE can do

We can boot from an iPXE image of various kinds (e.g. ISO image, USB stick, .efi executable) or get a bootable image over the network by the system’s native PXE booting method.

The iPXE image can have embedded in it a script that tells it where to go and get and display a menu of installation options. It then has the ability to pull in the installation files by http and boot and complete the installation.

Example iPXE setup

Building an iPXE image

Get the source:

git clone git://git.ipxe.org/ipxe.git

In the directory ipxe/src create an iPXE script that will get embedded in your images - this can go out and chainload another iPXE script (which is our installation menu) via http.

Contents of file ipxe/src/chain.ipxe:

#!ipxe

dhcp
chain http://192.168.1.25/install.ipxe

Build the image with the script chain.ipxe embedded in it.

For an ISO image, in the directory ipxe/src:

make bin/ipxe.iso EMBED=chain.ipxe

For a standard USB stick image:

make bin/ipxe.usb EMBED=chain.ipxe

For an image to get by standard PXE by network boot:

make bin/undionly.kpxe EMBED=chain.ipxe

This will require setting up DHCP and TFTP server in an appropriate way to serve the file undionly.kpxe. See http://ipxe.org/howto/chainloading

However, to boot EFI systems by PXE you will need to serve ipxe.efi.

This means that if you want a setup that works with legacy and EFI systems, the DHCP server needs to be able to detect the type of system that is connecting and specify which file they get (see below).

For an EFI executable:

make bin-x86_64-efi/ipxe.efi EMBED=chain.ipxe

[Note: this last failed for me with recent source (November 2015) but worked fine with an earlier version (June 2015). I’m not sure if this is a problem with the source or with the tool chain.]

[Note: it’s also possible to use the on-line build setup at https://rom-o-matic.eu/, which is capable of embedding scripts. Choose the "Advanced" menu and "EFI PXE bootstrap 64-bit (.efi)"]

Installation menu

The file install.ipxe in this case is in the root of the web server at 192.168.1.25 and can look something like this:

#!ipxe

:start
menu Please choose an operating system to install
item --gap openSUSE
item 42.1 openSUSE Leap
item tumbleweed.20151022 tumbleweed.20151022
item 13.2-64-bit openSUSE 13.2 64-bit
item --gap SLES 12
item sles12-ga SLES 12 GA 64-bit
item --gap ipxe shell
item shell       Drop to iPXE shell

choose target && goto ${target}

:failed
echo Booting failed, dropping to shell
goto shell

:shell
echo Type 'exit' to get the back to the menu
shell
set menu-timeout 0
set submenu-timeout 0
goto start

:42.1
kernel http://192.168.1.25/42.1/boot/x86_64/loader/linux initrd=initrd install=http://192.168.1.25/42.1/
initrd http://192.168.1.25/42.1/boot/x86_64/loader/initrd
boot || goto failed

:tumbleweed.20151022
kernel http://192.168.1.25/tumbleweed.20151022/boot/x86_64/loader/linux initrd=initrd install=http://192.168.1.25/tumbleweed.20151022/
initrd http://192.168.1.25/tumbleweed.20151022/boot/x86_64/loader/initrd
boot || goto failed

:13.2-64-bit
kernel http://192.168.1.25/13.2/x86_64/boot/x86_64/loader/linux initrd=initrd install=http://192.168.1.25/13.2/x86_64/
initrd http://192.168.1.25/13.2/x86_64/boot/x86_64/loader/initrd
boot || goto failed

:sles12-ga
kernel http://192.168.1.25/12ga/x86_64/boot/x86_64/loader/linux initrd=initrd install=http://192.168.1.25/12ga/x86_64/
initrd http://192.168.1.25/12ga/x86_64/boot/x86_64/loader/initrd

This produces a menu that looks something like:

iPXE Menu

Selecting a menu item should set off the install.

(Note: for EFI systems you need initrd=initrd in the kernel line or booting the installer will fail. It does no harm for legacy systems.)

Serving the right files for legacy and EFI clients

Some conditional logic is needed in /etc/dhcpd.conf on the DHCP server to make this work. Also, the file undionly.kpxe is apparently too large to be loaded initially by legacy PXE boot. So it needs to be chainloaded by pxelinux.0.

The conditional setup in /etc/dhcpd.conf looks like this:

next-server 192.168.1.254;
option arch code 93 = unsigned integer 16;
  if option arch = 00:07 or option arch = 00:09 {
     filename "ipxe.efi";
  } else {
     filename "pxelinux.0";
}

On the TFTP server we now need to have the following setup:

# ls -l /srv/tftpboot/
total 924
-rw-r--r-- 1 root root 845888 Nov 27 11:38 ipxe.efi
-rw-r--r-- 1 root root  26744 Nov 27 13:58 pxelinux.0
drwxr-xr-x 1 root root     14 Nov 27 15:18 pxelinux.cfg
lrwxrwxrwx 1 root root     13 Nov 27 14:00 undionly.0 -> undionly.kpxe
-rw-r--r-- 1 root root  63437 Nov 27 14:30 undionly.kpxe

The file undionly.0 is a symbolic link to undionly.kpxe.

Then in the directory pxelinux.cfg is one file default with contents:

DEFAULT iPXE-legacy
LABEL iPXE-legacy
KERNEL undionly.0

Booting ipxe.efi directly from the EFI shell

On an EFI system you may be able to choose an EFI shell as a startup option. In that shell you can find and directly execute ipxe.efi from the command line if it is on a FAT formatted USB stick which is connected when the system is switched on.

Making an EFI-bootable USB stick for iPXE

Assuming here that the USB stick is /dev/sdc.

Use fdisk to make it contain a single partition of type b.

Format as VFAT:

mkfs -t vfat /dev/sdc1

Mount it and create a directory structure as follows:

mount /dev/sdc1 /mnt
mkdir -p /mnt/EFI/BOOT/

Copy ipxe.efi into that directory with the name bootx64.efi

cp ipxe.efi /mnt/EFI/BOOT/bootx64.efi

Unmount:

umount /mnt

Write the gptmbr.bin from the syslinux package to the start of the disk:

dd if=/usr/share/syslinux/gptmbr.bin of=/dev/sdc

It is now an EFI-bootable USB stick and when booted will run the iPXE boot and retrieve the chainloaded menu.