Step to UEFI (76) -----Dump ACPI DSDT

前面介绍了如何在 Shell下 Dump 系统中各种 ConfigurationTable的方法,这里介绍一下如何取得 ACPI 的 DSDT Table。
简单说一下原理:

1. 在ConfigurationTable中查找 GUID 为 ACPI_TABLE_GUID 和 EFI_ACPI_TABLE_GUID 的两个 Table. 检查获得 Table 的 Revision,只有 >=2 的才是我们需要的
2. 通过Table找到 XsdtAddress 这样我们能找到 XSDT Table
3. 在 XSDT 给出的Table中找到指向 FADT Table 的
4. 在 FADT 中直接给出了 DSDT 的地址
5. 最后将整个DSDT Table dump出来保存为文件即可

用图表简单描述上述过程

image001

代码看起来很复杂,主要是各种指针比较多。

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>
#include  <Library/BaseMemoryLib.h>

#include <Protocol/SimpleFileSystem.h>

#define ACPI_TABLE_GUID \
  { \
    0xeb9d2d30, 0x2d88, 0x11d3, {0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \
  }

#define EFI_ACPI_TABLE_GUID \
  { \
    0x8868e871, 0xe4f1, 0x11d3, {0xbc, 0x22, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81 } \
  }

#define zDebug 1

///
/// RSD_PTR Revision (as defined in ACPI 5.0 spec.)
///
#define EFI_ACPI_5_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION 0x02  
			///< ACPISpec (Revision 5.0) says current value is 2
  
extern EFI_SYSTEM_TABLE			 *gST;
extern EFI_BOOT_SERVICES         *gBS;

#pragma pack(1)
// \MdePkg\Include\IndustryStandard\Acpi50.h
//
// ACPI 5.0 table structures
//

///
/// Root System Description Pointer Structure
///
typedef struct {
  UINT64  Signature;
  UINT8   Checksum;
  UINT8   OemId[6];
  UINT8   Revision;
  UINT32  RsdtAddress;
  UINT32  Length;
  UINT64  XsdtAddress;
  UINT8   ExtendedChecksum;
  UINT8   Reserved[3];
} EFI_ACPI_5_0_ROOT_SYSTEM_DESCRIPTION_POINTER;

// \MdePkg\Include\IndustryStandard\Acpi10.h
///
/// The common ACPI description table header. 
/// This structure prefaces most ACPI tables.
///
typedef struct {
  UINT32  Signature;
  UINT32  Length;
  UINT8   Revision;
  UINT8   Checksum;
  UINT8   OemId[6];
  UINT64  OemTableId;
  UINT32  OemRevision;
  UINT32  CreatorId;
  UINT32  CreatorRevision;
} EFI_ACPI_DESCRIPTION_HEADER;

// \MdePkg\Include\IndustryStandard\Acpi50.h
///
/// ACPI 5.0 Generic Address Space definition
///
typedef struct {
  UINT8   AddressSpaceId;
  UINT8   RegisterBitWidth;
  UINT8   RegisterBitOffset;
  UINT8   AccessSize;
  UINT64  Address;
} EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE;

// \MdePkg\Include\Protocol\AcpiSystemDescriptionTable.h  
///
/// Fixed ACPI Description Table Structure (FADT)
///
typedef struct {
  EFI_ACPI_DESCRIPTION_HEADER             Header;
  UINT32                                  FirmwareCtrl;
  UINT32                                  Dsdt;
  UINT8                                   Reserved0;
  UINT8                                   PreferredPmProfile;
  UINT16                                  SciInt;
  UINT32                                  SmiCmd;
  UINT8                                   AcpiEnable;
  UINT8                                   AcpiDisable;
  UINT8                                   S4BiosReq;
  UINT8                                   PstateCnt;
  UINT32                                  Pm1aEvtBlk;
  UINT32                                  Pm1bEvtBlk;
  UINT32                                  Pm1aCntBlk;
  UINT32                                  Pm1bCntBlk;
  UINT32                                  Pm2CntBlk;
  UINT32                                  PmTmrBlk;
  UINT32                                  Gpe0Blk;
  UINT32                                  Gpe1Blk;
  UINT8                                   Pm1EvtLen;
  UINT8                                   Pm1CntLen;
  UINT8                                   Pm2CntLen;
  UINT8                                   PmTmrLen;
  UINT8                                   Gpe0BlkLen;
  UINT8                                   Gpe1BlkLen;
  UINT8                                   Gpe1Base;
  UINT8                                   CstCnt;
  UINT16                                  PLvl2Lat;
  UINT16                                  PLvl3Lat;
  UINT16                                  FlushSize;
  UINT16                                  FlushStride;
  UINT8                                   DutyOffset;
  UINT8                                   DutyWidth;
  UINT8                                   DayAlrm;
  UINT8                                   MonAlrm;
  UINT8                                   Century;
  UINT16                                  IaPcBootArch;
  UINT8                                   Reserved1;
  UINT32                                  Flags;
  EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE  ResetReg;
  UINT8                                   ResetValue;
  UINT8                                   Reserved2[3];
  UINT64                                  XFirmwareCtrl;
  UINT64                                  XDsdt;
  EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE  XPm1aEvtBlk;
  EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE  XPm1bEvtBlk;
  EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE  XPm1aCntBlk;
  EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE  XPm1bCntBlk;
  EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE  XPm2CntBlk;
  EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE  XPmTmrBlk;
  EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE  XGpe0Blk;
  EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE  XGpe1Blk;
  EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE  SleepControlReg;
  EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE  SleepStatusReg;
} EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE;  
#pragma pack()

EFI_STATUS
PrintGuid (
  IN EFI_GUID *Guid
  )
  
/*++

Routine Description:

  This function prints a GUID to STDOUT.

Arguments:

  Guid    Pointer to a GUID to print.

Returns:

  EFI_SUCCESS             The GUID was printed.
  EFI_INVALID_PARAMETER   The input was NULL.

--*/
{
  if (Guid == NULL) {
	Print(L"Parameter error!\n");
    return EFI_INVALID_PARAMETER;
  }

  Print (
    L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x ",
    (unsigned) Guid->Data1,
    Guid->Data2,
    Guid->Data3,
    Guid->Data4[0],
    Guid->Data4[1],
    Guid->Data4[2],
    Guid->Data4[3],
    Guid->Data4[4],
    Guid->Data4[5],
    Guid->Data4[6],
    Guid->Data4[7]
    );
  return EFI_SUCCESS;
}

UINTN
CompareGUID (
  IN EFI_GUID *Guid1,
  IN EFI_GUID *Guid2
  )
  
/*++

Routine Description:

  This function compares 2 GUIDs

Arguments:

  Guid1    Pointer to 1st GUID.
  Guid2    Pointer to 12st GUID.

Returns:

  0             The GUID was same.
  1  		    The input was different.
  2				PARAMETER error.

--*/
{

  if ((Guid1 == NULL)||(Guid2 == NULL)) {
	Print(L"Parameter error!\n");
    return 2;
  }

  if ((Guid1->Data1 != Guid2->Data1) ||
	  (Guid1->Data2 != Guid2->Data2) ||
	  (Guid1->Data3 != Guid2->Data3) ||
      (Guid1->Data4[0] != Guid2->Data4[0]) ||
      (Guid1->Data4[1] != Guid2->Data4[1]) ||
	  (Guid1->Data4[2] != Guid2->Data4[2]) ||
      (Guid1->Data4[3] != Guid2->Data4[3]) ||
      (Guid1->Data4[4] != Guid2->Data4[4]) ||
	  (Guid1->Data4[5] != Guid2->Data4[5]) ||
      (Guid1->Data4[6] != Guid2->Data4[6]) ||
      (Guid1->Data4[7] != Guid2->Data4[7]))	
	{
		return 1;
	}
	return 0;
}

EFI_STATUS
SaveDSDTToFile (
	CHAR8	*P,
	UINTN	length
  )
  
/*++

Routine Description:

  Write a memory to a file

Arguments:

  P          Pointer to the contant
  length	 Sizeof the contant

Returns:

  EFI_SUCCESS             The GUID was printed.
  EFI_INVALID_PARAMETER   The input was NULL.

--*/
{
    EFI_STATUS          			Status;
	EFI_FILE_PROTOCOL 				*Root,*FileHandle=0;
	EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;	
	
	Status = gBS->LocateProtocol(
					&gEfiSimpleFileSystemProtocolGuid, 
					NULL,
					(VOID **)&SimpleFileSystem);
						
	if (EFI_ERROR(Status)) {
			Print(L"Cannot find EFI_SIMPLE_FILE_SYSTEM_PROTOCOL \r\n");
			return Status;	
		}

	Status = SimpleFileSystem->OpenVolume(SimpleFileSystem,&Root);
	if (EFI_ERROR(Status)) {
			Print(L"OpenVolume error \r\n");
			return Status;	
		}
   
	Status = Root -> Open(Root,
				&FileHandle,
				(CHAR16 *) L"dsdt.aml",
				EFI_FILE_MODE_CREATE | EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,
				0);
			
	if (EFI_ERROR(Status) || (FileHandle==0)) {
			Print(L"Open error \r\n");
			return Status;	
		}	

	Status = FileHandle -> Write(FileHandle, &length, P);
	
	Status  = FileHandle -> Close (FileHandle);
	
	return EFI_SUCCESS;
}

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
	UINTN 		i,j,EntryCount;
	EFI_STATUS	Status;
	CHAR16		Sign[20];
	UINT64		*EntryPtr;
	EFI_GUID	AcpiTableGuid  = ACPI_TABLE_GUID;
	EFI_GUID	Acpi2TableGuid = EFI_ACPI_TABLE_GUID;
	EFI_CONFIGURATION_TABLE		*C=NULL;	
	EFI_ACPI_DESCRIPTION_HEADER				 		*XSDT,*Entry,*DSDT;
	EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE		*FADT;
	EFI_ACPI_5_0_ROOT_SYSTEM_DESCRIPTION_POINTER	*Root;

  C=gST->ConfigurationTable;
  
  for (i=0;i<gST->NumberOfTableEntries-1;i++)
	{	  
		//Step1. Find the table for ACPI
		if ((CompareGUID(&C->VendorGuid,&AcpiTableGuid) == 0) ||
			(CompareGUID(&C->VendorGuid,&Acpi2TableGuid) == 0))
			{	
				Print(L"Found table:");
				Status=PrintGuid(&C->VendorGuid); 
				Print(L"@[0x%X]\n",C);
				
				Root=C->VendorTable;
				if (zDebug) {Print(L"ROOT SYSTEM DESCRIPTION @[0x%X]\n",Root);}
				
				if (zDebug) {
						ZeroMem(Sign,sizeof(Sign));
						for (j=0;j<8;j++) { Sign[j]=(Root->Signature >> (j*8) & 0xFF); }
						Print(L"Signature [%s]\n",Sign);
						Print(L"Revision [%d]\n",Root->Revision);
						ZeroMem(Sign,sizeof(Sign));
						for (j=0;j<6;j++) { Sign[j]= (Root->OemId[j] & 0xFF); }
						Print(L"OEMID [%s]\n",Sign);
					}
				
				Print(L"RSDT address= [0x%X], Length=[0x%X]\n",
										Root->RsdtAddress,Root->Length);
				Print(L"XSDT address= [0x%LX]\n",Root->XsdtAddress);
				
				// Step2. Check the Revision, we olny accept Revision >= 2
				if (Root->Revision>=EFI_ACPI_5_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION)
					{
						// Step3. Get XSDT address
						XSDT=(EFI_ACPI_DESCRIPTION_HEADER *)(UINTN) Root->XsdtAddress;
						EntryCount = (XSDT->Length - sizeof(EFI_ACPI_DESCRIPTION_HEADER)) 
											/ sizeof(UINT64);
						
						if (zDebug) {
							ZeroMem(Sign,sizeof(Sign));
							Sign[0]= (XSDT->Signature & 0xFF);
							Sign[1]= (XSDT->Signature >> 8 & 0xFF);
							Sign[2]= (XSDT->Signature >> 16 & 0xFF);
							Sign[3]= (XSDT->Signature >> 24 & 0xFF);
							Print(L"Sign [%s]\n",Sign);						
							Print(L"length [%d]\n",XSDT->Length);						
							Print(L"Counter [%d]\n",EntryCount);	
							}
						
						// Step4. Check the signature of every entry
						EntryPtr=(UINT64 *)(XSDT+1);
						for (j=0;j<EntryCount; j++,EntryPtr++)
						  {
							
							Entry=(EFI_ACPI_DESCRIPTION_HEADER *)((UINTN)(*EntryPtr));
							
							if (zDebug) {
								ZeroMem(Sign,sizeof(Sign));
								Sign[0]= (Entry->Signature & 0xFF);
								Sign[1]= (Entry->Signature >> 8 & 0xFF);
								Sign[2]= (Entry->Signature >> 16 & 0xFF);
								Sign[3]= (Entry->Signature >> 24 & 0xFF);
								Print(L"%d: [%s] @[%X]\n",j,Sign,Entry);
							 }
							
							// Step5. Find the FADT table
							if (Entry->Signature==0x50434146) { //'FACP'
								FADT = (EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE *)(UINTN) Entry;
								
								// Step6. Get DSDT address
								DSDT = (EFI_ACPI_DESCRIPTION_HEADER *) (FADT->Dsdt);
								if (zDebug) {
									Print(L"DSDT table @[%X]\n",DSDT);
								}
								
								// Step7. Save DSDT as a file
								SaveDSDTToFile((CHAR8 *)DSDT,DSDT->Length);
							}
						  }
					}
			}
		C++;
	}
	
  return EFI_SUCCESS;
}

 

运行结果(注意:上述代码是在NT32模拟环境下编译通过的,但是因为模拟坏境中没有对应的 ACPI Table,所以必须在实体机上运行才有结果):

image002

最后得到的 DSDT.AML 我们可以直接使用 ASL 工具反编译。

完整代码下载

GetACPI

本文代码参阅了【参考1】,这里表示感谢。

参考:
1. http://blog.fpmurphy.com/2015/01/list-acpi-tables-from-uefi-shell.html List ACPI Tables From UEFI Shell
2. http://www.cnblogs.com/junzhkevin/archive/2013/02/25/2932801.html ACPI Tables (不确定是否为文章出处。这里有对ACPI Table进行简单的介绍,值得阅读)

《Step to UEFI (76) -----Dump ACPI DSDT》有12个想法

  1. Hi sir,
    For the source code, you can Add/change to ....
    1. SIGNATURE_32 --> #include
    - Before:
    // Step5. Find the FADT table
    if (Entry->Signature==0x50434146) { //'FACP'
    - Change:
    if (Entry->Signature == SIGNATURE_32 ('F', 'A', 'C', 'P')) {
    2. Get MSDM address
    // Step8. Get MSDM address
    if (Entry->Signature == SIGNATURE_32 ('M', 'S', 'D', 'M')) {
    MSDM = (EFI_ACPI_MSDM *)((UINTN)(*EntryPtr));
    if (zDebug) {
    Print(L"MSDM table @[%X]\n",MSDM);
    ParseMSDM(MSDM);
    }
    }
    //refer 'ParseMSDM()' functio and "EFI_ACPI_MSDM" structure from http://blog.fpmurphy.com/2015/02/retrieve-microsoft-windows-product-key-from-uefi-shell.html

    Best Regards,
    Timothy

      1. 是的,看MdePkg关于printlib的说明,在MdePkg.chm中搜索Printlib:
        %s,The argument is a pointer to a Unicode string. This does not follow ANSI C.

        部分可以简化一下,分享:
        #include
        #include

        #include
        //EfiGetSystemConfigurationTable (IN EFI_GUID *TableGuid, OUT VOID **Table)
        //AsciiPrint

        #include
        //global gST gBS

        #include
        //CopyMem (OUT VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length)
        //ZeroMem (OUT VOID *Buffer, IN UINTN Length)

        #include
        //gEfiAcpi10TableGuid,gEfiAcpi20TableGuid

        #include
        //EFI_ACPI_DESCRIPTION_HEADER
        #include
        //EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER

        EFI_STATUS
        EFIAPI
        UefiMain (
        IN EFI_HANDLE ImageHandle,
        IN EFI_SYSTEM_TABLE *SystemTable
        )
        {
        EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER* RSDP;
        EFI_ACPI_DESCRIPTION_HEADER* RSDT;
        CHAR8 SignatureStr[20];

        UINTN entry_counts=SystemTable->NumberOfTableEntries;
        Print(L"number of entries:%u\n",entry_counts);
        for(UINTN i=0;iConfigurationTable[i].VendorGuid));
        }

        if(EFI_SUCCESS==EfiGetSystemConfigurationTable(&gEfiAcpi10TableGuid,(VOID **)&RSDP))
        {
        Print(L"ACPI 1.0 Table find.\n");
        ZeroMem(SignatureStr,sizeof(SignatureStr));
        CopyMem(SignatureStr,&(RSDP->Signature),sizeof(RSDP->Signature));
        AsciiPrint("RSDP_signature_str:%a\n",SignatureStr);

        RSDT=(EFI_ACPI_DESCRIPTION_HEADER*)(&(RSDP->RsdtAddress));
        ZeroMem(SignatureStr,sizeof(SignatureStr));
        CopyMem(SignatureStr,&(RSDT->Signature),sizeof(RSDT->Signature));
        AsciiPrint("RSDT_signature_str:%a\n",SignatureStr);
        }else if(EFI_SUCCESS==EfiGetSystemConfigurationTable(&gEfiAcpi20TableGuid,(VOID **)&RSDP))
        {
        Print(L"ACPI 2.0 Table find.\n");
        }else
        {//...}
        return EFI_SUCCESS;
        }

        最后感谢博主分享。

  2. 你好,我想问一下的是包含DefinitionBlock的asl文件,被编译器编译成aml文件,这种文件是如何人填入到dsdt表中的??

      1. 你好,因为我是在uefi中想添加acpi的支持,看了好几天代码,没有找到这个aml字节码是怎么添加到dsdt表中的。因为内核获取的时候是通过fadt获取到dsdt的指针,再来获取aml的,像您说的,load到内存后,内核如何知道这个数据在什么地方的 ?

回复 ziv2013 取消回复

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