以下內容爲學習《UEFI原理與編程》(戴正華)第四章和第六章的摘要
UEFI是用C語言來實現的,但UEFI中Protocol是作爲一種對象來設計和使用。例如:
// 通過這個protocol可以控制塊設備
struct _EFI_BLOCK_IO_PROTOCOL{
UINT64 Revision;
EFI_BLOCK_IO_MEDIA *Media;
EFI_BLOCK_RESET Reset;
EFI_BLOCK_READ ReadBlocks;
EFI_BLOCK_ERITE WriteBlocks;
EFI_BLOCK_FLUSH FlushBlocks;
};
extern EFI_GUID gEfiBlockIoProtocolGuid;
///
/// Global ID for EFI_PEI_RECOVERY_BLOCK_IO_PPI
///
#define EFI_PEI_RECOVERY_BLOCK_IO_PPI_GUID \
{ \
0x695d8aa1, 0x42ee, 0x4c46, { 0x80, 0x5c, 0x6e, 0xa6, 0xbc, 0xe7, 0x99, 0xe3 } \
}
EFI_BLOCK_IO_PROTOCOL的ReadBlocks服務的函數原型
/*++
Routine Description:
Read BufferSize bytes from Lba into Buffer.
Arguments:
This - Protocol instance pointer.
MediaId - Id of the media, changes every time the media is replaced.
Lba - The starting Logical Block Address to read from
BufferSize - Size of Buffer, must be a multiple of device block size.
Buffer - Buffer containing read data
Returns:
EFI_SUCCESS - The data was read correctly from the device.
EFI_DEVICE_ERROR - The device reported an error while performing the read.
EFI_NO_MEDIA - There is no media in the device.
EFI_MEDIA_CHANGED - The MediaId does not matched the current device.
EFI_BAD_BUFFER_SIZE - The Buffer was not a multiple of the block size of the device.
EFI_INVALID_PARAMETER - The read request contains device addresses that are not valid for the device.
--*/
typedef
EFI_STATUS
(EFIAPI *EFI_BLOCK_READ) (
IN EFI_BLOCK_IO_PROTOCOL * This,
IN UINT32 MediaId,
IN EFI_LBA Lba,
IN UINTN BufferSize,
OUT VOID *Buffer
)
使用Protocol之前,要先弄清楚Protocol在UEFI內核中是怎樣表示的。首先來認識EFI_HANDLE.
typedef VOID *EFI_HANDLE;
EFI_HANDLE 是指向某種對象的指針,UEFI用它來表示某個對象。UEFI掃描總線後,會爲每個設備建立一個Controller對象,用於控制設備,所有該設備的驅動以Protocol的形式安裝到這個Controller中,這個Controller就是一個EFI_HANDLE對象。當我們加載.efi文件到內存中時,UEFI也會爲該文件建立一個Image對象,這個對象也是EFI_HANGDLE對象。在UEFI內部,EFI_HANDLE被理解爲IHANDLE. 可查看Handle.h
///
/// IHANDLE - contains a list of protocol handles
///
typedef struct {
UINTN Signature;
/// All handles list of IHANDLE
LIST_ENTRY AllHandles;
/// List of PROTOCOL_INTERFACE's for this handle
LIST_ENTRY Protocols;
UINTN LocateRequest;
/// The Handle Database Key value when this handle was last created or modified
UINT64 Key;
} IHANDLE;
所有IHANDLE通過AllHandles鏈接,而每個IHANDLE的Protocol通過Protocols鏈接。
IHANDLE的Protocols是一個雙向鏈表,鏈表中的每一個元素是PROTOCOL_INTERFACE, 通過PROTOCOL_INTERFACE的Protocol指針可以得到這個Protocol的GUID,通過Interface指針可以得到這個Protocol的實例。
///
/// PROTOCOL_INTERFACE - each protocol installed on a handle is tracked
/// with a protocol interface structure
///
typedef struct {
UINTN Signature;
/// Link on IHANDLE.Protocols
LIST_ENTRY Link;
/// Back pointer
IHANDLE *Handle;
/// Link on PROTOCOL_ENTRY.Protocols
LIST_ENTRY ByProtocol;
/// The protocol ID
PROTOCOL_ENTRY *Protocol;
/// The interface value
VOID *Interface;
/// OPEN_PROTOCOL_DATA list
LIST_ENTRY OpenList;
UINTN OpenListCount;
} PROTOCOL_INTERFACE;
///
/// PROTOCOL_ENTRY - each different protocol has 1 entry in the protocol
/// database. Each handler that supports this protocol is listed, along
/// with a list of registered notifies.
///
typedef struct {
UINTN Signature;
/// Link Entry inserted to mProtocolDatabase
LIST_ENTRY AllEntries;
/// ID of the protocol
EFI_GUID ProtocolID;
/// All protocol interfaces
LIST_ENTRY Protocols;
/// Registerd notification handlers
LIST_ENTRY Notify;
} PROTOCOL_ENTRY;
所有的Protocol均放在mProtocolDatabase指向的PROTOCOL_ENTRY鏈表中。PROTOCOL_ENTRY包含三個鏈表。
AllEntries是PROTOCOL_ENTRY鏈。
Protocols指向此Protocol的所有實例。 例如:系統中可能有很多塊設備,每個塊設備都有一個EFI_BLOCK_IO_PROTOCOL實例,所有的EFI_BLOCK_IO_PROTOCOL實例都會插入到PROTOCOL_ENTRY.Protocols鏈表中。
Notify指向PROTOCOL_NOTIFY鏈表,當PROTOCOL_ENTRY,ProtocolID對應的Protocol安裝時,Notify鏈表中所有Event都會觸發。