mirror of
https://github.com/Fennix-Project/Kernel.git
synced 2025-05-27 06:54:35 +00:00
226 lines
6.6 KiB
C++
226 lines
6.6 KiB
C++
#include "acpi.hpp"
|
|
|
|
#include <time.hpp>
|
|
#include <debug.h>
|
|
#include <io.h>
|
|
|
|
// #include "../timer.h"
|
|
// #include "apic.hpp"
|
|
// #include "smp.hpp"
|
|
|
|
#define ACPI_TIMER 0x0001
|
|
#define ACPI_BUSMASTER 0x0010
|
|
#define ACPI_GLOBAL 0x0020
|
|
#define ACPI_POWER_BUTTON 0x0100
|
|
#define ACPI_SLEEP_BUTTON 0x0200
|
|
#define ACPI_RTC_ALARM 0x0400
|
|
#define ACPI_PCIE_WAKE 0x4000
|
|
#define ACPI_WAKE 0x8000
|
|
|
|
namespace ACPI
|
|
{
|
|
__attribute__((always_inline)) inline bool IsCanonical(uint64_t Address)
|
|
{
|
|
return ((Address <= 0x00007FFFFFFFFFFF) || ((Address >= 0xFFFF800000000000) && (Address <= 0xFFFFFFFFFFFFFFFF)));
|
|
}
|
|
|
|
#define ACPI_ENABLED 0x0001
|
|
#define ACPI_SLEEP 0x2000
|
|
|
|
#define ACPI_GAS_MMIO 0
|
|
#define ACPI_GAS_IO 1
|
|
#define ACPI_GAS_PCI 2
|
|
|
|
void DSDT::SCIHandler(CPU::x64::TrapFrame *regs)
|
|
{
|
|
debug("SCI Handle Triggered");
|
|
uint16_t event = this->GetSCIevent();
|
|
debug("SCI Event: %#llx", event);
|
|
if (event & ACPI_TIMER)
|
|
event &= ~ACPI_TIMER; // Remove the ACPI timer flag.
|
|
switch (event)
|
|
{
|
|
case ACPI_POWER_BUTTON:
|
|
{
|
|
this->Shutdown();
|
|
Time tm = ReadClock();
|
|
while (tm.Second == ReadClock().Second)
|
|
;
|
|
outw(0xB004, 0x2000);
|
|
outw(0x604, 0x2000);
|
|
outw(0x4004, 0x3400);
|
|
CPU::Stop();
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
warn("unknown event 0x%04p", event);
|
|
return;
|
|
}
|
|
}
|
|
UNUSED(regs);
|
|
}
|
|
|
|
void DSDT::Shutdown()
|
|
{
|
|
trace("Shutting down...");
|
|
if (!ACPIShutdownSupported)
|
|
{
|
|
outl(0xB004, 0x2000); // for qemu
|
|
outl(0x604, 0x2000); // if qemu not working, bochs and older versions of qemu
|
|
outl(0x4004, 0x3400); // virtual box
|
|
}
|
|
else if (SCI_EN == 1)
|
|
{
|
|
outw(acpi->FADT->PM1aControlBlock, (inw(acpi->FADT->PM1aControlBlock) & 0xE3FF) | ((SLP_TYPa << 10) | ACPI_SLEEP));
|
|
if (acpi->FADT->PM1bControlBlock)
|
|
outw(acpi->FADT->PM1bControlBlock, (inw(acpi->FADT->PM1bControlBlock) & 0xE3FF) | ((SLP_TYPb << 10) | ACPI_SLEEP));
|
|
outw(PM1a_CNT, SLP_TYPa | SLP_EN);
|
|
if (PM1b_CNT)
|
|
outw(PM1b_CNT, SLP_TYPb | SLP_EN);
|
|
}
|
|
CPU::Stop();
|
|
}
|
|
|
|
void DSDT::Reboot()
|
|
{
|
|
trace("Rebooting...");
|
|
if (!ACPIShutdownSupported)
|
|
{
|
|
uint8_t val = 0x02;
|
|
while (val & 0x02)
|
|
val = inb(0x64);
|
|
outb(0x64, 0xFE);
|
|
|
|
warn("Executing the second attempt to reboot...");
|
|
|
|
// second attempt to reboot
|
|
// https://wiki.osdev.org/Reboot
|
|
uint8_t temp;
|
|
asm volatile("cli");
|
|
do
|
|
{
|
|
temp = inb(0x64);
|
|
if (((temp) & (1 << (0))) != 0)
|
|
inb(0x60);
|
|
} while (((temp) & (1 << (1))) != 0);
|
|
outb(0x64, 0xFE);
|
|
|
|
CPU::Stop();
|
|
}
|
|
switch (acpi->FADT->ResetReg.AddressSpace)
|
|
{
|
|
case ACPI_GAS_MMIO:
|
|
*(uint8_t *)(acpi->FADT->ResetReg.Address) = acpi->FADT->ResetValue;
|
|
break;
|
|
case ACPI_GAS_IO:
|
|
outb(acpi->FADT->ResetReg.Address, acpi->FADT->ResetValue);
|
|
break;
|
|
case ACPI_GAS_PCI:
|
|
fixme("ACPI_GAS_PCI not supported.");
|
|
/*
|
|
seg - 0
|
|
bus - 0
|
|
dev - (FADT->ResetReg.Address >> 32) & 0xFFFF
|
|
function - (FADT->ResetReg.Address >> 16) & 0xFFFF
|
|
offset - FADT->ResetReg.Address & 0xFFFF
|
|
value - FADT->ResetValue
|
|
*/
|
|
break;
|
|
}
|
|
CPU::Stop();
|
|
}
|
|
|
|
uint16_t DSDT::GetSCIevent()
|
|
{
|
|
uint16_t a = 0, b = 0;
|
|
if (acpi->FADT->PM1aEventBlock)
|
|
{
|
|
a = inw(acpi->FADT->PM1aEventBlock);
|
|
outw(acpi->FADT->PM1aEventBlock, a);
|
|
}
|
|
if (acpi->FADT->PM1bEventBlock)
|
|
{
|
|
b = inw(acpi->FADT->PM1bEventBlock);
|
|
outw(acpi->FADT->PM1bEventBlock, b);
|
|
}
|
|
return a | b;
|
|
}
|
|
|
|
void DSDT::SetSCIevent(uint16_t value)
|
|
{
|
|
uint16_t a = acpi->FADT->PM1aEventBlock + (acpi->FADT->PM1EventLength / 2);
|
|
uint16_t b = acpi->FADT->PM1bEventBlock + (acpi->FADT->PM1EventLength / 2);
|
|
if (acpi->FADT->PM1aEventBlock)
|
|
outw(a, value);
|
|
if (acpi->FADT->PM1bEventBlock)
|
|
outw(b, value);
|
|
}
|
|
|
|
void DSDT::RegisterSCIEvents()
|
|
{
|
|
this->SetSCIevent(ACPI_POWER_BUTTON | ACPI_SLEEP_BUTTON | ACPI_WAKE);
|
|
GetSCIevent();
|
|
}
|
|
|
|
void DSDT::InitSCI()
|
|
{
|
|
// this should be done for all CPUs
|
|
if (ACPIShutdownSupported)
|
|
{
|
|
debug("Registering SCI Handler to vector IRQ%d", acpi->FADT->SCI_Interrupt);
|
|
this->RegisterSCIEvents();
|
|
// RegisterInterrupt(this->SCIHandler, acpi->FADT->SCI_Interrupt + CPU::x64::IRQ0, true, true);
|
|
}
|
|
}
|
|
|
|
DSDT::DSDT(ACPI *acpi)
|
|
{
|
|
uint64_t Address = ((IsCanonical(acpi->FADT->X_Dsdt) && acpi->XSDTSupported) ? acpi->FADT->X_Dsdt : acpi->FADT->Dsdt);
|
|
uint8_t *S5Address = (uint8_t *)(Address) + 36;
|
|
ACPI::ACPI::ACPIHeader *Header = (ACPI::ACPI::ACPIHeader *)Address;
|
|
uint64_t Length = Header->Length;
|
|
Address *= 2;
|
|
while (Length-- > 0)
|
|
{
|
|
if (!memcmp(S5Address, "_S5_", 4))
|
|
break;
|
|
S5Address++;
|
|
}
|
|
if (Length <= 0)
|
|
{
|
|
warn("_S5 not present in ACPI");
|
|
return;
|
|
}
|
|
if ((*(S5Address - 1) == 0x08 || (*(S5Address - 2) == 0x08 && *(S5Address - 1) == '\\')) && *(S5Address + 4) == 0x12)
|
|
{
|
|
S5Address += 5;
|
|
S5Address += ((*S5Address & 0xC0) >> 6) + 2;
|
|
if (*S5Address == 0x0A)
|
|
S5Address++;
|
|
SLP_TYPa = *(S5Address) << 10;
|
|
S5Address++;
|
|
if (*S5Address == 0x0A)
|
|
S5Address++;
|
|
SLP_TYPb = *(S5Address) << 10;
|
|
SMI_CMD = acpi->FADT->SMI_CommandPort;
|
|
ACPI_ENABLE = acpi->FADT->AcpiEnable;
|
|
ACPI_DISABLE = acpi->FADT->AcpiDisable;
|
|
PM1a_CNT = acpi->FADT->PM1aControlBlock;
|
|
PM1b_CNT = acpi->FADT->PM1bControlBlock;
|
|
PM1_CNT_LEN = acpi->FADT->PM1ControlLength;
|
|
SLP_EN = 1 << 13;
|
|
SCI_EN = 1;
|
|
trace("ACPI Shutdown is supported");
|
|
ACPIShutdownSupported = true;
|
|
return;
|
|
}
|
|
warn("Failed to parse _S5 in ACPI");
|
|
SCI_EN = 0;
|
|
}
|
|
|
|
DSDT::~DSDT()
|
|
{
|
|
}
|
|
}
|