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-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 bool failed = false;
360 try
361 {
362 Device_port::Buffer dst;
363 rp.start(mem_info(), r, &dst);
364
365 for (;;)
366 {
367 chunk += src.copy_to(&dst);
368 if (!src.left)
369 break;
370 if (!rp.next(mem_info(), &dst))
371 break;
372 }
373 }
374 catch (Bad_descriptor const &)
375 {
376 failed = true;
377 }
378
379 q->finish(r, this, chunk);
380 total += chunk;
381
382 if (failed)
383 {
384 device_error();
385 break;
386 }
387 }
388
389 if (total < len)
390 p.poll_out_req = true;
391
392 return total;
393 }
394
407 {
408 if (!value)
409 return;
410
411 for (unsigned i = 0; i < max_ports(); ++i)
412 port_add(i);
413 }
414
430 {
432
433 Port *p = port(id);
434 if (p->status == Port::Port_failed)
435 port_remove(id);
436 else if (p->status == Port::Port_ready)
437 port_open(id, true);
438 }
439
451 {
452 static_cast<void>(id);
453 static_cast<void>(value);
454 }
455
456protected:
457 Port* port(unsigned idx) override
458 {
459 return &_ports[idx];
460 }
461
462 Port const *port(unsigned idx) const override
463 {
464 return &_ports[idx];
465 }
466
467private:
468 Irq_object _irq_handler;
469 cxx::unique_ptr<Device_port[]> _ports;
470 L4Re::Util::Unique_cap<L4::Irq> _kick_driver_irq;
471};
472
473}}} // name space
C++ interface for capabilities.
Definition capability.h:249
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_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.
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)
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.
Virtio_con(unsigned max_ports, bool enable_multiport)
Create a new multiport console device.
int handle_control_message()
Handle control message received from the driver.
Mem_list const * mem_info() const
Definition l4virtio:939
T * local(Ptr< T > p) const
Get the local address for driver address p.
Definition l4virtio:640
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:575
void start(DESC_MAN *dm, Virtqueue *ring, Virtqueue::Head_desc const &request, ARGS... args)
Start processing a new request.
Definition virtio:503
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:93
l4_uint32_t len
Length of described buffer.
Definition virtqueue:115
Ptr< void > addr
Address stored in descriptor.
Definition virtqueue:114
bool no_notify_guest() const
Get the no IRQ flag of this queue.
Definition virtqueue:433
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:121
@ L4VIRTIO_IRQ_STATUS_CONFIG
CONFIG IRQ pending flag.
Definition virtio.h:122
L4::Detail::Unique_cap_impl< T, L4Re::Util::Smart_cap_auto< L4_FP_ALL_SPACES > > Unique_cap
Unique capability that implements automatic free and unmap of the capability slot.
Definition unique_cap:43
T chkcap(T &&cap, char const *extra="", l4_ret_t err=-L4_ENOMEM)
Check for valid capability or raise C++ exception.
Definition error_helper:149
l4_ret_t chksys(l4_ret_t err, char const *extra="", l4_ret_t ret=0)
Generate C++ exception on error.
Definition error_helper:72
L4-VIRTIO Transport C++ API.
Definition l4virtio:26
Our C++ library.
Definition arith:11
Base class for interface implementations.
Definition ipc_epiface:264
Epiface implementation for interrupt handlers.
Definition ipc_epiface:401
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