Intel® System Studio – UEFI BIOS Debugging

In a previous article I took a look at the JTAG debugger XDB that is part of Intel® System Studio and showed some of the new features and functions built into the product.

Here I want to take a closer look at the EFI debugging features in XDB and also show a little example EFI debugging session.


Available EFI Commands

Debug information

Where is My Module?

   Use the EFI Shell and System Table to Find a Module

   Use your own Code to find your module!

A Windows Subst Tip

Evaluation Window is your Friend



Available EFI Commands

Let’s start with a quick look at the more important EFI commands available in XDB. Type “efi” in the XDB console to get a full list:

EFI "LOADTHIS {<addr>}"This will be you most used command! It attempts to load debug info for the given address. It will start to scan memory at the given address for module headers. If no address is given it will use the current IP
EFI "SETSYSTAB <addr>"Sets the EFI system table base address. Use this command to tell XDB where the EFI system table is located.
EFI "SHOWSYSTAB"Scans for the system table. Best to set the address for the system table first, otherwise scans the defined memory search area which is of course slower.
EFI "SHOWMODULES"Shows all EFI modules loaded in DXE phase (you can also use the EFI Shell of course).
EFI "LOAD <name>|<ID>"Attempts to load debug info for the given module ID or module name. This command depends on the debugger knowing the system table address. I recommend to use LOADTHIS instead.


Debug information

XDB can debug PE/COFF modules compiled and linked using either a Microsoft compiler or GCC. You can even mix both in the same project & debug session! If you are e.g. a Linux developer and need to add a module to an existing BIOS that was developed with a Microsoft tool chain then you can debug the complete BIOS regardless of its origin!

When debugging EFI please do not use the  EFI "LOAD" command to load symbol information - please only use EFI "LOADTHIS".  Loadthis will load the symbols and correctly do relocation calculation to where the module is located. This is important as modules may be relocated e.g. from Flash to RAM.

Where is My Module?

There are different ways to find out where your module is. I will explain two methods here.

Use the EFI Shell & System Table to Find a Module

You can use the system table to get the location of all modules loaded. But where is the table? Here is one way of finding it, use the EFI shell!

Connect XDB to your target, make sure you can access the target. Reset the target and let it run into the efi Shell. Now use the shell command mem. Mem without an address parameter will display the EFI system table pointer entries.  Here an example:

Memory Address 00000000A27EEF18 200 Bytes
A27EEF18: 49 42 49 20 53 59 53 54-1F 00 02 00 78 00 00 00  *IBI SYST....x...*
A27EEF28: EA EF B4 8D 00 00 00 00-98 72 7D A2 00 00 00 00  *.........r......*
A27EEF38: 00 00 01 00 00 00 00 00-98 47 36 9F 00 00 00 00  *.........G6.....*
A27EEF48: 60 03 82 95 00 00 00 00-98 7F 48 9F 00 00 00 00  *`.........H.....*
A27EEF58: F8 D9 19 A1 00 00 00 00-18 3F 36 9F 00 00 00 00  *.........?6.....*

-	Some lines cut out here -

A27EF108: AF AF AF AF AF AF AF AF-AF AF AF AF AF AF AF AF  *................*

Valid EFI Header at Address 00000000A27EEF18
System: Table Structure size 00000078 revision 0002001F
ConIn (9F364798) ConOut (9F487F98) StdErr (9F363F18)
Console Out on PciRoot(0x0)/Pci(0x1D,0x0)/USB(0x1,0x0)/USB(0x5,0x0)/\/mem.txt
Runtime Services     00000000A27EEE18
Boot Services        00000000A11B9A70
ACPI Table           00000000A2FE9000
ACPI 2.0 Table       00000000A2FE9014
MPS Table            00000000A2A43000
SMBIOS Table         00000000A2A9E000

The address 0x A27EEF18 is exactly what we want. Now in XDB stop the target and enter this into the console:

xdb> efi "setsystab 0xa27eef18"
xdb> efi "showsystab"
EFI System table at 0x00000000A27EEF18

Configuration Tables:
GUID:                              Pointer:    Name:
GUID f880aae0, e4ac, 4c64, {...}   0xa2fae000
GUID 05ad34ba, 6f02, 4214, {...}   0xa11b9bf0  DXE_SERVICES_TABLE
GUID 7739f24c, 93d7, 11d4, {...}   0xa2fea018  HOB_LIST

-	Some lines cut here -

EFI_RAISE_TPL                    0x00000000A11A8300
EFI_RESTORE_TPL                  0x00000000A11A8384

-	Some lines cut here -

EFI_ALLOCATE_PAGES               0x00000000A11A494C
EFI_FREE_PAGES                   0x00000000A11A4A54
EFI_CREATE_EVENT_EX              0x00000000A11A33E8

Even more informative is this command efi "showmodules"! Once XDB has the systemtable it also knows what modules are loaded and where they are!

xdb> efi "showmodules"
INFO: Using cached EFI State Information
ModuleID  Base                Size        Name
00197     0x0000000095817000  0x000BC400  
00196     0x0000000095B40000  0x00001780  
00195     0x0000000095B1C000  0x0001F8C0  UsbRt.efi
00194     0x0000000095B7E000  0x00001A20  
00193     0x0000000095B83000  0x00001AC0  LegacyInterruptHookDxe.efi

-	Some lines cut here

00170     0x000000009F232000  0x00004F00  HitachiH8s2113.efi
00169     0x000000009F23A000  0x00001740  NbInt15.efi
00168     0x000000009F23E000  0x000043A0  PlatformFviDxe.efi
00167     0x000000009F243000  0x00004480  OverClockDxe.efi
00166     0x000000009F248000  0x00005B20  ScsiDisk.efi
00165     0x000000009F24E000  0x00003A20  ScsiBus.efi
00164     0x000000009F252000  0x00003920  UsbMouseDxe.efi
00163     0x000000009F256000  0x00005EA0  UsbMassStorageDxe.efi
00162     0x000000009F25C000  0x00006300  UsbKbDxe.efi
00161     0x000000009F263000  0x0000A1A0  XhciDxe.efi
00160     0x000000009F26E000  0x00008F80  UsbBusDxe.efi

-	Some lines cut here

00016     0x000000009FEB7000  0x00001A40  Legacy8259.efi
00015     0x000000009FEB9000  0x00002680  DataHubDxe.efi
00014     0x000000009FEBD000  0x00015B60  HiiDatabase.efi
00013     0x000000009FED4000  0x00002060  CpuIo2Dxe.efi
00012     0x00000000A27C9000  0x00002680  CpuIoDxe.efi
00011     0x00000000A27CC000  0x00002EE0  ReportStatusCodeRouterRuntimeDxe.efi
00010     0x000000009FED7000  0x00001600  TbtDxe.efi
00009     0x000000009FED9000  0x000016A0  PlatformPrivateDxe.efi
00008     0x000000009FEDB000  0x000037C0  AcpiSupportDxe.efi
00007     0x000000009FEDF000  0x000010E0  AsfTable.efi
00006     0x000000009FEE1000  0x00002300  WdtDxe.efi
00005     0x000000009FEE4000  0x00002660  PchSerialGpio.efi
00004     0x000000009FF1A000  0x00001F80  ActiveBios.efi
00003     0x000000009FF1C000  0x00001280  PlatformInfoDxe.efi
00002     0x000000009FEE7000  0x00004420  PcdDxe.efi
00001     0x000000009FF1E000  0x00001CE0  MeUlvCheck.efi
00000     0x00000000A119E000  0x00020000  DxeCore.efi

Now you can tell xdb to load the debug information for a module (use either the module name or ID):

xdb> efi “load HpetTimerDxe.efi”

Once the debug information is loaded open XDB's source file window (View -> Source Files) and drill down the tree to the file you are interested to debug (in this case e.g. PcAtChipsetPkg\HpetTimerDxe\HpetTimer.c). Open the source file, find the correct location for a breakpoint and start debugging!

Use your own Code to find your module!

I learned another method to find a module from my colleague Zach: use your own code code! Set a "jmp $" or "while(1);" in the entry function to create an endless loop.  Once the system is stuck there stop it and voila you are in your own module!
Here is a very simple example to explain the method. I used the "HelloWorld" example code that is part of the UEFI Development Kit 2010. I had to use a pragma to disable the "unreachable code" warning as the default settings flag that warning as an error and no code is generated! I also changed the code to simply print "Hello World" ten times. Here is the modified code:

/** @file  This sample application bases on HelloWorld PCD setting   to print "UEFI Hello World!" to the UEFI Console.  Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.<BR>  This program and the accompanying materials                            are licensed and made available under the terms and conditions of the BSD License           which accompanies this distribution.  The full text of the license may be found at  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                       WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             **/#include <Uefi.h>#include <Library/PcdLib.h>#include <Library/UefiLib.h>#include <Library/UefiApplicationEntryPoint.h>#pragma warning (disable : 4702)/**  The user Entry Point for Application. The user code starts with this function  as the real entry point for the application.  @param[in] ImageHandle    The firmware allocated handle for the EFI image.    @param[in] SystemTable    A pointer to the EFI System Table.    @retval EFI_SUCCESS       The entry point is executed successfully.  @retval other             Some error occurs when executing this entry point.**/
UefiMain (
IN EFI_HANDLE        ImageHandle,

UINT32 Index;endless:;gotoendless;

Index =0;//// Three PCD type (FeatureFlag, UINT32 and String) are used as the sample.//if(FeaturePcdGet (PcdHelloWorldPrintEnable)){for(Index =0; Index <10; Index ++){//// Use UefiLib Print API to print string to UEFI console//
Print ((CHAR16*)PcdGetPtr (PcdHelloWorldPrintString));}}return EFI_SUCCESS;}

Here is the build command I used:

Build -a X64 -p MdeModulePkg\MdeModulePkg.dsc -m MdeModulePkg\Application\HelloWorld\HelloWorld.inf

Build the module and start it from the EFI shell. Let it run until you are sure you are stuck in the loop  (in this case immediately). Then use XDB to stop the system. The IP should be in the endless loop (or an interrupt routine, just keep trying). Now you can use efi “loadthis” to tell XDB to find the module header starting at the IP and load the debug information for this module.

Once you have done this use the same steps I described above to open the source code, and set your breakpoint. To continue execution you will need to set the IP to the instruction after the jmp and let the system run.


A Windows Subst Tip

Often when developing a BIOS you will have several projects on your system. To keep the paths similar and short many developers on Windows use subst to define a driver to the root of the project. They then build the project on that drive. The path to the source and debug information is embedded in the module. XDB uses this information to find the debug information and source files for you.

One disadvantage of subst is that it is only for the current user – not system wide – and is lost after reboot. An alternative I like to use is psubst – permanent subst. It will setup a registry entry that will make the subst available for all users and it will remain in place over a reboot! Details, binary and source code can be found here:  . More information on the subject is found e.g. here:


Evaluation Window is your Friend

On debugging a good tool to use is the evaluation window! Right click on a variable, select “On Selection “xxx” got to the submenu and select “Add To Eval Window”. Then the variable will be displayed in the window. If the variable is a e.g. struct you can see the contents of all its elements – very handy!

Evaluation Window


I hope EFI debugging will be easier for you with these new commands and wish you Happy Debugging!


Helpful Links:

EFI Shells and Scripting:

  • Explains the EFI Shell Commands and gives some useful examples.

Writing a Simple EDK II UEFI Application:

  • A very useful how-to to get your started on EFI application development.

UEFI Development Kit UDK2010 Download release

  • All the tools and sources you need to write the sample application.

Psubst the permanent subst: :

Windows subst explained:



Para obter informações mais completas sobre otimizações do compilador, consulte nosso aviso de otimização.