Code: Select all
unit acpi.pas:
//
// 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;
//did this in assembler thanks to DJ Delorie... --Jazz
//delorie's code causes triple-fault. we are not powering off.[emu or otherwise]
//only thing i can think as to why is we are not in real mode.
//this is software controlled version with safeguards
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.
if (PM1b_CNT <> 0)
writeportw( PM1b_CNT, SLP_TYPb | SLP_EN);
writeln("acpi poweroff failed.");
end;
end.{unit}
no, im already certified a 'nut'.thank you.