Step to UEFI (163)替换已经存在Protocol中函数的实验

熟悉汇编语言的朋友都知道,DOS下面有很多INT服务,通过这样的入口能够实现硬件相关的服务。比如:INT 15 中可以支持关闭显示。与之类似,UEFI 里面是通过Protocol来实现这样的功能的。找到需要的 Protocol 即可 Call 到其提供的函数。这样的设计也给我们一个机会,可以用自己编写的函数来替换真实的函数。为此,设计了一个实验替换掉 Simple File System 中提供的 Read函数,这样每次 Application 读取到的都会是我们提供的数据。
代码比较长,从原理的角度解释具体流程:
首先,编写测试用的 Application,用 LocateHandleBuffer 枚举出来所有的 Simple File System Protocol,然后在找到的 Handle 上用 OpenVolume 打开 Root Directory。打开之后再用Open 函数打开文件,最后用 Read 来读取文件内容并且显示出来。实验中读取的是一个 44 Bytes 长的 Test.txt 文件。
实验环境是 NT32模拟器,我们只在 fs0: 上放置了 test.txt,因此,能找到2个SimpleFileSystemProtcol,第一个能够读取并且显示内容,第二个会出现Open File error 的错误。

接下来,编写我们的“驱动”,为了简单起见,这里并不是一个正经的驱动。对于我们来说,只是需要程序常驻内存,结束之后不希望被释放掉,否则会出现其他程序调用而无法找到函数的情况。因此,我们在 MdeModulePkg 中写程序,编译之后使用 Load 来加载。代码流程如下:
1. 在入口 MyEntryPoint 中查找一个 SimpleFileSystemProtocol,先保存OpenVolume 的实际入口,再用自己编写的 MySimpleFileSystemOpenVolume替换掉这个入口;
2. Application 在调用 OpenVolume 函数的时候,实际上是会进入MySimpleFileSystemOpenVolume 中。在这个函数中,使用之前保存的实际OpenVolume完成工作,然后将 OpenVolume返回的EFI_FILE_PROTOCOL替换为 MySimpleFileSystemOpen;
3. Application在调用 Open 函数的时候,实际上是会进入MySimpleFileSystemOpen。我们在这里检查参数判断要打开的是否为我们指定的 test.txt 文件,如果是,那么用 MySimpleFileSystemOpen来替换实际的 Open 函数;
4. 这样,当 Application 要Read test.txt 的时候,我们就可以送一个假的值出去。这样就实现了替换的功能。

完整的代码:
用于测试的 Application

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>
#include  <Library/BaseMemoryLib.h>
#include  <Protocol/SimpleFileSystem.h>
#include  <Library/MemoryAllocationLib.h>
                        
extern EFI_RUNTIME_SERVICES      *gRT;
extern EFI_BOOT_SERVICES         *gBS;

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{

    EFI_STATUS Status;
    EFI_HANDLE  *HandleBuffer = NULL;
    UINTN       NumHandles;
    UINTN       Index; 
    EFI_FILE_IO_INTERFACE *ioDevice; 
    EFI_FILE_HANDLE handleRoots;
    EFI_FILE_PROTOCOL *TestFile;
    CHAR8      Buffer[4*1024];
    UINTN      BufferSize;
    
    //Find all Simnple File System Protocol Handle
    Status = gBS->LocateHandleBuffer(
        ByProtocol,
        &gEfiSimpleFileSystemProtocolGuid,
        NULL,
        &NumHandles,
        &HandleBuffer);
        
    Print(L"Walk handles %ld\n", NumHandles);
    for(Index =0; Index<NumHandles; Index++){
        //Get one Simple File Protocol from a Handle    
        Status = gBS->HandleProtocol (
                        HandleBuffer[Index],
                        &gEfiSimpleFileSystemProtocolGuid,
                        (VOID **) &ioDevice
                        );
        //Opens the root directory on the volume                
        Status = ioDevice->OpenVolume( ioDevice, &handleRoots );   
        if (EFI_ERROR(Status)) {
                Print(L"OpenVolume error \n");
        }           
        //Open a file
        Status = handleRoots->Open(handleRoots,&TestFile,L"test.txt",EFI_FILE_MODE_READ, 0);
        if (!EFI_ERROR(Status)) {
                //The size of "Test.txt" is 44bytes, I use hard code here
                BufferSize=44;
                TestFile->Read(TestFile,&BufferSize,&Buffer);
                Print(L"%a",Buffer);
                TestFile->Close(TestFile);
        }
        else
        {
                Print(L"Open file error [%r]\n",Status);    
        }
    }
    
  FreePool (HandleBuffer);
  
  return EFI_SUCCESS;
}

 

简单的“驱动” 代码

#include <PiDxe.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/DebugLib.h>
#include <Library/UefiDriverEntryPoint.h>
#include <Library/MemoryAllocationLib.h>
#include <Protocol/SimpleFileSystem.h>
#include <Library/MemoryAllocationLib.h>

extern EFI_SYSTEM_TABLE         *gST;

EFI_GUID        gEfiSimpleFileSystemProtocolGuid ={ 0x964E5B22, 0x6459, 0x11D2, 
                        { 0x8E, 0x39, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }};

//Backup of Read function                        
EFI_FILE_READ OldSimpleFileSystemRead;
//Backup of Open function
EFI_FILE_OPEN OldSimpleFileSystemOpen;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_OPEN_VOLUME         OldSimpleFileSystemOpenVolume;

//This one will replace the Read function of Read in Simple File System
EFI_STATUS
EFIAPI
MySimpleFileSystemRead (
  IN     EFI_FILE_PROTOCOL  *This,
  IN OUT UINTN              *BufferSize,
  OUT    VOID               *Buffer 
)
{
     CHAR8   TestBuffer[]="Replaced buffer\n\r";
     AsciiStrCpyS(Buffer,AsciiStrnLenS(TestBuffer,255)+1,TestBuffer);
     return EFI_SUCCESS;
}

//This one will replace the Open function of Open in Simple File System
EFI_STATUS
EFIAPI
MySimpleFileSystemOpen (
  IN  EFI_FILE_PROTOCOL  *This,
  OUT EFI_FILE_PROTOCOL  **NewHandle,
  IN  CHAR16             *FileName,
  IN  UINT64             OpenMode,
  IN  UINT64             Attributes
  )
{
        EFI_STATUS Status;

        //Call into Open function in the Simple File system
        Status=(*OldSimpleFileSystemOpen)(This,NewHandle,FileName,OpenMode,Attributes);
        if (!EFI_ERROR(Status)) {
                //Check the filename to make sure it's the one we will replace
                if (StrnCmp(FileName,L"test.txt",StrLen(FileName))==0) {
                        if ((**NewHandle).Read!=MySimpleFileSystemRead) {
                                //Backup the Read Function
                                OldSimpleFileSystemRead=(**NewHandle).Read;
                                //Replace the Read Function
                                (**NewHandle).Read=MySimpleFileSystemRead;
                        }
                        
                }
        }
       return Status;
} 


EFI_STATUS
EFIAPI
MySimpleFileSystemOpenVolume (
  IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL  *This,
  OUT EFI_FILE_PROTOCOL               **Root
  )
{
        EFI_STATUS Status;
        
        Status=(*OldSimpleFileSystemOpenVolume)(This,Root);
         
        if (!EFI_ERROR(Status)) {
                if ((**Root).Open!=MySimpleFileSystemOpen) {
                        OldSimpleFileSystemOpen=(**Root).Open;
                        (**Root).Open=MySimpleFileSystemOpen;
                }        
        }
       return Status;
}  
EFI_STATUS
EFIAPI
MyEntryPoint (
  IN EFI_HANDLE           ImageHandle,
  IN EFI_SYSTEM_TABLE     *SystemTable
  )
{
        EFI_STATUS        Status;
        //EFI_FILE_PROTOCOL *Root;
        EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFile;
        
        //Look for one Simple File System Protocol 
        Status = gBS->LocateProtocol (
                        &gEfiSimpleFileSystemProtocolGuid,
                        NULL,
                        &SimpleFile);
        if (EFI_ERROR(Status)) {
           gST->ConOut->OutputString(gST->ConOut,L"Can't find Simple File PROTOCOL\n");
           return Status;
        }
        OldSimpleFileSystemOpenVolume=SimpleFile->OpenVolume;
        SimpleFile->OpenVolume=MySimpleFileSystemOpenVolume;
        
        return Status;
}

 

运行结果:

完整的代码下载:

repfun

发表评论

电子邮件地址不会被公开。 必填项已用*标注