通常,每个UEFI系统至少有一个 ESP (EFI System Partition)分区,在这个分区上存放启动文件。EFI内置了EFI_SIMPLE_FILE_SYSTEM_PROTOCOL(简称 FileSystemIo)可以用来操作FAT文件系统【参考1】。
相关的介绍:
\MdePkg\Include\Protocol\SimpleFileSystem.h
/// /// The EFI_FILE_PROTOCOL provides file IO access to supported file systems. /// An EFI_FILE_PROTOCOL provides access to a file's or directory's contents, /// and is also a reference to a location in the directory tree of the file system /// in which the file resides. With any given file handle, other files may be opened /// relative to this file's location, yielding new file handles. /// 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; };
\MdePkg\Include\Protocol\SimpleFileSystem.h
#define EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID \ { \ 0x964e5b22, 0x6459, 0x11d2, {0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b } \ }
\EdkCompatibilityPkg\Foundation\Efi\Protocol\SimpleFileSystem\SimpleFileSystem.c
EFI_GUID gEfiSimpleFileSystemProtocolGuid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
从原理上来说,先通过这个Protocol OpenVolume,即可获得FAT文件系统上的根目录句柄。目录句柄(EFI_FILE_PROTOCOL)包含了操作该目录里文件的文件操作接口。之后我们再用 Open打开这个目录,选定需要操作的文件即可写入。
\MdePkg\Include\Protocol\SimpleFileSystem.h
/** Writes data to a file. @param This A pointer to the EFI_FILE_PROTOCOL instance that is the file handle to write data to. @param BufferSize On input, the size of the Buffer. On output, the amount of data actually written. In both cases, the size is measured in bytes. @param Buffer The buffer of data to write. @retval EFI_SUCCESS Data was written. @retval EFI_UNSUPPORTED Writes to open directory files are not supported. @retval EFI_NO_MEDIA The device has no medium. @retval EFI_DEVICE_ERROR The device reported an error. @retval EFI_DEVICE_ERROR An attempt was made to write to a deleted file. @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. @retval EFI_WRITE_PROTECTED The file or medium is write-protected. @retval EFI_ACCESS_DENIED The file was opened read only. @retval EFI_VOLUME_FULL The volume is full. **/ typedef EFI_STATUS (EFIAPI *EFI_FILE_WRITE)( IN EFI_FILE_PROTOCOL *This, IN OUT UINTN *BufferSize, IN VOID *Buffer );
完整代码:
#include <Uefi.h> #include <Library/UefiLib.h> #include <Library/ShellCEntryLib.h> #include <Protocol/EfiShell.h> #include <Library/ShellLib.h> #include <Protocol/SimpleFileSystem.h> extern EFI_BOOT_SERVICES *gBS; extern EFI_SYSTEM_TABLE *gST; int EFIAPI main ( IN int Argc, IN char **Argv ) { EFI_STATUS Status; EFI_FILE_PROTOCOL *Root; EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem; UINTN BufSize; CHAR16 *Textbuf = (CHAR16*) L"www.lab-z.com"; EFI_FILE_PROTOCOL *FileHandle=0; Status = gBS->LocateProtocol( &gEfiSimpleFileSystemProtocolGuid, NULL, (VOID **)&SimpleFileSystem); if (EFI_ERROR(Status)) { Print(L"Cannot find EFI_SIMPLE_FILE_SYSTEM_PROTOCOL \r\n"); return Status; } Status = SimpleFileSystem->OpenVolume(SimpleFileSystem,&Root); if (EFI_ERROR(Status)) { Print(L"OpenVolume error \r\n"); return Status; } Status = Root -> Open(Root, &FileHandle, (CHAR16 *) L"atest.txt", EFI_FILE_MODE_CREATE | EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, 0); if (EFI_ERROR(Status) || (FileHandle==0)) { Print(L"Open error \r\n"); return Status; } BufSize = StrLen (Textbuf) * 2; Print(L"[%d]\r\n",BufSize); Print(L"[%s]\r\n",Textbuf); Status = FileHandle -> Write(FileHandle, &BufSize, Textbuf); Print(L"Write Done \r\n"); Status = FileHandle -> Close (FileHandle); return EFI_SUCCESS; }
运行结果:

比较有意思的是,我们按照上面的程序写入之后,使用 type 文件名 来显示文件内容和我们的预期有差别,原因是Type命令默认使用 ASCII 来显内容,而我们的文件中写入的是 Unicode。使用 type -u 强制使用 Unicode即可看到我们期望的内容。
根据【参考2】的提示,我们还可以通过给TXT文件加一个头来通知当前内容格式的方法克服这个问题。
关键代码:
UINT16 TextHeader =0xFEFF;
BufSize = 2; Status = FileHandle -> Write(FileHandle, &BufSize, &TextHeader); BufSize = StrLen (Textbuf) * 2; Print(L"[%d]\r\n",BufSize); Print(L"[%s]\r\n",Textbuf); Status = FileHandle -> Write(FileHandle, &BufSize, Textbuf);
运行结果:

可以看到经过这样的处理,都可以正常显示内容了。
完整代码下载
ESPTest
参考:
1.UEFI 原理与编程 戴正华 著 P152页
2.http://www.biosren.com/thread-6541-2-1.html UEFI下使用EFI_FILE_PROTOCOL 檔案操作能多次讀寫嗎?
=============================================================
2024年9月20日
下面这个代码段能够实现将指定内存内容保存为文件,方便调试,有需要的朋友可以试试。
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Protocol/SimpleFileSystem.h>
extern EFI_HANDLE gImageHandle;
EFI_STATUS
WriteMemoryToFile(
IN EFI_HANDLE ImageHandle,
IN CHAR16 *FileName,
IN VOID *Buffer,
IN UINTN BufferSize
)
{
EFI_STATUS Status;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;
EFI_FILE_PROTOCOL *Root;
EFI_FILE_PROTOCOL *File;
EFI_HANDLE *Handles = NULL;
UINTN HandleCount = 0;
// Locate all handles that support the Simple File System Protocol
Status = gBS->LocateHandleBuffer(ByProtocol, &gEfiSimpleFileSystemProtocolGuid, NULL, &HandleCount, &Handles);
if (EFI_ERROR(Status)) {
Print(L"Failed to locate handles for Simple File System Protocol: %r\n", Status);
return Status;
}
// Open the first Simple File System Protocol
Status = gBS->HandleProtocol(Handles[0], &gEfiSimpleFileSystemProtocolGuid, (VOID **)&SimpleFileSystem);
if (EFI_ERROR(Status)) {
Print(L"Failed to open Simple File System Protocol: %r\n", Status);
return Status;
}
// Open the root directory
Status = SimpleFileSystem->OpenVolume(SimpleFileSystem, &Root);
if (EFI_ERROR(Status)) {
Print(L"Failed to open root directory: %r\n", Status);
return Status;
}
// Create or open the file
Status = Root->Open(Root, &File, FileName, EFI_FILE_MODE_CREATE | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_READ, 0);
if (EFI_ERROR(Status)) {
Print(L"Failed to open or create file: %r\n", Status);
return Status;
}
// Write the buffer to the file
Status = File->Write(File, &BufferSize, Buffer);
if (EFI_ERROR(Status)) {
Print(L"Failed to write to file: %r\n", Status);
File->Close(File);
return Status;
}
// Close the file
Status = File->Close(File);
if (EFI_ERROR(Status)) {
Print(L"Failed to close file: %r\n", Status);
return Status;
}
Print(L"Memory successfully written to file: %s\n", FileName);
return EFI_SUCCESS;
}