Basic services system table of UEFI

For UEFI application and driver developers, system table is one of the most important data structures, which is the channel from user space to kernel space. With it, UEFI applications and drivers can access UEFI kernel, hardware resources and I/O devices.
  

1 access system tables in applications and drivers

After the computer system enters the DXE stage, the system table is initialized, so the system table can only be used in the DXE stage and later applications and drivers.
The system table is a global structure of the UEFI kernel, and its pointer is passed to the user space as a parameter of the program Image entry function.
The entry function of program image (including UEFI application, DXE driver and UEFI driver) has a unified format, and its function prototype is as follows:

/**
  This is the declaration of the EFI image entry point. For UEFI applications, the UEFI OS loader and UEFI driver (including device driver and bus driver) have the same entry point.

  @param[in]  ImageHandle       Firmware assigned handle for UEFI image
  @param[in]  SystemTable       Pointer to EFI system table.

  @retval EFI_SUCCESS           The operation completed successfully.
  @retval Others                An unexpected error occurred.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_IMAGE_ENTRY_POINT)(
  IN  EFI_HANDLE                   ImageHandle,
  IN  EFI_SYSTEM_TABLE             *SystemTable
  );

The process of transferring system table pointer from kernel to user space

Generally, the entry function of program Image is ﹣ ModuleEntryPoint (when an Image is started by the StartImage service of the startup service, this entry function is executed). When an application or driver is loaded into memory to form an Image (ImageHandle is the handle of the Image), the address of the ModuleEntryPoint function is assigned to the EntryPoint of the Image object, and then the Image - > EntryPoint (ImageHandle, SystemTable) will be executed, and finally the entry function of the module (the entry function of the module is through. inf) will be executed from the entry function of the Image The function specified by entry [point] in the file).

3 composition of system table

Through the system table, the application program can get the input / output control equipment
You can also get the startup service table and runtime service table, and access the hardware devices and kernel services through the startup service and runtime service table
  
The system table can be divided into the following six parts:

  • Header: including version number and CRC check code of the table.
  • Firmware information: includes the string of firmware developer name and firmware version number.
  • Standard input console, standard output console, standard error console.
  • Start service table
  • Runtime service table
  • System configuration table

System table data structure:

// EFI system table
typedef struct {
  ///
  /// The table header for the EFI System Table.
  ///
  EFI_TABLE_HEADER                  Hdr;
  ///
  /// A pointer to a null terminated string that identifies the vendor
  /// that produces the system firmware for the platform.
  ///
  CHAR16                            *FirmwareVendor;
  ///
  /// A firmware vendor specific value that identifies the revision
  /// of the system firmware for the platform.
  ///
  UINT32                            FirmwareRevision;
  ///
  /// The handle for the active console input device. This handle must support
  /// EFI_SIMPLE_TEXT_INPUT_PROTOCOL and EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
  ///
  EFI_HANDLE                        ConsoleInHandle;
  ///
  /// A pointer to the EFI_SIMPLE_TEXT_INPUT_PROTOCOL interface that is
  /// associated with ConsoleInHandle.
  ///
  EFI_SIMPLE_TEXT_INPUT_PROTOCOL    *ConIn;
  ///
  /// The handle for the active console output device.
  ///
  EFI_HANDLE                        ConsoleOutHandle;
  ///
  /// A pointer to the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL interface
  /// that is associated with ConsoleOutHandle.
  ///
  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL   *ConOut;
  ///
  /// The handle for the active standard error console device.
  /// This handle must support the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.
  ///
  EFI_HANDLE                        StandardErrorHandle;
  ///
  /// A pointer to the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL interface
  /// that is associated with StandardErrorHandle.
  ///
  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL   *StdErr;
  ///
  /// A pointer to the EFI Runtime Services Table.
  ///
  EFI_RUNTIME_SERVICES              *RuntimeServices;
  ///
  /// A pointer to the EFI Boot Services Table.
  ///
  EFI_BOOT_SERVICES                 *BootServices;
  ///
  /// The number of system configuration tables in the buffer ConfigurationTable.
  ///
  UINTN                             NumberOfTableEntries;
  ///
  /// A pointer to the system configuration tables.
  /// The number of entries in the table is NumberOfTableEntries.
  ///
  EFI_CONFIGURATION_TABLE           *ConfigurationTable;
} EFI_SYSTEM_TABLE;

Introduce the important components of the system table:

(1) EFI table header

The tables in UEFI usually start with EFI table header. The data structure of EFI table header is defined as follows:

  • edk2/BaseTools/Source/C/Include/Common/UefiMultiPhase.h
// Data structure prior to all standard EFI table types.
typedef struct {
  UINT64  Signature;
  UINT32  Revision;
  UINT32  HeaderSize;
  UINT32  CRC32;
  UINT32  Reserved;
} EFI_TABLE_HEADER;
  • To help developers, EDK2 provides the macro signature ʄ (A,B,C,D,E,F,G,H), which is used to convert ASCII code strings to 64 bit unsigned integers.
  • edk2/MdePkg/Include/Base.h
/**
  Returns a 64-bit signature built from 8 ASCII characters.

  This macro returns a 64-bit value built from the eight ASCII characters specified
  by A, B, C, D, E, F, G,and H.

  @param  A    The first ASCII character.
  @param  B    The second ASCII character.
  @param  C    The third ASCII character.
  @param  D    The fourth ASCII character.
  @param  E    The fifth ASCII character.
  @param  F    The sixth ASCII character.
  @param  G    The seventh ASCII character.
  @param  H    The eighth ASCII character.

  @return A 64-bit value built from the two ASCII characters specified by A, B,
          C, D, E, F, G and H.

**/
#define SIGNATURE_64(A, B, C, D, E, F, G, H) \
    (SIGNATURE_32 (A, B, C, D) | ((UINT64) (SIGNATURE_32 (E, F, G, H)) << 32))

For example, the Signature of EFI system table is Signature U64 ('I', 'B', 'I', 'S',' Y ',' S', 'T')

  • HeaderSize is the length of the whole table. For the system table, it is sizeof (EFI [system] table)
  • C0RC32 is the subject check code. When calculating the CRC32 check code, first clear the CRC32 field in the data structure, then calculate the CRC32 code of the whole table (table size is HeaderSize), and then fill the check code into the CRC32 field.

(2) standard input console, standard output console and standard error console.

The system table also provides three console devices and protocols for operating the three consoles.

  • ConIn is used to read characters from the input console ConsoleInHandle, usually the input console is the keyboard
  • ConOut is used to output strings to the output consoleconsoleouthandle, which is usually a screen
  • stdErr is used to output a string to the standard error console, which is usually a screen

These three console devices, as well as three protocols of ConIn, ConOut and stdErr, are initialized in the drive of edk2 / mdemodulepkg / Universal / console / splitterdxe.

(3) system configuration table

ConfigurationTable is a system configuration table, which points to EFI? Configuration? Table array. Each item in the array is a table.

Data structure of EFI? Configuration? Table:

  • edk2/MdePkg/Include/Uefi/UefiSpec.h
// Contains a set of GUID / pointer pairs consisting of the ConfigurationTable fields in the EFI system table
typedef struct {
  // A 128 bit GUID value that uniquely identifies the system configuration table
  EFI_GUID                          VendorGuid;
  // Pointer to the table associated with the VendorGuid
  VOID                              *VendorTable;
} EFI_CONFIGURATION_TABLE;

For example, a ConfigurationTable usually contains an APCI (Advance Configuration and Power Interface) table. ACPI can be expressed in the system configuration table as follows: {gEfiAcpiTableGuid, EFI ﹣ ACPI ﹣ 3 ﹣ root ﹣ system ﹣ description ﹣ pointer *}

4 use system table

The system table is the global data structure of UEFI kernel space, and the application program runs in user space.

So how does user space get the system table pointer of kernel space?
In fact, there is only one address space in UEFI. All programs run at RING0 priority. The address space of application program occupies a part of UEFI address space.
Since user space and kernel space are a whole, any address of kernel space can be used directly in the application program. Then, as long as the address of system table is obtained in the application program, the system table can be used.

4.1 use system table in user space

The address of the system table can be obtained by the parameters of the module's entry function.

#include <Uefi.h>
EFI_Status UefiMain(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable)
{
    EFI_STATUS Status;
    UINTN Index;
    EFI_INPUT_KEY Key;
    CHAR16 StrBuffer[3] = {0};
    SystemTable->BootServices->WaitForEvent(1,&SystemTable->ConIn->WaitForKey,&Index);
    Status = SystemTable->ConIn->ReadKeyStroke(SystemTable->ConIn,&Key);
    StrBuffer[0] = Key.UnicodeChar;
    StrBuffer[1] = '\n';
    SystemTable->ConOut->OutputString(SystemTable->ConOut,StrBuffer);
    return EFI_SUCCESS;
}
  • Systemtable - > bootservices refers to the startup service table of the system
  • Systemtable - > conin points to EFI? Simple? Text? Input? Protocol installed on the standard input device
  • Systemtable - > conout points to EFI? Simple? Text? Output? Protocol installed on the standard output device

The example first uses WaitForEvent to wait for the keyboard event, then calls the ConIn ReadKeyStroke to read the keyboard, and finally calls the ConOut OutputString service to display the key to the screen.

The EFI ﹣ status waitforevent (in uintn numberofevents, in EFI ﹣ Event, out uintn Index) service provided by BootServices is used to wait for the occurrence of any Event in the Event array. The function returns when any Event in the Event array (NumberOfEvent is the array size) is triggered, and the Index returns the Index of the triggered Event in the Event array. This function is a blocking function.
EFI ﹣ status readkeystroke (EFI ﹣ simple ﹣ text ﹣ input ﹣ protocol This, outefi ﹣ input ﹣ key) is used to read keys. It is a member function of coinprotocol. The first parameter is This pointer, and the second parameter is used to return keys.
EFI? Status outputstring (EFI? Simple? Text? Output? Protocol * This, char16 string) is used to print strings to the screen. It is a member function of ConOut Protocol. The first parameter is also This pointer, and the second parameter is the string printed to the screen.

4.2 using gST to access system tables in user space

The module entry function UefiMain in the example in 4.1 above uses the incoming parameter SystemTable to access the system table.
EDK2 is convenient for developers and provides edk2/MdePkg/Include/Library/UefiBootServicesTableLib.h,
Defined in UefiLib

  • Global variable gST (pointing to SystemTable)
///
/// Cache pointer to the EFI System Table
///
extern EFI_SYSTEM_TABLE   *gST;
  • gBS (point to systemtable - > bootservices)
///
/// Cache pointer to the EFI Boot Services Table
///
extern EFI_BOOT_SERVICES  *gBS;
  • gImageHandle(ImageHandle)
///
/// Cache the Image Handle
///
extern EFI_HANDLE         gImageHandle;

These three global variables are initialized in the function UefiBootServicesTableLibConstructor, which is the constructor of the library UefiBootServicesTableLib and called in the ProcessLibraryConstructorList in AutoGen.c. The ProcessLibraryConstructorList is called before UefiMain.

Constructor UefiBootServicesTableLibConstructor source code:

  • edk2/MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.c
EFI_HANDLE         gImageHandle = NULL;
EFI_SYSTEM_TABLE   *gST         = NULL;
EFI_BOOT_SERVICES  *gBS         = NULL;

/**
  The constructor function caches the pointer of Boot Services Table.

  The constructor function caches the pointer of Boot Services Table through System Table.
  It will ASSERT() if the pointer of System Table is NULL.
  It will ASSERT() if the pointer of Boot Services Table is NULL.
  It will always return EFI_SUCCESS.

  @param  ImageHandle   The firmware allocated handle for the EFI image.
  @param  SystemTable   A pointer to the EFI System Table.

  @retval EFI_SUCCESS   The constructor always returns EFI_SUCCESS.

**/
EFI_STATUS
EFIAPI
UefiBootServicesTableLibConstructor (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  //
  // Cache the Image Handle
  //
  gImageHandle = ImageHandle;
  ASSERT (gImageHandle != NULL);

  //
  // Cache pointer to the EFI System Table
  //
  gST = SystemTable;
  ASSERT (gST != NULL);

  //
  // Cache pointer to the EFI Boot Services Table
  //
  gBS = SystemTable->BootServices;
  ASSERT (gBS != NULL);

  return EFI_SUCCESS;
}

The gST variable is defined in user space, and the system table it points to is defined in the UEFI kernel. After referencing UefiBootServicesTableLib in the [LibraryClasses] of the application or driver project file, you can use gST to access the system table.

Example: gST used

#include<Uefi.h>
#include<Library/UefiBootServicesTableLib.h>
EFI_Status
UefiMain(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable)
{
    EFI_STATUS Status;
    UINTN Index;
    EFI_INPUT_KEY Key;
    CHAR16 StrBuffer[4] = {0};
    gST -> ConOut -> OutputString(gST -> ConOut,L"Please enter any key\n");
    gBS->WaitForEvent(1,&gST->ConIn->WaitForKey,&Index);
    Status = gST->ConIn->ReadKeyStroke(gST->ConIn,&Key);
    StrBuffer[0] = Key.UnicodeChar;
    StrBuffer[1] = '\n';
    gST->ConOut->OutputString(gST->ConOut,StrBuffer);
    return EFI_SUCCESS;
}

From the code point of view, it is actually to replace SystemTable and SystemTable - > bootservices with gST and gBS.

5 Summary

This paper focuses on the structure of system tables, the process of transferring system tables from kernel space to user space, and two methods of accessing system tables in user space. Get the system table to access the UEFI kernel in user space. The application and driver control the kernel through two core members BootServices and runtime services. EDK2 allocates global variables gBS and gRT in user space to refer to these two services.

Reference resources

This article is based on the platform of blog one article multiple sending OpenWrite Release!

Tags: Java ascii Programming

Posted on Wed, 04 Dec 2019 10:05:42 -0800 by adders