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.

Cập nhật 8 phút đọc
Driver Types cover

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 Services
  • EVT_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.mdx cho 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

  1. Tại sao DXE_RUNTIME_DRIVER cần EfiRuntimeServicesData thay vì EfiBootServicesData?
  2. EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE được signal khi nào và bởi ai?
  3. Nếu quên ConvertPointer cho một pointer, lỗi xuất hiện lúc nào?
  4. Tại sao driver hoạt động đúng trong UEFI Shell nhưng fail sau OS boot?
  5. Sau ExitBootServices(), runtime driver còn được gọi những Boot Service nào?

Bài liên quan

Nguồn tham khảo public

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.

Biến note thành bài viết hoàn chỉnh

Notes là nơi ghi nhanh khái niệm.