如何在 LattePanda 上启用 ACPI(用于关机)
How to enable ACPI (for powering off) on LattePanda
我编写此代码是为了使用 ACPI 从我从 64 位 UEFI 启动的程序中关闭计算机。
(对不起,代码很长,但我认为所有部分都是必要的)
#include <stdint.h>
#define in8(data, port) __asm__ __volatile__ \
("xor %%eax, %%eax\n\tin %%dx, %%al\n\t" : "=a"(data) : "d"(port))
#define in16(data, port) __asm__ __volatile__ \
("xor %%eax, %%eax\n\tin %%dx, %%ax\n\t" : "=a"(data) : "d"(port))
#define out8(data, port) __asm__ __volatile__ \
("out %%al, %%dx\n\t" : : "a"(data), "d"(port))
#define out16(data, port) __asm__ __volatile__ \
("out %%ax, %%dx\n\t" : : "a"(data), "d"(port))
void initSerialPort(void) {
/* set speed to 115200bps */
out8(0x83, 0x03FB); /* enable LSB/MSB register */
out8(0x00, 0x03F9); /* MSB */
out8(0x01, 0x03F8); /* LSB */
/* set line info */
out8(0x03, 0x03FB); /* disable LSB/MSB register, no parity, no 2-bit stop, 8-bit data */
/* set modem info */
out8(0x03, 0x03FC); /* no loopback, no interrupt, RTS on, DTR on */
/* enable FIFO */
out8(0x07, 0x03FA); /* interrupt at 1 byte, clear TX, clear RX, enable */
/* disable interrupt */
out8(0x00, 0x03F9);
}
void printChar(int c) {
int status;
/* wait until TX buffer is empty */
do {
in8(status, 0x03FD);
} while (!(status & 0x20));
/* put data to send */
out8(c, 0x03F8);
}
void printString(const char* str) {
while (*str != '[=10=]') printChar((unsigned char)*(str++));
}
void printInt(uint64_t value, int radix) {
char vStr[128] = "";
char* pStr = vStr + 120;
do {
*(pStr--) = "0123456789ABCDEF"[value % radix];
value /= radix;
} while (value > 0);
printString(pStr + 1);
}
void stop(void) {
__asm__ __volatile__(
"cli\n\t"
"1:\n\t"
"hlt\n\t"
"jmp 1b\n\t"
);
}
char* searchPuttern(char* text, uint64_t textSize, const char* puttern, uint64_t putternSize) {
uint64_t i, j;
if (textSize < putternSize) return 0;
for (i = 0; i <= textSize - putternSize; i++) {
int ok = 1;
for (j = 0; j < putternSize; j++) {
if (text[i + j] != puttern[j]) {
ok = 0;
break;
}
}
if (ok) return text + i;
}
return 0;
}
void entry(void* unused, uint64_t* table) {
uint64_t* guidTable;
uint64_t guidTableNumElements;
uint64_t i;
uint64_t* rsdp;
uint32_t* rsdt, *xsdt;
uint32_t* fadt_from_rsdt, *fadt, *dsdt;
uint64_t xsdtNumElements;
uint32_t smi_cmd, acpi_enable, pm1a_cnt_blk, pm1b_cnt_blk;
uint32_t amlLength;
char* aml, *s5;
int pm1a_value, pm1b_value;
int status;
int printLimit;
(void)unused;
initSerialPort();
/* parse things to get required values */
printString("argument table = 0x"); printInt((uint64_t)table, 16); printChar('\n');
guidTable = (uint64_t*)table[14];
guidTableNumElements = table[13];
printString("GUID table = 0x"); printInt((uint64_t)guidTable, 16); printChar('\n');
printString("GUID table size = "); printInt(guidTableNumElements, 10); printChar('\n');
rsdp = 0;
for (i = 0; i < guidTableNumElements; i++) {
if (guidTable[i * 3 + 0] == UINT64_C(0x11D3E4F18868E871) &&
guidTable[i * 3 + 1] == UINT64_C(0x81883CC7800022BC)) {
rsdp = (uint64_t*)guidTable[i * 3 + 2];
break;
}
}
if (rsdp == 0) {
printString("RSDP not found\n");
stop();
}
printString("RSDP = 0x"); printInt((uint64_t)rsdp, 16); printChar('\n');
printString("RSDP signature = 0x"); printInt(rsdp[0], 16); printChar('\n');
rsdt = (uint32_t*)(rsdp[2] & UINT64_C(0xffffffff));
xsdt = (uint32_t*)rsdp[3];
printString("RSDT = 0x"); printInt((uint64_t)rsdt, 16); printChar('\n');
fadt_from_rsdt = 0;
if (rsdt != 0) {
uint64_t rsdtNumElements;
printString("RSDT signature = 0x"); printInt(rsdt[0], 16); printChar('\n');
printString("RSDT length = "); printInt(rsdt[1], 10); printChar('\n');
rsdtNumElements = (rsdt[1] - 36) / 4;
fadt = 0;
for (i = 0; i < rsdtNumElements; i++) {
uint32_t* addr = (uint32_t*)(uint64_t)rsdt[9 + i];
printString("*0x"); printInt((uint64_t)addr, 16);
printString(" = 0x"); printInt(addr[0], 16);
printChar('\n');
if ((addr[0] & UINT64_C(0xffffffff)) == UINT64_C(0x50434146)) {
fadt_from_rsdt = addr;
}
}
}
printString("XSDT = 0x"); printInt((uint64_t)xsdt, 16); printChar('\n');
if (xsdt == 0) {
printString("XSDT not found\n");
stop();
}
printString("XSDT signature = 0x"); printInt(xsdt[0], 16); printChar('\n');
printString("XSDT length = "); printInt(xsdt[1], 10); printChar('\n');
xsdtNumElements = (xsdt[1] - 36) / 8;
fadt = 0;
for (i = 0; i < xsdtNumElements; i++) {
uint32_t* addr = (uint32_t*)((uint64_t*)&xsdt[9])[i];
printString("*0x"); printInt((uint64_t)addr, 16);
printString(" = 0x"); printInt(addr[0], 16);
printChar('\n');
if ((addr[0] & UINT64_C(0xffffffff)) == UINT64_C(0x50434146)) {
fadt = addr;
}
}
printString("FADT from RSDT = 0x"); printInt((uint64_t)fadt_from_rsdt, 16); printChar('\n');
printString("FADT from XSDT = 0x"); printInt((uint64_t)fadt, 16); printChar('\n');
if (fadt == 0) fadt = fadt_from_rsdt;
if (fadt == 0) {
printString("FADT not found\n");
stop();
}
printString("FADT = 0x"); printInt((uint64_t)fadt, 16); printChar('\n');
printString("FADT signature = 0x"); printInt(fadt[0], 16); printChar('\n');
printString("FADT length = "); printInt(fadt[1], 10); printChar('\n');
dsdt = (uint32_t*)(uint64_t)fadt[10];
smi_cmd = fadt[12];
acpi_enable = fadt[13] & 0xff;
pm1a_cnt_blk = fadt[16];
pm1b_cnt_blk = fadt[17];
printString("DSDT = 0x"); printInt((uint64_t)dsdt, 16); printChar('\n');
printString("SMI_CMD = 0x"); printInt(smi_cmd, 16); printChar('\n');
printString("ACPI_ENABLE = 0x"); printInt(acpi_enable, 16); printChar('\n');
printString("PM1a_CNT_BLK = 0x"); printInt(pm1a_cnt_blk, 16); printChar('\n');
printString("PM1b_CNT_BLK = 0x"); printInt(pm1b_cnt_blk, 16); printChar('\n');
printString("PM1_CNT_LEN = "); printInt((fadt[22] >> 8) & 0xff, 10); printChar('\n');
if (fadt[1] >= 148) {
printString("X_DSDT = 0x");
printInt(fadt[35] | ((uint64_t)fadt[36] << 32), 16);
printChar('\n');
}
if (fadt[1] >= 184) {
printString("X_PM1a_CNT_BLK = 0x"); printInt(fadt[43], 16);
printString(" 0x"); printInt(fadt[44], 16);
printString(" 0x"); printInt(fadt[45], 16);
printChar('\n');
}
if (fadt[1] >= 196) {
printString("X_PM1b_CNT_BLK = 0x"); printInt(fadt[46], 16);
printString(" 0x"); printInt(fadt[47], 16);
printString(" 0x"); printInt(fadt[48], 16);
printChar('\n');
}
printString("DSDT signature = 0x"); printInt(dsdt[0], 16); printChar('\n');
printString("DSDT length = "); printInt(dsdt[1], 10); printChar('\n');
if (dsdt[1] < 36) {
printString("DSDT too short\n");
stop();
}
amlLength = dsdt[1] - 36;
aml = (char*)&dsdt[9];
s5 = searchPuttern(aml, amlLength, "\x08_S5_\x12", 6);
if (s5 == 0) {
printString("_S5_ not found\n");
stop();
}
printString("_S5_ dump:\n");
for (i = 0; i < 16; i++) {
if (i > 0) printChar(' ');
printString("0x"); printInt((unsigned char)s5[i], 16);
}
printChar('\n');
if (s5[8] == 0) {
pm1a_value = 0;
if (s5[9] == 0) pm1b_value = 0; else pm1b_value = (unsigned char)s5[10];
} else {
pm1a_value = (unsigned char)s5[9];
if (s5[10] == 0) pm1b_value = 0; else pm1b_value = (unsigned char)s5[11];
}
printString("value for PM1a_CNT.SLP_TYP = 0x"); printInt(pm1a_value, 16); printChar('\n');
printString("value for PM1b_CNT.SLP_TYP = 0x"); printInt(pm1b_value, 16); printChar('\n');
/* enable ACPI for powering off */
in16(status, pm1a_cnt_blk);
printString("status = 0x"); printInt(status, 16); printChar('\n');
if (!(status & 1)) {
printString("sending ACPI_ENABLE to SMI_CMD\n");
out8(acpi_enable, smi_cmd);
printLimit = 10;
do {
in16(status, pm1a_cnt_blk);
if (printLimit-- > 0) {
printString("status = 0x"); printInt(status, 16); printChar('\n');
}
} while (!(status & 1));
}
/* power off */
printString("switching state\n");
in16(status, pm1a_cnt_blk);
out16((status & ~(7 << 10)) | ((pm1a_value & 7) << 10) | (1 << 13), pm1a_cnt_blk);
if (pm1b_cnt_blk != 0) {
in16(status, pm1b_cnt_blk);
out16((status & ~(7 << 10)) | ((pm1b_value & 7) << 10) | (1 << 13), pm1b_cnt_blk);
}
printString("state switch sent\n");
stop();
}
我用 TDM-GCC (gcc (tdm64-1) 9.2.0
) 编译了这个。
编译命令为:
C:\MyInstalledApps\TDM-GCC-64\bin\gcc -Wall -Wextra -nostdlib -e entry -m64 -Wl,--subsystem=10 acpi_test.c -o bootx64.efi
它没有给我任何警告或错误消息。
我在我的 LattePanda(使用 2G RAM 和 32G eMMC)上试过这段代码,但它无法关闭电源。查看跟踪,似乎无法通过将 ACPI_ENABLE
发送到端口 SMI_CMD
来启用 ACPI。
我的意思是 PM1a_CNT_BLK
寄存器中的 SCI_EN
位似乎没有设置。
我也在 QEMU 和 VirtualBox 上尝试了这段代码并成功关闭电源,但它看起来没有帮助,因为它似乎已经在这些 VM 软件中启用了 ACPI。
我的 QEMU 版本是 QEMU emulator version 5.1.92 (v5.2.0-rc2-11843-gf571c4ffb5-dirty)
并使用 GitHub - BlankOn/ovmf-blobs: BIOS.bin for qemu to support UEFI 作为 BIOS。
根据串口的输出,ACPI_ENABLE
和SMI_CMD
的值看起来比较合理。 (不知道对不对)
我是不是忽略了什么?我应该如何在 LattePanda 上启用 ACPI 并关闭我从 UEFI 启动的程序?
LattePanda 的串口输出:
argument table = 0x7BA13F18
GUID table = 0x7BA12E18
GUID table size = 13
RSDP = 0x7B12E000
RSDP signature = 0x2052545020445352
RSDT = 0x7B12E028
RSDT signature = 0x54445352
RSDT length = 124
*0x7B12E188 = 0x50434146
*0x7B14AB40 = 0x43495041
*0x7B14ABC8 = 0x54445046
*0x7B14AC10 = 0x54444946
*0x7B14ACB0 = 0x4D44534D
*0x7B14AD08 = 0x4746434D
*0x7B14AD48 = 0x54445353
*0x7B14EEC0 = 0x54445353
*0x7B14F518 = 0x54445353
*0x7B14F570 = 0x49464555
*0x7B14F5B8 = 0x54445353
*0x7B14F828 = 0x54455048
*0x7B14F860 = 0x54445353
*0x7B14FFC8 = 0x54445353
*0x7B150258 = 0x54445353
*0x7B1503D8 = 0x5449504C
*0x7B1504E0 = 0x47464342
*0x7B150620 = 0x4D415250
*0x7B150650 = 0x54524742
*0x7B150688 = 0x324D5054
*0x7B1506C0 = 0x54525343
*0x7B150810 = 0x54414457
XSDT = 0x7B12E0A8
XSDT signature = 0x54445358
XSDT length = 212
*0x7B14AA30 = 0x50434146
*0x7B14AB40 = 0x43495041
*0x7B14ABC8 = 0x54445046
*0x7B14AC10 = 0x54444946
*0x7B14ACB0 = 0x4D44534D
*0x7B14AD08 = 0x4746434D
*0x7B14AD48 = 0x54445353
*0x7B14EEC0 = 0x54445353
*0x7B14F518 = 0x54445353
*0x7B14F570 = 0x49464555
*0x7B14F5B8 = 0x54445353
*0x7B14F828 = 0x54455048
*0x7B14F860 = 0x54445353
*0x7B14FFC8 = 0x54445353
*0x7B150258 = 0x54445353
*0x7B1503D8 = 0x5449504C
*0x7B1504E0 = 0x47464342
*0x7B150620 = 0x4D415250
*0x7B150650 = 0x54524742
*0x7B150688 = 0x324D5054
*0x7B1506C0 = 0x54525343
*0x7B150810 = 0x54414457
FADT from RSDT = 0x7B12E188
FADT from XSDT = 0x7B14AA30
FADT = 0x7B14AA30
FADT signature = 0x50434146
FADT length = 268
DSDT = 0x7B12E210
SMI_CMD = 0xB2
ACPI_ENABLE = 0xA0
PM1a_CNT_BLK = 0x404
PM1b_CNT_BLK = 0x0
PM1_CNT_LEN = 2
X_DSDT = 0x7B12E210
X_PM1a_CNT_BLK = 0x2001001 0x404 0x0
X_PM1b_CNT_BLK = 0x1 0x0 0x0
DSDT signature = 0x54445344
DSDT length = 116766
_S5_ dump:
0x8 0x5F 0x53 0x35 0x5F 0x12 0x7 0x4 0xA 0x7 0x0 0x0 0x0 0x14 0x1E 0x5F
value for PM1a_CNT.SLP_TYP = 0x7
value for PM1b_CNT.SLP_TYP = 0x0
status = 0x0
sending ACPI_ENABLE to SMI_CMD
status = 0x0
status = 0x0
status = 0x0
status = 0x0
status = 0x0
status = 0x0
status = 0x0
status = 0x0
status = 0x0
status = 0x0
QEMU 的串口输出:
argument table = 0x7EED018
GUID table = 0x7EED0D8
GUID table size = 9
RSDP = 0x7EF9014
RSDP signature = 0x2052545020445352
RSDT = 0x7EF8074
RSDT signature = 0x54445352
RSDT length = 52
*0x7EF5000 = 0x50434146
*0x7EF4000 = 0x43495041
*0x7EF3000 = 0x54455048
*0x7EF2000 = 0x54454157
XSDT = 0x7EF80E8
XSDT signature = 0x54445358
XSDT length = 68
*0x7EF5000 = 0x50434146
*0x7EF4000 = 0x43495041
*0x7EF3000 = 0x54455048
*0x7EF2000 = 0x54454157
FADT from RSDT = 0x7EF5000
FADT from XSDT = 0x7EF5000
FADT = 0x7EF5000
FADT signature = 0x50434146
FADT length = 116
DSDT = 0x7EF6000
SMI_CMD = 0xB2
ACPI_ENABLE = 0xF1
PM1a_CNT_BLK = 0xB004
PM1b_CNT_BLK = 0x0
PM1_CNT_LEN = 2
DSDT signature = 0x54445344
DSDT length = 5060
_S5_ dump:
0x8 0x5F 0x53 0x35 0x5F 0x12 0x6 0x4 0x0 0x0 0x0 0x0 0x10 0x3B 0x5C 0x2E
value for PM1a_CNT.SLP_TYP = 0x0
value for PM1b_CNT.SLP_TYP = 0x0
status = 0x1
switching state
state switch sent
VirtualBox 6.1.18 r142142 (Qt5.6.2) 的串口输出:
argument table = 0x6FEE018
GUID table = 0x6FEEC98
GUID table size = 10
RSDP = 0x6FFA014
RSDP signature = 0x2052545020445352
RSDT = 0x6FF9074
RSDT signature = 0x54445352
RSDT length = 52
*0x6FF7000 = 0x50434146
*0x6FF8000 = 0x43495041
*0x6FF3000 = 0x54445353
*0x5E39000 = 0x54524742
XSDT = 0x6FF90E8
XSDT signature = 0x54445358
XSDT length = 68
*0x6FF7000 = 0x50434146
*0x6FF8000 = 0x43495041
*0x6FF3000 = 0x54445353
*0x5E39000 = 0x54524742
FADT from RSDT = 0x6FF7000
FADT from XSDT = 0x6FF7000
FADT = 0x6FF7000
FADT signature = 0x50434146
FADT length = 244
DSDT = 0x6FF4000
SMI_CMD = 0x442E
ACPI_ENABLE = 0xA1
PM1a_CNT_BLK = 0xB004
PM1b_CNT_BLK = 0x0
PM1_CNT_LEN = 2
X_DSDT = 0x6FF4000
X_PM1a_CNT_BLK = 0x2001001 0xB004 0x0
X_PM1b_CNT_BLK = 0x0 0x0 0x0
DSDT signature = 0x54445344
DSDT length = 8997
_S5_ dump:
0x8 0x5F 0x53 0x35 0x5F 0x12 0x6 0x2 0xA 0x5 0xA 0x5 0x14 0x23 0x5F 0x50
value for PM1a_CNT.SLP_TYP = 0x5
value for PM1b_CNT.SLP_TYP = 0x5
status = 0x1
switching state
LattePanda 环境:
BIOS Information
BIOS Vendor American Megatrends
Core Version 5.011
Compliancy UEFI 2.4; PI 1.3
Project Version S70CR200 3.06 x64
Build Date and Time 03/30/2018 14:41:43
根据@user3840170 的建议,我
- 已从 Super Grub2 Disk
下载 super_grub2_disk_standalone_x86_64_efi_2.04s1.EFI
- 放在我U盘的根目录下
- 通过内置 UEFI 在我的 LattePanda 上启动它 Shell
- 按菜单上的
c
进入命令行模式
- 键入以下命令:
serial --speed=115200
terminal_output --append serial
set debug=acpi
halt
然后,我得到了以下日志,我的 LattePanda 关机了。
grub> halt
commands/acpihalt.c:403: rsdp1=0x7b12e000
commands/acpihalt.c:423: PM1a port=404
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 24
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 2e
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 34
commands/acpihalt.c:107: data type = 0xb
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 3c
commands/acpihalt.c:107: data type = 0xa
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 43
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 4d
commands/acpihalt.c:107: data type = 0xb
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 55
commands/acpihalt.c:107: data type = 0xb
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 5d
commands/acpihalt.c:107: data type = 0xa
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 64
commands/acpihalt.c:107: data type = 0xa
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 6b
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 75
commands/acpihalt.c:107: data type = 0xb
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 7d
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 87
commands/acpihalt.c:107: data type = 0xb
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 8f
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 99
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell a3
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell a9
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell af
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell b5
commands/acpihalt.c:107: data type = 0x1
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell bb
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell c1
commands/acpihalt.c:107: data type = 0x1
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell c7
commands/acpihalt.c:107: data type = 0x1
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell cd
commands/acpihalt.c:107: data type = 0x1
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell d3
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell d9
commands/acpihalt.c:107: data type = 0xb
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell e1
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell eb
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell f5
commands/acpihalt.c:107: data type = 0xa
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell fc
commands/acpihalt.c:107: data type = 0xa
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 103
commands/acpihalt.c:107: data type = 0xa
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 10a
commands/acpihalt.c:107: data type = 0xa
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 111
commands/acpihalt.c:107: data type = 0xa
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 118
commands/acpihalt.c:107: data type = 0xa
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 11f
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 125
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 12b
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 131
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 137
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 13d
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 143
commands/acpihalt.c:107: data type = 0x1
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 149
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 153
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 15d
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 167
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 171
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 177
commands/acpihalt.c:107: data type = 0x1
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 17d
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 187
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 191
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 19b
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 1a5
commands/acpihalt.c:107: data type = 0x1
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 1ab
commands/acpihalt.c:107: data type = 0xa
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 1b2
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x5b
commands/acpihalt.c:242: Tell 1bc
commands/acpihalt.c:188: Extended opcode: 0x80
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:107: data type = 0xb
commands/acpihalt.c:241: Opcode 0x5b
commands/acpihalt.c:242: Tell 1cb
commands/acpihalt.c:188: Extended opcode: 0x81
commands/acpihalt.c:241: Opcode 0x14
commands/acpihalt.c:242: Tell 84f
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 858
commands/acpihalt.c:107: data type = 0x12
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 862
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 868
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 86e
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 874
commands/acpihalt.c:107: data type = 0x1
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 87a
commands/acpihalt.c:107: data type = 0xb
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 882
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 88c
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 896
commands/acpihalt.c:107: data type = 0x1
commands/acpihalt.c:241: Opcode 0x10
commands/acpihalt.c:242: Tell 89c
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 8a3
commands/acpihalt.c:107: data type = 0x11
commands/acpihalt.c:241: Opcode 0x6
commands/acpihalt.c:242: Tell 8b2
commands/acpihalt.c:241: Opcode 0x6
commands/acpihalt.c:242: Tell 8bb
commands/acpihalt.c:241: Opcode 0x6
commands/acpihalt.c:242: Tell 8c4
commands/acpihalt.c:241: Opcode 0x6
commands/acpihalt.c:242: Tell 8cd
commands/acpihalt.c:241: Opcode 0x6
commands/acpihalt.c:242: Tell 8d6
commands/acpihalt.c:241: Opcode 0x6
commands/acpihalt.c:242: Tell 8df
commands/acpihalt.c:241: Opcode 0x6
commands/acpihalt.c:242: Tell 8e8
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 8f1
commands/acpihalt.c:107: data type = 0x12
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell a42
commands/acpihalt.c:107: data type = 0x12
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell b65
commands/acpihalt.c:107: data type = 0x12
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell b9f
commands/acpihalt.c:107: data type = 0x12
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell bd1
commands/acpihalt.c:107: data type = 0x12
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell c0b
commands/acpihalt.c:107: data type = 0x12
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell c3d
commands/acpihalt.c:107: data type = 0x12
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell c77
commands/acpihalt.c:107: data type = 0x12
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell ca9
commands/acpihalt.c:107: data type = 0x12
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell ce3
commands/acpihalt.c:107: data type = 0x12
commands/acpihalt.c:241: Opcode 0x10
commands/acpihalt.c:242: Tell d15
commands/acpihalt.c:241: Opcode 0x5b
commands/acpihalt.c:242: Tell d1d
commands/acpihalt.c:188: Extended opcode: 0x82
commands/acpihalt.c:241: Opcode 0x10
commands/acpihalt.c:242: Tell 207b
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 2081
commands/acpihalt.c:107: data type = 0x12
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 208d
commands/acpihalt.c:107: data type = 0x12
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 209a
commands/acpihalt.c:269: S5 found
commands/acpihalt.c:444: SLP_TYP = 7, port = 0x404
RSDP 地址、PM1a 端口和 SLP_TYP
看起来与我的程序获得的相同。
我不知道这是否真的有帮助,但我注意到 GRUB 中的 ACPI 断电实现有几个不同之处(正如提问者所报告的那样,它确实有效):
GRUB 不会费心向 SMI 端口发送 任何东西。 GRUB 只有一次执行任何端口 I/O,那是在写入 PM1A 寄存器时。 (它也不会影响 PM1B。)grub-core/commands/acpihalt.c
is just for locating and parsing ACPI tables. And yes, grub_acpi_halt
seems to be cold-invoked without any preceding ACPI initialisation call present. It does seem to release EFI resources beforehand, though, as seen in grub-core/lib/efi/halt.c
and grub-core/kern/i386/efi/init.c
, and ultimately grub-core/kern/efi/init.c
中的所有其他代码(但 不会 终止 EFI 引导服务)。
GRUB 不保留“未使用”的 PM1A 寄存器位。询问者的代码首先读取 PM1A 寄存器,以小心屏蔽掉它不想修改的位域。 GRUB 不会打扰,它只是将零放在那里。从提问者的代码翻译成名字,好像可以
out16((1 << 13) | (pm1a_value << 10), pm1a_cnt_blk);
所以首先我建议完全删除 ACPI_ENABLE
位,然后调整对 PM1A 端口的写入。 ACPI 6.0 规范的文本表明只有当您确实希望接收和处理 SCI 中断时才需要前者;如果您只想关闭系统,您可以跳过它。
我编写此代码是为了使用 ACPI 从我从 64 位 UEFI 启动的程序中关闭计算机。
(对不起,代码很长,但我认为所有部分都是必要的)
#include <stdint.h>
#define in8(data, port) __asm__ __volatile__ \
("xor %%eax, %%eax\n\tin %%dx, %%al\n\t" : "=a"(data) : "d"(port))
#define in16(data, port) __asm__ __volatile__ \
("xor %%eax, %%eax\n\tin %%dx, %%ax\n\t" : "=a"(data) : "d"(port))
#define out8(data, port) __asm__ __volatile__ \
("out %%al, %%dx\n\t" : : "a"(data), "d"(port))
#define out16(data, port) __asm__ __volatile__ \
("out %%ax, %%dx\n\t" : : "a"(data), "d"(port))
void initSerialPort(void) {
/* set speed to 115200bps */
out8(0x83, 0x03FB); /* enable LSB/MSB register */
out8(0x00, 0x03F9); /* MSB */
out8(0x01, 0x03F8); /* LSB */
/* set line info */
out8(0x03, 0x03FB); /* disable LSB/MSB register, no parity, no 2-bit stop, 8-bit data */
/* set modem info */
out8(0x03, 0x03FC); /* no loopback, no interrupt, RTS on, DTR on */
/* enable FIFO */
out8(0x07, 0x03FA); /* interrupt at 1 byte, clear TX, clear RX, enable */
/* disable interrupt */
out8(0x00, 0x03F9);
}
void printChar(int c) {
int status;
/* wait until TX buffer is empty */
do {
in8(status, 0x03FD);
} while (!(status & 0x20));
/* put data to send */
out8(c, 0x03F8);
}
void printString(const char* str) {
while (*str != '[=10=]') printChar((unsigned char)*(str++));
}
void printInt(uint64_t value, int radix) {
char vStr[128] = "";
char* pStr = vStr + 120;
do {
*(pStr--) = "0123456789ABCDEF"[value % radix];
value /= radix;
} while (value > 0);
printString(pStr + 1);
}
void stop(void) {
__asm__ __volatile__(
"cli\n\t"
"1:\n\t"
"hlt\n\t"
"jmp 1b\n\t"
);
}
char* searchPuttern(char* text, uint64_t textSize, const char* puttern, uint64_t putternSize) {
uint64_t i, j;
if (textSize < putternSize) return 0;
for (i = 0; i <= textSize - putternSize; i++) {
int ok = 1;
for (j = 0; j < putternSize; j++) {
if (text[i + j] != puttern[j]) {
ok = 0;
break;
}
}
if (ok) return text + i;
}
return 0;
}
void entry(void* unused, uint64_t* table) {
uint64_t* guidTable;
uint64_t guidTableNumElements;
uint64_t i;
uint64_t* rsdp;
uint32_t* rsdt, *xsdt;
uint32_t* fadt_from_rsdt, *fadt, *dsdt;
uint64_t xsdtNumElements;
uint32_t smi_cmd, acpi_enable, pm1a_cnt_blk, pm1b_cnt_blk;
uint32_t amlLength;
char* aml, *s5;
int pm1a_value, pm1b_value;
int status;
int printLimit;
(void)unused;
initSerialPort();
/* parse things to get required values */
printString("argument table = 0x"); printInt((uint64_t)table, 16); printChar('\n');
guidTable = (uint64_t*)table[14];
guidTableNumElements = table[13];
printString("GUID table = 0x"); printInt((uint64_t)guidTable, 16); printChar('\n');
printString("GUID table size = "); printInt(guidTableNumElements, 10); printChar('\n');
rsdp = 0;
for (i = 0; i < guidTableNumElements; i++) {
if (guidTable[i * 3 + 0] == UINT64_C(0x11D3E4F18868E871) &&
guidTable[i * 3 + 1] == UINT64_C(0x81883CC7800022BC)) {
rsdp = (uint64_t*)guidTable[i * 3 + 2];
break;
}
}
if (rsdp == 0) {
printString("RSDP not found\n");
stop();
}
printString("RSDP = 0x"); printInt((uint64_t)rsdp, 16); printChar('\n');
printString("RSDP signature = 0x"); printInt(rsdp[0], 16); printChar('\n');
rsdt = (uint32_t*)(rsdp[2] & UINT64_C(0xffffffff));
xsdt = (uint32_t*)rsdp[3];
printString("RSDT = 0x"); printInt((uint64_t)rsdt, 16); printChar('\n');
fadt_from_rsdt = 0;
if (rsdt != 0) {
uint64_t rsdtNumElements;
printString("RSDT signature = 0x"); printInt(rsdt[0], 16); printChar('\n');
printString("RSDT length = "); printInt(rsdt[1], 10); printChar('\n');
rsdtNumElements = (rsdt[1] - 36) / 4;
fadt = 0;
for (i = 0; i < rsdtNumElements; i++) {
uint32_t* addr = (uint32_t*)(uint64_t)rsdt[9 + i];
printString("*0x"); printInt((uint64_t)addr, 16);
printString(" = 0x"); printInt(addr[0], 16);
printChar('\n');
if ((addr[0] & UINT64_C(0xffffffff)) == UINT64_C(0x50434146)) {
fadt_from_rsdt = addr;
}
}
}
printString("XSDT = 0x"); printInt((uint64_t)xsdt, 16); printChar('\n');
if (xsdt == 0) {
printString("XSDT not found\n");
stop();
}
printString("XSDT signature = 0x"); printInt(xsdt[0], 16); printChar('\n');
printString("XSDT length = "); printInt(xsdt[1], 10); printChar('\n');
xsdtNumElements = (xsdt[1] - 36) / 8;
fadt = 0;
for (i = 0; i < xsdtNumElements; i++) {
uint32_t* addr = (uint32_t*)((uint64_t*)&xsdt[9])[i];
printString("*0x"); printInt((uint64_t)addr, 16);
printString(" = 0x"); printInt(addr[0], 16);
printChar('\n');
if ((addr[0] & UINT64_C(0xffffffff)) == UINT64_C(0x50434146)) {
fadt = addr;
}
}
printString("FADT from RSDT = 0x"); printInt((uint64_t)fadt_from_rsdt, 16); printChar('\n');
printString("FADT from XSDT = 0x"); printInt((uint64_t)fadt, 16); printChar('\n');
if (fadt == 0) fadt = fadt_from_rsdt;
if (fadt == 0) {
printString("FADT not found\n");
stop();
}
printString("FADT = 0x"); printInt((uint64_t)fadt, 16); printChar('\n');
printString("FADT signature = 0x"); printInt(fadt[0], 16); printChar('\n');
printString("FADT length = "); printInt(fadt[1], 10); printChar('\n');
dsdt = (uint32_t*)(uint64_t)fadt[10];
smi_cmd = fadt[12];
acpi_enable = fadt[13] & 0xff;
pm1a_cnt_blk = fadt[16];
pm1b_cnt_blk = fadt[17];
printString("DSDT = 0x"); printInt((uint64_t)dsdt, 16); printChar('\n');
printString("SMI_CMD = 0x"); printInt(smi_cmd, 16); printChar('\n');
printString("ACPI_ENABLE = 0x"); printInt(acpi_enable, 16); printChar('\n');
printString("PM1a_CNT_BLK = 0x"); printInt(pm1a_cnt_blk, 16); printChar('\n');
printString("PM1b_CNT_BLK = 0x"); printInt(pm1b_cnt_blk, 16); printChar('\n');
printString("PM1_CNT_LEN = "); printInt((fadt[22] >> 8) & 0xff, 10); printChar('\n');
if (fadt[1] >= 148) {
printString("X_DSDT = 0x");
printInt(fadt[35] | ((uint64_t)fadt[36] << 32), 16);
printChar('\n');
}
if (fadt[1] >= 184) {
printString("X_PM1a_CNT_BLK = 0x"); printInt(fadt[43], 16);
printString(" 0x"); printInt(fadt[44], 16);
printString(" 0x"); printInt(fadt[45], 16);
printChar('\n');
}
if (fadt[1] >= 196) {
printString("X_PM1b_CNT_BLK = 0x"); printInt(fadt[46], 16);
printString(" 0x"); printInt(fadt[47], 16);
printString(" 0x"); printInt(fadt[48], 16);
printChar('\n');
}
printString("DSDT signature = 0x"); printInt(dsdt[0], 16); printChar('\n');
printString("DSDT length = "); printInt(dsdt[1], 10); printChar('\n');
if (dsdt[1] < 36) {
printString("DSDT too short\n");
stop();
}
amlLength = dsdt[1] - 36;
aml = (char*)&dsdt[9];
s5 = searchPuttern(aml, amlLength, "\x08_S5_\x12", 6);
if (s5 == 0) {
printString("_S5_ not found\n");
stop();
}
printString("_S5_ dump:\n");
for (i = 0; i < 16; i++) {
if (i > 0) printChar(' ');
printString("0x"); printInt((unsigned char)s5[i], 16);
}
printChar('\n');
if (s5[8] == 0) {
pm1a_value = 0;
if (s5[9] == 0) pm1b_value = 0; else pm1b_value = (unsigned char)s5[10];
} else {
pm1a_value = (unsigned char)s5[9];
if (s5[10] == 0) pm1b_value = 0; else pm1b_value = (unsigned char)s5[11];
}
printString("value for PM1a_CNT.SLP_TYP = 0x"); printInt(pm1a_value, 16); printChar('\n');
printString("value for PM1b_CNT.SLP_TYP = 0x"); printInt(pm1b_value, 16); printChar('\n');
/* enable ACPI for powering off */
in16(status, pm1a_cnt_blk);
printString("status = 0x"); printInt(status, 16); printChar('\n');
if (!(status & 1)) {
printString("sending ACPI_ENABLE to SMI_CMD\n");
out8(acpi_enable, smi_cmd);
printLimit = 10;
do {
in16(status, pm1a_cnt_blk);
if (printLimit-- > 0) {
printString("status = 0x"); printInt(status, 16); printChar('\n');
}
} while (!(status & 1));
}
/* power off */
printString("switching state\n");
in16(status, pm1a_cnt_blk);
out16((status & ~(7 << 10)) | ((pm1a_value & 7) << 10) | (1 << 13), pm1a_cnt_blk);
if (pm1b_cnt_blk != 0) {
in16(status, pm1b_cnt_blk);
out16((status & ~(7 << 10)) | ((pm1b_value & 7) << 10) | (1 << 13), pm1b_cnt_blk);
}
printString("state switch sent\n");
stop();
}
我用 TDM-GCC (gcc (tdm64-1) 9.2.0
) 编译了这个。
编译命令为:
C:\MyInstalledApps\TDM-GCC-64\bin\gcc -Wall -Wextra -nostdlib -e entry -m64 -Wl,--subsystem=10 acpi_test.c -o bootx64.efi
它没有给我任何警告或错误消息。
我在我的 LattePanda(使用 2G RAM 和 32G eMMC)上试过这段代码,但它无法关闭电源。查看跟踪,似乎无法通过将 ACPI_ENABLE
发送到端口 SMI_CMD
来启用 ACPI。
我的意思是 PM1a_CNT_BLK
寄存器中的 SCI_EN
位似乎没有设置。
我也在 QEMU 和 VirtualBox 上尝试了这段代码并成功关闭电源,但它看起来没有帮助,因为它似乎已经在这些 VM 软件中启用了 ACPI。
我的 QEMU 版本是 QEMU emulator version 5.1.92 (v5.2.0-rc2-11843-gf571c4ffb5-dirty)
并使用 GitHub - BlankOn/ovmf-blobs: BIOS.bin for qemu to support UEFI 作为 BIOS。
根据串口的输出,ACPI_ENABLE
和SMI_CMD
的值看起来比较合理。 (不知道对不对)
我是不是忽略了什么?我应该如何在 LattePanda 上启用 ACPI 并关闭我从 UEFI 启动的程序?
LattePanda 的串口输出:
argument table = 0x7BA13F18
GUID table = 0x7BA12E18
GUID table size = 13
RSDP = 0x7B12E000
RSDP signature = 0x2052545020445352
RSDT = 0x7B12E028
RSDT signature = 0x54445352
RSDT length = 124
*0x7B12E188 = 0x50434146
*0x7B14AB40 = 0x43495041
*0x7B14ABC8 = 0x54445046
*0x7B14AC10 = 0x54444946
*0x7B14ACB0 = 0x4D44534D
*0x7B14AD08 = 0x4746434D
*0x7B14AD48 = 0x54445353
*0x7B14EEC0 = 0x54445353
*0x7B14F518 = 0x54445353
*0x7B14F570 = 0x49464555
*0x7B14F5B8 = 0x54445353
*0x7B14F828 = 0x54455048
*0x7B14F860 = 0x54445353
*0x7B14FFC8 = 0x54445353
*0x7B150258 = 0x54445353
*0x7B1503D8 = 0x5449504C
*0x7B1504E0 = 0x47464342
*0x7B150620 = 0x4D415250
*0x7B150650 = 0x54524742
*0x7B150688 = 0x324D5054
*0x7B1506C0 = 0x54525343
*0x7B150810 = 0x54414457
XSDT = 0x7B12E0A8
XSDT signature = 0x54445358
XSDT length = 212
*0x7B14AA30 = 0x50434146
*0x7B14AB40 = 0x43495041
*0x7B14ABC8 = 0x54445046
*0x7B14AC10 = 0x54444946
*0x7B14ACB0 = 0x4D44534D
*0x7B14AD08 = 0x4746434D
*0x7B14AD48 = 0x54445353
*0x7B14EEC0 = 0x54445353
*0x7B14F518 = 0x54445353
*0x7B14F570 = 0x49464555
*0x7B14F5B8 = 0x54445353
*0x7B14F828 = 0x54455048
*0x7B14F860 = 0x54445353
*0x7B14FFC8 = 0x54445353
*0x7B150258 = 0x54445353
*0x7B1503D8 = 0x5449504C
*0x7B1504E0 = 0x47464342
*0x7B150620 = 0x4D415250
*0x7B150650 = 0x54524742
*0x7B150688 = 0x324D5054
*0x7B1506C0 = 0x54525343
*0x7B150810 = 0x54414457
FADT from RSDT = 0x7B12E188
FADT from XSDT = 0x7B14AA30
FADT = 0x7B14AA30
FADT signature = 0x50434146
FADT length = 268
DSDT = 0x7B12E210
SMI_CMD = 0xB2
ACPI_ENABLE = 0xA0
PM1a_CNT_BLK = 0x404
PM1b_CNT_BLK = 0x0
PM1_CNT_LEN = 2
X_DSDT = 0x7B12E210
X_PM1a_CNT_BLK = 0x2001001 0x404 0x0
X_PM1b_CNT_BLK = 0x1 0x0 0x0
DSDT signature = 0x54445344
DSDT length = 116766
_S5_ dump:
0x8 0x5F 0x53 0x35 0x5F 0x12 0x7 0x4 0xA 0x7 0x0 0x0 0x0 0x14 0x1E 0x5F
value for PM1a_CNT.SLP_TYP = 0x7
value for PM1b_CNT.SLP_TYP = 0x0
status = 0x0
sending ACPI_ENABLE to SMI_CMD
status = 0x0
status = 0x0
status = 0x0
status = 0x0
status = 0x0
status = 0x0
status = 0x0
status = 0x0
status = 0x0
status = 0x0
QEMU 的串口输出:
argument table = 0x7EED018
GUID table = 0x7EED0D8
GUID table size = 9
RSDP = 0x7EF9014
RSDP signature = 0x2052545020445352
RSDT = 0x7EF8074
RSDT signature = 0x54445352
RSDT length = 52
*0x7EF5000 = 0x50434146
*0x7EF4000 = 0x43495041
*0x7EF3000 = 0x54455048
*0x7EF2000 = 0x54454157
XSDT = 0x7EF80E8
XSDT signature = 0x54445358
XSDT length = 68
*0x7EF5000 = 0x50434146
*0x7EF4000 = 0x43495041
*0x7EF3000 = 0x54455048
*0x7EF2000 = 0x54454157
FADT from RSDT = 0x7EF5000
FADT from XSDT = 0x7EF5000
FADT = 0x7EF5000
FADT signature = 0x50434146
FADT length = 116
DSDT = 0x7EF6000
SMI_CMD = 0xB2
ACPI_ENABLE = 0xF1
PM1a_CNT_BLK = 0xB004
PM1b_CNT_BLK = 0x0
PM1_CNT_LEN = 2
DSDT signature = 0x54445344
DSDT length = 5060
_S5_ dump:
0x8 0x5F 0x53 0x35 0x5F 0x12 0x6 0x4 0x0 0x0 0x0 0x0 0x10 0x3B 0x5C 0x2E
value for PM1a_CNT.SLP_TYP = 0x0
value for PM1b_CNT.SLP_TYP = 0x0
status = 0x1
switching state
state switch sent
VirtualBox 6.1.18 r142142 (Qt5.6.2) 的串口输出:
argument table = 0x6FEE018
GUID table = 0x6FEEC98
GUID table size = 10
RSDP = 0x6FFA014
RSDP signature = 0x2052545020445352
RSDT = 0x6FF9074
RSDT signature = 0x54445352
RSDT length = 52
*0x6FF7000 = 0x50434146
*0x6FF8000 = 0x43495041
*0x6FF3000 = 0x54445353
*0x5E39000 = 0x54524742
XSDT = 0x6FF90E8
XSDT signature = 0x54445358
XSDT length = 68
*0x6FF7000 = 0x50434146
*0x6FF8000 = 0x43495041
*0x6FF3000 = 0x54445353
*0x5E39000 = 0x54524742
FADT from RSDT = 0x6FF7000
FADT from XSDT = 0x6FF7000
FADT = 0x6FF7000
FADT signature = 0x50434146
FADT length = 244
DSDT = 0x6FF4000
SMI_CMD = 0x442E
ACPI_ENABLE = 0xA1
PM1a_CNT_BLK = 0xB004
PM1b_CNT_BLK = 0x0
PM1_CNT_LEN = 2
X_DSDT = 0x6FF4000
X_PM1a_CNT_BLK = 0x2001001 0xB004 0x0
X_PM1b_CNT_BLK = 0x0 0x0 0x0
DSDT signature = 0x54445344
DSDT length = 8997
_S5_ dump:
0x8 0x5F 0x53 0x35 0x5F 0x12 0x6 0x2 0xA 0x5 0xA 0x5 0x14 0x23 0x5F 0x50
value for PM1a_CNT.SLP_TYP = 0x5
value for PM1b_CNT.SLP_TYP = 0x5
status = 0x1
switching state
LattePanda 环境:
BIOS Information
BIOS Vendor American Megatrends
Core Version 5.011
Compliancy UEFI 2.4; PI 1.3
Project Version S70CR200 3.06 x64
Build Date and Time 03/30/2018 14:41:43
根据@user3840170 的建议,我
- 已从 Super Grub2 Disk 下载
- 放在我U盘的根目录下
- 通过内置 UEFI 在我的 LattePanda 上启动它 Shell
- 按菜单上的
c
进入命令行模式 - 键入以下命令:
super_grub2_disk_standalone_x86_64_efi_2.04s1.EFI
serial --speed=115200
terminal_output --append serial
set debug=acpi
halt
然后,我得到了以下日志,我的 LattePanda 关机了。
grub> halt
commands/acpihalt.c:403: rsdp1=0x7b12e000
commands/acpihalt.c:423: PM1a port=404
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 24
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 2e
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 34
commands/acpihalt.c:107: data type = 0xb
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 3c
commands/acpihalt.c:107: data type = 0xa
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 43
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 4d
commands/acpihalt.c:107: data type = 0xb
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 55
commands/acpihalt.c:107: data type = 0xb
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 5d
commands/acpihalt.c:107: data type = 0xa
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 64
commands/acpihalt.c:107: data type = 0xa
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 6b
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 75
commands/acpihalt.c:107: data type = 0xb
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 7d
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 87
commands/acpihalt.c:107: data type = 0xb
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 8f
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 99
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell a3
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell a9
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell af
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell b5
commands/acpihalt.c:107: data type = 0x1
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell bb
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell c1
commands/acpihalt.c:107: data type = 0x1
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell c7
commands/acpihalt.c:107: data type = 0x1
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell cd
commands/acpihalt.c:107: data type = 0x1
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell d3
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell d9
commands/acpihalt.c:107: data type = 0xb
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell e1
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell eb
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell f5
commands/acpihalt.c:107: data type = 0xa
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell fc
commands/acpihalt.c:107: data type = 0xa
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 103
commands/acpihalt.c:107: data type = 0xa
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 10a
commands/acpihalt.c:107: data type = 0xa
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 111
commands/acpihalt.c:107: data type = 0xa
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 118
commands/acpihalt.c:107: data type = 0xa
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 11f
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 125
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 12b
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 131
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 137
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 13d
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 143
commands/acpihalt.c:107: data type = 0x1
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 149
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 153
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 15d
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 167
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 171
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 177
commands/acpihalt.c:107: data type = 0x1
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 17d
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 187
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 191
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 19b
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 1a5
commands/acpihalt.c:107: data type = 0x1
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 1ab
commands/acpihalt.c:107: data type = 0xa
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 1b2
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x5b
commands/acpihalt.c:242: Tell 1bc
commands/acpihalt.c:188: Extended opcode: 0x80
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:107: data type = 0xb
commands/acpihalt.c:241: Opcode 0x5b
commands/acpihalt.c:242: Tell 1cb
commands/acpihalt.c:188: Extended opcode: 0x81
commands/acpihalt.c:241: Opcode 0x14
commands/acpihalt.c:242: Tell 84f
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 858
commands/acpihalt.c:107: data type = 0x12
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 862
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 868
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 86e
commands/acpihalt.c:107: data type = 0x0
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 874
commands/acpihalt.c:107: data type = 0x1
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 87a
commands/acpihalt.c:107: data type = 0xb
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 882
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 88c
commands/acpihalt.c:107: data type = 0xc
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 896
commands/acpihalt.c:107: data type = 0x1
commands/acpihalt.c:241: Opcode 0x10
commands/acpihalt.c:242: Tell 89c
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 8a3
commands/acpihalt.c:107: data type = 0x11
commands/acpihalt.c:241: Opcode 0x6
commands/acpihalt.c:242: Tell 8b2
commands/acpihalt.c:241: Opcode 0x6
commands/acpihalt.c:242: Tell 8bb
commands/acpihalt.c:241: Opcode 0x6
commands/acpihalt.c:242: Tell 8c4
commands/acpihalt.c:241: Opcode 0x6
commands/acpihalt.c:242: Tell 8cd
commands/acpihalt.c:241: Opcode 0x6
commands/acpihalt.c:242: Tell 8d6
commands/acpihalt.c:241: Opcode 0x6
commands/acpihalt.c:242: Tell 8df
commands/acpihalt.c:241: Opcode 0x6
commands/acpihalt.c:242: Tell 8e8
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 8f1
commands/acpihalt.c:107: data type = 0x12
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell a42
commands/acpihalt.c:107: data type = 0x12
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell b65
commands/acpihalt.c:107: data type = 0x12
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell b9f
commands/acpihalt.c:107: data type = 0x12
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell bd1
commands/acpihalt.c:107: data type = 0x12
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell c0b
commands/acpihalt.c:107: data type = 0x12
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell c3d
commands/acpihalt.c:107: data type = 0x12
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell c77
commands/acpihalt.c:107: data type = 0x12
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell ca9
commands/acpihalt.c:107: data type = 0x12
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell ce3
commands/acpihalt.c:107: data type = 0x12
commands/acpihalt.c:241: Opcode 0x10
commands/acpihalt.c:242: Tell d15
commands/acpihalt.c:241: Opcode 0x5b
commands/acpihalt.c:242: Tell d1d
commands/acpihalt.c:188: Extended opcode: 0x82
commands/acpihalt.c:241: Opcode 0x10
commands/acpihalt.c:242: Tell 207b
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 2081
commands/acpihalt.c:107: data type = 0x12
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 208d
commands/acpihalt.c:107: data type = 0x12
commands/acpihalt.c:241: Opcode 0x8
commands/acpihalt.c:242: Tell 209a
commands/acpihalt.c:269: S5 found
commands/acpihalt.c:444: SLP_TYP = 7, port = 0x404
RSDP 地址、PM1a 端口和 SLP_TYP
看起来与我的程序获得的相同。
我不知道这是否真的有帮助,但我注意到 GRUB 中的 ACPI 断电实现有几个不同之处(正如提问者所报告的那样,它确实有效):
GRUB 不会费心向 SMI 端口发送 任何东西。 GRUB 只有一次执行任何端口 I/O,那是在写入 PM1A 寄存器时。 (它也不会影响 PM1B。)
grub-core/commands/acpihalt.c
is just for locating and parsing ACPI tables. And yes,grub_acpi_halt
seems to be cold-invoked without any preceding ACPI initialisation call present. It does seem to release EFI resources beforehand, though, as seen ingrub-core/lib/efi/halt.c
andgrub-core/kern/i386/efi/init.c
, and ultimatelygrub-core/kern/efi/init.c
中的所有其他代码(但 不会 终止 EFI 引导服务)。GRUB 不保留“未使用”的 PM1A 寄存器位。询问者的代码首先读取 PM1A 寄存器,以小心屏蔽掉它不想修改的位域。 GRUB 不会打扰,它只是将零放在那里。从提问者的代码翻译成名字,好像可以
out16((1 << 13) | (pm1a_value << 10), pm1a_cnt_blk);
所以首先我建议完全删除 ACPI_ENABLE
位,然后调整对 PM1A 端口的写入。 ACPI 6.0 规范的文本表明只有当您确实希望接收和处理 SCI 中断时才需要前者;如果您只想关闭系统,您可以跳过它。