L4Re Operating System Framework
Interface and Usage Documentation
Loading...
Searching...
No Matches
virtio-console
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/l4virtio/server/l4virtio>
13#include <l4/re/error_helper>
14
15namespace L4virtio { namespace Svr { namespace Console {
16
19{
20 Features() = default;
23 CXX_BITFIELD_MEMBER(0, 0, console_size, raw);
25 CXX_BITFIELD_MEMBER(1, 1, console_multiport, raw);
27 CXX_BITFIELD_MEMBER(2, 2, emerg_write, raw);
28};
29
63
66{
72 Driver_mem_region *mem;
73};
74
107struct Port
108{
125
127 enum { Control_queue_size = 0x10 };
128
132 unsigned vq_max;
133
134 Port() : status(Port_disabled), vq_max(Control_queue_size) {}
135 Port(Port const &) = delete;
136 Port &operator = (Port const &) = delete;
137
138 virtual ~Port() = default;
139
141 bool is_open() const
142 { return status == Port_open; }
143
145 virtual void reset()
146 {
148 }
149
151 bool queues_ready() const
152 { return tx.ready() && rx.ready(); }
153
155 bool rx_ready() const
156 { return is_open() && rx.ready(); }
157
159 bool tx_ready() const
160 { return is_open() && tx.ready(); }
161};
162
186{
187 enum Virtqueue_names
188 {
189 Ctrl_rx = 2,
190 Ctrl_tx = 3,
191 };
192
193 struct Serial_config_space
194 {
195 l4_uint16_t cols;
196 l4_uint16_t rows;
197 l4_uint32_t max_nr_ports;
198 l4_uint32_t emerg_wr;
199 } __attribute__((packed));
200
201public:
211 explicit Virtio_con(unsigned max_ports, bool enable_multiport)
212 : L4virtio::Svr::Device(&_dev_config),
213 _num_ports(enable_multiport ? max_ports : 1),
214 _dev_config(L4VIRTIO_VENDOR_KK, L4VIRTIO_ID_CONSOLE,
215 enable_multiport ? max_ports * 2 + 2 : 2)
216 {
217 if (_num_ports < 1)
218 L4Re::chksys(-L4_EINVAL, "At least one port is required.");
219
220 Features hf(0);
221
222 hf.console_multiport() = enable_multiport;
223
224 _dev_config.host_features(0) = hf.raw;
225
226 if (enable_multiport)
227 _dev_config.priv_config()->max_nr_ports = _num_ports;
228 _dev_config.reset_hdr();
229 }
230
231 void reset_queue_configs()
232 {
233 for (unsigned q = 0; q < _dev_config.num_queues(); ++q)
234 reset_queue_config(q, max_queue_size(q));
235 }
236
237 int reconfig_queue(unsigned index) override
238 {
239 if (index >= _dev_config.num_queues())
240 return -L4_ERANGE;
241
242 if (setup_queue(get_queue(index), index, max_queue_size(index)))
243 return 0;
244
245 return -L4_EINVAL;
246 }
247
252 bool multiport_enabled() const
253 {
254 return _negotiated_features.console_multiport()
255 && _dev_config.num_queues() > Ctrl_rx;
256 }
257
258 bool ctrl_queue_ready() const
259 { return _ctrl_port.is_open(); }
260
261 bool check_features(void) override
262 {
263 _negotiated_features = Features(_dev_config.negotiated_features(0));
264 return true;
265 }
266
267 bool check_queues() override
268 {
269 // NOTE
270 // The VIRTIO specification states:
271 // "The port 0 receive and transmit queues always exist"
272 // The linux driver however does not setup port 0 if the multiport feature
273 // is negotiated.
274 // We just go along with the linux driver and do not expect port 0 to be up,
275 // if the multiport feature is negotiated.
276
277 if (multiport_enabled())
278 // If MULTIPORT was negotiated, ctrl queues should be set up.
279 return _ctrl_port.queues_ready();
280
281 // If MULTIPORT was not negotiated, port 0 should be set up.
283 return port(0)->queues_ready();
284 }
285
298 int port_add(unsigned idx)
299 {
300 Port *p = port(idx);
301
302 if (p->status != Port::Port_disabled)
303 return -L4_EPERM;
304
306
307 if (ret == L4_EOK)
309
310 return ret;
311 }
312
325 int port_remove(unsigned idx)
326 {
327 Port *p = port(idx);
328
329 if (p->status != Port::Port_open
330 && p->status != Port::Port_ready
331 && p->status != Port::Port_failed)
332 return -L4_EPERM;
333
335
336 if (ret == L4_EOK)
337 p->reset();
338
339 return ret;
340 }
341
355 int port_open(unsigned idx, bool open)
356 {
357 Port *p = port(idx);
358
359 if ((open && p->status != Port::Port_ready)
360 || (!open && p->status != Port::Port_open))
361 return -L4_EPERM;
362
364
365 if (ret == L4_EOK)
367
368 return ret;
369 }
370
394 l4_uint16_t value = 0, const char *name = 0)
395 {
396 if (!ctrl_queue_ready())
397 return -L4_ENODEV;
398
399 Virtqueue *q = &_ctrl_port.rx;
400 if (!q->ready())
401 return -L4_ENODEV;
402
403 Virtqueue::Request r = q->next_avail();
404 if (!r)
405 return -L4_EBUSY;
406
408 Control_request req;
409 rp.start(this, r, &req);
410
411 if (req.len < sizeof(Control_message))
412 return -L4_ENOMEM;
413
414 Control_message msg(idx, event, value);
415
416 memcpy(req.msg, &msg, sizeof(msg));
417
418 if (event == Control_message::Port_name && name)
419 {
420 size_t name_len = cxx::min(req.len - sizeof(msg), strlen(name));
421 memcpy(reinterpret_cast<char*>(req.msg) + sizeof(msg), name, name_len);
422 q->finish(r, this, sizeof(msg) + name_len);
423 }
424 else
425 q->finish(r, this, sizeof(msg));
426
427 return L4_EOK;
428 }
429
444 {
445 Virtqueue *q = &_ctrl_port.tx;
446 if (!q->ready())
447 return -L4_ENODEV;
448
449 Virtqueue::Request r;
450 while ((r = q->next_avail()))
451 {
453 Control_request req;
454
455 rp.start(this, r, &req);
456
457 Control_message msg;
458 if (req.len < sizeof(msg))
459 {
460 // Just ignore malformed input.
461 q->consumed(r);
462 return L4_EOK;
463 }
464
465 memcpy(&msg, req.msg, sizeof(msg));
466 q->consumed(r);
467
468 if (_ctrl_port.status == Port::Port_disabled)
469 {
470 // When the control queue is disabled, only device ready is accepted.
472 {
473 if (msg.value)
474 _ctrl_port.status = Port::Port_open;
475 }
476
478 continue;
479 }
480
481 if (!ctrl_queue_ready())
482 continue;
483
484 switch (msg.event)
485 {
487 // Ignore invalid port ids
488 if (msg.id >= max_ports())
489 break;
490
491 // Ignore repeated PORT_READY messages.
492 if ((port(msg.id)->status == Port::Port_disabled)
493 || (!msg.value && port(msg.id)->status == Port::Port_added))
494 break;
495
496 process_port_ready(msg.id, msg.value);
497 break;
498
500 // Ignore invalid port ids
501 if (msg.id >= max_ports())
502 break;
503
504 // Ignore misplaced PORT_OPEN messages.
505 if ((msg.value && port(msg.id)->status != Port::Port_ready)
506 || (!msg.value && port(msg.id)->status != Port::Port_open))
507 break;
508
509 process_port_open(msg.id, msg.value);
510 break;
511 default:
512 return -L4_EINVAL;
513 }
514 }
515
516 return L4_EOK;
517 }
518
520 void load_desc(L4virtio::Virtqueue::Desc const &desc,
521 Request_processor const *proc,
522 L4virtio::Virtqueue::Desc const **table)
523 {
524 this->_mem_info.load_desc(desc, proc, table);
525 }
526
528 void load_desc(L4virtio::Virtqueue::Desc const &desc,
529 Request_processor const *proc,
530 Control_request *data)
531 {
532 auto *region = this->_mem_info.find(desc.addr.get(), desc.len);
533 if (L4_UNLIKELY(!region))
535
536 data->msg = reinterpret_cast<Control_message *>(region->local(desc.addr));
537 data->len = desc.len;
538 data->mem = region;
539 }
540
541 void reset() override
542 {
543 for (unsigned p = 0; p < _num_ports; ++p)
544 port(p)->reset();
545
546 _ctrl_port.reset();
547 reset_queue_configs();
548 _dev_config.reset_hdr();
549 _negotiated_features = Features(0);
550
551 reset_device();
552 }
553
560 virtual void reset_device() {}
561
571 virtual void notify_queue(Virtqueue *queue) = 0;
572
580 virtual Port *port(unsigned port) = 0;
581 virtual Port const *port(unsigned port) const = 0;
582
593 virtual void process_device_ready(l4_uint16_t value) = 0;
594
607 {
608 Port *p = port(id);
609
610 switch (p->status)
611 {
612 case Port::Port_added:
613 case Port::Port_ready:
615 break;
616 case Port::Port_open:
617 if (!value)
619 break;
620 default:
621 // invalid state for PORT_READY message
622 break;
623 }
624 }
625
636 virtual void process_port_open(l4_uint32_t id, l4_uint16_t value) = 0;
637
638 unsigned max_ports() const
639 { return _num_ports; }
640
641private:
642 bool is_control_queue(unsigned q) const
643 { return q == Ctrl_rx || q == Ctrl_tx; }
644
645 unsigned queue_to_port(unsigned q) const
646 { return (q == 0 || q == 1) ? 0 : (q / 2) - 1; }
647
656 unsigned max_queue_size(unsigned q) const
657 {
658 if (is_control_queue(q))
659 return _ctrl_port.vq_max;
660
661 return port(queue_to_port(q))->vq_max;
662 }
663
672 Virtqueue *get_queue(unsigned q)
673 {
674 Port *p;
675 if (is_control_queue(q))
676 p = &_ctrl_port;
677 else
678 p = port(queue_to_port(q));
679
680 if (q & 1)
681 return &p->tx;
682 else
683 return &p->rx;
684 }
685
686 unsigned _num_ports;
687
688protected:
689 Dev_config_t<Serial_config_space> _dev_config;
690 Port _ctrl_port;
691 Features _negotiated_features{0};
692};
693
694}}} // name space
l4_uint64_t get() const
Definition virtqueue:69
Base class implementing a virtio console device with L4Re-based notification handling.
Base class implementing a virtio console functionality.
virtual void process_port_open(l4_uint32_t id, l4_uint16_t value)=0
Callback called on PORT_OPEN event.
bool check_queues() override
callback for checking if the queues at DRIVER_OK transition
virtual void reset_device()
Reset the state of the actual console device.
bool multiport_enabled() const
Return true if the multiport feature is enabled and control queues are available.
bool check_features(void) override
callback for checking the subset of accepted features
virtual void notify_queue(Virtqueue *queue)=0
Notify queue of available data.
void reset() override
reset callback, called for doing a device reset
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.
virtual void process_device_ready(l4_uint16_t value)=0
Callback called on DEVICE_READY event.
int port_add(unsigned idx)
Send a DEVICE_ADD message and update the internal state.
int reconfig_queue(unsigned index) override
callback for client queue-config request
Virtio_con(unsigned max_ports, bool enable_multiport)
Create a new multiport console device.
virtual Port * port(unsigned port)=0
Return the specified port.
int handle_control_message()
Handle control message received from the driver.
int send_control_message(l4_uint32_t idx, l4_uint16_t event, l4_uint16_t value=0, const char *name=0)
Send control message to driver.
Server-side L4-VIRTIO device stub.
Definition l4virtio:796
Mem_list _mem_info
Memory region list.
Definition l4virtio:801
bool setup_queue(Virtqueue *q, unsigned qn, unsigned num_max)
Enable/disable the specified queue.
Definition l4virtio:1041
void reset_queue_config(unsigned idx, unsigned num_max, bool inc_generation=false)
Trigger reset for the configuration space for queue idx.
Definition l4virtio:996
Encapsulate the state for processing a VIRTIO request.
Definition virtio:454
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
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
void consumed(Head_desc const &r, l4_uint32_t len=0)
Put the given descriptor into the used ring.
Definition virtio:171
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 ready() const
Test if this queue is in working state.
Definition virtqueue:406
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_ERANGE
Range error.
Definition err.h:59
@ L4_EINVAL
Invalid argument.
Definition err.h:57
@ L4_EBUSY
Object currently busy, try later.
Definition err.h:53
@ L4_ENODEV
No such thing.
Definition err.h:55
@ L4_EOK
Ok.
Definition err.h:43
@ L4_EPERM
No permission.
Definition err.h:44
@ L4_ENOMEM
No memory.
Definition err.h:50
#define L4_UNLIKELY(x)
Expression is unlikely to execute.
Definition compiler.h:296
@ L4VIRTIO_ID_CONSOLE
Simple device for data IO via ports.
Definition virtio.h:65
long chksys(long err, char const *extra="", long ret=0)
Generate C++ exception on error.
Definition error_helper:68
L4-VIRTIO Transport C++ API.
Definition l4virtio:26
Exception used by Queue to indicate descriptor errors.
Definition virtio:379
@ Bad_address
Address cannot be translated.
Definition virtio:383
Virtio console control message.
@ Device_ready
Sent by driver at initialization.
@ Console_port
Sent by device to nominate port as console port.
@ Port_open
Sent by device and driver to indicate whether a port is open.
@ Port_name
Sent by device to tag a port.
@ Device_remove
Sent by device to remove added ports.
@ Device_add
Sent by device to create new ports.
@ Port_ready
Sent by driver as response to Device_add.
@ Resize
Sent by device to indicate a console size change.
l4_uint16_t value
Extra information.
l4_uint16_t event
Control event, see Events.
Specialised Virtqueue::Request providing access to control message payload.
Control_message * msg
Virtual address of the data block (in device space).
l4_uint32_t len
Length of datablock in bytes.
Driver_mem_region * mem
Pointer to driver memory region.
Virtio console specific feature bits.
constexpr emerg_write_bfm_t::Val emerg_write() const
Get the emerg_write bits ( 2 to 2 ) of raw.
constexpr console_multiport_bfm_t::Val console_multiport() const
Get the console_multiport bits ( 1 to 1 ) of raw.
constexpr console_size_bfm_t::Val console_size() const
Get the console_size bits ( 0 to 0 ) of raw.
Representation of a Virtio console port.
Port_status status
State the port is in.
Virtqueue rx
Transmitq of the port.
unsigned vq_max
Maximum queue sizes for this port.
virtual void reset()
Reset the port to the initial state and disable its virtqueues.
Virtqueue tx
Receiveq of the port.
bool is_open() const
Check that the port is open.
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_status
Possible states of a virtio console port.
@ Port_added
Port has been added by device, waiting for ready message.
@ 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.
Type for device feature bitmap.
Definition virtio:67
l4_uint32_t raw
The raw value of the features bitmap.
Definition virtio:68