[UEFI]ROM镜像的备份与还原

ROM镜像的备份与还原

实现Setup下面BIOS的备份还原

该功能实现两个方面,备份到U盘、从U盘还原

1、备份到U盘

把rom里的数据复制到盘中
1)找到FAT32的文件系统
每个UEFI系统至少有一个ESP(EFI System Partition)分区,在这个分区上存放了启动文件。既然操作系统加载器以文件的形式存放在ESP分区内,UEFI就需要有读写文件的功能。

Status = gBS->LocateHandleBuffer(
                 ByProtocol,
                 &gEfiSimpleFileSystemProtocolGuid,
                 NULL,
                 &HandleCount,
                 &FileSystemHandles
             );

2) 通过EFI_SIMPLE_FILE_SYSYTEM_PROTOCOL中的OpenVolume,可以获得FAT文件系统上的根目录句柄,目录句柄(EFI_FILE_PROTOCOL)包含了操作该目录里文件的文件操作接口。

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_FILE_MODE_READ 文件用于读
:EFI_FILE_MODE_WRITE 文件用于写
EFI_FILE_MODE_CREATE 若文件不存在,则创建:

3)使用SpiFlashRead读BIOS中的数据,再用EFI_FILE_PROTOCOL->Write写到文件中
写文件
FileIo的Write函数

typedef
EFI_STATUS
(EFIAPI *EFI_FILE_WRITE)(
  IN EFI_FILE_PROTOCOL        *This,        //文件句柄
  IN OUT UINTN                *BufferSize,        //输入:要写入的数据长度;输出:实际写入的数据长度
  IN VOID                     *Buffer                     //待写入数据
  );

Write 只能写数据到文件,不能写数据到目录。通过,Write函数会写BufferSize指定的字节数到文件中(实际写的字节数等于指定要写的字节数),仅在遇到错误时(例如,卷上没有多余空间时)会写部分数据到文件,此时bufferSize返回实际写的字节数。
4)使用gEfiSimpleFileSystemProtocolGuid寻找FAT32 分区,如果U盘或者硬盘没有该分区,也无法备份

2、从U盘恢复镜像

从盘中读数据写到rom上
1)找到FAT32的文件系统
每个UEFI系统至少有一个ESP(EFI System Partition)分区,在这个分区上存放了启动文件。既然操作系统加载器以文件的形式存放在ESP分区内,UEFI就需要有读写文件的功能。

Status = gBS->LocateHandleBuffer(
                 ByProtocol,
                 &gEfiSimpleFileSystemProtocolGuid,
                 NULL,
                 &HandleCount,
                 &FileSystemHandles
             );

2) 通过EFI_SIMPLE_FILE_SYSYTEM_PROTOCOL中的OpenVolume,可以获得FAT文件系统上的根目录句柄,目录句柄(EFI_FILE_PROTOCOL)包含了操作该目录里文件的文件操作接口。

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;
};

3)SPI 接口可以用于连接 FLASH 芯片

struct _EFI_SPI_PROTOCOL {
  EFI_SPI_INIT      Init;
  EFI_SPI_LOCK      Lock;
  EFI_SPI_ERASE     Erase;
  EFI_SPI_PROGRAM   Program;
  EFI_SPI_READ      Read;
  EFI_SPI_EXECUTE   Execute;
  EFI_SPI_GET_INFO  GetInfo;
};

使用 SpiFlashRead 读BIOS中的数据,
使用 SpiFlashBlockErase 擦掉BIOS中的数据,
使用 SpiFlashWrite 把文件数据写入rom里

3、设备区分

如果多个FAT文件块,怎么区分备份到U盘还是SATA盘

Status = gBS->LocateHandleBuffer(
                  ByProtocol,
                  &gEfiSimpleFileSystemProtocolGuid,
                  NULL,
                  &HandleCount,
                  &FileSystemHandles
              );
for (Index = 0; Index < HandleCount; Index++){
Status = gBS->HandleProtocol (
                  FileSystemHandles[Index],
                  &gEfiBlockIoProtocolGuid,
                  (VOID **) &BlkIo 

USB:BlkIo->Media->RemovableMedia = 1
SATA:BlkIo->Media->RemovableMedia = 0