Loading...
Searching...
No Matches
virtio-console-device
// vi:ft=cpp
/* SPDX-License-Identifier: MIT */
/*
* Copyright (C) 2019-2024 Kernkonzept GmbH.
* Author(s): Sarah Hoffmann <sarah.hoffmann@kernkonzept.com>
* Phillip Raffeck <phillip.raffeck@kernkonzept.com>
* Steffen Liebergeld <steffen.liebergeld@kernkonzept.com>
* Jan Klötzke <jan.kloetzke@kernkonzept.com>
*/
#pragma once
#include <l4/cxx/bitmap>
#include <l4/cxx/static_vector>
#include <l4/l4virtio/server/l4virtio>
#include <l4/l4virtio/server/virtio-console>
#include <l4/re/error_helper>
namespace L4virtio { namespace Svr { namespace Console {
struct Device_port : public Port
{
struct Buffer : Data_buffer
{
Buffer() = default;
Buffer(Driver_mem_region const *r,
Virtqueue::Desc const &d,
Request_processor const *)
{
pos = static_cast<char *>(r->local(d.addr));
left = d.len;
}
};
Request_processor rp;
Virtqueue::Request request;
Buffer src;
bool poll_in_req = true;
bool poll_out_req = true;
void reset() override
{
Port::reset();
request = Virtqueue::Request();
poll_in_req = true;
poll_out_req = true;
}
};
class Device
: public Virtio_con
{
class Irq_object : public L4::Irqep_t<Irq_object>
{
public:
Irq_object(Device *parent) : _parent(parent) {}
void handle_irq() { _parent->kick(); }
private:
Device *_parent;
};
protected:
L4::Epiface *irq_iface()
{ return &_irq_handler; }
public:
explicit Device(unsigned vq_max)
: Virtio_con(1, false),
_irq_handler(this),
_ports(cxx::make_unique<Device_port[]>(1))
{
_ports[0].vq_max = vq_max;
reset_queue_configs();
}
explicit Device(unsigned vq_max, unsigned ports)
: Virtio_con(ports, true),
_irq_handler(this),
_ports(cxx::make_unique<Device_port[]>(ports))
{
for (unsigned i = 0; i < ports; ++i)
_ports[i].vq_max = vq_max;
reset_queue_configs();
}
explicit Device(cxx::static_vector<unsigned> const &vq_max_nums)
: Virtio_con(vq_max_nums.size(), true),
_irq_handler(this),
_ports(cxx::make_unique<Device_port[]>(max_ports()))
{
for (unsigned i = 0; i < vq_max_nums.size(); ++i)
_ports[i].vq_max = vq_max_nums[i];
reset_queue_configs();
}
void register_single_driver_irq() override
{
_kick_driver_irq = L4Re::Util::Unique_cap<L4::Irq>(
L4Re::chkcap(server_iface()->rcv_cap<L4::Irq>(0)));
L4Re::chksys(server_iface()->realloc_rcv_cap(0));
}
L4::Cap<L4::Irq> device_notify_irq() const override
{ return _irq_handler.obj_cap(); }
void notify_queue(Virtqueue *queue) override
{
if (queue->no_notify_guest())
return;
_dev_config.add_irq_status(L4VIRTIO_IRQ_STATUS_VRING);
_kick_driver_irq->trigger();
}
virtual void rx_data_available(unsigned port) = 0;
virtual void tx_space_available(unsigned port) = 0;
virtual bool queues_stopped()
{ return false; }
void trigger_driver_config_irq() override
{
_dev_config.add_irq_status(L4VIRTIO_IRQ_STATUS_CONFIG);
_kick_driver_irq->trigger();
}
void kick()
{
if (queues_stopped())
return;
// We're not interested in logging any errors, just ignore return value.
handle_control_message();
for (unsigned i = 0; i < max_ports(); ++i)
{
auto &p = _ports[i];
if (p.poll_in_req && p.tx_ready() && p.tx.desc_avail())
{
p.poll_in_req = false;
rx_data_available(i);
}
if (p.poll_out_req && p.rx_ready() && p.rx.desc_avail())
{
p.poll_out_req = false;
tx_space_available(i);
}
}
}
unsigned port_read(char *buf, unsigned len, unsigned port = 0)
{
unsigned total = 0;
Device_port &p = _ports[port];
Virtqueue *q = &p.tx;
Data_buffer dst;
dst.pos = buf;
dst.left = len;
while (dst.left)
{
try
{
// Make sure we have a valid request where we can read data from
if (!p.request.valid())
{
p.request = p.tx_ready() ? q->next_avail()
: Virtqueue::Request();
if (!p.request.valid())
break;
p.rp.start(mem_info(), p.request, &p.src);
}
total += p.src.copy_to(&dst);
// We might have eaten up the current descriptor. Move to the next
// if this is the case. At the end of the descriptor chain we have
// to retire the current request altogether.
if (!p.src.left)
{
if (!p.rp.next(mem_info(), &p.src))
{
q->finish(p.request, this);
p.request = Virtqueue::Request();
}
}
}
catch (Bad_descriptor const &)
{
q->finish(p.request, this);
p.request = Virtqueue::Request();
device_error();
break;
}
}
if (total < len)
p.poll_in_req = true;
return total;
}
unsigned port_write(char const *buf, unsigned len, unsigned port = 0)
{
unsigned total = 0;
Device_port &p = _ports[port];
Virtqueue *q = &p.rx;
Data_buffer src;
src.pos = const_cast<char*>(buf);
src.left = len;
Request_processor rp;
while (src.left)
{
auto r = p.rx_ready() ? q->next_avail() : Virtqueue::Request();
if (!r.valid())
break;
l4_uint32_t chunk = 0;
try
{
Device_port::Buffer dst;
rp.start(mem_info(), r, &dst);
for (;;)
{
chunk += src.copy_to(&dst);
if (!src.left)
break;
if (!rp.next(mem_info(), &dst))
break;
}
}
catch (Bad_descriptor const &)
{
device_error();
}
q->finish(r, this, chunk);
total += chunk;
}
if (total < len)
p.poll_out_req = true;
return total;
}
void process_device_ready(l4_uint16_t value) override
{
if (!value)
return;
for (unsigned i = 0; i < max_ports(); ++i)
port_add(i);
}
void process_port_ready(l4_uint32_t id, l4_uint16_t value) override
{
Virtio_con::process_port_ready(id, value);
Port *p = port(id);
if (p->status == Port::Port_failed)
port_remove(id);
else if (p->status == Port::Port_ready)
port_open(id, true);
}
virtual void process_port_open(l4_uint32_t id, l4_uint16_t value)
{
static_cast<void>(id);
static_cast<void>(value);
}
protected:
Port* port(unsigned idx) override
{
return &_ports[idx];
}
Port const *port(unsigned idx) const override
{
return &_ports[idx];
}
private:
Irq_object _irq_handler;
cxx::unique_ptr<Device_port[]> _ports;
L4Re::Util::Unique_cap<L4::Irq> _kick_driver_irq;
};
}}} // name space
L4::Cap
C++ interface for capabilities.
Definition capability.h:224
L4virtio::Svr::Console::Device::notify_queue
void notify_queue(Virtqueue *queue) override
Notify queue of available data.
Definition virtio-console-device:202
L4virtio::Svr::Console::Device::trigger_driver_config_irq
void trigger_driver_config_irq() override
callback for triggering configuration change notification IRQ
Definition virtio-console-device:227
L4virtio::Svr::Console::Device::Device
Device(unsigned vq_max, unsigned ports)
Create a new console device.
Definition virtio-console-device:163
L4virtio::Svr::Console::Device::tx_space_available
virtual void tx_space_available(unsigned port)=0
Callback to notify that data can be written to port.
L4virtio::Svr::Console::Device::port
Port * port(unsigned idx) override
Return the specified port.
Definition virtio-console-device:450
L4virtio::Svr::Console::Device::port_write
unsigned port_write(char const *buf, unsigned len, unsigned port=0)
Write data to port.
Definition virtio-console-device:341
L4virtio::Svr::Console::Device::device_notify_irq
L4::Cap< L4::Irq > device_notify_irq() const override
callback to gather the device notification IRQ (old-style)
Definition virtio-console-device:199
L4virtio::Svr::Console::Device::process_device_ready
void process_device_ready(l4_uint16_t value) override
Callback called on DEVICE_READY event.
Definition virtio-console-device:399
L4virtio::Svr::Console::Device::process_port_ready
void process_port_ready(l4_uint32_t id, l4_uint16_t value) override
Callback called on PORT_READY event.
Definition virtio-console-device:422
L4virtio::Svr::Console::Device::queues_stopped
virtual bool queues_stopped()
Return true, if the queues should not be processed further.
Definition virtio-console-device:224
L4virtio::Svr::Console::Device::Device
Device(unsigned vq_max)
Create a new console device.
Definition virtio-console-device:145
L4virtio::Svr::Console::Device::process_port_open
virtual void process_port_open(l4_uint32_t id, l4_uint16_t value)
Callback called on PORT_OPEN event.
Definition virtio-console-device:443
L4virtio::Svr::Console::Device::rx_data_available
virtual void rx_data_available(unsigned port)=0
Callback to notify that new data is available to be read from port.
L4virtio::Svr::Console::Device::Device
Device(cxx::static_vector< unsigned > const &vq_max_nums)
Create a new console Device.
Definition virtio-console-device:182
L4virtio::Svr::Console::Device::port_read
unsigned port_read(char *buf, unsigned len, unsigned port=0)
Read data from port.
Definition virtio-console-device:272
L4virtio::Svr::Console::Device::register_single_driver_irq
void register_single_driver_irq() override
callback for registering a single guest IRQ for all queues (old-style)
Definition virtio-console-device:192
L4virtio::Svr::Console::Virtio_con::port_open
int port_open(unsigned idx, bool open)
Send a PORT_OPEN message and update the internal state.
Definition virtio-console:428
L4virtio::Svr::Console::Virtio_con::process_port_ready
virtual void process_port_ready(l4_uint32_t id, l4_uint16_t value)
Callback called on PORT_READY event.
Definition virtio-console:698
L4virtio::Svr::Console::Virtio_con::port_remove
int port_remove(unsigned idx)
Send a DEVICE_REMOVE message and update the internal state.
Definition virtio-console:403
L4virtio::Svr::Console::Virtio_con::port_add
int port_add(unsigned idx)
Send a DEVICE_ADD message and update the internal state.
Definition virtio-console:379
L4virtio::Svr::Console::Virtio_con::Virtio_con
Virtio_con(unsigned max_ports, bool enable_multiport)
Create a new multiport console device.
Definition virtio-console:293
L4virtio::Svr::Console::Virtio_con::handle_control_message
int handle_control_message()
Handle control message received from the driver.
Definition virtio-console:536
L4virtio::Svr::Device_t< No_custom_data >::device_error
void device_error()
Definition l4virtio:1024
L4virtio::Svr::Device_t< No_custom_data >::mem_info
Mem_list const * mem_info() const
Definition l4virtio:898
L4virtio::Svr::Driver_mem_region_t::local
T * local(Ptr< T > p) const
Get the local address for driver address p.
Definition l4virtio:616
L4virtio::Svr::Request_processor
Encapsulate the state for processing a VIRTIO request.
Definition virtio:473
L4virtio::Svr::Request_processor::next
bool next(DESC_MAN *dm, ARGS... args)
Switch to the next descriptor in a descriptor chain.
Definition virtio:570
L4virtio::Svr::Request_processor::start
void start(DESC_MAN *dm, Virtqueue *ring, Virtqueue::Head_desc const &request, ARGS... args)
Start processing a new request.
Definition virtio:501
L4virtio::Svr::Virtqueue::Head_desc::valid
bool valid() const
Definition virtio:105
L4virtio::Svr::Virtqueue
Virtqueue implementation for the device.
Definition virtio:88
L4virtio::Svr::Virtqueue::next_avail
Request next_avail()
Get the next available descriptor from the available ring.
Definition virtio:136
L4virtio::Svr::Virtqueue::finish
void finish(Head_desc &d, QUEUE_OBSERVER *o, l4_uint32_t len=0)
Add a descriptor to the used ring, and notify an observer.
Definition virtio:240
L4virtio::Virtqueue::Desc
Descriptor in the descriptor table.
Definition virtqueue:87
L4virtio::Virtqueue::Desc::len
l4_uint32_t len
Length of described buffer.
Definition virtqueue:109
L4virtio::Virtqueue::Desc::addr
Ptr< void > addr
Address stored in descriptor.
Definition virtqueue:108
L4virtio::Virtqueue::no_notify_guest
bool no_notify_guest() const
Get the no IRQ flag of this queue.
Definition virtqueue:414
cxx::static_vector
Simple encapsulation for a dynamically allocated array.
Definition static_vector:17
error_helper
Error helper.
l4_uint32_t
unsigned int l4_uint32_t
Unsigned 32bit value.
Definition l4int.h:29
l4_uint16_t
unsigned short int l4_uint16_t
Unsigned 16bit value.
Definition l4int.h:27
L4VIRTIO_IRQ_STATUS_VRING
@ L4VIRTIO_IRQ_STATUS_VRING
VRING IRQ pending flag.
Definition virtio.h:112
L4VIRTIO_IRQ_STATUS_CONFIG
@ L4VIRTIO_IRQ_STATUS_CONFIG
CONFIG IRQ pending flag.
Definition virtio.h:113
L4Re::Util::Unique_cap
L4::Detail::Unique_cap_impl< T, Smart_cap_auto< L4_FP_ALL_SPACES > > Unique_cap
Unique capability that implements automatic free and unmap of the capability selector.
Definition unique_cap:43
L4Re::chksys
long chksys(long err, char const *extra="", long ret=0)
Generate C++ exception on error.
Definition error_helper:72
L4Re::chkcap
T chkcap(T &&cap, char const *extra="", long err=-L4_ENOMEM)
Check for valid capability or raise C++ exception.
Definition error_helper:149
L4virtio
L4-VIRTIO Transport C++ API.
Definition l4virtio:26
cxx
Our C++ library.
Definition arith:11
L4::Epiface
Base class for interface implementations.
Definition ipc_epiface:146
L4::Irqep_t
Epiface implementation for interrupt handlers.
Definition ipc_epiface:283
L4virtio::Svr::Bad_descriptor
Exception used by Queue to indicate descriptor errors.
Definition virtio:398
L4virtio::Svr::Console::Device_port
A console port with associated read/write state.
Definition virtio-console-device:27
L4virtio::Svr::Console::Device_port::reset
void reset() override
Reset the port to the initial state and disable its virtqueues.
Definition virtio-console-device:47
L4virtio::Svr::Console::Device_port::rp
Request_processor rp
Request processor associated with current request.
Definition virtio-console-device:40
L4virtio::Svr::Console::Device_port::request
Virtqueue::Request request
Current virtio tx queue request.
Definition virtio-console-device:41
L4virtio::Svr::Console::Device_port::src
Buffer src
Source data block to process.
Definition virtio-console-device:42
L4virtio::Svr::Console::Port
Representation of a Virtio console port.
Definition virtio-console:110
L4virtio::Svr::Console::Port::status
Port_status status
State the port is in.
Definition virtio-console:135
L4virtio::Svr::Console::Port::rx
Virtqueue rx
Transmitq of the port.
Definition virtio-console:134
L4virtio::Svr::Console::Port::reset
virtual void reset()
Reset the port to the initial state and disable its virtqueues.
Definition virtio-console:150
L4virtio::Svr::Console::Port::tx
Virtqueue tx
Receiveq of the port.
Definition virtio-console:133
L4virtio::Svr::Console::Port::rx_ready
bool rx_ready() const
Check that device implementation may write to receive queues.
Definition virtio-console:161
L4virtio::Svr::Console::Port::tx_ready
bool tx_ready() const
Check that device implementation may read from transmit queues.
Definition virtio-console:165
L4virtio::Svr::Console::Port::Port_ready
@ Port_ready
Port is ready but still closed.
Definition virtio-console:121
L4virtio::Svr::Console::Port::Port_failed
@ Port_failed
Device failure, port unusable.
Definition virtio-console:125
L4virtio::Svr::Data_buffer
Abstract data buffer.
Definition virtio:307
L4virtio::Svr::Data_buffer::copy_to
l4_uint32_t copy_to(Data_buffer *dst, l4_uint32_t max=UINT_MAX)
Copy contents from this buffer to the destination buffer.
Definition virtio:354
L4virtio::Svr::Data_buffer::left
l4_uint32_t left
Bytes left in buffer.
Definition virtio:309
L4virtio::Svr::Data_buffer::pos
char * pos
Current buffer position.
Definition virtio:308