I wrote this code to use ACPI for powering off computers from my program launched from 64-bit UEFI.
(sorry for long code, but I think all parts are necessary)
#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 != '\0') 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();
}
I compiled this with TDM-GCC (gcc (tdm64-1) 9.2.0
).
Compilation command is:
C:\MyInstalledApps\TDM-GCC-64\bin\gcc -Wall -Wextra -nostdlib -e entry -m64 -Wl,--subsystem=10 acpi_test.c -o bootx64.efi
It gives me no warning or error message.
I tried this code on my LattePanda (with 2G RAM and 32G eMMC), but it failed to powering off that. Looking at the trace, it seems failing to enable ACPI by sending ACPI_ENABLE
to the port SMI_CMD
.
I mean the SCI_EN
bit in the PM1a_CNT_BLK
register doesn't seem set.
I also tried this code on QEMU and VirtualBox and succeeded to powering off, but it didn't look helpful because itseems ACPI was already enabled in these VM softwares.
My QEMU version is QEMU emulator version 5.1.92 (v5.2.0-rc2-11843-gf571c4ffb5-dirty)
and used GitHub - BlankOn/ovmf-blobs: BIOS.bin for qemu to support UEFI as BIOS.
According to the output to the serial port, the values of ACPI_ENABLE
and SMI_CMD
looks reasonable. (I don't know if they are actually correct)
Am I overlooking something? What should I do to enable ACPI on LattePanda and powering off from my program launched from UEFI?
Serial port output from 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
Serial port output from 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
Serial port output from 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 environment:
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
Based on @user3840170's advice, I
super_grub2_disk_standalone_x86_64_efi_2.04s1.EFI
from Super Grub2 Diskc
on the menu to enter command-line modeserial --speed=115200
terminal_output --append serial
set debug=acpi
halt
Then, I got following log and my LattePanda got powered off.
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
The RSDP address, PM1a port and SLP_TYP
looks the same as what is got by my program.
I have no idea if this is actually going to help, but I have noticed a couple of differences from the ACPI power-off implementation in GRUB (which, as reported by the asker, does actually work):
GRUB doesn’t bother with sending anything to the SMI port. There is only one time GRUB performs any port I/O whatsoever, and that’s when writing to the PM1A register. (It doesn’t bother with PM1B either.) All that other code in 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
(but does not terminate EFI boot services).
GRUB doesn’t preserve ‘unused’ PM1A register bits. The asker’s code first reads off the PM1A register in order to carefully mask out bitfields it doesn’t want to modify. GRUB doesn’t bother, it just puts zeroes there. Translated into names from the asker’s code, it seems to do
out16((1 << 13) | (pm1a_value << 10), pm1a_cnt_blk);
So first I’d suggest dropping the ACPI_ENABLE
bit entirely, then adjusting the write to the PM1A port. The text of the ACPI 6.0 specification suggests the former is only needed if you actually expect to receive and handle SCI interrupts; if all you want to do is shut down the system, you might get away with skipping it.