DEV Community

MachineHunter
MachineHunter

Posted on • Updated on

How to build DXE Driver by EDK2

In this series, I will show you how to build custom DXE Driver by EDK2 and execute it on UP2 Pro (Single Board Computer). To check if it was executed successfully, this DXE Driver will store current time in UEFI Variable so that, you can check if the time was stored correctly from UEFI Shell.

Preparing EDK2

  1. sudo apt install build-essential uuid-dev iasl nasm git python python3-distutils python3-apt acpica-tools
  2. go to your project's folder and git clone https://github.com/tianocore/edk2.git
  3. cd edk2 and git submodule update --init
  4. make -C BaseTool
  5. source edksetup.sh

BaseTool is a set of tools to build UEFI modules and format the module to EFI_SECTION, FFS, FV and so on. By executing edksetup.sh, you can execute commands in BaseTool without specifying 'PATH'. You might want to take a brief look at the link to know what kind of tools are in BaseTools and what kind of arguments you can specify.

Configuration of EDK2 for making DXE Drivers

1: Editing target.txt

Edit edk2/Conf/target.txt like this (Comments excluded).

ACTIVE_PLATFORM = MdeModulePkg/MdeModulePkg.dsc
TARGET          = DEBUG
TARGET_ARCH     = X64
TOOL_CHAIN_CONF = Conf/tools_def.txt
TOOL_CHAIN_TAG  = GCC5
BUILD_RULE_CONF = Conf/build_rule.txt
Enter fullscreen mode Exit fullscreen mode

You might want to execute build in edk2/ and see if it returns - Done -. If you got any fails at this point, then some configs on your edk2 are wrong.

2: Making necessary files

Make folder for your DXE Driver inside edk2/MdeModulePkg/ and prepare two files inside it like this.

MyDxe
├── MyDxe.c
└── MyDxe.inf
Enter fullscreen mode Exit fullscreen mode

3: Editing MdeModulePkg.dsc

Add your module under the [Component] section inside edk2/MdeModulePkg/MdeModulePkg.dsc like followings.

[Components]
  MdeModulePkg/MyDxe/MyDxe.inf # add only this
  MdeModulePkg/Application/HelloWorld/HelloWorld.inf
  MdeModulePkg/Application/DumpDynPcd/DumpDynPcd.inf
  MdeModulePkg/Application/MemoryProfileInfo/MemoryProfileInfo.inf
Enter fullscreen mode Exit fullscreen mode

Configuring MyDxe.inf

[Defines]
  INF_VERSION                    = 0x00010006
  BASE_NAME                      = MyDxe
  FILE_GUID                      = <put your GUID generated by uuidgen>
  MODULE_TYPE                    = DXE_DRIVER
  VERSION_STRING                 = 1.0
  ENTRY_POINT                    = MyDxeEntry

[Sources]
    MyDxe.c

[Packages]
    MdePkg/MdePkg.dec
    MdeModulePkg/MdeModulePkg.dec

[LibraryClasses]
    UefiDriverEntryPoint
    UefiRuntimeServicesTableLib
    UefiLib
    PrintLib

[Protocols]

[Depex]
    TRUE
Enter fullscreen mode Exit fullscreen mode

I'm not using any Protocols in this DXE Driver so I left [Protocols] blank. If this DXE Driver has to be executed after specific Protocols are installed, you have to put that into [Depex], but I'm not using any Protocols so I just put TRUE. For further details, you can check CodeRush's post in Win-Raid Forum.

Source Code of DXE Driver (MyDxe.c)

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/PrintLib.h>
#include <Library/UefiDriverEntryPoint.h>
#include <Library/UefiRuntimeServicesTableLib.h>

EFI_STATUS EFIAPI MyDxeEntry(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) {
    UINT32 myvarSize      = 30;
    CHAR8  myvarValue[30] = {0};
    CHAR16 myvarName[30]  = L"MyDxeStatus";
    EFI_TIME time;

    // 11223344-5566-7788-99aa-bbccddeeff00
    EFI_GUID myvarGUID = { 0x11223344, 0x5566, 0x7788, { 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00 }};

    gRT->GetTime(&time, NULL);

    AsciiSPrint(myvarValue, 12, "%2d/%2d %2d:%2d", time.Month, time.Day, time.Hour, time.Minute);

    gRT->SetVariable(
            myvarName,
            &myvarGUID,
            EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
            myvarSize,
            myvarValue);

    return EFI_SUCCESS;
}
Enter fullscreen mode Exit fullscreen mode

This will create UEFI variable named "MyDxeStatus" and stores current time derived by GetTime function in the Runtime Services. To prepare format string, I used AsciiSPrint from
PrintLib Library.

Build DXE Driver

You can execute build to build this driver. If there were no errors, it will show Done at the end and output files can be found in edk2/Build/MdeModule/DEBUG_GCC5/X64/MdeModulePkg/MyDxe/MyDxe/OUTPUT.

Generating FFS File from the DXE Driver

If you want to add this DXE Driver into BIOS image, you have to convert this into Fimware FileSystem (FFS) format. FFS is consisted of EFI_SECTION Files, which in most case are below 4 sections.

  • DXE dependency section
  • PE32 image section
  • User interface section
  • Version section

You can refer to EDK2 Build Specification for the information related to these. But anyway, you have to first generate these section files and then, combine them to construct FFS File. Generating section files can be done by using GenSec command from BaseTools. Also, there is GenFfs command from BaseTools to combine them into FFS file.

Inside edk2/Build/MdeModule/DEBUG_GCC5/X64/MdeModulePkg/MyDxe/MyDxe/OUTPUT/, I made a folder "FFS" and put the generated files there. Section files are generated by the following commands.

GenSec MyDxe.efi -S EFI_SECTION_PE32 -o FFS/MyDxe.pe32
GenSec MyDxe.efi -S EFI_SECTION_USER_INTERFACE -o FFS/MyDxe.ui -n "MyDxe"
GenSec MyDxe.efi -S EFI_SECTION_VERSION -o FFS/MyDxe.ver

cd FFS
GenFfs -t EFI_FV_FILETYPE_DRIVER -d 1 -g "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -i MyDxe.pe32 -i MyDxe.ui -i MyDxe.ver -o MyDxe.ffs
Enter fullscreen mode Exit fullscreen mode

You need to generate GUID for this FFS and specify it in GenFfs. In this DXE Driver, I didn't use DEPEX so I didn't put that, but if you have, you can execute GenSec MyDxe.depex -S EFI_SECTION_DXE_DEPEX -o FFS/MyDxe.depex (MyDxe.depex is already built with build command in the OUTPUT folder) and specify that with -i option in GenFfs command.

Discussion (0)