96 lines
2.5 KiB
C++
96 lines
2.5 KiB
C++
#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(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(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();
|
|
} |