platform_drv: wait for device's availability

Instead of returning an invalid device capability when a device
is (not yet) available, e.g. a PCI device is requested before the
PCI bus got parsed accordingly, we check the device capability
within the Platform::Connection utilities, and register temporarily
an Io_signal_handler to wait for changes of the devices ROM, and
try the device aquisition again. Thereby, simple drivers so not have
to take the burden to do so.

To enable this feature for all drivers, we always have to export a
devices ROM, but limit the information about physical resources
(I/O memory addresses, IRQ numbers, I/O port ranges) to clients with
'info=yes' in their policy description.

Fix genodelabs/genode#4496
This commit is contained in:
Stefan Kalkowski 2022-05-02 11:37:04 +02:00 committed by Christian Helmuth
parent a1564d1826
commit de7fdd3e1a
9 changed files with 78 additions and 70 deletions

View File

@ -17,6 +17,7 @@
#include <base/attached_dataspace.h>
#include <base/connection.h>
#include <base/env.h>
#include <base/signal.h>
#include <platform_session/client.h>
#include <rom_session/client.h>
#include <util/xml_node.h>
@ -34,31 +35,58 @@ class Platform::Connection : public Genode::Connection<Session>,
{
private:
/* 'Device' and 'Dma_buffer' access the '_rm' member */
/* 'Device' and 'Dma_buffer' access the '_env' member */
friend class Device;
friend class Dma_buffer;
Region_map &_rm;
Rom_session_client _rom {devices_rom()};
Constructible<Attached_dataspace> _ds {};
Env &_env;
Rom_session_client _rom {devices_rom()};
Constructible<Attached_dataspace> _ds {};
Constructible<Io_signal_handler<Connection>> _handler {};
void _try_attach()
{
_ds.destruct();
try { _ds.construct(_rm, _rom.dataspace()); }
try { _ds.construct(_env.rm(), _rom.dataspace()); }
catch (Attached_dataspace::Invalid_dataspace) {
warning("Invalid devices rom dataspace returned!");}
}
void _handle_io() {}
template <typename FN>
Capability<Device_interface> _wait_for_device(FN const & fn)
{
for (;;) {
/* repeatedly check for availability of device */
Capability<Device_interface> cap = fn();
if (cap.valid()) {
if (_handler.constructed()) {
sigh(Signal_context_capability());
_handler.destruct();
}
return cap;
}
if (!_handler.constructed()) {
_handler.construct(_env.ep(), *this,
&Connection::_handle_io);
sigh(*_handler);
}
_env.ep().wait_and_dispatch_one_io_signal();
}
}
public:
Connection(Env &env)
:
Genode::Connection<Session>(env, session(env.parent(),
"ram_quota=%u, cap_quota=%u",
RAM_QUOTA, CAP_QUOTA)),
"ram_quota=%u, cap_quota=%u",
RAM_QUOTA, CAP_QUOTA)),
Client(cap()),
_rm(env.rm())
_env(env)
{
_try_attach();
}
@ -75,14 +103,18 @@ class Platform::Connection : public Genode::Connection<Session>,
Capability<Device_interface> acquire_device(Device_name const &name) override
{
return retry_with_upgrade(Ram_quota{6*1024}, Cap_quota{6}, [&] () {
return Client::acquire_device(name); });
return _wait_for_device([&] () {
return retry_with_upgrade(Ram_quota{6*1024}, Cap_quota{6}, [&] () {
return Client::acquire_device(name); });
});
}
Capability<Device_interface> acquire_device()
{
return retry_with_upgrade(Ram_quota{6*1024}, Cap_quota{6}, [&] () {
return Client::acquire_single_device(); });
return _wait_for_device([&] () {
return retry_with_upgrade(Ram_quota{6*1024}, Cap_quota{6}, [&] () {
return Client::acquire_single_device(); });
});
}
Ram_dataspace_capability alloc_dma_buffer(size_t size, Cache cache) override
@ -105,30 +137,29 @@ class Platform::Connection : public Genode::Connection<Session>,
Capability<Device_interface> device_by_type(char const * type)
{
using String = Genode::String<64>;
return _wait_for_device([&] () {
Capability<Device_interface> cap;
using String = Genode::String<64>;
with_xml([&] (Xml_node & xml) {
xml.for_each_sub_node("device", [&] (Xml_node node) {
Capability<Device_interface> cap;
/* already found a device? */
if (cap.valid())
return;
with_xml([&] (Xml_node & xml) {
xml.for_each_sub_node("device", [&] (Xml_node node) {
if (node.attribute_value("type", String()) != type)
return;
/* already found a device? */
if (cap.valid())
return;
Device_name name = node.attribute_value("name", Device_name());
cap = acquire_device(name);
if (node.attribute_value("type", String()) != type)
return;
Device_name name = node.attribute_value("name", Device_name());
cap = acquire_device(name);
});
});
if (!cap.valid()) {
error(__func__, ": type=", type, " not found!");
error("device ROM content: ", xml);
}
});
return cap;
return cap;
});
}
};

View File

@ -59,7 +59,7 @@ class Platform::Device : Interface, Noncopyable
return _cap.call<Device_interface::Rpc_io_port_range>(index);
}
Region_map &_rm() { return _platform._rm; }
Region_map &_rm() { return _platform._env.rm(); }
public:

View File

@ -46,7 +46,7 @@ class Platform::Dma_buffer : Noncopyable
} _allocation;
Attached_dataspace _ds { _allocation.platform._rm, _allocation.cap };
Attached_dataspace _ds { _allocation.platform._env.rm(), _allocation.cap };
public:

View File

@ -88,7 +88,7 @@ install_config {
<config verbose="no"/>
</start>
<start name="platform_drv">
<start name="platform_drv" managing_system="yes">
<resource name="RAM" quantum="1M"/>
<provides> <service name="Platform"/> </provides>
<route>
@ -168,25 +168,6 @@ set good_string {
[init -> test-platform_drv] <irq number="32"/>
[init -> test-platform_drv] </device>
[init -> test-platform_drv] </devices>
[init -> test-platform_drv] Error: Device 0 not valid!
[init -> test-platform_drv] <devices version="7">
[init -> test-platform_drv] <device name="0" type="dummy-device" used="true">
[init -> test-platform_drv] <io_mem phys_addr="0x40000100" size="0x1000"/>
[init -> test-platform_drv] <irq number="32"/>
[init -> test-platform_drv] </device>
[init -> test-platform_drv] <device name="1" type="dummy-device" used="false">
[init -> test-platform_drv] <io_mem phys_addr="0x40001100" size="0x1000"/>
[init -> test-platform_drv] <irq number="33"/>
[init -> test-platform_drv] </device>
[init -> test-platform_drv] <device name="2" type="dummy-device" used="false">
[init -> test-platform_drv] <io_mem phys_addr="0x40002100" size="0x1000"/>
[init -> test-platform_drv] <irq number="34"/>
[init -> test-platform_drv] </device>
[init -> test-platform_drv] <device name="3" type="dummy-device" used="false">
[init -> test-platform_drv] <io_mem phys_addr="0x40003100" size="0x1000"/>
[init -> test-platform_drv] <irq number="35"/>
[init -> test-platform_drv] </device>
[init -> test-platform_drv] </devices>
[init -> test-platform_drv] Found next valid device of dummy type
[init -> test-platform_drv] Test has ended!
}

View File

@ -42,9 +42,10 @@ physical to virtual identical mappings of DMA memory for the device_pd
On some systems, e.g., base-hw kernel on certain ARM platforms, it allows the
platform driver to call system management firmware via kernel syscalls.
The 'info' attribute in a policy node describe, whether the client gets
permission to access a devices ROM containing detailed information about
the devices of its virtual bus.
The 'info' attribute in a policy node describe, whether the client's devices
ROM will contain detailed information about physical resources of the devices
of its virtual bus. This is only useful when using ported legacy drivers, which
operate with global names of physical resources.
Report facilities
-----------------

View File

@ -112,7 +112,8 @@ void Driver::Device::release(Session_component & sc)
}
void Driver::Device::report(Xml_generator & xml, Device_model & devices)
void Driver::Device::report(Xml_generator & xml, Device_model & devices,
bool info)
{
xml.node("device", [&] () {
xml.attribute("name", name());
@ -120,17 +121,23 @@ void Driver::Device::report(Xml_generator & xml, Device_model & devices)
xml.attribute("used", _owner.valid());
_io_mem_list.for_each([&] (Io_mem & io_mem) {
xml.node("io_mem", [&] () {
if (!info)
return;
xml.attribute("phys_addr", String<16>(Hex(io_mem.range.start)));
xml.attribute("size", String<16>(Hex(io_mem.range.size)));
});
});
_irq_list.for_each([&] (Irq & irq) {
xml.node("irq", [&] () {
if (!info)
return;
xml.attribute("number", irq.number);
});
});
_io_port_range_list.for_each([&] (Io_port_range & io_port_range) {
xml.node("io_port_range", [&] () {
if (!info)
return;
xml.attribute("phys_addr", String<16>(Hex(io_port_range.addr)));
xml.attribute("size", String<16>(Hex(io_port_range.size)));
});
@ -171,7 +178,7 @@ void Driver::Device_model::update_report()
if (_reporter.enabled()) {
Reporter::Xml_generator xml(_reporter, [&] () {
for_each([&] (Device & device) {
device.report(xml, *this); });
device.report(xml, *this, true); });
});
}
}

View File

@ -167,7 +167,7 @@ class Driver::Device : private List_model<Device>::Element
fn(idx++, ipr.addr, ipr.size); });
}
void report(Xml_generator &, Device_model &);
void report(Xml_generator &, Device_model &, bool);
protected:

View File

@ -97,14 +97,11 @@ void Session_component::update_policy(bool info, Policy_version version)
void Session_component::produce_xml(Xml_generator &xml)
{
if (!_info)
return;
if (_version.valid())
xml.attribute("version", _version);
_devices.for_each([&] (Device & dev) {
if (matches(dev)) dev.report(xml, _devices); });
if (matches(dev)) dev.report(xml, _devices, _info); });
}

View File

@ -60,7 +60,6 @@ struct Main
Reporter device_reporter { env, "devices" };
Reconstructible<Platform::Connection> platform { env };
Constructible<Platform::Connection> platform_2 { };
Signal_handler<Main> device_rom_handler { env.ep(), *this,
&Main::handle_device_update };
@ -193,14 +192,6 @@ struct Main
next_step(1, 1, 0x40000100, 32);
return;
case 6:
start_driver(0);
platform_2.construct(env);
devices[1].construct(*platform_2, Platform::Device::Name(0));
stop_driver(1);
platform_2.destruct();
next_step(4, 4, 0x40000100, 32);
return;
case 7:
{
Platform::Device dev (*platform, Platform::Device::Type({"dummy-device"}));
if (dev._cap.valid()) log("Found next valid device of dummy type");