How to build DXE Driver by EDK2
Tags: uefi, edk2, dxe, tutorial
Published: 2022-06-26 14:47:00

In this series, I will show you how to build custom DXE Driver by EDK2 and execute it on [UP2 Pro](https://up-shop.org/up-squared-pro-atom-quad-core-0464.html) (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 BaseTools`
5. `source edksetup.sh`

[BaseTool](https://edk2-docs.gitbook.io/edk-ii-basetools-user-guides/build) 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).
```txt
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
```
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.
```zsh
MyDxe
├── MyDxe.c
└── MyDxe.inf
```

### 3: Editing MdeModulePkg.dsc
Add your module under the `[Component]` section inside `edk2/MdeModulePkg/MdeModulePkg.dsc` like followings.
```inf
[Components]
  MdeModulePkg/MyDxe/MyDxe.inf # add only this
  MdeModulePkg/Application/HelloWorld/HelloWorld.inf
  MdeModulePkg/Application/DumpDynPcd/DumpDynPcd.inf
  MdeModulePkg/Application/MemoryProfileInfo/MemoryProfileInfo.inf
```

## Configuring MyDxe.inf
```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
```
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](https://winraid.level1techs.com/t/what-is-depex-dxe-section/32234/5).


## Source Code of DXE Driver (MyDxe.c)
```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;
}
```
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](https://edk2-docs.gitbook.io/edk-ii-build-specification/2_design_discussion/22_uefipi_firmware_images) 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.
```zsh
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
```
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.