¿Cómo apagar la computadora desde un entorno independiente?

Estoy haciendo un sistema operativo en modo protegido basado en la architecture x86 de Intel, y estaba buscando información sobre cómo apagar la computadora a través del código ensamblador, o algo así. ¿Podrías ayudarme con este problema?

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

El cierre de ACPI es técnicamente una cosa realmente simple, todo lo que se necesita es un outw (PM1a_CNT, SLP_TYPa | SLP_EN); y la computadora está apagada. El problema radica en la recostackción de estos valores, especialmente dado que SLP_TYPa está en el objeto _S5 que está en el DSDT y, por lo tanto, codificado en AML.

A continuación se muestra un “mapa” simple de dónde encontrar estos campos.

     "RSD PTR"
       ||
     RsdtDirector de dirección en el desplazamiento 16
       ||
       \ /
     "RSDT"
       ||
     puntero en offset 36 + 4 * n (verifique el objective para el sig "FACP" para obtener el n correcto)
       ||
       \ /
     "FACP"
       ||
       || ===== \
       ||  ||
       ||  PM1a_CNT_BLK;  desplazamiento: 64 (ver sección 4.7.3.2)
       ||  PM1b_CNT_BLK;  desplazamiento: 68
       ||  ||
       ||  \ /
       ||  SLP_TYPx;  bit 10-12
       ||  SLP_EN;  bit 13
       ||
     Puntero DSDT en offset 40
       ||
       \ /
     "DSDT" (exportar el objeto \ _S5 de alguna manera).

Para exportar el objeto \_S5 uno normalmente usaría un intérprete AML, pero obviamente no es una opción, considerando que estamos construyendo un sistema operativo hobby. La solución simple es escanear el DSDT manualmente. El lenguaje AML especifica que _… los objetos se definen solo una vez, lo que hace que sea muy simple encontrar el objeto \_S5 dado que un simple memcmp() es suficiente. Una vez encontrados, se extraen los valores de SLP_TYPx .

     bytecode del objeto \ _S5
     -----------------------------------------
             |  (opcional) |  |  |  |
     NameOP |  \ |  _ |  S |  5 |  _
     08 |  5A |  5F |  53 |  35 |  5F

     -------------------------------------------------- -------------------------------------------------- -------
                |  |  |  (SLP_TYPa) |  (SLP_TYPb) |  (Reservado) |  (Reservado)
     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 recostackción de la información se realiza mejor en la inicialización del SO porque después de eso puede reutilizar el RAM y no necesita preocuparse por corromperlo.

Ahora todo lo que queda es outw(PM1a_CNT, SLP_TYPa | SLP_EN ); y te has ido Si PM1b_CNT != 0 necesita repetirlo con b.

Si eso fuera un poco demasiado abstracto aquí hay un código para mirar

 // // here is the slighlty complicated ACPI poweroff code // #include  #include  #include  #include  #include  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; iRevision == 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) addrDSDT, "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"); } 

Para más información lea las secciones correspondientes de la especificación ACPI 1.0a

     9.1.7 Transición del estado de trabajo al apagado
     7.5.2 \ _Sx estados
     7.4.1 \ _S5
     4.7.2.3 Sleeping / Wake Control

     16.3 valores de Byte Byree Byte de AML
     16.2.3 Codificación de longitud del paquete

Esto funciona en todas mis máquinas bochs y qemu. pero noté que uno no necesita habilitar ACPI para que la PC se apague. Aunque no sé si este es siempre el caso.

Si solo quieres jugar un poco. Para bochs y qemu es outw( 0xB004, 0x0 | 0x2000 );

Método APM probado en 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 

Para la comstackción exacta y los pasos de ejecución en QEMU, consulte este informe.

Artículos de osdev.org: http://wiki.osdev.org/Shutdown , http://wiki.osdev.org/APM

ACPI es el método más nuevo y mejor.