Kernel/core/dsdt.cpp

335 lines
7.9 KiB
C++

/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <acpi.hpp>
#include <time.hpp>
#include <debug.h>
#include <smp.hpp>
#include <io.h>
#if defined(a64)
#include "../arch/amd64/cpu/apic.hpp"
#elif defined(a32)
#include "../arch/i386/cpu/apic.hpp"
#endif
#include "../kernel.h"
#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
extern std::atomic<bool> ExceptionLock;
namespace ACPI
{
__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::OnInterruptReceived(CPU::TrapFrame *)
{
debug("SCI Handle Triggered");
uint16_t Event = 0;
{
uint16_t a = 0, b = 0;
if (acpi->FADT->PM1aEventBlock)
{
a = inw(s_cst(uint16_t, acpi->FADT->PM1aEventBlock));
outw(s_cst(uint16_t, acpi->FADT->PM1aEventBlock), a);
}
if (acpi->FADT->PM1bEventBlock)
{
b = inw(s_cst(uint16_t, acpi->FADT->PM1bEventBlock));
outw(s_cst(uint16_t, acpi->FADT->PM1bEventBlock), b);
}
Event = a | b;
}
#ifdef DEBUG
char dbgEvStr[128];
dbgEvStr[0] = '\0';
if (Event & ACPI_BUSMASTER)
strcat(dbgEvStr, "BUSMASTER ");
if (Event & ACPI_GLOBAL)
strcat(dbgEvStr, "GLOBAL ");
if (Event & ACPI_POWER_BUTTON)
strcat(dbgEvStr, "POWER_BUTTON ");
if (Event & ACPI_SLEEP_BUTTON)
strcat(dbgEvStr, "SLEEP_BUTTON ");
if (Event & ACPI_RTC_ALARM)
strcat(dbgEvStr, "RTC_ALARM ");
if (Event & ACPI_PCIE_WAKE)
strcat(dbgEvStr, "PCIE_WAKE ");
if (Event & ACPI_WAKE)
strcat(dbgEvStr, "WAKE ");
if (Event & ACPI_TIMER)
strcat(dbgEvStr, "ACPI_TIMER ");
KPrint("SCI Event: %s", dbgEvStr);
#endif
if (Event & ACPI_BUSMASTER)
{
fixme("ACPI Busmaster");
}
else if (Event & ACPI_GLOBAL)
{
fixme("ACPI Global");
}
else if (Event & ACPI_POWER_BUTTON)
{
if (ExceptionLock.load())
{
this->Shutdown();
CPU::Stop();
}
Tasking::PCB *pcb = thisProcess;
if (pcb && !pcb->GetContext()->IsPanic())
{
Tasking::Task *ctx = pcb->GetContext();
ctx->CreateThread(ctx->GetKernelProcess(),
Tasking::IP(KST_Shutdown))
->Rename("Shutdown");
}
else
KernelShutdownThread(false);
}
else if (Event & ACPI_SLEEP_BUTTON)
{
fixme("ACPI Sleep Button");
}
else if (Event & ACPI_RTC_ALARM)
{
fixme("ACPI RTC Alarm");
}
else if (Event & ACPI_PCIE_WAKE)
{
fixme("ACPI PCIe Wake");
}
else if (Event & ACPI_WAKE)
{
fixme("ACPI Wake");
}
else if (Event & ACPI_TIMER)
{
fixme("ACPI Timer");
}
else
{
error("ACPI unknown event %#lx on CPU %d",
Event, GetCurrentCPU()->ID);
KPrint("ACPI unknown event %#lx on CPU %d",
Event, GetCurrentCPU()->ID);
}
}
void DSDT::Shutdown()
{
trace("Shutting down...");
if (SCI_EN != 1)
{
error("ACPI Shutdown not supported");
return;
}
if (inw(s_cst(uint16_t, PM1a_CNT) & SCI_EN) == 0)
{
KPrint("ACPI was disabled, enabling...");
if (SMI_CMD == 0 || ACPI_ENABLE == 0)
{
error("ACPI Shutdown not supported");
KPrint("ACPI Shutdown not supported");
return;
}
outb(s_cst(uint16_t, SMI_CMD), ACPI_ENABLE);
uint16_t Timeout = 3000;
while ((inw(s_cst(uint16_t, PM1a_CNT)) & SCI_EN) == 0 && Timeout-- > 0)
;
if (Timeout == 0)
{
error("ACPI Shutdown not supported");
KPrint("ACPI Shutdown not supported");
return;
}
if (PM1b_CNT)
{
Timeout = 3000;
while ((inw(s_cst(uint16_t, PM1b_CNT)) & SCI_EN) == 0 && Timeout-- > 0)
;
}
}
outw(s_cst(uint16_t, PM1a_CNT), SLP_TYPa | SLP_EN);
if (PM1b_CNT)
outw(s_cst(uint16_t, PM1b_CNT), SLP_TYPb | SLP_EN);
}
void DSDT::Reboot()
{
trace("Rebooting...");
switch (acpi->FADT->ResetReg.AddressSpace)
{
case ACPI_GAS_MMIO:
{
*(uint8_t *)(acpi->FADT->ResetReg.Address) = acpi->FADT->ResetValue;
break;
}
case ACPI_GAS_IO:
{
outb(s_cst(uint16_t, 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;
}
default:
{
error("Unknown reset register address space: %d", acpi->FADT->ResetReg.AddressSpace);
break;
}
}
}
DSDT::DSDT(ACPI *acpi) : Interrupts::Handler(acpi->FADT->SCI_Interrupt, true)
{
/* TODO: AML Interpreter */
this->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;
Memory::Virtual vmm;
if (!vmm.Check(Header))
{
warn("DSDT is not mapped");
debug("DSDT: %#lx", Address);
vmm.Map(Header, Header, Memory::RW);
}
size_t Length = Header->Length;
vmm.Map(Header, Header, Length, Memory::RW);
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 = s_cst(uint16_t, *(S5Address) << 10);
S5Address++;
if (*S5Address == 0x0A)
S5Address++;
SLP_TYPb = s_cst(uint16_t, *(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;
KPrint("ACPI Shutdown is supported");
ACPIShutdownSupported = true;
{
const uint16_t value = /*ACPI_TIMER |*/ ACPI_BUSMASTER | ACPI_GLOBAL |
ACPI_POWER_BUTTON | ACPI_SLEEP_BUTTON | ACPI_RTC_ALARM |
ACPI_PCIE_WAKE | ACPI_WAKE;
uint16_t a = s_cst(uint16_t, acpi->FADT->PM1aEventBlock + (acpi->FADT->PM1EventLength / 2));
uint16_t b = s_cst(uint16_t, acpi->FADT->PM1bEventBlock + (acpi->FADT->PM1EventLength / 2));
debug("SCI Event: %#x [a:%#x b:%#x]", value, a, b);
if (acpi->FADT->PM1aEventBlock)
outw(a, value);
if (acpi->FADT->PM1bEventBlock)
outw(b, value);
}
{
uint16_t a = 0, b = 0;
if (acpi->FADT->PM1aEventBlock)
{
a = inw(s_cst(uint16_t, acpi->FADT->PM1aEventBlock));
outw(s_cst(uint16_t, acpi->FADT->PM1aEventBlock), a);
}
if (acpi->FADT->PM1bEventBlock)
{
b = inw(s_cst(uint16_t, acpi->FADT->PM1bEventBlock));
outw(s_cst(uint16_t, acpi->FADT->PM1bEventBlock), b);
}
}
((APIC::APIC *)Interrupts::apic[0])->RedirectIRQ(0, uint8_t(acpi->FADT->SCI_Interrupt), 1);
return;
}
warn("Failed to parse _S5_ in ACPI");
SCI_EN = 0;
}
DSDT::~DSDT()
{
}
}