2010-06-29 8 views
13

Ich mache ein Protected-Mode-Betriebssystem, das auf Intels x86-Architektur basiert, und suchte nach Informationen, wie man den Computer über Assembler-Code oder etwas ähnliches abschaltet. Könnten Sie mir bei diesem Problem helfen?Wie schalte ich den Computer aus einer freistehenden Umgebung aus?

+3

http://osdev.org ist ein schöner Ort zum Anschauen ... Ich habe nie Abschaltcode in meinem eigenen Hobby-Betriebssystem zu arbeiten, obwohl so kann ich keine gute Antwort geben – Earlz

+1

mögliche Duplikate von [Herunterfahren des Computers Assembly verwenden] (http: // stackoverflow.com/questions/678458/shutdown-the-computer-using-assembly) oder sehr ähnlich –

+2

@Preet, relevant, aber ich glaube nicht, dass dies ein exaktes Duplikat ist. Diese Frage fragt, wie man es von Ihrem eigenen Betriebssystem (oder einer freistehenden Umgebung) herunterfahren lässt, wobei der andere nicht davon ausgeht, dass ... und @Carlos, in welchem ​​Prozessormodus Sie sich befinden? Real-Modus, Protected-Modus oder Long-Modus? (16bit, 32bit oder 64bit) – Earlz

Antwort

8

von http://forum.osdev.org/viewtopic.php?t=16990

Die ACPI Shutdown ist technisch eine wirklich einfache Sache alles, was benötigt wird, ist ein OUTW (PM1a_CNT, SLP_TYPa | SLP_EN); und der Computer ist ausgeschaltet. Das Problem liegt in der Erfassung dieser Werte, besonders da SLP_TYPa in dem _S5-Objekt ist, das in der DSDT und daher AML-codiert ist.

Im Folgenden finden Sie eine einfache "Karte", wo Sie diese Felder finden können.

 
    "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.) 

das \_S5 Objekt exportieren, die man normalerweise ein AML-Interpreter verwenden würde, aber das ist natürlich keine Option bedenkt, dass wir ein Hobby OS Gebäude sind. Die einfache Lösung besteht darin, den DSDT manuell zu scannen. Die AML-Sprache spezifiziert, dass _... Objekte nur einmal definiert werden, was es sehr einfach macht, das \_S5 Objekt zu finden, da ein einfaches memcmp() ausreicht. Einmal gefunden werden die SLP_TYPx Werte extrahiert.

 
    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 

Das Sammeln der Informationen wird am besten bei OS Initialisierung durchgeführt, da nach, dass Sie den Stempel wiederverwenden können und müssen es nicht zu korrumpieren kümmern.

Jetzt ist nur noch outw(PM1a_CNT, SLP_TYPa | SLP_EN); und du bist weg. Wenn PM1b_CNT != 0 Sie es mit b wiederholen müssen.

Wenn das war ein wenig zu abstrakt hier ist ein Code bei

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

Für weitere Informationen zu suchen, um die entsprechenden Abschnitte der ACPI 1.0a Spezifikation lesen

 
    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 

Dieses auf alle Werke von Meine Maschinen Bochs und Qemu. aber ich bemerkte, dass man ACPI nicht aktivieren muss, damit der PC ausschaltet. Obwohl ich nicht weiß, ob das immer so ist.

Wenn Sie nur ein wenig spielen möchten. Für bochs und QEMU auf qemu-system-i386 2.0.0 Ubuntu 14.04 getestet outw(0xB004, 0x0 | 0x2000);

0

APM Methode ist:

mov $0x5301, %ax 
xor %bx, %bx 
int $0x15 

/* Try to set apm version (to 1.2). */ 
mov $0x530e, %ax 
xor %bx, %bx 
mov $0x0102, %cx 
int $0x15 

/* Turn off the system. */ 
mov $0x5307, %ax 
mov $0x0001, %bx 
mov $0x0003, %cx 
int $0x15 

Für die genaue Zusammenstellung und Laufschritte auf QEMU,

osdev.org Artikel see this repo : http://wiki.osdev.org/Shutdown, http://wiki.osdev.org/APM

ACPI ist die neuere, bessere Methode.