UEFI Device Driver là gì?

UEFI Device Driver bind vào child handle do bus driver tạo, install abstraction protocol. Hiểu protocol chain đến boot menu và lỗi khi device có handle nhưng không lên boot option.

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

UEFI Device Driver bind vào controller/child handle và install abstraction protocol - giao diện mà các tầng cao hơn (BDS, partition driver, filesystem driver) dùng để truy cập device mà không cần biết chi tiết hardware.

Điểm khác với bus driver: một device driver thuần thường không tạo child handle - nó manage một handle cụ thể và publish protocol lên đó. Trong cách phân loại nghiêm ngặt, driver nào tạo child handle thì đang thực hiện vai trò bus driver cho tầng con đó. Tuy nhiên có những driver mang tính hybrid/bus-like: chúng bind vào một controller vật lý nhưng bên trong có nhiều logical unit (NVMe namespace, SCSI LUN), nên driver sẽ tạo child handle cho từng logical unit.

Device driver nhận handle từ đâu

Device driver không bind vào raw hardware - nó bind vào child handle đã được bus driver tạo. Đây là thứ tự thực tế:

1. Platform firmware publish hardware resource (Root Bridge, Memory Map)

2. Bus driver (PCI Bus Driver) bind vào Root Bridge
   → Tạo child handle cho mỗi PCI device
   → Install EFI_PCI_IO_PROTOCOL + Device Path lên child handle

3. Device driver bind vào child handle
   → Đọc PCI IO để kiểm tra Vendor/Device ID trong Supported()
   → Trong Start(): init hardware, install abstraction protocol

4. Abstraction protocol được publish
   → Partition driver, filesystem driver, BDS tiếp tục từ đây

Nếu bước 2 chưa xong, Supported() của device driver sẽ fail ngay từ đầu vì protocol cần thiết chưa có trên handle.

Protocol device driver thường install

Mục Giá trị Ghi chú
EFI_BLOCK_IO_PROTOCOL Storage device NVMe, SATA, USB Mass Storage driver install Block I/O. Partition driver bind vào đây để tạo partition handle.
EFI_BLOCK_IO2_PROTOCOL Storage device (async) Version async của Block I/O, hỗ trợ non-blocking I/O. Thường đi kèm Block I/O.
EFI_SIMPLE_NETWORK_PROTOCOL Network interface NIC driver install. PXE và network boot stack dùng để gửi/nhận packet.
EFI_GRAPHICS_OUTPUT_PROTOCOL Display GOP driver install. BDS và UEFI Shell dùng để output text/graphics.
EFI_SERIAL_IO_PROTOCOL Serial port UART driver install. Console redirect và debug output dùng đây.
EFI_DISK_IO_PROTOCOL Block device abstraction Filesystem driver có thể dùng Disk I/O hoặc Block I/O tùy implementation/layer.

Protocol chain đến boot menu

Device driver publish protocol, nhưng để device lên boot menu, cần toàn bộ chain đầy đủ:

NVMe Controller Handle (từ PCI Bus Driver)
  └─ EFI_PCI_IO_PROTOCOL
  └─ Device Path: PciRoot(0)/Pci(0x1D,0x0)

NVMe Driver bind vào → tạo Namespace Handle
  └─ EFI_BLOCK_IO_PROTOCOL
  └─ EFI_DEVICE_PATH_PROTOCOL: .../NVMe(1,...)

Partition Driver bind vào Namespace Handle → tạo Partition Handle
  └─ EFI_BLOCK_IO_PROTOCOL (partition)
  └─ EFI_DEVICE_PATH_PROTOCOL: .../HD(1,GPT,...)
  └─ EFI_DISK_IO_PROTOCOL (tùy driver stack/platform)

FAT Driver bind vào Partition Handle
  └─ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL  ← BDS cần để LoadImage
  └─ EFI_DEVICE_PATH_PROTOCOL: .../HD(1,GPT,...)  ← Device Path của partition handle
                                                   ← (không có File() node ở đây)

Boot#### / LoadImage File Path
  └─ .../HD(1,GPT,...)/File(\EFI\BOOT\BOOTX64.EFI) ← File() node nằm trong boot option

Hybrid/bus-like driver - NVMe namespace là ví dụ

NVMe driver là ví dụ driver hybrid/bus-like: bind vào PCI NVMe controller nhưng tạo child handle cho từng namespace:

// NVMe driver Start() flow
// 1. Bind vào PCI controller handle
gBS->OpenProtocol(ControllerHandle, &gEfiPciIoProtocolGuid, ..., BY_DRIVER);

// 2. Init NVMe controller hardware

// 3. Enumerate namespace - mỗi namespace là một logical unit
for (UINTN i = 0; i < NamespaceCount; i++) {
    EFI_HANDLE NamespaceHandle = NULL;

    // Tạo child handle cho namespace
    gBS->InstallProtocolInterface(
        &NamespaceHandle,
        &gEfiBlockIoProtocolGuid,
        EFI_NATIVE_INTERFACE,
        &NamespaceCtx[i].BlockIo
    );

    // Install Device Path: parent path + NVMe namespace node
    gBS->InstallProtocolInterface(
        &NamespaceHandle,
        &gEfiDevicePathProtocolGuid,
        EFI_NATIVE_INTERFACE,
        NamespaceDevicePath[i]
    );

    // Link parent-child relationship
    gBS->OpenProtocol(
        ControllerHandle,
        &gEfiPciIoProtocolGuid,
        ...,
        This->DriverBindingHandle,
        NamespaceHandle,
        EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
    );
}

Đây là lý do bios/uefi-driver-model.mdx không thể nói cứng “device driver không tạo child handle” - nó phụ thuộc loại device.

Failure pattern - device driver bind nhưng không có effect

Block I/O có nhưng không có partition handle:

Shell> devtree   # Không thấy child handle của namespace/disk handle
# → Partition table corrupt, partition type không được nhận, hoặc PartitionDxe chưa bind

Partition handle có nhưng không có Simple File System:

Shell> dh -p EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
# Không thấy handle nào từ partition đó
# → FAT/filesystem driver chưa bind
# → Partition không phải filesystem firmware hỗ trợ
# → ESP không đúng format hoặc filesystem lỗi

Device driver bind nhưng install sai protocol:

  • Install protocol lên sai handle (ControllerHandle thay vì NamespaceHandle)
  • Install protocol không đầy đủ - thiếu Device Path trên child handle
  • BDS không thể resolve Device Path → boot option không được tạo

Supported() pass nhưng Start() fail im lặng:

Shell> dh -v <pci_handle>
# Không thấy BY_DRIVER open từ device driver → Start() fail
Shell> drivers
# Driver có trong list nhưng "I" (Images) không có Controller
# → Supported() pass nhưng Start() trả lỗi ngay

NVMe / SATA không lên boot menu dù detect được:

  • Controller bind OK, namespace handle tạo OK
  • Partition table corrupt → partition driver fail → không có Simple File System
  • GPT partition signature mismatch với Boot#### Device Path

Trace khi device driver có vấn đề

# Bước 1: Driver có bind không?
Shell> drivers
# Xem cột controller/handle managed (tùy Shell version) - nếu = 0 thì Supported() fail hoặc không được gọi

# Bước 2: Protocol chain từ controller handle
Shell> devtree           # cây controller → namespace → partition
Shell> dh -v <handle>    # protocol list của từng handle

# Bước 3: Protocol chain đến Simple File System
Shell> dh -p EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
# Không thấy handle nào → chain bị đứt ở đâu đó

# Bước 4: Xác định đứt ở đâu bằng devtree + dh -v
Shell> devtree                          # có partition child handle không?
# Nếu disk/namespace Block I/O có nhưng không có partition child handle
# → partition layer fail (PartitionDxe, partition table)
Shell> dh -v <partition_handle>         # có SFSP không?
# Nếu partition handle có nhưng không có SFSP
# → FAT/filesystem driver chưa bind

# EDK II trace - device driver Start()
grep -r "InstallProtocolInterface\|EFI_BLOCK_IO_PROTOCOL" \
    edk2/MdeModulePkg/Bus/Nvme/NvmExpressDxe/ --include="*.c" | head -10

Checklist Device Driver

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

  1. Device driver khác bus driver ở điểm nào về việc tạo child handle?
  2. Tại sao device có Block I/O nhưng vẫn không lên boot menu?
  3. NVMe driver tạo child handle vì lý do gì - khác với driver thông thường thế nào?
  4. Nếu dh -p EFI_SIMPLE_FILE_SYSTEM_PROTOCOL không thấy handle nào, bước trace tiếp theo là gì?
  5. Protocol cần thiết để BDS tạo boot option cho một storage device là gì?

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.