OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Mar 28, 2024 2:01 pm

All times are UTC - 6 hours




Post new topic This topic is locked, you cannot edit posts or make further replies.  [ 34 posts ]  Go to page 1, 2, 3  Next
Author Message
 Post subject: ACPI poweroff
PostPosted: Tue May 13, 2008 6:34 am 
Offline

Joined: Tue May 13, 2008 2:55 am
Posts: 4
Location: Germany
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.
Code:
"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.)


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.

Code:
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


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
Code:
//
// 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");
}


For further information read the corresponding sections of the acpi 1.0a specification

Code:
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


#############################################

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 );


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 13, 2008 7:11 am 
Offline
Member
Member

Joined: Tue Apr 15, 2008 6:37 pm
Posts: 191
Location: Gotham, Batmanistan
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.


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 13, 2008 10:12 am 
Offline

Joined: Tue May 13, 2008 2:55 am
Posts: 4
Location: Germany
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


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 13, 2008 12:33 pm 
Offline
Member
Member
User avatar

Joined: Fri Aug 03, 2007 6:03 am
Posts: 536
Location: Cambridge, UK
That looks very useful, do you think it could be added to the wiki?

_________________
~ Lukem95 [ Cake ]
Release: 0.08b
Image


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 13, 2008 8:17 pm 
Offline
Member
Member
User avatar

Joined: Thu Dec 21, 2006 7:42 pm
Posts: 1391
Location: Unknown. Momentum is pretty certain, however.
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?
Code:
initAcpi();
Is all you need, right?

-JL

_________________
SeaOS: Adding VT-x, networking, and ARM support
dbittman on IRC, @danielbittman on twitter
https://dbittman.github.io


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 14, 2008 5:49 am 
Offline

Joined: Tue May 13, 2008 2:55 am
Posts: 4
Location: Germany
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.


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 14, 2008 8:35 am 
Offline
Member
Member
User avatar

Joined: Thu Dec 21, 2006 7:42 pm
Posts: 1391
Location: Unknown. Momentum is pretty certain, however.
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

_________________
SeaOS: Adding VT-x, networking, and ARM support
dbittman on IRC, @danielbittman on twitter
https://dbittman.github.io


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 14, 2008 4:46 pm 
Offline

Joined: Tue May 13, 2008 2:55 am
Posts: 4
Location: Germany
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.


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 14, 2008 6:05 pm 
Offline
Member
Member
User avatar

Joined: Thu Dec 21, 2006 7:42 pm
Posts: 1391
Location: Unknown. Momentum is pretty certain, however.
Well, it doesn't work for me. Maybe it's my memcmp function, thats the only one I didn't have already:
Code:
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;
}

Does it need more or is it just completly messed up?

-JL

_________________
SeaOS: Adding VT-x, networking, and ARM support
dbittman on IRC, @danielbittman on twitter
https://dbittman.github.io


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 14, 2008 6:53 pm 
Offline
Member
Member
User avatar

Joined: Tue Oct 17, 2006 9:29 pm
Posts: 2426
Location: Canada
piranha wrote:
Well, it doesn't work for me. Maybe it's my memcmp function, thats the only one I didn't have already:
Code:
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;
}

Does it need more or is it just completly messed up?

-JL
That's not even a standards compliant implementation.. heck, it's not even a working implementation.

Do you even read standards and stress test the functions you write? :roll:

_________________
Image
Twitter: @canadianbryan. Award by smcerm, I stole it. Original was larger.


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 14, 2008 7:20 pm 
Offline
Member
Member
User avatar

Joined: Thu Dec 21, 2006 7:42 pm
Posts: 1391
Location: Unknown. Momentum is pretty certain, however.
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:

_________________
SeaOS: Adding VT-x, networking, and ARM support
dbittman on IRC, @danielbittman on twitter
https://dbittman.github.io


Top
 Profile  
 
 Post subject: Re: ACPI poweroff
PostPosted: Sun Mar 08, 2009 4:50 am 
Offline

Joined: Tue Mar 03, 2009 3:52 pm
Posts: 7
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.

Code:
//

// 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.



Top
 Profile  
 
 Post subject: Re: ACPI poweroff
PostPosted: Mon Mar 09, 2009 4:45 am 
Offline
Member
Member

Joined: Wed Oct 31, 2007 9:09 am
Posts: 1385
frazzledjazz wrote:
can someone check the above code to this please?


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!


JAL


Top
 Profile  
 
 Post subject: Re: ACPI poweroff
PostPosted: Sun Feb 27, 2011 8:18 pm 
Offline
Member
Member
User avatar

Joined: Tue Feb 01, 2011 6:18 pm
Posts: 31
Location: London, UK
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


Top
 Profile  
 
 Post subject: Re: ACPI poweroff
PostPosted: Sun Feb 27, 2011 9:41 pm 
Offline
Member
Member
User avatar

Joined: Sat Jan 15, 2005 12:00 am
Posts: 8561
Location: At his keyboard!
Hi,

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?


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:
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?


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.

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.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic This topic is locked, you cannot edit posts or make further replies.  [ 34 posts ]  Go to page 1, 2, 3  Next

All times are UTC - 6 hours


Who is online

Users browsing this forum: DotBot [Bot] and 61 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group