菜单

kaoqi
kaoqi
发布于 2026-07-31 / 6 阅读
0
0

EFI_BLOCK_IO_PROTOCOL简介

这个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.c

  • AHCI (SATA) 驱动:MdeModulePkg/Universal/Disk/AhciDxe/AhciBlkIo.c

  • SD 卡驱动:MdeModulePkg/Universal/Disk/SdDxe/SdBlockIo.cs

Protocol的实现说明:

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


评论