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?
Antwort
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);
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.
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
mögliche Duplikate von [Herunterfahren des Computers Assembly verwenden] (http: // stackoverflow.com/questions/678458/shutdown-the-computer-using-assembly) oder sehr ähnlich –
@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