Handle Database trong UEFI là gì?

Handle Database là nơi DXE quản lý quan hệ giữa handle và protocol, giúp debug driver binding, locate fail và boot device chain.

Cập nhật 6 phút đọc
BIOS Terms cover

Handle Database là nơi DXE Core lưu toàn bộ quan hệ giữa handleprotocol. Mỗi handle đại diện cho một entity - có thể là image, controller, child device, hay service abstraction. Protocol được install lên handle để mô tả entity đó có khả năng gì.

Nếu Protocol là interface, thì Handle là nơi các interface đó được treo lên - và Handle Database là danh bạ để driver tìm đúng địa chỉ.

Handle là gì?

Handle trong UEFI là một opaque pointer - về mặt kỹ thuật là một con trỏ trỏ đến internal structure của DXE Core. Driver không cần biết cấu trúc bên trong, chỉ cần dùng handle như một token để thao tác.

Mục Giá trị Ghi chú
Image Handle Đại diện cho một EFI image đã load Entry point của mọi driver/app nhận ImageHandle làm tham số đầu tiên.
Controller Handle Đại diện cho một hardware controller PCI bus driver tạo handle cho mỗi PCI device nó enumerate được.
Child Handle Đại diện cho child device của controller NVMe driver tạo child handle cho mỗi namespace. Partition driver tạo child handle cho mỗi partition.
Service Handle Đại diện cho một service abstraction Không gắn với hardware cụ thể. Ví dụ: console input/output handle, hoặc handle dùng để publish protocol dịch vụ nền tảng.

Cấu trúc handle/protocol trong thực tế

Ví dụ handle database cho một NVMe disk

PCI Root Bridge Handle
├─ EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL
└─ EFI_DEVICE_PATH_PROTOCOL

PCI Controller Handle  (NVMe PCI device)
├─ EFI_PCI_IO_PROTOCOL
└─ EFI_DEVICE_PATH_PROTOCOL

NVMe Controller Handle
├─ EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL
└─ EFI_DEVICE_PATH_PROTOCOL

NVMe Namespace Handle
├─ EFI_BLOCK_IO_PROTOCOL
├─ EFI_DISK_INFO_PROTOCOL
└─ EFI_DEVICE_PATH_PROTOCOL

Partition Handle
├─ EFI_BLOCK_IO_PROTOCOL
├─ EFI_DISK_IO_PROTOCOL
└─ EFI_DEVICE_PATH_PROTOCOL

Volume Handle
├─ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
└─ EFI_DEVICE_PATH_PROTOCOL

Mỗi tầng trong cây này có thể do một driver khác nhau tạo ra. BDS thường cần đi đến handle có EFI_SIMPLE_FILE_SYSTEM_PROTOCOL để mở file .efi. Nếu một tầng quan trọng trong chain thiếu, boot device có thể không xuất hiện.

Các API thao tác với Handle Database

Tìm tất cả handle có một protocol cụ thể:

EFI_HANDLE  *HandleBuffer;
UINTN       HandleCount;

// Tìm tất cả handle có EFI_BLOCK_IO_PROTOCOL
Status = gBS->LocateHandleBuffer(
               ByProtocol,
               &gEfiBlockIoProtocolGuid,
               NULL,
               &HandleCount,
               &HandleBuffer
             );

for (UINTN i = 0; i < HandleCount; i++) {
  EFI_BLOCK_IO_PROTOCOL *BlockIo;
  gBS->HandleProtocol(HandleBuffer[i], &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo);
  // xử lý từng Block I/O device
}

gBS->FreePool(HandleBuffer);  // nhớ free buffer

Trong tool/debug code, HandleProtocol() dùng để inspect nhanh là chấp nhận được. Trong driver model thật, nên dùng OpenProtocol() với attribute phù hợp.

Lấy tất cả protocol trên một handle:

EFI_GUID  **ProtocolBuffer;
UINTN     ProtocolCount;

// Liệt kê tất cả protocol install trên handle
Status = gBS->ProtocolsPerHandle(
               Handle,
               &ProtocolBuffer,
               &ProtocolCount
             );

// ProtocolBuffer là array of GUID pointers
gBS->FreePool(ProtocolBuffer);

Lấy danh sách tất cả handle trong database:

EFI_HANDLE *HandleBuffer;
UINTN      HandleCount;

Status = gBS->LocateHandleBuffer(
               AllHandles,
               NULL,
               NULL,
               &HandleCount,
               &HandleBuffer
             );

if (!EFI_ERROR(Status)) {
  // dùng HandleBuffer[0..HandleCount-1]
  gBS->FreePool(HandleBuffer);
}

Open Protocol tracking

Handle Database không chỉ lưu “protocol nào gắn với handle nào” - nó còn track ai đang mở protocol đó và với attribute gì.

// Xem ai đang open một protocol trên handle
EFI_OPEN_PROTOCOL_INFORMATION_ENTRY  *EntryBuffer;
UINTN                                 EntryCount;

gBS->OpenProtocolInformation(
       Handle,
       &gEfiPciIoProtocolGuid,
       &EntryBuffer,
       &EntryCount
     );

for (UINTN i = 0; i < EntryCount; i++) {
  // EntryBuffer[i].AgentHandle   - handle của driver đang mở
  // EntryBuffer[i].ControllerHandle - handle liên quan
  // EntryBuffer[i].Attributes   - BY_DRIVER, BY_CHILD_CONTROLLER, ...
  // EntryBuffer[i].OpenCount    - số lần mở
}

gBS->FreePool(EntryBuffer);

Thông tin này rất hữu ích khi debug tại sao Stop() fail: nếu một protocol vẫn có open entry với BY_DRIVER, driver đó phải CloseProtocol trước khi uninstall được.

Inspect handle database bằng UEFI Shell

UEFI Shell có các lệnh trực tiếp để debug handle database mà không cần thêm code:

# Liệt kê tất cả handle và protocol
dh -v

# Xem protocol trên một handle cụ thể (ví dụ handle 1A)
dh -v 1A

# Tìm handle có protocol cụ thể - tùy UEFI Shell build, có thể dùng tên alias hoặc GUID
dh -p BlockIo

# Liệt kê tất cả handle có Device Path
dh -d

Tùy UEFI Shell build, tên protocol alias có thể khác; nếu không nhận BlockIo, dùng help dh hoặc GUID tương ứng để tìm đúng syntax.

dh (dump handles) là công cụ đầu tiên nên dùng khi debug driver binding, boot device không thấy, hoặc protocol locate fail.

Khi nào cần quan tâm đến Handle Database?

Nếu bạn chỉ viết DXE driver đơn giản, install một protocol lên image/service handle rồi return, bạn chưa cần đào sâu. Nhưng khi debug:

  • Driver Start() success nhưng BDS không thấy device → thiếu protocol ở đâu đó trong chain
  • LocateProtocol trả EFI_NOT_FOUND dù driver đã chạy → protocol install lên sai handle
  • Stop() fail với EFI_ACCESS_DENIED → có open entry chưa close
  • Disconnect/reconnect controller hoạt động không đúng → open protocol tracking bị sai

Checklist khi debug Handle Database

Lỗi hiểu nhầm hay gặp

Handle không phải pointer đến hardware. Handle là opaque token - bên trong là structure của DXE Core, không phải địa chỉ register hay MMIO của device. Giá trị số của handle thay đổi mỗi lần boot.

Một GUID protocol có thể xuất hiện trên nhiều handle. EFI_BLOCK_IO_PROTOCOL có thể install trên NVMe handle, SATA handle, và USB storage handle đồng thời. LocateProtocol chỉ trả handle đầu tiên - nếu cần tất cả, dùng LocateHandleBuffer.

Handle Database là cấu trúc boot-time do DXE Core quản lý. Sau ExitBootServices(), OS không được dùng gBS để locate/open protocol nữa.

Câu hỏi tự kiểm tra

  1. Handle trong UEFI đại diện cho những loại entity nào?
  2. Vì sao một GUID protocol có thể xuất hiện trên nhiều handle khác nhau?
  3. LocateHandleBuffer khác LocateProtocol ở điểm nào?
  4. OpenProtocol tracking dùng để làm gì - tại sao quan trọng với Stop()?
  5. Driver dùng HandleProtocol thay vì OpenProtocol gây ra vấn đề gì?
  6. Lệnh Shell nào dùng để inspect handle database nhanh nhất?
  7. Nếu Stop() trả EFI_ACCESS_DENIED, bạn kiểm tra gì trước?

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.