以下内容为学习《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都会触发。