diff --git a/repos/os/recipes/pkg/drivers_interactive-virt_qemu/README b/repos/os/recipes/pkg/drivers_interactive-virt_qemu/README
new file mode 100644
index 0000000000..3273ce23de
--- /dev/null
+++ b/repos/os/recipes/pkg/drivers_interactive-virt_qemu/README
@@ -0,0 +1,3 @@
+
+ Device drivers needed to run interactive
+ scenarios on the Virt platform as emulated by Qemu
diff --git a/repos/os/recipes/pkg/drivers_interactive-virt_qemu/archives b/repos/os/recipes/pkg/drivers_interactive-virt_qemu/archives
new file mode 100644
index 0000000000..455264e522
--- /dev/null
+++ b/repos/os/recipes/pkg/drivers_interactive-virt_qemu/archives
@@ -0,0 +1,4 @@
+_/src/virt_qemu_drivers
+_/src/platform_drv
+_/src/event_filter
+_/raw/drivers_interactive-virt_qemu
diff --git a/repos/os/recipes/pkg/drivers_interactive-virt_qemu/hash b/repos/os/recipes/pkg/drivers_interactive-virt_qemu/hash
new file mode 100644
index 0000000000..18dfb2eb5e
--- /dev/null
+++ b/repos/os/recipes/pkg/drivers_interactive-virt_qemu/hash
@@ -0,0 +1 @@
+2021-04-16-n dfdb663efd7c1a6b2e9bb399f03ac7bb42657ba5
diff --git a/repos/os/recipes/raw/drivers_interactive-virt_qemu/content.mk b/repos/os/recipes/raw/drivers_interactive-virt_qemu/content.mk
new file mode 100644
index 0000000000..54b94f2896
--- /dev/null
+++ b/repos/os/recipes/raw/drivers_interactive-virt_qemu/content.mk
@@ -0,0 +1,7 @@
+content: drivers.config event_filter.config en_us.chargen special.chargen
+
+drivers.config event_filter.config:
+ cp $(REP_DIR)/recipes/raw/drivers_interactive-virt_qemu/$@ $@
+
+en_us.chargen special.chargen:
+ cp $(REP_DIR)/src/server/event_filter/$@ $@
diff --git a/repos/os/recipes/raw/drivers_interactive-virt_qemu/drivers.config b/repos/os/recipes/raw/drivers_interactive-virt_qemu/drivers.config
new file mode 100644
index 0000000000..8464a17e5b
--- /dev/null
+++ b/repos/os/recipes/raw/drivers_interactive-virt_qemu/drivers.config
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/repos/os/recipes/raw/drivers_interactive-virt_qemu/event_filter.config b/repos/os/recipes/raw/drivers_interactive-virt_qemu/event_filter.config
new file mode 100644
index 0000000000..55cdbc6167
--- /dev/null
+++ b/repos/os/recipes/raw/drivers_interactive-virt_qemu/event_filter.config
@@ -0,0 +1,26 @@
+
+
+
+
diff --git a/repos/os/recipes/raw/drivers_interactive-virt_qemu/hash b/repos/os/recipes/raw/drivers_interactive-virt_qemu/hash
new file mode 100644
index 0000000000..3ce371e3be
--- /dev/null
+++ b/repos/os/recipes/raw/drivers_interactive-virt_qemu/hash
@@ -0,0 +1 @@
+2021-04-16-b 1ac1f670f6c96ac6098145f15924265616f99c6a
diff --git a/repos/os/recipes/src/virt_qemu_drivers/content.mk b/repos/os/recipes/src/virt_qemu_drivers/content.mk
new file mode 100644
index 0000000000..bce2ccada9
--- /dev/null
+++ b/repos/os/recipes/src/virt_qemu_drivers/content.mk
@@ -0,0 +1,7 @@
+include $(GENODE_DIR)/repos/base/recipes/src/content.inc
+
+content: src/drivers
+
+src/drivers:
+ mkdir -p $@/framebuffer
+ cp -r $(REP_DIR)/src/drivers/framebuffer/ram/* $@/framebuffer
diff --git a/repos/os/recipes/src/virt_qemu_drivers/hash b/repos/os/recipes/src/virt_qemu_drivers/hash
new file mode 100644
index 0000000000..d7c654a4ec
--- /dev/null
+++ b/repos/os/recipes/src/virt_qemu_drivers/hash
@@ -0,0 +1 @@
+2021-04-16-m ba0d520e8e92df0c87e0406d31115966a0301fb1
diff --git a/repos/os/recipes/src/virt_qemu_drivers/used_apis b/repos/os/recipes/src/virt_qemu_drivers/used_apis
new file mode 100644
index 0000000000..2d46ac2a1b
--- /dev/null
+++ b/repos/os/recipes/src/virt_qemu_drivers/used_apis
@@ -0,0 +1,7 @@
+base
+os
+capture_session
+event_session
+platform_session
+timer_session
+blit
diff --git a/repos/os/src/drivers/framebuffer/ram/README b/repos/os/src/drivers/framebuffer/ram/README
new file mode 100644
index 0000000000..ac088a5b67
--- /dev/null
+++ b/repos/os/src/drivers/framebuffer/ram/README
@@ -0,0 +1,2 @@
+Framebuffer driver for Qemu's 'ramfb' device. In order to enablethe RAM
+framebuffer add the '-device ramfb' option to Qemu's command line.
diff --git a/repos/os/src/drivers/framebuffer/ram/main.cc b/repos/os/src/drivers/framebuffer/ram/main.cc
new file mode 100644
index 0000000000..208d095435
--- /dev/null
+++ b/repos/os/src/drivers/framebuffer/ram/main.cc
@@ -0,0 +1,209 @@
+/*
+ * \brief RAM framebuffer driver for Qemu
+ * \author Sebastian Sumpf
+ * \date 2021-04-15
+ */
+
+/*
+ * Copyright (C) 2021 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
+
+using namespace Genode;
+
+class Main
+{
+ private:
+
+ enum {
+ SCR_WIDTH = 1024u,
+ SCR_HEIGHT = 768u,
+ SCR_STRIDE = SCR_WIDTH * 4,
+ };
+
+ /*****************************
+ ** Qemu firmware interface **
+ *****************************/
+
+ struct Fw : Mmio
+ {
+ template
+ struct Data : Register<0x0, sizeof(T) * 8> { };
+ struct Selector : Register<0x8, 16> { };
+ struct Dma : Register<0x10, 64> { };
+
+ Fw(addr_t const base)
+ :
+ Mmio(base) { }
+ };
+
+ Env &_env;
+
+ /************************
+ ** Genode integration **
+ ************************/
+
+ using Type = Platform::Device::Type;
+
+ Platform::Connection _platform { _env };
+ Platform::Device _fw_dev { _platform, Type { "qemu,fw-cfg-mmio" } };
+ Platform::Device::Mmio _fw_mem { _fw_dev };
+ Fw _fw { (addr_t)_fw_mem.local_addr() };
+
+ Ram_dataspace_capability _fb_ds_cap {
+ _platform.alloc_dma_buffer(SCR_HEIGHT * SCR_STRIDE, UNCACHED) };
+ Attached_dataspace _fb_ds { _env.rm(), _fb_ds_cap };
+
+ Ram_dataspace_capability _config_ds_cap {
+ _platform.alloc_dma_buffer(0x1000, UNCACHED) };
+ Attached_dataspace _config_ds { _env.rm(), _config_ds_cap };
+
+ Capture::Area const _size { SCR_WIDTH, SCR_HEIGHT };
+ Capture::Connection _capture { _env };
+ Capture::Connection::Screen _captured_screen { _capture, _env.rm(), _size };
+
+ Timer::Connection _timer { _env };
+
+ Signal_handler _timer_handler { _env.ep(), *this, &Main::_handle_timer };
+
+ void _handle_timer()
+ {
+ using Pixel = Capture::Pixel;
+
+ Surface surface(_fb_ds.local_addr(), _size);
+
+ _captured_screen.apply_to_surface(surface);
+ }
+
+ /* device selector */
+ void _fw_selector(uint16_t key) {
+ _fw.write(host_to_big_endian(key)); }
+
+ /* read data for selected key */
+ template T _fw_data() {
+ return host_to_big_endian(_fw.read>()); }
+
+ /* DMA control structure */
+ struct Fw_dma_config : Genode::Mmio
+ {
+ struct Control : Register<0x0, 32> { };
+ struct Length : Register<0x4, 32> { };
+ struct Address : Register<0x8, 64> { };
+
+ Fw_dma_config(addr_t const base)
+ :
+ Mmio(base)
+ {
+ /* set write bit */
+ write(host_to_big_endian(1u << 4));
+ }
+ };
+
+ /* file structure for directory traversal (key=0x19) */
+ struct Fw_config_file
+ {
+ uint32_t size;
+ uint16_t key;
+ uint16_t reserved;
+ char name[56];
+ };
+
+ /* ramfb configuration */
+ struct Ram_fb_config : Genode::Mmio
+ {
+ struct Address : Register<0x0, 64> { };
+ struct Drm_format : Register<0x8, 32> { };
+ struct Width : Register<0x10, 32> { };
+ struct Height : Register<0x14, 32> { };
+ struct Stride : Register<0x18, 32> { };
+
+ Ram_fb_config(addr_t const base)
+ :
+ Mmio(base)
+ {
+ enum {
+ DRM_FORMAT_ARGB8888 = 0x34325241,
+ };
+ /* RGBA32 */
+ write(host_to_big_endian(DRM_FORMAT_ARGB8888));
+ write(host_to_big_endian(SCR_STRIDE));
+ }
+ };
+
+ Fw_config_file _find_ramfb()
+ {
+ /* file directory */
+ _fw_selector(0x19);
+ uint32_t count = _fw_data();
+
+ Fw_config_file file { };
+ for (unsigned i = 0; i < count; i++) {
+
+ file.size = _fw_data();
+ file.key = _fw_data();
+ file.reserved = _fw_data();
+
+ for (unsigned j = 0; j < 56; j++)
+ file.name[j] = _fw_data();
+
+ if (Genode::strcmp(file.name, "etc/ramfb") == 0) {
+ log("RAM FB found with key ", file.key);
+ return file;
+ }
+ }
+
+ error("'etc/ramfb' not found, try the '-device ramfb' option with Qemu");
+ throw -1;
+ }
+
+ void _setup_framebuffer(Fw_config_file const &file)
+ {
+ enum { FW_OFFSET = 28 };
+
+ _fw_selector(file.key);
+
+ addr_t config_addr = (addr_t)_config_ds.local_addr();
+ addr_t config_phys = (addr_t)_platform.dma_addr(_config_ds_cap);
+ addr_t fb_phys = (addr_t)_platform.dma_addr(_fb_ds_cap);
+
+ Ram_fb_config config { config_addr };
+ config.write(host_to_big_endian(fb_phys));
+ config.write(host_to_big_endian(SCR_WIDTH));
+ config.write(host_to_big_endian(SCR_HEIGHT));
+
+ Fw_dma_config fw_dma { config_addr + FW_OFFSET};
+ fw_dma.write(host_to_big_endian(file.size));
+ fw_dma.write(host_to_big_endian(config_phys));
+
+ _fw.write(host_to_big_endian(config_phys + FW_OFFSET));
+ }
+
+ public:
+
+ Main(Env &env)
+ :
+ _env(env)
+ {
+ _setup_framebuffer(_find_ramfb());
+
+ _timer.sigh(_timer_handler);
+ _timer.trigger_periodic(20*1000);
+ }
+};
+
+void Component::construct(Genode::Env &env)
+{
+ log ("--- Qemu Ramfb driver --");
+ static Main main(env);
+};
diff --git a/repos/os/src/drivers/framebuffer/ram/target.mk b/repos/os/src/drivers/framebuffer/ram/target.mk
new file mode 100644
index 0000000000..6544c8b9e8
--- /dev/null
+++ b/repos/os/src/drivers/framebuffer/ram/target.mk
@@ -0,0 +1,5 @@
+TARGET = ram_fb_drv
+SRC_CC = main.cc
+LIBS = base blit
+
+REQUIRES = arm_v8