L4Re Operating System Framework
Interface and Usage Documentation
Loading...
Searching...
No Matches
main.cc
1/*
2 * Copyright (C) 2016-2020, 2022-2024 Kernkonzept GmbH.
3 * Author(s): Jean Wolter <jean.wolter@kernkonzept.com>
4 * Manuel von Oltersdorff-Kalettka <manuel.kalettka@kernkonzept.com>
5 *
6 * License: see LICENSE.spdx (in this directory or the directories above)
7 */
8#include <l4/re/util/meta>
9#include <l4/re/util/object_registry>
10#include <l4/re/util/br_manager>
11
12#include <l4/sys/factory>
13#include <l4/sys/task>
14
15#include <l4/sys/cxx/ipc_epiface>
16#include <l4/sys/cxx/ipc_varg>
17#include <l4/cxx/string>
18
19#include <vector>
20#include <string>
21#include <terminate_handler-l4>
22
23#include "debug.h"
24#include "options.h"
25#include "switch.h"
26#include "vlan.h"
27
46/*
47 * Registry for our server, used to register
48 * - factory capability
49 * - irq object for capability deletion irqs
50 * - virtio host kick irqs
51 */
53
54using Ds_vector = std::vector<L4::Cap<L4Re::Dataspace>>;
55static std::shared_ptr<Ds_vector> trusted_dataspaces;
56
57static bool
58parse_int_param(L4::Ipc::Varg const &param, char const *prefix, int *out)
59{
60 l4_size_t headlen = strlen(prefix);
61
62 if (param.length() < headlen)
63 return false;
64
65 char const *pstr = param.value<char const *>();
66
67 if (strncmp(pstr, prefix, headlen) != 0)
68 return false;
69
70 std::string tail(pstr + headlen, param.length() - headlen);
71
72 if (!parse_int_optstring(tail.c_str(), out))
73 {
74 Err(Err::Normal).printf("Bad paramter '%s'. Invalid number specified.\n",
75 prefix);
77 }
78
79 return true;
80}
81
93class Switch_factory : public L4::Epiface_t<Switch_factory, L4::Factory>
94{
98 class Port : public Virtio_port
99 {
100 // Irq used to notify the guest
101 L4::Cap<L4::Irq> _device_notify_irq;
102
103 L4::Cap<L4::Irq> device_notify_irq() const override
104 { return _device_notify_irq; }
105
106 public:
107 Port(unsigned vq_max, unsigned num_ds, char const *name,
108 l4_uint8_t const *mac)
109 : Virtio_port(vq_max, num_ds, name, mac) {}
110
112 void register_end_points(L4Re::Util::Object_registry* registry,
113 L4::Epiface *kick_irq)
114 {
115 // register virtio host kick irq
116 _device_notify_irq = L4Re::chkcap(registry->register_irq_obj(kick_irq));
117
118 // register virtio endpoint
119 L4Re::chkcap(registry->register_obj(this));
120
121 // decrement ref counter to get a notification when the last
122 // external reference vanishes
123 obj_cap()->dec_refcnt(1);
124 }
125
126 virtual ~Port()
127 { server.registry()->unregister_obj(this); }
128 };
129
133 class Switch_port : public Port
134 {
145 class Kick_irq : public L4::Irqep_t<Kick_irq>
146 {
147 Virtio_switch *_switch;
148 Virtio_port *_port;
150 public:
157 void handle_irq()
158 { _switch->handle_port_irq(_port); }
159
160 Kick_irq(Virtio_switch *virtio_switch, Virtio_port *port)
161 : _switch{virtio_switch}, _port{port} {}
162 };
163
164 Kick_irq _kick_irq;
166 public:
167 Switch_port(L4Re::Util::Object_registry *registry,
168 Virtio_switch *virtio_switch, unsigned vq_max, unsigned num_ds,
169 char const *name, l4_uint8_t const *mac)
170 : Port(vq_max, num_ds, name, mac),
171 _kick_irq(virtio_switch, this)
172 { register_end_points(registry, &_kick_irq); }
173
174 virtual ~Switch_port()
175 {
176 // We need to delete the IRQ object created in register_irq_obj() ourselves
177 L4::Cap<L4::Task>(L4Re::This_task)
178 ->unmap(_kick_irq.obj_cap().fpage(),
180 server.registry()->unregister_obj(&_kick_irq);
181 }
182 };
183
187 class Monitor_port : public Port
188 {
194 class Kick_irq : public L4::Irqep_t<Kick_irq>
195 {
196 Virtio_port *_port;
197
198 public:
206 void handle_irq()
207 {
208 do
209 {
210 _port->tx_q()->disable_notify();
211 _port->rx_q()->disable_notify();
212
213 _port->drop_requests();
214
215 _port->tx_q()->enable_notify();
216 _port->rx_q()->enable_notify();
217
218 L4virtio::wmb();
219 L4virtio::rmb();
220 }
221 while (_port->tx_work_pending());
222 }
223
224 Kick_irq(Virtio_port *port) : _port{port} {}
225 };
226
227 Kick_irq _kick_irq;
228
229 public:
230 Monitor_port(L4Re::Util::Object_registry* registry,
231 unsigned vq_max, unsigned num_ds, char const *name,
232 l4_uint8_t const *mac)
233 : Port(vq_max, num_ds, name, mac), _kick_irq(this)
234 { register_end_points(registry, &_kick_irq); }
235
236 virtual ~Monitor_port()
237 {
238 // We need to delete the IRQ object created in register_irq_obj() ourselves
239 L4::Cap<L4::Task>(L4Re::This_task)
240 ->unmap(_kick_irq.obj_cap().fpage(),
242 server.registry()->unregister_obj(&_kick_irq);
243 }
244 };
245
246 /*
247 * Handle vanishing caps by telling the switch that a port might have gone
248 */
249 struct Del_cap_irq : public L4::Irqep_t<Del_cap_irq>
250 {
251 public:
252 void handle_irq()
253 { _switch->check_ports(); }
254
255 Del_cap_irq(Virtio_switch *virtio_switch) : _switch{virtio_switch} {}
256
257 private:
258 Virtio_switch *_switch;
259 };
260
261 Virtio_switch *_virtio_switch;
264 unsigned _vq_max_num;
265 Del_cap_irq _del_cap_irq;
266
279 bool handle_opt_arg(L4::Ipc::Varg const &opt, bool &monitor,
280 char *name, size_t size,
281 l4_uint16_t &vlan_access,
282 std::vector<l4_uint16_t> &vlan_trunk,
283 l4_uint8_t mac[6], bool &mac_set)
284 {
285 assert(opt.is_of<char const *>());
286 unsigned len = opt.length();
287 const char *opt_str = opt.data();
288 Err err(Err::Normal);
289
290
291 if (len > 5)
292 {
293 if (!strncmp("type=", opt_str, 5))
294 {
295 if (!strncmp("type=monitor", opt_str, len))
296 {
297 monitor = true;
298 return true;
299 }
300 else if (!strncmp("type=none", opt_str, len))
301 return true;
302
303 err.printf("Unknown type '%.*s'\n", opt.length() - 5, opt.data() + 5);
304 return false;
305 }
306 else if (!strncmp("name=", opt_str, 5))
307 {
308 snprintf(name, size, "%.*s", opt.length() - 5, opt.data() + 5);
309 return true;
310 }
311 else if (!strncmp("vlan=", opt_str, 5))
312 {
313 cxx::String str(opt_str + 5, strnlen(opt_str + 5, len - 5));
315
316 if ((idx = str.starts_with("access=")))
317 {
318 str = str.substr(idx);
319 l4_uint16_t vid;
320 int next = str.from_dec(&vid);
321 if (next && next == str.len() && vlan_valid_id(vid))
322 vlan_access = vid;
323 else
324 {
325 err.printf("Invalid VLAN access port id '%.*s'\n",
326 opt.length(), opt.data());
327 return false;
328 }
329 }
330 else if ((idx = str.starts_with("trunk=")))
331 {
332 int next;
333 l4_uint16_t vid;
334 str = str.substr(idx);
335 while ((next = str.from_dec(&vid)))
336 {
337 if (!vlan_valid_id(vid))
338 break;
339 vlan_trunk.push_back(vid);
340 if (next < str.len() && str[next] != ',')
341 break;
342 str = str.substr(next+1);
343 }
344
345 if (!str.empty())
346 {
347 err.printf("Invalid VLAN trunk port spec '%.*s'\n",
348 opt.length(), opt.data());
349 return false;
350 }
351 }
352 else
353 {
354 err.printf("Invalid VLAN specification..\n");
355 return false;
356 }
357
358 return true;
359 }
360 else if (!strncmp("mac=", opt_str, 4))
361 {
362 size_t const OPT_LEN = 4 /* mac= */ + 6*2 /* digits */ + 5 /* : */;
363 // expect NUL terminated string for simplicity
364 if (len > OPT_LEN && opt_str[OPT_LEN] == '\0' &&
365 sscanf(opt_str+4, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &mac[0],
366 &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) == 6)
367 {
368 mac_set = true;
369 return true;
370 }
371
372 err.printf("Invalid mac address '%.*s'\n", len - 4, opt_str + 4);
373 return false;
374 }
375 }
376
377 err.printf("Unknown option '%.*s'\n", opt.length(), opt.data());
378 return false;
379 }
380
381public:
382 Switch_factory(Virtio_switch *virtio_switch, unsigned vq_max_num)
383 : _virtio_switch{virtio_switch}, _vq_max_num{vq_max_num},
384 _del_cap_irq{virtio_switch}
385 {
386 auto c = L4Re::chkcap(server.registry()->register_irq_obj(&_del_cap_irq));
387 L4Re::chksys(L4Re::Env::env()->main_thread()->register_del_irq(c));
388 };
389
396 long op_create(L4::Factory::Rights, L4::Ipc::Cap<void> &res,
398 {
399 Dbg warn(Dbg::Port, Dbg::Warn, "Port");
400 Dbg info(Dbg::Port, Dbg::Info, "Port");
401
402 info.printf("Incoming port request\n");
403
404 // test for supported object types
405 if (type != 0)
406 {
407 warn.printf("Invalid object type\n");
408 return -L4_EINVAL;
409 }
410
411 bool monitor = false;
412 char name[20] = "";
413 unsigned arg_n = 2;
414 l4_uint16_t vlan_access = 0;
415 std::vector<l4_uint16_t> vlan_trunk;
416
417 // Default MAC address. Might be overridden by a "mac=..." option.
418 // First octet: 0x02
419 // * bit 0: Individual/Group address bit
420 // * bit 1: Universally/Locally Administered address bit
421 // Last two octets are filled with port number.
422 l4_uint8_t mac[6] = { 0x02, 0x08, 0x0f, 0x2a, 0x00, 0x00 };
423 bool mac_set = false;
424 int num_ds = 2;
425
426 for (L4::Ipc::Varg opt: va)
427 {
428 if (!opt.is_of<char const *>())
429 {
430 warn.printf("Unexpected type for argument %d\n", arg_n);
431 return -L4_EINVAL;
432 }
433
434 if (parse_int_param(opt, "ds-max=", &num_ds))
435 {
436 if (num_ds <= 0 || num_ds > 80)
437 {
438 Err(Err::Normal).printf("warning: client requested invalid number"
439 " of data spaces: 0 < %d <= 80\n", num_ds);
440 return -L4_EINVAL;
441 }
442 }
443 else if (!handle_opt_arg(opt, monitor, name, sizeof(name), vlan_access,
444 vlan_trunk, mac, mac_set))
445 return -L4_EINVAL;
446
447 ++arg_n;
448 }
449
450 int port_num = _virtio_switch->port_available(monitor);
451 if (port_num < 0)
452 {
453 warn.printf("No port available\n");
454 return -L4_ENOMEM;
455 }
456
457 if (vlan_access && !vlan_trunk.empty())
458 {
459 warn.printf("Port cannot be access and trunk VLAN port simultaneously.\n");
460 return -L4_EINVAL;
461 }
462
463 if (name[0])
464 {
465 // append port number
466 unsigned len = strlen(name);
467 snprintf(name + len, sizeof(name) - len, "[%d]", port_num);
468 }
469 else
470 snprintf(name, sizeof(name), "%s[%d]", monitor ? "monitor" : "",
471 port_num);
472
473 info.printf(" Creating port %s%s\n", name,
474 monitor ? " as monitor port" : "");
475
476 if (!mac_set)
477 {
478 // assign a dedicated MAC address to the monitor interface
479 // assuming we will never have more than 57000 (0xdea8) normal
480 // ports
481 if (monitor)
482 {
483 mac[4] = 0xde;
484 mac[5] = 0xad;
485 }
486 else
487 {
488 mac[4] = (l4_uint8_t)(port_num >> 8);
489 mac[5] = (l4_uint8_t)port_num;
490 }
491 }
492 l4_uint8_t *mac_ptr = (mac_set || Options::get_options()->assign_mac())
493 ? mac : nullptr;
494
495 // create port
496 Port *port;
497 if (monitor)
498 {
499 port = new Monitor_port(server.registry(), _vq_max_num, num_ds, name,
500 mac_ptr);
501 port->set_monitor();
502
503 if (vlan_access)
504 warn.printf("vlan=access=<id> ignored on monitor ports!\n");
505 if (!vlan_trunk.empty())
506 warn.printf("vlan=trunk=... ignored on monitor ports!\n");
507 }
508 else
509 {
510 port = new Switch_port(server.registry(), _virtio_switch, _vq_max_num,
511 num_ds, name, mac_ptr);
512
513 if (vlan_access)
514 port->set_vlan_access(vlan_access);
515 else if (!vlan_trunk.empty())
516 port->set_vlan_trunk(vlan_trunk);
517 }
518
519 port->add_trusted_dataspaces(trusted_dataspaces);
520 if (!trusted_dataspaces->empty())
521 port->enable_trusted_ds_validation();
522
523 // hand port over to the switch
524 bool added = monitor ? _virtio_switch->add_monitor_port(port)
525 : _virtio_switch->add_port(port);
526 if (!added)
527 {
528 delete port;
529 return -L4_ENOMEM;
530 }
531 res = L4::Ipc::make_cap(port->obj_cap(), L4_CAP_FPAGE_RWSD);
532
533 info.printf(" Created port %s\n", name);
534 return L4_EOK;
535 };
536};
537
538
539int main(int argc, char *argv[])
540{
541 trusted_dataspaces = std::make_shared<Ds_vector>();
542 auto *opts = Options::parse_options(argc, argv, trusted_dataspaces);
543 if (!opts)
544 {
545 Err().printf("Error during command line parsing.\n");
546 return 1;
547 }
548
549 // Show welcome message if debug level is not set to quiet
550 if (Dbg(Dbg::Core, Dbg::Warn).is_active())
551 printf("Hello from l4virtio switch\n");
552
553 Virtio_switch *virtio_switch = new Virtio_switch(opts->get_max_ports());
554 Switch_factory *factory = new Switch_factory(virtio_switch,
555 opts->get_virtq_max_num());
556 L4::Cap<void> cap = server.registry()->register_obj(factory, "svr");
557 if (!cap.is_valid())
558 {
559 Err().printf("error registering switch\n");
560 return 2;
561 }
562
563 /*
564 * server loop will handle 4 types of events
565 * - Switch_factory
566 * - factory protocol
567 * - capability deletion
568 * - delegated to Virtio_switch::check_ports()
569 * - Switch_factory::Switch_port
570 * - irqs triggered by clients
571 * - delegated to Virtio_switch::handle_port_irq()
572 * - Virtio_net_transfer
573 * - timeouts for pending transfer requests added by
574 * Virtio_port::handle_request() via registered via
575 * L4::Epiface::server_iface()->add_timeout()
576 */
577 server.loop();
578 return 0;
579}
580
static Env const * env() noexcept
Returns the initial environment for the current task.
Definition env:95
A registry that manages server objects and their attached IPC gates for a single server loop for a sp...
L4::Cap< L4::Irq > register_irq_obj(L4::Epiface *o) override
Register a handler for an interrupt.
L4::Cap< void > register_obj(L4::Epiface *o, char const *service) override
Register a new server object to a pre-allocated receive endpoint.
A server loop object which has a Object_registry included.
bool is_valid() const noexcept
Test whether the capability is a valid capability index (i.e., not L4_INVALID_CAP).
Definition capability.h:57
C++ interface for capabilities.
Definition capability.h:219
Capability type for RPC interfaces (see L4::Cap<T>).
Definition ipc_types:699
List of variable-sized RPC parameters as received by the server.
Definition ipc_varg:254
Variably sized RPC argument.
Definition ipc_varg:97
Va_type< V >::Ret_value value() const
Definition ipc_varg:155
bool is_of() const
Definition ipc_varg:169
unsigned length() const
Get the size of the RPC argument.
Definition ipc_varg:114
void data(char const *d)
Set Varg to indirect data value (usually in UTCB)
Definition ipc_varg:120
Exception for an abstract runtime error.
Definition exceptions:129
The IPC interface for creating ports.
Definition main.cc:94
long op_create(L4::Factory::Rights, L4::Ipc::Cap< void > &res, l4_umword_t type, L4::Ipc::Varg_list_ref va)
Handle factory protocol.
Definition main.cc:396
Virtqueue * rx_q()
Getter for the receive queue.
Definition virtio_net.h:298
Virtqueue * tx_q()
Getter for the transmission queue.
Definition virtio_net.h:296
A Port on the Virtio Net Switch.
Definition port.h:36
bool tx_work_pending() const
Check whether there is any work pending on the transmission queue.
Definition port.h:191
Mac_addr mac() const
Get MAC address.
Definition port.h:156
void drop_requests()
Drop all requests pending in the transmission queue.
Definition port.h:225
The Virtio switch contains all ports and processes network requests.
Definition switch.h:29
bool add_monitor_port(Virtio_port *port)
Add a monitor port to the switch.
Definition switch.cc:55
bool add_port(Virtio_port *port)
Add a port to the switch.
Definition switch.cc:30
void handle_port_irq(Virtio_port *port)
Handle an incoming irq on a given port.
Definition switch.cc:143
int port_available(bool monitor)
Is there still a free port on this switch available?
Definition switch.h:123
Allocation free string class with explicit length field.
Definition string:31
bool empty() const
Check if the string has length zero.
Definition string:65
String substr(unsigned long idx, unsigned long len=~0UL) const
Substring of length len starting at idx.
Definition string:84
char const * Index
Character index type.
Definition string:35
int from_dec(INT *v) const
Convert decimal string to integer.
Definition string:228
Index starts_with(cxx::String const &c) const
Check if c is a prefix of string.
Definition string:155
int len() const
Length.
Definition string:58
Common factory related definitions.
unsigned int l4_size_t
Unsigned size type.
Definition l4int.h:24
unsigned long l4_umword_t
Unsigned machine word.
Definition l4int.h:40
unsigned char l4_uint8_t
Unsigned 8bit value.
Definition l4int.h:25
unsigned short int l4_uint16_t
Unsigned 16bit value.
Definition l4int.h:27
@ L4_EINVAL
Invalid argument.
Definition err.h:46
@ L4_EOK
Ok.
Definition err.h:32
@ L4_ENOMEM
No memory.
Definition err.h:39
@ L4_CAP_FPAGE_RWSD
Full rights for capability flexpages.
Definition __l4_fpage.h:212
@ L4_FP_DELETE_OBJ
Flag that indicates that an unmap operation on object capabilities shall try to delete the correspond...
Definition consts.h:201
@ L4_FP_ALL_SPACES
Flag to tell the unmap operation to revoke permissions from all child mappings including the mapping ...
Definition consts.h:187
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
Cap< T > make_cap(L4::Cap< T > cap, unsigned rights) noexcept
Make an L4::Ipc::Cap<T> for the given capability and rights.
Definition ipc_types:785
Debug C interface.
Cap< RPC_IFACE > obj_cap() const
Get the (typed) capability to this object.
Definition ipc_epiface:269
Epiface implementation for Kobject-based interface implementations.
Definition ipc_epiface:504
Base class for interface implementations.
Definition ipc_epiface:146
Epiface implementation for interrupt handlers.
Definition ipc_epiface:283
Common task related definitions.