Using UEFI Runtime Services in your Kernel

From OSDev Wiki
Jump to navigation Jump to search

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);
}