5#include "pedigree/kernel/Log.h"
6#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
7#include "pedigree/kernel/processor/VirtualAddressSpace.h"
8#include "pedigree/kernel/utilities/utility.h"
13uint16_t readControl(uint16_t port) {
15 asm volatile(
"inw %1, %0" :
"=a"(value) :
"Nd"(port));
19void writeControl(uint16_t port, uint16_t value) {
20 asm volatile(
"outw %0, %1" : :
"a"(value),
"Nd"(port));
23void writeCommand(uint16_t port, uint8_t value) {
24 asm volatile(
"outb %0, %1" : :
"a"(value),
"Nd"(port));
28void Acpi::initialisePowerManagement() {
29 const uint8_t* fadt =
reinterpret_cast<const uint8_t*
>(m_pFacp);
30 if (m_pFacp->header.length >= 129 && (m_pFacp->flags & (1U << 10))) {
32 MemoryCopy(&address, fadt + 120,
sizeof(address));
35 if (fadt[116] == 1 && fadt[117] == 8 && fadt[118] == 0 && fadt[119] <= 1 && address &&
37 m_ResetPort = address;
38 m_ResetValue = fadt[128];
39 NOTICE(
"ACPI: reset port " <<
Hex << m_ResetPort);
44 if ((m_pFacp->flags & (1U << 20)) || m_pFacp->pm1ControlLength < 2 ||
45 !m_pFacp->pm1aControlBlock || m_pFacp->pm1aControlBlock > 0xfffe ||
46 m_pFacp->pm1bControlBlock > 0xfffe)
49 uint64_t dsdtAddress = m_pFacp->dsdt;
50 if (m_pFacp->header.length >= 148) {
51 uint64_t extendedAddress;
52 MemoryCopy(&extendedAddress, fadt + 140,
sizeof(extendedAddress));
54 dsdtAddress = extendedAddress;
56 if (!dsdtAddress || dsdtAddress > ~uintptr_t(0))
60 const physical_uintptr_t page = dsdtAddress & ~(pageSize - 1);
61 const size_t offset = dsdtAddress - page;
66 if (!pmm.allocateRegion(dsdt,
67 (offset +
sizeof(SystemDescriptionTableHeader) + pageSize - 1) / pageSize,
70 const auto* table = dsdt.convertPhysicalPointer<SystemDescriptionTableHeader>(dsdtAddress);
71 const size_t length = table->length;
72 if (table->signature != 0x54445344 || length <
sizeof(*table) || length > 1024 * 1024)
75 if (!pmm.allocateRegion(dsdt, (offset + length + pageSize - 1) / pageSize, flags,
78 table = dsdt.convertPhysicalPointer<SystemDescriptionTableHeader>(dsdtAddress);
79 if (table->length != length || !checksum(table))
81 const uint8_t* bytes =
reinterpret_cast<const uint8_t*
>(table);
82 if (AcpiS5::mayHaveSleepHooks(bytes +
sizeof(*table), length -
sizeof(*table))) {
83 WARNING(
"ACPI: firmware sleep methods require an interpreter; static poweroff unavailable");
87 const size_t entries = (m_pRsdt->length -
sizeof(*m_pRsdt)) /
sizeof(uint32_t);
88 const uint8_t* entry =
reinterpret_cast<const uint8_t*
>(m_pRsdt) +
sizeof(*m_pRsdt);
89 for (
size_t i = 0; i < entries; ++i) {
91 MemoryCopy(&address, entry + i *
sizeof(address),
sizeof(address));
92 const uint64_t base = m_AcpiMemoryRegion.physicalAddress();
93 const uint64_t regionSize = m_AcpiMemoryRegion.size();
94 if (address < base || address - base > regionSize ||
95 regionSize - (address - base) <
sizeof(SystemDescriptionTableHeader))
97 const auto* secondary =
98 m_AcpiMemoryRegion.convertPhysicalPointer<SystemDescriptionTableHeader>(address);
99 if (secondary->signature != 0x54445353 && secondary->signature != 0x54445350)
101 if (secondary->length <
sizeof(*secondary) ||
102 secondary->length > regionSize - (address - base) || !checksum(secondary))
104 const auto* aml =
reinterpret_cast<const uint8_t*
>(secondary) +
sizeof(*secondary);
105 if (AcpiS5::mayHaveSleepHooks(aml, secondary->length -
sizeof(*secondary))) {
107 "ACPI: secondary firmware sleep methods require an interpreter; static poweroff "
113 AcpiS5::find(bytes +
sizeof(*table), bytes + length, m_SleepTypeA, m_SleepTypeB);
114 if (m_PowerOffValid) {
115 NOTICE(
"ACPI: static S5 sleep types " <<
Dec << m_SleepTypeA <<
", " << m_SleepTypeB);
117 WARNING(
"ACPI: no supported static root _S5 package; poweroff unavailable");
121void Acpi::setPowerManagement(
const PowerManagement* provider) {
122 __atomic_store_n(&m_PowerManagement, provider, __ATOMIC_RELEASE);
125bool Acpi::prepareShutdown(
bool powerOff) {
126 const auto* provider = __atomic_load_n(&m_PowerManagement, __ATOMIC_ACQUIRE);
127 return !provider || provider->prepare(powerOff);
131 const auto* provider = __atomic_load_n(&m_PowerManagement, __ATOMIC_ACQUIRE);
132 if (provider && provider->reset) {
137 writeCommand(m_ResetPort, m_ResetValue);
138 for (
size_t i = 0; i < 100000; ++i)
139 asm volatile(
"pause");
142bool Acpi::supportsPowerOff()
const {
143 const auto* provider = __atomic_load_n(&m_PowerManagement, __ATOMIC_ACQUIRE);
144 if (provider && provider->powerOff) {
147 return m_PowerOffValid && ((readControl(m_pFacp->pm1aControlBlock) & 1) ||
148 (m_pFacp->smiCommandPort && m_pFacp->smiCommandPort <= 0xffff &&
149 m_pFacp->acpiEnableCommand));
152const char* Acpi::powerOff() {
153 const auto* provider = __atomic_load_n(&m_PowerManagement, __ATOMIC_ACQUIRE);
154 if (provider && provider->powerOff) {
155 return provider->powerOff();
157 if (!m_PowerOffValid) {
158 return "ACPI: no supported S5 power-off method is available.";
160 const uint16_t portA = m_pFacp->pm1aControlBlock;
161 const uint16_t portB = m_pFacp->pm1bControlBlock;
162 if (!(readControl(portA) & 1)) {
163 if (!m_pFacp->smiCommandPort || m_pFacp->smiCommandPort > 0xffff ||
164 !m_pFacp->acpiEnableCommand) {
165 return "ACPI: firmware provides no usable ACPI-mode enable command.";
169 writeCommand(m_pFacp->smiCommandPort, m_pFacp->acpiEnableCommand);
170 size_t attempts = 1000000;
171 while (!(readControl(portA) & 1) && --attempts)
172 asm volatile(
"pause");
174 ERROR_NOLOCK(
"ACPI: timed out enabling ACPI mode for power off");
175 return "ACPI: timed out enabling ACPI mode for power-off.";
179 const uint16_t sleepMask = (7U << 10) | (1U << 13);
180 const uint16_t controlA = (readControl(portA) & ~sleepMask) | (m_SleepTypeA << 10);
181 const uint16_t controlB = portB ? (readControl(portB) & ~sleepMask) | (m_SleepTypeB << 10) : 0;
183 writeControl(portA, controlA);
185 writeControl(portB, controlB);
186 asm volatile(
"wbinvd" : : :
"memory");
187 writeControl(portA, controlA | (1U << 13));
189 writeControl(portB, controlB | (1U << 13));
190 for (
size_t i = 0; i < 1000000; ++i)
191 asm volatile(
"pause");
192 return "ACPI: static S5 register writes returned without powering off.";
Special memory entity in the kernel's virtual address space.
static const size_t continuous
static PhysicalMemoryManager & instance()
static constexpr size_t getPageSize() PURE
static const size_t force
static const size_t nonRamMemory
static const size_t KernelMode