Using UEFI Runtime Services in your Kernel
UEFI Runtime Services are functions provided by the firmware that can be used in any CPU mode, and at any time, even after you exit UEFI Boot Services. Runtime Services can contain useful functions for an operating system, such as resetting/shutting down the system, getting the current time, setting the current time, etc. One may wish to use these functions outside of the boot process. The process is relatively trivial.
Obtaining the Runtime Services Pointer
Bootloader
In order to obtain the Runtime Services pointer it is highly recommended to use a modified, minimal, UEFI-exclusive bootloader. Some bootloaders that will not work:
- GRUB
- coreboot
- NexBoot
- Any BIOS-based bootloader
Obtaining
In order to obtain the Runtime Services pointer you will need an info structure that you pass to your kernel. If you do not have one it is imperative that you create one.
You will need to simply add a member to the structure.
typedef struct s_boot_info
{
// A pointer to the EFI runtime services structure
// that contains numerous useful function pointers
EFI_RUNTIME_SERVICES *RT;
} boot_info;
In order to pass the pointer to the kernel, you need to define the EFI_RUNTIME_SERVICES structure yourself.
You need to define the Runtime Services header and structure.
typedef struct s_efi_table_header {
uint64_t signature;
uint32_t rev;
uint32_t size;
uint32_t crc;
uint32_t reserved;
} Efi_Table_Header;
// A structure replicating that of the EFI_RUNTIME_SERVICES structure in UEFI
typedef struct s_efi_runtime_service_handle {
Efi_Table_Header header;
Efi_Get_Time GetTime;
Efi_Set_Time SetTime;
Efi_Get_Wakeup_Time GetWakeupTime;
Efi_Set_Wakeup_Time SetWakeupTime;
Efi_Set_Virtual_Address_Map SetVirtualAddressMap;
Efi_Convert_Pointer ConvertPointer;
Efi_Get_Variable GetVariable;
Efi_Get_Next_Variable_Name GetNextVariableName;
Efi_Set_Variable SetVariable;
Efi_Get_Next_High_Mono_Count GetNextHighMonotonicCount;
Efi_Reset_System ResetSystem;
Efi_Update_Capsule UpdateCapsule;
Efi_Query_Capsule_Capabilities QueryCapsuleCapabilities;
Efi_Query_Variable_Info QueryVariableInfo;
} Efi_Runtime_Services;
You will also need to declare the methods as well as dependent structures and enums yourself.
typedef enum {
EfiResetCold,
EfiResetWarm,
EfiResetShutdown
} Efi_Reset_Type;
typedef
uint64_t (EfiApi *Efi_Reset_System)
(
Efi_Reset_Type ResetType,
uint64_t ResetStatus,
uint64_t DataSize,
uint16_t *ResetData
);
To find the correct declarations/structures, refer to the UEFI headers, specifically GNU-EFI's efiapi.h.
EFIAPI
This definition is not a type, it is a macro that specifies that a function must use the UEFI calling convention.
#define EfiApi __attribute__(ms_abi)
Using the Runtime Services
Now that you have a pointer, you can now start calling UEFI functions.
void kmain(boot_info *info)
{
// Shuts down the system
info->RT->ResetSystem(EfiResetShutdown, 0, 0, nullptr);
}