Step to UEFI (201)直接取得函数返回地址

在64位模式下,Microsoft Visual Studio 不支持内嵌汇编。但是编译器本身支持一些intrinsic 预定义的指令,这次就研究一下_AddressOfReturnAddress。他的作用是“提供保存当前函数的返回地址的内存位置的地址。 此地址不能用于访问其他内存位置 (例如, 函数的参数)。”【参考1】

编写一个简单的Application 验证之:

#include  <Uefi.h>
#include  <Library/BaseLib.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>
#include  <Library/IoLib.h>

/**
  Microsoft Visual Studio 7.1 Function Prototypes for I/O Intrinsics.
**/

void* _AddressOfReturnAddress (VOID);

#pragma intrinsic(_AddressOfReturnAddress)

void func() {
   void* pvAddressOfReturnAddress = _AddressOfReturnAddress();
   Print(L"%lx\n", pvAddressOfReturnAddress);
   Print(L"%lx\n", *((void**) pvAddressOfReturnAddress));
   Print(L"%lx\n", _ReturnAddress());
}

/***
  Print a welcoming message.

  Establishes the main structure of the application.

  @retval  0         The application exited normally.
  @retval  Other     An error occurred.
***/
INTN
EFIAPI
ShellAppMain (
  IN UINTN Argc,
  IN CHAR16 **Argv
  )
{
        CpuBreakpoint();
        func();
        return(0);
}

运行结果如下(NT32环境)

为了便于观察我们在代码中使用 CPU BreakPoint 触发中断用 VS2015进行调试:1.触发中断

2. 从Break 中返回,call 到void func()

可以看到,当前处于call func() 的指令下,他的下一条地址是 0x1BE56C75638。和运行的结果显示一样,因此就是说可以用_AddressOfReturnAddress得到当前的函数返回的地址(就是调用语句的下一条指令)。

查看对应的汇编语句,这是通过直接将堆栈中保存的值取出来实现的。

;	COMDAT func
_TEXT	SEGMENT
pvAddressOfReturnAddress$ = 32
__$ReturnAddr$ = 56
func	PROC						; COMDAT

; 15   : void func() {

$LN3:
  00000	48 83 ec 38	 sub	 rsp, 56			; 00000038H

; 16   :    void* pvAddressOfReturnAddress = _AddressOfReturnAddress();

  00004	48 8d 44 24 38	 lea	 rax, QWORD PTR __$ReturnAddr$[rsp]
  00009	48 89 44 24 20	 mov	 QWORD PTR pvAddressOfReturnAddress$[rsp], rax

; 17   :    Print(L"%lx\n", pvAddressOfReturnAddress);

  0000e	48 8b 54 24 20	 mov	 rdx, QWORD PTR pvAddressOfReturnAddress$[rsp]
  00013	48 8d 0d 00 00
	00 00		 lea	 rcx, OFFSET FLAT:??_C@_19LKIJIFCB@?$AA?$CF?$AAl?$AAx?$AA?6?$AA?$AA@
  0001a	e8 00 00 00 00	 call	 Print

; 18   :    Print(L"%lx\n", *((void**) pvAddressOfReturnAddress));

  0001f	48 8b 44 24 20	 mov	 rax, QWORD PTR pvAddressOfReturnAddress$[rsp]
  00024	48 8b 10	 mov	 rdx, QWORD PTR [rax]
  00027	48 8d 0d 00 00
	00 00		 lea	 rcx, OFFSET FLAT:??_C@_19LKIJIFCB@?$AA?$CF?$AAl?$AAx?$AA?6?$AA?$AA@
  0002e	e8 00 00 00 00	 call	 Print

; 19   :    Print(L"%lx\n", _ReturnAddress());

  00033	48 8b 44 24 38	 mov	 rax, QWORD PTR __$ReturnAddr$[rsp]
  00038	48 8b d0	 mov	 rdx, rax
  0003b	48 8d 0d 00 00
	00 00		 lea	 rcx, OFFSET FLAT:??_C@_19LKIJIFCB@?$AA?$CF?$AAl?$AAx?$AA?6?$AA?$AA@
  00042	e8 00 00 00 00	 call	 Print

; 20   : }

  00047	48 83 c4 38	 add	 rsp, 56			; 00000038H
  0004b	c3		 ret	 0
func	ENDP

完整的代码下载:

参考:

1. https://docs.microsoft.com/zh-cn/cpp/intrinsics/addressofreturnaddress?view=vs-2017

Step to UEFI (200)按键实现音量控制的一种方法

这篇文章其实和BIOS关系不大,之所以放在这里更关键的是给大家提供一种Windows 下针对IRQ, Interrupt Vector 的分析方法。

我有一台 WHL HDK ,上面有4个实体按键,分别是 Power Button ,Volume Up, Volume Down 和Reset。从电路图上来看,Volume Up 和 Down 是直接连接进入EC的。起初我以为按下时会产生Q Event,但始终无法在 ASL 中触发对应的 Event。后来仔细琢磨:所谓“条条大路通罗马”,抓不到的原因非常可能是EC 并不是通过 Q_Event 的方式来通知的系统,很可能是通过多媒体按键的键值方式传递这个消息的。为此,进行下面的实验:

1.打开设备管理器查看一下 PS2 键盘,上面给出的 IRQ1:

2.连接好 WinDBG 后查看IRQ1 的中断号(如果出错,那么需要用 .reload 加载一下Symbols),可以看到对应的Vector 是 80:

||0:1: kd> !ioapic
Controller at 0xfffff798800537f0 I/O APIC at VA 0xfffff79880057000
IoApic @ FEC00000  ID:2 (20)  Arb:0
Inti00.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti01.: 00150000`00000080  Vec:80  FixedDel  IrtIdx:000a      edg high        
Inti02.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti03.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti04.: 00170000`00000070  Vec:70  FixedDel  IrtIdx:000b      edg high       
Inti05.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti06.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti07.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti08.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti09.: 000f0000`000080b0  Vec:B0  FixedDel  IrtIdx:0007      lvl high       
Inti0A.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti0B.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti0C.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti0D.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti0E.: 00130000`0000a0a0  Vec:A0  FixedDel  IrtIdx:0009      lvl low        
Inti0F.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti10.: 001d0000`0000a0a1  Vec:A1  FixedDel  IrtIdx:000e      lvl low        
Inti11.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti12.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti13.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti14.: 001f0000`0000a091  Vec:91  FixedDel  IrtIdx:000f      lvl low        
Inti15.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti16.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti17.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti18.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti19.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti1A.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti1B.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti1C.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti1D.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti1E.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti1F.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti20.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti21.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti22.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti23.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti24.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti25.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti26.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti27.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti28.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti29.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti2A.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti2B.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti2C.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti2D.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti2E.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti2F.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti30.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti31.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti32.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti33.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti34.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti35.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti36.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti37.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti38.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti39.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti3A.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti3B.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti3C.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti3D.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti3E.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti3F.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti40.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti41.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti42.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti43.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti44.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti45.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti46.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti47.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti48.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti49.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti4A.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti4B.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti4C.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti4D.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti4E.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti4F.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti50.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti51.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti52.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti53.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti54.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti55.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti56.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti57.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti58.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti59.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti5A.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti5B.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti5C.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti5D.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti5E.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti5F.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti60.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti61.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti62.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti63.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti64.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti65.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti66.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti67.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti68.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti69.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti6A.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti6B.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti6C.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti6D.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti6E.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti6F.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti70.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti71.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti72.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti73.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti74.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti75.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti76.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m
Inti77.: 00000000`000100ff  Vec:FF  FixedDel  Ph:00000000      edg high      m

Controller at 0xfffff79880053a38 PIC
Controller at 0xfffff79880053c60 PIC

3.接下来使用 IDT 查看0x80 这个 Vector,描述为i8042prt!I8042KeyboardInterruptService

||0:1: kd> !idt

Dumping IDT: ffff920126fb1000

00:	fffff801ff7a7d00 nt!KiDivideErrorFault
01:	fffff801ff7a8080 nt!KiDebugTrapOrFault
02:	fffff801ff7a84c0 nt!KiNmiInterrupt	Stack = 0xFFFF920126FB0200
03:	fffff801ff7a8980 nt!KiBreakpointTrap
04:	fffff801ff7a8d00 nt!KiOverflowTrap
05:	fffff801ff7a9080 nt!KiBoundFault
06:	fffff801ff7a9640 nt!KiInvalidOpcodeFault
07:	fffff801ff7a9bc0 nt!KiNpxNotAvailableFault
08:	fffff801ff7a9f00 nt!KiDoubleFaultAbort	Stack = 0xFFFF920126FAC200
09:	fffff801ff7aa240 nt!KiNpxSegmentOverrunAbort
0a:	fffff801ff7aa580 nt!KiInvalidTssFault
0b:	fffff801ff7aa8c0 nt!KiSegmentNotPresentFault
0c:	fffff801ff7aacc0 nt!KiStackFault
0d:	fffff801ff7ab080 nt!KiGeneralProtectionFault
0e:	fffff801ff7ab400 nt!KiPageFault
10:	fffff801ff7abb00 nt!KiFloatingErrorFault
11:	fffff801ff7abf00 nt!KiAlignmentFault
12:	fffff801ff7ac280 nt!KiMcheckAbort	Stack = 0xFFFF920126FAE200
13:	fffff801ff7acdc0 nt!KiXmmException
14:	fffff801ff7ad1c0 nt!KiVirtualizationException
1f:	fffff801ff7a0890 nt!KiApcInterrupt
20:	fffff801ff7a15a0 nt!KiSwInterrupt
29:	fffff801ff7ad780 nt!KiRaiseSecurityCheckFailure
2c:	fffff801ff7adb00 nt!KiRaiseAssertion
2d:	fffff801ff7ade80 nt!KiDebugServiceTrap
2f:	fffff801ff7a36d0 nt!KiDpcInterrupt
30:	fffff801ff7a0ed0 nt!KiHvInterrupt
31:	fffff801ff7a1c00 nt!KiVmbusInterrupt0
32:	fffff801ff7a22b0 nt!KiVmbusInterrupt1
33:	fffff801ff7a2960 nt!KiVmbusInterrupt2
34:	fffff801ff7a3010 nt!KiVmbusInterrupt3
35:	fffff801ff79f0d8 hal!HalpInterruptCmciService (KINTERRUPT fffff801fffcceb0)

50:	fffff801ff79f1b0 USBXHCI!Interrupter_WdfEvtInterruptIsr (KMDF) (KINTERRUPT ffff9201281edc80)

60:	fffff801ff79f230 iaStorAC+0xf4b4 (STORPORT) (KINTERRUPT ffff920127556c80)

70:	fffff801ff79f2b0 serial!SerialCIsrSw (KINTERRUPT ffff920127556b40)

80:	fffff801ff79f330 i8042prt!I8042KeyboardInterruptService (KINTERRUPT ffff920127556500)

81:	fffff801ff79f338 TeeDriverW8x64+0x129e0 (KMDF) (KINTERRUPT ffff9201281eda00)

91:	fffff801ff79f3b8 ISH+0xa840 (KMDF) (KINTERRUPT ffff9201281ed8c0)

a0:	fffff801ff79f430 msgpioclx!GpioClxEvtInterruptIsr (KMDF) (KINTERRUPT ffff920127556a00)

	                 msgpioclx!GpioClxEvtInterruptIsr (KMDF) (KINTERRUPT ffff9201275568c0)

	                 msgpioclx!GpioClxEvtInterruptIsr (KMDF) (KINTERRUPT ffff920127556780)

	                 msgpioclx!GpioClxEvtInterruptIsr (KMDF) (KINTERRUPT ffff920127556640)

	                 msgpioclx!GpioClxEvtInterruptIsr (KMDF) (KINTERRUPT ffff9201275563c0)

	                 msgpioclx!GpioClxEvtInterruptIsr (KMDF) (KINTERRUPT ffff920127556280)

	                 msgpioclx!GpioClxEvtInterruptIsr (KMDF) (KINTERRUPT ffff920127556140)

	                 msgpioclx!GpioClxEvtInterruptIsr (KMDF) (KINTERRUPT ffff920127556000)

	                 msgpioclx!GpioClxEvtInterruptIsr (KMDF) (KINTERRUPT ffff9201281eddc0)

	                 msgpioclx!GpioClxEvtInterruptIsr (KMDF) (KINTERRUPT ffff9201281edb40)

a1:	fffff801ff79f438 iaLPSS2_I2C+0x5470 (KMDF) (KINTERRUPT ffff9201281ed780)

	                 HDAudBus!HdaController::Isr (KINTERRUPT ffff9201281ed640)

b0:	fffff801ff79f4b0 ACPI!ACPIInterruptServiceRoutine (KINTERRUPT ffff920127556dc0)

b1:	fffff801ff79f4b8 dxgkrnl!DpiFdoMessageInterruptRoutine (KINTERRUPT ffff9201281ed500)

cd:	fffff801ff79f598 hal!HalpInterruptThermalService (KINTERRUPT ffffaa82d3cf3400)

ce:	fffff801ff79f5a0 hal!HalpIommuInterruptRoutine (KINTERRUPT ffffaa82d3cef100)

d1:	fffff801ff79f5b8 hal!HalpTimerClockInterrupt (KINTERRUPT ffffaa82d3cef300)

d2:	fffff801ff79f5c0 hal!HalpTimerClockIpiRoutine (KINTERRUPT ffffaa82d3cef200)

d7:	fffff801ff79f5e8 hal!HalpInterruptRebootService (KINTERRUPT fffff801fffcd1b0)

d8:	fffff801ff79f5f0 hal!HalpInterruptStubService (KINTERRUPT fffff801fffcd0b0)

df:	fffff801ff79f628 hal!HalpInterruptSpuriousService (KINTERRUPT fffff801fffccfb0)

e1:	fffff801ff7a3c60 nt!KiIpiInterrupt
e2:	fffff801ff79f640 hal!HalpInterruptLocalErrorService (KINTERRUPT fffff801fffcd2b0)

e3:	fffff801ff79f648 hal!HalpInterruptDeferredRecoveryService (KINTERRUPT ffffaa82d3cef000)

fe:	fffff801ff79f720 hal!HalpPerfInterrupt (KINTERRUPT fffff801fffcd3b0)

4.直接对其下中断

bp i8042prt!I8042KeyboardInterruptService

5. 随后按下VolumeUp或者VolumeDown即可触发这个中断

Breakpoint 0 hit
i8042prt!I8042KeyboardInterruptService:
fffff80b`da5d5e40 488bc4          mov     rax,rsp

最终证明当前的 WHL HDK 是用发PS2键盘消息的方式来实现音量的增减的。如果想修改为Q_Event 的方式,那么需要 EC 工程师配合才能实现。

Windows 命令 “where”

当我们在 cmd 窗口下输入一个命令后,首先Windows会在当前目录中查找,之后会在 Path 指定的路径下查找。我们在编译过程中经常会遇到:输入一个可执行文件,但是又不知道是从哪里执行的。特别是当系统中有很多个同名文件的时候。最近发现Windows下有一个命令能够帮助我们很快完成查找的工作,就是 Where 命令。

例如,我想知道当前 python 执行的是哪个,可以输入 where python

WHERE.exe【参考1】

Locate and display files in a directory tree.

The WHERE command is roughly equivalent to the UNIX ‘which’ command. By default, the search is done in the current directory and in the PATH.

Syntax
      WHERE [/r Dir] [/q] [/f] [/t] Pattern ...

      WHERE [/q] [/f] [/t] [$ENV:Pattern

      In PowerShell:
      C:\Windows\System32\WHERE.exe ..options as above

key
   /r      A recursive search, starting with the specified Dir directory.

   /q      Don’t display the files but return either an exit code of 0 for success
           or 1 for failure.

   /f      Display the output file name in quotation marks.

   /t      Display the size, time stamp, and date stamp of the file.

  pattern  The Drive\Directory\file, or set of files to be found.
           you can use wildcard characters ( ? * ) and UNC paths.

   ENV     Path to search where ENV is an existing environment variable containing one or more paths.

By default, WHERE searches the current directory and the paths specified in the PATH environment variable.

The WHERE command is particularly useful to reveal multiple versions of the same comand/script on the system PATH such as a Resource Kit utility – Robocopy or ForFiles.

To run the WHERE command from PowerShell it is necessary to give the full path C:\Windows\System32\WHERE.exe otherwise the Where-Object cmdlet will take precedence.

Optional search paths (in pattern) should not be used in conjunction with /r Dir.

Examples

Find all copies of robocopy.exe in the current system path:

C:\Windows\System32\WHERE robocopy.exe

Find all files named ‘Zappa’ on the remote computer ‘Server64’ searching the subdirectories of Share1:

WHERE /r \\Server64\Share1 Zappa.*

参考:

1: https://ss64.com/nt/where.html

Step to UEFI (199)如何关闭 Cache

很多年前的BIOS Setup中通常存在 Disable  Cache的选项。时至今日这个选项已经消失的无影无踪,而当你提出这个问题时,人们第一个反应是“为什么要 Disable 它”?不过最近我遇到客户提出这样的需求,只得进行一番研究。

在 “Intel® 64 and IA-32 Architectures Software Developer’s Manual”【参考1】对此有明确表述:

上文提到的操作就是设置 CR0的2个 Bits ,再用 WBINVD 清除缓冲队列,最后Disable MTRR即可,具体代码如下:

AsmDisableCache();        //这个函数自带 WBINVD指令,所以这里无需再写一次
        //
        // Disable MTRRs
        //
AsmWriteMsr64 (MSR_IA32_MTRR_DEF_TYPE, 0);

运行之后会死在Application中。无奈中只得架设起来 DCI 对代码进行调试,起初我以为是在 Disable Cache之后发生了某些异常,但是追踪发现出现问题的时候并没有停在某个死循环上,反而不停有中断进入CommonExceptionHandlerWorker () 要求处理。之后继续试验,在前面提到的函数中加入了Debug Message 然后编译成 Debug 版本的BIOS 发现在 HPET Enable之后不断有中断进入处理,直到Disable Cache之后依然如此,这样的现象着实令人费解。猜测是时间中断导致的问题,但是并没有导致异常,一切看起来仍然按部就班在运行…… 终于,晚上灵光乍现,Cache对于系统来说是透明的,当Disable Cache 之后,系统唯一的变化是处理时间变长,因此有一种可能是:中断处理来不及处理导致中断事件堵塞了CPU,这样系统实际上是活着的没有异常,只是对于用户来说是Hang了。

经过研究,UEFI 的时间中是断EFI_TIMER_ARCH_PROTOCOL 提供的,具体可以由 8254 或者 HPET 来实现。现在的平台大多数都是后者。在 \MdePkg\Include\Protocol\Timer.h 有定义一些函数可供调用:

///
/// This protocol provides the services to initialize a periodic timer
/// interrupt, and to register a handler that is called each time the timer
/// interrupt fires.  It may also provide a service to adjust the rate of the
/// periodic timer interrupt.  When a timer interrupt occurs, the handler is
/// passed the amount of time that has passed since the previous timer
/// interrupt.
///
struct _EFI_TIMER_ARCH_PROTOCOL {
  EFI_TIMER_REGISTER_HANDLER          RegisterHandler;
  EFI_TIMER_SET_TIMER_PERIOD          SetTimerPeriod;
  EFI_TIMER_GET_TIMER_PERIOD          GetTimerPeriod;
  EFI_TIMER_GENERATE_SOFT_INTERRUPT   GenerateSoftInterrupt;
};

第一个试验是直接在 Application 中使用SetTimerPeriod 关闭时间中断,运行之后 Application 可以正常退出到 fs0: 这样的盘符下,但是 Shell 已经无法工作,因此就是说没有了这个时钟源, UEFI 好比失去了心跳;接下来就是先用GetTimerPeriod 取得当前设置的时间中断发生的间隔,直接扩大十倍再用SetTimerPeriod 回写。之后 Shell 仍然可以正常工作。

最后试验进入 OS,感觉一直死机,后来挂上 DCI 不断查看 EIP 是否有变,终于确认没有发生死机,只是慢而已。耐心等待,花费80分钟进入了 OS看到了桌面。随后又测试了几次都是差不多90分钟左右才进入桌面。这样证明能够正常工作。

完整代码如下:

#include  <Uefi.h>
#include  <Library/BaseLib.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>
#include  <Library/IoLib.h>
#include <Library/CpuLib.h>
#include <Protocol/Timer.h>

#define MSR_IA32_MTRR_DEF_TYPE 0x000002FF

extern  EFI_BOOT_SERVICES   *gBS;

EFI_GUID gEfiTimerArchProtocolGuid = { 0x26BACCB3, 0x6F42, 0x11D4, 
                { 0xBC, 0xE7, 0x00, 0x80, 0xC7, 0x3C, 0x88, 0x81 }};

/***
  Print a welcoming message.

  Establishes the main structure of the application.

  @retval  0         The application exited normally.
  @retval  Other     An error occurred.
***/
INTN
EFIAPI
ShellAppMain (
  IN UINTN Argc,
  IN CHAR16 **Argv
  )
{
        EFI_TIMER_ARCH_PROTOCOL *gTimer         = NULL;
        UINT64                  t;
        
        // Get  Timer Arch Protocol
        gBS->LocateProtocol(
                        &gEfiTimerArchProtocolGuid, 
                        NULL, 
                        (VOID **)&gTimer);
        // Disable Timer
        gTimer->GetTimerPeriod (gTimer, &t);  
        gTimer->SetTimerPeriod (gTimer, 0);        
        AsmDisableCache();        
        //
        // Disable MTRRs
        //
        AsmWriteMsr64 (MSR_IA32_MTRR_DEF_TYPE, 0);
        Print(L"Cache disabled\n");

        gTimer->SetTimerPeriod (gTimer, t*10); 
        Print(L"[CR0]=%lX\n",AsmReadCr0());  
        gTimer->GetTimerPeriod (gTimer, &t); 
        Print(L"Set a new timer for Shell\n");         
        return(0);
}

下载:

参考:

1. Intel® 64 and IA-32 Architectures Software Developer’s Manual Combined Volumes:  1, 2A, 2B, 2C, 2D, 3A, 3B, 3C, 3D and 4

Step to UEFI (198)Shell 下中断和异常的处理代码

前面介绍了 IDT ,这里继续研究Shell 下是如何处理 Exception的。

同样,前面提到过,Shell下当一个中断发生之后会从 IDT 中查找Vector入口,然后跳转进去执行。

1.代码在\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\ExceptionHandlerAsm.nasm 可以看作用是在尽量短的代码中跳转到对应的处理代码

AsmIdtVectorBegin:
%rep  32
    db      0x6a        ; push  #VectorNum
    db      ($ - AsmIdtVectorBegin) / ((AsmIdtVectorEnd - AsmIdtVectorBegin) / 32) ; VectorNum
    push    rax
    mov     rax, strict qword 0 ;    mov     rax, ASM_PFX(CommonInterruptEntry)
    jmp     rax
%endrep
AsmIdtVectorEnd:

2.同一个文件中CommonInterruptEntry   函数,在堆栈中有当前的 Vector Number

;---------------------------------------;
; CommonInterruptEntry                  ;
;---------------------------------------;
; The follow algorithm is used for the common interrupt routine.
; Entry from each interrupt with a push eax and eax=interrupt number
; Stack frame would be as follows as specified in IA32 manuals:
;
; +---------------------+ <-- 16-byte aligned ensured by processor
; +    Old SS           +
; +---------------------+
; +    Old RSP          +
; +---------------------+
; +    RFlags           +
; +---------------------+
; +    CS               +
; +---------------------+
; +    RIP              +
; +---------------------+
; +    Error Code       +
; +---------------------+
; +   Vector Number     +
; +---------------------+
; +    RBP              +
; +---------------------+ <-- RBP, 16-byte aligned
; The follow algorithm is used for the common interrupt routine.
global ASM_PFX(CommonInterruptEntry)
ASM_PFX(CommonInterruptEntry):
    cli
    pop     rax
    ;
    ; All interrupt handlers are invoked through interrupt gates, so
    ; IF flag automatically cleared at the entry point
    ;
    xchg    rcx, [rsp]      ; Save rcx into stack and save vector number into rcx
    and     rcx, 0xFF
    cmp     ecx, 32         ; Intel reserved vector for exceptions?
    jae     NoErrorCode
    bt      [ASM_PFX(mErrorCodeFlag)], ecx
jc      HasErrorCode
 ……省略…….
    ;
    ; Per X64 calling convention, allocate maximum parameter stack space
    ; and make sure RSP is 16-byte aligned
    ;
    sub     rsp, 4 * 8 + 8
    call    ASM_PFX(CommonExceptionHandler)
    add     rsp, 4 * 8 + 8
……省略…….

3.对于 Shell 下,会跳转到\UefiCpuPkg\Library\CpuExceptionHandlerLib\DxeException.c 这个文件中。 所有的 Shell 下中断都会经过这里。

/**
  Common exception handler.

  @param ExceptionType  Exception type.
  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
**/
VOID
EFIAPI
CommonExceptionHandler (
  IN EFI_EXCEPTION_TYPE          ExceptionType,
  IN EFI_SYSTEM_CONTEXT          SystemContext
  )
{
  CommonExceptionHandlerWorker (ExceptionType, SystemContext, &mExceptionHandlerData);
}

4.真正工作的是 \UefiCpuPkg\Library\CpuExceptionHandlerLib\PeiDxeSmmCpuException.c

/**
  Internal worker function for common exception handler.

  @param ExceptionType         Exception type.
  @param SystemContext         Pointer to EFI_SYSTEM_CONTEXT.
  @param ExceptionHandlerData  Pointer to exception handler data.
**/
VOID
CommonExceptionHandlerWorker (
  IN EFI_EXCEPTION_TYPE          ExceptionType,
  IN EFI_SYSTEM_CONTEXT          SystemContext,
  IN EXCEPTION_HANDLER_DATA      *ExceptionHandlerData
  )
{
  EXCEPTION_HANDLER_CONTEXT      *ExceptionHandlerContext;
  RESERVED_VECTORS_DATA          *ReservedVectors;
  EFI_CPU_INTERRUPT_HANDLER      *ExternalInterruptHandler;

  ExceptionHandlerContext  = (EXCEPTION_HANDLER_CONTEXT *) (UINTN) (SystemContext.SystemContextIa32);
  ReservedVectors          = ExceptionHandlerData->ReservedVectors;
  ExternalInterruptHandler = ExceptionHandlerData->ExternalInterruptHandler;
……省略……

这里,可以使用 DEBUG宏输出调试信息,可以用串口输出看到完整的信息。

上面的流程使用 DCI 确认过,有兴趣的朋友可以同样尝试使用 DCI来观察。需要注意的是,有时候调用无法使用单步跟踪指令完成,推荐使用在要跳转到的位置下断点的方式来进行追踪。

此外,IBV 的代码可能会对文件进行 Override,就是同一个代码中出现两个同样名称的文件,两者大部分代码相同,但是会存在一些细节差别,这在追踪过程中需要特别注意。

Step to UEFI (197)EFI 文件的研究(2)

前面介绍了静态条件下的分析,下面研究一下EFI 文件加载在内存中的情况。

用一个图来说明情况:

左侧是文件,右侧是加载到内存后的情况,可以看到对于头是照搬到内存中。对于Section 的话,看起来就比较麻烦,一般情况下内存的对齐要求会比PE文件要求的大。比如:PE 中按照 16Bytes对齐,在内存中可能要求按照 64Bytes对齐,相比是因为 PE文件希望紧凑一些,内存的数据希望读取更快所以要做成这样的。

用一个GenCRC32.exe为例:

加载到内存中的是按照 0x1000对齐,数据在文件中存放是按照 0x200对齐。

继续查看 .text Section 可以看到 RVA=0x1000,意思是:当这个PE被加载到内存后,会放在 BaseAddress+0x1000的内存地址;Pointer to Raw Data 给出 0x400意思是这个段在文件中的位置是从 0x400开始的。

下面我们查看之前的 es.efi ,可以看到文件对齐和内存对齐是相同的。

再查看 Section,可以看到 RVA 和 Pointer to Raw Data 是相同的:

有兴趣的可以多查看几个EFI和 Section ,和上面是相同的。为此,我们再做一个实验,在\MdePkg\Library\UefiApplicationEntryPoint\ApplicationEntryPoint.c 加入一个中断:

EFI_STATUS
EFIAPI
_ModuleEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS                 Status;

  if (_gUefiDriverRevision != 0) {
    //
    // Make sure that the EFI/UEFI spec revision of the platform is >= EFI/UEFI spec revision of the application.
    //
    if (SystemTable->Hdr.Revision < _gUefiDriverRevision) {
      return EFI_INCOMPATIBLE_VERSION;
    }
  }
CpuBreakpoint(); //LABZ_Debug
  //
  // Call constructor for all libraries.
  //
  ProcessLibraryConstructorList (ImageHandle, SystemTable);

重新编译一次es.efi(注意不要重新编译 NT32Pkg)。执行之后报错可以用 VS2015断下来:

跳出int中断就来到我们EFI 代码的领空:

对照ApplicationEntryPoint.cod 查看停在CpuBreakpoint() 之后的语句了:

_ModuleEntryPoint PROC					; COMDAT

; 45   : {

$LN25:
  00000	48 89 5c 24 08	 mov	 QWORD PTR [rsp+8], rbx
  00005	48 89 74 24 10	 mov	 QWORD PTR [rsp+16], rsi
  0000a	57		 push	 rdi
  0000b	48 83 ec 20	 sub	 rsp, 32			; 00000020H
  0000f	48 8b fa	 mov	 rdi, rdx
  00012	48 8b f1	 mov	 rsi, rcx

; 46   :   EFI_STATUS                 Status;
; 47   : 
; 48   :   if (_gUefiDriverRevision != 0) {
; 49   :     //
; 50   :     // Make sure that the EFI/UEFI spec revision of the platform is >= EFI/UEFI spec revision of the application.
; 51   :     //
; 52   :     if (SystemTable->Hdr.Revision < _gUefiDriverRevision) {
; 53   :       return EFI_INCOMPATIBLE_VERSION;
; 54   :     }
; 55   :   }
; 56   : CpuBreakpoint(); //LABZ_Debug

  00015	e8 00 00 00 00	 call	 CpuBreakpoint

; 57   :   //
; 58   :   // Call constructor for all libraries.
; 59   :   //
; 60   :   ProcessLibraryConstructorList (ImageHandle, SystemTable);

  0001a	48 8b d7	 mov	 rdx, rdi
  0001d	48 8b ce	 mov	 rcx, rsi
  00020	e8 00 00 00 00	 call	 ProcessLibraryConstructorList

当前的位置是 0x 26BABD252DA,那么这个 .text 段是在26BABD252C0 开始的,我们还可以推理出Image 加载位置(BaseAddress)0x26BABD252C0 – 0x2c0=0x26BABD25000:

.text 段在 0x26BABD25000 + 0x2C0

.rdata段在0x26BABD25000 + 0x1700

.data段在0x26BABD25000 + 0x1F20

无名段在0x26BABD25000 +  0x1F60

.xdata段在0x26BABD25000 + 0x2020

.reloc段在0x26BABD25000 + 0x20C0

我们只验证一下 .reloc 段:

可见他们是完全相同的,就是说 EFI 文件加载到内存之后依然是相同的对齐。从设计上说,这样可以使得文件更加紧凑,能够化简加载动作,同时方便调试。

参考:

1. https://www.cnblogs.com/gd-luojialin/p/11306135.html

2019年上海创客嘉年华

2019年10月19-20日,本站会参加位于上海市杨浦区五角场市级副中心国和路 346 号江湾体育场举办的创客嘉年华活动,展位编号 M10。主要展示 Arduino 设计相关内容。

展览中出售的模块如下:

  1. USB Host Mini http://www.lab-z.com/cuhm/
  2. USB Host Shield http://www.lab-z.com/arduinousb1/
  3. Leonardo2UNO Shield http://www.lab-z.com/l2u/
  4. ProtoShield V1 http://www.lab-z.com/prototype-shield-v3/
  5. ProtoShield V3 http://www.lab-z.com/prototype-shield-v3/

欢迎新老朋友前来捧场。

Step to UEFI (196)Pause指令

很多年前我去 DELL 面试,里面的BIOS工程师问如何实现一个delay。我讲了一些使用硬件Timer的方法来实现精确的delay,他都一直摇头。最后我实在忍不住问他正确答案是什么。他的回答是 90h 也就是 NOP指令。当然,NOP是最简单的方法,但是这种方法密切和CPU速度相关的,在不同的CPU上实现的效果不同。

最近查看UEFI 发现其中实现了一个delay使用的是 PAUSE 指令看起来很有意思,于是做了一番研究。具体代码在 \MdePkg\Library\BaseLib\Ia32\CpuPause.c 和\MdePkg\Library\BaseLib\X64\CpuPause.nasm 中。殊途同归,最终都是通过pause 指令来实现。

PAUSE Spin Loop Hint

Opcode Mnemonic           Description

F3 90     PAUSE   Gives hint to processor that improves performance of spin-wait loops.

Description

Improves the performance of spin-wait loops. When executing a “spin-wait loop,” a Pentium 4 or Intel Xeon processor suffers a severe performance penalty when exiting the loop because it detects a possible memory order violation. The PAUSE instruction provides a hint to the processor that the code sequence is a spin-wait loop. The processor uses this hint to avoid the memory order violation in most situations, which greatly improves processor performance. For this reason, it is recommended that a PAUSE instruction be placed in all spin-wait loops.

An additional function of the PAUSE instruction is to reduce the power consumed by a Pentium 4 processor while executing a spin loop. The Pentium 4 processor can execute a spinwait loop extremely quickly, causing the processor to consume a lot of power while it waits for the resource it is spinning on to become available. Inserting a pause instruction in a spinwait loop greatly reduces the processor’s power consumption.

This instruction was introduced in the Pentium 4 processors, but is backward compatible with all IA-32 processors. In earlier IA-32 processors, the PAUSE instruction operates like a NOP instruction. The Pentium 4 and Intel Xeon processors implement the PAUSE instruction as a pre-defined delay. The delay is finite and can be zero for some processors. This instruction does not change the architectural state of the processor (that is, it performs essentially a delaying noop operation). 【参考1】

从上面的资料来看 Pause 的机器码和 NOP 的非常像。因此,这样可以实现兼容之前的 CPU。

上面一段话翻译如下:

PAUSE指令提升了自旋等待循环(spin-wait loop)的性能。当执行一个循环等待时,Intel P4Intel Xeon处理器会因为检测到一个可能的内存顺序违规(memory order violation)而在退出循环时使性能大幅下降。PAUSE指令给处理器提了个醒:这段代码序列是个循环等待。处理器利用这个提示可以避免在大多数情况下的内存顺序违规,这将大幅提升性能。因为这个原因,所以推荐在循环等待中使用PAUSE指令。

PAUSE的另一个功能就是降低Intel P4在执行循环等待时的耗电量。Intel P4处理器在循环等待时会执行得非常快,这将导致处理器消耗大量的电力,而在循环中插入一个PAUSE指令会大幅降低处理器的电力消耗。”【参考2】

参考:

  1. https://c9x.me/x86/html/file_module_x86_id_232.html
  2. https://blog.csdn.net/misterliwei/article/details/3951103

测试 Arduino Pro Micro 的 SPI 接口

Arduino Pro Micro Pinout

首先测试1M 速率, 测试D15(SCK). 因为先是发送0x90,然后再发送一个 0x00(也是读取),所以会送2次 SCK.

查看 MOSI 上面的信号

再测试8M,这是 32U4可以达到的最高速度(16Mhz的一半)

信号幅度是5V,因此,如果和 3.3V设备通讯,必须进行电平转换.

=============================================================

2023年2月1日

使用 USBTinyISP 烧写 Booloader的方法:

1:MISO 接 D14

2.VCC 接 VIN

3.SCK接D15

4.MOSI接D16

5.RESET接RST

6.GND接GND

之后,直接使用 Arduino IDE 自带的Bootloader功能烧录即可。