题目有点绕口,简单的说目标就是:我打算用 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL 中的 OpenVolume 打开 FsX: 上面的文件怎么办?
实现的思路是:
1. 查找系统中所有支持 FS Protocol的Device
2. 对于每一个有 FS Protocol 的 Device 用 DevicePathFromHandle 取得 DevicePath
3. 再用 GetFsName 功能取得 FS0 ,FS1 这样的名称,然后判断是否为我们希望的名称
4. 如果是的话,再取得这个设备上的 SimpleFileSystem protocol
5. 最后用 OpenVolue 打开文件。
具体代码:
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>
#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include <Protocol/EfiShell.h>
#include <Library/ShellLib.h>
#include <Protocol/SimpleFileSystem.h>
#include <Protocol/BlockIo.h>
#include <Library/DevicePathLib.h>
#include <Library/HandleParsingLib.h>
#include <Library/SortLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>
extern EFI_BOOT_SERVICES *gBS;
extern EFI_SHELL_ENVIRONMENT2 *mEfiShellEnvironment2;
extern EFI_HANDLE gImageHandle;
EFI_STATUS
EFIAPI
PerformSingleMappingDisplay(
IN CONST EFI_HANDLE Handle
)
{
EFI_DEVICE_PATH_PROTOCOL *DevPath;
CHAR16 *CurrentName;
CHAR16 *FSNAME=L"fsnt1";
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;
EFI_FILE_INFO *FileInfo = NULL;
EFI_STATUS Status;
EFI_FILE_PROTOCOL *FileProtocol;
EFI_FILE_HANDLE FileHandle;
UINTN FileDataLength;
CHAR16 *FileData;
CurrentName = NULL;
DevPath = DevicePathFromHandle(Handle);
//4. Covnver DevicePath to FSx (E.x FS2, FSNT1.....)
mEfiShellEnvironment2->GetFsName(DevPath,FALSE,&CurrentName);
//5. If the "FSx" string is what we want
if (StrCmp(CurrentName,FSNAME)==0) {
Print (L"%s \r\n", CurrentName);
//6. Open the SimpleFileSystem Protocol on it
Status = gBS->OpenProtocol(
Handle,
&gEfiSimpleFileSystemProtocolGuid,
(VOID**)&SimpleFileSystem,
gImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR(Status)) {
Print (L"LocateProtocol SimpleFileSystem Error \r\n");
return (EFI_NOT_FOUND);
}
//7. Use OpenVolue to get FileProtocol
Status = SimpleFileSystem->OpenVolume(SimpleFileSystem, &FileProtocol);
if (EFI_ERROR(Status)) {
Print (L"SimpleFileSystem OpenVolume Error \r\n");
return Status;
}
//8. At last we can operate file by FileProtocol
Status = FileProtocol->Open(FileProtocol,
&FileHandle,
L"Hello.txt",
EFI_FILE_MODE_READ,
0);
if (EFI_ERROR(Status)) {
Print (L"FileProtocol Open Error [%r]\r\n",Status);
return Status;
}
FileInfo = ShellGetFileInfo( (SHELL_FILE_HANDLE)FileHandle);
Print(L"Filesize [%ld] bytes\n",FileInfo-> FileSize);
FileDataLength=(UINTN) FileInfo->FileSize;
FileData = AllocatePool((UINTN) FileInfo->FileSize);
Status = FileHandle->Read(FileHandle, &FileDataLength, FileData );
if (EFI_ERROR(Status)) {
Print(L"Loading file error! \n");
}
FileData[FileDataLength/2 -1]=0x0;
Print(L"File contants: [%s]",FileData);
FreePool(FileData);
FileProtocol->Close(FileHandle);
}
if ((CurrentName) != NULL) { FreePool((CurrentName)); CurrentName = NULL; }
return EFI_SUCCESS;
}
int
EFIAPI
main (
IN int Argc,
IN char **Argv
)
{
EFI_STATUS Status;
EFI_HANDLE *HandleBuffer=NULL;
UINTN BufferSize=0;
UINTN LoopVar;
BOOLEAN Found;
//1. We have to use some function in SE2
//
// UEFI 2.0 shell interfaces (used preferentially)
//
Status = gBS->OpenProtocol(
gImageHandle,
&gEfiShellProtocolGuid,
(VOID **)&gEfiShellProtocol,
gImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR(Status)) {
//
// Search for the shell protocol
//
Status = gBS->LocateProtocol(
&gEfiShellProtocolGuid,
NULL,
(VOID **)&gEfiShellProtocol
);
if (EFI_ERROR(Status)) {
gEfiShellProtocol = NULL;
}
}
//
//2. Look up all SimpleFileSystems in the platform
//
Status = gBS->LocateHandle(
ByProtocol,
&gEfiSimpleFileSystemProtocolGuid,
NULL,
&BufferSize,
HandleBuffer);
if (Status == EFI_BUFFER_TOO_SMALL) {
HandleBuffer = AllocateZeroPool(BufferSize);
if (HandleBuffer == NULL) {
return (SHELL_OUT_OF_RESOURCES);
}
Status = gBS->LocateHandle(
ByProtocol,
&gEfiSimpleFileSystemProtocolGuid,
NULL,
&BufferSize,
HandleBuffer);
}
//
// Get the map name(s) for each one.
//
for ( LoopVar = 0, Found = FALSE
; LoopVar < (BufferSize / sizeof(EFI_HANDLE)) && HandleBuffer != NULL
; LoopVar ++
) {
//3.Emulate every Handle which has SimpileFileSystem
Status = PerformSingleMappingDisplay(HandleBuffer[LoopVar]);
if (!EFI_ERROR(Status)) {
Found = TRUE;
}
}
FreePool(HandleBuffer);
return EFI_SUCCESS;
}
我是在虚拟机下实验的,运行之后打开并且读取 fsnt2:\hello.txt 的内容(内容是 www.lab-z.com123)。
特别注意的是:直接读取之后按照 CHAR16 的字符串来处理,但是读取内容没有 0x00 0x00的结尾。直接用Print 输出的时候字符串后面会有意料之外的字符。所以用下面这个语句 FileData[FileDataLength/2 -1]=0x0; 直接添加一个结尾。这也是为什么字符3被截掉的原因。
完整代码下载
OpenFSX
参考:
1. http://www.lab-z.com/esptest/ Step to UEFI (54) —– EFI_SIMPLE_FILE_SYSTEM_PROTOCOL 写文件
2. http://www.lab-z.com/shellfsx/ Step to UEFI (36) —– 枚举Shell下的全部盘符
3. http://www.lab-z.com/stu63/ Step to UEFI (63) —– 常用的字符串函数(下)
4. http://www.lab-z.com/nstring/ Step to UEFI (62) —– 常用的字符串函数(上)





















