如何通过ACPI进行电源关闭?

问题描述 投票:0回答:1
我想关闭ACPI开发OS下的基于64位的X86体系结构,因此我保持以下步骤

  1. Finding ACPI RSDP table
    
    
  2. Validate with Sign. & Checksum
    
    
  3. Find RSDT/XSDT
    
    
  4. Find FADT
    
    
  5. Parse FADT to get pm1a_control & pm1b_control
    
    
  6. define two variables #define SLP_EN (1 << 13) & #define S5_SLEEP_TYPA (5 << 10)
    
    
  7. send poweroff command by outw(pm1a_control, S5_SLEEP_TYPA | SLP_EN);
    
    
Whather

我是从limine bootloader获取RSDP地址

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;
        }
    }
}
最终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"); } }
不幸的是,即使我已经在真实机器中进行了测试,Acpi_poweroff也没有使电源关闭。
Qemu中的调试显示
pm1a_control = 0x604

S5_SLEEP_TYPA | SLP_EN = 3400
。 这本教程对我来说是无法理解的。如果您请分享一些解决方案,这将是有帮助的。
完整代码存在于
Github

代码非常适合真实机器和虚拟盒,尽管

acpi_poweroff()

不适合QEMU(我不知道原因!)。我的outw功能是错误的

void outw(uint16_t port, uint16_t value) { asm volatile ("outw %0, %1" : : "a"(value), "Nd"(port)); }
x86 osdev
1个回答
0
投票

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.