L4Re Operating System Framework
Interface and Usage Documentation
Loading...
Searching...
No Matches
virtio-console-device
1// vi:ft=cpp
2/* SPDX-License-Identifier: MIT */
3/*
4 * Copyright (C) 2019-2023 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 = false;
46
47 void reset() override
48 {
50 request = Virtqueue::Request();
51 }
52};
53
117: public Virtio_con
118{
119 class Irq_object : public L4::Irqep_t<Irq_object>
120 {
121 public:
122 Irq_object(Device *parent) : _parent(parent) {}
123
124 void handle_irq() { _parent->kick(); }
125
126 private:
127 Device *_parent;
128 };
129
130protected:
131 L4::Epiface *irq_iface()
132 { return &_irq_handler; }
133
134public:
143 explicit Device(unsigned vq_max)
144 : Virtio_con(1, false),
145 _irq_handler(this),
146 _ports(cxx::make_unique<Device_port[]>(1)),
147 _control_message_stalled(0)
148 {
149 _ports[0].vq_max = vq_max;
150 reset_queue_configs();
151 }
152
162 explicit Device(unsigned vq_max, unsigned ports)
163 : Virtio_con(ports, true),
164 _irq_handler(this),
165 _ports(cxx::make_unique<Device_port[]>(ports)),
166 _control_message_stalled(0)
167 {
168 if (ports > sizeof(_control_message_stalled) * 8)
169 L4Re::chksys(-L4_ENOMEM, "Only 32 ports are supported.");
170 for (unsigned i = 0; i < ports; ++i)
171 _ports[i].vq_max = vq_max;
172 reset_queue_configs();
173 }
174
184 explicit Device(cxx::static_vector<unsigned> const &vq_max_nums)
185 : Virtio_con(vq_max_nums.size(), true),
186 _irq_handler(this),
187 _ports(cxx::make_unique<Device_port[]>(max_ports())),
188 _control_message_stalled(0)
189 {
190 if (max_ports() > sizeof(_control_message_stalled)*8)
191 L4Re::chksys(-L4_ENOMEM, "Only 32 ports are supported.");
192
193 for (unsigned i = 0; i < vq_max_nums.size(); ++i)
194 _ports[i].vq_max = vq_max_nums[i];
195 reset_queue_configs();
196 }
197
199 {
200 _kick_driver_irq = L4Re::Util::Unique_cap<L4::Irq>(
201 L4Re::chkcap(server_iface()->rcv_cap<L4::Irq>(0)));
202 L4Re::chksys(server_iface()->realloc_rcv_cap(0));
203 }
204
206 { return _irq_handler.obj_cap(); }
207
208 void notify_queue(Virtqueue *queue) override
209 {
210 if (queue->no_notify_guest())
211 return;
212
213 _dev_config.add_irq_status(L4VIRTIO_IRQ_STATUS_VRING);
214 _kick_driver_irq->trigger();
215 }
216
220 virtual void rx_data_available(unsigned port) = 0;
221
225 virtual void tx_space_available(unsigned port) = 0;
226
230 virtual bool queues_stopped()
231 { return false; }
232
234 {
235 _dev_config.add_irq_status(L4VIRTIO_IRQ_STATUS_CONFIG);
236 _kick_driver_irq->trigger();
237 }
238
239 void kick()
240 {
241 if (queues_stopped())
242 return;
243
244 // We're not interested in logging any errors, just ignore return value.
246
247 for (unsigned i = 0; i < max_ports(); ++i)
248 {
249 auto &p = _ports[i];
250 if (p.poll_in_req && p.tx_ready() && p.tx.desc_avail())
251 {
252 p.poll_in_req = false;
254 }
255
256 if (p.poll_out_req && p.rx_ready() && p.rx.desc_avail())
257 {
258 p.poll_out_req = false;
260 }
261 }
262 }
263
278 unsigned port_read(char *buf, unsigned len, unsigned port = 0)
279 {
280 unsigned total = 0;
281 Device_port &p = _ports[port];
282 Virtqueue *q = &p.tx;
283
284 Data_buffer dst;
285 dst.pos = buf;
286 dst.left = len;
287
288 while (dst.left)
289 {
290 try
291 {
292 // Make sure we have a valid request where we can read data from
293 if (!p.request.valid())
294 {
295 p.request = p.tx_ready() ? q->next_avail()
296 : Virtqueue::Request();
297 if (!p.request.valid())
298 break;
299
300 p.rp.start(mem_info(), p.request, &p.src);
301 }
302
303 total += p.src.copy_to(&dst);
304
305 // We might have eaten up the current descriptor. Move to the next
306 // if this is the case. At the end of the descriptor chain we have
307 // to retire the current request altogether.
308 if (!p.src.left)
309 {
310 if (!p.rp.next(mem_info(), &p.src))
311 {
312 q->finish(p.request, this);
313 p.request = Virtqueue::Request();
314 }
315 }
316 }
317 catch (Bad_descriptor const &)
318 {
319 q->finish(p.request, this);
320 p.request = Virtqueue::Request();
321 }
322 }
323
324 if (total < len)
325 p.poll_in_req = true;
326
327 return total;
328 }
329
345 unsigned port_write(char const *buf, unsigned len, unsigned port = 0)
346 {
347 unsigned total = 0;
348 Device_port &p = _ports[port];
349 Virtqueue *q = &p.rx;
350
351 Data_buffer src;
352 src.pos = const_cast<char*>(buf);
353 src.left = len;
354
356 while (src.left)
357 {
358 auto r = p.rx_ready() ? q->next_avail() : Virtqueue::Request();
359 if (!r.valid())
360 break;
361
362 l4_uint32_t chunk = 0;
363 try
364 {
365 Device_port::Buffer dst;
366 rp.start(mem_info(), r, &dst);
367
368 for (;;)
369 {
370 chunk += src.copy_to(&dst);
371 if (!src.left)
372 break;
373 if (!rp.next(mem_info(), &dst))
374 break;
375 }
376 }
377 catch (Bad_descriptor const &)
378 { /* Ignore */ }
379
380 q->finish(r, this, chunk);
381 total += chunk;
382 }
383
384 if (total < len)
385 p.poll_out_req = true;
386
387 return total;
388 }
389
401 {
402 if (_control_message_stalled)
404
405 return handle_control_message();
406 }
407
408 bool ctrl_work_pending() const
409 {
410 return (L4_LIKELY(_ctrl_port.queues_ready())
411 && _ctrl_port.tx.desc_avail()) || _control_message_stalled;
412 }
413
427 {
428 if (!value)
429 return;
430
431 for (unsigned i = 0; i < max_ports(); ++i)
432 check_stalled(i, port_add(i));
433 }
434
449 {
451
452 Port *p = port(id);
453 if (p->status == Port::Port_failed)
454 check_stalled(id, port_remove(id));
455 }
456
471 { check_stalled(id, port_open(id, value)); }
472
473protected:
479 {
480 for (unsigned i = 0; i < max_ports(); ++i)
481 {
482 if (!(_control_message_stalled & (1U << i)))
483 continue;
484
485 switch(_ports[i].status)
486 {
488 check_stalled(i, port_add(i));
489 break;
491 check_stalled(i, port_remove(i));
492 break;
493 case Port::Port_ready:
494 check_stalled(i, port_open(i, 1));
495 break;
496 case Port::Port_open:
497 check_stalled(i, port_open(i, 0));
498 break;
499 default:
500 break;
501 }
502 }
503 }
504
505 Port* port(unsigned idx) override
506 {
507 return &_ports[idx];
508 }
509
510 Port const *port(unsigned idx) const override
511 {
512 return &_ports[idx];
513 }
514
515 void check_stalled(unsigned port, int retval)
516 {
517 if (retval == -L4_ENODEV || retval == -L4_EBUSY)
518 _control_message_stalled |= 1U << port;
519 else
520 _control_message_stalled &= ~1U << port;
521 }
522
523private:
524 Irq_object _irq_handler;
525 cxx::unique_ptr<Device_port[]> _ports;
526 L4Re::Util::Unique_cap<L4::Irq> _kick_driver_irq;
528 l4_uint32_t _control_message_stalled;
529};
530
531}}} // 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.
void resend_stalled_control_messages()
Try to resend a stalled control message and update the port and global state accordingly.
L4::Cap< L4::Irq > device_notify_irq() const override
callback to gather the device notification IRQ (old-style)
void process_port_open(l4_uint32_t id, l4_uint16_t value) override
Callback called on PORT_OPEN event.
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.
int do_control_work()
Receive new control messages and resend stalled ones.
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 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.
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:454
bool next(DESC_MAN *dm, ARGS... args)
Switch to the next descriptor in a descriptor chain.
Definition virtio:551
void start(DESC_MAN *dm, Virtqueue *ring, Virtqueue::Head_desc const &request, ARGS... args)
Start processing a new request.
Definition virtio:482
Virtqueue implementation for the device.
Definition virtio:88
bool desc_avail() const
Test for available descriptors.
Definition virtio:156
Request next_avail()
Get the next available descriptor from the available ring.
Definition virtio:138
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:221
Descriptor in the descriptor table.
Definition virtqueue:94
l4_uint32_t len
Length of described buffer.
Definition virtqueue:116
Ptr< void > addr
Address stored in descriptor.
Definition virtqueue:115
bool no_notify_guest() const
Get the no IRQ flag of this queue.
Definition virtqueue:420
Simple encapsulation for a dynamically allocated array.
Definition static_vector:17
Error helper.
unsigned int l4_uint32_t
Unsigned 32bit value.
Definition l4int.h:40
unsigned short int l4_uint16_t
Unsigned 16bit value.
Definition l4int.h:38
@ L4_EBUSY
Object currently busy, try later.
Definition err.h:53
@ L4_ENODEV
No such thing.
Definition err.h:55
@ L4_ENOMEM
No memory.
Definition err.h:50
#define L4_LIKELY(x)
Expression is likely to execute.
Definition compiler.h:295
@ L4VIRTIO_IRQ_STATUS_VRING
VRING IRQ pending flag.
Definition virtio.h:109
@ L4VIRTIO_IRQ_STATUS_CONFIG
CONFIG IRQ pending flag.
Definition virtio.h:110
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:54
long chksys(long err, char const *extra="", long ret=0)
Generate C++ exception on error.
Definition error_helper:68
T chkcap(T &&cap, char const *extra="", long err=-L4_ENOMEM)
Check for valid capability or raise C++ exception.
Definition error_helper:145
L4-VIRTIO Transport C++ API.
Definition l4virtio:26
Our C++ library.
Definition arith:22
Base class for interface implementations.
Definition ipc_epiface:157
Epiface implementation for interrupt handlers.
Definition ipc_epiface:294
Exception used by Queue to indicate descriptor errors.
Definition virtio:379
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_open
Port is in a working state.
@ Port_disabled
Reset state, waiting for port to be added.
@ Port_failed
Device failure, port unusable.
bool queues_ready() const
Check that both virtqueues are set up correctly.
Abstract data buffer.
Definition virtio:288
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:335
l4_uint32_t left
Bytes left in buffer.
Definition virtio:290
char * pos
Current buffer position.
Definition virtio:289