#define MODULE "power" #include "misc/reboot.h" #include "acpi/FADT.h" #include "acpi/RSDT.h" #include "assert.h" #include "interrupts/IDT.h" #include "interrupts/Interrupts.h" #include "io/IO.h" #include "log/Log.h" #include "misc/hang.h" #include "std/string.h" static void try_acpi_reboot() { kdbgln("Fetching pointer to RSDT/XSDT"); ACPI::SDTHeader* rootSDT = ACPI::get_rsdt_or_xsdt(); if (!rootSDT) { kwarnln("The pointer to the RSDT/XSDT is null"); return; } if (!ACPI::validate_rsdt_or_xsdt(rootSDT)) { kwarnln("The RSDT/XSDT is invalid"); return; } kdbgln("Searching for the FADT"); ACPI::FADT* fadt = (ACPI::FADT*)ACPI::find_table(rootSDT, "FACP"); if (!fadt) { kwarnln("Unable to find the FADT"); return; } if (fadt->header.Revision < 2) { kwarnln("ACPI revision is too low (%d), ACPI Reset is only implemented in ACPI 2.0+", fadt->header.Revision); return; } if (!(fadt->Flags & 1 << 10)) { kwarnln("This system does not support ACPI Reset"); return; } switch (fadt->ResetReg.AddressSpace) { case ACPI::SystemIO: kdbgln("Attempting ACPI Reset via SystemIO: sending byte %d to port %lx", fadt->ResetValue, fadt->ResetReg.Address); IO::outb((uint16_t)fadt->ResetReg.Address, fadt->ResetValue); break; case ACPI::GeneralPurposeIO: kdbgln("Attempting ACPI Reset via GeneralPurposeIO: sending byte %d to port %lx", fadt->ResetValue, fadt->ResetReg.Address); IO::outb((uint16_t)fadt->ResetReg.Address, fadt->ResetValue); break; default: kwarnln("This method of rebooting via ACPI is not yet implemented"); return; } kerrorln("Tried to reboot, but apparently did nothing"); } static void try_kbd_reboot() { Interrupts::disable(); uint8_t temp; do { temp = IO::inb(0x64); if (temp & 0b1) IO::inb(0x60); } while (temp & 0b10); IO::outb(0x64, 0xFE); } static void try_idt_triple_fault() { IDTR idtr; idtr.limit = 0x0000; idtr.offset = 0x0000; asm volatile("lidt %0" : : "m"(idtr)); Interrupts::enable(); asm volatile("int $0x80"); } [[noreturn]] void reboot() { Interrupts::disable(); kinfoln("Attempting reboot using ACPI"); try_acpi_reboot(); kinfoln("Attempting reboot using keyboard RESET pulsing"); try_kbd_reboot(); kinfoln("Attempting reboot by triple faulting"); try_idt_triple_fault(); kerrorln("Failed to reboot, halting machine"); hang(); }