From e9b880648c18d3680ae10cc5660f0dbe981af5d8 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 29 Sep 2026 10:41:36 +0200 Subject: kapi/interrupt: rework enablement API --- kernel/kernel/main.cpp | 2 +- kernel/kernel/test_support/kapi/interrupts.cpp | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) (limited to 'kernel') diff --git a/kernel/kernel/main.cpp b/kernel/kernel/main.cpp index bc192950..bbbd46c0 100644 --- a/kernel/kernel/main.cpp +++ b/kernel/kernel/main.cpp @@ -293,7 +293,7 @@ auto main() -> int kapi::devices::init_platform_devices(); kstd::println("[OS] Platform devices initialized."); - kapi::interrupts::enable(); + kapi::interrupts::enabled(true); kstd::println("[OS] Interrupts enabled."); auto modules_bus = kernel::devices::storage::init(); diff --git a/kernel/kernel/test_support/kapi/interrupts.cpp b/kernel/kernel/test_support/kapi/interrupts.cpp index 00772669..79b03729 100644 --- a/kernel/kernel/test_support/kapi/interrupts.cpp +++ b/kernel/kernel/test_support/kapi/interrupts.cpp @@ -1,11 +1,23 @@ #include +#include + namespace kapi::interrupts { - auto enable() -> void + namespace + { + std::atomic_bool interrupt_state = false; + } + + auto enabled() -> bool + { + return interrupt_state.load(std::memory_order::relaxed); + } + + auto enabled(bool value) -> void { - // TODO: enable simulated interrupts. + interrupt_state.store(value, std::memory_order::relaxed); } } // namespace kapi::interrupts \ No newline at end of file -- cgit v1.2.3 From 61deb2cc5ffe086f83908ea9b980f7d8dab0198a Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 29 Sep 2026 11:30:12 +0200 Subject: kapi/interrupts: tests: make IRQ state thread specific --- kernel/kernel/test_support/kapi/interrupts.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'kernel') diff --git a/kernel/kernel/test_support/kapi/interrupts.cpp b/kernel/kernel/test_support/kapi/interrupts.cpp index 79b03729..9bd3a515 100644 --- a/kernel/kernel/test_support/kapi/interrupts.cpp +++ b/kernel/kernel/test_support/kapi/interrupts.cpp @@ -1,23 +1,32 @@ #include #include +#include +#include namespace kapi::interrupts { namespace { - std::atomic_bool interrupt_state = false; + std::map interrupt_state{}; } auto enabled() -> bool { - return interrupt_state.load(std::memory_order::relaxed); + return interrupt_state[std::this_thread::get_id()].test(); } auto enabled(bool value) -> void { - interrupt_state.store(value, std::memory_order::relaxed); + if (value) + { + interrupt_state[std::this_thread::get_id()].test_and_set(std::memory_order::relaxed); + } + else + { + interrupt_state[std::this_thread::get_id()].clear(std::memory_order::relaxed); + } } } // namespace kapi::interrupts \ No newline at end of file -- cgit v1.2.3 From 2315e95d48337aa612b2bb20b5f8d3d0e061716c Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 29 Sep 2026 11:54:20 +0200 Subject: kapi/interrupts: tests: fix state data race --- kernel/kernel/test_support/kapi/interrupts.cpp | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) (limited to 'kernel') diff --git a/kernel/kernel/test_support/kapi/interrupts.cpp b/kernel/kernel/test_support/kapi/interrupts.cpp index 9bd3a515..3791b0dc 100644 --- a/kernel/kernel/test_support/kapi/interrupts.cpp +++ b/kernel/kernel/test_support/kapi/interrupts.cpp @@ -1,32 +1,21 @@ #include -#include -#include -#include - namespace kapi::interrupts { namespace { - std::map interrupt_state{}; + thread_local bool interrupt_state{}; } auto enabled() -> bool { - return interrupt_state[std::this_thread::get_id()].test(); + return interrupt_state; } auto enabled(bool value) -> void { - if (value) - { - interrupt_state[std::this_thread::get_id()].test_and_set(std::memory_order::relaxed); - } - else - { - interrupt_state[std::this_thread::get_id()].clear(std::memory_order::relaxed); - } + interrupt_state = value; } } // namespace kapi::interrupts \ No newline at end of file -- cgit v1.2.3 From 569f0e50663095e8ba7363cdab5421d0aeb9e6c3 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 29 Sep 2026 12:40:48 +0200 Subject: kapi/interrupts: add tests --- kernel/kapi/interrupts.stress.cpp | 65 +++++++++++++ kernel/kapi/interrupts.tests.cpp | 189 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 254 insertions(+) create mode 100644 kernel/kapi/interrupts.stress.cpp create mode 100644 kernel/kapi/interrupts.tests.cpp (limited to 'kernel') diff --git a/kernel/kapi/interrupts.stress.cpp b/kernel/kapi/interrupts.stress.cpp new file mode 100644 index 00000000..d3baf7b8 --- /dev/null +++ b/kernel/kapi/interrupts.stress.cpp @@ -0,0 +1,65 @@ +#include + +#include + +#include + +#include +#include + +SCENARIO("irq_lock remote unlock protection", "[kapi][interrupts]") +{ + GIVEN("a simple irq_lock") + { + auto lock = kapi::interrupts::irq_lock{}; + + WHEN("it is locked on the main core") + { + auto guard = std::lock_guard{lock}; + + THEN("unlocking it on a different core panics") + { + auto unlocker = [&] { + try + { + lock.unlock(); + return false; + } + catch (kernel::tests::cpu::halt const &) + { + return true; + } + }; + + auto result = std::async(std::launch::async, unlocker); + + REQUIRE(result.get()); + } + } + } + + GIVEN("A heap allocated irq_lock") + { + // We use plain new-delete here, since the destruction path of std::shared_ptr is fully noexcept. + auto lock = new kapi::interrupts::irq_lock<>{}; + + THEN("destroying it on a different core panics") + { + auto destroyer = [&] { + try + { + delete lock; + return false; + } + catch (kernel::tests::cpu::halt const &) + { + return true; + } + }; + + auto result = std::async(std::launch::async, destroyer); + + REQUIRE(result.get()); + } + } +} \ No newline at end of file diff --git a/kernel/kapi/interrupts.tests.cpp b/kernel/kapi/interrupts.tests.cpp new file mode 100644 index 00000000..e87c6a00 --- /dev/null +++ b/kernel/kapi/interrupts.tests.cpp @@ -0,0 +1,189 @@ +#include + +#include + +#include + +#include + +SCENARIO("irq_lock semantics", "[kapi][interrupts]") +{ + GIVEN("a simple irq_lock") + { + auto lock = kapi::interrupts::irq_lock{}; + + AND_GIVEN("IRQs are currently disabled") + { + kapi::interrupts::enabled(false); + REQUIRE_FALSE(kapi::interrupts::enabled()); + + WHEN("locking the irq_lock") + { + lock.lock(); + + THEN("IRQs are still disabled") + { + REQUIRE_FALSE(kapi::interrupts::enabled()); + + AND_WHEN("unlocking the irq_lock") + { + lock.unlock(); + + THEN("IRQs are still disabled") + { + REQUIRE_FALSE(kapi::interrupts::enabled()); + } + } + } + + THEN("the irq_lock is locked") + { + REQUIRE_FALSE(lock.try_lock()); + lock.unlock(); + } + } + + THEN("the irq_lock can be locked") + { + REQUIRE(lock.try_lock()); + lock.unlock(); + } + } + + AND_GIVEN("IRQs are currently enabled") + { + kapi::interrupts::enabled(true); + REQUIRE(kapi::interrupts::enabled()); + + WHEN("locking the irq_lock") + { + lock.lock(); + + THEN("IRQs are disabled") + { + REQUIRE_FALSE(kapi::interrupts::enabled()); + + AND_WHEN("unlocking the irq_lock") + { + lock.unlock(); + + THEN("IRQs are enabled again") + { + REQUIRE(kapi::interrupts::enabled()); + } + } + } + } + } + } + + GIVEN("a wrapping irq_lock") + { + auto mutex = kapi::tracked_mutex{}; + auto lock = kapi::interrupts::irq_lock{mutex}; + + AND_GIVEN("IRQs are currently disabled") + { + kapi::interrupts::enabled(false); + REQUIRE_FALSE(kapi::interrupts::enabled()); + + WHEN("locking the irq_lock") + { + lock.lock(); + + THEN("IRQs are still disabled") + { + REQUIRE_FALSE(kapi::interrupts::enabled()); + + AND_WHEN("unlocking the irq_lock") + { + lock.unlock(); + + THEN("IRQs are still disabled") + { + REQUIRE_FALSE(kapi::interrupts::enabled()); + } + + THEN("the inner mutex is unlocked") + { + REQUIRE(mutex.try_lock()); + mutex.unlock(); + } + } + } + + THEN("the irq_lock is locked") + { + REQUIRE_FALSE(lock.try_lock()); + lock.unlock(); + } + + THEN("the inner mutex is locked") + { + REQUIRE_FALSE(mutex.try_lock()); + lock.unlock(); + } + } + + THEN("the irq_lock can be locked") + { + REQUIRE(lock.try_lock()); + lock.unlock(); + } + } + + AND_GIVEN("IRQs are currently enabled") + { + kapi::interrupts::enabled(true); + REQUIRE(kapi::interrupts::enabled()); + + WHEN("locking the irq_lock") + { + lock.lock(); + + THEN("IRQs are disabled") + { + REQUIRE_FALSE(kapi::interrupts::enabled()); + + AND_WHEN("unlocking the irq_lock") + { + lock.unlock(); + + THEN("IRQs are enabled again") + { + REQUIRE(kapi::interrupts::enabled()); + } + + THEN("the inner mutex is unlocked") + { + REQUIRE(mutex.try_lock()); + mutex.unlock(); + } + } + } + + THEN("the irq_lock is locked") + { + REQUIRE_FALSE(lock.try_lock()); + lock.unlock(); + } + + THEN("the inner mutex is locked") + { + REQUIRE_FALSE(mutex.try_lock()); + lock.unlock(); + } + } + } + + AND_GIVEN("the inner mutex is already locked") + { + auto guard = kstd::lock_guard{mutex}; + + THEN("the irq_lock cannot be locked") + { + REQUIRE_FALSE(lock.try_lock()); + } + } + } +} \ No newline at end of file -- cgit v1.2.3