ACPI poweroff
Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
ACPI poweroff
I have written a "simple" acpi poweroff routine and wanted to share my knowledge since it seems here isn't that much acpi related stuff available.
Maybe this could be added to the wiki?
###################################
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 especialy 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.
To export the \_S5 object one would normally use an AML interpreter but thats 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.
The gatering of the informations is best performed at OS initialization because after that you can reuse the ram and don't need to worry about corrupting it.
Now all that remains is outw(PM1a_CNT, SLP_TYPa | SLP_EN ); and your'e gone.
If PM1b_CNT is != 0 you need to repeat it with b.
If that was a little too abstract here is some code to look at
For further information read the corresponding sections of the acpi 1.0a specification
#############################################
This works on all my machines bochs and qemu.
but i noticed that one needn't enable acpi for the pc to power down. Tough i dont know if this is always the case.
If you yust want to play a little.
For bochs and qemu it's outw( 0xB004, 0x0 | 0x2000 );
Maybe this could be added to the wiki?
###################################
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 especialy 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.
Code: Select all
"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.)
Once found the SLP_TYPx values are extracted.
Code: Select all
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
Now all that remains is outw(PM1a_CNT, SLP_TYPa | SLP_EN ); and your'e gone.
If PM1b_CNT is != 0 you need to repeat it with b.
If that was a little too abstract here is some code to look at
Code: Select all
//
// 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");
}
Code: Select all
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 EncodingThis works on all my machines bochs and qemu.
but i noticed that one needn't enable acpi for the pc to power down. Tough i dont know if this is always the case.
If you yust want to play a little.
For bochs and qemu it's outw( 0xB004, 0x0 | 0x2000 );
Technically I think there's one method you're suppose to execute first as well \_PTS. There's an optional method \_GTS too, but I think what you're describing should be fine for an emulator. Method execution would definitely require the use of interpreter as well. I don't know if a real system would require you todo an SMI->OS handoff of the resources first as well.
youre right the _PTS should be executed but as far as i could see that only makes a aml local? thing and for \_S4 some pci things. so dosnt realy matter. me thinks
and actually all of my real hardware even powers down when acpi wasnt even enabled so there seems to be no problem. though maybe there a lot of machines where this dosnt apply
and actually all of my real hardware even powers down when acpi wasnt even enabled so there seems to be no problem. though maybe there a lot of machines where this dosnt apply
- piranha
- Member
Member - Posts: 1391
- Joined: Thu Dec 21, 2006 7:42 pm
- Location: Unknown. Momentum is pretty certain, however.
- Contact:
Say, is this supposed to work in QEMU, or is there a flag I need to set in the Qemu command? Or am I just calling the enable function wrong?
Is all you need, right?
-JL
Code: Select all
initAcpi();-JL
SeaOS: Adding VT-x, networking, and ARM support
dbittman on IRC, @danielbittman on twitter
https://dbittman.github.io
dbittman on IRC, @danielbittman on twitter
https://dbittman.github.io
No initAcpi();just retrieves everything needed and stores it so that you can use the ram used by acpi its basically a function you want to add into your kernel initialization the actual shutdown is performed by a later call to acpiPowerOff();
And i am okay with it being added to the wiki actually i strove to write it in a fashion which would enable a simple copy and paste. Hope i was able to acomplish it.
And i am okay with it being added to the wiki actually i strove to write it in a fashion which would enable a simple copy and paste. Hope i was able to acomplish it.
- piranha
- Member
Member - Posts: 1391
- Joined: Thu Dec 21, 2006 7:42 pm
- Location: Unknown. Momentum is pretty certain, however.
- Contact:
Yeah, I meant just to enable ACPI or start it or whatever, but the Init call doesn't work, it returns 'no acpi'. Is this a problem only in Qemu?
-JL
-JL
SeaOS: Adding VT-x, networking, and ARM support
dbittman on IRC, @danielbittman on twitter
https://dbittman.github.io
dbittman on IRC, @danielbittman on twitter
https://dbittman.github.io
You usually get the no acpi message when the RSDT wasnt found which is the case when acpi isnt available tough a disabled a20 gate could also be the cause since the header may lay in an area above 1m
its certainly not a qemu problem as it works in my qemu.
i have qemu v0.9.1 tough i didnt compile it myself but used the default ubuntu hardy package.
its certainly not a qemu problem as it works in my qemu.
i have qemu v0.9.1 tough i didnt compile it myself but used the default ubuntu hardy package.
- piranha
- Member
Member - Posts: 1391
- Joined: Thu Dec 21, 2006 7:42 pm
- Location: Unknown. Momentum is pretty certain, however.
- Contact:
Well, it doesn't work for me. Maybe it's my memcmp function, thats the only one I didn't have already:
Does it need more or is it just completly messed up?
-JL
Code: Select all
int memcmp(char *s1, char *s2, int n)
{
int i;
for(i=0;i<n;i++)
{
if(s1[n] != s2[n])
return 1;
}
return 0;
}-JL
SeaOS: Adding VT-x, networking, and ARM support
dbittman on IRC, @danielbittman on twitter
https://dbittman.github.io
dbittman on IRC, @danielbittman on twitter
https://dbittman.github.io
- Brynet-Inc
- Member
Member - Posts: 2426
- Joined: Tue Oct 17, 2006 9:29 pm
- Libera.chat IRC: brynet
- Location: Canada
- Contact:
Post by Brynet-Inc »
That's not even a standards compliant implementation.. heck, it's not even a working implementation.piranha wrote:Well, it doesn't work for me. Maybe it's my memcmp function, thats the only one I didn't have already:Does it need more or is it just completly messed up?Code: Select all
int memcmp(char *s1, char *s2, int n) { int i; for(i=0;i<n;i++) { if(s1[n] != s2[n]) return 1; } return 0; }
-JL
Do you even read standards and stress test the functions you write? :roll:
- piranha
- Member
Member - Posts: 1391
- Joined: Thu Dec 21, 2006 7:42 pm
- Location: Unknown. Momentum is pretty certain, however.
- Contact:
That one I wrote quickly because I had about 5 min before I had to leave and I did want to test the ACPI implementation, so I didn't bother spending a while researching it, but I'll do that now.
EDIT: Oops, yeah, that was the problem, sorry about these posts, they were stupid.
-JL :oops:
EDIT: Oops, yeah, that was the problem, sorry about these posts, they were stupid.
-JL :oops:
SeaOS: Adding VT-x, networking, and ARM support
dbittman on IRC, @danielbittman on twitter
https://dbittman.github.io
dbittman on IRC, @danielbittman on twitter
https://dbittman.github.io
- frazzledjazz
- Posts: 7
- Joined: Tue Mar 03, 2009 3:52 pm
Re: ACPI poweroff
Post by frazzledjazz »
can someone check the above code to this please?
I have that code above already and need it in 32bits assembler or 32bits pascal for FPC. I THINK I ported it correctly.
The assembler cannot be inline, this is a FPC limitation, however I can accept either ATT or intel styles.
All examples I otherwise have are 16bit calls.[which wont work with FPC] I have yet to see any real world examples on dropping down to real mode for shutdown.
I have that code above already and need it in 32bits assembler or 32bits pascal for FPC. I THINK I ported it correctly.
The assembler cannot be inline, this is a FPC limitation, however I can accept either ATT or intel styles.
All examples I otherwise have are 16bit calls.[which wont work with FPC] I have yet to see any real world examples on dropping down to real mode for shutdown.
Code: Select all
//
// here is the slighlty complicated ACPI poweroff code
//
uses
x86,strings;
type
record RSDPtr begin
Signature:byte=8;
CheckSum:byte;
OemID:byte=6;
Revision:byte;
^RsdtAddress:dword;
end;
record FACP begin
Signature:byte=4;
Length:dword;
unneded1:byte=32;
^DSDT:dword;
unneded2:byte=4;
^SMI_CMD:dword;
ACPI_ENABLE:byte;
ACPI_DISABLE:byte;
unneded3:byte=10;
^PM1a_CNT_BLK:dword;
^PM1b_CNT_BLK:dword;
unneded4:byte=67;
PM1_CNT_LEN:byte;
end;
var
^SMI_CMD:dword;
ACPI_ENABLE:byte;
ACPI_DISABLE:byte;
^PM1a_CNT:dword;
^PM1b_CNT:dword;
SLP_TYPa:word;
SLP_TYPb:word;
SLP_EN:word;
SCI_EN:word;
PM1_CNT_LEN:byte;
// check if the given address has a valid header
function ^acpiCheckRSDPtr(^ptr:integer);
^sig:string;
rsdp:rsdPtr;
bptr:integer;
check:byte;
i:integer;
begin
^sig:="RSD PTR";
rsdp := ^ ptr;
check:=0;
rsdp:=^ptr
if (memcmp(sig, rsdp, 8) = 0) then begin
// check checksum rsdpd
bptr := ptr;
i:=0;
repeat
check += ^bptr; //huh??
inc(bptr);
inc(i);
until sizeof(rsdp);) >i
// found valid rsdpd
if (check == 0) {
/*
if (desc->Revision == 0)
wrstr("acpi 1");
else
wrstr("acpi 2");
*/
return rsdp->RsdtAddress;
end;
end;
return NULL;
end;
// finds the acpi header and returns the address of the rsdt
procedure ^acpiGetRSDPtr
^addr:integer;
^rsdp:integer;
begin
// search below the 1mb mark for RSDP signature
addr = 0x000E0000;
repeat
addr += 0x10/sizeof(addr));
{is this inc() or dec() im confused...}
rsdp = acpiCheckRSDPtr(addr);
if (rsdp != NULL)
return rsdp;
until addr > 0x00100000;
// 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;
end;
// checks for a given header and validates checksum
procedure acpiCheckHeader(^ptr:integer; ^sig:char);
var
^checkPtr,len:integer;
check:char;
begin
if (memcmp(ptr, sig, 4) = 0) then begin
^checkPtr = ptr;
len = ^(ptr + 1);
check = 0;
while (len >0) do begin
check += ^checkPtr; //huh ??
inc(checkPtr);
end;
if (check = 0)
return 0;
end;
return -1;
end;
procedure acpiEnable;
var
i:integer;
begin
// check if acpi is enabled
if ((readportw (PM1a_CNT) and SCI_EN) = 0) then begin
// check if acpi can be enabled
if (SMI_CMD <> 0 & ACPI_ENABLE <> 0) then begin
writeportb( SMI_CMD, ACPI_ENABLE); // send acpi enable command --writeportb
// give 3 seconds time to enable acpi
i:=0;
repeat
if ((readportw( PM1a_CNT) and SCI_EN) = 1) then
break;
delay(3000); //about 3 secs
inc(i);
until i > 300;
if (PM1b_CNT <> 0) then
i:=0;
repeat
if ((readportw( PM1b_CNT) and SCI_EN) = 1) then //if readportw () and ()=1
break;
delay(3000);
inc(i);
until i > 300;
if (i>300) then begin
writeln("enabled acpi.");
return 0;
end;
else begin
writeln("couldn"t enable acpi.");
return -1;
end;
else begin
writeln("no known way to enable acpi.\n");
return -1;
end;
else begin
writeln("acpi was already enabled.\n");
return 0;
end;
end;
//
// 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])
//
procedure initAcpi();
var
^ptr,entries,dsdtlength:integer;
^S5Addr,facp:pointer of char;
begin
^ptr = acpiGetRSDPtr();
// check if address is correct ( if acpi is available on this pc )
if (ptr <> NULL & acpiCheckHeader(ptr, "RSDT") = 0)then begin
// the RSDT contains an unknown number of pointers to acpi tables
entrys = ^(ptr + 1);
entrys = (entrys-36) /4;
ptr += 36/4; // skip header information
while (entrys>0) do begin
// looks like there is a decrement courter weirdly implemented here.
// check if the desired table is reached
if (acpiCheckHeader( ^ptr, "FACP") = 0) then begin
entrys = -2;
^facp = ^ptr; // i think this is right..
if (acpiCheckHeader(facp->DSDT, "DSDT") = 0) then begin
// search the \_S5 package in the DSDT
^S5Addr = facp->DSDT +36; // skip header
dsdtLength= ^(facp->DSDT+1) -36;
while (dsdtLength >0) do begin
// again with the dec counter
if (memcmp(S5Addr, "_S5_", 4) = 0)
break;
inc(S5Addr);
}
// check if \_S5 was found
if (dsdtLength > 0) begin
// check for valid AML structure
if ((^(S5Addr-1) = 0x08 or {||} (^(S5Addr-2) == 0x08 and ^(S5Addr-1) = '\\')) and ^(S5Addr+4) = 0x12) then begin
S5Addr += 5;
S5Addr += ((^S5Addr &0xC0)>>6) +2; // calculate PkgLength size shr(6)?
if (^S5Addr = 0x0A)
inc(S5Addr); // skip byteprefix
SLP_TYPa = ^(S5Addr)<<10; // shl (10)??
inc (S5Addr);
if (^S5Addr = 0x0A)
inc(S5Addr); // skip byteprefix
SLP_TYPb = ^(S5Addr)<<10; //shl(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; // shl(13) ??
SCI_EN = 1;
return 0;
end;
else begin
writeln("_S5 parse error.");
end;
end;
else begin
writeln("_S5 not present.");
end;
end;
else begin
writeln("DSDT invalid.");
end;
end;
inc(ptr);
end;
writeln("no valid FACP present.");
end;
else begin
writeln("no acpi.");
end;
return -1;
end;
procedure acpiPowerOff;
begin
// SCI_EN is set to 1 if acpi shutdown is possible
if (SCI_EN = 0)
return;
acpiEnable();
// send the shutdown command
writeportw(PM1a_CNT, SLP_TYPa | SLP_EN); // what the hell does | (single pipe) mean? there can be only ONE command.
//IE: writeport[b,w,l](port num ,byte[or word])
if (PM1b_CNT <> 0)
writeportw( PM1b_CNT, SLP_TYPb | SLP_EN);
writeln("acpi poweroff failed.");
end;
end.
Re: ACPI poweroff
I think you've been told before, but we are not here to do your homework. Also, please stop copy/pasting with double linewidth, it's quite annoying!frazzledjazz wrote:can someone check the above code to this please?
JAL
Re: ACPI poweroff
Sorry to revive an old thread, but I found this really useful and I have a couple of questions.
For my operating system, Visopsys, I was really only interested in implementing ACPI power off for the time being, but I started reading the spec and implementing the basic stuff, gathering the structures, etc., then I hit the namespace stuff and the bytecode stuff and my head just started spinning. I haven't seen a spec this computer science wanker-ish since I read the USB human interface spec (and I say that as a CompSci guy). This post really helped distill the essential elements, thanks.
I like to give credit when I follow an example like this (normally full name, email address) - can you tell me how I should credit this?
Now to the questions:
On my laptop system, it successfully powers off to the soft-off state, but when the power button is subsequently pressed, all I see is a small cursor, and it requires a hard reset to boot normally. It's almost as if the system expects to wake from that state. Is there something (such as the wakeup vector) that I need to clear, in order to prevent this? Could this relate to the things Cognition mentioned?
Second, on the same laptop system, enabling ACPI as shown seems to interfere with other devices. It almost looks as if there are unserviced interrupts causing problems with (for example) my ATA driver. Should I be implementing an interrupt handler to at least acknowledge any ACPI event interrupts?
Cheers for any help
For my operating system, Visopsys, I was really only interested in implementing ACPI power off for the time being, but I started reading the spec and implementing the basic stuff, gathering the structures, etc., then I hit the namespace stuff and the bytecode stuff and my head just started spinning. I haven't seen a spec this computer science wanker-ish since I read the USB human interface spec (and I say that as a CompSci guy). This post really helped distill the essential elements, thanks.
I like to give credit when I follow an example like this (normally full name, email address) - can you tell me how I should credit this?
Now to the questions:
On my laptop system, it successfully powers off to the soft-off state, but when the power button is subsequently pressed, all I see is a small cursor, and it requires a hard reset to boot normally. It's almost as if the system expects to wake from that state. Is there something (such as the wakeup vector) that I need to clear, in order to prevent this? Could this relate to the things Cognition mentioned?
Second, on the same laptop system, enabling ACPI as shown seems to interfere with other devices. It almost looks as if there are unserviced interrupts causing problems with (for example) my ATA driver. Should I be implementing an interrupt handler to at least acknowledge any ACPI event interrupts?
Cheers for any help
- Brendan
- Member
Member - Posts: 8561
- Joined: Sat Jan 15, 2005 12:00 am
- Location: At his keyboard!
- Contact:
Re: ACPI poweroff
Hi,
When any ACPI events occur, you have to execute the corresponding AML.
Cheers,
Brendan
Cognition is right, but that's only the beginning. You'd have to decipher the entire ACPI specification to get a full picture of how many different ways kaworu's code can break in lots of different situations. For example, the "\_S5" method alone could call a bunch of different methods elsewhere, and can even do different things depending on the current state, or which OS you told ACPI you are, or whatever; and none of that is taken into account. You need a full AML interpreter. You can't extract a few values and use them directly and hope that will work.andymc wrote:On my laptop system, it successfully powers off to the soft-off state, but when the power button is subsequently pressed, all I see is a small cursor, and it requires a hard reset to boot normally. It's almost as if the system expects to wake from that state. Is there something (such as the wakeup vector) that I need to clear, in order to prevent this? Could this relate to the things Cognition mentioned?
In "default mode" the firmware takes care of all the power management stuff for you. This is like having a chauffeur drive your car - you get less control over how things are done, but don't need to worry about the details. When you enable ACPI you're effectively telling the firmware that you will take control over all the power management stuff (with the assistance of the AML). This is like telling the chauffeur that you'll drive yourself (with the assistance of a TomTom navigation system or something). When you enable ACPI and don't take control of all the power management stuff then things go wrong. This is like a car rolling down the street with its cruise control on and nobody behind the wheel.andymc wrote:Second, on the same laptop system, enabling ACPI as shown seems to interfere with other devices. It almost looks as if there are unserviced interrupts causing problems with (for example) my ATA driver. Should I be implementing an interrupt handler to at least acknowledge any ACPI event interrupts?
When any ACPI events occur, you have to execute the corresponding AML.
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.