UEFI——文件操作(FileIO)

UEFI的文件系统栈如图所示,在UEFI中都含有一个ESP(EFI System Patition)分区,这个分区上存放了启动文件。UEFI就需要有读写文件的功能。文件的读写管理通过文件系统来完成,要支持读写文件,UEFI必须首先能操作ESP上的文件系统。UEFI内置了EFI_SIMPLE_FILE_SYSTEM_PROTOCOL(简称FileSystemIo)用于操作FAT文件系统

 首先可以通过两种方式获取EFI_SHELL_PROTOCOL协议接口,EFI_SHELL_PROTOCOL是UEFI规范中定义的一个协议,这个协议允许UEFI应用或者驱动程序与UEFI Shell交互,执行命令、运行其他应用程序、访问文件系统等。

步骤1:检查协议是否可用

使用OpenProtocol()打开协议并获取接口指针,其代码原型为

/**
    查询一个句柄看它是否支持指定的协议。如果Handle支持该协议,将代表调用者打开这个协议
**/

typedef
EFI_STATUS
(EFIAPI *EFI_OPEN_PROTOCOL)(
  IN  EFI_HANDLE                Handle, //正在打开协议接口的指针的句柄
  IN  EFI_GUID                  *Protocol, //协议的唯一标识符,指协议的GUID
  OUT VOID                      **Interface  OPTIONAL, //输出参数,返回指向相应协议接口的指针的地址
  IN  EFI_HANDLE                AgentHandle, //打开由protocol和interface指定的协议接口的agent的句柄。agent通常是执行某操作的实体,例如UEFI应用或驱动程序,接口是指协议的具体实现
  IN  EFI_HANDLE                ControllerHandle, //如果打开协议的agent是遵循UEFI驱动程序模型的驱动程序,则此参数是需要协议接口的控制器句柄,如果不遵循,则此参数是可选的,并且可能为NULL
  IN  UINT32                    Attributes //由句柄和协议指定的协议接口的打开模式
  );

其中协议接口的打开方式有如下几种:

#define EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL   0x00000001
#define EFI_OPEN_PROTOCOL_GET_PROTOCOL         0x00000002
#define EFI_OPEN_PROTOCOL_TEST_PROTOCOL        0x00000004
#define EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER  0x00000008
#define EFI_OPEN_PROTOCOL_BY_DRIVER            0x00000010
#define EFI_OPEN_PROTOCOL_EXCLUSIVE            0x00000020

/**
在UEFI环境中,OpenProtocol 函数用于打开一个协议接口,以便UEFI应用程序或驱动程序可以与该协议进行交互。以下是一些用于 OpenProtocol 函数的标志(Flags),它们定义了打开协议的不同方式和权限。

EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL:
值:0x00000001
描述:这个标志指示 OpenProtocol 应该通过一个句柄和一个协议GUID来打开协议。这是最常用的方式,用于打开由特定句柄支持的协议。
EFI_OPEN_PROTOCOL_GET_PROTOCOL:
值:0x00000002
描述:这个标志用于获取协议的接口指针,但不实际打开协议。它通常用于检查协议是否可用。
EFI_OPEN_PROTOCOL_TEST_PROTOCOL:
值:0x00000004
描述:这个标志用于测试一个句柄是否支持特定的协议,但不返回协议的接口指针。它用于确认协议的存在。
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER:
值:0x00000008
描述:这个标志用于通过一个控制器的子控制器来打开协议。它通常用于在控制器和其子控制器之间打开协议。
EFI_OPEN_PROTOCOL_BY_DRIVER:
值:0x00000010
描述:这个标志用于指示 OpenProtocol 应该通过一个驱动程序句柄来打开协议。这通常用于驱动程序需要打开它们管理的设备的协议。
EFI_OPEN_PROTOCOL_EXCLUSIVE:
值:0x00000020
描述:这个标志用于指示协议应该以独占方式打开。这意味着一旦协议被一个代理以独占方式打开,其他代理就不能再打开它,直到它被关闭。
**/

 

步骤二2:获取相应的接口实例

使用LocateProtocol()打开协议并获取接口指针,其代码原型为

/**
  Returns the first protocol instance that matches the given protocol.
  返回执行协议的第一个协议实例

**/
typedef
EFI_STATUS
(EFIAPI *EFI_LOCATE_PROTOCOL)(
  IN  EFI_GUID  *Protocol, //查找的协议GUID
  IN  VOID      *Registration  OPTIONAL, //从RegisterProtocolNoyify()返回的可选注册密钥
  OUT VOID      **Interface //输出参数,返回一个指针,指向与Protocol和Register匹配的第一个接口的指针。
  );

在EFI_SHELL_PROTOCOL中有很多对操作,EFI_SHELL_OPEN_FILE_BY_NAME是其中一种,目的是根据文件名打开一个文件或目录,代码原型为

 

/**
按文件名打开文件或目录,此函数在指定的OpenMode中打开指定的文件,并返回文件句柄。
    如果文件名以 '>v' 开头,则返回的文件句柄引用具有指定名称的 shell 环境变量。如果 shell 环境变量存在,是非易失性的,并且 OpenMode 指示EFI_FILE_MODE_WRITE,则返回 EFI_INVALID_PARAMETER。
    如果文件名为 '>i',则返回的文件句柄引用标准输入。如果 OpenMode 指示 EFI_FILE_MODE_WRITE,则返回 EFI_INVALID_PARAMETER。
    如果文件名为 '>o',则返回的文件句柄引用标准输出。如果 OpenMode 指示 EFI_FILE_MODE_READ,则返回 EFI_INVALID_PARAMETER。
    如果文件名为“>e”,则返回的文件句柄引用标准错误。如果 OpenMode 指示 EFI_FILE_MODE_READ,则返回 EFI_INVALID_PARAMETER。
    如果文件名为 'NUL',则返回的文件句柄引用标准 NUL 文件。如果 OpenMode 指示 EFI_FILE_MODE_READ,则返回 EFI_INVALID_PARAMETER。
    如果返回 EFI_SUCCESS,则 FileHandle 是打开文件的句柄,否则,FileHandle 为 NULL。
**/

typedef
EFI_STATUS
(EFIAPI *EFI_SHELL_OPEN_FILE_BY_NAME)(
  IN CONST CHAR16 *FileName, //指向以 NULL 结尾的 UCS-2 编码文件名。
  OUT SHELL_FILE_HANDLE *FileHandle, //输出变量,指向文件句柄
  IN UINT64 OpenMode //打开方式,有读、写、执行
  );

完整代码如下:

#include <uefi.h> 
#include <Library/UefiLib.h> 
#include <Library/ShellCEntryLib.h> 
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/UefiRuntimeLib.h>
#include <Protocol/GraphicsOutput.h>
#include <Protocol/SimplePointer.h>
#include <Protocol/SimpleTextInEx.h>
#include <Protocol/SimpleFileSystem.h>
#include <Library/DebugLib.h>

#include <Guid/GlobalVariable.h>
#include <Guid/ShellLibHiiGuid.h>

#include <Protocol/Shell.h>
#include <Protocol/ShellParameters.h>
#include <Protocol/DevicePath.h>
#include <Protocol/LoadedImage.h>
#include <Protocol/UnicodeCollation.h>

#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/PcdLib.h>
#include <Library/ShellCommandLib.h>
#include <Library/ShellLib.h>
#include <Library/UefiLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/HiiLib.h>
#include <Library/SortLib.h>
#include <Library/FileHandleLib.h>

#include <Protocol/EfiShell.h>
#include <guid/FileInfo.h>

EFI_STATUS
EFIAPI
BZero (
  OUT     CHAR16                    *Destination,
  IN      UINTN                     Length
  )
 {
	UINT32 i ;
	CHAR8 * ptr = (CHAR8 * )Destination;
	for (i = 0; i < 2*Length ; i ++){
		ptr [i] = 0;
	}
	return EFI_SUCCESS;
 }

EFI_STATUS OpenShellProtocol( EFI_SHELL_PROTOCOL            **gEfiShellProtocol )
{
    EFI_STATUS                      Status;
    Status = gBS->OpenProtocol(
            gImageHandle, //gImageHandle是一个常用的全局变量,用于存储当前正在执行的UEFI应用或驱动程序的句柄。
            &gEfiShellProtocolGuid, //协议的GUID
            (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;
        }
  }
  return Status;
}

VOID
Ascii2UnicodeString (
  CHAR8    *String,
  CHAR16   *UniString
  )
{
  while (*String != '\0') {
    *(UniString++) = (CHAR16) *(String++);
  }
  //
  // End the UniString with a NULL.
  //
  *UniString = '\0';
} 


INTN
EFIAPI
ShellAppMain( UINTN Argc, CHAR16 **Argv) //Argc是一个UINTN类型的参数,表示传递给Shell应用程序的命令行参数的数量。CHAR16 **Argv是一个指针数组,包含了实际的命令行参数字符串
{ 
  CHAR16 * OldLogFileName = NULL;
  CHAR16  *LineBuff = NULL;
  CHAR16  NewFileName[128] = {0};
  CHAR16 * ArrayBuffer  = NULL;
  EFI_STATUS Status ;
  SHELL_FILE_HANDLE FileHandle;
  UINTN Index = 0;
  UINTN WbufSize = 0;
  UINTN FileSize = 0;
  UINTN i = 0;
  UINTN StartIndex = 0;
  CHAR8 *Ptr = NULL;
  
  if (Argc <= 1){
  	Print(L"Please input file name!\n");
  	return (-1);
  }
  OldLogFileName = Argv[1];
  Print(L"The Old file name is %s!\n",OldLogFileName);
  
  EFI_SHELL_PROTOCOL            *gEfiShellProtocol;
  Status = OpenShellProtocol(&gEfiShellProtocol);
  Status = gEfiShellProtocol->OpenFileByName((CONST CHAR16*)OldLogFileName, &FileHandle, EFI_FILE_MODE_READ); //以读的方式打开OldLogFileName文件,并获取该文件的句柄FileHandle
  if (EFI_ERROR(Status)){
	  Print(L"Please Input Valid Filename!\n");
	  return (-1);
  }
  StrnCpyS(NewFileName,128,OldLogFileName,StrLen(OldLogFileName)-4);将OldLogFileName的长度-4个字节复制到大小为128的NewFileName中
  StrCatS(NewFileName,128,L"_New.txt"); //strCatS可以将两个字符串拼接在一起,将_New.txt放到NewFileName的后面
  Print(L"New FileName is %s\n",NewFileName);
  
  //删除同名的文件
  gEfiShellProtocol->DeleteFileByName(NewFileName);
  
  //获取文件实际大小
  Status = gEfiShellProtocol->GetFileSize(FileHandle,&FileSize);//获取文件的大小,并储存在FileSize中
  if (EFI_ERROR (Status)) {
    gEfiShellProtocol->CloseFile (FileHandle); //关闭文件句柄
    return Status;
  }
  Print (L"File FileSize is %d!\n",FileSize);
  
  if (FileSize < 0){
	  Print (L"File cotent is empty!\n");
	  return (-1);
  }
  
  FileSize += 1;
  
  //根据文件大小申请对应大小的内存
  Status = gBS -> AllocatePool (EfiReservedMemoryType, FileSize , &ArrayBuffer);
  Status = gBS -> AllocatePool (EfiReservedMemoryType, FileSize , &LineBuff);
  
  BZero(ArrayBuffer,FileSize);
  BZero(LineBuff,FileSize);
  Status = gEfiShellProtocol->ReadFile(FileHandle, &FileSize ,ArrayBuffer); 
  
  if (EFI_ERROR(Status)){
  	Print(L"Read Filename Error!\n");
  	return (-1);
  }
  
  //创建新的文件句柄
  Status = gEfiShellProtocol->CreateFile((CONST CHAR16*)NewFileName,0 ,&FileHandle); 
  if (EFI_ERROR(Status)){
	  Print(L"Create Filename %s Fail!\n",NewFileName);
	  return (-1);
  }
  
  //读取的文件内容写入新建文件
  WbufSize = FileSize;
  Status = gEfiShellProtocol->WriteFile(FileHandle,&WbufSize,ArrayBuffer);
  
  //关闭文件句柄
  Status = gEfiShellProtocol->CloseFile(FileHandle);
  
  Ptr = (CHAR8 * )ArrayBuffer;
  for (i = 0 ; i < FileSize ; i ++ ){
	  if (Ptr[i] == '\n'){
		Ptr[i] = '\0';  
		Ascii2UnicodeString(Ptr + StartIndex, LineBuff);
		StartIndex = i + 1;
		
		Index += 1;
		//按行输出
		Print(L"Line %d: %S!\n",Index,LineBuff);
		BZero(LineBuff,FileSize);
	  }
  }
  
  return (0);
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值