又有时间写博客了,这次来写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;
- }
运行结果