UEFI Bus Driver là gì?

UEFI Bus Driver enumerate child device và tạo child handle với Device Path. Hiểu chuỗi protocol từ bus đến boot option và lỗi khi child handle thiếu hoặc Device Path sai.

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

UEFI Bus Driver là driver quản lý một bus/controller và tạo child handle cho từng device phía sau bus. Đây là điểm khác biệt cốt lõi với device driver: bus driver không chỉ publish protocol cho chính nó, mà còn tạo ra các handle mới đại diện cho từng child device.

Nếu bus driver không tạo child handle đúng, device driver phía sau không có handle để bind - device không bao giờ xuất hiện trong hệ thống dù phần cứng đang hoạt động.

Vai trò của bus driver trong handle database

Trước khi bus driver Start():
  ControllerHandle
    └─ EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL  ← bus chưa enumerate

Sau khi bus driver Start():
  ControllerHandle
    ├─ EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL
    └─ [Bus/private management protocol nếu driver cần]

  ChildHandle[0]   ← child device 0
    ├─ EFI_PCI_IO_PROTOCOL
    ├─ EFI_DEVICE_PATH_PROTOCOL
    └─ [open BY_CHILD_CONTROLLER → ControllerHandle]

  ChildHandle[1]   ← child device 1
    ├─ EFI_PCI_IO_PROTOCOL
    ├─ EFI_DEVICE_PATH_PROTOCOL
    └─ [open BY_CHILD_CONTROLLER → ControllerHandle]

Chuỗi protocol từ bus đến boot option

Bus driver là một trong những mắt xích đầu tiên trong chuỗi protocol mà BDS cần để resolve Device Path và đi tới file bootloader. Nếu một mắt xích thiếu, BDS không thể resolve Device Path:

Mục Giá trị Ghi chú
PCI Bus Driver Tạo child handle cho mỗi PCI function Install EFI_PCI_IO_PROTOCOL + Device Path lên child. PCI device driver bind vào child này.
Storage Controller Driver Tạo child handle cho mỗi disk/namespace Install EFI_BLOCK_IO_PROTOCOL + Device Path lên child. Partition driver bind vào đây.
Partition Driver Tạo child handle cho mỗi partition Install Block I/O/Disk I/O + Device Path cho partition handle. Filesystem driver như FAT sau đó bind vào partition handle và install Simple File System Protocol.
File System Driver (FAT, v.v.) Expose filesystem cho BDS Bind vào partition/block handle và install EFI_SIMPLE_FILE_SYSTEM_PROTOCOL. BDS cần protocol này để mở file .efi và LoadImage.
USB Bus Driver Tạo child handle cho mỗi USB device Install USB IO Protocol + Device Path. USB device driver bind vào child này.

Device Path chain - tại sao bus driver phải build đúng

Device Path của child handle phải là extension của parent: lấy Device Path của ControllerHandle rồi append thêm node cho child device.

// Pseudo-code rút gọn - code thật phải check Status từng bước
// AppendDevicePathNode() tạo buffer mới; nếu install protocol fail thì free,
// nếu install thành công thì Stop() phải cleanup đúng ownership

// Build Device Path cho child: parent path + bus-specific node
EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
EFI_DEVICE_PATH_PROTOCOL *ChildDevicePath;

// Lấy Device Path của parent controller
gBS->OpenProtocol(
    ControllerHandle,
    &gEfiDevicePathProtocolGuid,
    (VOID **)&ParentDevicePath,
    This->DriverBindingHandle,
    ControllerHandle,
    EFI_OPEN_PROTOCOL_GET_PROTOCOL
);

// Tạo PCI device path node cho child
PCI_DEVICE_PATH PciNode;
PciNode.Header.Type    = HARDWARE_DEVICE_PATH;
PciNode.Header.SubType = HW_PCI_DP;
SetDevicePathNodeLength(&PciNode.Header, sizeof(PCI_DEVICE_PATH));
PciNode.Device   = DeviceNumber;
PciNode.Function = FunctionNumber;

// Append node vào parent path → tạo child path
ChildDevicePath = AppendDevicePathNode(ParentDevicePath, &PciNode.Header);
// ChildDevicePath = PciRoot(0)/Pci(DeviceNumber, FunctionNumber)

BY_CHILD_CONTROLLER - parent-child relationship

Sau khi tạo child handle, bus driver phải open protocol trên parent với BY_CHILD_CONTROLLER để firmware biết child handle nào thuộc controller nào:

// Mở parent protocol với BY_CHILD_CONTROLLER
// Dùng để firmware và Stop() biết child thuộc về controller nào
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *RootBridgeIo;
gBS->OpenProtocol(
    ControllerHandle,                      // handle của bus/parent
    &gEfiPciRootBridgeIoProtocolGuid,      // protocol trên parent
    (VOID **)&RootBridgeIo,
    This->DriverBindingHandle,             // AgentHandle: driver binding
    ChildHandle,                           // ControllerHandle param: child handle
    EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER  // parent-child relationship
);

Relationship này quan trọng vì:

  • DisconnectController() dùng nó để biết child nào cần stop trước
  • Stop() với NumberOfChildren > 0 dùng nó để xác định child list
  • openinfo trong UEFI Shell hiển thị relationship để debug

RemainingDevicePath trong bus driver Start()

Bus driver Start() nhận RemainingDevicePath để biết cần tạo child nào:

if (RemainingDevicePath != NULL && !IsDevicePathEnd(RemainingDevicePath)) {
    // Chỉ tạo child khớp với node đầu tiên của RemainingDevicePath
    PCI_DEVICE_PATH *PciNode = (PCI_DEVICE_PATH *)RemainingDevicePath;
    // Tạo child handle duy nhất cho Device/Function cụ thể
    CreateChildHandle(PciNode->Device, PciNode->Function);
} else {
    // NULL hoặc End node: enumerate toàn bộ child
    for (UINTN i = 0; i < DeviceCount; i++) {
        CreateChildHandle(Devices[i].Device, Devices[i].Function);
    }
}

Đây là cơ chế ConnectDevicePath() của BDS dùng để connect chỉ controller trên boot path - không phải toàn bộ bus.

Supported()Start() phải xử lý RemainingDevicePath nhất quán: Supported() xác nhận node hợp lệ, còn Start() thực sự tạo child handle tương ứng.

Failure pattern - device có trên bus nhưng không lên boot menu

Trace từng tầng khi device không xuất hiện:

1. Bus driver Start() có chạy không?
   → Shell> drivers  (kiểm tra driver đã bind)
   → Shell> dh -v    (xem protocol trên controller handle)

2. Child handle có được tạo không?
   → Shell> devtree  (cây controller/child)
   → Nếu không có child → bus driver Start() fail hoặc RemainingDevicePath sai

3. Child handle có đủ protocol không?
   → Shell> dh -v <child_handle>
   → Phải có: Device Path + bus-specific protocol (PCI IO, Block IO, v.v.)
   → Nếu thiếu Device Path → BDS không thể dùng Device Path để resolve

4. Device driver có bind vào child handle không?
   → Shell> openinfo <child_handle>
   → Phải thấy BY_DRIVER open từ device driver
   → Nếu không → device driver Supported() fail trên child handle

5. Protocol chain có đủ đến Simple File System không?
   → Shell> dh -p EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
   → Nếu không có → có thể partition driver chưa tạo partition Block I/O handle, hoặc filesystem driver như FAT chưa bind/install EFI_SIMPLE_FILE_SYSTEM_PROTOCOL

Module liên quan khi debug

Mục Giá trị Ghi chú
PCI Bus Driver (PciBusDxe) EDK II edk2/MdeModulePkg/Bus/Pci/PciBusDxe/ - enumerate PCI device, tạo child handle với EFI_PCI_IO_PROTOCOL.
devtree UEFI Shell Xem toàn bộ cây controller/child. Nếu child handle thiếu → vấn đề ở bus driver.
dh -v UEFI Shell Xem protocol list và open info trên từng handle. Kiểm tra Device Path và bus protocol.
connect -r UEFI Shell Recursive connect toàn bộ. Nếu device xuất hiện sau connect -r nhưng không xuất hiện khi boot → BDS ConnectAll bị skip.

Checklist Bus Driver

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

  1. Tại sao bus driver tạo child handle thay vì chỉ install protocol lên controller handle?
  2. Device Path của child handle phải có quan hệ gì với Device Path của parent?
  3. BY_CHILD_CONTROLLER khác BY_DRIVER thế nào - ai dùng nó?
  4. Nếu devtree không thấy child handle sau connect -r, bước debug tiếp theo là gì?
  5. RemainingDevicePath ảnh hưởng thế nào đến số lượng child handle được tạ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.