diff --git a/repos/os/recipes/src/virtdev_rom/content.mk b/repos/os/recipes/src/virtdev_rom/content.mk new file mode 100644 index 0000000000..9307f711ae --- /dev/null +++ b/repos/os/recipes/src/virtdev_rom/content.mk @@ -0,0 +1,2 @@ +SRC_DIR = src/drivers/virtdev_rom +include $(GENODE_DIR)/repos/base/recipes/src/content.inc diff --git a/repos/os/recipes/src/virtdev_rom/hash b/repos/os/recipes/src/virtdev_rom/hash new file mode 100644 index 0000000000..d0356ead5d --- /dev/null +++ b/repos/os/recipes/src/virtdev_rom/hash @@ -0,0 +1 @@ +2020-07-03 e1c0e261929b71468814fdbcba02c7e0f6455c25 diff --git a/repos/os/recipes/src/virtdev_rom/used_apis b/repos/os/recipes/src/virtdev_rom/used_apis new file mode 100644 index 0000000000..ec3bf565df --- /dev/null +++ b/repos/os/recipes/src/virtdev_rom/used_apis @@ -0,0 +1,2 @@ +base +os diff --git a/repos/os/src/drivers/virtdev_rom/README b/repos/os/src/drivers/virtdev_rom/README new file mode 100644 index 0000000000..5daf22ecc8 --- /dev/null +++ b/repos/os/src/drivers/virtdev_rom/README @@ -0,0 +1,31 @@ +This driver probes available VirtIO MMIO devices on Qemu's virt platform and +exports the results in XML format via a ROM session. The exported ROM module +is suitable as configuration for the generic ARM platform driver. + +This driver can currently recognize NIC, block, console, RNG, GPU, and +input devices. When a device of a given type is detected, it is assigned a +name based on its type and index on a bus. For example, if the system has +two VirtIO NIC devices, their names will be "nic0" and "nic1". + +To simplify probing devices of the same type, each XML device node has +an additional "type" property node. The value of this property is simply +set to a string indicating device type, without any extra indices, e.g., +"nic", "gpu", "input", etc. + +! +! +! +! + +Configuration +~~~~~~~~~~~~~ + +The virtdev_rom component appends the unmodified content of its own configuration +ROM to its output. This makes it possible to easily append extra policies +to the device configuration produced by the driver, e.g.,: + +! +! +! +! +! diff --git a/repos/os/src/drivers/virtdev_rom/main.cc b/repos/os/src/drivers/virtdev_rom/main.cc new file mode 100644 index 0000000000..b7dc07ebe6 --- /dev/null +++ b/repos/os/src/drivers/virtdev_rom/main.cc @@ -0,0 +1,200 @@ +/* + * \brief Virt Qemu device config generator for ARM platform driver + * \author Piotr Tworek + * \date 2020-07-01 + */ + +/* + * Copyright (C) 2020 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Virtdev_rom { + using namespace Genode; + class Session_component; + class Root; + struct Main; +}; + + +class Virtdev_rom::Session_component : public Rpc_object +{ + private: + + /* + * Noncopyable + */ + Session_component(Session_component const &) = delete; + Session_component &operator = (Session_component const &) = delete; + + Rom_dataspace_capability const _rom_cap; + + public: + + Session_component(Rom_dataspace_capability cap) : _rom_cap(cap) + { } + + Rom_dataspace_capability dataspace() override { return _rom_cap; } + + void sigh(Signal_context_capability) override { } +}; + + +class Virtdev_rom::Root : public Root_component +{ + private: + + /* + * Noncopyable + */ + Root(Root const &) = delete; + Root &operator = (Root const &) = delete; + + Env &_env; + Ram_dataspace_capability _ds; + + Session_component *_create_session(const char *) override + { + auto cap = static_cap_cast(_ds); + return new (md_alloc()) Session_component( + static_cap_cast(cap)); + } + + public: + + Root(Env &env, Allocator &md_alloc, Ram_dataspace_capability &cap) + : Root_component(env.ep(), md_alloc), + _env(env), + _ds(cap) + { } +}; + + +struct Virtdev_rom::Main +{ + enum { + /* Taken from include/hw/arm/virt.h in Qemu source tree. */ + NUM_VIRTIO_TRANSPORTS = 32, + /* Taken from hw/arm/virt.c in Qemu source tree. */ + BASE_ADDRESS = 0x0A000000, + DEVICE_SIZE = 0x200, + IRQ_BASE = 48, + VIRTIO_MMIO_MAGIC = 0x74726976, + }; + + enum { MAX_ROM_SIZE = 4096, DEVICE_NAME_LEN = 64 }; + + Env &_env; + Ram_dataspace_capability _ds { _env.ram().alloc(MAX_ROM_SIZE) }; + Sliced_heap _heap { _env.ram(), _env.rm() }; + Virtdev_rom::Root _root { _env, _heap, _ds }; + + struct Device : public Attached_mmio + { + struct Magic : Register<0x000, 32> { }; + struct Id : Register<0x008, 32> { + enum Value { + INVALID = 0, + NIC = 1, + BLOCK = 2, + CONSOLE = 3, + RNG = 4, + GPU = 16, + INPUT = 18, + MAX_VAL = INPUT, + }; + }; + + Device(Env &env, addr_t base, size_t size) + : Attached_mmio(env, base, size, false) { } + }; + + static char const *_name_for_id(unsigned id) + { + switch (id) { + case Device::Id::NIC : return "nic"; + case Device::Id::BLOCK : return "block"; + case Device::Id::CONSOLE : return "console"; + case Device::Id::RNG : return "rng"; + case Device::Id::GPU : return "gpu"; + case Device::Id::INPUT : return "input"; + default: { + warning("Unhandled VirtIO device ID: ", Hex(id)); + return "virtio"; + } + } + } + + void _probe_devices() + { + Attached_dataspace ds(_env.rm(), _ds); + Attached_rom_dataspace config { _env, "config" }; + + Xml_generator xml(ds.local_addr(), ds.size(), "config", [&] () + { + uint8_t device_type_idx[Device::Id::MAX_VAL] = { 0 }; + + for (size_t idx = 0; idx < NUM_VIRTIO_TRANSPORTS; ++idx) { + addr_t addr = BASE_ADDRESS + idx * DEVICE_SIZE; + Device device { _env, BASE_ADDRESS + idx * DEVICE_SIZE, DEVICE_SIZE }; + + if (device.read() != VIRTIO_MMIO_MAGIC) { + warning("Found non VirrtIO MMIO device @ ", addr); + continue; + } + + auto id = device.read(); + if (id == Device::Id::INVALID) + continue; + + xml.node("device", [&] () + { + static char name[DEVICE_NAME_LEN]; + snprintf(name, sizeof(name), "%s%u", _name_for_id(id), device_type_idx[id - 1]++); + xml.attribute("name", name); + xml.node("io_mem", [&] () { + xml.attribute("address", addr); + xml.attribute("size", DEVICE_SIZE); + }); + xml.node("irq", [&] () { + xml.attribute("number", IRQ_BASE + idx); + }); + xml.node("property", [&] () { + xml.attribute("name", "type"); + xml.attribute("value", _name_for_id(id)); + }); + }); + } + + config.xml().with_raw_content([&] (char const *txt, size_t sz) { + xml.append(txt, sz); + }); + }); + } + + Main(Env &env) : _env(env) + { + _probe_devices(); + env.parent().announce(_env.ep().manage(_root)); + } + + ~Main() { _env.ram().free(_ds); } +}; + + +void Component::construct(Genode::Env &env) +{ + static Virtdev_rom::Main main(env); +} diff --git a/repos/os/src/drivers/virtdev_rom/target.mk b/repos/os/src/drivers/virtdev_rom/target.mk new file mode 100644 index 0000000000..b4a142362a --- /dev/null +++ b/repos/os/src/drivers/virtdev_rom/target.mk @@ -0,0 +1,3 @@ +TARGET = virtdev_rom +SRC_CC = main.cc +LIBS = base