Reputation: 456
I want to shutdown a x86 architecture based 64 bit under developing OS by ACPI, So I maintain following steps
Finding ACPI RSDP table
Validate with Sign. & Checksum
Find RSDT/XSDT
Find FADT
Parse FADT to get pm1a_control & pm1b_control
define two variables #define SLP_EN (1 << 13) & #define S5_SLEEP_TYPA (5 << 10)
send poweroff command by outw(pm1a_control, S5_SLEEP_TYPA | SLP_EN);
Where
I am getting rsdp address from limine bootloader
void *find_acpi_table() {
if (!rsdp_request.response || !rsdp_request.response->address) {
printf("ACPI is not available\n");
return NULL; // ACPI is not available
}
rsdp_t *rsdp = (rsdp_t *) rsdp_request.response->address;
if (rsdp->revision >= 2) {
rsdp_ext_t *rsdp_ext = (rsdp_ext_t *)rsdp;
return (void *)(uintptr_t)rsdp_ext; // Use XSDT for 64-bit systems
}
return (void *)(uintptr_t)rsdp; // Use RSDT for ACPI 1.0
}
void validate_acpi_table(void *table_addr){
rsdp_t *rsdp = (rsdp_t *) table_addr;
if(rsdp){
uint64_t acpi_version = (rsdp->revision >= 2) ? 2 : 1;
if(!memcmp(rsdp->signature, "RSD PTR ", 8)){
uint8_t sum = 0;
uint8_t *ptr = (uint8_t *) rsdp;
for (int i = 0; i < 20; i++) {
sum += ptr[i];
}
if((sum % 256) == 0){
printf("ACPI %d.0 is signature and checksum validated\n", acpi_version);
}else{
printf("ACPI %d.0 is not checksum validated\n", acpi_version);
}
}else{
printf("ACPI %d.0 is not signature validated\n", acpi_version);
}
}else{
printf("ACPI Table not found\n");
}
}
void find_fadt(void *table_addr) {
rsdp_t *rsdp = (rsdp_t *) table_addr;
rsdt_t *rsdt = (rsdt_t *) rsdp->rsdt_address;
rsdp_ext_t *rsdp_ext = (rsdp->revision >= 2) ? (rsdp_ext_t *) table_addr : 0;;
xsdt_t *xsdt = (rsdp->revision >= 2) ? (xsdt_t *)rsdp_ext->xsdt_address : 0;
acpi_header_t header = (rsdp->revision >= 2) ? xsdt->header : rsdt->header;
int entry_size = (rsdp->revision >= 2) ? sizeof(uint64_t) : sizeof(uint32_t);
int entry_count = (header.length - sizeof(acpi_header_t)) / entry_size;
uint32_t *entries_32 = (uint32_t *) rsdt->entries;
uint64_t *entries_64 = (uint64_t *) xsdt->entries;
void *entries = (rsdp->revision >= 2) ? (void *)entries_64 : (void *)entries_32;
for (int i = 0; i < entry_count; i++) {
acpi_header_t *entry = (acpi_header_t *)(uintptr_t)((rsdp->revision >= 2) ? ((uint64_t *)entries)[i] : ((uint32_t *)entries)[i]);
if (!memcmp(entry->signature, "FACP", 4)) {
fadt = (fadt_t *) entry;
}
}
}
And finally acpi_powroff
void acpi_poweroff() {
if (!fadt) {
printf("FADT not found, ACPI shutdown unavailable!\n");
return;
}
// Enable ACPI first (if needed)
if(!is_acpi_enabled()){
acpi_enable();
}
uint32_t pm1a_control = 0;
pm1a_control = (fadt->header.revision >= 2 && fadt->X_PM1aControlBlock.Address) ? (uint32_t)fadt->X_PM1aControlBlock.Address : fadt->PM1aControlBlock;
uint32_t pm1b_control = fadt->PM1bControlBlock;
if (!pm1a_control) {
printf("PM1a Control Block not found!\n");
return;
}
printf("Sending ACPI shutdown command: outw(%x, %x)\n", pm1a_control, S5_SLEEP_TYPA | SLP_EN);
// Shutdown by setting SLP_EN (bit 13) with S5 sleep type (bits 10-12)
outw(pm1a_control, S5_SLEEP_TYPA | SLP_EN);
if(pm1b_control) outw(pm1b_control, S5_SLEEP_TYPA | SLP_EN);
// If ACPI fails, use fallback methods
printf("ACPI Shutdown failed, halting system!\n");
while (1) {
__asm__ volatile ("hlt");
}
}
Unfortunately acpi_poweroff is not making power shut even I have tested in real machine.
THe debug in Qemu is showing pm1a_control = 0x604
and S5_SLEEP_TYPA | SLP_EN = 3400
.
This tutorials is not understandable for me. It will be helpful if you please share some solution.
The full code is present in GitHub .
Upvotes: 1
Views: 59
Reputation: 456
The code is perfect for real machine and virtualbox although acpi_poweroff()
is not working for QEmu(I don't know the reason!). My outw
function was wrong so it was not working. Now fixed outw function is
void outw(uint16_t port, uint16_t value) {
asm volatile ("outw %0, %1" : : "a"(value), "Nd"(port));
}
Upvotes: 0