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