Windows下是不允许应用程序直接访问硬件的,必须通过驱动。类似 RW Everything这样的需要访问硬件的工具实际上是自带驱动的,当运行应用程序的时候会自动把驱动释放出去,然后通过加载驱动的方式再进行硬件的访问的。本文就介绍一下,如何在UEFI 中实现同样的功能。
我们有之前做出来的PrintDriver,用一个 Application 在编译期将它包进去,然后运行期释放到硬盘上,然后Load之,再按照Protocol的方式调用。
特别注意的地方是:我将之前的 PrintDriver.efi 用工具转换为C的字节定义,放在文件头中。用 Const 定义,保证它编译后会处于 .rdata段中。
代码如下:
#include <Uefi.h> #include <Library/UefiLib.h> #include <Library/ShellCEntryLib.h> #include <Library/ShellLib.h> #include "Print9.h" EFI_GUID gEfiPrint9ProtocolGuid = { 0xf05976ef, 0x83f1, 0x4f3d, { 0x86, 0x19, 0xf7, 0x59, 0x5d, 0x41, 0xe5, 0x61 } }; extern EFI_BOOT_SERVICES *gBS; extern EFI_SYSTEM_TABLE *gST; extern EFI_RUNTIME_SERVICES *gRT; extern EFI_HANDLE gImageHandle; const CHAR8 MyDriver[] ={ #include "Mydriver.h" }; int EFIAPI main ( IN int Argc, IN CHAR16 **Argv ) { EFI_PRINT9_PROTOCOL *Print9Protocol; CHAR16 *Buffer=L"12345678"; RETURN_STATUS Status; EFI_FILE_HANDLE FileHandle; UINTN FileSize=sizeof(MyDriver); EFI_HANDLE *HandleBuffer=(EFI_HANDLE)&MyDriver; CHAR16 *CommandLine=L"load MyDriver.efi"; EFI_STATUS CmdStat; Print(L"Length of driver = %d \n",sizeof(MyDriver)); //Create a new file Status = ShellOpenFileByName(L"MyDriver.efi", (SHELL_FILE_HANDLE *)&FileHandle, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE| EFI_FILE_MODE_CREATE, 0); if(Status != RETURN_SUCCESS) { Print(L"CreatFile failed [%r]!\n",Status); return EFI_SUCCESS; } Status = ShellWriteFile(FileHandle, &FileSize, HandleBuffer ); if(Status != RETURN_SUCCESS) { Print(L"Writefile failed [%r]!\n",Status); return EFI_SUCCESS; } Print(L"Driver has been released to the disk!\n"); //Close the source file ShellCloseFile(&FileHandle); Status = ShellExecute( &gImageHandle, CommandLine, FALSE, NULL, &CmdStat); if(Status != RETURN_SUCCESS) { Print(L"Driver load error!\n",Status); return EFI_SUCCESS; } // Search for the Print9 Protocol // Status = gBS->LocateProtocol( &gEfiPrint9ProtocolGuid, NULL, (VOID **)&Print9Protocol ); if (EFI_ERROR(Status)) { Print9Protocol = NULL; Print(L"Can't find Print9Protocol.\n"); return EFI_SUCCESS; } Print(L"Find Print9Protocol.\n"); Print9Protocol->UnicodeSPrint(Buffer,8,L"%d",200); Print(L"%s\n",Buffer); return EFI_SUCCESS; }
第一次加载失败的原因是因为当时处于 shell 下面,没有盘符,这样无法正常释放文件。第二次,在fsnt0: 下运行,驱动正常释放,可能够正常加载。所以取得了期望的结果。
最后提一下,PE格式段的问题。打开一个代码,比如之前测试驱动的Application PDT.EFI,查看编译期生成的 pdt.map :
Preferred load address is 00000000
Start Length Name Class
0001:00000000 000045e5H .text CODE
0002:00000000 0000186eH .rdata DATA
0002:00001870 0000006bH .rdata$debug DATA
0003:00000000 00000350H .data DATA
0003:00000360 00002850H .bss DATA
这些段的含义如下【参考1】:
.text 可执行代码段
数据段.bss、.rdata、.data
.rdata段表示只读的数据,比如字符串文字量、常量和调试目录信息。
.bss段表示应用程序的未初始化数据,包括所有函数或源模块中声明为static的变量。
.data段存储所有其它变量(除了出现在栈上的自动变量)。基本上,这些是应用程序或模块的全局变量。
所以我们希望,定义的数据段出现在 rdata 中,再查看我们的 pdt2.map,其中的 rdata段因为包括了我们定义的 Driver长度明显变大了。
Preferred load address is 00000000
Start Length Name Class
0001:00000000 000046c5H .text CODE
0002:00000000 00002e16H .rdata DATA
0002:00002e18 0000006eH .rdata$debug DATA
0003:00000000 00000350H .data DATA
0003:00000360 00002850H .bss DATA
完整的代码下载:
pdt2
参考:
1. http://blog.csdn.net/feidegengao/article/details/16966357 PE文件格式详解(下)
Length of driver = 5216
Driver has been released to the disk!
Image type IA32 is not supported by this X64 shell
Can’t find Print9Protocol.
一直又这样的错误,换了一个驱动也是,请问这是什么原因
应该是我里面装的 Driver 有问题,回头我修复一下。
请问把.efi文件转换为C的字节定义的工具是什么?
https://www.lab-z.com/bmpinefi/
这里面的 bin2c