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.
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
PciRoot(0x0)
Điểm bắt đầu từ PCI root bridge.
Pci(0x17,0x0)
Controller cụ thể trên PCI bus.
HD(1,GPT,...)
Partition chứa EFI System Partition.
File(\EFI\BOOT\BOOTX64.EFI)
EFI image sẽ được load.
Mỗi node là một struct nhị phân gồm Type, SubType và Length. 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
- Device Path khác đường dẫn text của OS thế nào?
HD()node chứa signature gì trong trường hợp GPT và MBR?- Tại sao clone disk làm
HD()node trong Boot#### trở nên stale? - Device Path gắn với Handle Database như thế nào?
- Nếu
LoadImage()trảEFI_NOT_FOUND, bạn sẽ debug Device Path theo thứ tự nào? - End node trong Device Path có ý nghĩa gì?
Bài liên quan
Nguồn tham khảo public
- UEFI Specification 2.11 - Device Path Protocol
- EDK II MdePkg - Protocol/DevicePath.h
- EDK II MdePkg - Library/DevicePathLib.h
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.
Boot####, BootOrder và BootNext là gì?
Boot#### là EFI_LOAD_OPTION trong NVRAM chứa attributes, Device Path và optional data. BootOrder và BootNext điều khiển thứ tự BDS thử boot.
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.
BDS là gì trong UEFI?
BDS là phase chọn boot option sau DXE. Firmware đọc BootOrder, connect device, load và start EFI image. Hiểu BDS giúp debug khi máy không boot đúng option.
Biến note thành bài viết hoàn chỉnh
Notes là nơi ghi nhanh khái niệm.