Protocol trong UEFI là gì?

Protocol là interface install lên handle trong DXE, cho phép driver giao tiếp không phụ thuộc implementation. Hiểu Protocol giúp debug driver binding và locate fail.

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

Trong UEFI, Protocol là một interface được install lên handle trong handle database. Driver hoặc application khác locate protocol đó theo GUID để sử dụng service - mà không cần biết implementation bên dưới là gì.

Protocol trong DXE đóng vai trò tương tự PPI trong PEI: giúp các module tìm nhau và gọi service qua interface chuẩn, nhưng Protocol gắn với handle database và Boot Services.

Cấu trúc một Protocol

Mỗi Protocol gồm hai thứ: một GUID để định danh và một struct chứa function pointers.

// Simplified - xem Protocol/BlockIo.h để có đầy đủ function pointer typedefs
typedef struct _EFI_BLOCK_IO_PROTOCOL {
  UINT64                Revision;
  EFI_BLOCK_IO_MEDIA    *Media;
  EFI_BLOCK_RESET       Reset;
  EFI_BLOCK_READ        ReadBlocks;
  EFI_BLOCK_WRITE       WriteBlocks;
  EFI_BLOCK_FLUSH       FlushBlocks;
} EFI_BLOCK_IO_PROTOCOL;

// GUID định danh protocol này
extern EFI_GUID gEfiBlockIoProtocolGuid;

Driver install struct này lên handle. Consumer locate theo GUID rồi gọi function qua pointer - không cần biết driver nào đứng sau.

Install Protocol

InstallProtocolInterface - install một protocol lên handle:

// Tạo handle mới và install protocol lên đó
EFI_HANDLE Handle = NULL;
Status = gBS->InstallProtocolInterface(
               &Handle,                    // NULL → tạo handle mới
               &gEfiBlockIoProtocolGuid,
               EFI_NATIVE_INTERFACE,
               &mBlockIoProtocol           // con trỏ đến implementation
             );

InstallMultipleProtocolInterfaces - install nhiều protocol lên cùng một handle, thường dùng hơn vì code gọn và firmware có thể xử lý cleanup tốt hơn nếu một bước install fail:

Status = gBS->InstallMultipleProtocolInterfaces(
               &Handle,
               &gEfiBlockIoProtocolGuid,    &mBlockIoProtocol,
               &gEfiDevicePathProtocolGuid, &mDevicePath,
               NULL                         // terminate
             );

Dùng InstallMultipleProtocolInterfaces khi install nhiều protocol lên cùng một handle để code gọn hơn và để firmware xử lý cleanup tốt hơn nếu một bước install fail.

Ba cách lookup Protocol

Đây là điểm người mới hay nhầm nhất - có ba API lookup với mục đích khác nhau:

Mục Giá trị Ghi chú
LocateProtocol() Tìm protocol đầu tiên trong toàn database Không quan tâm handle nào. Dùng cho global service hoặc protocol chỉ kỳ vọng có một instance, ví dụ ConIn, ConOut.
HandleProtocol() Lấy protocol từ handle cụ thể Wrapper đơn giản của OpenProtocol. Không track ai đang dùng - dùng hạn chế.
OpenProtocol() Mở protocol với attribute rõ ràng Đúng cách cho driver. Track open count, hỗ trợ EFI_OPEN_PROTOCOL_BY_DRIVER để disconnect hoạt động đúng.

LocateProtocol - tìm nhanh không cần biết handle:

EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut;
Status = gBS->LocateProtocol(
               &gEfiSimpleTextOutProtocolGuid,
               NULL,
               (VOID **)&ConOut
             );

OpenProtocol - cách đúng cho driver, có attribute tracking:

EFI_PCI_IO_PROTOCOL *PciIo;
Status = gBS->OpenProtocol(
               ControllerHandle,
               &gEfiPciIoProtocolGuid,
               (VOID **)&PciIo,
               This->DriverBindingHandle,  // ai đang mở
               ChildHandle,                // child handle liên quan
               EFI_OPEN_PROTOCOL_BY_DRIVER // attribute
             );

EFI_OPEN_PROTOCOL_BY_DRIVER báo cho firmware biết driver đang giữ protocol này - khi Stop() được gọi, firmware biết driver nào cần cleanup.

Notify khi Protocol mới xuất hiện

Tương tự NotifyPpi trong PEI, DXE có RegisterProtocolNotify:

EFI_EVENT Event;
VOID      *Registration;  // token để dùng với LocateHandle(ByRegisterNotify, ...)

// Đăng ký callback - sẽ tự chạy khi gEfiDiskIoProtocolGuid được install
Status = gBS->CreateEvent(
               EVT_NOTIFY_SIGNAL,
               TPL_CALLBACK,
               MyDiskIoNotify,    // callback function
               NULL,
               &Event
             );

gBS->RegisterProtocolNotify(
       &gEfiDiskIoProtocolGuid,
       Event,
       &Registration
     );

Hữu ích khi driver cần react với protocol mới xuất hiện mà không biết trước thứ tự dispatch - ví dụ file system driver chờ Disk I/O Protocol. Khi không cần notify nữa, nhớ gọi gBS->CloseEvent(Event) để tránh leak.

RegisterProtocolNotify() chỉ signal event khi protocol được install. Trong callback, driver thường phải dùng LocateHandle(ByRegisterNotify, ...) với Registration để lấy các handle mới liên quan - callback không tự nhận protocol pointer.

Architectural Protocols

Một số Protocol đặc biệt gọi là Architectural Protocol - đây là các protocol nền tảng mà DXE Core hoặc các phase sau như BDS cần dựa vào để hệ thống tiếp tục boot. Nhiều architectural protocol được cung cấp bởi DXE driver chuyên trách. Nếu một protocol bắt buộc không xuất hiện, DXE/BDS có thể dừng hoặc không thể chuyển sang bước tiếp theo.

Mục Giá trị Ghi chú
EFI_CPU_ARCH_PROTOCOL CPU abstraction Interrupt enable/disable, flush cache, memory attributes.
EFI_TIMER_ARCH_PROTOCOL Hardware timer Dùng cho event-based timing trong DXE.
EFI_BDS_ARCH_PROTOCOL Boot Device Selection DXE Core gọi BDS qua protocol này khi dispatch xong.
EFI_SECURITY_ARCH_PROTOCOL Security policy Authenticate image trước khi load. Liên quan Secure Boot.
EFI_RUNTIME_ARCH_PROTOCOL Runtime service registration Quản lý virtual address mapping cho runtime driver.

Khi debug DXE Core không tiến được, kiểm tra xem các Architectural Protocol này có được install đầy đủ không.

Một ví dụ thực tế: protocol chain của storage

ControllerHandle (PCI device)
  ├─ EFI_PCI_IO_PROTOCOL          ← PCI bus driver install
  └─ EFI_DEVICE_PATH_PROTOCOL

  ChildHandle (NVMe controller)
  ├─ EFI_NVM_EXPRESS_PASS_THRU    ← NVMe driver install
  ├─ EFI_BLOCK_IO_PROTOCOL
  └─ EFI_DEVICE_PATH_PROTOCOL

    ChildHandle (Partition)
    ├─ EFI_BLOCK_IO_PROTOCOL      ← Partition driver install
    ├─ EFI_DISK_IO_PROTOCOL
    └─ EFI_DEVICE_PATH_PROTOCOL

      ChildHandle (Volume)
      ├─ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL  ← FAT driver install
      └─ EFI_DEVICE_PATH_PROTOCOL

BDS cần EFI_SIMPLE_FILE_SYSTEM_PROTOCOL để mở file .efi. Nếu bất kỳ mắt xích nào ở trên thiếu, chain sẽ bị đứt và boot menu sẽ không thấy device đó.

Checklist khi debug Protocol

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

Protocol không phải global singleton. Nhiều driver có thể install cùng một GUID lên các handle khác nhau. LocateProtocol chỉ trả về cái đầu tiên tìm thấy. Nếu cần protocol của một device cụ thể, phải tìm theo handle.

HandleProtocolOpenProtocol không giống nhau. HandleProtocol là legacy wrapper, không track open count - dùng trong driver sẽ làm disconnect/reconnect hoạt động sai.

Protocol và PPI không interchangeable. Protocol thuộc DXE, PPI thuộc PEI. Không thể locate Protocol bằng PEI service và ngược lại.

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

  1. Protocol trong UEFI gồm GUID và interface struct để làm gì?
  2. Protocol được install lên đâu trong handle database?
  3. LocateProtocol() khác OpenProtocol() ở điểm nào?
  4. Vì sao driver model nên dùng OpenProtocol() thay vì HandleProtocol()?
  5. InstallMultipleProtocolInterfaces() hữu ích khi nào?
  6. Vì sao một GUID protocol có thể xuất hiện trên nhiều handle khác nhau?
  7. Storage protocol chain cần những protocol nào để BDS thấy boot device?
  8. RegisterProtocolNotify() signal event xong, driver thường cần làm gì tiếp?

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.