题目有点绕口,简单的说目标就是:我打算用 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) ----- 常用的字符串函数(上)