A portable and easy-to-integrate implementation of the Advanced Configuration and Power Interface (ACPI)
A portable and easy-to-integrate implementation of the Advanced Configuration and Power Interface (ACPI).
Over the decades of development, ACPICA has accumulated a lot of workarounds for
AML expecting NT-specific behaviors, and is still missing compatibility in a lot
of critical aspects.
uACPI, on the other hand, is built to be natively NT-compatible without extra
workarounds.
Some specific highlights include:
uACPI is built to always assume the worst about the AML byte code it’s executing,
and as such, has a more sophisticated object lifetime tracking system, as well
as carefully designed handling for various edge-cases, including race conditions.
Some of the standard uACPI test cases crash both ACPICA, and the NT AML
interpreters.
While a permanent fuzzing solution for uACPI is currently WIP, it has already
been fuzzed quite extensively and all known issues have been fixed.
Running at kernel level has a lot of very strict limitations, one of which is a
tiny stack size, which can sometimes be only a few pages in length.
Of course, both ACPICA and uACPI have non-recursive AML interpreters, but there
are still edge cases that cause potentially unbounded recursion.
One such example are the dynamic table load operators from AML
(Load
/LoadTable
): these cause a linear growth in stack usage per call in
ACPICA, whereas in uACPI these are treated as special method calls,
and as such, don’t increase stack usage whatsoever.
Expressions within package:
Method (TEST) {
Local0 = 10
Local1 = Package { Local0 * 5 }
Return (DerefOf(Local1[0]))
}
// ACPICA: AE_SUPPORT, Expressions within package elements are not supported
// Windows, uACPI: Local0 = 50
Local0 = TEST()
Packages outside of a control method:
// ACPICA: internal error
// Windows, uACPI: ok
Local0 = Package { 1 }
Reference rebind semantics:
Local0 = 123
Local1 = RefOf(Local0)
// ACPICA: Local1 = 321, Local0 = 123
// Windows, uACPI: Local1 = reference->Local0, Local0 = 321
Local1 = 321
Increment/Decrement:
Local0 = 123
Local1 = RefOf(Local0)
// ACPICA: error
// Windows, uACPI: Local0 = 124
Local1++
Multilevel references:
Local0 = 123
Local1 = RefOf(Local0)
Local2 = RefOf(Local1)
// ACPICA: Local3 = reference->Local0
// Windows, uACPI: Local3 = 123
Local3 = DerefOf(Local2)
Implict-cast semantics:
Name (TEST, "BAR")
// ACPICA: TEST = "00000000004F4F46"
// Windows, uACPI: TEST = "FOO"
TEST = 0x4F4F46
Buffer size mutability:
Name (TEST, "XXXX")
Name (VAL, "")
// ACPICA: TEST = "LONGSTRING"
// Windows, UACPI: TEST = "LONG"
TEST = "LONGSTRING"
// ACPICA: VAL = "FOO"
// Windows, UACPI: VAL = ""
VAL = "FOO"
Returning a reference to a local object:
Method (TEST) {
Local0 = 123
// Use-after-free in ACPICA, perfectly fine in uACPI
Return (RefOf(Local0))
}
Method (FOO) {
Name (TEST, 123)
// Use-after-free in ACPICA, object lifetime prolonged in uACPI (node is still removed from the namespace)
Return (RefOf(TEST))
}
CopyObject into self:
Method (TEST) {
CopyObject(123, TEST)
Return (1)
}
// Segfault in ACPICA, prints 1 in uACPI
Debug = TEST()
// Unreachable in ACPICA, prints 123 in uACPI
Debug = TEST
There’s even more examples, but this should be enough to demonstrate the fundamental differences in designs.
Simply add the following lines to your cmake:
include(uacpi/uacpi.cmake)
target_sources(
my-kernel
PRIVATE
${UACPI_SOURCES}
)
target_include_directories(
my-kernel
PRIVATE
${UACPI_INCLUDES}
)
Add the following lines to your meson.build:
uacpi = subproject('uacpi')
uacpi_sources = uacpi.get_variable('sources')
my_kernel_sources += uacpi_sources
uacpi_includes = uacpi.get_variable('includes')
my_kernel_includes += uacpi_includes
uACPI defines all platform/architecture-specific functionality in a few headers inside include/uacpi/platform
All of the headers can be “implemented” by your project in a few ways:
UACPI_OVERRIDE_X
variable.uacpi_x.h
header exported by your project.Currently used platform-specific headers are:
stdint.h
header. You don’t have to override thisstdint.h
.uACPI relies on kernel-specific API to do things like mapping/unmapping memory, writing/reading to/from IO, PCI config space, and many more things.
This API is declared in kernel_api.h and is implemented by your kernel.
That’s it, uACPI is now integrated into your project.
You should proceed to initialization.
Refer to the uACPI page on osdev wiki to see a
snippet for basic initialization, as well as some code examples of how you may
want to use certain APIs.
All of the headers and APIs defined in uacpi are public and may be utilized by your project.
Anything inside uacpi/internal is considered private/undocumented and unstable API.
Most development work is fully doable in userland using the test runner.
Simply open tests/runner/CMakeLists.txt in your favorite IDE.
For Visual Studio:
cd tests\runner && mkdir build && cd build && cmake ..
Then just simply open the .sln file generated by cmake.
./tests/run_tests.py
If you want to contribute:
All contributions are very welcome!
Project | Description | (qemu w/ Q35 + KVM) ops/s | CPU |
---|---|---|---|
proxima | A monolithic Unix-like operating system | 8,468,599 | Intel Core i9-13900KS |
Crescent2 | An NT driver compatible kernel and userspace | 6,818,418 | Intel Core i5-13600K |
davix | Yet another unix-like by some bored nerd | 6,364,623 | Intel Core i7-13700K |
ilobilix | Yet another monolithic Linux clone wannabe. Currently under a rewrite | 5,960,392 | Intel Core i9-13900KS |
Managarm | Pragmatic microkernel-based OS with fully asynchronous I/O | 5,618,646 | Intel Core i7-14700K |
pmOS | Microkernel-based operating system written from scratch with uACPI running in userspace | 5,354,445 | AMD Ryzen 9 5900X |
menix | A minimal and expandable Unix-like operating system | 5,239,043 | Intel Core Ultra 7 265KF |
Ironclad | Formally verified, hard real-time capable kernel written in SPARK and Ada | 4,802,816 | Intel Core i9-13900KS |
Astral | Operating system written in C which aims be POSIX-compliant | 4,189,189 | Intel Core i5-13600K |
Keyronex | Layered kernel with fundamentally asynchronous I/O and working set model-based memory management | 4,013,691 | AMD Ryzen 5800X |
Orange | x86_64 Unix-like OS | 2,377,330 | AMD Ryzen 5 3600 |
OBOS | Hybrid Kernel with advanced driver loading | 2,141,179 | Intel Core i5-13600K |
ElysiumOS | Hybrid Unix-like kernel | 1,737,654 | AMD Ryzen 7 5800X3D |
imaginarium | Ziggy osdev experiments inspired by the NT kernel (using the zig general purpose allocator) | 1,504,436 | AMD Ryzen 7 3700X |
BadgerOS | A monolithic lightweight UNIX clone | 1,018,518 | AMD Ryzen 5 3600 |
NyauxKC | Monolithic UNIX-like multi-architecture kernel | 985,988 | Intel Core Ultra 7 265K |
uACPI is licensed under the MIT License.
The full license text is provided in the LICENSE file inside the root directory.