问题描述
我在做基于英特尔x86架构的保护模式的操作系统,并一直在寻找如何通过组装code,或者类似的东西在电脑关机的一些信息。你能帮我解决这个问题?
I'm making a protected-mode OS based on Intel's x86 architecture, and was looking for some information on how to power off the computer via assembly code, or something like that. Could you help me with this problem?
推荐答案
href=\"http://forum.osdev.org/viewtopic.php?t=16990\">http://forum.osdev.org/viewtopic.php?t=16990
from http://forum.osdev.org/viewtopic.php?t=16990
在ACPI关闭在技术上是一个非常简单的事情一切需要的是一个outw(PM1a_CNT,SLP_TYPa | SLP_EN);并且计算机处于关机状态。
问题在于这些值的聚集特别是因为SLP_TYPa在_S5对象,它是在DSDT,因此反洗钱EN codeD。
The ACPI shutdown is technically a really simple thing all that is needed is a outw(PM1a_CNT, SLP_TYPa | SLP_EN ); and the computer is powered off.The problem lies in the gathering of these values especially since the SLP_TYPa is in the _S5 object which is in the DSDT and therefore AML encoded.
下面是在哪里可以找到这些领域一个简单的地图。
Below is a simple "map" of where to find these fields.
"RSD PTR "
||
RsdtAddress pointer at offset 16
||
\/
"RSDT"
||
pointer at offset 36 + 4 * n (check the target for the sig "FACP" to get the right n)
||
\/
"FACP"
||
||=====\
|| ||
|| PM1a_CNT_BLK; offset: 64 (see section 4.7.3.2)
|| PM1b_CNT_BLK; offset: 68
|| ||
|| \/
|| SLP_TYPx; bit 10-12
|| SLP_EN; bit 13
||
DSDT pointer at offset 40
||
\/
"DSDT" (export the \_S5 object somehow.)
要导出 \\ _ S5
对象中一个通常会使用AML间preTER但是这显然不是考虑到我们正在建设一个爱好操作系统的选项。简单的解决方法是手动扫描DSDT。反垄断法语言指定_...对象只定义一次,这使得它很简单,找到 \\ _因为一个简单的
就足够了。 memcmp S5
对象()
一旦发现被提取的 SLP_TYPx
值。
To export the \_S5
object one would normally use an AML interpreter but that's obviously not an option considering we're building a hobby OS. The simple solution is to scan the DSDT manually. The AML language specifies that _... objects are defined only once which makes it very simple to find the \_S5
object since a simple memcmp()
is enough.Once found the SLP_TYPx
values are extracted.
bytecode of the \_S5 object
-----------------------------------------
| (optional) | | | |
NameOP | \ | _ | S | 5 | _
08 | 5A | 5F | 53 | 35 | 5F
-----------------------------------------------------------------------------------------------------------
| | | ( SLP_TYPa ) | ( SLP_TYPb ) | ( Reserved ) | (Reserved )
PackageOP | PkgLength | NumElements | byteprefix Num | byteprefix Num | byteprefix Num | byteprefix Num
12 | 0A | 04 | 0A 05 | 0A 05 | 0A 05 | 0A 05
----this-structure-was-also-seen----------------------
PackageOP | PkgLength | NumElements |
12 | 06 | 04 | 00 00 00 00
的信息收集最好在OS初始化完成后,因为可以重复使用的RAM,也不需要担心破坏了。
The gathering of the information is best performed at OS initialization because after that you can reuse the ram and don't need to worry about corrupting it.
现在剩下的工作就是 outw(PM1a_CNT,SLP_TYPa | SLP_EN);
键,你走了。
如果 PM1b_CNT!= 0
你需要用b重复。
Now all that remains is outw(PM1a_CNT, SLP_TYPa | SLP_EN );
and you're gone.If PM1b_CNT != 0
you need to repeat it with b.
如果这是一个有点过于抽象这里是一些code看看
If that was a little too abstract here is some code to look at
//
// here is the slighlty complicated ACPI poweroff code
//
#include <stddef.h>
#include <print.h>
#include <string.h>
#include <io.h>
#include <time.h>
dword *SMI_CMD;
byte ACPI_ENABLE;
byte ACPI_DISABLE;
dword *PM1a_CNT;
dword *PM1b_CNT;
word SLP_TYPa;
word SLP_TYPb;
word SLP_EN;
word SCI_EN;
byte PM1_CNT_LEN;
struct RSDPtr
{
byte Signature[8];
byte CheckSum;
byte OemID[6];
byte Revision;
dword *RsdtAddress;
};
struct FACP
{
byte Signature[4];
dword Length;
byte unneded1[40 - 8];
dword *DSDT;
byte unneded2[48 - 44];
dword *SMI_CMD;
byte ACPI_ENABLE;
byte ACPI_DISABLE;
byte unneded3[64 - 54];
dword *PM1a_CNT_BLK;
dword *PM1b_CNT_BLK;
byte unneded4[89 - 72];
byte PM1_CNT_LEN;
};
// check if the given address has a valid header
unsigned int *acpiCheckRSDPtr(unsigned int *ptr)
{
char *sig = "RSD PTR ";
struct RSDPtr *rsdp = (struct RSDPtr *) ptr;
byte *bptr;
byte check = 0;
int i;
if (memcmp(sig, rsdp, 8) == 0)
{
// check checksum rsdpd
bptr = (byte *) ptr;
for (i=0; i<sizeof(struct RSDPtr); i++)
{
check += *bptr;
bptr++;
}
// found valid rsdpd
if (check == 0) {
/*
if (desc->Revision == 0)
wrstr("acpi 1");
else
wrstr("acpi 2");
*/
return (unsigned int *) rsdp->RsdtAddress;
}
}
return NULL;
}
// finds the acpi header and returns the address of the rsdt
unsigned int *acpiGetRSDPtr(void)
{
unsigned int *addr;
unsigned int *rsdp;
// search below the 1mb mark for RSDP signature
for (addr = (unsigned int *) 0x000E0000; (int) addr<0x00100000; addr += 0x10/sizeof(addr))
{
rsdp = acpiCheckRSDPtr(addr);
if (rsdp != NULL)
return rsdp;
}
// at address 0x40:0x0E is the RM segment of the ebda
int ebda = *((short *) 0x40E); // get pointer
ebda = ebda*0x10 &0x000FFFFF; // transform segment into linear address
// search Extended BIOS Data Area for the Root System Description Pointer signature
for (addr = (unsigned int *) ebda; (int) addr<ebda+1024; addr+= 0x10/sizeof(addr))
{
rsdp = acpiCheckRSDPtr(addr);
if (rsdp != NULL)
return rsdp;
}
return NULL;
}
// checks for a given header and validates checksum
int acpiCheckHeader(unsigned int *ptr, char *sig)
{
if (memcmp(ptr, sig, 4) == 0)
{
char *checkPtr = (char *) ptr;
int len = *(ptr + 1);
char check = 0;
while (0<len--)
{
check += *checkPtr;
checkPtr++;
}
if (check == 0)
return 0;
}
return -1;
}
int acpiEnable(void)
{
// check if acpi is enabled
if ( (inw((unsigned int) PM1a_CNT) &SCI_EN) == 0 )
{
// check if acpi can be enabled
if (SMI_CMD != 0 && ACPI_ENABLE != 0)
{
outb((unsigned int) SMI_CMD, ACPI_ENABLE); // send acpi enable command
// give 3 seconds time to enable acpi
int i;
for (i=0; i<300; i++ )
{
if ( (inw((unsigned int) PM1a_CNT) &SCI_EN) == 1 )
break;
sleep(10);
}
if (PM1b_CNT != 0)
for (; i<300; i++ )
{
if ( (inw((unsigned int) PM1b_CNT) &SCI_EN) == 1 )
break;
sleep(10);
}
if (i<300) {
wrstr("enabled acpi.\n");
return 0;
} else {
wrstr("couldn't enable acpi.\n");
return -1;
}
} else {
wrstr("no known way to enable acpi.\n");
return -1;
}
} else {
//wrstr("acpi was already enabled.\n");
return 0;
}
}
//
// bytecode of the \_S5 object
// -----------------------------------------
// | (optional) | | | |
// NameOP | \ | _ | S | 5 | _
// 08 | 5A | 5F | 53 | 35 | 5F
//
// -----------------------------------------------------------------------------------------------------------
// | | | ( SLP_TYPa ) | ( SLP_TYPb ) | ( Reserved ) | (Reserved )
// PackageOP | PkgLength | NumElements | byteprefix Num | byteprefix Num | byteprefix Num | byteprefix Num
// 12 | 0A | 04 | 0A 05 | 0A 05 | 0A 05 | 0A 05
//
//----this-structure-was-also-seen----------------------
// PackageOP | PkgLength | NumElements |
// 12 | 06 | 04 | 00 00 00 00
//
// (Pkglength bit 6-7 encode additional PkgLength bytes [shouldn't be the case here])
//
int initAcpi(void)
{
unsigned int *ptr = acpiGetRSDPtr();
// check if address is correct ( if acpi is available on this pc )
if (ptr != NULL && acpiCheckHeader(ptr, "RSDT") == 0)
{
// the RSDT contains an unknown number of pointers to acpi tables
int entrys = *(ptr + 1);
entrys = (entrys-36) /4;
ptr += 36/4; // skip header information
while (0<entrys--)
{
// check if the desired table is reached
if (acpiCheckHeader((unsigned int *) *ptr, "FACP") == 0)
{
entrys = -2;
struct FACP *facp = (struct FACP *) *ptr;
if (acpiCheckHeader((unsigned int *) facp->DSDT, "DSDT") == 0)
{
// search the \_S5 package in the DSDT
char *S5Addr = (char *) facp->DSDT +36; // skip header
int dsdtLength = *(facp->DSDT+1) -36;
while (0 < dsdtLength--)
{
if ( memcmp(S5Addr, "_S5_", 4) == 0)
break;
S5Addr++;
}
// check if \_S5 was found
if (dsdtLength > 0)
{
// check for valid AML structure
if ( ( *(S5Addr-1) == 0x08 || ( *(S5Addr-2) == 0x08 && *(S5Addr-1) == '\\') ) && *(S5Addr+4) == 0x12 )
{
S5Addr += 5;
S5Addr += ((*S5Addr &0xC0)>>6) +2; // calculate PkgLength size
if (*S5Addr == 0x0A)
S5Addr++; // skip byteprefix
SLP_TYPa = *(S5Addr)<<10;
S5Addr++;
if (*S5Addr == 0x0A)
S5Addr++; // skip byteprefix
SLP_TYPb = *(S5Addr)<<10;
SMI_CMD = facp->SMI_CMD;
ACPI_ENABLE = facp->ACPI_ENABLE;
ACPI_DISABLE = facp->ACPI_DISABLE;
PM1a_CNT = facp->PM1a_CNT_BLK;
PM1b_CNT = facp->PM1b_CNT_BLK;
PM1_CNT_LEN = facp->PM1_CNT_LEN;
SLP_EN = 1<<13;
SCI_EN = 1;
return 0;
} else {
wrstr("\\_S5 parse error.\n");
}
} else {
wrstr("\\_S5 not present.\n");
}
} else {
wrstr("DSDT invalid.\n");
}
}
ptr++;
}
wrstr("no valid FACP present.\n");
} else {
wrstr("no acpi.\n");
}
return -1;
}
void acpiPowerOff(void)
{
// SCI_EN is set to 1 if acpi shutdown is possible
if (SCI_EN == 0)
return;
acpiEnable();
// send the shutdown command
outw((unsigned int) PM1a_CNT, SLP_TYPa | SLP_EN );
if ( PM1b_CNT != 0 )
outw((unsigned int) PM1b_CNT, SLP_TYPb | SLP_EN );
wrstr("acpi poweroff failed.\n");
}
有关更多信息,请阅读ACPI 1.0a规范的相应部分。
For further information read the corresponding sections of the ACPI 1.0a specification
9.1.7 Transitioning from the Working to the Soft Off State
7.5.2 \_Sx states
7.4.1 \_S5
4.7.2.3 Sleeping/Wake Control
16.3 AML Byte Streeam Byte Values
16.2.3 Package Length Encoding
这适用于所有我的机器的Bochs和QEMU的。
但我注意到,人们不必启用ACPI的PC断电。虽然我不知道这是不是总是如此。
This works on all of my machines bochs and qemu.but I noticed that one needn't enable ACPI for the pc to power down. Though i don't know if this is always the case.
如果你只是想玩一点点。
对于Bochs的和QEMU是 outw(0xB004,为0x0 |为0x2000);
If you just want to play a little.For bochs and qemu it's outw( 0xB004, 0x0 | 0x2000 );
这篇关于如何从一个独立的环境中的计算机断电?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!