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 (155)系统保留内存探究》有8个想法

  1. 是这样...

    System Reserved Memory Range :
    ---------------------------------------------------------------------------------------------------------------------
    Index Start Address Limit Address Size About Flag
    1 0x0000000000000000 0x0000000000100000 0x0000000000100000 1024.00KB 0x00000103
    2 0x0000000000103000 0x00000000001A7000 0x00000000000A4000 656.00KB 0x00000103
    3 0x00000000002E1000 0x00000000002F1000 0x0000000000010000 64.00KB 0x00000103
    4 0x000000008DDAB000 0x000000008F40E000 0x0000000001663000 22.39MB 0x00000103
    5 0x000000008F40F000 0x0000000090000000 0x0000000000BF1000 11.94MB 0x00000103
    6 0x00000000E0000000 0x00000000F0000000 0x0000000010000000 256.00MB 0x00000103
    7 0x00000000FE000000 0x00000000FE011000 0x0000000000011000 68.00KB 0x00000103
    8 0x00000000FEC00000 0x00000000FEC01000 0x0000000000001000 4.00KB 0x00000103
    9 0x00000000FED00000 0x00000000FED04000 0x0000000000004000 16.00KB 0x00000103
    10 0x00000000FEE00000 0x00000000FEE01000 0x0000000000001000 4.00KB 0x00000103
    11 0x00000000FF000000 0x0000000100000000 0x0000000001000000 16.00MB 0x00000103
    ---------------------------------------------------------------------------------------------------------------------
    Total 0x000000001341F000 308.12MB

    1. 有研究过每一段后面的Flag如何解析?
      例如我解析到的Flag 0x00000103, 0x02000107具体代表什么?
      貌似没有查到具体代表的意义是什么...

      Physical Memory Range :
      ------------------------------------------------------------------------------------------------------------------
      Index Start Address Limit Address Size About Flag
      1 0x0000000000001000 0x000000000009F000 0x000000000009E000 632.00KB 0x00000103
      2 0x0000000000100000 0x00000000791C3000 0x00000000790C3000 1.89GB 0x00000103
      3 0x000000007A2A9000 0x000000007A392000 0x00000000000E9000 932.00KB 0x00000103
      4 0x000000007AC0E000 0x000000007AC0F000 0x0000000000001000 4.00KB 0x00000103
      5 0x0000000100000000 0x000000047E000000 0x000000037E000000 13.97GB 0x02000107
      ------------------------------------------------------------------------------------------------------------------
      Total 0x00000003F724B000 15.8616GB

      1. 谢谢站长的回复。
        我的问题是这样子的,要想在内核中reserve 一块特定物理内存,不被系统分配使用,而是只由自己使用。
        gBS -> AllocatePool 函数只能指定memory type,无法指定具体的地址值吧?
        比如, 以内存位置0x22000000为起始地址,reserve 1KB的内存,使得这1KB的内存不被OS使用。

        1. 是的,这个函数无法指定内存位置。如果有这样的需求你只能去研究一下扫描 PCI 设备空间分配的内存的函数看看了。具体我没做过,只是猜测。

发表回复

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