前面提到的 EFI_CPU_ARCH_PROTOCOL ,这次试试这个Protocol RegisterInterrupt,注册一个我们自己的中断试试。
为了完成这个目标,我们设计下面的方案:
1. 编写一个驱动,在这个驱动里面注册我们自己的 Interrupt,我使用的是 0x41 作为中断向量(经过检查,实际机器上使用了0x40作为给HPET Timer的Interrupt,这里我们选择一个不冲突的即可)。方便起见,选择使用之前我们设计的 Print9 Protocol 的代码,这个代码会在系统中注册我们自己定义的 Protocol,然后这个 Protocol 中留出来一个输出变量(Var2)的函数,以便调用。我们的InterruptHandler也在这个文件中,内容很简单,就是将 Var2 加一;
2. 编写一个 Application 来在系统中查找上面驱动注册的Protocol,找到之后调用输出变量的函数,这样我们可以知道这个函数是否有变化;
3. 最后再编写一个 Application 用来产生 int 0x41。
首先是第一个的代码,这个需要放置在 MdeModule 中编译而不是我们经常用的 AppPkg中,插入在 MdeModulePkg.dsc 文件中
MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.inf MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.inf #LabZDebug_Start MdeModulePkg/PrintDriver3/PrintDriver3.inf #LabZDebug_End [Components.IA32, Components.X64, Components.IPF, Components.AARCH64] MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxeBcDxe.inf MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxe.inf
代码如下:
/** @file This driver produces Print9 protocol layered on top of the PrintLib from the MdePkg. Copyright (c) 2009, Intel Corporation. All rights reserved.<BR> This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at http://opensource.org/licenses/bsd-license.php THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ #include <PiDxe.h> #include <Library/UefiLib.h> #include <Library/PrintLib.h> #include <Library/UefiBootServicesTableLib.h> #include <Library/DebugLib.h> #include <Library/UefiDriverEntryPoint.h> #include <Library/MemoryAllocationLib.h> #include "Print9.h" #include <Protocol/Cpu.h> #include <Library/CpuLib.h> extern EFI_SYSTEM_TABLE *gST; EFI_GUID gEfiPrint9ProtocolGuid = { 0xf05976ef, 0x83f1, 0x4f3d, { 0x86, 0x19, 0xf7, 0x59,0x5d, 0x41, 0xe5, 0x61 } }; EFI_GUID gEfiCpuArchProtocolGuid = { 0x26BACCB1, 0x6F42, 0x11D4, { 0xBC, 0xE7, 0x00, 0x80, 0xC7, 0x3C, 0x88, 0x81 }}; EFI_PRINT9_PRIVATE_DATA *Image; EFI_HANDLE mPrintThunkHandle = NULL; //Copied from \MdeModulePkg\Library\DxePrintLibPrint2Protocol\PrintLib.c UINTN EFIAPI MyPrint () { CHAR16 *Buffer=L"1 2 3 4 5 6 7 8 9 0 A B C E D F "; UnicodeSPrint(Buffer,16,L"%d\r\n",Image->Var2); gST->ConOut->OutputString(gST->ConOut,Buffer); return 0; } VOID EFIAPI MyInterruptHandler ( IN EFI_EXCEPTION_TYPE InterruptType, IN EFI_SYSTEM_CONTEXT SystemContext ) { Image->Var2++; } /** The user Entry Point for Print module. This is the entry point for Print DXE Driver. It installs the Print2 Protocol. @param[in] ImageHandle The firmware allocated handle for the EFI image. @param[in] SystemTable A pointer to the EFI System Table. @retval EFI_SUCCESS The entry point is executed successfully. @retval Others Some error occurs when executing this entry point. **/ EFI_STATUS EFIAPI PrintEntryPoint ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; EFI_CPU_ARCH_PROTOCOL *Cpu; // // Allocate a new image structure // Image = AllocateZeroPool (sizeof(EFI_PRINT9_PRIVATE_DATA)); if (Image == NULL) { Status = EFI_OUT_OF_RESOURCES; ASSERT_EFI_ERROR (Status); } Image->Signature = PRINT9_PRIVATE_DATA_SIGNATURE; Image->PRINT9.UnicodeBSPrint=UnicodeBSPrint; Image->PRINT9.UnicodeSPrint=UnicodeSPrint; Image->PRINT9.UnicodeBSPrintAsciiFormat=UnicodeBSPrintAsciiFormat; Image->PRINT9.UnicodeSPrintAsciiFormat=MyPrint; //Image->PRINT9.UnicodeValueToString=UnicodeValueToString; Image->PRINT9.AsciiBSPrint=AsciiBSPrint; Image->PRINT9.AsciiSPrint=AsciiSPrint; Image->PRINT9.AsciiBSPrintUnicodeFormat=AsciiBSPrintUnicodeFormat; Image->PRINT9.AsciiSPrintUnicodeFormat=AsciiSPrintUnicodeFormat; //Image->PRINT9.AsciiValueToString=AsciiValueToString; Image->Var2=1984; Status = gBS->InstallMultipleProtocolInterfaces ( &mPrintThunkHandle, &gEfiPrint9ProtocolGuid, &Image->PRINT9, NULL ); ASSERT_EFI_ERROR (Status); // // Locate the Cpu Arch Protocol. // Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, &Cpu); ASSERT_EFI_ERROR (Status); Status = Cpu->RegisterInterruptHandler (Cpu, 0x41, MyInterruptHandler); ASSERT_EFI_ERROR (Status); return Status; }
上述代码有两个需要特别注意的地方:
1. 之前的 Print9代码是在 UDK2014中编译通过的,但是在 UDK2017中无法编译通过,根本原因是 UDK2017中因为安全原因删除了 UnicodeValueToString 和AsciiValueToString两个函数,对我们来说,在代码中注视掉这两个函数不使用即可;
2. MyPrint 是我们输出函数,他会输出 Image->Var2 的值;
3. MyInterruptHandler 是我们的中断函数,里面只是简单的对 Image->Var2 加一。
第二个代码,在系统中查找我们自定义的 Print9Protocol,相对来说简单多了:
#include <Uefi.h> #include <Library/UefiLib.h> #include <Library/ShellCEntryLib.h> #include "Print9.h" EFI_GUID gEfiPrint9ProtocolGuid = { 0xf05976ef, 0x83f1, 0x4f3d, { 0x86, 0x19, 0xf7, 0x59, 0x5d, 0x41, 0xe5, 0x61 } }; extern EFI_BOOT_SERVICES *gBS; int EFIAPI main ( IN int Argc, IN CHAR16 **Argv ) { EFI_PRINT9_PROTOCOL *Print9Protocol; EFI_STATUS Status; // Search for the Print9 Protocol Status = gBS->LocateProtocol( &gEfiPrint9ProtocolGuid, NULL, (VOID **)&Print9Protocol ); if (EFI_ERROR(Status)) { Print(L"Can't find Print9Protocol.\n"); return EFI_SUCCESS; } Print(L"Find Print9Protocol.\n"); Print9Protocol->UnicodeSPrintAsciiFormat(); Print(L"\n"); return EFI_SUCCESS; }
第三个代码,发出 Int 0x41 中断。起初我打算使用 int al 这样的指令,后来查了一下手册,原来 int 后面只能接立即数,于是直接写成 int 0x41 。因为,Vistual Studio 的 X64无法使用内嵌汇编,我们只好单独写一个 asm 出来。
IntX.c:
/** @file Simple interrupt test. Copyright (c) 2013, Intel Corporation. All rights reserved.<BR> This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at http://opensource.org/licenses/bsd-license.php THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ #include <Uefi.h> #include <Library/BaseLib.h> #include <Library/UefiLib.h> #include "IntX.h" /** The user Entry Point for Application. The user code starts with this function as the real entry point for the application. @param[in] ImageHandle The firmware allocated handle for the EFI image. @param[in] SystemTable A pointer to the EFI System Table. @retval EFI_SUCCESS The entry point is executed successfully. @retval other Some error occurs when executing this entry point. **/ EFI_STATUS EFIAPI UefiMain ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { Print (L"Generate a Interrupt\n"); SimpleInterrupt(); return EFI_SUCCESS; }
IntDemo.inf:
## @file # A simple, basic, application showing how the Hello application could be # built using the "Standard C Libraries" from StdLib. # # Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR> # This program and the accompanying materials # are licensed and made available under the terms and conditions of the BSD License # which accompanies this distribution. The full text of the license may be found at # http://opensource.org/licenses/bsd-license. # # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. ## [Defines] INF_VERSION = 0x00010006 BASE_NAME = IntDemo FILE_GUID = 4ea97c01-7491-4dfd-0090-747010f3ce5f MODULE_TYPE = UEFI_APPLICATION VERSION_STRING = 0.1 ENTRY_POINT = UefiMain # # VALID_ARCHITECTURES = X64 # [Sources.common] IntX.c IntX.h [Sources.IA32] [Sources.X64] X64/AsmInt.asm [Packages] MdePkg/MdePkg.dec ShellPkg/ShellPkg.dec [LibraryClasses] UefiApplicationEntryPoint UefiLib
用来定义 Int X 的源代码 \x64\AsmInt.asm
;------------------------------------------------------------------------------ ; ; Copyright (c) 2013, Intel Corporation. All rights reserved.<BR> ; This program and the accompanying materials ; are licensed and made available under the terms and conditions of the BSD License ; which accompanies this distribution. The full text of the license may be found at ; http://opensource.org/licenses/bsd-license.php. ; ; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, ; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. ; ; Module Name: ; ; AsmRdRand.Asm ; ; Abstract: ; ; Generate an Interrupt by INT x ; ; Notes: ; ; Visual Studio coding practices do not use inline asm since multiple compilers and ; architectures are supported assembler not recognizing rdrand instruction so using DB's. ; ;------------------------------------------------------------------------------ .code ;------------------------------------------------------------------------------ ; Generates a interrupt by Int(CD). ;------------------------------------------------------------------------------ SimpleInterrupt PROC int 0x41 ret SimpleInterrupt ENDP END
最终运行结果,可以看到每次运行 IntDemo之后,会有中断触发,数值不断变大。
完整的代码和编译后的 efi 文件下载(我只在X64 上测试过,NT32模拟环境不支持)
IntTest