如何从独立环境中关闭计算机?

编程入门 行业动态 更新时间:2024-10-25 04:26:03
本文介绍了如何从独立环境中关闭计算机?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

我正在制作一个基于英特尔 x86 架构的保护模式操作系统,并且正在寻找一些关于如何通过汇编代码或类似的东西关闭计算机电源的信息.你能帮我解决这个问题吗?

解决方案

来自 /forum.osdev/viewtopic.php?t=16990

ACPI 关闭在技术上是一件非常简单的事情,只需要一个 outw(PM1a_CNT, SLP_TYPa | SLP_EN );并且计算机已关闭.问题在于这些值的收集,尤其是因为 SLP_TYPa 位于 DSDT 中的 _S5 对象中,因此进行了 AML 编码.

下面是在哪里可以找到这些字段的简单地图".

RSD PTR"||偏移量 16 处的 RsdtAddress 指针||/RSDT"||偏移量 36 + 4 * n 处的指针(检查 sig "FACP" 的目标以获得正确的 n)||/FACP"||||======||||||PM1a_CNT_BLK;偏移量:64(参见第 4.7.3.2 节)||PM1b_CNT_BLK;偏移量:68||||||/||SLP_TYPx;位 10-12||SLP_EN;位 13||偏移量 40 处的 DSDT 指针||/DSDT"(以某种方式导出 \_S5 对象.)

要导出 \_S5 对象,通常会使用 AML 解释器,但考虑到我们正在构建一个爱好操作系统,这显然不是一种选择.简单的解决方案是手动扫描 DSDT.AML 语言规定 _... 对象只定义一次,这使得查找 \_S5 对象变得非常简单,因为一个简单的 memcmp() 就足够了.一旦找到 SLP_TYPx 值就会被提取出来.

\_S5 对象的字节码-----------------------------------------|(可选)||||名称OP | |_ |小号 |5 |_08 |5A |5F |53 |35 |5F------------------------------------------------------------------------------------------------------------|||( SLP_TYPa ) |(SLP_TYPb) |(保留) |(预订的 )包OP |包装长度 |数字元素 |字节前缀编号 |字节前缀编号 |字节前缀编号 |字节前缀编号12 |0A |04 |0A 05 |0A 05 |0A 05 |0A 05----这个结构也见过----------包OP |包装长度 |数字元素 |12 |06 |04 |00 00 00 00

信息的收集最好在操作系统初始化时进行,因为之后您可以重复使用 ram 而不必担心损坏它.

现在剩下的就是 outw(PM1a_CNT, SLP_TYPa | SLP_EN ); 你走了.如果 PM1b_CNT != 0 你需要用 b 重复它.

如果这有点太抽象,这里有一些代码可以查看

////这里是稍微复杂的 ACPI poweroff 代码//#include <stddef.h>#include <print.h>#include <string.h>#include <io.h>#include <time.h>双字 *SMI_CMD;字节 ACPI_ENABLE;字节 ACPI_DISABLE;双字 *PM1a_CNT;双字*PM1b_CNT;字 SLP_TYPa;字 SLP_TYPb;字 SLP_EN;字SCI_EN;字节 PM1_CNT_LEN;结构体 RSDPtr{字节签名[8];字节校验和;字节 OemID[6];字节修订;dword *RsdtAddress;};结构 FACP{字节签名[4];双字长度;字节 unneded1[40 - 8];双字*DSDT;字节 unneded2[48 - 44];双字 *SMI_CMD;字节 ACPI_ENABLE;字节 ACPI_DISABLE;字节 unneded3[64 - 54];双字*PM1a_CNT_BLK;双字*PM1b_CNT_BLK;字节 unneded4[89 - 72];字节 PM1_CNT_LEN;};//检查给定地址是否具有有效的标头无符号整数 *acpiCheckRSDPtr(无符号整数 *ptr){char *sig = "RSD PTR";结构 RSDPtr *rsdp = (结构 RSDPtr *) ptr;字节 *bptr;字节检查 = 0;诠释我;if (memcmp(sig, rsdp, 8) == 0){//检查校验和 rsdpdbptr = (字节 *) ptr;for (i=0; iRevision == 0)wrstr("acpi 1");别的wrstr("acpi 2");*/return (unsigned int *) rsdp->RsdtAddress;}}返回空值;}//找到 acpi 头并返回 rsdt 的地址无符号整数 *acpiGetRSDPtr(void){无符号整数 * 地址;无符号整数 *rsdp;//在 1mb 标记以下搜索 RSDP 签名for (addr = (unsigned int *) 0x000E0000; (int) addr<0x00100000; addr += 0x10/sizeof(addr)){rsdp = acpiCheckRSDPtr(addr);如果(rsdp!= NULL)返回rsdp;}//地址 0x40:0x0E 是 ebda 的 RM 段int ebda = *((短 *) 0x40E);//获取指针ebda = ebda*0x10 &0x000FFFFF;//将段转换为线性地址//为根系统描述指针签名搜索扩展 BIOS 数据区for (addr = (unsigned int *) ebda; (int) addrDSDT+1) -36;而 (0 0){//检查有效的 AML 结构if ( ( *(S5Addr-1) == 0x08 || ( *(S5Addr-2) == 0x08 && *(S5Addr-1) == '\') ) && *(S5Addr+4) == 0x12){S5Addr += 5;S5Addr += ((*S5Addr &0xC0)>>6) +2;//计算 PkgLength 大小如果(*S5Addr == 0x0A)S5地址++;//跳过字节前缀SLP_TYPa = *(S5Addr)<<10;S5地址++;如果(*S5Addr == 0x0A)S5地址++;//跳过字节前缀SLP_TYPb = *(S5Addr)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;返回0;} 别的 {wrstr("\_S5 解析错误. ");}} 别的 {wrstr("\_S5 不存在. ");}} 别的 {wrstr("DSDT 无效. ");}}指针++;}wrstr("没有有效的 FACP 存在. ");} 别的 {wrstr("没有 acpi. ");}返回-1;}无效 acpiPowerOff(无效​​){//如果 acpi 可以关闭,则 SCI_EN 设置为 1如果(SCI_EN == 0)返回;acpiEnable();//发送关机命令outw((unsigned int) PM1a_CNT, SLP_TYPa | SLP_EN );如果(PM1b_CNT!= 0)outw((unsigned int) PM1b_CNT, SLP_TYPb | SLP_EN );wrstr("acpi 关机失败. ");}

有关更多信息,请阅读 ACPI 1.0a 规范的相应部分

9.1.7 从工作状态过渡到软关机状态7.5.2 \_Sx 状态7.4.1 \_S54.7.2.3 休眠/唤醒控制16.3 AML 字节流字节值16.2.3 包长度编码

这适用于我所有的机器 bochs 和 qemu.但我注意到不需要启用 ACPI 来让电脑关机.虽然我不知道是否总是这样.

如果你只是想玩一点.对于 bochs 和 qemu,它是 outw( 0xB004, 0x0 | 0x2000 );

I'm making a protected-mode OS based on Intel's x86 architecture, and was looking for some information on how to power off the computer via assembly code, or something like that. Could you help me with this problem?

解决方案

from forum.osdev/viewtopic.php?t=16990

The ACPI shutdown is technically a really simple thing all that is needed is a outw(PM1a_CNT, SLP_TYPa | SLP_EN ); and the computer is powered off. The problem lies in the gathering of these values especially since the SLP_TYPa is in the _S5 object which is in the DSDT and therefore AML encoded.

Below is a simple "map" of where to find these fields.

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

To export the \_S5 object one would normally use an AML interpreter but that's obviously not an option considering we're building a hobby OS. The simple solution is to scan the DSDT manually. The AML language specifies that _... objects are defined only once which makes it very simple to find the \_S5 object since a simple memcmp() is enough. Once found the SLP_TYPx values are extracted.

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

The gathering of the information is best performed at OS initialization because after that you can reuse the ram and don't need to worry about corrupting it.

Now all that remains is outw(PM1a_CNT, SLP_TYPa | SLP_EN ); and you're gone. If PM1b_CNT != 0 you need to repeat it with b.

If that was a little too abstract here is some code to look at

// // 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. "); return 0; } else { wrstr("couldn't enable acpi. "); return -1; } } else { wrstr("no known way to enable acpi. "); return -1; } } else { //wrstr("acpi was already enabled. "); 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. "); } } else { wrstr("\_S5 not present. "); } } else { wrstr("DSDT invalid. "); } } ptr++; } wrstr("no valid FACP present. "); } else { wrstr("no acpi. "); } 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. "); }

For further information read the corresponding sections of the ACPI 1.0a specification

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

This works on all of my machines bochs and qemu. but I noticed that one needn't enable ACPI for the pc to power down. Though i don't know if this is always the case.

If you just want to play a little. For bochs and qemu it's outw( 0xB004, 0x0 | 0x2000 );

更多推荐

如何从独立环境中关闭计算机?

本文发布于:2023-11-02 13:47:57,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1552562.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:独立   关闭计算机   环境

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!