The Pedigree Project 0.1
modules/system/acpi/main.cc
1/* Copyright (c) 2026, Pedigree Developers. SPDX-License-Identifier: ISC */
2#include "pedigree/kernel/Log.h"
3#include "pedigree/kernel/machine/Machine.h"
4#include "pedigree/kernel/machine/PciFirmware.h"
5#include "pedigree/kernel/utilities/StaticString.h"
6
7#include "AcpiEc.h"
8#include "AcpiEvents.h"
9#include "PciRouting.h"
10#include "modules/Module.h"
11#include <uacpi/namespace.h>
12#include <uacpi/sleep.h>
13#include <uacpi/uacpi.h>
14
15#if MACH_PC && ACPI
16#include "system/kernel/machine/mach_pc/Acpi.h"
17#endif
18
19namespace {
20#if MACH_PC && ACPI
21bool prepareShutdown(bool powerOff);
22Acpi::PowerManagement powerManagement{&prepareShutdown, nullptr, nullptr};
23bool sleepPrepared = false;
24uacpi_status sleepPreparationResult = UACPI_STATUS_OK;
25StaticString<LOG_LENGTH> sleepPreparationDetail;
26char sleepLogSnapshot[4096];
27
28bool prepareShutdown(bool powerOff) {
29 if (powerOff && powerManagement.powerOff) {
30 Machine::setShutdownPhase(Machine::ShutdownPhase::Firmware, "ACPI: preparing S5");
31 installAcpiEcHandlersForShutdown();
32 NOTICE("ACPI: preparing S5 via uACPI");
33 const auto result = uacpi_prepare_for_sleep_state(UACPI_SLEEP_STATE_S5);
34 sleepPreparationResult = result;
35 sleepPrepared = result == UACPI_STATUS_OK;
36 sleepPreparationDetail.clear();
37 if (!sleepPrepared) {
38 // The final shutdown screen replaces the log, so retain the operation
39 // region error that identifies a missing firmware handler.
40 const size_t bytes = Log::instance().copyText(sleepLogSnapshot, sizeof(sleepLogSnapshot));
41 size_t start = 0;
42 for (size_t i = 0; i < bytes; ++i) {
43 if (sleepLogSnapshot[i] != '\n') {
44 continue;
45 }
46 StaticString<LOG_LENGTH> entry(sleepLogSnapshot + start, i - start);
47 start = i + 1;
48 if (entry.contains("ACPI: preparing S5 via uACPI")) {
49 sleepPreparationDetail.clear();
50 } else if (entry.contains("unable to attach") && entry.contains("operation region")) {
51 sleepPreparationDetail = entry;
52 } else if (!sleepPreparationDetail.length() && entry.contains("error while evaluating")) {
53 sleepPreparationDetail = entry;
54 }
55 }
56 ERROR("ACPI: S5 preparation failed: " << uacpi_status_to_string(result));
57 }
58 }
59 Machine::setShutdownPhase(Machine::ShutdownPhase::Firmware, "ACPI: draining event workers");
60 return shutdownAcpiEvents();
61}
62
63const char* powerOff() {
64 // This pinned provider retains the result until the kernel copies it for
65 // display after processor teardown. Terminal shutdown has one owner.
67 message.clear();
68 if (!sleepPrepared) {
69 message += "ACPI: S5 preparation failed: ";
70 message += uacpi_status_to_string(sleepPreparationResult);
71 if (sleepPreparationDetail.length()) {
72 message += "\n";
73 message += sleepPreparationDetail;
74 }
75 return message;
76 }
77 NOTICE_NOLOCK("ACPI: entering S5 via uACPI");
78 Machine::setShutdownPhase(Machine::ShutdownPhase::FinalAction, "ACPI: entering S5");
79 const auto result = uacpi_enter_sleep_state(UACPI_SLEEP_STATE_S5);
80 message += "ACPI: S5 entry returned: ";
81 message += uacpi_status_to_string(result);
82 ERROR_NOLOCK(static_cast<const char*>(message));
83 return message;
84}
85
86void reset() {
87 NOTICE_NOLOCK("ACPI: resetting via uACPI");
88 Machine::setShutdownPhase(Machine::ShutdownPhase::FinalAction, "ACPI: requesting reset");
89 const auto result = uacpi_reboot();
90 ERROR_NOLOCK("ACPI: reset returned: " << uacpi_status_to_string(result));
91}
92#endif
93
94bool entry() {
95 uacpi_phys_addr rsdp = 0;
96 if (uacpi_kernel_get_rsdp(&rsdp) != UACPI_STATUS_OK) {
97 (void)PciFirmware::discover();
98 return true;
99 }
100 if (!initialiseAcpiEvents()) {
101 return false;
102 }
103 uacpi_status result = uacpi_initialize(UACPI_FLAG_BAD_CSUM_FATAL);
104#if MACH_PC && ACPI
105 if (result == UACPI_STATUS_OK) {
106 powerManagement.reset = &reset;
107 }
108#endif
109 if (result == UACPI_STATUS_OK) {
110 result = uacpi_namespace_load();
111 }
112 if (result == UACPI_STATUS_OK) {
113 result = uacpi_namespace_initialize();
114 }
115 if (result != UACPI_STATUS_OK) {
116 ERROR("ACPI: namespace unavailable: " << uacpi_status_to_string(result));
117#if MACH_PC && ACPI
118 Acpi::instance().setPowerManagement(&powerManagement);
119#endif
120 return true;
121 }
122 NOTICE("ACPI: AML namespace initialized");
123#if MACH_PC && ACPI
124 uacpi_namespace_node* s5 = nullptr;
125 if (uacpi_namespace_node_find(uacpi_namespace_root(), "_S5", &s5) == UACPI_STATUS_OK) {
126 powerManagement.powerOff = &powerOff;
127 }
128 Acpi::instance().setPowerManagement(&powerManagement);
129#endif
130 (void)PciFirmware::discover();
131 initialisePciRouting();
132 return true;
133}
134void exit() {}
135} // namespace
136
137MODULE_INFO_NON_UNLOADABLE("acpi", &entry, &exit);
static EXPORTED_PUBLIC Log & instance()
Definition Log.cc:117
EXPORTED_PUBLIC size_t copyText(char *buffer, size_t capacity)
Definition Log.cc:454
static void setShutdownPhase(ShutdownPhase phase, const char *detail=nullptr)
Definition Machine.cc:33