IPXE

From Leo's Notes
Last edited on 10 December 2021, at 06:34.

iPXE is a PXE boot firmware which can be used to PXE boot systems that may not have support. iPXE supersedes the older and no longer actively maintained gPXE boot firmware.

Compiling iPXE[edit | edit source]

You can build iPXE from source with a functional C compiler in a standard development environment. Alternatively, you can use an automated web application called ROM-o-matic to automatically generate the compiled iPXE files based on your desired build options.

Using ROM-o-matic[edit | edit source]

The easiest way to get ROM-o-matic working is to use the Docker image from this project: https://github.com/xbgmsharp/ipxe-buildweb/. Under the hood, the build environment is based on Ubuntu. I had some issues with the web interface throwing a 500 error but it seems to work after retrying when that happens.

# docker pull xbgmsharp/ipxe-buildweb
# docker run  -d \
	--publish 8080:80 \
	--publish 22:22 \
	--name ipxe-buildweb \
	xbgmsharp/ipxe-buildweb

# Navigate to docker-host:8080

Building from source[edit | edit source]

On a system with the C development packages installed, you can use the project's Make file to generate the desired iPXE file.

The following script will build iPXE with an embedded script which automatically boots from a HTTP resource.

# git clone git://git.ipxe.org/ipxe.git
# cd git

# cat <<EOF > arch.ipxe
#!ipxe
dhcp
set 209:string cfg/arch.cfg
set 210:string http://linbuild.cs.ucalgary.ca/archlinux/boot/
chain ${210:string}pxelinux.0
EOF

# make bin/ipxe.lkrn EMBED=arch.ipxe

You may also make a USB image by making bin/ipxe.usb and then dd'ing the image to the USB device.

Troubleshooting[edit | edit source]

If you get the following error while building:

[VERSION] bin/version.udionly.kpxe.o
[LD] bin/udionly.kpxe.tmp
--defsym:2: undefined symbol `obj_udionly' referenced in expression
make: *** [bin/udionly.kpxe.tmp] Error 1
rm bin/version.udionly.kpxe.o

Install the syslinux-devel package.

Scripting[edit | edit source]

For more information, see iPXE's documentation.

Commands[edit | edit source]

See iPXE's command reference

Common commands that will be used are:

Command Description
dhcp Obtain IP address via DHCP
boot, imgexec, chain Downloads and boots an executable image
initrd, module, imgfetch Downloads an executable image
isset Checks if a value exists (typically for settings).
iseq Checks if a value is equal to something
echo Prints text to console
goto Go to a specific label

You can specify kernel arguments after the kernel URL/path. Eg:

#!ipxe

kernel vmlinuz-3.16.0-rc4 bootfile=http://boot.ipxe.org/demo/boot.php fastboot initrd=initrd.img
initrd initrd.img
boot

Flow Control[edit | edit source]

Create a script label using a colon followed by the label name. Program execution will jump to the label using the goto command.

Use the bash-like || to run a command when something fails or && when something succeeds. Eg:

true && echo Success
false || echo Failure

Script Termination[edit | edit source]

Script execution will stop whenever a command fails.

To make the script continue even if a command can error out (such as dhcp, use the || operator at the end of the command which will always make the command succeed. (ie. dhcp || is analogous to dhcp || true).

Settings[edit | edit source]

iPXE autodetects a bunch of settings based on the network and hardware it is running on. These include the hardware type (models, IDs) and also network settings (IP, MAC addresses, DHCP settings, etc).

One use case I had with ${product} was to make an auto BIOS update boot option that requests for a boot image from a web server by chaining a URL with the variable.

Example:

:menu
set conn_type https
chain --autofree https://${base_url}/update.ipxe?product=${product}

It's entirely possible to make use of these exposed config settings and make iPXE a quick ROM that updates an inventory server.

See more at:

Example scripts[edit | edit source]

Description Script
Obtain an IP from DHCP and PXE boot.

The file will be obtained via tftp from the next-server as configured by the DHCP server.

#!ipxe

:retry_dhcp
dhcp && isset ${filename} | goto retry_dhcp

# Boot image provided by DHCP
echo Booting from ${filename}
chain {$filename}
Obtain an IP from DHCP and override the next-server.

Download and run the main.ipxe script. If this fails, enter a shell.

#!ipxe
dhcp
set next-server 100.64.1.119
chain http://${next-server}/main.ipxe || shell
Set a static IP address and boot netboot.xyz.

This may be useful when your network has no DHCP server.

#!ipxe
set net0/ip      100.64.1.127
set net0/netmask 255.255.255.0
set net0/gateway 100.64.1.1
set dns          8.8.8.8
ifopen net0


Booting into SCCM[edit | edit source]

Suppose you have a customized SCCM .iso that you want to boot instead of having to manually boot each computer.

At a glance, to get iPXE booting the SCCM image:

  1. Set up boot files by extracting files from the iso and modify boot.wim and place them on a web server
  2. Set up iPXE to boot WinPE from the modified boot.wim file

Setup SCCM Files[edit | edit source]

Extract the following files from the iso.

  • bootmgr
  • bcd
  • boot.sdi
  • boot.wim

Obtain wimboot from http://git.ipxe.org/releases/wimboot/wimboot-latest.zip

Customization made to the SCCM image will be under the SMS directory in the iso. Since we are only loading the boot.wim image rather than the entire iso, these customization must be copied into the boot.wim file using ImageX (from the Windows Automated Installation Kit or WAIK).

While there is a linux utility of the same name from wimlib, it didn't work for me as it just creates a corrupt .wim that doesn't boot properly.

Create winpeshl.ini:

[LaunchApps]
"wscript.exe","%SYSTEMDRIVE%\sms\bin\x64\bootstrap.vbs"

Create bootstrap.vbs:

Set os = WScript.CreateObject ( "WScript.Shell" )
os.Run "%COMSPEC%", 7, false
os.Run "%COMSPEC% /c title Initialising... && wpeinit " & "&& net start dnscache", 1, true
os.RegWrite "HKLM\SYSTEM\CurrentControlSet\Control\PEBootType", "Ramdisk:OpticalDrive", "REG_SZ"
os.Run WScript.ScriptFullName & "\..\TsmBootStrap.exe /env:WinPE " & "/configpath:%SYSTEMDRIVE%\sms\data", 1, true

Copy the SMS directory, the winpeshl.ini, and the bootstrap.vbs files into the wim image.

# mkdir mnt
# imagex /mountrw boot.wim 1 mnt
ImageX Tool for Windows
Copyright (C) Microsoft Corp. All rights reserved.
Version: 6.1.7600.16385

Mounting: [C:\Users\admin\Desktop\boot.wim, 1] -> [C:\Users\admin\Desktop\mnt]...

[ 100% ] Mounting progress

Successfully mounted image.

Total elapsed time: 34 sec

# xcopy /e /f /y iso\sms mnt\sms
# copy winpeshl.ini mnt\windows\system32\
# copy bootstrap.vbs mnt\sms\bin\x64\
# imagex /unmount /commit mnt
ImageX Tool for Windows
Copyright (C) Microsoft Corp. All rights reserved.
Version: 6.1.7600.16385

Committing: [C:\Users\admin\Desktop\mnt]...

[ 100% ] Committing Image progress

Successfully committed image.

Unmounting: [C:\Users\admin\Desktop\mnt]...


[ 100% ] Mount cleanup progress

Successfully unmounted image.

Total elapsed time: 22 sec

Copy these files to a web server:

  1. wimboot
  2. bootmgr
  3. bcd
  4. boot.sdi
  5. boot.wim

Booting the SCCM Image[edit | edit source]

Create a custom iPXE ROM with the following script. Modify the URL so that it goes to your web server containing the files from above.

#!ipxe

:retry_dhcp
dhcp || goto retry_dhcp

:boot
set url http://pages.cpsc.ucalgary.ca/~leo/boot/win/

kernel ${url}/wimboot
initrd ${url}/bootmgr bootmgr
initrd ${url}/bcd bcd
initrd ${url}/boot.sdi boot.sdi
initrd ${url}/boot.wim boot.wim
boot || goto boot

Troubleshooting[edit | edit source]

A winpeshl.ini file is present, but no commands were successfully launched[edit | edit source]

Issue: The image doesn't run the vbscript on start up. Instead, a command prompt opens with the following message:

A winpeshl.ini file is present, but no commands were successfully launched. This could be caused by incorrect formatting or an invalid executable name. Please consult the documentation for more information.

Solution: I messed up the ini file. Copy it as it is shown above. I did not copy the interpreter which was the first argument.

unrecognized block type 0. bad MZ magic d5e9[edit | edit source]

Issue: When using imagexfrom wimlib, I got weird boot errors including:

unrecognized block type 0
bad MZ magic d5e9

Solution: Use Microsoft's WAIK imagex utility instead.


Troubleshooting[edit | edit source]

Unsupported devices[edit | edit source]

The following servers and workstations do not work with iPXE.

Product Issue
PowerEdge 2950 BNX2 driver fails to download from HTTP. Errors out with "Transmit timed out"
Alienware Aurora R6 Does not have the alx driver and therefore cannot see the Atheros Killer network device. On BIOS revisions prior to version 7, iPXE will cause the computer to reboot immediately.

Initramfs unpacking failed: junk in compressed archive[edit | edit source]

When attempting to boot Linux with iPXE using a custom netboot.xyz menu, I constantly ran into an issue where the kernel isn't able to extract the loaded initrd image. iPXE was able to boot the same kernel and initrd directly when I bypassed netboot.xyz. The kernel boot messages do show:

Unpacking initramfs...
Initramfs unpacking failed: junk in compressed archive
Freeing initrd memory: 46288K

It turns out that you need to free the loaded iPXE images before loading the kernel and initrd. The loaded iPXE scripts were interfering with the initrd payload. The fix here is to call imgfree before loading the kernel and initrd.

See Also[edit | edit source]