The Pedigree Project 0.1
KernelApi.cc
1/* Copyright (c) 2026, Pedigree Developers. SPDX-License-Identifier: ISC */
2#include "pedigree/kernel/BootstrapInfo.h"
3#include "pedigree/kernel/LockGuard.h"
4#include "pedigree/kernel/Log.h"
5#include "pedigree/kernel/Spinlock.h"
6#include "pedigree/kernel/machine/Device.h"
7#include "pedigree/kernel/machine/Pci.h"
8#include "pedigree/kernel/panic.h"
9#include "pedigree/kernel/process/Mutex.h"
10#include "pedigree/kernel/process/Semaphore.h"
11#include "pedigree/kernel/processor/MemoryRegion.h"
12#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
13#include "pedigree/kernel/processor/Processor.h"
14#include "pedigree/kernel/processor/VirtualAddressSpace.h"
15#include "pedigree/kernel/time/Time.h"
16#include "pedigree/kernel/utilities/new"
17
18#include <uacpi/kernel_api.h>
19
20namespace {
21struct Mapping {
22 MemoryRegion region{"ACPI firmware"};
23 void* address = nullptr;
24 size_t bytes = 0;
25 Mapping* next = nullptr;
26};
27Mutex mappingsLock;
28Mapping* mappings = nullptr;
29
30struct PortRange {
31 uint16_t base;
32 size_t bytes;
33};
34
35bool wait(Semaphore* semaphore, uint16_t milliseconds) {
36 if (!milliseconds) {
37 return semaphore->tryAcquire();
38 }
39 if (milliseconds == 0xffff) {
40 return semaphore->acquireForCompletion();
41 }
42 return semaphore->acquireForCompletion(1, milliseconds / 1000, (milliseconds % 1000) * 1000);
43}
44
45uacpi_status status(bool success) {
46 return success ? UACPI_STATUS_OK : UACPI_STATUS_INTERNAL_ERROR;
47}
48
49template <class T>
50uacpi_status readPort(uacpi_handle handle, size_t offset, T* value) {
51 const auto* range = static_cast<const PortRange*>(handle);
52 if (!range || !value || offset >= range->bytes || sizeof(T) > range->bytes - offset) {
53 return UACPI_STATUS_INVALID_ARGUMENT;
54 }
55#if X86 || X64
56 const uint16_t port = range->base + offset;
57 if constexpr (sizeof(T) == 1) {
58 asm volatile("inb %1, %0" : "=a"(*value) : "Nd"(port));
59 } else if constexpr (sizeof(T) == 2) {
60 asm volatile("inw %1, %0" : "=a"(*value) : "Nd"(port));
61 } else {
62 asm volatile("inl %1, %0" : "=a"(*value) : "Nd"(port));
63 }
64 return UACPI_STATUS_OK;
65#else
66 return UACPI_STATUS_UNIMPLEMENTED;
67#endif
68}
69
70template <class T>
71uacpi_status writePort(uacpi_handle handle, size_t offset, T value) {
72 const auto* range = static_cast<const PortRange*>(handle);
73 if (!range || offset >= range->bytes || sizeof(T) > range->bytes - offset) {
74 return UACPI_STATUS_INVALID_ARGUMENT;
75 }
76#if X86 || X64
77 const uint16_t port = range->base + offset;
78 if constexpr (sizeof(T) == 1) {
79 asm volatile("outb %0, %1" : : "a"(value), "Nd"(port));
80 } else if constexpr (sizeof(T) == 2) {
81 asm volatile("outw %0, %1" : : "a"(value), "Nd"(port));
82 } else {
83 asm volatile("outl %0, %1" : : "a"(value), "Nd"(port));
84 }
85 return UACPI_STATUS_OK;
86#else
87 return UACPI_STATUS_UNIMPLEMENTED;
88#endif
89}
90} // namespace
91
92uacpi_status uacpi_kernel_get_rsdp(uacpi_phys_addr* result) {
93 const uintptr_t address = g_pBootstrapInfo ? g_pBootstrapInfo->getAcpiRsdp() : 0;
94 if (!address || !result) {
95 return UACPI_STATUS_NOT_FOUND;
96 }
97#if X64
98 // The UEFI loader supplies a direct-map alias, backed by large pages that
99 // the ordinary page-mapping lookup intentionally does not expose.
100 constexpr uintptr_t DirectMapBase = 0xffff800000000000ULL;
101 constexpr uintptr_t DirectMapEnd = 0xffffc00000000000ULL;
102 if (address >= DirectMapBase && address < DirectMapEnd) {
103 *result = address - DirectMapBase;
104 return UACPI_STATUS_OK;
105 }
106#endif
107 const size_t pageSize = PhysicalMemoryManager::getPageSize();
108 physical_uintptr_t physical = 0;
109 size_t flags = 0;
111 reinterpret_cast<void*>(address & ~(pageSize - 1)), physical, flags)) {
112 return UACPI_STATUS_MAPPING_FAILED;
113 }
114 *result = physical + (address & (pageSize - 1));
115 return UACPI_STATUS_OK;
116}
117
118void* uacpi_kernel_map(uacpi_phys_addr address, uacpi_size bytes) {
119 const size_t pageSize = PhysicalMemoryManager::getPageSize();
120 const size_t offset = address & (pageSize - 1);
121 if (!bytes || address > ~physical_uintptr_t{0} || bytes - 1 > ~physical_uintptr_t{0} - address ||
122 bytes > ~size_t{0} - offset - (pageSize - 1)) {
123 return UACPI_MAP_FAILED;
124 }
125 auto* mapping = new Mapping;
128 if (g_pBootstrapInfo) {
129 for (void* entry = g_pBootstrapInfo->getMemoryMap(); entry;
130 entry = g_pBootstrapInfo->nextMemoryMapEntry(entry)) {
131 const uint64_t base = g_pBootstrapInfo->getMemoryMapEntryAddress(entry);
132 const uint64_t length = g_pBootstrapInfo->getMemoryMapEntryLength(entry);
133 const uint32_t type = g_pBootstrapInfo->getMemoryMapEntryType(entry);
134 if ((type == 1 || type == 3 || type == 4) && address >= base && address - base < length &&
135 bytes <= length - (address - base)) {
136 // RAM-backed firmware tables share the direct map's cache policy.
137 flags &= ~VirtualAddressSpace::CacheDisable;
138 break;
139 }
140 }
141 }
143 mapping->region, (bytes + offset + pageSize - 1) / pageSize,
146 flags, address & ~(physical_uintptr_t(pageSize) - 1))) {
147 delete mapping;
148 return UACPI_MAP_FAILED;
149 }
150 mapping->address = static_cast<uint8_t*>(mapping->region.virtualAddress()) + offset;
151 mapping->bytes = bytes;
152 LockGuard<Mutex> guard(mappingsLock);
153 mapping->next = mappings;
154 mappings = mapping;
155 return mapping->address;
156}
157
158void uacpi_kernel_unmap(void* address, uacpi_size bytes) {
159 Mapping* found = nullptr;
160 {
161 LockGuard<Mutex> guard(mappingsLock);
162 for (Mapping** cursor = &mappings; *cursor; cursor = &(*cursor)->next) {
163 if ((*cursor)->address == address && (*cursor)->bytes == bytes) {
164 found = *cursor;
165 *cursor = found->next;
166 break;
167 }
168 }
169 }
170 if (!found) {
171 panic("ACPI: invalid firmware mapping retirement");
172 }
173 delete found;
174}
175
176void uacpi_kernel_log(uacpi_log_level level, const uacpi_char* message) {
177 if (level <= UACPI_LOG_WARN) {
178 WARNING("ACPI: " << message);
179 } else {
180 NOTICE("ACPI: " << message);
181 }
182}
183
184uacpi_status uacpi_kernel_pci_device_open(uacpi_pci_address address, uacpi_handle* result) {
185 if (!result || address.segment || address.device >= 32 || address.function >= 8) {
186 return UACPI_STATUS_NOT_FOUND;
187 }
188 auto* device = new Device;
189 device->setPciPosition(address.bus, address.device, address.function);
190 *result = device;
191 return UACPI_STATUS_OK;
192}
193void uacpi_kernel_pci_device_close(uacpi_handle device) {
194 delete static_cast<Device*>(device);
195}
196
197uacpi_status uacpi_kernel_pci_read8(uacpi_handle device, uacpi_size offset, uacpi_u8* value) {
198 return status(device && value && offset < 4096 &&
199 PciBus::instance().readConfig8(static_cast<Device*>(device), offset, *value));
200}
201uacpi_status uacpi_kernel_pci_read16(uacpi_handle device, uacpi_size offset, uacpi_u16* value) {
202 return status(device && value && offset < 4096 &&
203 PciBus::instance().readConfig16(static_cast<Device*>(device), offset, *value));
204}
205uacpi_status uacpi_kernel_pci_read32(uacpi_handle device, uacpi_size offset, uacpi_u32* value) {
206 return status(device && value && offset < 4096 &&
207 PciBus::instance().readConfig32(static_cast<Device*>(device), offset, *value));
208}
209uacpi_status uacpi_kernel_pci_write8(uacpi_handle device, uacpi_size offset, uacpi_u8 value) {
210 return status(device && offset < 4096 &&
211 PciBus::instance().writeConfig8(static_cast<Device*>(device), offset, value));
212}
213uacpi_status uacpi_kernel_pci_write16(uacpi_handle device, uacpi_size offset, uacpi_u16 value) {
214 return status(device && offset < 4096 &&
215 PciBus::instance().writeConfig16(static_cast<Device*>(device), offset, value));
216}
217uacpi_status uacpi_kernel_pci_write32(uacpi_handle device, uacpi_size offset, uacpi_u32 value) {
218 return status(device && offset < 4096 &&
219 PciBus::instance().writeConfig32(static_cast<Device*>(device), offset, value));
220}
221
222uacpi_status uacpi_kernel_io_map(uacpi_io_addr base, uacpi_size bytes, uacpi_handle* result) {
223 if (!result || !bytes || base > 0xffff || bytes > 0x10000 - base) {
224 return UACPI_STATUS_INVALID_ARGUMENT;
225 }
226#if X86 || X64
227 *result = new PortRange{static_cast<uint16_t>(base), bytes};
228 return UACPI_STATUS_OK;
229#else
230 return UACPI_STATUS_UNIMPLEMENTED;
231#endif
232}
233void uacpi_kernel_io_unmap(uacpi_handle handle) {
234 delete static_cast<PortRange*>(handle);
235}
236uacpi_status uacpi_kernel_io_read8(uacpi_handle h, uacpi_size o, uacpi_u8* v) {
237 return readPort(h, o, v);
238}
239uacpi_status uacpi_kernel_io_read16(uacpi_handle h, uacpi_size o, uacpi_u16* v) {
240 return readPort(h, o, v);
241}
242uacpi_status uacpi_kernel_io_read32(uacpi_handle h, uacpi_size o, uacpi_u32* v) {
243 return readPort(h, o, v);
244}
245uacpi_status uacpi_kernel_io_write8(uacpi_handle h, uacpi_size o, uacpi_u8 v) {
246 return writePort(h, o, v);
247}
248uacpi_status uacpi_kernel_io_write16(uacpi_handle h, uacpi_size o, uacpi_u16 v) {
249 return writePort(h, o, v);
250}
251uacpi_status uacpi_kernel_io_write32(uacpi_handle h, uacpi_size o, uacpi_u32 v) {
252 return writePort(h, o, v);
253}
254
255void* uacpi_kernel_alloc(uacpi_size bytes) {
256 return new uint8_t[bytes];
257}
258void uacpi_kernel_free(void* memory) {
259 delete[] static_cast<uint8_t*>(memory);
260}
261uacpi_u64 uacpi_kernel_get_nanoseconds_since_boot() {
262 return Time::getTicks();
263}
264void uacpi_kernel_stall(uacpi_u8 microseconds) {
265 const auto deadline = Time::getTicks() + microseconds * Time::Multiplier::Microsecond;
266 while (Time::getTicks() < deadline) {
268 }
269}
270void uacpi_kernel_sleep(uacpi_u64 milliseconds) {
271 while (milliseconds) {
272 const auto chunk = milliseconds > 1000 ? 1000 : milliseconds;
273 const auto deadline = Time::getTicks() + chunk * Time::Multiplier::Millisecond;
274 auto now = Time::getTicks();
275 while (now < deadline) {
276 Time::delay(deadline - now);
277 now = Time::getTicks();
278 }
279 milliseconds -= chunk;
280 }
281}
282
283uacpi_handle uacpi_kernel_create_mutex() {
284 return new Mutex;
285}
286void uacpi_kernel_free_mutex(uacpi_handle handle) {
287 delete static_cast<Mutex*>(handle);
288}
289uacpi_status uacpi_kernel_acquire_mutex(uacpi_handle handle, uacpi_u16 timeout) {
290 return wait(static_cast<Mutex*>(handle), timeout) ? UACPI_STATUS_OK : UACPI_STATUS_TIMEOUT;
291}
292void uacpi_kernel_release_mutex(uacpi_handle handle) {
293 static_cast<Mutex*>(handle)->release();
294}
295uacpi_handle uacpi_kernel_create_event() {
296 return new Semaphore(0, false);
297}
298void uacpi_kernel_free_event(uacpi_handle handle) {
299 delete static_cast<Semaphore*>(handle);
300}
301uacpi_bool uacpi_kernel_wait_for_event(uacpi_handle handle, uacpi_u16 timeout) {
302 return wait(static_cast<Semaphore*>(handle), timeout);
303}
304void uacpi_kernel_signal_event(uacpi_handle handle) {
305 static_cast<Semaphore*>(handle)->release();
306}
307void uacpi_kernel_reset_event(uacpi_handle handle) {
308 [[maybe_unused]] const size_t drained = static_cast<Semaphore*>(handle)->drainAvailable();
309}
310uacpi_thread_id uacpi_kernel_get_thread_id() {
311 return Processor::information().getCurrentThread();
312}
313uacpi_interrupt_state uacpi_kernel_disable_interrupts() {
314 const bool enabled = Processor::getInterrupts();
316 return enabled;
317}
318void uacpi_kernel_restore_interrupts(uacpi_interrupt_state state) {
319 Processor::setInterrupts(state != 0);
320}
321uacpi_handle uacpi_kernel_create_spinlock() {
322 return new Spinlock;
323}
324void uacpi_kernel_free_spinlock(uacpi_handle handle) {
325 delete static_cast<Spinlock*>(handle);
326}
327uacpi_cpu_flags uacpi_kernel_lock_spinlock(uacpi_handle handle) {
328 auto* lock = static_cast<Spinlock*>(handle);
329 lock->acquire();
330 return lock->interrupts();
331}
332void uacpi_kernel_unlock_spinlock(uacpi_handle handle, uacpi_cpu_flags) {
333 static_cast<Spinlock*>(handle)->release();
334}
335uacpi_status uacpi_kernel_handle_firmware_request(uacpi_firmware_request* request) {
336 WARNING("ACPI: firmware request " << Dec << request->type);
337 return request->type == UACPI_FIRMWARE_REQUEST_TYPE_BREAKPOINT ? UACPI_STATUS_OK
338 : UACPI_STATUS_DENIED;
339}
Special memory entity in the kernel's virtual address space.
Definition Mutex.h:56
static PhysicalMemoryManager & instance()
virtual bool allocateRegion(MemoryRegion &Region, size_t cPages, size_t pageConstraints, size_t Flags, physical_uintptr_t start=-1)=0
static bool getInterrupts()
static ProcessorInformation & information()
static void pause()
static void setInterrupts(bool bEnable)
MUST_USE_RESULT bool acquireForCompletion(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
Definition Semaphore.cc:372
bool tryAcquire(size_t n=1)
Definition Semaphore.cc:484
bool acquire(bool recurse=false, bool safe=true)
Definition Spinlock.cc:36
static EXPORTED_PUBLIC VirtualAddressSpace & getKernelAddressSpace()
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:118
@ Dec
Definition Log.h:126