Device Path trong UEFI là gì?

Device Path mô tả đường đi từ bus phần cứng đến file EFI qua chuỗi node nhị phân. Hiểu cấu trúc này giúp debug boot fail và Boot#### stale sau thay disk.

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

Firmware không thể chỉ dựa vào đường dẫn text kiểu C:\boot.efi như OS. Ở thời điểm BDS chạy, firmware cần mô tả cả đường đi phần cứng tới device chứa file đó.

Device Path là cấu trúc mô tả đường đi đó - một chuỗi node nhị phân, mỗi node nói “đi tiếp qua thiết bị hoặc lớp abstraction này”.

Cấu trúc tổng quát

01 ROOT

PciRoot(0x0)

Điểm bắt đầu từ PCI root bridge.

02 BUS

Pci(0x17,0x0)

Controller cụ thể trên PCI bus.

03 DISK

HD(1,GPT,...)

Partition chứa EFI System Partition.

04 FILE

File(\EFI\BOOT\BOOTX64.EFI)

EFI image sẽ được load.

Mỗi node là một struct nhị phân gồm Type, SubTypeLength. Chuỗi node kết thúc bằng End node (Type=0x7F).

Các loại node phổ biến

Mục Giá trị Ghi chú
PciRoot(x) ACPI Path - PCI Root Bridge Điểm xuất phát của cây PCI. x là UID của root bridge.
Pci(dev,func) Hardware Path - PCI device Device và Function number trên PCI bus.
Sata(port,pm,lun) Messaging Path - SATA Port, port multiplier và LUN của SATA device.
NVMe(ns,identifier) Messaging Path - NVMe Namespace ID và identifier của NVMe namespace. Cách hiển thị text có thể khác nhau tùy tool/UEFI version.
USB(port,iface) Messaging Path - USB Port number và interface number.
HD(part,sig,type,start,size) Media Path - Partition Partition number, signature (GPT GUID hoặc MBR disk sig), partition type, start LBA, size.
File(path) Media Path - File path Đường dẫn file trên filesystem. Thường là \EFI\xxx\xxx.efi.
IPv4(...) / MAC(...) Messaging Path - Network Dùng cho PXE boot hoặc network boot option.

Ví dụ thực tế: Windows Boot Manager

PciRoot(0x0)/Pci(0x17,0x0)/Sata(0x0,0x0,0x0)/HD(1,GPT,9A1B...,0x800,0x100000)/File(\EFI\Microsoft\Boot\bootmgfw.efi)

Đọc từ trái sang phải: PCI root bridge → storage controller → SATA disk → partition 1 (GPT, với partition GUID cụ thể) → file loader.

Device Path gắn với Handle Database

Device Path Protocol (EFI_DEVICE_PATH_PROTOCOL) thường được install lên controller handle và child handle trong handle database. BDS/Boot Manager dùng Device Path cùng Handle Database để tìm hoặc connect tới handle tương ứng. Quá trình này thường gồm connect controller theo Device Path, locate handle có Device Path Protocol hoặc Simple File System Protocol phù hợp, rồi mới gọi LoadImage().

Nếu các handle trên đường đi không publish Device Path Protocol phù hợp, hoặc controller chưa được connect nên protocol cần thiết chưa xuất hiện, Boot Manager có thể không resolve được boot option.

Khi debug boot fail - tách từng node

Đừng chỉ hỏi “file .efi có tồn tại không?”. Tách Device Path ra từng node:

Boot#### active
  └─ Device Path còn đúng controller không?
      └─ HD node còn đúng GPT partition GUID không?
          └─ File node còn đúng đường dẫn loader không?

Một lỗi rất phổ biến: thay SSD hoặc clone Windows sang disk khác. BootOrder vẫn còn, Boot0000 vẫn còn, nhưng HD() node chứa partition GUID cũ. BDS đọc được boot option nhưng không resolve được handle → EFI_NOT_FOUND.

Cấu trúc nhị phân của một node

Mỗi Device Path node có header 4 byte, sau đó là data riêng của từng node type:

// Header chung - mọi node đều bắt đầu bằng struct này
typedef struct {
  UINT8   Type;       // 0x01=Hardware, 0x02=ACPI, 0x03=Messaging,
                      // 0x04=Media, 0x05=BIOS, 0x7F=End
  UINT8   SubType;    // Sub-type trong cùng Type group
  UINT16  Length;     // Tổng byte của node này (bao gồm header)
} EFI_DEVICE_PATH_PROTOCOL;

// Ví dụ: PCI node (Type=0x01 Hardware, SubType=0x01)
typedef struct {
  EFI_DEVICE_PATH_PROTOCOL  Header;  // Type=1, SubType=1, Length=6
  UINT8                     Function;
  UINT8                     Device;
} PCI_DEVICE_PATH;

// Ví dụ: HD() node (Type=0x04 Media, SubType=0x01)
typedef struct {
  EFI_DEVICE_PATH_PROTOCOL  Header;  // Type=4, SubType=1, Length=42
  UINT32                    PartitionNumber;
  UINT64                    PartitionStart;   // Start LBA
  UINT64                    PartitionSize;    // Size in LBA
  UINT8                     Signature[16];   // GPT: 16-byte partition GUID; MBR: 4-byte disk signature nằm trong field này
  UINT8                     MBRType;         // 1=MBR, 2=GPT
  UINT8                     SignatureType;   // 0=None, 1=MBR, 2=GUID
} HARDDRIVE_DEVICE_PATH;

// End node - kết thúc toàn bộ Device Path
// Type=0x7F, SubType=0xFF, Length=4

Traverse và phân loại node trong code

// Hàm đọc từng node trong Device Path
VOID
PrintDevicePathNodes (
  IN EFI_DEVICE_PATH_PROTOCOL  *DevicePath
  )
{
  EFI_DEVICE_PATH_PROTOCOL  *Node;

  for (Node = DevicePath;
       !IsDevicePathEnd(Node);        // macro check End node (Type=0x7F)
       Node = NextDevicePathNode(Node) // advance theo Length
       ) {

    switch (Node->Type) {

      case HARDWARE_DEVICE_PATH:      // 0x01
        if (Node->SubType == HW_PCI_DP) {         // 0x01
          PCI_DEVICE_PATH *Pci = (PCI_DEVICE_PATH *)Node;
          // Pci->Device, Pci->Function
        }
        break;

      case ACPI_DEVICE_PATH:          // 0x02
        if (Node->SubType == ACPI_DP) {           // 0x01
          // ACPI_HID_DEVICE_PATH - PciRoot, EISAID, ...
        }
        break;

      case MESSAGING_DEVICE_PATH:     // 0x03
        if (Node->SubType == MSG_SATA_DP) {       // 0x12
          // SATA_DEVICE_PATH
        } else if (Node->SubType == MSG_NVME_NAMESPACE_DP) { // 0x17
          // NVME_NAMESPACE_DEVICE_PATH
        } else if (Node->SubType == MSG_USB_DP) { // 0x05
          // USB_DEVICE_PATH
        }
        break;

      case MEDIA_DEVICE_PATH:         // 0x04
        if (Node->SubType == MEDIA_HARDDRIVE_DP) {  // 0x01
          HARDDRIVE_DEVICE_PATH *Hd = (HARDDRIVE_DEVICE_PATH *)Node;
          if (Hd->SignatureType == SIGNATURE_TYPE_GUID) {
            // GPT: Hd->Signature là partition GUID
          } else if (Hd->SignatureType == SIGNATURE_TYPE_MBR) {
            // MBR: 4 byte disk signature
          }
        } else if (Node->SubType == MEDIA_FILEPATH_DP) { // 0x04
          FILEPATH_DEVICE_PATH *Fp = (FILEPATH_DEVICE_PATH *)Node;
          // Fp->PathName là CHAR16 string
        }
        break;

      default:
        break;
    }
  }
  // Thoát loop khi gặp End node
}

Ngoài tự parse, EDK II cung cấp DevicePathLib với các helper thường dùng:

// Convert Device Path sang text và ngược lại
CHAR16 *Text = ConvertDevicePathToText(DevicePath, FALSE, FALSE);
EFI_DEVICE_PATH_PROTOCOL *Dp = ConvertTextToDevicePath(L"PciRoot(0)/Pci(0x17,0)/...");

// Tìm node cuối cùng trước End node
EFI_DEVICE_PATH_PROTOCOL *LastNode = LastDevicePathNode(DevicePath);

// Thêm node vào Device Path (allocate buffer mới)
EFI_DEVICE_PATH_PROTOCOL *NewDp = AppendDevicePathNode(DevicePath, NewNode);

Inspect Device Path bằng UEFI Shell

# Xem Device Path của tất cả handle có DevicePath protocol
Shell> dh -d

# Dump boot option và Device Path
Shell> bcfg boot dump

# Xem Device Path text của một handle (nếu shell hỗ trợ)
Shell> devpath <handlenum>

Checklist khi đọc Device Path

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

  1. Device Path khác đường dẫn text của OS thế nào?
  2. HD() node chứa signature gì trong trường hợp GPT và MBR?
  3. Tại sao clone disk làm HD() node trong Boot#### trở nên stale?
  4. Device Path gắn với Handle Database như thế nào?
  5. Nếu LoadImage() trả EFI_NOT_FOUND, bạn sẽ debug Device Path theo thứ tự nào?
  6. End node trong Device Path có ý nghĩa 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.