/* 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 . */ #include #include #include #include #include #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 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() { } }