2010-06-29 2 views
13

Я создаю OS в защищенном режиме на основе архитектуры x86 от Intel и искал некоторую информацию о том, как отключить компьютер через код сборки или что-то в этом роде. Не могли бы вы помочь мне с этой проблемой?Как отключить компьютер от автономной среды?

+3

http://osdev.org - приятное место для просмотра ... У меня никогда не было кода завершения работы в моей собственной операционной системе хобби, хотя я не могу дать хороший ответ – Earlz

+1

Возможный дубликат [Выключение компьютера используя сборку] (http: // stackoverflow.com/questions/678458/shutdown-the-computer-using-assembly) или очень симпатичный –

+2

@Preet, актуальный, но я не думаю, что это точный дубликат. Этот вопрос спрашивает, как заставить его отключиться от вашей собственной ОС (или автономной среды), где другой не предполагает, что .. и @Carlos, в каком режиме процессора вы находитесь? Режим реального режима, защищенный режим или длинный режим? (16 бит, 32 бит или 64 бит) – Earlz

ответ

8

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

Отключение ACPI технически очень простая вещь, все, что требуется, это outw (PM1a_CNT, SLP_TYPa | SLP_EN); и компьютер выключен. Проблема заключается в сборе этих значений, особенно потому, что SLP_TYPa находится в объекте _S5, который находится в DSDT и поэтому кодируется AML.

Ниже приведена простая «карта», где можно найти эти поля.

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

Чтобы экспортировать \_S5 объект один, как правило, использовать интерпретатор AML, но это явно не вариант, учитывая, что мы строим хобби ОС. Простым решением является сканирование DSDT вручную. Язык AML указывает, что объекты _... определены только один раз, что очень упрощает поиск объекта \_S5, так как достаточно простого memcmp(). После обнаружения извлекаются значения SLP_TYPx.

 
    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 

Сбор информации лучше всего выполняются при инициализации ОС, так как после того, что вы можете использовать баран и не нужно беспокоиться о развращении ее.

Теперь все, что осталось, это outw(PM1a_CNT, SLP_TYPa | SLP_EN);, и вы ушли. Если PM1b_CNT != 0, вам нужно повторить его с помощью b.

Если бы это было немного слишком абстрактными здесь какой-то код, чтобы посмотреть на

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

Для получения более подробной информации читайте соответствующие разделы спецификации ACPI 1.0a

 
    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 

Это работает на всех мои машины bochs и qemu. , но я заметил, что не нужно включать ACPI для питания компьютера. Хотя я не знаю, всегда ли это так.

Если вы просто хотите немного поиграть. Для Bochs и в Qemu это outw(0xB004, 0x0 | 0x2000);

0

APM Метод испытания на 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 

Для точного составления и текущих шагов по QEMU, see this repo

osdev.org статьи : http://wiki.osdev.org/Shutdown, http://wiki.osdev.org/APM

ACPI - это новый, лучший метод.