vmm: implement VirtIO input model

Fix genodelabs/genode#4698
This commit is contained in:
Stefan Kalkowski 2022-12-09 12:00:41 +01:00 committed by Christian Helmuth
parent 85c8bd7d7e
commit 80687e702c
6 changed files with 332 additions and 9 deletions

View File

@ -71,10 +71,9 @@ For each virtio_device node the following attributes need to be set:
:type: :type:
The Virtio type of device. One can decide in between "console", "net", "gpu", The Virtio type of device. One can decide in between "console", "net", "gpu",
and "block". The "console" type gets mapped to a Genode Terminal session, "input", and "block". The "console" type gets mapped to a Genode Terminal
"net" is mapped to a Nic session, "gpu" is mapped to a Gui session, session, "net" is mapped to a Nic session, "gpu" is mapped to a Gui session,
and "block" to a Block session. "input" to the event part of the Gui session, and "block" to a Block session.
Additional devices Additional devices
------------------ ------------------

View File

@ -38,7 +38,7 @@ class Vmm::Config
struct Virtio_device : List_model<Virtio_device>::Element struct Virtio_device : List_model<Virtio_device>::Element
{ {
enum Type { INVALID, CONSOLE, NET, BLOCK, GPU }; enum Type { INVALID, CONSOLE, NET, BLOCK, GPU, INPUT };
enum { MMIO_SIZE = 0x200 }; enum { MMIO_SIZE = 0x200 };
@ -95,6 +95,7 @@ class Vmm::Config
if (type == "net") t = Virtio_device::NET; if (type == "net") t = Virtio_device::NET;
if (type == "block") t = Virtio_device::BLOCK; if (type == "block") t = Virtio_device::BLOCK;
if (type == "gpu") t = Virtio_device::GPU; if (type == "gpu") t = Virtio_device::GPU;
if (type == "input") t = Virtio_device::INPUT;
return t; return t;
} }

View File

@ -305,7 +305,7 @@ class Vmm::Virtio_gpu_device : public Virtio_device<Virtio_gpu_queue, 2>
Env & _env; Env & _env;
Heap & _heap; Heap & _heap;
Attached_ram_dataspace & _ram_ds; Attached_ram_dataspace & _ram_ds;
Gui::Connection _gui { _env }; Gui::Connection & _gui;
Cpu::Signal_handler<Virtio_gpu_device> _handler; Cpu::Signal_handler<Virtio_gpu_device> _handler;
Constructible<Attached_dataspace> _fb_ds { }; Constructible<Attached_dataspace> _fb_ds { };
Framebuffer::Mode _fb_mode { _gui.mode() }; Framebuffer::Mode _fb_mode { _gui.mode() };
@ -452,11 +452,12 @@ class Vmm::Virtio_gpu_device : public Virtio_device<Virtio_gpu_queue, 2>
Ram & ram, Ram & ram,
Env & env, Env & env,
Heap & heap, Heap & heap,
Attached_ram_dataspace & ram_ds) Attached_ram_dataspace & ram_ds,
Gui::Connection & gui)
: :
Virtio_device<Virtio_gpu_queue, 2>(name, addr, size, Virtio_device<Virtio_gpu_queue, 2>(name, addr, size,
irq, cpu, bus, ram, GPU), irq, cpu, bus, ram, GPU),
_env(env), _heap(heap), _ram_ds(ram_ds), _env(env), _heap(heap), _ram_ds(ram_ds), _gui(gui),
_handler(cpu, env.ep(), *this, &Virtio_gpu_device::_mode_change) _handler(cpu, env.ep(), *this, &Virtio_gpu_device::_mode_change)
{ {
_gui.mode_sigh(_handler); _gui.mode_sigh(_handler);

View File

@ -0,0 +1,310 @@
/*
* \brief Virtio Input implementation
* \author Stefan Kalkowski
* \date 2022-12-06
*/
/*
* Copyright (C) 2022 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.
*/
#ifndef _VIRTIO_INPUT_H_
#define _VIRTIO_INPUT_H_
#include <input/event.h>
#include <input_session/client.h>
#include <virtio_device.h>
namespace Vmm {
class Virtio_input_device;
using namespace Genode;
}
namespace Linux_evdev
{
enum Type {
EV_SYNC = 0x0,
EV_KEY = 0x1,
EV_REL = 0x2,
EV_ABS = 0x3,
EV_REP = 0x14
};
enum Relative {
REL_WHEEL = 8,
EV_REL_FEATURES = 1U << REL_WHEEL
};
enum Absolute {
ABS_X = 0,
ABS_Y = 1,
EV_ABS_FEATURES = (1U << ABS_X) | (1U << ABS_Y)
};
};
class Vmm::Virtio_input_device : public Virtio_device<Virtio_split_queue, 2>
{
private:
Env & _env;
Heap & _heap;
Input::Session_client & _input;
Attached_dataspace _input_ds { _env.rm(), _input.dataspace() };
Input::Event const * const _events {
_input_ds.local_addr<Input::Event>() };
enum State { READY, IN_MOTION, SYNC };
State _state { READY };
unsigned _num_events { 0U };
unsigned _idx_events { 0U };
int _motion_y { -1 };
Cpu::Signal_handler<Virtio_input_device> _handler;
struct Virtio_input_event : Mmio
{
enum { SIZE = 8 };
struct Type : Register<0, 16> {};
struct Code : Register<2, 16> {};
struct Value : Register<4, 32> {};
using Mmio::Mmio;
};
struct Configuration_area : Mmio_register
{
Virtio_input_device & dev;
struct Abs_info
{
enum { SIZE = 20 };
uint32_t min;
uint32_t max;
uint32_t fuzz;
uint32_t flat;
};
enum Offsets
{
SELECT = 0,
SUB_SELECT = 1,
SIZE = 2,
DATA = 8,
DATA_MAX = DATA + 128,
};
enum Select {
UNSET = 0x00,
ID_NAME = 0x01,
ID_SERIAL = 0x02,
ID_DEVIDS = 0x03,
PROP_BITS = 0x10,
EV_BITS = 0x11,
ABS_INFO = 0x12,
};
uint8_t _select { 0 };
uint8_t _sub_select { 0 };
String<16> _name { "vinput0" };
String<16> _serial { "serial0" };
String<16> _dev_id { "0" };
uint8_t _size()
{
using namespace Linux_evdev;
switch (_select) {
case ID_NAME: return _name.length() - 1;
case ID_SERIAL: return _serial.length() - 1;
case ID_DEVIDS: return _dev_id.length() - 1;
case PROP_BITS: return 0; /* Unsupported */
case EV_BITS:
switch (_sub_select) {
case EV_KEY: return 36;
case EV_REL: return 2;
case EV_ABS: return 1;
case EV_REP: return 1;
default: return 0; /* Unsupported */
};
case ABS_INFO: return 20;
default: break;
};
error("Unknown size for ", _select, " ", _sub_select);
return 0;
}
Register _data(addr_t off)
{
using namespace Linux_evdev;
switch (_select) {
case ID_NAME:
return (off < (_name.length()-1)) ? _name.string()[off] : 0;
case ID_SERIAL:
return (off < (_serial.length()-1)) ? _serial.string()[off]
: 0;
case ID_DEVIDS:
return (off < (_dev_id.length()-1)) ? _dev_id.string()[off]
: 0;
case EV_BITS:
switch (_sub_select) {
case EV_ABS: return EV_ABS_FEATURES;
case EV_REL: return EV_REL_FEATURES;
case EV_KEY: return 0xffffffff;
default: return 0;
};
case ABS_INFO:
switch (_sub_select) {
case ABS_X: return (off == 4) ? 1920 : 0;
case ABS_Y: return (off == 4) ? 1050 : 0;
default: return 0;
};
default: break;
};
error("Invalid data offset for selectors ",
_select, " ", _sub_select);
return 0;
}
Register read(Address_range & range, Cpu&) override
{
if (range.start == SIZE)
return _size();
if (range.start >= DATA && range.start < DATA_MAX)
return _data(range.start-DATA);
error("Reading from virtio input config space ",
"at offset ", range.start, " is not allowed");
return 0;
}
void write(Address_range & range, Cpu&, Register v) override
{
switch (range.start) {
case SELECT: _select = v; return;
case SUB_SELECT: _sub_select = v; return;
default:
error("Writing to virtio input config space ",
"at offset ", range.start, " is not allowed");
}
}
Configuration_area(Virtio_input_device & device)
: Mmio_register("Input config area",
Mmio_register::RO, 0x100, 0xa4),
dev(device) { device.add(*this); }
} _config_area{ *this };
void _handle_input()
{
if (!_queue[0].constructed())
return;
bool irq = _queue[0]->notify([&] (addr_t addr, size_t size) {
if (size < Virtio_input_event::SIZE) {
warning("wrong virtioqueue packet size for input ", size);
return 0UL;
}
Virtio_input_event vie(addr);
if (_state == IN_MOTION) {
vie.write<Virtio_input_event::Type>(Linux_evdev::EV_ABS);
vie.write<Virtio_input_event::Code>(Linux_evdev::ABS_Y);
vie.write<Virtio_input_event::Value>(_motion_y);
_state = SYNC;
return size;
}
if (_state == SYNC) {
vie.write<Virtio_input_event::Type>(Linux_evdev::EV_SYNC);
vie.write<Virtio_input_event::Code>(0);
vie.write<Virtio_input_event::Value>(0);
_state = READY;
return size;
}
if (_num_events == _idx_events) {
_num_events = _input.flush();
_idx_events = 0;
}
while (_idx_events < _num_events &&
!_events[_idx_events].valid())
_idx_events++;
if (_num_events == _idx_events)
return 0UL;
Input::Event const event = _events[_idx_events++];
auto press = [&] (Input::Keycode key, bool press) {
vie.write<Virtio_input_event::Type>(Linux_evdev::EV_KEY);
vie.write<Virtio_input_event::Code>(key);
vie.write<Virtio_input_event::Value>(press);
_state = SYNC;
};
event.handle_press([&] (Input::Keycode key, Genode::Codepoint) {
press(key, true); });
event.handle_release([&] (Input::Keycode key) {
press(key, false); });
event.handle_absolute_motion([&] (int x, int y)
{
vie.write<Virtio_input_event::Type>(Linux_evdev::EV_ABS);
vie.write<Virtio_input_event::Code>(Linux_evdev::ABS_X);
vie.write<Virtio_input_event::Value>(x);
_motion_y = y;
_state = IN_MOTION;
});
return size;
});
if (irq) _assert_irq();
}
void _notify(unsigned idx) override
{
if (idx) {
error("VirtIO input queue for status event not implemented");
return;
}
_handle_input();
}
enum Device_id { INPUT = 18 };
public:
Virtio_input_device(const char * const name,
const uint64_t addr,
const uint64_t size,
unsigned irq,
Cpu & cpu,
Mmio_bus & bus,
Ram & ram,
Env & env,
Heap & heap,
Input::Session_client & input)
:
Virtio_device<Virtio_split_queue, 2>(name, addr, size,
irq, cpu, bus, ram, INPUT),
_env(env), _heap(heap), _input(input),
_handler(cpu, env.ep(), *this, &Virtio_input_device::_handle_input)
{
_input.sigh(_handler);
}
};
#endif /* _VIRTIO_INPUT_H_ */

View File

@ -17,6 +17,7 @@
#include <virtio_net.h> #include <virtio_net.h>
#include <virtio_block.h> #include <virtio_block.h>
#include <virtio_gpu.h> #include <virtio_gpu.h>
#include <virtio_input.h>
using Vmm::Vm; using Vmm::Vm;
@ -124,10 +125,18 @@ Vm::Vm(Genode::Env & env, Heap & heap, Config & config)
_bus, _ram, env, heap)); _bus, _ram, env, heap));
return; return;
case Config::Virtio_device::GPU: case Config::Virtio_device::GPU:
if (!_gui.constructed()) _gui.construct(env);
_device_list.insert(new (_heap) _device_list.insert(new (_heap)
Virtio_gpu_device(dev.name.string(), (uint64_t)dev.mmio_start, Virtio_gpu_device(dev.name.string(), (uint64_t)dev.mmio_start,
dev.mmio_size, dev.irq, boot_cpu(), dev.mmio_size, dev.irq, boot_cpu(),
_bus, _ram, env, heap, _vm_ram)); _bus, _ram, env, heap, _vm_ram, *_gui));
return;
case Config::Virtio_device::INPUT:
if (!_gui.constructed()) _gui.construct(env);
_device_list.insert(new (_heap)
Virtio_input_device(dev.name.string(), (uint64_t)dev.mmio_start,
dev.mmio_size, dev.irq, boot_cpu(),
_bus, _ram, env, heap, *_gui->input()));
default: default:
return; return;
}; };

View File

@ -25,6 +25,7 @@
#include <base/attached_ram_dataspace.h> #include <base/attached_ram_dataspace.h>
#include <base/attached_rom_dataspace.h> #include <base/attached_rom_dataspace.h>
#include <gui_session/connection.h>
#include <vm_session/connection.h> #include <vm_session/connection.h>
namespace Vmm { namespace Vmm {
@ -64,6 +65,8 @@ class Vmm::Vm
List<Virtio_device_base> _device_list; List<Virtio_device_base> _device_list;
Pl011 _uart; Pl011 _uart;
Constructible<Gui::Connection> _gui {};
addr_t _initrd_offset() const; addr_t _initrd_offset() const;
addr_t _dtb_offset() const; addr_t _dtb_offset() const;