2010-06-29 3 views
13

Je crée un système d'exploitation en mode protégé basé sur l'architecture x86 d'Intel, et je cherchais des informations sur la façon d'éteindre l'ordinateur via le code d'assemblage, ou quelque chose comme ça. Pourriez-vous m'aider avec ce problème?Comment éteindre l'ordinateur d'un environnement autonome?

+3

http://osdev.org est un endroit agréable à regarder ... Je n'ai jamais eu le code d'arrêt de travailler dans mon propre hobby OS si je ne peux pas donner une bonne réponse – Earlz

+1

duplication possible de [Éteindre l'ordinateur en utilisant l'assembly] (http: // stackoverflow.com/questions/678458/shutdown-the-computer-using-assembly) ou très simmilar –

+2

@Preet, pertinent mais je ne pense pas que ce soit un doublon exact. Cette question demande comment l'arrêter de votre propre système d'exploitation (ou un environnement autonome) où l'autre ne suppose pas que .. et @Carlos, quel mode de processeur êtes-vous? Mode réel, mode protégé ou mode long? (16bit, 32bit ou 64bit) – Earlz

Répondre

8

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

L'arrêt est techniquement une ACPI chose très simple tout ce qui est nécessaire est un outw (PM1a_CNT, SLP_TYPa | SLP_EN); et l'ordinateur est éteint. Le problème réside dans la collecte de ces valeurs d'autant plus que le SLP_TYPa est dans l'objet _S5 qui est dans le DSDT et donc codé AML.

Vous trouverez ci-dessous une simple "carte" d'où trouver ces champs.

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

Pour exporter l'objet \_S5 on utilise normalement un interprète AML mais c'est évidemment pas une option étant donné que nous construisons un système d'exploitation de passe-temps. La solution simple est de scanner le DSDT manuellement. Le langage AML spécifie que les objets _... sont définis une seule fois ce qui rend très simple la recherche de l'objet \_S5 puisqu'un simple memcmp() suffit. Une fois trouvées, les valeurs SLP_TYPx sont extraites.

 
    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 

La collecte des informations est préférable d'effectuer lors de l'initialisation OS car après que vous pouvez réutiliser la RAM et ne pas besoin de se soucier de la corrompre.

Maintenant tout ce qui reste est outw(PM1a_CNT, SLP_TYPa | SLP_EN); et vous êtes parti. Si PM1b_CNT != 0 vous devez le répéter avec b.

Si cela était un peu trop abstrait ici est un code à regarder

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

Pour plus d'informations, lisez les sections correspondantes de la spécification 1.0a ACPI

 
    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 

Cela fonctionne sur tous mes machines bochs et qemu. mais j'ai remarqué que l'on n'a pas besoin d'activer ACPI pour que l'ordinateur s'éteigne. Bien que je ne sais pas si c'est toujours le cas.

Si vous voulez juste jouer un peu. Pour Bochs et qemu il est outw(0xB004, 0x0 | 0x2000);

0

APM méthode testée sur qemu-system-i386 2.0.0 Ubuntu 14.04:

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 

Pour la compilation exacte et étapes en cours d'exécution sur QEMU, see this repo

articles osdev.org : http://wiki.osdev.org/Shutdown, http://wiki.osdev.org/APM

ACPI est la nouvelle méthode, meilleure.