The only thing doing weird stuff is exec(), so that's commented out and throws ENOSYS right now. But we have two user tasks running in parallel, isolated from each other!
102 lines
2.7 KiB
C++
102 lines
2.7 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 "memory/VMM.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();
|
|
if (!VMM::is_using_kernel_address_space())
|
|
{
|
|
VMM::switch_back_to_kernel_address_space();
|
|
VMM::apply_address_space();
|
|
}
|
|
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();
|
|
} |