aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@ost.ch>2026-07-27 16:10:38 +0200
committerFelix Morgner <felix.morgner@ost.ch>2026-07-27 16:10:38 +0200
commitdca8353027526b9e522ad8be169d9763e447b08b (patch)
tree45614de8059a2a67a853c8407dbbd6073f902e40
parent1cad7524b291fd7a6f77e01bb90e763e6b557620 (diff)
downloadkernel-dca8353027526b9e522ad8be169d9763e447b08b.tar.xz
kernel-dca8353027526b9e522ad8be169d9763e447b08b.zip
kapi: fix device resource lookup
-rw-r--r--kapi/kapi/devices/resource.hpp53
-rw-r--r--kernel/kapi/devices/device.cpp9
-rw-r--r--kernel/kapi/devices/device.tests.cpp317
3 files changed, 372 insertions, 7 deletions
diff --git a/kapi/kapi/devices/resource.hpp b/kapi/kapi/devices/resource.hpp
index 5c01aba5..1b491225 100644
--- a/kapi/kapi/devices/resource.hpp
+++ b/kapi/kapi/devices/resource.hpp
@@ -102,8 +102,54 @@ namespace kapi::devices
, m_value{.channel = channel}
{}
- //! The exact type of this resource.
- resource_type type{resource_type::invalid};
+ constexpr auto friend operator==(resource const & lhs, resource const & rhs) noexcept -> bool
+ {
+ if (lhs.type != rhs.type)
+ {
+ return false;
+ }
+
+ switch (lhs.type)
+ {
+ case resource_type::invalid:
+ return true;
+ case resource_type::mmio:
+ return lhs.m_value.range == rhs.m_value.range;
+ case resource_type::port:
+ return lhs.m_value.port == rhs.m_value.port;
+ case resource_type::irq:
+ return lhs.m_value.irq == rhs.m_value.irq;
+ case resource_type::dma:
+ return lhs.m_value.channel == rhs.m_value.channel;
+ };
+
+ return false;
+ }
+
+ constexpr auto friend operator==(resource const & lhs, invalid_resource const &) noexcept -> bool
+ {
+ return lhs.type == resource_type::invalid;
+ }
+
+ constexpr auto friend operator==(resource const & lhs, mmio_range const & rhs) noexcept -> bool
+ {
+ return lhs.type == resource_type::mmio && lhs.m_value.range == rhs;
+ }
+
+ constexpr auto friend operator==(resource const & lhs, io_port const & rhs) noexcept -> bool
+ {
+ return lhs.type == resource_type::port && lhs.m_value.port == rhs;
+ }
+
+ constexpr auto friend operator==(resource const & lhs, interrupt_line const & rhs) noexcept -> bool
+ {
+ return lhs.type == resource_type::irq && lhs.m_value.irq == rhs;
+ }
+
+ constexpr auto friend operator==(resource const & lhs, dma_channel const & rhs) noexcept -> bool
+ {
+ return lhs.type == resource_type::dma && lhs.m_value.channel == rhs;
+ }
//! Get the resource value.
template<resource_type Type>
@@ -136,6 +182,9 @@ namespace kapi::devices
return m_value;
}
+ //! The exact type of this resource.
+ resource_type type{resource_type::invalid};
+
private:
union
{
diff --git a/kernel/kapi/devices/device.cpp b/kernel/kapi/devices/device.cpp
index a05a1653..237183c9 100644
--- a/kernel/kapi/devices/device.cpp
+++ b/kernel/kapi/devices/device.cpp
@@ -93,11 +93,10 @@ namespace kapi::devices
{
auto guard = kstd::lock_guard{m_lock};
- auto numbered_resources = std::views::enumerate(m_resources);
- auto found = std::ranges::find_if(numbered_resources, [&](auto entry) {
- auto const & [number, resource] = entry;
- return resource.type == type && static_cast<std::size_t>(number) == index;
- });
+ auto filtered = std::views::filter(m_resources, [&](auto e) { return e.type == type; });
+ auto numbered_resources = std::views::enumerate(filtered);
+ auto found = std::ranges::find_if(
+ numbered_resources, [&](auto entry) { return static_cast<std::size_t>(std::get<0>(entry)) == index; });
if (found != numbered_resources.end())
{
diff --git a/kernel/kapi/devices/device.tests.cpp b/kernel/kapi/devices/device.tests.cpp
index 8104f6eb..138e1161 100644
--- a/kernel/kapi/devices/device.tests.cpp
+++ b/kernel/kapi/devices/device.tests.cpp
@@ -159,6 +159,22 @@ SCENARIO("Devices allow access to their resources", "[kapi][devices]")
}
}
+ AND_WHEN("trying to get the first MMIO resource via the unsafe accessor")
+ {
+ auto result = device->request_resource(kapi::devices::resource_type::mmio);
+
+ THEN("the result is a success")
+ {
+ REQUIRE(result);
+ }
+
+ THEN("the result is the expected MMIO range")
+ {
+ REQUIRE(result == kapi::devices::resource{range});
+ REQUIRE(result == range);
+ }
+ }
+
AND_WHEN("trying to get the first I/O port resource via the type-safe accessor")
{
auto result = device->request_resource<kapi::devices::resource_type::port>(0);
@@ -174,6 +190,21 @@ SCENARIO("Devices allow access to their resources", "[kapi][devices]")
}
}
+ AND_WHEN("trying to get the first I/O port resource via the unsafe accessor")
+ {
+ auto result = device->request_resource(kapi::devices::resource_type::port, 0);
+
+ THEN("the result is an error")
+ {
+ REQUIRE_FALSE(result);
+ }
+
+ THEN("the error is 'invalid argument'")
+ {
+ REQUIRE(result.error() == kstd::errc::invalid_argument);
+ }
+ }
+
AND_WHEN("trying to get the first IRQ resource via the type-safe accessor")
{
auto result = device->request_resource<kapi::devices::resource_type::irq>(0);
@@ -189,6 +220,21 @@ SCENARIO("Devices allow access to their resources", "[kapi][devices]")
}
}
+ AND_WHEN("trying to get the first IRQ resource via the unsafe accessor")
+ {
+ auto result = device->request_resource(kapi::devices::resource_type::irq, 0);
+
+ THEN("the result is an error")
+ {
+ REQUIRE_FALSE(result);
+ }
+
+ THEN("the error is 'invalid argument'")
+ {
+ REQUIRE(result.error() == kstd::errc::invalid_argument);
+ }
+ }
+
AND_WHEN("trying to get the first DMA resource via the type-safe accessor")
{
auto result = device->request_resource<kapi::devices::resource_type::dma>(0);
@@ -203,6 +249,21 @@ SCENARIO("Devices allow access to their resources", "[kapi][devices]")
REQUIRE(result.error() == kstd::errc::invalid_argument);
}
}
+
+ AND_WHEN("trying to get the first DMA resource via the unsafe accessor")
+ {
+ auto result = device->request_resource(kapi::devices::resource_type::dma, 0);
+
+ THEN("the result is an error")
+ {
+ REQUIRE_FALSE(result);
+ }
+
+ THEN("the error is 'invalid argument'")
+ {
+ REQUIRE(result.error() == kstd::errc::invalid_argument);
+ }
+ }
}
WHEN("adding an I/O port resource")
@@ -225,6 +286,21 @@ SCENARIO("Devices allow access to their resources", "[kapi][devices]")
}
}
+ AND_WHEN("trying to get the first MMIO resource via the unsafe accessor")
+ {
+ auto result = device->request_resource(kapi::devices::resource_type::mmio);
+
+ THEN("the result is en error")
+ {
+ REQUIRE_FALSE(result);
+ }
+
+ THEN("the error is 'invalid argument'")
+ {
+ REQUIRE(result.error() == kstd::errc::invalid_argument);
+ }
+ }
+
AND_WHEN("trying to get the first I/O port resource via the type-safe accessor")
{
auto result = device->request_resource<kapi::devices::resource_type::port>();
@@ -240,6 +316,22 @@ SCENARIO("Devices allow access to their resources", "[kapi][devices]")
}
}
+ AND_WHEN("trying to get the first I/O port resource via the unsafe accessor")
+ {
+ auto result = device->request_resource(kapi::devices::resource_type::port);
+
+ THEN("the result is a success")
+ {
+ REQUIRE(result);
+ }
+
+ THEN("the result is the expected MMIO range")
+ {
+ REQUIRE(result == kapi::devices::resource{port});
+ REQUIRE(result == port);
+ }
+ }
+
AND_WHEN("trying to get the first IRQ resource via the type-safe accessor")
{
auto result = device->request_resource<kapi::devices::resource_type::irq>(0);
@@ -255,6 +347,21 @@ SCENARIO("Devices allow access to their resources", "[kapi][devices]")
}
}
+ AND_WHEN("trying to get the first IRQ resource via the unsafe accessor")
+ {
+ auto result = device->request_resource(kapi::devices::resource_type::irq, 0);
+
+ THEN("the result is an error")
+ {
+ REQUIRE_FALSE(result);
+ }
+
+ THEN("the error is 'invalid argument'")
+ {
+ REQUIRE(result.error() == kstd::errc::invalid_argument);
+ }
+ }
+
AND_WHEN("trying to get the first DMA resource via the type-safe accessor")
{
auto result = device->request_resource<kapi::devices::resource_type::dma>(0);
@@ -269,6 +376,21 @@ SCENARIO("Devices allow access to their resources", "[kapi][devices]")
REQUIRE(result.error() == kstd::errc::invalid_argument);
}
}
+
+ AND_WHEN("trying to get the first DMA resource via the unsafe accessor")
+ {
+ auto result = device->request_resource(kapi::devices::resource_type::dma, 0);
+
+ THEN("the result is an error")
+ {
+ REQUIRE_FALSE(result);
+ }
+
+ THEN("the error is 'invalid argument'")
+ {
+ REQUIRE(result.error() == kstd::errc::invalid_argument);
+ }
+ }
}
WHEN("adding an interrupt line resource")
@@ -291,6 +413,21 @@ SCENARIO("Devices allow access to their resources", "[kapi][devices]")
}
}
+ AND_WHEN("trying to get the first MMIO resource via the unsafe accessor")
+ {
+ auto result = device->request_resource(kapi::devices::resource_type::mmio);
+
+ THEN("the result is en error")
+ {
+ REQUIRE_FALSE(result);
+ }
+
+ THEN("the error is 'invalid argument'")
+ {
+ REQUIRE(result.error() == kstd::errc::invalid_argument);
+ }
+ }
+
AND_WHEN("trying to get the first I/O port resource via the type-safe accessor")
{
auto result = device->request_resource<kapi::devices::resource_type::port>(0);
@@ -306,6 +443,21 @@ SCENARIO("Devices allow access to their resources", "[kapi][devices]")
}
}
+ AND_WHEN("trying to get the first I/O port resource via the unsafe accessor")
+ {
+ auto result = device->request_resource(kapi::devices::resource_type::port, 0);
+
+ THEN("the result is an error")
+ {
+ REQUIRE_FALSE(result);
+ }
+
+ THEN("the error is 'invalid argument'")
+ {
+ REQUIRE(result.error() == kstd::errc::invalid_argument);
+ }
+ }
+
AND_WHEN("trying to get the first IRQ resource via the type-safe accessor")
{
auto result = device->request_resource<kapi::devices::resource_type::irq>(0);
@@ -321,6 +473,22 @@ SCENARIO("Devices allow access to their resources", "[kapi][devices]")
}
}
+ AND_WHEN("trying to get the first IRQ resource via the unsafe accessor")
+ {
+ auto result = device->request_resource(kapi::devices::resource_type::irq, 0);
+
+ THEN("the result is a success")
+ {
+ REQUIRE(result);
+ }
+
+ THEN("the result is the expected interrupt line")
+ {
+ REQUIRE(result == kapi::devices::resource{line});
+ REQUIRE(result == line);
+ }
+ }
+
AND_WHEN("trying to get the first DMA resource via the type-safe accessor")
{
auto result = device->request_resource<kapi::devices::resource_type::dma>(0);
@@ -335,6 +503,21 @@ SCENARIO("Devices allow access to their resources", "[kapi][devices]")
REQUIRE(result.error() == kstd::errc::invalid_argument);
}
}
+
+ AND_WHEN("trying to get the first DMA resource via the unsafe accessor")
+ {
+ auto result = device->request_resource(kapi::devices::resource_type::dma, 0);
+
+ THEN("the result is an error")
+ {
+ REQUIRE_FALSE(result);
+ }
+
+ THEN("the error is 'invalid argument'")
+ {
+ REQUIRE(result.error() == kstd::errc::invalid_argument);
+ }
+ }
}
WHEN("adding a DMA channel resource")
@@ -357,6 +540,21 @@ SCENARIO("Devices allow access to their resources", "[kapi][devices]")
}
}
+ AND_WHEN("trying to get the first MMIO resource via the unsafe accessor")
+ {
+ auto result = device->request_resource(kapi::devices::resource_type::mmio);
+
+ THEN("the result is en error")
+ {
+ REQUIRE_FALSE(result);
+ }
+
+ THEN("the error is 'invalid argument'")
+ {
+ REQUIRE(result.error() == kstd::errc::invalid_argument);
+ }
+ }
+
AND_WHEN("trying to get the first I/O port resource via the type-safe accessor")
{
auto result = device->request_resource<kapi::devices::resource_type::port>(0);
@@ -372,6 +570,21 @@ SCENARIO("Devices allow access to their resources", "[kapi][devices]")
}
}
+ AND_WHEN("trying to get the first I/O port resource via the unsafe accessor")
+ {
+ auto result = device->request_resource(kapi::devices::resource_type::port, 0);
+
+ THEN("the result is an error")
+ {
+ REQUIRE_FALSE(result);
+ }
+
+ THEN("the error is 'invalid argument'")
+ {
+ REQUIRE(result.error() == kstd::errc::invalid_argument);
+ }
+ }
+
AND_WHEN("trying to get the first IRQ resource via the type-safe accessor")
{
auto result = device->request_resource<kapi::devices::resource_type::irq>(0);
@@ -387,6 +600,21 @@ SCENARIO("Devices allow access to their resources", "[kapi][devices]")
}
}
+ AND_WHEN("trying to get the first IRQ resource via the unsafe accessor")
+ {
+ auto result = device->request_resource(kapi::devices::resource_type::irq, 0);
+
+ THEN("the result is an error")
+ {
+ REQUIRE_FALSE(result);
+ }
+
+ THEN("the error is 'invalid argument'")
+ {
+ REQUIRE(result.error() == kstd::errc::invalid_argument);
+ }
+ }
+
AND_WHEN("trying to get the first DMA resource via the type-safe accessor")
{
auto result = device->request_resource<kapi::devices::resource_type::dma>(0);
@@ -401,6 +629,95 @@ SCENARIO("Devices allow access to their resources", "[kapi][devices]")
REQUIRE(result == channel);
}
}
+
+ AND_WHEN("trying to get the first DMA resource via the unsafe accessor")
+ {
+ auto result = device->request_resource(kapi::devices::resource_type::dma, 0);
+
+ THEN("the result is a success")
+ {
+ REQUIRE(result);
+ }
+
+ THEN("the result it the expected DMA channel")
+ {
+ REQUIRE(result == kapi::devices::resource{channel});
+ REQUIRE(result == channel);
+ }
+ }
+ }
+ }
+
+ GIVEN("A device with one memory-mapped I/O and two I/O port resources")
+ {
+ auto device = kstd::make_shared<kapi::devices::device>("test_device");
+
+ auto range = kapi::devices::mmio_range{{}, kstd::units::bytes{128}};
+ auto port_a = kapi::devices::io_port{0x40, 4};
+ auto port_b = kapi::devices::io_port{0x50, 1};
+
+ device->set_resources(
+ {kapi::devices::resource{range}, kapi::devices::resource{port_a}, kapi::devices::resource{port_b}});
+
+ THEN("all resources are returned in order")
+ {
+ auto all = device->resources();
+ REQUIRE(all.size() == 3);
+ REQUIRE(all[0] == range);
+ REQUIRE(all[1] == port_a);
+ REQUIRE(all[2] == port_b);
+ }
+
+ THEN("requesting the MMIO resource finds it via the type-safe accessor")
+ {
+ auto result = device->request_resource<kapi::devices::resource_type::mmio>();
+ REQUIRE(result == range);
+ }
+
+ THEN("requesting the MMIO resource finds it via the unsafe accessor")
+ {
+ auto result = device->request_resource(kapi::devices::resource_type::mmio);
+ REQUIRE(result == range);
+ }
+
+ THEN("requesting the I/O port resources finds them at the correct index via the type-safe accessor")
+ {
+ auto result_a = device->request_resource<kapi::devices::resource_type::port>(0);
+ auto result_b = device->request_resource<kapi::devices::resource_type::port>(1);
+ REQUIRE(result_a == port_a);
+ REQUIRE(result_b == port_b);
+ }
+
+ THEN("requesting the I/O port resources finds them at the correct index via the unsafe accessor")
+ {
+ auto result_a = device->request_resource(kapi::devices::resource_type::port, 0);
+ auto result_b = device->request_resource(kapi::devices::resource_type::port, 1);
+ REQUIRE(result_a == port_a);
+ REQUIRE(result_b == port_b);
+ }
+
+ THEN("requesting an out of range index of an assigned type fails")
+ {
+ auto result = device->request_resource<kapi::devices::resource_type::mmio>(1);
+ REQUIRE_FALSE(result);
+ }
+
+ WHEN("the resources are reassigned")
+ {
+ auto replacement = kapi::devices::dma_channel{1};
+
+ device->set_resources({kapi::devices::resource{replacement}});
+
+ THEN("only the resources are present")
+ {
+ auto all = device->resources();
+ REQUIRE(all.size() == 1);
+ REQUIRE(all[0] == replacement);
+
+ REQUIRE_FALSE(device->request_resource<kapi::devices::resource_type::mmio>(0));
+ REQUIRE_FALSE(device->request_resource<kapi::devices::resource_type::port>(0));
+ REQUIRE_FALSE(device->request_resource<kapi::devices::resource_type::port>(1));
+ }
}
}
}