又有时间写博客了,这次来写ACPI table的遍历,还是比较简单的
ACPI:AdvAdvanced Configuration and PowerInterfaceanced Configuration and PowerInterface,高级配置和电源管理接口
当前,ACPI的电源管理特性一般只适用便携式计算机,
ACPI Table
BIOS在开机过程中会把包在BIOS ROM中的Acpi Table 载入到RAM中,然后留下一些信息给OS来找到他们,
最简单的例子就是RSDP Structure会放在1M以下的某个位置(一般是E0000h~FFFFh,也有可能在0x40e~(0x40e+1KB)),然后OS就可以透过搜寻
Signature(某个标记字)的方式来找到其他的Acpi Table entry point。
根据ACPI spc的说明:
①ACPI table中,RSDP是找出其他table的关键
现在来看RSDP的结构
signature是一个字符串,也就是每个PCI table的名字,在offset16的4bytes地方,是RSDT的基地址;offset24地方的8bytes是XSDT的基地址(高8byte为0)
②通过RSDP找到RSDT
RSDT的36byte后的空间~length,每个entry都指向一个ACPI table,length在offset4的4bytes,(length-36)/4便是RSDT所指向的table个数
③同理,通过RSDP找到XSDT
跟RSDT类似,XSDT36byte后的每个entry也都指向一个table
注:XSDT其实和RSDT相同,打印XSDT即可,现在的OS支持ACPI2.0的用的就必须是XSDT
③这是FADT结构(RSDT下),36指向FACS,40指向DSDT,这里提到一个关键的表FADT
这样,我们就能找到所有的ACPI table了
列出所有的ACPI table
//This section specifies the structure of the system description tables:
/*...........................
• Root System Description Pointer (RSDP)
• System Description Table Header
• Root System Description Table (RSDT)
• Fixed ACPI Description Table (FADT)
• Firmware ACPI Control Structure (FACS)
• Differentiated System Description Table (DSDT)
• Secondary System Description Table (SSDT)
• Multiple APIC Description Table (MADT)
• Smart Battery Table (SBST)
• Extended System Description Table (XSDT)
• Embedded Controller Boot Resources Table (ECDT)
• System Locality Distance Information Table (SLIT)
Advanced Configuration and Power Interface Specification
Hewlett-Packard/Intel/Microsoft/Phoenix/Toshiba 105
• System Resource Affinity Table (SRAT)
• Corrected Platform Error Polling Table (CPEP)
• Maximum System Characteristics Table (MSCT)
• ACPI RAS FeatureTable (RASF)
• Memory Power StateTable (MPST)
• Platform Memory Topology Table (PMTT)
• Boot Graphics Resource Table (BGRT)
• Firmware Performance Data Table (FPDT)
• Generic Timer Description Table (GTDT)
..................................*/
下面来实现重启变关机的过程
在FADT下,有控制重启的port和value,我们只需要根据FADT修改相应的值为关机的,就可以实现变重启为关机了
①来看FADT下控制重启的内容
简单来说,就是当我们在选择重新启动键的时候,BIOS会把RESET_VALUE载入到RESET_REG里,实现重启
所以我们需要做的就是把关机的信息写到RESET_VALUE,把
②这里还有一个位置,offset64的地方,有PM1a_CNT:power mangement1 control Register
实现关机就是在这个地址写0x3c00(0011 1100 0000 0000),FADT提供给了我们这个地址,就是PM1a_CNT_BLOCK,一般不支持PM1b_CNT
③我们现在来看FADT offset64的结构,通过GAS可以找到
在offset4的地方就是写3c00的地方了,也就是FADT offset 64+4,我的机子上读出来,也可以通过RW看到,这个值是1804
参考代码(bc下编译,dos下执行)
注:第一个打印ACPI table可以在笔记本或其他板子执行,但重启变关机必须在note book上执行
Windows 98是支持ACPI的第一个微软的操作系统,而支持ACPI2.0的OS必须使用的是XSDT而非RSDT,这就是我们在程序中使用的是RSDT的原因
#include<stdio.h>
#include<string.h>
#include<dos.h>
typedef unsigned long uint32;
typedef unsigned char uint8;
#define ONEKB 1024//2^10即1Kb
//这也是访问4GB空间的函数所需的定义
unsigned long GDT_def[] = { 0, 0, 0x0000FFFF, 0x008F9200 };
unsigned char GDT_Addr[6] = { 0 };
uint8 Readmm8(uint32 address)
{
uint8 temp;
asm push eax
asm push esi
asm push ds
//asm pushad;
asm mov ax,0
asm mov ds,ax
asm mov esi,address
asm mov al,[esi]
asm mov temp,al
//asm popad;
asm pop ds
asm pop esi
asm pop eax
return temp;
}
uint32 Readmm32(uint32 address)
{
asm push eax
asm push esi
asm push ds
//asm pushad;
asm mov ax,0
asm mov ds,ax
asm mov esi,address
asm mov eax,[esi]
asm mov address,eax
//asm popad;
asm pop ds
asm pop esi
asm pop eax
return address;
}
void Wirtemm32(uint32 address,uint32 localdata)
{
asm push eax
asm push esi
asm push ds
asm mov ax,0
asm mov ds,ax
asm mov esi,address
asm mov eax,localdata
asm mov [esi],eax
asm pop ds
asm pop esi
asm pop eax
}
//DOS下默认访问1M空间,以下这两个函数为了使DOS能访问到4GB内存空间
void openA20()
{
while(inp(0x64) & 2); outp(0x64,0xd1);
while(inp(0x64) & 2); outp(0x60,0xdf);
while(inp(0x64) & 2); outp(0x64,0xff);
}
void set4gb()
{ asm{
cli
push ds
push es
mov word ptr GDT_Addr[0], (2*8-1)
mov eax,ds
shl eax,4
xor ebx,ebx
mov bx,offset GDT_def
add eax,ebx
mov dword ptr GDT_Addr[2],eax
lgdt fword ptr GDT_Addr
mov bx,8
mov eax,cr0
or al,1
mov cr0,eax
jmp flush1
}
flush1: asm{
mov ds,bx
mov es,bx
and al,0feh
mov cr0,eax
jmp flush2
}
flush2: asm{
pop es ; pop ds
sti
}
}
void realToProtect()
{
openA20();
set4gb();
}
uint32 CheckRSDP(uint32 begin,uint32 end)//找RSDP地址
{
int i=0;
uint32 addr;
uint8 signat[9] = {"RSD PTR "};//RSDP的signature
int count=0;
//find RSDP(root System Description Pointer)
for(addr=begin ; addr<=(end) ; addr+=0x10)//16 bytes boundaries
{
count=0;
for(i=0 ; Readmm8(addr+i)==signat[i] ; ++i)
{
++count;
if((signat[i+1] == '\0')&&(count==8))
return addr;
}
}
return 0;
}
uint32 FindRSDP()
{
uint32 addr;
if((addr=CheckRSDP(0x40e&~((uint32)0x0f)+0x10,(0x40e) + (ONEKB))) == 0)//40e,16bytes align
addr=CheckRSDP(0x0e0000,0xfffff);
return addr;
}
uint32 PrintSignat(uint32 addr)
{
int i;
uint8 arr[5]={0};
uint8 brr[5] = {"FACP"};
for(i=0 ; i<4 ; ++i)
{
arr[i] = Readmm8(addr+i);
printf("%c",arr[i]);
}
printf(" , %llx\n",addr);
if(strcmp(arr,brr) == 0) //找到FADT并返回基地址
return addr;
return 0;
}
uint32 ReadEntry(uint32 addr,uint32 width)
{
uint32 length;
uint32 num;
uint32 i;
uint32 offset;
uint32 data;
uint32 data1 = 0;
uint32 tmp;
offset=36; //entry in offset 36
length = Readmm32(addr+4); //offset04,length
num = (length-offset)/width; //pointer 4 bytes
for(i=0 ; i<num ; ++i)
{
data = Readmm32(addr+offset);
if((tmp=PrintSignat(data))!=0)
data1 = tmp; //FADT
offset += width;
}
return data1;
}
//以下两个函数就是在笔记本上实现重启变关机的功能函数了(注意拔掉电源)
uint8 CheckSum(uint32 addr)
{
uint32 i;
uint8 sum=0;
uint32 length;
length = Readmm32(addr+4);//length
for(i=0 ; i<length ; ++i)
{
if(i == 9)
continue;//except checksum
sum += Readmm8(addr+i);
}
return sum;
}
void ChangeReset(uint32 addr)//set reset to shut off,XSDT base address
{
uint32 data;
uint32 PM1_addr;
uint8 check;
uint8 sum;
data = Readmm32(addr+112);//check the flag
if((data&BIT(10)) == 0)
{
printf("reset is not supported by FACP\n");
Wirtemm32(addr+112,data|BIT(10));
}
printf("FADT:%llx\n",addr);
PM1_addr = Readmm32(addr+64);//PM_CNT(1804)
printf("PM1 addr:%llx\n",PM1_addr);
Wirtemm32(addr+116+4,PM1_addr+1);
printf("reset_reg:%llx\n",Readmm32(addr+116+4));
//Wirtemm32(addr+116+8,0);//upper
Wirtemm8(addr+128,0x3c);//address of reset_value,3c00
printf("reset_value:%x\n",Readmm8(addr+128));
sum = CheckSum(addr);
printf("othres sum:%x\n",sum);
check = 0-sum;
printf("check:%x\n",check);
Wirtemm8(addr+9,check);
}
int main()
{
uint32 RSDP_addr;
uint32 RSDT_addr;
uint32 XSDT_addr_low;//XHCI,64bit
uint32 XSDT_addr_hign;
uint32 addr;
uint32 tmp;
realToProtect();
printf("main\n");
RSDP_addr = FindRSDP();
if(RSDP_addr != 0)
printf("the base address of RSDP:%llx\n",RSDP_addr);
else
printf("find RSDP error\n");
RSDT_addr = Readmm32(RSDP_addr+16);//0x10
XSDT_addr_low = Readmm32(RSDP_addr+24);
XSDT_addr_hign = Readmm32(RSDP_addr+28);//upper
printf("ACPI Tables:\n");
printf("RSD PTR , %llx\n",RSDP_addr);
printf("RSDT , %llx\n",RSDT_addr);
printf("XSDT , %llx\n",XSDT_addr_low);
//RSDT
if((addr = ReadEntry(XSDT_addr_low,8))!=0)//addr是返回的XSDT所指向的FADT(旗下有两个table)
{
tmp=Readmm32(addr+36);//FACS
PrintSignat(tmp);
tmp=Readmm32(addr+40);//DSDT
PrintSignat(tmp);
}
else
printf("not find\n");
return 0;
}
运行结果