diff --git a/repos/gems/recipes/pkg/drivers_managed-pc/README b/repos/gems/recipes/pkg/drivers_managed-pc/README
new file mode 100644
index 0000000000..01323fae15
--- /dev/null
+++ b/repos/gems/recipes/pkg/drivers_managed-pc/README
@@ -0,0 +1,4 @@
+
+ Device-driver subsystem that starts drivers for
+ framebuffer, input, and block devices on demand
+
diff --git a/repos/gems/recipes/pkg/drivers_managed-pc/archives b/repos/gems/recipes/pkg/drivers_managed-pc/archives
new file mode 100644
index 0000000000..c5a5c8a7ae
--- /dev/null
+++ b/repos/gems/recipes/pkg/drivers_managed-pc/archives
@@ -0,0 +1,15 @@
+_/src/platform_drv
+_/src/acpi_drv
+_/src/ps2_drv
+_/src/usb_drv
+_/src/usb_block_drv
+_/src/vesa_drv
+_/src/intel_fb_drv
+_/src/ahci_drv
+_/src/fs_report
+_/src/fs_rom
+_/src/ram_fs
+_/src/input_filter
+_/src/init
+_/src/driver_manager
+_/raw/drivers_managed-pc
diff --git a/repos/gems/recipes/pkg/drivers_managed-pc/hash b/repos/gems/recipes/pkg/drivers_managed-pc/hash
new file mode 100644
index 0000000000..f5c1ae97e3
--- /dev/null
+++ b/repos/gems/recipes/pkg/drivers_managed-pc/hash
@@ -0,0 +1 @@
+2017-09-06-a d0f1da3a828141f7c8e57557c873a910faa19caf
diff --git a/repos/gems/recipes/raw/drivers_managed-pc/content.mk b/repos/gems/recipes/raw/drivers_managed-pc/content.mk
new file mode 100644
index 0000000000..fb737425d7
--- /dev/null
+++ b/repos/gems/recipes/raw/drivers_managed-pc/content.mk
@@ -0,0 +1,10 @@
+content: drivers.config fb_drv.config input_filter.config en_us.chargen
+
+drivers.config:
+ cp $(REP_DIR)/recipes/raw/drivers_managed-pc/$@ $@
+
+fb_drv.config input_filter.config:
+ cp $(GENODE_DIR)/repos/os/recipes/raw/drivers_interactive-pc/$@ $@
+
+en_us.chargen:
+ cp $(GENODE_DIR)/repos/os/src/server/input_filter/$@ $@
diff --git a/repos/gems/recipes/raw/drivers_managed-pc/drivers.config b/repos/gems/recipes/raw/drivers_managed-pc/drivers.config
new file mode 100644
index 0000000000..63258d100d
--- /dev/null
+++ b/repos/gems/recipes/raw/drivers_managed-pc/drivers.config
@@ -0,0 +1,240 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/repos/gems/recipes/raw/drivers_managed-pc/hash b/repos/gems/recipes/raw/drivers_managed-pc/hash
new file mode 100644
index 0000000000..820d223182
--- /dev/null
+++ b/repos/gems/recipes/raw/drivers_managed-pc/hash
@@ -0,0 +1 @@
+2017-06-30 a5a5feaf6577dcf69602022f0a55078a039b7eda
diff --git a/repos/gems/recipes/src/driver_manager/content.mk b/repos/gems/recipes/src/driver_manager/content.mk
new file mode 100644
index 0000000000..1080a34fe8
--- /dev/null
+++ b/repos/gems/recipes/src/driver_manager/content.mk
@@ -0,0 +1,2 @@
+SRC_DIR := src/app/driver_manager
+include $(GENODE_DIR)/repos/base/recipes/src/content.inc
diff --git a/repos/gems/recipes/src/driver_manager/hash b/repos/gems/recipes/src/driver_manager/hash
new file mode 100644
index 0000000000..38aae52573
--- /dev/null
+++ b/repos/gems/recipes/src/driver_manager/hash
@@ -0,0 +1 @@
+2017-09-07-a 6616814f9b594c0a130f086432b83a349ac5aa48
diff --git a/repos/gems/recipes/src/driver_manager/used_apis b/repos/gems/recipes/src/driver_manager/used_apis
new file mode 100644
index 0000000000..285587958c
--- /dev/null
+++ b/repos/gems/recipes/src/driver_manager/used_apis
@@ -0,0 +1,8 @@
+base
+os
+report_session
+block_session
+usb_session
+framebuffer_session
+timer_session
+platform_session
diff --git a/repos/gems/run/driver_manager.run b/repos/gems/run/driver_manager.run
new file mode 100644
index 0000000000..47a76c2790
--- /dev/null
+++ b/repos/gems/run/driver_manager.run
@@ -0,0 +1,118 @@
+if {![have_spec x86] || [have_spec linux]} {
+ puts "Platform is unsupported."
+ exit 0
+}
+
+build { app/driver_manager test/driver_manager lib/ld }
+
+create_boot_directory
+
+import_from_depot genodelabs/src/[base_src] \
+ genodelabs/pkg/drivers_managed-pc \
+ genodelabs/src/report_rom \
+ genodelabs/src/init
+
+install_config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
+
+set fd [open [run_dir]/genode/fb_drv.config "w"]
+puts $fd {
+
+
+
+}
+close $fd
+
+
+#
+# Override 'drivers.config' as supplied with the 'drivers_managed-pc' pkg
+# with the (possibly modified) version found in the source tree. This avoids
+# the need to re-create the pkg while testing modifications of this file.
+#
+file copy -force [genode_dir]/repos/gems/recipes/raw/drivers_managed-pc/drivers.config \
+ [run_dir]/genode/drivers.config
+
+build_boot_image { driver_manager test-driver_manager ld.lib.so }
+
+#
+# Build ext2 file-system images to be supplied to Qemu
+#
+if {[have_include "power_on/qemu"]} {
+
+ set mke2fs [check_installed mke2fs]
+ set dd [check_installed dd]
+
+ catch { exec $dd if=/dev/zero of=bin/hdd_disk.raw bs=1M count=32 }
+ catch { exec $mke2fs -F bin/hdd_disk.raw }
+}
+
+#
+# Qemu opts for supplying hdd_disk.raw as AHCI disk
+#
+append qemu_args " -m 128 "
+append qemu_args " -nographic "
+append qemu_args " -device ahci,id=ahci "
+append qemu_args " -drive id=hdd,file=bin/hdd_disk.raw,format=raw,if=none -device ide-hd,drive=hdd,bus=ahci.0 "
+
+run_genode_until {.*all expected devices present and accessible.*\n} 120
+
diff --git a/repos/gems/src/app/driver_manager/main.cc b/repos/gems/src/app/driver_manager/main.cc
new file mode 100644
index 0000000000..6f0d408bf0
--- /dev/null
+++ b/repos/gems/src/app/driver_manager/main.cc
@@ -0,0 +1,448 @@
+/*
+ * \brief Driver manager
+ * \author Norman Feske
+ * \date 2017-06-13
+ */
+
+/*
+ * Copyright (C) 2017 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace Driver_manager {
+ using namespace Genode;
+ struct Main;
+ struct Block_devices_generator;
+ struct Device_driver;
+ struct Intel_fb_driver;
+ struct Vesa_fb_driver;
+ struct Ahci_driver;
+
+ struct Priority { int value; };
+}
+
+
+struct Driver_manager::Block_devices_generator
+{
+ virtual void generate_block_devices() = 0;
+};
+
+
+class Driver_manager::Device_driver : Noncopyable
+{
+ public:
+
+ typedef String<64> Name;
+ typedef String<100> Binary;
+ typedef String<32> Service;
+
+ protected:
+
+ static void _gen_common_start_node_content(Xml_generator &xml,
+ Name const &name,
+ Binary const &binary,
+ Ram_quota ram,
+ Cap_quota caps,
+ Priority priority)
+ {
+ xml.attribute("name", name);
+ xml.attribute("caps", String<64>(caps));
+ xml.attribute("priority", priority.value);
+ xml.node("binary", [&] () { xml.attribute("name", binary); });
+ xml.node("resource", [&] () {
+ xml.attribute("name", "RAM");
+ xml.attribute("quantum", String<64>(ram));
+ });
+ }
+
+ template
+ static void _gen_provides_node(Xml_generator &xml)
+ {
+ xml.node("provides", [&] () {
+ xml.node("service", [&] () {
+ xml.attribute("name", SESSION::service_name()); }); });
+ }
+
+ static void _gen_config_route(Xml_generator &xml, char const *config_name)
+ {
+ xml.node("service", [&] () {
+ xml.attribute("name", Rom_session::service_name());
+ xml.attribute("label", "config");
+ xml.node("parent", [&] () {
+ xml.attribute("label", config_name); });
+ });
+ }
+
+ static void _gen_default_parent_route(Xml_generator &xml)
+ {
+ xml.node("any-service", [&] () {
+ xml.node("parent", [&] () { }); });
+ }
+
+ template
+ static void _gen_forwarded_service(Xml_generator &xml,
+ Device_driver::Name const &name)
+ {
+ xml.node("service", [&] () {
+ xml.attribute("name", SESSION::service_name());
+ xml.node("default-policy", [&] () {
+ xml.node("child", [&] () {
+ xml.attribute("name", name);
+ });
+ });
+ });
+ };
+
+ virtual ~Device_driver() { }
+
+ public:
+
+ virtual void generate_start_node(Xml_generator &xml) const = 0;
+};
+
+
+struct Driver_manager::Intel_fb_driver : Device_driver
+{
+ void generate_start_node(Xml_generator &xml) const override
+ {
+ xml.node("start", [&] () {
+ _gen_common_start_node_content(xml, "intel_fb_drv", "intel_fb_drv",
+ Ram_quota{20*1024*1024}, Cap_quota{100},
+ Priority{0});
+ _gen_provides_node(xml);
+ xml.node("route", [&] () {
+ _gen_config_route(xml, "fb_drv.config");
+ _gen_default_parent_route(xml);
+ });
+ });
+ _gen_forwarded_service(xml, "intel_fb_drv");
+ }
+};
+
+
+struct Driver_manager::Vesa_fb_driver : Device_driver
+{
+ void generate_start_node(Xml_generator &xml) const override
+ {
+ xml.node("start", [&] () {
+ _gen_common_start_node_content(xml, "vesa_fb_drv", "fb_drv",
+ Ram_quota{8*1024*1024}, Cap_quota{100},
+ Priority{-1});
+ _gen_provides_node(xml);
+ xml.node("route", [&] () {
+ _gen_config_route(xml, "fb_drv.config");
+ _gen_default_parent_route(xml);
+ });
+ });
+ _gen_forwarded_service(xml, "vesa_fb_drv");
+ }
+};
+
+
+struct Driver_manager::Ahci_driver : Device_driver
+{
+ void generate_start_node(Xml_generator &xml) const override
+ {
+ xml.node("start", [&] () {
+ _gen_common_start_node_content(xml, "ahci_drv", "ahci_drv",
+ Ram_quota{10*1024*1024}, Cap_quota{100},
+ Priority{-1});
+ _gen_provides_node(xml);
+ xml.node("config", [&] () {
+ xml.node("report", [&] () { xml.attribute("ports", "yes"); });
+ for (unsigned i = 0; i < 6; i++) {
+ xml.node("policy", [&] () {
+ xml.attribute("label_suffix", String<64>(" ahci-", i));
+ xml.attribute("device", i);
+ xml.attribute("writeable", "yes");
+ });
+ }
+ });
+ xml.node("route", [&] () {
+ xml.node("service", [&] () {
+ xml.attribute("name", "Report");
+ xml.node("parent", [&] () { xml.attribute("label", "ahci_ports"); });
+ });
+ _gen_default_parent_route(xml);
+ });
+ });
+ }
+
+ void generate_block_service_forwarding_policy(Xml_generator &xml) const
+ {
+ for (unsigned i = 0; i < 6; i++) {
+ xml.node("policy", [&] () {
+ xml.attribute("label_suffix", String<64>(" ahci-", i));
+ xml.node("child", [&] () {
+ xml.attribute("name", "ahci_drv");
+ });
+ });
+ }
+ }
+};
+
+
+struct Driver_manager::Main : Block_devices_generator
+{
+ Env &_env;
+
+ Attached_rom_dataspace _init_state { _env, "init_state" };
+ Attached_rom_dataspace _usb_devices { _env, "usb_devices" };
+ Attached_rom_dataspace _pci_devices { _env, "pci_devices" };
+ Attached_rom_dataspace _ahci_ports { _env, "ahci_ports" };
+
+ Reporter _init_config { _env, "config", "init.config" };
+ Reporter _usb_drv_config { _env, "config", "usb_drv.config" };
+ Reporter _block_devices { _env, "block_devices" };
+
+ Constructible _intel_fb_driver;
+ Constructible _vesa_fb_driver;
+ Constructible _ahci_driver;
+
+ void _handle_pci_devices_update();
+
+ Signal_handler _pci_devices_update_handler {
+ _env.ep(), *this, &Main::_handle_pci_devices_update };
+
+ void _handle_usb_devices_update();
+
+ Signal_handler _usb_devices_update_handler {
+ _env.ep(), *this, &Main::_handle_usb_devices_update };
+
+ void _handle_ahci_ports_update();
+
+ Signal_handler _ahci_ports_update_handler {
+ _env.ep(), *this, &Main::_handle_ahci_ports_update };
+
+ static void _gen_parent_service_xml(Xml_generator &xml, char const *name)
+ {
+ xml.node("service", [&] () { xml.attribute("name", name); });
+ };
+
+ void _generate_init_config (Reporter &) const;
+ void _generate_usb_drv_config (Reporter &, Xml_node) const;
+ void _generate_block_devices (Reporter &) const;
+
+ /**
+ * Block_devices_generator interface
+ */
+ void generate_block_devices() override { _generate_block_devices(_block_devices); }
+
+ Main(Env &env) : _env(env)
+ {
+ _init_config.enabled(true);
+ _usb_drv_config.enabled(true);
+ _block_devices.enabled(true);
+
+ _pci_devices.sigh(_pci_devices_update_handler);
+ _usb_devices.sigh(_usb_devices_update_handler);
+ _ahci_ports .sigh(_ahci_ports_update_handler);
+
+ _generate_init_config(_init_config);
+ _generate_usb_drv_config(_usb_drv_config, Xml_node(""));
+
+ _handle_pci_devices_update();
+ _handle_usb_devices_update();
+ _handle_ahci_ports_update();
+ }
+};
+
+
+void Driver_manager::Main::_handle_pci_devices_update()
+{
+ _pci_devices.update();
+
+ bool has_vga = false;
+ bool has_intel_graphics = false;
+ bool has_ahci = false;
+
+ _pci_devices.xml().for_each_sub_node([&] (Xml_node device) {
+
+ uint16_t const vendor_id = device.attribute_value("vendor_id", 0UL);
+ uint16_t const class_code = device.attribute_value("class_code", 0UL) >> 8;
+
+ enum {
+ VENDOR_INTEL = 0x8086U,
+ CLASS_VGA = 0x300U,
+ CLASS_AHCI = 0x106U,
+ };
+
+ if (class_code == CLASS_VGA)
+ has_vga = true;
+
+ if (vendor_id == VENDOR_INTEL && class_code == CLASS_VGA)
+ has_intel_graphics = true;
+
+ if (vendor_id == VENDOR_INTEL && class_code == CLASS_AHCI)
+ has_ahci = true;
+ });
+
+ if (!_intel_fb_driver.constructed() && has_intel_graphics) {
+ _intel_fb_driver.construct();
+ _vesa_fb_driver.destruct();
+ _generate_init_config(_init_config);
+ }
+
+ if (!_vesa_fb_driver.constructed() && has_vga && !has_intel_graphics) {
+ _intel_fb_driver.destruct();
+ _vesa_fb_driver.construct();
+ _generate_init_config(_init_config);
+ }
+
+ if (!_ahci_driver.constructed() && has_ahci) {
+ _ahci_driver.construct();
+ _generate_init_config(_init_config);
+ }
+}
+
+
+void Driver_manager::Main::_handle_ahci_ports_update()
+{
+ _ahci_ports.update();
+ _generate_block_devices(_block_devices);
+}
+
+
+void Driver_manager::Main::_handle_usb_devices_update()
+{
+ _usb_devices.update();
+
+ _generate_usb_drv_config(_usb_drv_config, _usb_devices.xml());
+}
+
+
+void Driver_manager::Main::_generate_init_config(Reporter &init_config) const
+{
+ Reporter::Xml_generator xml(init_config, [&] () {
+
+ xml.attribute("verbose", false);
+ xml.attribute("prio_levels", 2);
+
+ xml.node("report", [&] () { xml.attribute("child_ram", true); });
+
+ xml.node("parent-provides", [&] () {
+ _gen_parent_service_xml(xml, Rom_session::service_name());
+ _gen_parent_service_xml(xml, Io_mem_session::service_name());
+ _gen_parent_service_xml(xml, Io_port_session::service_name());
+ _gen_parent_service_xml(xml, Cpu_session::service_name());
+ _gen_parent_service_xml(xml, Pd_session::service_name());
+ _gen_parent_service_xml(xml, Rm_session::service_name());
+ _gen_parent_service_xml(xml, Log_session::service_name());
+ _gen_parent_service_xml(xml, Timer::Session::service_name());
+ _gen_parent_service_xml(xml, Platform::Session::service_name());
+ _gen_parent_service_xml(xml, Report::Session::service_name());
+ _gen_parent_service_xml(xml, Usb::Session::service_name());
+ });
+
+
+ if (_intel_fb_driver.constructed())
+ _intel_fb_driver->generate_start_node(xml);
+
+ if (_vesa_fb_driver.constructed())
+ _vesa_fb_driver->generate_start_node(xml);
+
+ if (_ahci_driver.constructed())
+ _ahci_driver->generate_start_node(xml);
+
+ /* block-service forwarding rules */
+ xml.node("service", [&] () {
+ xml.attribute("name", Block::Session::service_name());
+ if (_ahci_driver.constructed())
+ _ahci_driver->generate_block_service_forwarding_policy(xml);
+ });
+ });
+}
+
+
+void Driver_manager::Main::_generate_block_devices(Reporter &block_devices) const
+{
+ Reporter::Xml_generator xml(block_devices, [&] () {
+
+ _ahci_ports.xml().for_each_sub_node([&] (Xml_node ahci_port) {
+
+ xml.node("device", [&] () {
+
+ unsigned long const
+ num = ahci_port.attribute_value("num", 0UL),
+ block_count = ahci_port.attribute_value("block_count", 0UL),
+ block_size = ahci_port.attribute_value("block_size", 0UL);
+
+ typedef String<80> Model;
+ Model const model = ahci_port.attribute_value("model", Model());
+
+ xml.attribute("label", String<64>("ahci-", num));
+ xml.attribute("block_count", block_count);
+ xml.attribute("block_size", block_size);
+ xml.attribute("model", model);
+ });
+ });
+ });
+}
+
+
+void Driver_manager::Main::_generate_usb_drv_config(Reporter &usb_drv_config,
+ Xml_node devices) const
+{
+ Reporter::Xml_generator xml(usb_drv_config, [&] () {
+
+ xml.attribute("uhci", true);
+ xml.attribute("ehci", true);
+ xml.attribute("xhci", true);
+ xml.node("hid", [&] () { });
+ xml.node("raw", [&] () {
+ xml.node("report", [&] () { xml.attribute("devices", true); });
+
+ devices.for_each_sub_node("device", [&] (Xml_node device) {
+
+ typedef String<64> Label;
+ typedef String<32> Id;
+
+ Label const label = device.attribute_value("label", Label());
+ Id const vendor_id = device.attribute_value("vendor_id", Id());
+ Id const product_id = device.attribute_value("product_id", Id());
+
+ /*
+ * Limit USB sessions to storage in order to avoid conflicts with
+ * the USB driver's built-in HID drivers.
+ */
+ unsigned long const class_code = device.attribute_value("class", 0UL);
+
+ enum { USB_CLASS_MASS_STORAGE = 8 };
+
+ bool const expose_as_usb_raw = (class_code == USB_CLASS_MASS_STORAGE);
+ if (!expose_as_usb_raw)
+ return;
+
+ xml.node("policy", [&] () {
+ xml.attribute("label_suffix", label);
+ xml.attribute("vendor_id", vendor_id);
+ xml.attribute("product_id", product_id);
+
+ /* annotate policy to make storage devices easy to spot */
+ if (class_code == USB_CLASS_MASS_STORAGE)
+ xml.attribute("class", "storage");
+ });
+ });
+ });
+ });
+}
+
+
+void Component::construct(Genode::Env &env) { static Driver_manager::Main main(env); }
diff --git a/repos/gems/src/app/driver_manager/target.mk b/repos/gems/src/app/driver_manager/target.mk
new file mode 100644
index 0000000000..8dafdfea07
--- /dev/null
+++ b/repos/gems/src/app/driver_manager/target.mk
@@ -0,0 +1,5 @@
+TARGET = driver_manager
+SRC_CC = main.cc
+LIBS += base
+
+REQUIRES = x86
diff --git a/repos/gems/src/test/driver_manager/main.cc b/repos/gems/src/test/driver_manager/main.cc
new file mode 100644
index 0000000000..ddb7e50ccf
--- /dev/null
+++ b/repos/gems/src/test/driver_manager/main.cc
@@ -0,0 +1,142 @@
+/*
+ * \brief Test for validating the device detection of the driver manager
+ * \author Norman Feske
+ * \date 2017-06-30
+ */
+
+/*
+ * Copyright (C) 2017 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace Test {
+ using namespace Genode;
+ struct Main;
+}
+
+
+struct Test::Main
+{
+ Env &_env;
+
+ Attached_rom_dataspace _config { _env, "config" };
+ Attached_rom_dataspace _block_devices { _env, "block_devices" };
+
+ static bool _attribute_matches(char const *attr, Xml_node expected, Xml_node checked)
+ {
+ typedef String<80> Value;
+ return !expected.has_attribute(attr)
+ || (expected.attribute_value(attr, Value()) ==
+ checked .attribute_value(attr, Value()));
+ }
+
+ static bool _block_device_matches(Xml_node expect, Xml_node device)
+ {
+ return _attribute_matches("label", expect, device)
+ && _attribute_matches("block_size", expect, device)
+ && _attribute_matches("block_count", expect, device);
+ }
+
+ static bool _usb_block_device_matches(Xml_node expect, Xml_node device)
+ {
+ return _block_device_matches(expect, device)
+ && _attribute_matches("vendor", expect, device)
+ && _attribute_matches("product", expect, device);
+ }
+
+ static bool _ahci_block_device_matches(Xml_node expect, Xml_node device)
+ {
+ return _block_device_matches(expect, device)
+ && _attribute_matches("model", expect, device)
+ && _attribute_matches("serial", expect, device);
+ }
+
+ void _check_conditions()
+ {
+ _block_devices.update();
+
+ bool expected_devices_present = true;
+
+ log("-- check presence of expected block devices --");
+
+ _config.xml().for_each_sub_node([&] (Xml_node expect) {
+
+ /* skip nodes that are unrelated to block devices */
+ if (expect.type() != "check_usb_block_device"
+ && expect.type() != "check_ahci_block_device")
+ return;
+
+ bool device_exists = false;
+
+ _block_devices.xml().for_each_sub_node("device", [&] (Xml_node device) {
+
+ if (expect.type() == "check_usb_block_device"
+ && _usb_block_device_matches(expect, device))
+ device_exists = true;
+
+ if (expect.type() == "check_ahci_block_device"
+ && _ahci_block_device_matches(expect, device))
+ device_exists = true;
+ });
+
+ log("block device '", expect.attribute_value("label", String<80>()), "' ",
+ device_exists ? "present" : "not present");
+
+ if (!device_exists)
+ expected_devices_present = false;
+ });
+
+ if (!expected_devices_present)
+ return;
+
+ /* attempt to create a session to each block device */
+ _block_devices.xml().for_each_sub_node("device", [&] (Xml_node device) {
+
+ typedef String<64> Label;
+ Label label = device.attribute_value("label", Label());
+
+ log("connect to block device '", label, "'");
+
+ Heap heap(_env.ram(), _env.rm());
+ Allocator_avl packet_alloc(&heap);
+ Block::Connection block(_env, &packet_alloc, 128*1024, label.string());
+ });
+
+ log("all expected devices present and accessible");
+ }
+
+ Signal_handler _block_devices_update_handler {
+ _env.ep(), *this, &Main::_check_conditions };
+
+ Main(Env &env) : _env(env)
+ {
+ if (_config.xml().has_sub_node("check_framebuffer")) {
+ log("connect to framebuffer driver");
+ Framebuffer::Mode mode(640, 480, Framebuffer::Mode::RGB565);
+ Framebuffer::Connection fb(_env, mode);
+ }
+
+ if (_config.xml().has_sub_node("check_input")) {
+ log("connect to input driver");
+ Input::Connection input(_env);
+ }
+
+ _block_devices.sigh(_block_devices_update_handler);
+ _check_conditions();
+ }
+};
+
+
+void Component::construct(Genode::Env &env) { static Test::Main main(env); }
+
diff --git a/repos/gems/src/test/driver_manager/target.mk b/repos/gems/src/test/driver_manager/target.mk
new file mode 100644
index 0000000000..a09c0d524a
--- /dev/null
+++ b/repos/gems/src/test/driver_manager/target.mk
@@ -0,0 +1,3 @@
+TARGET = test-driver_manager
+SRC_CC = main.cc
+LIBS += base