L4Re Operating System Framework
Interface and Usage Documentation
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
virtio-console-device
1// vi:ft=cpp
2/* SPDX-License-Identifier: MIT */
3/*
4 * Copyright (C) 2019-2024 Kernkonzept GmbH.
5 * Author(s): Sarah Hoffmann <sarah.hoffmann@kernkonzept.com>
6 * Phillip Raffeck <phillip.raffeck@kernkonzept.com>
7 * Steffen Liebergeld <steffen.liebergeld@kernkonzept.com>
8 * Jan Klötzke <jan.kloetzke@kernkonzept.com>
9 */
10#pragma once
11
12#include <l4/cxx/bitmap>
13#include <l4/cxx/static_vector>
14#include <l4/l4virtio/server/l4virtio>
15#include <l4/l4virtio/server/virtio-console>
16#include <l4/re/error_helper>
17
18namespace L4virtio { namespace Svr { namespace Console {
19
26struct Device_port : public Port
27{
28 struct Buffer : Data_buffer
29 {
30 Buffer() = default;
31 Buffer(Driver_mem_region const *r,
32 Virtqueue::Desc const &d,
33 Request_processor const *)
34 {
35 pos = static_cast<char *>(r->local(d.addr));
36 left = d.len;
37 }
38 };
39
41 Virtqueue::Request request;
42 Buffer src;
43
44 bool poll_in_req = true;
45 bool poll_out_req = true;
46
47 void reset() override
48 {
50 request = Virtqueue::Request();
51 poll_in_req = true;
52 poll_out_req = true;
53 }
54};
55
119: public Virtio_con
120{
121 class Irq_object : public L4::Irqep_t<Irq_object>
122 {
123 public:
124 Irq_object(Device *parent) : _parent(parent) {}
125
126 void handle_irq() { _parent->kick(); }
127
128 private:
129 Device *_parent;
130 };
131
132protected:
133 L4::Epiface *irq_iface()
134 { return &_irq_handler; }
135
136public:
145 explicit Device(unsigned vq_max)
146 : Virtio_con(1, false),
147 _irq_handler(this),
148 _ports(cxx::make_unique<Device_port[]>(1))
149 {
150 _ports[0].vq_max = vq_max;
151 reset_queue_configs();
152 }
153
163 explicit Device(unsigned vq_max, unsigned ports)
164 : Virtio_con(ports, true),
165 _irq_handler(this),
166 _ports(cxx::make_unique<Device_port[]>(ports))
167 {
168 for (unsigned i = 0; i < ports; ++i)
169 _ports[i].vq_max = vq_max;
170 reset_queue_configs();
171 }
172
182 explicit Device(cxx::static_vector<unsigned> const &vq_max_nums)
183 : Virtio_con(vq_max_nums.size(), true),
184 _irq_handler(this),
185 _ports(cxx::make_unique<Device_port[]>(max_ports()))
186 {
187 for (unsigned i = 0; i < vq_max_nums.size(); ++i)
188 _ports[i].vq_max = vq_max_nums[i];
189 reset_queue_configs();
190 }
191
193 {
194 _kick_driver_irq = L4Re::Util::Unique_cap<L4::Irq>(
195 L4Re::chkcap(server_iface()->rcv_cap<L4::Irq>(0)));
196 L4Re::chksys(server_iface()->realloc_rcv_cap(0));
197 }
198
200 { return _irq_handler.obj_cap(); }
201
202 void notify_queue(Virtqueue *queue) override
203 {
204 if (queue->no_notify_guest())
205 return;
206
207 _dev_config.add_irq_status(L4VIRTIO_IRQ_STATUS_VRING);
208 _kick_driver_irq->trigger();
209 }
210
214 virtual void rx_data_available(unsigned port) = 0;
215
219 virtual void tx_space_available(unsigned port) = 0;
220
224 virtual bool queues_stopped()
225 { return false; }
226
228 {
229 _dev_config.add_irq_status(L4VIRTIO_IRQ_STATUS_CONFIG);
230 _kick_driver_irq->trigger();
231 }
232
233 void kick()
234 {
235 if (queues_stopped())
236 return;
237
238 // We're not interested in logging any errors, just ignore return value.
240
241 for (unsigned i = 0; i < max_ports(); ++i)
242 {
243 auto &p = _ports[i];
244 if (p.poll_in_req && p.tx_ready() && p.tx.desc_avail())
245 {
246 p.poll_in_req = false;
248 }
249
250 if (p.poll_out_req && p.rx_ready() && p.rx.desc_avail())
251 {
252 p.poll_out_req = false;
254 }
255 }
256 }
257
272 unsigned port_read(char *buf, unsigned len, unsigned port = 0)
273 {
274 unsigned total = 0;
275 Device_port &p = _ports[port];
276 Virtqueue *q = &p.tx;
277
278 Data_buffer dst;
279 dst.pos = buf;
280 dst.left = len;
281
282 while (dst.left)
283 {
284 try
285 {
286 // Make sure we have a valid request where we can read data from
287 if (!p.request.valid())
288 {
289 p.request = p.tx_ready() ? q->next_avail()
290 : Virtqueue::Request();
291 if (!p.request.valid())
292 break;
293
294 p.rp.start(mem_info(), p.request, &p.src);
295 }
296
297 total += p.src.copy_to(&dst);
298
299 // We might have eaten up the current descriptor. Move to the next
300 // if this is the case. At the end of the descriptor chain we have
301 // to retire the current request altogether.
302 if (!p.src.left)
303 {
304 if (!p.rp.next(mem_info(), &p.src))
305 {
306 q->finish(p.request, this);
307 p.request = Virtqueue::Request();
308 }
309 }
310 }
311 catch (Bad_descriptor const &)
312 {
313 q->finish(p.request, this);
314 p.request = Virtqueue::Request();
315 device_error();
316 break;
317 }
318 }
319
320 if (total < len)
321 p.poll_in_req = true;
322
323 return total;
324 }
325
341 unsigned port_write(char const *buf, unsigned len, unsigned port = 0)
342 {
343 unsigned total = 0;
344 Device_port &p = _ports[port];
345 Virtqueue *q = &p.rx;
346
347 Data_buffer src;
348 src.pos = const_cast<char*>(buf);
349 src.left = len;
350
352 while (src.left)
353 {
354 auto r = p.rx_ready() ? q->next_avail() : Virtqueue::Request();
355 if (!r.valid())
356 break;
357
358 l4_uint32_t chunk = 0;
359 try
360 {
361 Device_port::Buffer dst;
362 rp.start(mem_info(), r, &dst);
363
364 for (;;)
365 {
366 chunk += src.copy_to(&dst);
367 if (!src.left)
368 break;
369 if (!rp.next(mem_info(), &dst))
370 break;
371 }
372 }
373 catch (Bad_descriptor const &)
374 {
375 device_error();
376 }
377
378 q->finish(r, this, chunk);
379 total += chunk;
380 }
381
382 if (total < len)
383 p.poll_out_req = true;
384
385 return total;
386 }
387
400 {
401 if (!value)
402 return;
403
404 for (unsigned i = 0; i < max_ports(); ++i)
405 port_add(i);
406 }
407
423 {
425
426 Port *p = port(id);
427 if (p->status == Port::Port_failed)
428 port_remove(id);
429 else if (p->status == Port::Port_ready)
430 port_open(id, true);
431 }
432
444 {
445 static_cast<void>(id);
446 static_cast<void>(value);
447 }
448
449protected:
450 Port* port(unsigned idx) override
451 {
452 return &_ports[idx];
453 }
454
455 Port const *port(unsigned idx) const override
456 {
457 return &_ports[idx];
458 }
459
460private:
461 Irq_object _irq_handler;
462 cxx::unique_ptr<Device_port[]> _ports;
463 L4Re::Util::Unique_cap<L4::Irq> _kick_driver_irq;
464};
465
466}}} // name space
C++ interface for capabilities.
Definition capability.h:219
Base class implementing a virtio console device with L4Re-based notification handling.
void notify_queue(Virtqueue *queue) override
Notify queue of available data.
void trigger_driver_config_irq() override
callback for triggering configuration change notification IRQ
Device(unsigned vq_max, unsigned ports)
Create a new console device.
virtual void tx_space_available(unsigned port)=0
Callback to notify that data can be written to port.
Port * port(unsigned idx) override
Return the specified port.
unsigned port_write(char const *buf, unsigned len, unsigned port=0)
Write data to port.
L4::Cap< L4::Irq > device_notify_irq() const override
callback to gather the device notification IRQ (old-style)
void process_device_ready(l4_uint16_t value) override
Callback called on DEVICE_READY event.
void process_port_ready(l4_uint32_t id, l4_uint16_t value) override
Callback called on PORT_READY event.
virtual bool queues_stopped()
Return true, if the queues should not be processed further.
Device(unsigned vq_max)
Create a new console device.
virtual void process_port_open(l4_uint32_t id, l4_uint16_t value)
Callback called on PORT_OPEN event.
virtual void rx_data_available(unsigned port)=0
Callback to notify that new data is available to be read from port.
Device(cxx::static_vector< unsigned > const &vq_max_nums)
Create a new console Device.
unsigned port_read(char *buf, unsigned len, unsigned port=0)
Read data from port.
void register_single_driver_irq() override
callback for registering a single guest IRQ for all queues (old-style)
Base class implementing a virtio console functionality.
int port_open(unsigned idx, bool open)
Send a PORT_OPEN message and update the internal state.
virtual void process_port_ready(l4_uint32_t id, l4_uint16_t value)
Callback called on PORT_READY event.
int port_remove(unsigned idx)
Send a DEVICE_REMOVE message and update the internal state.
int port_add(unsigned idx)
Send a DEVICE_ADD message and update the internal state.
int handle_control_message()
Handle control message received from the driver.
void device_error()
Transition device into DEVICE_NEEDS_RESET state.
Definition l4virtio:1018
Mem_list const * mem_info() const
Get the memory region list used for this device.
Definition l4virtio:892
Encapsulate the state for processing a VIRTIO request.
Definition virtio:473
bool next(DESC_MAN *dm, ARGS... args)
Switch to the next descriptor in a descriptor chain.
Definition virtio:570
void start(DESC_MAN *dm, Virtqueue *ring, Virtqueue::Head_desc const &request, ARGS... args)
Start processing a new request.
Definition virtio:501
Virtqueue implementation for the device.
Definition virtio:88
Request next_avail()
Get the next available descriptor from the available ring.
Definition virtio:136
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
Descriptor in the descriptor table.
Definition virtqueue:87
l4_uint32_t len
Length of described buffer.
Definition virtqueue:109
Ptr< void > addr
Address stored in descriptor.
Definition virtqueue:108
bool no_notify_guest() const
Get the no IRQ flag of this queue.
Definition virtqueue:413
Simple encapsulation for a dynamically allocated array.
Definition static_vector:17
Error helper.
unsigned int l4_uint32_t
Unsigned 32bit value.
Definition l4int.h:29
unsigned short int l4_uint16_t
Unsigned 16bit value.
Definition l4int.h:27
@ L4VIRTIO_IRQ_STATUS_VRING
VRING IRQ pending flag.
Definition virtio.h:110
@ L4VIRTIO_IRQ_STATUS_CONFIG
CONFIG IRQ pending flag.
Definition virtio.h:111
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
long chksys(long err, char const *extra="", long ret=0)
Generate C++ exception on error.
Definition error_helper:72
T chkcap(T &&cap, char const *extra="", long err=-L4_ENOMEM)
Check for valid capability or raise C++ exception.
Definition error_helper:149
L4-VIRTIO Transport C++ API.
Definition l4virtio:26
Our C++ library.
Definition arith:11
Base class for interface implementations.
Definition ipc_epiface:146
Epiface implementation for interrupt handlers.
Definition ipc_epiface:283
Exception used by Queue to indicate descriptor errors.
Definition virtio:398
A console port with associated read/write state.
void reset() override
Reset the port to the initial state and disable its virtqueues.
Request_processor rp
Request processor associated with current request.
Virtqueue::Request request
Current virtio tx queue request.
Buffer src
Source data block to process.
Representation of a Virtio console port.
Port_status status
State the port is in.
Virtqueue rx
Transmitq of the port.
virtual void reset()
Reset the port to the initial state and disable its virtqueues.
Virtqueue tx
Receiveq of the port.
bool rx_ready() const
Check that device implementation may write to receive queues.
bool tx_ready() const
Check that device implementation may read from transmit queues.
@ Port_ready
Port is ready but still closed.
@ Port_failed
Device failure, port unusable.
Abstract data buffer.
Definition virtio:307
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
l4_uint32_t left
Bytes left in buffer.
Definition virtio:309
char * pos
Current buffer position.
Definition virtio:308