This page describes the boot prefixes and debugging tips. Debugging a boot prefix can be challenging because most code is assembly and it is not possible to call into gPXE for utility and I/O functions.
A crash, hang, or other issue may be prefix-related if it occurs before gPXE prints its banner. If gPXE fails to load then it may be a prefix issue. Odd behavior when exiting gPXE may also be prefix issues.
The boot prefix is the first code executed when gPXE is started. Its task is to install gPXE into memory and set up the environment in which C code can run. Prefix code is the only part of a gPXE image which is not compressed because it contains the decompressor that is used to load the rest of gPXE.
The x86 boot prefix code is located in arch/i386/prefix/
. It consists of a
medium-specific prefix assembly file and common prefix code used for all media.
For example, the floppy prefix is dskprefix.S
and most of the common code is in
libprefix.S
.
The machine state when gPXE begins running is medium-specific. For example, the floppy prefix starts with a floppy disk boot sector which the BIOS loads at 07c0:0000 in memory.
The medium-specific prefix code:
libprefix.S:install
to decompress and install gPXE into memory. When this returns the .text16 and .text segments are ready.main()
in protected mode. When this returns gPXE has finished executing.libprefix.S:uninstall
to clean up before returning to firmware.
Most of the work is done in libprefix.S:install
. Decompressing and installing
gPXE into memory is done in several stages. When memory regions above the
first MB need to be accessed, the code switches into protected mode but always
switches back to real mode before completing.
It is difficult to use debuggers at this early stage of execution because most gPXE functionality is not yet available and the CPU runs in real mode. Modern debuggers may not support real mode well because applications run under protected mode.
If the issue can be reproduced in a virtual machine then it may be possible to use a debugger.
Usually printf()
-style debugging is a good approach except there is no
printf()
function at this stage. Instead consider using:
libprefix.S:print_character
and related functions. These work in real mode.int $0x10 ah=0xe
to print a character in real mode.An infinite loop can be placed along the code path to identify the location of a crash. If the machine hangs on the infinite loop, then the location is being reached. If the machine crashes, then the location is not reached.
1: /* Hang here for debugging */ jmp 1b
If the issue is a hang then it may not be possible to use an infinite loop to determine where the hang occurs. Instead, a reboot can be used to identify where code goes wrong.
/* Reboot */ jmpl $0xf000, $0xfff0