UEFI Runtime Driver là gì?
UEFI Runtime Driver dùng DXE_RUNTIME_DRIVER để code/data tồn tại sau ExitBootServices. Hiểu runtime memory, ConvertPointer, EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE và giới hạn sau EBS.
UEFI Runtime Driver là driver có code và data phải tồn tại sau ExitBootServices() để OS có thể gọi được. Nó khác DXE driver thông thường ở một điểm cốt lõi: toàn bộ code và data cần thiết sau EBS phải được đặt trong runtime memory - OS giữ lại vùng này sau khi firmware bàn giao.
Nếu viết runtime driver không đúng - dùng boot memory, quên convert pointer, hay gọi Boot Services sau EBS - lỗi thường không xuất hiện ngay. Nó xuất hiện khi OS gọi Runtime Service lần đầu sau SetVirtualAddressMap(), thường là crash im lặng hoặc wrong result.
DXE_RUNTIME_DRIVER vs DXE_DRIVER - khác nhau ở đâu
| Mục | Giá trị | Ghi chú |
|---|---|---|
| MODULE_TYPE | DXE_RUNTIME_DRIVER | Báo build system và loader biết driver này cần runtime memory. Khác với DXE_DRIVER thông thường. |
| Memory type | EfiRuntimeServicesCode / EfiRuntimeServicesData | Code và data của runtime driver phải nằm trong runtime memory type. OS giữ lại các vùng này sau EBS. |
| Tồn tại sau EBS | Có - nếu implement đúng | DXE driver thông thường dùng EfiBootServicesCode/Data - OS có thể reclaim sau EBS. Runtime driver dùng EfiRuntimeServicesCode/Data - OS phải giữ. |
| EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE | Cần register nếu có internal pointer | Khi OS gọi SetVirtualAddressMap(), firmware signal event này. Runtime driver thực tế gần như luôn cần handler này nếu có global/context/table pointer dùng sau EBS. |
| Boot Services sau EBS | Không được dùng | Sau EBS, Boot Services table không còn valid. Runtime driver không được gọi AllocatePool, LocateProtocol, v.v. sau ranh giới này. |
INF file - khai báo đúng module type
[Defines]
MODULE_TYPE = DXE_RUNTIME_DRIVER
BASE_NAME = MyRuntimeDriver
FILE_GUID = ...
ENTRY_POINT = MyRuntimeDriverEntryPoint
[Depex]
gEfiVariableArchProtocolGuid AND
gEfiVariableWriteArchProtocolGuid
MODULE_TYPE = DXE_RUNTIME_DRIVER cho firmware loader/build metadata biết đây là runtime-capable DXE driver. Khi image được load, code/data của image runtime driver sẽ được xử lý theo runtime memory type phù hợp để OS giữ lại sau ExitBootServices(). Tuy nhiên, các buffer private mà driver tự allocate vẫn phải dùng đúng memory type như EfiRuntimeServicesData - build system không tự biến mọi allocation thành runtime memory.
Register EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
Đây là bước bắt buộc với mọi runtime driver có internal pointer:
EFI_STATUS EFIAPI
MyRuntimeDriverEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_EVENT VirtualAddressChangeEvent;
// Khởi tạo runtime context, allocate runtime memory...
Status = gBS->AllocatePool(
EfiRuntimeServicesData, // ← PHẢI dùng runtime type, không dùng EfiBootServicesData
sizeof(MY_RUNTIME_CONTEXT),
(VOID **)&mRuntimeContext
);
if (EFI_ERROR(Status)) return Status;
// Register handler cho virtual address change
Status = gBS->CreateEventEx(
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
MyVirtualAddressChangeHandler,
NULL,
&gEfiEventVirtualAddressChangeGuid,
&VirtualAddressChangeEvent
);
if (EFI_ERROR(Status)) return Status;
// Install protocol/arch protocol dùng trong boot phase, hoặc hook/register
// runtime service entry point tùy driver/platform design.
// Lưu ý: protocol handle database là Boot Services world - sau EBS, OS không
// locate protocol. Runtime path phải đi qua gRT hoặc runtime entry point.
return EFI_SUCCESS;
}
ConvertPointer - convert tất cả internal pointer
Khi OS gọi SetVirtualAddressMap(), firmware signal EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE. Handler phải convert mọi pointer trỏ vào runtime memory từ physical sang virtual:
VOID EFIAPI
MyVirtualAddressChangeHandler (
IN EFI_EVENT Event,
IN VOID *EventContext
)
{
// Pseudo-code - code thật phải cẩn thận thứ tự convert
// Convert sub-pointer TRƯỚC qua local physical pointer, rồi convert context CUỐI
MY_RUNTIME_CONTEXT *RuntimeContext = mRuntimeContext; // giữ physical pointer để dereference
gRT->ConvertPointer(0, (VOID **)&RuntimeContext->DataBuffer); // sub-pointer trước
gRT->ConvertPointer(0, (VOID **)&RuntimeContext->ServiceTable); // sub-pointer trước
gRT->ConvertPointer(0, (VOID **)&mRuntimeContext); // context pointer cuối
// Sau khi mRuntimeContext đã được convert sang virtual address,
// không tiếp tục dùng physical local pointer để dereference lung tung.
// Code thật nên tách rõ phase convert và phase runtime access.
// KHÔNG convert pointer trỏ vào boot memory
}
Những gì không được làm sau ExitBootServices
Runtime driver phải tự giới hạn sau khi EBS được gọi:
| Mục | Giá trị | Ghi chú |
|---|---|---|
| Không gọi Boot Services | AllocatePool, LocateProtocol, InstallProtocol... | Boot Services table không còn valid sau EBS. Gọi là undefined behavior. |
| Không dùng boot memory | EfiBootServicesCode/Data | OS có thể reclaim boot memory bất kỳ lúc nào sau EBS. Data ở đây sẽ bị corrupt không báo trước. |
| Không dùng Protocol | LocateProtocol, OpenProtocol... | Protocol handle database là Boot Service. Sau EBS không thể locate hay open protocol. Kể cả protocol pointer đã cache từ trước EBS không nên dùng sau EBS trừ khi interface đó được thiết kế rõ là runtime-safe và backing memory hợp lệ. |
| Được dùng | Runtime Services, runtime memory đã allocate | gRT->GetVariable/SetVariable/GetTime/ResetSystem. Chỉ dùng memory đã allocate với EfiRuntimeServicesData. |
| Được dùng | Pointer đã convert qua ConvertPointer | Sau SetVirtualAddressMap(), chỉ pointer đã convert mới valid trong virtual address space. |
Pattern thực tế - runtime variable service
Một ví dụ phổ biến là UEFI Variable runtime driver. Nó phải hoạt động cả trước và sau EBS:
// Trước EBS: có thể dùng Boot Services bình thường
// Sau EBS: chỉ dùng runtime memory và runtime-safe backend theo platform
EFI_STATUS EFIAPI
RuntimeSetVariable (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
IN UINT32 Attributes,
IN UINTN DataSize,
IN VOID *Data
)
{
// Không gọi gBS->AllocatePool ở đây
// Không gọi gBS->LocateProtocol ở đây
// Dùng mRuntimeContext đã allocate với EfiRuntimeServicesData
// Dùng runtime-safe backend tùy platform:
// variable store, flash access, hoặc SMM communication tùy kiến trúc
return VariableServiceInternal(mRuntimeContext, VariableName, ...);
}
ExitBootServices event - chốt ranh giới boot/runtime
Ngoài EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE, runtime driver thường cần register thêm EVT_SIGNAL_EXIT_BOOT_SERVICES để biết khi nào ranh giới boot/runtime xảy ra:
EFI_EVENT ExitBootServicesEvent;
gBS->CreateEventEx(
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
MyExitBootServicesHandler,
NULL,
&gEfiEventExitBootServicesGuid,
&ExitBootServicesEvent
);
VOID EFIAPI
MyExitBootServicesHandler (
IN EFI_EVENT Event,
IN VOID *Context
)
{
// Chốt flag để code path sau này biết EBS đã xảy ra
mAtRuntime = TRUE;
// Dừng timer/event boot-time
// Đóng/migrate resource boot-time
// Từ đây không được gọi Boot Services nữa
}
Hai event này phục vụ hai mục đích khác nhau:
EVT_SIGNAL_EXIT_BOOT_SERVICES- báo ranh giới boot/runtime, driver chốt flag và dừng dùng Boot ServicesEVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE- báo OS đã map virtual address, driver convert pointer
Runtime driver phải hoạt động đúng ở cả hai giai đoạn: trước SetVirtualAddressMap() ở physical addressing, và sau đó ở virtual addressing. Bug thường chỉ lộ sau khi OS đã gọi SetVirtualAddressMap().
Failure pattern - runtime driver không hoạt động đúng sau boot
SetVariable fail từ OS sau khi Linux/Windows boot:
- Runtime driver dùng boot memory cho buffer → OS reclaim, data corrupt
- ConvertPointer bị quên cho một pointer → driver dereference physical address trong virtual space
- Xem thêm:
firmware-flow/runtime-phase.mdxcho trace cụ thể từ OS side
Runtime driver crash im lặng:
- Sau
SetVirtualAddressMap(), driver gọi pointer chưa convert - Không có exception trong firmware context - crash trong SMM hoặc runtime call context khó trace
Driver hoạt động trong UEFI Shell nhưng fail sau OS boot:
- UEFI Shell chạy trước EBS - Boot Services vẫn available
- Sau OS boot, EBS đã xảy ra - driver không còn dùng được Boot Services
- Thường do driver gọi
gBS->trong code path được kích hoạt sau EBS
Trace khi runtime driver fail
# Trên Linux - xem runtime service có available không
dmesg | grep -i "efi\|runtime"
# Kiểm tra runtime memory regions
cat /proc/iomem | grep "EFI Runtime"
# Variable service fail
efibootmgr -v 2>&1
# EFI_ACCESS_DENIED, EFI_WRITE_PROTECTED → security/lock policy
# Crash/hang → có thể pointer convert bug trong runtime driver
# EDK II - tìm runtime driver
grep -r "DXE_RUNTIME_DRIVER\|EFI_RUNTIME_SERVICES_DATA" \
edk2/ --include="*.inf" | head -10
grep -r "gEfiEventVirtualAddressChangeGuid\|ConvertPointer" \
edk2/ --include="*.c" -l | head -10
Checklist Runtime Driver
Câu hỏi tự kiểm tra
- Tại sao
DXE_RUNTIME_DRIVERcầnEfiRuntimeServicesDatathay vìEfiBootServicesData? EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGEđược signal khi nào và bởi ai?- Nếu quên
ConvertPointercho một pointer, lỗi xuất hiện lúc nào? - Tại sao driver hoạt động đúng trong UEFI Shell nhưng fail sau OS boot?
- Sau
ExitBootServices(), runtime driver còn được gọi những Boot Service nào?
Bài liên quan
- Runtime phase là gì?
- ExitBootServices là gì?
- Runtime Services là gì?
- UEFI Variable là gì?
- UEFI Service Driver là gì?
Nguồn tham khảo public
- UEFI Specification 2.11 - Runtime Services
- EDK II - RuntimeDxe
- EDK II - Variable RuntimeDxe
- EDK II UEFI Driver Writer’s Guide
Thấy nội dung này hữu ích?
Lưu lại hoặc chia sẻ cho người cũng đang học firmware, BIOS/UEFI và embedded systems.
Nội dung liên quan
Một số bài viết, ghi chú hoặc project có liên quan đến nội dung bạn vừa đọc.
BDS → TSL Handoff là gì?
Giải thích cách BDS chọn boot option, load EFI image và bàn giao cho OS loader trước ExitBootServices.
Runtime sau ExitBootServices là gì?
Giải thích firmware còn lại gì sau khi OS gọi ExitBootServices và vì sao Runtime Services vẫn quan trọng.
TSL là gì?
Giải thích Transient System Load phase trong UEFI boot flow và vai trò của OS loader trước ExitBootServices.
Biến note thành bài viết hoàn chỉnh
Notes là nơi ghi nhanh khái niệm.