UEFI Tips:UDK2017 fopen 失效了

最近在 Porting 一段代码的时候有使用到 fread(),但是非常奇怪的是一直无法正常工作,得到的错误返回值一直为0。后来在网上找到Robins_Lee 的文章提到了这个问题https://blog.csdn.net/mini92/article/details/79274823。原来新的EDK2中,需要在INF [LibraryClasses] 中加入 DevShell 才行。
对于这个要求在 \StdLib\ReadMe.txt 有描述如下:

Figure 5: Package Build Options
===============================

INF Files
=========
The INF files for most modules will not require special directives in order to
support the Standard Libraries. The two sections which require attention: LibraryClasses
and BuildOptions, are described below.

[LibraryClasses]
UefiLib
LibC
LibString
LibStdio
DevShell

Figure 6: Module Library Classes
================================

Modules of type UEFI_APPLICATION that perform file I/O must include library
class DevShell. Including this library class will allow file operations to be
handled by the UEFI Shell. Without this class, only Console I/O is supported.

An application’s INF file might need to include a [BuildOptions] section
specifying additional compiler and linker flags necessary to allow the
application to be built. Usually, this section is not needed. When building
code from external sources, though, it may be necessary to disable some
warnings or enable/disable some compiler features.

[BuildOptions]
INTEL:*_*_*_CC_FLAGS = /Qdiag-disable:181,186
MSFT:*_*_*_CC_FLAGS = /Oi- /wd4018 /wd4131
GCC:*_*_IPF_SYMRENAME_FLAGS = –redefine-syms=Rename.txt

编写测试代码:

#include <uefi.h>
#include <Library/UefiLib.h>

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include <errno.h>
#include<wchar.h>

int
main (
  IN int Argc,
  IN char *Argv[]
  )
{
	FILE *fd;
	char text[100];
        
	if (Argc<2) {
		printf("Please input filename\n");
		exit(1);
	}
	if ((fd = fopen(Argv[1], "rb")) == NULL)
	{
		printf("open file failed. %s\n", strerror(errno));
		exit(1);
	}
	memset(text,0,sizeof(text));
	fread(text, sizeof(char), 100, fd);
	printf("%s\n", text);
	fclose(fd);

	return 0;
}

 

[Defines]
  INF_VERSION                    = 0x00010006
  BASE_NAME                      = fot
  FILE_GUID                      = 4ea97c46-0995-4dfd-b442-747010f3ce5f
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 0.1
  ENTRY_POINT                    = ShellCEntryLib

#
#  VALID_ARCHITECTURES           = IA32 X64
#

[Sources]
  fopentest.c

[Packages]
  StdLib/StdLib.dec
  MdePkg/MdePkg.dec
  ShellPkg/ShellPkg.dec

[LibraryClasses]
  LibC
  LibStdio
  UefiLib
  DevShell

 

运行结果:

完整代码:
fopentest

2018 Maker Carnival Shanghai

2018年10月13日,本站会参加位于上海市淞沪路388号创智天地广场7号楼1楼举办的创客嘉年华活动,展位编号 A34。主要展示 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/

欢迎新老朋友前来捧场。

ProtoType Shield V3

很多时候,我们需要制作自己的 Shield板,这时候可以选用 Prototype Shield,可以理解为就是形状为Shield 的洞洞板。

这样做的好处是:方便使用,即插即用;成本比较低。

淘宝上也有做好的这样的 Shield 板, 价格为 6元

相比之下,我的设计的好处是:可以使用排针+排母的方式进行扩展。这样就避免购买长脚排母的麻烦。同时Taobao上找不到足够硬的排母,这意味着安装好的排母经不起几次插拔。

下面是焊接好的样子:

附件是 Eagle版的电路图。
zProtoShieldv1.1

此外,存在的问题:

1.在 leonrado上用力下压会顶到9V电源,没有短路之虞但是对于追求完美的人可能无法忍受;
2.在Uno 上,左上角会接触到 USB口,可能会短路,在使用的时候我都是贴上胶带的.

祖国版 USB HOST MINI 使用

第一, 我们淘宝上能买到的,不是原本的设计,有一些修改,这样的修改会导致USB设备的供电问题。下面是原版的设计,在绿色圈标记的位置,有一个焊接的跳线还有一个排针的位置。当焊接跳线接起来的时候,USB设备收到的是3.3V的电压(我不清楚有什么设备可以在3.3V供电的情况下工作,所以这个设计让我觉得莫名其妙)。

下面是我们能买到的祖国版

可以看出标记的位置是有差别的。对于我们来说,需要切开上面的连接,然后在排针上送入5V即可正常驱动 USB 设备工作。

第二, 模块有一个 USB Host Reset Pin,在正常使用的时候需要设置为High。为了简单起见,可以将这个 Pin 直接连接到3.3v。

实做:读取Audio Codec 的 VID/PID

最近在研究 Audio ,目前使用最广泛的是 Intel 的 HD Audio,和之前的 AC97 相比复杂多了,网上也少有关于如何驱动这个 Audio 的文章,看起来很痛苦。因为音频的数据量通常很大,所以通讯方面都是以 DMA 为主。好在除此之外设计上还提供了一套简单发送命令的机制。下面就是我在 Kabylake-R 的平台上实验直接发送 command 读取 codec 的 VID 和 PID的实验。

1. 找到 audio pci 设备,在 KBL-R 上是 D32:F2

我们需要的 Memory Mapped I/O 在 Offset 10 和 14 (一共是8 Bytes)

但是这时,对应的内存位置全都为0xFF:

2. 对 PCI_COMMAND_OFFSET(0x04)发送 EFI_PCI_COMMAND_MEMORY_SPACE (0x02) command:

3. 再打开 Memory Mapped I/O Register,在 0x2F FB43 0000,可以正常工作;

4. 根据 HD Audio Spec, ICW(0x60) IR(0x64) ICS(68h)。在 ICW 中写入NID=0,Verb ID=0xF00,Parameter ID=0x0的Verb Command

然后在ICS(68h) 写入 0x3

5. 很快,我们就能在IR(0x64) 看到返回值。这里我们用的是 Realtek 的 codec ,所以给出的VID 是 Realtek的,也证明方法的正确性。

如果细心观察BIOS中使用的 Verb Table 会发现在头部有一个 VID 和 PID,每次在Load 这个 Table 之前是有一个上述获取然后比较的动作的,因此,不用担心型号上的错误。比如:Realtek 298 和 798 的 PID 不同,如果使用错误会通过 Debug 口输出信息。

USB条码枪的显示

之前有写过 USB条码枪改直显和蓝牙,https://www.arduino.cn/forum.php?mod=viewthread&tid=23635&fromuid=36850

最近看到有些朋友在问,觉得有些奇怪,于是又买了一个条码枪重新实验。很快发现之前的代码无法在新的条码枪上使用。原因如下:

1.USB 条码枪 USB 数据结构有变化。换句话说,之前的那个条码枪更像是单纯的键盘,可以响应 Boot Protocol,但是新的不行。
2. USB HOST Shield 库有变化,会导致编译不过。

于是,重新实验编写了如下的代码:

barcs.ino

#include <SPI.h>
#include "bcsParser.h"
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x3F,16,2);  

USB Usb;
MSPARSER msparser(&Usb);

bool printTilt;

void setup() {
  Serial.begin(115200);
  lcd.init();
  
  if (Usb.Init() == -1) {
    Serial.print(F("\r\nOSC did not start"));
    while (1); // Halt
  }
  Serial.println(F("Barcode scaner\n\r"));
  // Print a message to the LCD.
  lcd.backlight();
  lcd.setCursor(0,0);
  lcd.print("Barcode scaner");
}

void loop() {
  Usb.Task();

  if (msparser.connected()) {
    
    }

}

 

bcsParser.cpp

#include "bcsParser.h"
#include <Mouse.h>
#include <LiquidCrystal_I2C.h>

extern LiquidCrystal_I2C lcd;
#define VALUE_WITHIN(v,l,h) (((v)>=(l)) && ((v)<=(h)))

uint8_t OemToAscii(uint8_t key) {
        
        // [1-9]
        if (VALUE_WITHIN(key, 0x1e, 0x26)) {
                        return (key -0x1e +1 +'0');
        }// Numbers
        //[0]
        if (key == 0x27) {return '0';}

        if (key == 0x28) {return 13;}
        // [1-9]
        if (VALUE_WITHIN(key, 0x04, 0x1D)) {
                        return (key - 0x04+'A');
        }// Alpha        

        return (0x00);
}

//解析USB鼠标的数据
void MSPARSER::ParseHIDData(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) {
        if (HIDUniversal::VID != STEELSERIES_VID || HIDUniversal::PID != STEELSERIES_SRWS1_PID) 
                return;

        if (len && buf)  {
          /*
                //输出收到的数据
                for (uint8_t i = 0; i < len; i++) {
                        if (buf[i]<0x10) {Serial.print("0");}
                        Serial.print(buf[i],HEX);
                        Serial.print(" ");
                }
                Serial.println();
          */
          
          char c=(OemToAscii(buf[2])&0xFF);
          if (c==13) {
            Serial.println(" "); 
              lcd.setCursor(0,1);
              lcd.print(s);
              Serial.print(s);
              for (int i=0;i<16-s.length();i++) {
                    Serial.print(" ");
                }
              s="";

            }
          else
          if (c!=0x00) {
                //输出收到的数据
                //Serial.print(c); 
                s=s+c;
                }
                 
              
          
        }
}

 

bcsParser.h

#ifndef __srws1_h__
#define __srws1_h__

#include <hiduniversal.h>

#define STEELSERIES_VID       0xFFFF
#define STEELSERIES_SRWS1_PID 0x0035

class MSPARSER : public HIDUniversal {
public:
        MSPARSER(USB *p) : HIDUniversal(p) {};
        bool connected() {
                return HIDUniversal::isReady() && HIDUniversal::VID == STEELSERIES_VID && HIDUniversal::PID == STEELSERIES_SRWS1_PID;
        };

private:
        void ParseHIDData(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf); // Called by the HIDUniversal library
        uint8_t OnInitSuccessful() { // Called by the HIDUniversal library on success
                if (HIDUniversal::VID != STEELSERIES_VID || HIDUniversal::PID != STEELSERIES_SRWS1_PID) // Make sure the right device is actually connected
                        return 1;
                return 0;
        };
        String s;
};

#endif

 

硬件上是Uno + USB Host Shield 插在一起就可以了。

照片上还有一个充放电管理板和功能没关系,普通用户可以直接使用充电宝之类给Uno供电。

完整代码下载

barcs

UEFI TIps:格式化GetLastError 结果的 FormatMessage

通常我们使用 GetLastError 来获得API 的错误代码,在取得之后还需要查表。其实可以直接使用FormatMessage 这个 API ,将错误代码转为错误的信息输出。

本文代码来自 https://www.cnblogs.com/passedbylove/p/6088096.html

static void
win32perror(const TCHAR *s)
{
	LPVOID buf;
	if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS
		| FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_MAX_WIDTH_MASK,
		NULL,
		GetLastError(),
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
		(LPTSTR)&buf,
		0,
		NULL)) {
		_ftprintf(stderr, _T("%s: %s"), s, buf);
		fflush(stderr);
		LocalFree(buf);
	}
	else
		_ftprintf(stderr, _T("%s: unknown Windows error\n"), s);
}

 

在使用的地方申明 TCHAR *wSZError = _T(“error message \n”);

调用上面的函数 win32perror(wSZError); 即可.

微软也提供了一个类似的例子 https://docs.microsoft.com/en-us/windows/desktop/Debug/retrieving-the-last-error-code

void ErrorExit(LPTSTR lpszFunction)
{
	// Retrieve the system error message for the last-error code

	LPVOID lpMsgBuf;
	LPVOID lpDisplayBuf;
	DWORD dw = GetLastError();

	FormatMessage(
		FORMAT_MESSAGE_ALLOCATE_BUFFER |
		FORMAT_MESSAGE_FROM_SYSTEM |
		FORMAT_MESSAGE_IGNORE_INSERTS,
		NULL,
		dw,
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
		(LPTSTR) &lpMsgBuf,
		0, NULL);

	// Display the error message and exit the process

	lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
		(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
	StringCchPrintf((LPTSTR)lpDisplayBuf,
		LocalSize(lpDisplayBuf) / sizeof(TCHAR),
		TEXT("%s failed with error %d: %s"),
		lpszFunction, dw, lpMsgBuf);
	//MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);

	LocalFree(lpMsgBuf);
	LocalFree(lpDisplayBuf);
	ExitProcess(dw);
}

 

Step to UEFI (156)UEFI 的表达式计算库

在软件工程领域,有一句著名的话,叫做“Don’t Reinvent the Wheel”—–不要重复发明轮子。究其原因一方面是重复发明效率低下,另一方面是重新发明的轮子未必好用,也许发明之后发现轴承有问题,或者是有着各种瑕疵…….对于编程来说,“复用代码”有着更明确的好处。因此,如果有可能,我们希望更多秉承鲁迅先生提出的“拿来主义”在代码设计上。最近研究了一些C语言的库,得益于UEFI 的设计和 CLIB 的支持,大部分库都可以直接使用。今天介绍的是一个可以用于表达式计算的库:TinyExpr【参考1】。

首先是要将 TinyExpr Porting 到UEFI上,直接编译会出现一些 Error 和 Warning。经过研究,需要在 INF中加入下面的内容:
1.引入LibMath,否则一些 cos 之类的函数无法识别

[LibraryClasses]
  ShellCEntryLib
  UefiLib
  LibC
  LibStdio
  LibMath

 

2.关闭一些 Warning

[BuildOptions]
   MSFT:*_*_IA32_CC_FLAGS         = /Ze /wd4201 /wd4152 /wd4090 /wd4204 /wd4055 /wd4244
   MSFT:*_*_X64_CC_FLAGS          = /Ze /wd4201 /wd4152 /wd4090 /wd4204 /wd4055 /wd4244

 

其中 C4201 Warning【参考2】,是 VS 编译器的扩展特性,比如下面这样的定义,在正经的 C 中是不允许的,但是 VC 中做了扩展是可以的:

struct S  
{  
   float y;  
   struct  
   {  
      int a, b, c;  // C4201  
   };  
} z;

 

这样扩展之后,可以直接使用 z.a 和 z.b。

此外,tinyexpr.c 中有关于 NAN 的定义和StdLib中的 Math.h中的存在冲突。我的解决方法是先用 #undef NAN 取消之前的定义,再根据 VS 编译器中 Math.h 的定义重写一次,结果如下:

//#ifndef NAN
//#define NAN (0.0/0.0)
#undef NAN
#define NAN        ((float)(INFINITY * 0.0F))
//#endif    

最终测试代码如下:
#include <Library/BaseLib.h>
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/PrintLib.h>
#include <Library/ShellCEntryLib.h>
#include <Protocol/EfiShell.h>
#include <Library/ShellLib.h>
#include <stdio.h>
#include <math.h>
#include "tinyexpr.h"

/**
  Set the socket options

  @param [in] Argc  The number of arguments
  @param [in] Argv  The argument value array

  @retval  0        The application exited normally.
  @retval  Other    An error occurred.
**/
int
main (
  IN int Argc,
  IN char **Argv
  )
{
    const char *c = "sqrt(5^2+7^2+11^2+(8-2)^2)";
    double r = te_interp(c, 0);
    printf("The expression:\n\t%s\nevaluates to:\n\t%f\n", c, r);

    return 0;
}

 

上述代码计算表达式

结果如下:

完整的代码下载:
expr

参考:
1.https://github.com/codeplea/tinyexpr
2.https://msdn.microsoft.com/en-us/library/c89bw853.aspx

Step to UEFI (155)系统保留内存探究

在进入Windows之后,Runtime Service 和 ACPI Table之类的仍然存在于内存中,但是Windows没有提供标准的方法进行访问。用户能获得的只有经过包装之后 ACPI Table (实际上是来自注册表) ,或者根本就没有提供(比如:Runtime Service Table)。如果想再次获得这样的信息,只能通过扫描系统保留内存来完成。但是 Windows 没有提供类似 E820 的机制告知用户(应该是因为这种信息对于用户来说是根本无需了解的)。经过一段时间的研究,找到了在 Windows下获得系统保留内存的方法:在注册表 HKLM\HARDWARE\RESOURCEMAP\System Resources\Loader Reserved的位置。以我目前使用的笔记本电脑为例,打开这个位置之后可以看到

在双击进入即可看到保留内存的信息:

此外,Device Manager中也会给出硬件占用的内存地址,但是这个和系统的保留内存是没有任何关系的。

前面给出了注册表的位置,接下来就是如何解析的问题。

CM_RESOURCE_LIST structure 【参考1】

typedef struct _CM_RESOURCE_LIST {
  ULONG                       Count;   
  CM_FULL_RESOURCE_DESCRIPTOR List[1];
} CM_RESOURCE_LIST, *PCM_RESOURCE_LIST;

 

接下来是下面这个结构体【参考2】

typedef struct _CM_FULL_RESOURCE_DESCRIPTOR {
	INTERFACE_TYPE InterfaceType; // unused for WDM   == 0
	ULONG BusNumber; // unused for WDM        
	CM_PARTIAL_RESOURCE_LIST PartialResourceList;
} CM_FULL_RESOURCE_DESCRIPTOR, *PCM_FULL_RESOURCE_DESCRIPTOR;

 

继续解析【参考3】。其中 ULONG 占用 4BYTES;USHORT占用2BYTES;UCHAR占用1BYTES.

typedef struct _CM_PARTIAL_RESOURCE_LIST {
	USHORT Version;   == 00
	USHORT Revision;
	ULONG Count;
	CM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptors[1];
} CM_PARTIAL_RESOURCE_LIST, *PCM_PARTIAL_RESOURCE_LIST;

 

USHORT Version ==00

USHORT Revision ==00

Count=0x19

接下来就是每一个保留的内存情况

CM_PARTIAL_RESOURCE_DESCRIPTOR structure【参考4】
typedef struct _CM_PARTIAL_RESOURCE_DESCRIPTOR {
  UCHAR  Type;
  UCHAR  ShareDisposition;
  USHORT Flags;
  union {
………………
………………
………………
}

 

根据上面的方法即可获得当前系统中 Loader 通知系统的 Reserved Memory
对于 type的定义在【参考5】
Identifies the resource type. The constant value specified for Type indicates which structure within the u union is valid, as indicated in the following table. (These flags are used within both CM_PARTIAL_RESOURCE_DESCRIPTORand IO_RESOURCE_DESCRIPTOR structures, except where noted.)

有了上面的基础就可以编写代码:

// ConsoleApplication3.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <Windows.h>
#include <strsafe.h>
#include <tchar.h>

HKEY m_hKey;

#define OffSet(type, field) ((size_t)&(((type*)0)->field))

#pragma pack(1)
//
// Physical address.
//

typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS;

typedef struct _CM_PARTIAL_RESOURCE_DESCRIPTOR {
	UCHAR Type;
	UCHAR ShareDisposition;
	USHORT Flags;
	union {

		//
		// Range of resources, inclusive.  These are physical, bus relative.
		// It is known that Port and Memory below have the exact same layout
		// as Generic.
		//

		struct {
			PHYSICAL_ADDRESS Start;
			ULONG Length;
		} Generic;

		//
		//

		struct {
			PHYSICAL_ADDRESS Start;
			ULONG Length;
		} Port;

		//
		//

		struct {
#if defined(NT_PROCESSOR_GROUPS)
			USHORT Level;
			USHORT Group;
#else
			ULONG Level;
#endif
			ULONG Vector;
			KAFFINITY Affinity;
		} Interrupt;

		//
		// Values for message signaled interrupts are distinct in the
		// raw and translated cases.
		//

		struct {
			union {
				struct {
#if defined(NT_PROCESSOR_GROUPS)
					USHORT Group;
#else
					USHORT Reserved;
#endif
					USHORT MessageCount;
					ULONG Vector;
					KAFFINITY Affinity;
				} Raw;

				struct {
#if defined(NT_PROCESSOR_GROUPS)
					USHORT Level;
					USHORT Group;
#else
					ULONG Level;
#endif
					ULONG Vector;
					KAFFINITY Affinity;
				} Translated;
			} DUMMYUNIONNAME;
		} MessageInterrupt;

		//
		// Range of memory addresses, inclusive. These are physical, bus
		// relative. The value should be the same as the one passed to
		// HalTranslateBusAddress().
		//

		struct {
			PHYSICAL_ADDRESS Start;    // 64 bit physical addresses.
			ULONG Length;
		} Memory;

		//
		// Physical DMA channel.
		//

		struct {
			ULONG Channel;
			ULONG Port;
			ULONG Reserved1;
		} Dma;

		struct {
			ULONG Channel;
			ULONG RequestLine;
			UCHAR TransferWidth;
			UCHAR Reserved1;
			UCHAR Reserved2;
			UCHAR Reserved3;
		} DmaV3;

		//
		// Device driver private data, usually used to help it figure
		// what the resource assignments decisions that were made.
		//

		struct {
			ULONG Data[3];
		} DevicePrivate;

		//
		// Bus Number information.
		//

		struct {
			ULONG Start;
			ULONG Length;
			ULONG Reserved;
		} BusNumber;

		//
		// Device Specific information defined by the driver.
		// The DataSize field indicates the size of the data in bytes. The
		// data is located immediately after the DeviceSpecificData field in
		// the structure.
		//

		struct {
			ULONG DataSize;
			ULONG Reserved1;
			ULONG Reserved2;
		} DeviceSpecificData;

		// The following structures provide support for memory-mapped
		// IO resources greater than MAXULONG
		struct {
			PHYSICAL_ADDRESS Start;
			ULONG Length40;
		} Memory40;

		struct {
			PHYSICAL_ADDRESS Start;
			ULONG Length48;
		} Memory48;

		struct {
			PHYSICAL_ADDRESS Start;
			ULONG Length64;
		} Memory64;

		struct {
			UCHAR Class;
			UCHAR Type;
			UCHAR Reserved1;
			UCHAR Reserved2;
			ULONG IdLowPart;
			ULONG IdHighPart;
		} Connection;

	} u;
} CM_PARTIAL_RESOURCE_DESCRIPTOR, *PCM_PARTIAL_RESOURCE_DESCRIPTOR;

//
// A Partial Resource List is what can be found in the ARC firmware
// or will be generated by ntdetect.com.
// The configuration manager will transform this structure into a Full
// resource descriptor when it is about to store it in the regsitry.
//
// Note: There must a be a convention to the order of fields of same type,
// (defined on a device by device basis) so that the fields can make sense
// to a driver (i.e. when multiple memory ranges are necessary).
//

typedef struct _CM_PARTIAL_RESOURCE_LIST {
	USHORT Version;
	USHORT Revision;
	ULONG Count;
	CM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptors[1];
} CM_PARTIAL_RESOURCE_LIST, *PCM_PARTIAL_RESOURCE_LIST;

//
// Define the I/O bus interface types.
//

typedef enum _INTERFACE_TYPE {
	InterfaceTypeUndefined = -1,
	Internal,
	Isa,
	Eisa,
	MicroChannel,
	TurboChannel,
	PCIBus,
	VMEBus,
	NuBus,
	PCMCIABus,
	CBus,
	MPIBus,
	MPSABus,
	ProcessorInternal,
	InternalPowerBus,
	PNPISABus,
	PNPBus,
	Vmcs,
	ACPIBus,
	MaximumInterfaceType
}INTERFACE_TYPE, *PINTERFACE_TYPE;

//
// A Full Resource Descriptor is what can be found in the registry.
// This is what will be returned to a driver when it queries the registry
// to get device information; it will be stored under a key in the hardware
// description tree.
//
// Note: There must a be a convention to the order of fields of same type,
// (defined on a device by device basis) so that the fields can make sense
// to a driver (i.e. when multiple memory ranges are necessary).
//

typedef struct _CM_FULL_RESOURCE_DESCRIPTOR {
	INTERFACE_TYPE InterfaceType; // unused for WDM
	ULONG BusNumber; // unused for WDM
	CM_PARTIAL_RESOURCE_LIST PartialResourceList;
} CM_FULL_RESOURCE_DESCRIPTOR, *PCM_FULL_RESOURCE_DESCRIPTOR;


//
// The Resource list is what will be stored by the drivers into the
// resource map via the IO API.
//

typedef struct _CM_RESOURCE_LIST {
	ULONG Count;
	CM_FULL_RESOURCE_DESCRIPTOR List[1];
} CM_RESOURCE_LIST, *PCM_RESOURCE_LIST;

int main()
{
	DWORD Index;
	BYTE  *v;

	if (RegOpenKeyEx(
			HKEY_LOCAL_MACHINE, 
			TEXT("HARDWARE\\RESOURCEMAP\\System Resources\\Loader Reserved"),
			0, 
			KEY_READ, 
			&m_hKey) != ERROR_SUCCESS)
	{
		printf("RegOpenKeyEx fail \n"); 
		getchar();
		exit(0);
	}

	BOOL bRet = FALSE;
	LPSTR lpstrValue;
	DWORD dwType = REG_SZ;
	DWORD lpcbData;
	DWORD r;

	r = RegQueryValueEx(m_hKey,
		TEXT(".Raw"),
		NULL,
		&dwType,
		NULL,
		&lpcbData);
	if (r != ERROR_SUCCESS)
	{
		printf("Can't get data from registry\n");
		getchar();
		exit(0);
	}
	bRet = FALSE;
	lpstrValue = (LPSTR)malloc(lpcbData);

	r = RegQueryValueEx(m_hKey,
		TEXT(".Raw"),
		NULL,
		&dwType,
		(BYTE*)(LPCTSTR)lpstrValue,
		&lpcbData);
	if (r != ERROR_SUCCESS)
	{
		printf("fail\n");
		getchar();
		exit(0);
	}

	for (Index = 0; Index < lpcbData; Index++) {
		if (Index % 16 == 0) { printf("\n"); }
		v = (BYTE*)lpstrValue;
		printf("%02X ", *(v + Index));

	}

	PCM_RESOURCE_LIST res= (PCM_RESOURCE_LIST)lpstrValue;
	PCM_PARTIAL_RESOURCE_LIST Partial;
	DWORD  Index1;

	for (Index = 0; Index < res->Count; Index++) 
	{
		printf("\nInteface type: %d\n Bus Number: %d\n", 
				res->List[Index].InterfaceType,
			    res->List[Index].BusNumber);
		Partial = (PCM_PARTIAL_RESOURCE_LIST) &res->List[Index].PartialResourceList;
		printf(" Version: %d\n Revision: %d\n Counter: %x\n",
			Partial->Version,
			Partial->Revision,
			Partial->Count);
		for (Index1 = 0; Index1 < Partial->Count; Index1++) {
			//printf("%d\n", Partial->PartialDescriptors[Index1].Type);
			if (Partial->PartialDescriptors[Index1].Type == 3) {
				printf("Start:%016I64x Length:%x \n",
						Partial->PartialDescriptors[Index1].u.Memory.Start,
						Partial->PartialDescriptors[Index1].u.Memory.Length
					);
			}
		}
	}
	free(lpstrValue);
	getchar();
    return 0;
}

 

运行结果如下(运行的机器和之前做分析的不同,所以 Counter数量不同)

参考:
1. https://msdn.microsoft.com/en-us/library/windows/hardware/ff541994(v=vs.85).aspx
2. https://msdn.microsoft.com/en-us/library/windows/hardware/ff541954(v=vs.85).aspx
3. https://msdn.microsoft.com/en-us/library/windows/hardware/ff541981(v=vs.85).aspx
4. https://msdn.microsoft.com/en-us/library/windows/hardware/ff541977(v=vs.85).aspx
5. https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ns-wdm-_cm_partial_resource_descriptor

Step to UEFI (154)反编译一个 EFI

之前介绍过,可以通过生成的 .cod 文件来查看生成的汇编代码,今天偶然在 https://blog.csdn.net/robinsongsog/article/details/77164704 看到 uefi_artisan 提供的更简单方法。
dumpbin.exe 是 VS 提供的一个工具,使用下面的方法可以将一个 EFI 直接反汇编:
dumpbin /disasm 文件名.efi
例子:反编译之前的 shorts.efi

得到的结果如下:

Microsoft (R) COFF/PE Dumper Version 12.00.21005.1
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file showrts.efi
File Type: DLL
00000000000002C0: 4C 8B DC mov r11,rsp
00000000000002C3: 49 89 5B 18 mov qword ptr [r11+18h],rbx
00000000000002C7: 49 89 73 20 mov qword ptr [r11+20h],rsi
00000000000002CB: 57 push rdi
00000000000002CC: 48 83 EC 30 sub rsp,30h
00000000000002D0: 4C 8B 52 60 mov r10,qword ptr [rdx+60h]
00000000000002D4: 48 8B 42 58 mov rax,qword ptr [rdx+58h]
00000000000002D8: 49 83 63 08 00 and qword ptr [r11+8],0
00000000000002DD: 49 83 63 10 00 and qword ptr [r11+10h],0
00000000000002E2: 48 8B F2 mov rsi,rdx
00000000000002E5: 48 89 15 5C 1E 00 mov qword ptr [00002148h],rdx

可以看到这样的方法可以让我们很容易得到结果。
关于 dumpbin 的更详细解释可以参考下面2篇文章:

https://blog.csdn.net/jamestaosh/article/details/4237756
https://blog.csdn.net/fengbingchun/article/details/43956673