diff --git a/kernel/include/kernel/ACPI/ACPI.h b/kernel/include/kernel/ACPI/ACPI.h index 20d3abb1..7c8da794 100644 --- a/kernel/include/kernel/ACPI/ACPI.h +++ b/kernel/include/kernel/ACPI/ACPI.h @@ -8,7 +8,7 @@ namespace Kernel::ACPI { - class ACPI + class ACPI : public Interruptable { public: static BAN::ErrorOr initialize(); @@ -31,10 +31,16 @@ namespace Kernel::ACPI // This function will return only if there was an error void poweroff(); + void handle_irq() override; + private: ACPI() = default; BAN::ErrorOr initialize_impl(); + FADT& fadt() { return *m_fadt; } + + void acpi_event_task(); + private: paddr_t m_header_table_paddr = 0; vaddr_t m_header_table_vaddr = 0; @@ -49,6 +55,10 @@ namespace Kernel::ACPI }; BAN::Vector m_mapped_headers; + FADT* m_fadt { nullptr }; + + Semaphore m_acpi_event_semaphore; + bool m_hardware_reduced { false }; BAN::RefPtr m_namespace; }; diff --git a/kernel/include/kernel/Process.h b/kernel/include/kernel/Process.h index 056cc93c..5ebc52c5 100644 --- a/kernel/include/kernel/Process.h +++ b/kernel/include/kernel/Process.h @@ -144,6 +144,7 @@ namespace Kernel BAN::ErrorOr sys_sync(bool should_block); + static BAN::ErrorOr clean_poweroff(int command); BAN::ErrorOr sys_poweroff(int command); BAN::ErrorOr mount(BAN::StringView source, BAN::StringView target); diff --git a/kernel/kernel/ACPI/ACPI.cpp b/kernel/kernel/ACPI/ACPI.cpp index 0e2dd0c9..29d150d0 100644 --- a/kernel/kernel/ACPI/ACPI.cpp +++ b/kernel/kernel/ACPI/ACPI.cpp @@ -7,8 +7,10 @@ #include #include #include +#include #include #include +#include #include #define RSPD_SIZE 20 @@ -77,11 +79,11 @@ acpi_release_global_lock: enum PM1Event : uint16_t { - PM1_EVN_TMR_EN = 1 << 0, - PM1_EVN_GBL_EN = 1 << 5, - PM1_EVN_PWRBTN_EN = 1 << 8, - PM1_EVN_SLPBTN_EN = 1 << 8, - PM1_EVN_RTC_EN = 1 << 10, + PM1_EVN_TMR = 1 << 0, + PM1_EVN_GBL = 1 << 5, + PM1_EVN_PWRBTN = 1 << 8, + PM1_EVN_SLPBTN = 1 << 8, + PM1_EVN_RTC = 1 << 10, PM1_EVN_PCIEXP_WAKE_DIS = 1 << 14, }; @@ -315,10 +317,14 @@ acpi_release_global_lock: .vaddr = dsdt_vaddr })); + m_fadt = fadt; m_hardware_reduced = fadt->flags & (1 << 20); } } + if (m_fadt == nullptr) + Kernel::panic("No FADT found"); + return {}; } @@ -404,21 +410,19 @@ acpi_release_global_lock: dprintln("Entering sleep state S5"); - auto* fadt = static_cast(get_header("FACP", 0)); - - uint16_t pm1a_data = IO::inw(fadt->pm1a_cnt_blk); + uint16_t pm1a_data = IO::inw(fadt().pm1a_cnt_blk); pm1a_data &= ~(PM1_CNT_SLP_TYP_MASK << PM1_CNT_SLP_TYP_SHIFT); pm1a_data |= (slp_typa.value() & PM1_CNT_SLP_TYP_MASK) << PM1_CNT_SLP_TYP_SHIFT; pm1a_data |= PM1_CNT_SLP_EN; - IO::outw(fadt->pm1a_cnt_blk, pm1a_data); + IO::outw(fadt().pm1a_cnt_blk, pm1a_data); - if (fadt->pm1b_cnt_blk != 0) + if (fadt().pm1b_cnt_blk != 0) { - uint16_t pm1b_data = IO::inw(fadt->pm1b_cnt_blk); + uint16_t pm1b_data = IO::inw(fadt().pm1b_cnt_blk); pm1b_data &= ~(PM1_CNT_SLP_TYP_MASK << PM1_CNT_SLP_TYP_SHIFT); pm1b_data |= (slp_typb.value() & PM1_CNT_SLP_TYP_MASK) << PM1_CNT_SLP_TYP_SHIFT; pm1b_data |= PM1_CNT_SLP_EN; - IO::outw(fadt->pm1b_cnt_blk, pm1b_data); + IO::outw(fadt().pm1b_cnt_blk, pm1b_data); } } @@ -430,31 +434,30 @@ acpi_release_global_lock: return BAN::Error::from_errno(EFAULT); // https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/16_Waking_and_Sleeping/initialization.html#placing-the-system-in-acpi-mode - auto* fadt = static_cast(get_header("FACP", 0)); // If not hardware-reduced ACPI and SCI_EN is not set - if (!hardware_reduced() && !(IO::inw(fadt->pm1a_cnt_blk) & PM1_CNT_SCI_EN)) + if (!hardware_reduced() && !(IO::inw(fadt().pm1a_cnt_blk) & PM1_CNT_SCI_EN)) { // https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/04_ACPI_Hardware_Specification/ACPI_Hardware_Specification.html#legacy-acpi-select-and-the-sci-interrupt - IO::outb(fadt->smi_cmd, fadt->acpi_enable); + IO::outb(fadt().smi_cmd, fadt().acpi_enable); // Spec says to poll until SCI_EN is set, but doesn't specify timeout for (size_t i = 0; i < 100; i++) { - if (IO::inw(fadt->pm1a_cnt_blk) & PM1_CNT_SCI_EN) + if (IO::inw(fadt().pm1a_cnt_blk) & PM1_CNT_SCI_EN) break; SystemTimer::get().sleep(10); } - if (!(IO::inw(fadt->pm1a_cnt_blk) & PM1_CNT_SCI_EN)) + if (!(IO::inw(fadt().pm1a_cnt_blk) & PM1_CNT_SCI_EN)) { dwarnln("Failed to enable ACPI mode"); return BAN::Error::from_errno(EINVAL); } // Enable power and sleep buttons - IO::outw(fadt->pm1a_evt_blk + fadt->pm1_evt_len / 2, PM1_EVN_PWRBTN_EN | PM1_EVN_SLPBTN_EN); - IO::outw(fadt->pm1b_evt_blk + fadt->pm1_evt_len / 2, PM1_EVN_PWRBTN_EN | PM1_EVN_SLPBTN_EN); + IO::outw(fadt().pm1a_evt_blk + fadt().pm1_evt_len / 2, PM1_EVN_PWRBTN | PM1_EVN_SLPBTN); + IO::outw(fadt().pm1b_evt_blk + fadt().pm1_evt_len / 2, PM1_EVN_PWRBTN | PM1_EVN_SLPBTN); } dprintln("Entered ACPI mode"); @@ -488,7 +491,67 @@ acpi_release_global_lock: dprintln("Devices are initialized"); + uint8_t irq = fadt().sci_int; + if (auto ret = InterruptController::get().reserve_irq(irq); ret.is_error()) + dwarnln("Could not enable ACPI interrupt: {}", ret.error()); + else + { + set_irq(irq); + enable_interrupt(); + Process::create_kernel([](void*) { get().acpi_event_task(); }, nullptr); + } + return {}; } + void ACPI::acpi_event_task() + { + auto get_fixed_event = + [&](uint16_t sts_port) + { + auto sts = IO::inw(sts_port); + auto en = IO::inw(sts_port + fadt().pm1_evt_len / 2); + if (auto pending = sts & en) + return pending & ~(pending - 1); + return 0; + }; + + while (true) + { + uint16_t sts_port; + uint16_t pending; + + sts_port = fadt().pm1a_evt_blk; + if (pending = get_fixed_event(sts_port); pending) + goto handle_event; + + sts_port = fadt().pm1b_evt_blk; + if (pending = get_fixed_event(sts_port); pending) + goto handle_event; + + // FIXME: this can cause missing of event if it happens between + // reading the status and blocking + m_acpi_event_semaphore.block_with_timeout(100); + continue; + +handle_event: + if (pending & PM1_EVN_PWRBTN) + { + if (auto ret = Process::clean_poweroff(POWEROFF_SHUTDOWN); ret.is_error()) + dwarnln("Failed to poweroff: {}", ret.error()); + } + else + { + dwarnln("Unhandled ACPI fixed event {H}", pending); + } + + IO::outw(sts_port, pending); + } + } + + void ACPI::handle_irq() + { + m_acpi_event_semaphore.unblock(); + } + } diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index c260b949..c388c844 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -1198,7 +1198,7 @@ namespace Kernel IDT::force_triple_fault(); } - BAN::ErrorOr Process::sys_poweroff(int command) + BAN::ErrorOr Process::clean_poweroff(int command) { if (command != POWEROFF_REBOOT && command != POWEROFF_SHUTDOWN) return BAN::Error::from_errno(EINVAL); @@ -1215,6 +1215,11 @@ namespace Kernel return BAN::Error::from_errno(EUNKNOWN); } + BAN::ErrorOr Process::sys_poweroff(int command) + { + return clean_poweroff(command); + } + BAN::ErrorOr Process::sys_readdir(int fd, DirectoryEntryList* list, size_t list_size) { LockGuard _(m_process_lock);