存储器相关的Protocol有BlockIo,BlockIo2,DiskIo,DiskIo2。DiskIo/DiskIo2的实现分别依赖与BlockIo/BlockIo2,其中BlockIo,DiskIo提供了访问设备的阻塞函数,BlockIo2,DiskIo2提供异步函数。。它们的区别是BlockIo只能按照扇区为单位访问存储器,而DiskIo可以按照字节为单位访问。UEFI也会为每个分区生成对应的控制器,并且根据分区属性安装对应的BlockIo/BlockIo2/DiskIo/DiskIo2实例。
除了上面的Protocol之外,UEFI还提供了文件系统的支持,通常UEFI需要提供一个ESP(EFI System Partition)分区,在这个分区上提供启动文件。这就要求UEFI具有提取文件的功能,要达到此目的,UEFI必须要支持文件系统。ESP主要用来存放启动相关的文件,对文件系统的要求比较简单,所以FAT32即可满足要求,UEFI提供了对此文件系统的支持。
本文只以DiskIo作介绍说明,其他几种Protocol的操作方法类似。
操作DiskIo,需要首先需要查找对应的控制器Handler,然后OpenProtocol打开对应的服务,接下来才可以使用DiskIo的操作函数来操作存储器。
对应的Protocol提供的操作有:
typedef struct _EFI_DISK_IO_PROTOCOL EFI_DISK_IO_PROTOCOL;
///
/// This protocol is used to abstract Block I/O interfaces.
///
struct _EFI_DISK_IO_PROTOCOL {
///
/// The revision to which the disk I/O interface adheres. All future
/// revisions must be backwards compatible. If a future version is not
/// backwards compatible, it is not the same GUID.
///
UINT64 Revision;
EFI_DISK_READ ReadDisk;
EFI_DISK_WRITE WriteDisk;
};
使用实例:
Status = gBS->OpenProtocol(HandleInfoList[0].Handle,
&gEfiDiskIoProtocolGuid,
(VOID **)&pDiskIo,
gImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (Status != EFI_SUCCESS) {
DEBUG((EFI_D_ERROR, "%a(): Failed to get open protocol from partition. Reason: %r\n", __func__, Status));
return Status;
}
Status = pDiskIo->ReadDisk(pDiskIo,
MediaId,
Offset,
DataSize,
(VOID *)Buffer);
UEFI实现了一个EFI_SIMPLE_FILE_SYSTEM_PROTOCOL用来支持FAT32文件系统。它的定义如下:
typedef struct _EFI_SIMPLE_FILE_SYSTEM_PROTOCOL EFI_SIMPLE_FILE_SYSTEM_PROTOCOL;
typedef
EFI_STATUS
(EFIAPI *EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_OPEN_VOLUME)(
IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
OUT EFI_FILE_PROTOCOL **Root //返回Root句柄
);
struct _EFI_SIMPLE_FILE_SYSTEM_PROTOCOL {
///
/// The version of the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL. The version
/// specified by this specification is 0x00010000. All future revisions
/// must be backwards compatible.
///
UINT64 Revision;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_OPEN_VOLUME OpenVolume;
};
此Protocol中只有一个关键函数OpenVolume,主要功能是用来获取对应分区Root句柄,而Root句柄实际上也是一个Protocol,其中真正包含了文件操作,如下所示:
typedef struct _EFI_FILE_PROTOCOL EFI_FILE_PROTOCOL;
typedef struct _EFI_FILE_PROTOCOL *EFI_FILE_HANDLE;
struct _EFI_FILE_PROTOCOL {
///
/// The version of the EFI_FILE_PROTOCOL interface. The version specified
/// by this specification is EFI_FILE_PROTOCOL_LATEST_REVISION.
/// Future versions are required to be backward compatible to version 1.0.
///
UINT64 Revision;
EFI_FILE_OPEN Open;
EFI_FILE_CLOSE Close;
EFI_FILE_DELETE Delete;
EFI_FILE_READ Read;
EFI_FILE_WRITE Write;
EFI_FILE_GET_POSITION GetPosition;
EFI_FILE_SET_POSITION SetPosition;
EFI_FILE_GET_INFO GetInfo;
EFI_FILE_SET_INFO SetInfo;
EFI_FILE_FLUSH Flush;
EFI_FILE_OPEN_EX OpenEx;
EFI_FILE_READ_EX ReadEx;
EFI_FILE_WRITE_EX WriteEx;
EFI_FILE_FLUSH_EX FlushEx;
};
实例:
EFI_STATUS TestOpen()
{
EFI_STATUS Status = 0;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;
EFI_FILE_PROTOCOL *Root = 0;
EFI_FILE_PROTOCOL *EfiDirectory = 0;
Status = gBS->LocateProtocol(
&gEfiSimpleFileSystemProtocolGuid,
NULL,
(VOID**)&SimpleFileSystem
);
if (EFI_ERROR(Status)) {
//未找到EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
return Status;
}
Status = SimpleFileSystem->OpenVolume(SimpleFileSystem, &Root);
// 在根目录下生成efi目录
Status = Root->Open(
Root,
&EfiDirectory,
(CHAR16*)L"efi",
EFI_FILE_MODE_CREATE | EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, //打开模式
EFI_FILE_DIRECTORY
);
//在efi目录下生成readme.txt文件
{
EFI_FILE_PROTOCOL *ReadMe = 0;
Status = EfiDirectory ->Open(
EfiDirectory, //This 指向目录\efi\
&ReadMe, //新文件的句柄
(CHAR16*)L"readme.txt", //文件全路径为\efi\readme.txt
EFI_FILE_MODE_CREATE | EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,//创建并打开
0 //生成普通文件,使用默认属性
);
Status = ReadMe -> Close(ReadMe);
}
Status = EfiDirectory->Close(EfiDirectory);
return Status;
}
参考:《UEFI 原理与编程》–戴正华