这个protocol可以向你解释为什么内核能够读取磁盘扇区。
Protocol是依赖于UEFI协议诞生的技术,其主要在DXE以及BDS阶段生效,主要作用就是实现各类硬件驱动的基本实例化。
因此,Protocol先天依赖于PEI与DXE阶段所提供的Boot Services服务表,也即由UEFI固件提供给你的一套完整的用于内存分配、事件注册、驱动加载的System table中的表格。
磁盘通过调用UEFI中System table里的Boot Service接口来加载这个EFI_BLOCK_IO_PROTOCOL协议。
注意《UEFI Spec》是一份公开的开源文件:
// UEFI SPEC 的定义
struct EFI_BLOCK_IO_PROTOCOL {
uint64_t Revision;
EFI_BLOCK_IO_READ *Read;
EFI_BLOCK_IO_WRITE *Write;
EFI_BLOCK_IO_Flush *Flush;
EFI_BLOCK_IO_MEDIA *Media;
};
struct EFI_BLOCK_IO_MEDIA {
uint32_t MediaId;
bool RemovableMedia;
bool MediaPresent;
uint64_t LastBlock;
uint32_t BlockSize;
uint32_t OptimalTransferLength;
uint32_t IOAlignment;
uint8_t WriteCaching;
};这里EFI_BLOCK_IO_PROTOCOL协议由3各方法read、write、flush,以及一个具体的描述media的struct组成。
使用Block IO协议的实例:(对EFI来说,就是从BS中调用对应的protocol,并描述正确的GUID,GUID是一个两面的,你需要在protocol本身的c文件中完成编译并搭配好GUID的值,然后再efi-core的主函数中调用boot service,然后生命并引用这个GUID以找到你自己编好的协议)
// 1、定位Block IO协议
EFI_BLOCK_IO_PROTOCOL *BlockIo;
EFI_GUID block_io_guid = EFI_BLOCK_IO_PROTOCOL_GUID;
// 这里调用guid,说明之前efi的驱动里肯定已经执行过了这个protocol的install命令了
Status = BS->LocateProtocol(&block_io_guid, NULL, (void**)&BlockIo);
// 2、读取1个扇区(LBA 1 = MBR)
uint8_t buffer[512];
uint64_t LBA = 1;
uint32_t Token = 0;
Status = BlockIo->Read(
BlockIo,
BlockIo->Media->MediaId,
LBA, // 起始LBA
1, // 只读1个扇区
(uint8_t*)buffer
);
// 检查返回
if (EFI_ERROR(Status)) {
Print(L"Read error: %r\n", Status);
return;
}
// buffer[510] = 0x55, buffer[511] = 0xAA ← MBR 签名Protocol是从DXE阶段才可以使用的东西,UEFI框架下提供了函数来存取Protocol。
这个Protocol的具体实现,是由具体的硬件进行的:(以EDK2项目举例如下)
NVMe 驱动:
MdeModulePkg/Universal/Disk/NvmExpressDxe/NvmExpressBlockIo.cAHCI (SATA) 驱动:
MdeModulePkg/Universal/Disk/AhciDxe/AhciBlkIo.cSD 卡驱动:
MdeModulePkg/Universal/Disk/SdDxe/SdBlockIo.cs
Protocol的实现说明:

更进一步的有关EDK2新项目中IO PROTOCOL的实现细节有赖未来的新文章阐述