diff options
| author | Felix Morgner <felix.morgner@ost.ch> | 2026-07-27 16:10:38 +0200 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@ost.ch> | 2026-07-27 16:10:38 +0200 |
| commit | dca8353027526b9e522ad8be169d9763e447b08b (patch) | |
| tree | 45614de8059a2a67a853c8407dbbd6073f902e40 | |
| parent | 1cad7524b291fd7a6f77e01bb90e763e6b557620 (diff) | |
| download | kernel-dca8353027526b9e522ad8be169d9763e447b08b.tar.xz kernel-dca8353027526b9e522ad8be169d9763e447b08b.zip | |
kapi: fix device resource lookup
| -rw-r--r-- | kapi/kapi/devices/resource.hpp | 53 | ||||
| -rw-r--r-- | kernel/kapi/devices/device.cpp | 9 | ||||
| -rw-r--r-- | kernel/kapi/devices/device.tests.cpp | 317 |
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)); + } } } } |
