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. ===== How to identify a prefix issue ===== 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. ===== Overview ===== 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: - initializes registers and sets up a stack. - ensures that the raw gPXE image is in memory, for example by reading it off disk. - calls ''libprefix.S:install'' to decompress and install gPXE into memory. When this returns the .text16 and .text segments are ready. - transfers execution to the .text16 segment. After this point the prefix segment is never used again. - calls C ''main()'' in protected mode. When this returns gPXE has finished executing. - calls ''libprefix.S:uninstall'' to clean up before returning to firmware. - returns to firmware to boot the next device. 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. ===== Debugging techniques ===== ==== Debuggers ==== 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. ==== Printing messages ==== 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. * manually calling ''int $0x10 ah=0xe'' to print a character in real mode. ==== Infinite loops ==== 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 ==== Reboots ==== 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