When the Extensible Firmware Interface (EFI) was being defined several years ago, debug resources were an important consideration. We all know that you can have great technology, but if you can’t effectively debug it, it’s not really all that great. Every new technology needs to be coupled with an efficient development paradigm for relatively easy adoption and deployment.
EFI was originally developed with static and dynamically-located code. Pre-EFI Initialization code (PEI) is statically located and Driver Execution Environment (DXE) dynamically locates code into memory. From a development standpoint, both PEI and DXE need proper development visibility and control. This means connecting the binary images to the original source code. With Microsoft Visual Studio and the UEFI plugin, developers can create EFI modules that contain the information that enables a straight forward and effective debug methodology.
With a debug build, resources are added to the binary images. In PEI, text paths are present to point to the original source code build path. These paths provide a means to display source code in a debugger which provides the control and visibility required by development engineers. The DXE paradigm is different. Since drivers are dynamically located, UEFI logs the locations of the loaded drivers in a special table where the location of the loaded binaries can be looked up. With this table, the EFI symbols can be ‘fixed up’ so they point to the proper memory addresses.
The result is a relatively easy way for debuggers to effectively provide both PEI and DXE phase debugging. Fortunately, the original x86 debug paradigm was carried over during the ARM port of UEFI. ARM UEFI developers can enjoy the visibility and control of both ARM PEI and DXE phases for development and debug.
If you’re interested in learning more about UEFI and UEFI debugging, check out this eBook. Although it references Intel platforms in the title, what it has to say about UEFI applies to ARM platforms as well.