L4Re Operating System Framework
Interface and Usage Documentation
Loading...
Searching...
No Matches
l4virtio
1// vi:ft=cpp
2/* SPDX-License-Identifier: MIT */
3/*
4 * Copyright (C) 2014-2025 Kernkonzept GmbH.
5 * Author(s): Alexander Warg <alexander.warg@kernkonzept.com>
6 * Manuel von Oltersdorff-Kalettka <manuel.kalettka@kernkonzept.com>
7 *
8 */
9#pragma once
10
11#include <algorithm>
12#include <limits.h>
13#include <memory>
14#include <vector>
15
16#include <l4/re/dataspace>
17#include <l4/re/util/debug>
18#include <l4/re/env>
19#include <l4/re/error_helper>
20#include <l4/re/rm>
21#include <l4/re/util/cap_alloc>
22#include <l4/re/util/shared_cap>
23#include <l4/re/util/unique_cap>
24
25#include <l4/sys/types.h>
26#include <l4/re/util/meta>
27
28#include <l4/cxx/bitfield>
29#include <l4/cxx/utils>
30#include <l4/cxx/unique_ptr>
31
32#include <l4/sys/cxx/ipc_legacy>
33
34#include "../l4virtio"
35#include "virtio"
36
40namespace L4virtio {
41namespace Svr {
42
53{
54public:
55 typedef Dev_status Status;
56 typedef Dev_features Features;
57
58private:
61
62 l4_uint32_t _vendor, _device, _qoffset, _nqueues;
63 l4_uint32_t _host_features[sizeof(l4virtio_config_hdr_t::dev_features_map)
64 / sizeof(l4_uint32_t)];
65 Cfg_cap _ds;
66 Cfg_region _config;
67 l4_addr_t _ds_offset = 0;
68
69 Status _status{0}; // status shadow, can be trusted by the device model
70
71 static l4_uint32_t align(l4_uint32_t x)
72 { return (x + 0xfU) & ~0xfU; }
73
74 void attach_n_init_cfg(Cfg_cap const &cfg, l4_addr_t offset)
75 {
76 L4Re::chksys(L4Re::Env::env()->rm()->attach(&_config, L4_PAGESIZE,
78 L4::Ipc::make_cap_rw(cfg.get()),
79 offset),
80 "Attach config space to local address space.");
81
82 _config->generation = 0;
83 memset(_config->driver_features_map, 0, sizeof(_config->driver_features_map));
84 memset(_host_features, 0, sizeof(_host_features));
85 set_host_feature(L4VIRTIO_FEATURE_VERSION_1);
86 reset_hdr();
87
88 _ds = cfg;
89 _ds_offset = offset;
90 }
91
92protected:
93 void volatile *get_priv_config() const
94 {
95 return l4virtio_device_config(_config.get());
96 }
97
98public:
99
113 unsigned cfg_size, l4_uint32_t num_queues = 0)
114 : _vendor(vendor), _device(device),
115 _qoffset(0x100 + align(cfg_size)),
116 _nqueues(num_queues)
117 {
118 using L4Re::Dataspace;
119 using L4Re::chkcap;
120 using L4Re::chksys;
121
122 if (sizeof(l4virtio_config_queue_t) * _nqueues + _qoffset > L4_PAGESIZE)
123 {
124 // too many queues does not fit into our page
125 _qoffset = 0;
126 _nqueues = 0;
127 }
128
129 auto cfg = chkcap(L4Re::Util::make_shared_cap<Dataspace>());
130 chksys(L4Re::Env::env()->mem_alloc()->alloc(L4_PAGESIZE, cfg.get()));
131
132 attach_n_init_cfg(cfg, 0);
133 }
134
146 Dev_config(Cfg_cap const &cfg, l4_addr_t cfg_offset,
147 l4_uint32_t vendor, l4_uint32_t device,
148 unsigned cfg_size, l4_uint32_t num_queues = 0)
149 : _vendor(vendor), _device(device),
150 _qoffset(0x100 + align(cfg_size)),
151 _nqueues(num_queues)
152 {
153 if (sizeof(l4virtio_config_queue_t) * _nqueues + _qoffset > L4_PAGESIZE)
154 {
155 // too many queues does not fit into our page
156 _qoffset = 0;
157 _nqueues = 0;
158 }
159
160 attach_n_init_cfg(cfg, cfg_offset);
161 }
162
163 void set_host_feature(unsigned feature)
164 { l4virtio_set_feature(_host_features, feature); }
165
166 void clear_host_feature(unsigned feature)
167 { l4virtio_clear_feature(_host_features, feature); }
168
169 bool get_host_feature(unsigned feature)
170 { return l4virtio_get_feature(_host_features, feature); }
171
172 bool get_guest_feature(unsigned feature)
173 { return l4virtio_get_feature(_config->driver_features_map, feature); }
174
175 l4_uint32_t &host_features(unsigned idx)
176 { return _host_features[idx]; }
177
178 l4_uint32_t host_features(unsigned idx) const
179 { return _host_features[idx]; }
180
187 void set_device_notify_index(unsigned idx)
188 { _config->cfg_device_notify_index = idx; }
189
194 { return _nqueues; }
195
207 l4_uint32_t guest_features(unsigned idx) const
208 { return _config->driver_features_map[idx]; }
209
222 { return _config->driver_features_map[idx] & _host_features[idx]; }
223
231 Status status() const { return _status; }
232
240 {
241 return hdr()->cmd;
242 }
243
251 {
252 const_cast<l4_uint32_t volatile &>(hdr()->cmd) = 0;
253 }
254
262 void set_status(Status status)
263 {
264 _status = status;
265 const_cast<l4_uint32_t volatile &>(hdr()->status) = status.raw;
266 }
267
275 {
276 const_cast<l4_uint32_t volatile &>(hdr()->irq_status) |= status;
277 }
278
286 {
287 _status.device_needs_reset() = 1;
288 const_cast<l4_uint32_t volatile &>(hdr()->status) = _status.raw;
289 }
290
296 {
297 if (sizeof(l4virtio_config_queue_t) * num_queues + _qoffset > L4_PAGESIZE)
298 // too many queues does not fit into our page
299 return false;
300
301 _nqueues = num_queues;
302 reset_hdr(true);
303 return true;
304 }
305
312 l4virtio_config_queue_t volatile const *qconfig(unsigned index) const
313 {
314 if (L4_UNLIKELY(_qoffset < sizeof (l4virtio_config_hdr_t)))
315 return 0;
316
317 if (L4_UNLIKELY(index >= _nqueues))
318 return 0;
319
320 return reinterpret_cast<l4virtio_config_queue_t const *>
321 (reinterpret_cast<char *>(_config.get()) + _qoffset) + index;
322 }
323
327 void reset_hdr(bool inc_generation = false) const
328 {
329 _config->magic = L4VIRTIO_MAGIC;
330 _config->version = 2;
331 _config->device = _device;
332 _config->vendor = _vendor;
333 _config->status = 0;
334 _config->irq_status = 0;
335 _config->num_queues = _nqueues;
336 _config->queues_offset = _qoffset;
337
338 memcpy(_config->dev_features_map, _host_features,
339 sizeof(_config->dev_features_map));
340 wmb();
341 if (inc_generation)
342 ++_config->generation;
343
344 }
345
356 bool reset_queue(unsigned index, unsigned num_max,
357 bool inc_generation = false,
358 unsigned device_notify_index = 0) const
359 {
360 l4virtio_config_queue_t volatile *qc;
361 // this function is allowed to write to the device config
362 qc = const_cast<l4virtio_config_queue_t volatile *>(qconfig(index));
363 if (L4_UNLIKELY(qc == 0))
364 return false;
365
366 qc->num_max = num_max;
367 qc->num = 0;
368 qc->ready = 0;
369 qc->device_notify_index = device_notify_index;
370 wmb();
371 if (inc_generation)
372 ++_config->generation;
373
374 return true;
375 }
376
381 l4virtio_config_hdr_t const volatile *hdr() const
382 { return _config.get(); }
383
388 L4::Cap<L4Re::Dataspace> ds() const { return _ds.get(); }
389
395 { return _ds_offset; }
396};
397
398
399template<typename PRIV_CONFIG>
400class Dev_config_t : public Dev_config
401{
402public:
404 typedef PRIV_CONFIG Priv_config;
405
417 Dev_config_t(l4_uint32_t vendor, l4_uint32_t device,
419 : Dev_config(vendor, device, sizeof(PRIV_CONFIG), num_queues)
420 {}
421
432 Dev_config_t(L4Re::Util::Shared_cap<L4Re::Dataspace> const &cfg,
433 l4_addr_t cfg_offset, l4_uint32_t vendor, l4_uint32_t device,
435 : Dev_config(cfg, cfg_offset, vendor, device, sizeof(PRIV_CONFIG),
437 {}
438
448 Priv_config volatile *priv_config() const
449 {
450 return static_cast<Priv_config volatile *>(get_priv_config());
451 }
452
453};
454
455struct No_custom_data {};
456
462template <typename DATA>
463class Driver_mem_region_t : public DATA
464{
465public:
466 struct Flags
467 {
468 Flags() = default;
469 explicit Flags(l4_uint32_t raw) : raw(raw) {}
470
471 l4_uint32_t raw;
472 CXX_BITFIELD_MEMBER(0, 0, rw, raw);
473 };
474
475private:
478
479 l4_uint64_t _drv_base;
480 l4_uint64_t _trans_offset;
481 l4_umword_t _size;
482 Flags _flags;
483
484 Ds_cap _ds;
485 l4_addr_t _ds_offset;
486
489
490 template<typename T>
491 T _local(l4_uint64_t addr) const
492 {
493 return reinterpret_cast<T>(addr - _trans_offset);
494 }
495
496public:
498 Driver_mem_region_t() : _size(0) {}
499
512 l4_addr_t offset, Ds_cap &&ds)
513 : _drv_base(l4_trunc_page(drv_base)), _size(0), _flags(0),
514 _ds_offset(l4_trunc_page(offset))
515 {
516 using L4Re::chksys;
517 using L4Re::Env;
518
520 // Sometimes we work with dataspaces that do not implement all dataspace
521 // methods and return an error instead. An example of such a dataspace is
522 // io's Vi::System_bus. We detect this case when the info method returns
523 // -L4_ENOSYS and simply assume the dataspace is good for us.
524 long err = ds->info(&ds_info);
525 if (err >= 0)
526 {
527 l4_addr_t ds_size = l4_round_page(ds_info.size);
528
529 if (ds_size < L4_PAGESIZE)
530 chksys(-L4_EINVAL, "DS too small");
531
532 if (_ds_offset >= ds_size)
533 chksys(-L4_ERANGE, "offset larger than DS size");
534
536 if (size > ds_size)
537 chksys(-L4_EINVAL, "size larger than DS size");
538
539 if (_ds_offset > ds_size - size)
540 chksys(-L4_EINVAL, "invalid offset or size");
541
542 // overflow check
543 if ((ULLONG_MAX - size) < _drv_base)
544 chksys(-L4_EINVAL, "invalid size");
545
546 _flags.rw() = (ds_info.flags & L4Re::Dataspace::F::W).raw != 0;
547 }
548 else if (err == -L4_ENOSYS)
549 {
550 _flags.rw() = true;
551 }
552 else
553 {
554 chksys(err, "getting data-space infos");
555 }
556
558 if (_flags.rw())
559 f |= L4Re::Rm::F::W;
560
561 // use a big alignment to save PT/TLB entries and kernel memory resources!
562 chksys(Env::env()->rm()->attach(&_local_base, size, f,
563 L4::Ipc::make_cap(ds.get(), _flags.rw()
566 _ds_offset, L4_SUPERPAGESHIFT));
567
568 _size = size;
569 _ds = cxx::move(ds);
570 _trans_offset = _drv_base - _local_base.get();
571 }
572
574 bool is_writable() const { return _flags.rw(); }
575
577 Flags flags() const { return _flags; }
578
580 bool empty() const
581 { return _size == 0; }
582
584 l4_uint64_t drv_base() const { return _drv_base; }
585
587 l4_addr_t local_base() const { return _local_base.get(); }
588
590 l4_umword_t size() const { return _size; }
591
593 l4_addr_t ds_offset() const { return _ds_offset; }
594
596 L4::Cap<L4Re::Dataspace> ds() const { return _ds.get(); }
597
606 {
607 if (base < _drv_base)
608 return false;
609
610 if (base > _drv_base + _size - 1)
611 return false;
612
613 if (size > _size)
614 return false;
615
616 if (base - _drv_base > _size - size)
617 return false;
618
619 return true;
620 }
621
628 template<typename T>
629 T *local(Ptr<T> p) const
630 { return _local<T*>(p.get()); }
631};
632
633typedef Driver_mem_region_t<No_custom_data> Driver_mem_region;
634
641template <typename DATA>
643{
644public:
645 typedef Driver_mem_region_t<DATA> Mem_region;
646
647private:
648 cxx::unique_ptr<Mem_region[]> _l;
649 unsigned _max;
650 unsigned _free;
651
652public:
655
657 Driver_mem_list_t() : _max(0), _free(0) {}
658
663 void init(unsigned max)
664 {
665 _l = cxx::make_unique<Driver_mem_region_t<DATA>[]>(max);
666 _max = max;
667 _free = 0;
668 }
669
671 bool full() const
672 { return _free == _max; }
673
682 Mem_region const *add(l4_uint64_t drv_base, l4_umword_t size,
683 l4_addr_t offset, Ds_cap &&ds)
684 {
685 if (full())
687
688 _l[_free++] = Mem_region(drv_base, size, offset, cxx::move(ds));
689 return &_l[_free - 1];
690 }
691
696 void remove(Mem_region const *r)
697 {
698 if (r < &_l[0] || r >= &_l[_free])
700
701 unsigned idx = r - &_l[0];
702
703 for (unsigned i = idx + 1; i < _free - 1; ++i)
704 _l[i] = cxx::move(_l[i + 1]);
705
706 _l[--_free] = Mem_region();
707 }
708
716 Mem_region *find(l4_uint64_t base, l4_umword_t size) const
717 {
718 return _find(base, size);
719 }
720
730 void load_desc(Virtqueue::Desc const &desc, Request_processor const *p,
731 Virtqueue::Desc const **table) const
732 {
733 Mem_region const *r = find(desc.addr.get(), desc.len);
734 if (L4_UNLIKELY(!r))
736
737 *table = static_cast<Virtqueue::Desc const *>(r->local(desc.addr));
738 }
739
750 void load_desc(Virtqueue::Desc const &desc, Request_processor const *p,
751 Mem_region const **data) const
752 {
753 Mem_region const *r = find(desc.addr.get(), desc.len);
754 if (L4_UNLIKELY(!r))
756
757 *data = r;
758 }
759
776 template<typename ARG>
777 void load_desc(Virtqueue::Desc const &desc, Request_processor const *p,
778 ARG *data) const
779 {
780 Mem_region *r = find(desc.addr.get(), desc.len);
781 if (L4_UNLIKELY(!r))
783
784 *data = ARG(r, desc, p);
785 }
786
787 Mem_region *begin() { return &_l[0]; }
788 Mem_region const *begin() const { return &_l[0]; }
789
790 Mem_region *end() { return &_l[_free]; }
791 Mem_region const *end() const { return &_l[_free]; }
792
793private:
794 Mem_region *_find(l4_uint64_t base, l4_umword_t size) const
795 {
796 for (unsigned i = 0; i < _free; ++i)
797 if (_l[i].contains(base, size))
798 return &_l[i];
799 return 0;
800 }
801
802
803};
804
805typedef Driver_mem_list_t<No_custom_data> Driver_mem_list;
806
813template<typename DATA>
815{
816public:
817 typedef Driver_mem_list_t<DATA> Mem_list;
818
819protected:
820 Mem_list _mem_info;
821
822private:
823 Dev_config *_device_config;
824
825 using Ds_vector = std::vector<L4::Cap<L4Re::Dataspace>>;
827 std::shared_ptr<Ds_vector const> _trusted_ds_caps;
828
830 bool _trusted_ds_validation_enabled = false;
831
832public:
833 L4_RPC_LEGACY_DISPATCH(L4virtio::Device);
834 template<typename IOS> int virtio_dispatch(unsigned r, IOS &ios)
835 { return dispatch(r, ios); }
836
838 virtual void reset() = 0;
839
841 virtual bool check_features()
842 { return true; }
843
845 virtual bool check_queues() = 0;
846
848 virtual int reconfig_queue(unsigned idx) = 0;
849
851 virtual void cfg_changed(unsigned /* reg */) {};
852
855 { L4Re::chksys(-L4_ENOSYS, "Legacy single IRQ interface not implemented."); }
856
858 virtual void trigger_driver_config_irq() = 0;
859
862 {
863 L4Re::chksys(-L4_ENOSYS, "Legacy single IRQ interface not implemented.");
864 return L4::Cap<L4::Irq>();
865 }
866
873 virtual void register_driver_irq(unsigned idx)
874 {
875 if (idx != 0)
876 L4Re::chksys(-L4_ENOSYS, "Multi IRQ interface not implemented.");
877
879 }
880
888 {
889 if (idx != 0)
890 L4Re::chksys(-L4_ENOSYS, "Multi IRQ interface not implemented.");
891
892 return device_notify_irq();
893 }
894
896 virtual unsigned num_events_supported() const
897 { return 1; }
898
899 virtual L4::Ipc_svr::Server_iface *server_iface() const = 0;
900
901 // Make it possible to react to changes in the list of memory regions
902 virtual void check_mem_list() {};
903
907 Device_t(Dev_config *dev_config)
908 : _device_config(dev_config)
909 {}
910
914 Mem_list const *mem_info() const
915 { return &_mem_info; };
916
917 long op_set_status(L4virtio::Device::Rights, unsigned status)
918 { return _set_status(status); }
919
920 long op_config_queue(L4virtio::Device::Rights, unsigned queue)
921 {
922 Dev_config::Status status = _device_config->status();
923 if (status.fail_state() || !status.acked() || !status.driver())
924 return -L4_EIO;
925
926 return reconfig_queue(queue);
927 }
928
929 long op_register_ds(L4virtio::Device::Rights,
930 L4::Ipc::Snd_fpage ds_cap_fp, l4_uint64_t ds_base,
931 l4_umword_t offset, l4_umword_t sz)
932 {
933 L4Re::Util::Dbg()
934 .printf("Registering dataspace from 0x%llx with %lu KiB, offset 0x%lx\n",
935 ds_base, sz >> 10, offset);
936
937 _check_n_init_shm(ds_cap_fp, ds_base, sz, offset);
938
939 return 0;
940 }
941
942 long op_device_config(L4virtio::Device::Rights,
943 L4::Ipc::Cap<L4Re::Dataspace> &config_ds,
944 l4_addr_t &ds_offset)
945 {
946 L4Re::Util::Dbg()
947 .printf("register client: config DS: %lx\n", _device_config->ds().cap());
948
949 config_ds = L4::Ipc::make_cap(_device_config->ds(), L4_CAP_FPAGE_RW);
950 ds_offset = _device_config->ds_offset();
951 return 0;
952 }
953
954 long op_device_notification_irq(L4virtio::Device::Rights,
955 unsigned idx,
956 L4::Ipc::Cap<L4::Triggerable> &irq)
957 {
958 auto cap = device_notify_irq(idx);
959
960 if (!cap.is_valid())
961 return -L4_EINVAL;
962
964 return L4_EOK;
965 }
966
967 int op_bind(L4::Icu::Rights, l4_umword_t idx, L4::Ipc::Snd_fpage irq_cap_fp)
968 {
969 if (idx >= num_events_supported())
970 return -L4_ERANGE;
971
972 if (!irq_cap_fp.cap_received())
973 return -L4_EINVAL;
974
976
977 return L4_EOK;
978 }
979
980 int op_unbind(L4::Icu::Rights, l4_umword_t, L4::Ipc::Snd_fpage)
981 {
982 return -L4_ENOSYS;
983 }
984
985 int op_info(L4::Icu::Rights, L4::Icu::_Info &info)
986 {
987 info.features = 0;
988 info.nr_irqs = num_events_supported();
989 info.nr_msis = 0;
990
991 return L4_EOK;
992 }
993
994 int op_msi_info(L4::Icu::Rights, l4_umword_t, l4_uint64_t, l4_icu_msi_info_t &)
995 { return -L4_ENOSYS; }
996
997 int op_mask(L4::Icu::Rights, l4_umword_t)
998 { return -L4_ENOSYS; }
999
1000 int op_unmask(L4::Icu::Rights, l4_umword_t)
1001 { return -L4_ENOREPLY; }
1002
1003 int op_set_mode(L4::Icu::Rights, l4_umword_t, l4_umword_t)
1004 { return -L4_ENOSYS; }
1005
1019 void reset_queue_config(unsigned idx, unsigned num_max,
1020 bool inc_generation = false,
1021 unsigned device_notify_index = 0)
1022 {
1023 _device_config->reset_queue(idx, num_max, inc_generation,
1024 device_notify_index);
1025 }
1026
1031 void init_mem_info(unsigned num)
1032 {
1033 _mem_info.init(num);
1034 }
1035
1044 {
1045 reset();
1046 _device_config->set_device_needs_reset();
1047
1048 // the device MUST NOT notify the driver before DRIVER_OK.
1049 if (_device_config->status().driver_ok())
1051 }
1052
1066 bool setup_queue(Virtqueue *q, unsigned qn, unsigned num_max)
1067 {
1068 l4virtio_config_queue_t volatile const *qc;
1069 qc = _device_config->qconfig(qn);
1070 if (L4_UNLIKELY(qc == 0))
1071 return false;
1072
1073 if (!qc->ready)
1074 {
1075 q->disable();
1076 return true;
1077 }
1078
1079 // read to local variables before check
1080 l4_uint32_t num = qc->num;
1081 l4_uint64_t desc = qc->desc_addr;
1082 l4_uint64_t avail = qc->avail_addr;
1083 l4_uint64_t used = qc->used_addr;
1084
1085 if (0)
1086 printf("%p: setup queue: num=0x%x max_num=0x%x desc=0x%llx avail=0x%llx used=0x%llx\n",
1087 this, num, num_max, desc, avail, used);
1088
1089 if (!num || num > num_max)
1090 return false;
1091
1092 // num must be power of two
1093 if (num & (num - 1))
1094 return false;
1095
1096 if (desc & 0xf)
1097 return false;
1098
1099 if (avail & 0x1)
1100 return false;
1101
1102 if (used & 0x3)
1103 return false;
1104
1105 auto const *desc_info = _mem_info.find(desc, Virtqueue::desc_size(num));
1106 if (L4_UNLIKELY(!desc_info))
1107 return false;
1108
1109 auto const *avail_info = _mem_info.find(avail, Virtqueue::avail_size(num));
1110 if (L4_UNLIKELY(!avail_info))
1111 return false;
1112
1113 auto const *used_info = _mem_info.find(used, Virtqueue::used_size(num));
1114 if (L4_UNLIKELY(!used_info || !used_info->is_writable()))
1115 return false;
1116
1117 L4Re::Util::Dbg()
1118 .printf("shm=[%llx-%llx] local=[%lx-%lx] desc=[%llx-%llx] (%p-%p)\n",
1119 desc_info->drv_base(), desc_info->drv_base() + desc_info->size() - 1,
1120 desc_info->local_base(),
1121 desc_info->local_base() + desc_info->size() - 1,
1122 desc, desc + Virtqueue::desc_size(num),
1123 desc_info->local(Ptr<char>(desc)),
1124 desc_info->local(Ptr<char>(desc)) + Virtqueue::desc_size(num));
1125
1126 L4Re::Util::Dbg()
1127 .printf("shm=[%llx-%llx] local=[%lx-%lx] avail=[%llx-%llx] (%p-%p)\n",
1128 avail_info->drv_base(), avail_info->drv_base() + avail_info->size() - 1,
1129 avail_info->local_base(),
1130 avail_info->local_base() + avail_info->size() - 1,
1131 avail, avail + Virtqueue::avail_size(num),
1132 avail_info->local(Ptr<char>(avail)),
1133 avail_info->local(Ptr<char>(avail)) + Virtqueue::avail_size(num));
1134
1135 L4Re::Util::Dbg()
1136 .printf("shm=[%llx-%llx] local=[%lx-%lx] used=[%llx-%llx] (%p-%p)\n",
1137 used_info->drv_base(), used_info->drv_base() + used_info->size() - 1,
1138 used_info->local_base(),
1139 used_info->local_base() + used_info->size() - 1,
1140 used, used + Virtqueue::used_size(num),
1141 used_info->local(Ptr<char>(used)),
1142 used_info->local(Ptr<char>(used)) + Virtqueue::used_size(num));
1143
1144 q->setup(num, desc_info->local(Ptr<void>(desc)),
1145 avail_info->local(Ptr<void>(avail)),
1146 used_info->local(Ptr<void>(used)));
1147 return true;
1148 }
1149
1150 void check_n_init_shm(L4Re::Util::Unique_cap<L4Re::Dataspace> &&shm,
1151 l4_uint64_t base, l4_umword_t size, l4_addr_t offset)
1152 {
1153 if (_mem_info.full())
1155
1156 auto const *i = _mem_info.add(base, size, offset, cxx::move(shm));
1157 L4Re::Util::Dbg()
1158 .printf("PORT[%p]: DMA guest [%llx-%llx] local [%lx-%lx] offset %lx\n",
1159 this, i->drv_base(), i->drv_base() + i->size() - 1,
1160 i->local_base(),
1161 i->local_base() + i->size() - 1,
1162 i->ds_offset());
1163 }
1164
1174 {
1175 l4_uint32_t cmd = _device_config->get_cmd();
1176 if (L4_LIKELY(!(cmd & L4VIRTIO_CMD_MASK)))
1177 return false;
1178
1179 switch (cmd & L4VIRTIO_CMD_MASK)
1180 {
1182 _set_status(cmd & ~L4VIRTIO_CMD_MASK);
1183 break;
1184
1187 break;
1188
1191 break;
1192
1193 default:
1194 // unknown command
1195 break;
1196 }
1197
1198 _device_config->reset_cmd();
1199
1200 return true;
1201 }
1202
1207 {
1208 _trusted_ds_validation_enabled = true;
1209 }
1210
1216 void
1217 add_trusted_dataspaces(std::shared_ptr<Ds_vector const> ds)
1218 {
1219 _trusted_ds_caps = ds;
1220 }
1221
1222
1223private:
1235 long validate_ds(L4::Cap<L4Re::Dataspace> ds)
1236 {
1237 if (!_trusted_ds_caps)
1238 return -L4_EINVAL;
1239 if (std::any_of(_trusted_ds_caps->cbegin(), _trusted_ds_caps->cend(),
1240 [&ds](L4::Cap<L4Re::Dataspace> cap)
1241 {
1242 return L4Re::Env::env()->task()
1243 ->cap_equal(ds, cap).label() == 1;
1244 }
1245 ))
1246 {
1247 return L4_EOK;
1248 }
1249 return -L4_EINVAL;
1250 }
1251
1252 void _check_n_init_shm(L4::Ipc::Snd_fpage shm_cap_fp,
1253 l4_uint64_t base, l4_umword_t size, l4_addr_t offset)
1254 {
1255 if (!shm_cap_fp.cap_received())
1257
1259 L4Re::chkcap(server_iface()->template rcv_cap<L4Re::Dataspace>(0)));
1260 L4Re::chksys(server_iface()->realloc_rcv_cap(0));
1261
1262 if (_trusted_ds_validation_enabled)
1263 L4Re::chksys(validate_ds(ds.get()), "Validating the dataspace.");
1264
1265 check_n_init_shm(cxx::move(ds), base, size, offset);
1266
1267 check_mem_list();
1268 }
1269
1270 bool check_features_internal()
1271 {
1272 static_assert(sizeof(l4virtio_config_hdr_t::driver_features_map)
1273 == sizeof(l4virtio_config_hdr_t::dev_features_map),
1274 "Driver and device feature maps must be of the same size");
1275
1276 // From the Virtio 1.0 specification 6.1 Driver Requirements and 6.2 Device
1277 // Requirements: A driver MUST accept VIRTIO_F_VERSION_1 if it is offered.
1278 // A device MUST offer VIRTIO_F_VERSION_1. A device MAY fail to operate
1279 // further if VIRTIO_F_VERSION_1 is not accepted.
1280 //
1281 // The L4virtio implementation does not support legacy interfaces so we
1282 // fail here if the Virtio 1.0 feature was not accepted.
1283 if (!_device_config->get_guest_feature(L4VIRTIO_FEATURE_VERSION_1))
1284 return false;
1285
1286 for (auto i = 0u;
1287 i < sizeof(l4virtio_config_hdr_t::driver_features_map)
1288 / sizeof(l4virtio_config_hdr_t::driver_features_map[0]);
1289 i++)
1290 {
1291 // Driver must not accept features that were not offered by device
1292 if (_device_config->guest_features(i)
1293 & ~_device_config->host_features(i))
1294 return false;
1295 }
1296 return check_features();
1297 }
1298
1320 void check_and_update_status(Dev_config::Status status)
1321 {
1322 // snapshot of current status
1323 Dev_config::Status current_status = _device_config->status();
1324
1325 // handle reset
1326 if (!status.raw)
1327 {
1328 _device_config->set_status(status);
1329 return;
1330 }
1331
1332 // Do no further processing in case of driver or device failure. If FAILED
1333 // or DEVICE_NEEDS_RESET are set only these fail_state bits will be set in
1334 // addition to the current status bits already set.
1335 if (current_status.fail_state() || status.fail_state())
1336 {
1337 if (current_status.fail_state() != status.fail_state())
1338 {
1339 current_status.fail_state() =
1340 current_status.fail_state() | status.fail_state();
1341 _device_config->set_status(current_status);
1342 }
1343 return;
1344 }
1345
1346 // Enforce init sequence ACKNOWLEDGE, DRIVER, FEATURES_OK, DRIVER_OK.
1347 // We do not enforce that only one additional new bit is set per call.
1348 if ((!status.acked() && status.driver())
1349 || (!status.driver() && status.features_ok())
1350 || (!status.features_ok() && status.driver_ok()))
1351 {
1352 current_status.device_needs_reset() = 1;
1353 _device_config->set_status(current_status);
1354 return;
1355 }
1356
1357 // only check feature compatibility before DRIVER_OK is set
1358 if (status.features_ok() && !status.driver_ok()
1359 && !check_features_internal())
1360 status.features_ok() = 0;
1361
1362 // Note that if FEATURES_OK and DRIVER_OK are both updated to being set
1363 // at the same time the above check_features_internal() is skipped; this is
1364 // considered undefined behaviour but it is not prevented.
1365 if (status.running() && !check_queues())
1366 {
1367 current_status.device_needs_reset() = 1;
1368 _device_config->set_status(current_status);
1369 return;
1370 }
1371
1372 _device_config->set_status(status);
1373 }
1374
1387 int _set_status(unsigned new_status)
1388 {
1389 if (new_status == 0)
1390 {
1391 L4Re::Util::Dbg().printf("Resetting device\n");
1392 reset();
1393 _device_config->reset_hdr(true);
1394 }
1395
1396 Dev_config::Status status(new_status);
1397 check_and_update_status(status);
1398
1399 return 0;
1400 }
1401
1402};
1403
1404typedef Device_t<No_custom_data> Device;
1405
1406} // namespace Svr
1407
1408}
Interface for memory-like objects.
Definition dataspace:53
C++ interface of the initial environment that is provided to an L4 task.
Definition env:79
static Env const * env() noexcept
Returns the initial environment for the current task.
Definition env:96
Unique region.
Definition rm:435
C++ interface for capabilities.
Definition capability.h:224
Send item or return item.
Definition ipc_types:324
bool cap_received() const noexcept
(Defined for return items only.) Check if at least one object capability has been mapped for this ite...
Definition ipc_types:496
Interface for server-loop related functions.
Definition ipc_epiface:37
IPC interface for virtio over L4 IPC.
Definition l4virtio:42
Pointer used in virtio descriptors.
Definition virtqueue:48
l4_uint64_t get() const
Definition virtqueue:62
Abstraction for L4-Virtio device config memory.
Definition l4virtio:53
void set_status(Status status)
Set device status register.
Definition l4virtio:262
l4virtio_config_hdr_t const volatile * hdr() const
Get a read-only pointer to the config header.
Definition l4virtio:381
void set_device_notify_index(unsigned idx)
Set index of interrupt that driver should trigger for config notifications.
Definition l4virtio:187
l4_uint32_t get_cmd() const
Get the value from the cmd register.
Definition l4virtio:239
Dev_config(l4_uint32_t vendor, l4_uint32_t device, unsigned cfg_size, l4_uint32_t num_queues=0)
Create a L4-Virtio config data space.
Definition l4virtio:112
bool change_queue_config(l4_uint32_t num_queues)
Setup new queue configuration.
Definition l4virtio:295
void add_irq_status(l4_uint32_t status)
Adds irq status bit.
Definition l4virtio:274
L4::Cap< L4Re::Dataspace > ds() const
Get data-space capability for the shared config data space.
Definition l4virtio:388
bool reset_queue(unsigned index, unsigned num_max, bool inc_generation=false, unsigned device_notify_index=0) const
Reset queue config for the given queue.
Definition l4virtio:356
Status status() const
Get current device status (trusted).
Definition l4virtio:231
void reset_cmd()
Reset the cmd register after execution of a command.
Definition l4virtio:250
l4_uint32_t num_queues() const
Return the number of queues currently usable.
Definition l4virtio:193
l4virtio_config_queue_t volatile const * qconfig(unsigned index) const
Get queue read-only config data for queue with the given index.
Definition l4virtio:312
void reset_hdr(bool inc_generation=false) const
Reset the config header to the initial contents.
Definition l4virtio:327
Dev_config(Cfg_cap const &cfg, l4_addr_t cfg_offset, l4_uint32_t vendor, l4_uint32_t device, unsigned cfg_size, l4_uint32_t num_queues=0)
Setup an L4-Virtio config space in an existing data space.
Definition l4virtio:146
l4_uint32_t guest_features(unsigned idx) const
Return a specific set of guest features.
Definition l4virtio:207
l4_uint32_t negotiated_features(unsigned idx) const
Compute a specific set of negotiated features.
Definition l4virtio:221
l4_addr_t ds_offset() const
Return the offset into the config dataspace where the device configuration starts.
Definition l4virtio:394
void set_device_needs_reset()
Set DEVICE_NEEDS_RESET bit in device status register.
Definition l4virtio:285
Server-side L4-VIRTIO device stub.
Definition l4virtio:815
void device_error()
Transition device into DEVICE_NEEDS_RESET state.
Definition l4virtio:1043
virtual void cfg_changed(unsigned)
callback for client device configuration changes
Definition l4virtio:851
void add_trusted_dataspaces(std::shared_ptr< Ds_vector const > ds)
Provide a list of trusted dataspaces that can be used for validation.
Definition l4virtio:1217
virtual void reset()=0
reset callback, called for doing a device reset
virtual void trigger_driver_config_irq()=0
callback for triggering configuration change notification IRQ
bool handle_mem_cmd_write()
Check for a value in the cmd register and handle a write.
Definition l4virtio:1173
virtual void register_driver_irq(unsigned idx)
Callback for registering an notification IRQ (multi IRQ).
Definition l4virtio:873
Device_t(Dev_config *dev_config)
Make a device for the given config.
Definition l4virtio:907
virtual void register_single_driver_irq()
callback for registering a single guest IRQ for all queues (old-style)
Definition l4virtio:854
virtual bool check_features()
callback for checking the subset of accepted features
Definition l4virtio:841
virtual unsigned num_events_supported() const
Return the highest notification index supported.
Definition l4virtio:896
bool setup_queue(Virtqueue *q, unsigned qn, unsigned num_max)
Enable/disable the specified queue.
Definition l4virtio:1066
void init_mem_info(unsigned num)
Initialize the memory region list to the given maximum.
Definition l4virtio:1031
Mem_list const * mem_info() const
Get the memory region list used for this device.
Definition l4virtio:914
virtual int reconfig_queue(unsigned idx)=0
callback for client queue-config request
void enable_trusted_ds_validation()
Enable trusted dataspace validation.
Definition l4virtio:1206
void reset_queue_config(unsigned idx, unsigned num_max, bool inc_generation=false, unsigned device_notify_index=0)
Trigger reset for the configuration space for queue idx.
Definition l4virtio:1019
virtual L4::Cap< L4::Irq > device_notify_irq(unsigned idx)
Callback to gather the device notification IRQ (multi IRQ).
Definition l4virtio:887
virtual L4::Cap< L4::Irq > device_notify_irq() const
callback to gather the device notification IRQ (old-style)
Definition l4virtio:861
virtual bool check_queues()=0
callback for checking if the queues at DRIVER_OK transition
List of driver memory regions assigned to a single L4-VIRTIO transport instance.
Definition l4virtio:643
Mem_region * find(l4_uint64_t base, l4_umword_t size) const
Find memory region containing the given driver address region.
Definition l4virtio:716
void load_desc(Virtqueue::Desc const &desc, Request_processor const *p, Virtqueue::Desc const **table) const
Default implementation for loading an indirect descriptor.
Definition l4virtio:730
void remove(Mem_region const *r)
Remove the given region from the list.
Definition l4virtio:696
Mem_region const * add(l4_uint64_t drv_base, l4_umword_t size, l4_addr_t offset, Ds_cap &&ds)
Add a new region to the list.
Definition l4virtio:682
L4Re::Util::Unique_cap< L4Re::Dataspace > Ds_cap
type for storing a data-space capability internally
Definition l4virtio:654
void init(unsigned max)
Make a fresh list with capacity max.
Definition l4virtio:663
void load_desc(Virtqueue::Desc const &desc, Request_processor const *p, Mem_region const **data) const
Default implementation returning the Driver_mem_region.
Definition l4virtio:750
void load_desc(Virtqueue::Desc const &desc, Request_processor const *p, ARG *data) const
Default implementation returning generic information.
Definition l4virtio:777
Driver_mem_list_t()
Make an empty, zero capacity list.
Definition l4virtio:657
Region of driver memory, that shall be managed locally.
Definition l4virtio:464
bool contains(l4_uint64_t base, l4_umword_t size) const
Test if the given driver address range is within this region.
Definition l4virtio:605
l4_addr_t ds_offset() const
Definition l4virtio:593
Driver_mem_region_t()
Make default empty memory region.
Definition l4virtio:498
l4_addr_t local_base() const
Definition l4virtio:587
Driver_mem_region_t(l4_uint64_t drv_base, l4_umword_t size, l4_addr_t offset, Ds_cap &&ds)
Make a local memory region for the given driver values.
Definition l4virtio:511
L4::Cap< L4Re::Dataspace > ds() const
Definition l4virtio:596
T * local(Ptr< T > p) const
Get the local address for driver address p.
Definition l4virtio:629
Encapsulate the state for processing a VIRTIO request.
Definition virtio:473
Virtqueue implementation for the device.
Definition virtio:88
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
void disable()
Completely disable the queue.
Definition virtqueue:224
void setup(unsigned num, void *desc, void *avail, void *used)
Enable this queue.
Definition virtqueue:349
static unsigned long desc_size(unsigned num)
Calculate the size of the descriptor table for num entries.
Definition virtqueue:261
static unsigned long used_size(unsigned num)
Calculate the size of the used ring for num entries.
Definition virtqueue:298
static unsigned long avail_size(unsigned num)
Calculate the size of the available ring for num entries.
Definition virtqueue:279
Dataspace interface.
Environment interface.
Error helper.
unsigned long l4_umword_t
Unsigned machine word.
Definition l4int.h:40
unsigned long l4_addr_t
Address type.
Definition l4int.h:34
unsigned int l4_uint32_t
Unsigned 32bit value.
Definition l4int.h:29
unsigned long long l4_uint64_t
Unsigned 64bit value.
Definition l4int.h:31
@ L4_ERANGE
Range error.
Definition err.h:49
@ L4_ENOSYS
No sys.
Definition err.h:51
@ L4_EINVAL
Invalid argument.
Definition err.h:47
@ L4_ENOREPLY
No reply.
Definition err.h:56
@ L4_EIO
I/O error.
Definition err.h:36
@ L4_EOK
Ok.
Definition err.h:33
@ L4_ENOMEM
No memory.
Definition err.h:40
@ L4_CAP_FPAGE_RO
Read right for capability flexpages.
Definition __l4_fpage.h:176
@ L4_CAP_FPAGE_RW
Read and interface specific 'W' right for capability flexpages.
Definition __l4_fpage.h:192
#define L4_SUPERPAGESHIFT
Size of a large page, log2-based.
Definition consts.h:31
l4_addr_t l4_trunc_page(l4_addr_t address) L4_NOTHROW
Round an address down to the next lower page boundary.
Definition consts.h:446
l4_addr_t l4_round_page(l4_addr_t address) L4_NOTHROW
Round address up to the next page.
Definition consts.h:471
#define L4_PAGESIZE
Minimal page size (in bytes).
Definition consts.h:389
#define L4_UNLIKELY(x)
Expression is unlikely to execute.
Definition compiler.h:289
#define L4_LIKELY(x)
Expression is likely to execute.
Definition compiler.h:288
void * l4virtio_device_config(l4virtio_config_hdr_t const *cfg)
Get the pointer to the device configuration.
Definition virtio.h:266
void l4virtio_clear_feature(l4_uint32_t *feature_map, unsigned feat)
Clear the given feature bit in a feature map.
Definition virtio.h:287
void l4virtio_set_feature(l4_uint32_t *feature_map, unsigned feat)
Set the given feature bit in a feature map.
Definition virtio.h:275
unsigned l4virtio_get_feature(l4_uint32_t *feature_map, unsigned feat)
Check if the given bit in a feature map is set.
Definition virtio.h:299
@ L4VIRTIO_FEATURE_VERSION_1
Virtio protocol version 1 supported. Must be 1 for L4virtio.
Definition virtio.h:104
@ L4VIRTIO_CMD_SET_STATUS
Set the status register.
Definition virtio.h:125
@ L4VIRTIO_CMD_CFG_QUEUE
Configure a queue.
Definition virtio.h:126
@ L4VIRTIO_CMD_MASK
Mask to get command bits.
Definition virtio.h:129
@ L4VIRTIO_CMD_CFG_CHANGED
Device config changed.
Definition virtio.h:127
struct l4_icu_msi_info_t l4_icu_msi_info_t
Info to use for a specific MSI.
Common L4 ABI Data Types.
Shared_cap< T > make_shared_cap()
Allocate a capability slot and wrap it in a Shared_cap.
Definition shared_cap:61
L4::Detail::Shared_cap_impl< T, L4Re::Util::Smart_count_cap< L4_FP_ALL_SPACES > > Shared_cap
Shared capability that implements automatic free and unmap of the capability selector.
Definition shared_cap:48
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 selector.
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
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
Cap< T > make_cap_rw(L4::Cap< T > cap) noexcept
Make an L4::Ipc::Cap<T> for the given capability with L4_CAP_FPAGE_RW rights.
Definition ipc_types:795
L4-VIRTIO Transport C++ API.
Definition l4virtio:26
Region mapper interface.
@ W
Request write-only mapping.
Definition dataspace:79
Information about the dataspace.
Definition dataspace:129
@ RW
Readable and writable region.
Definition rm:139
@ R
Readable region.
Definition rm:133
@ W
Writable region.
Definition rm:135
@ Search_addr
Search for a suitable address range.
Definition rm:113
Exception used by Queue to indicate descriptor errors.
Definition virtio:398
@ Bad_address
Address cannot be translated.
Definition virtio:402
Type for device feature bitmap.
Definition virtio:67
Type of the device status register.
Definition virtio:33
L4-VIRTIO config header, provided in shared data space.
Definition virtio.h:136
Queue configuration entry.
Definition virtio.h:227
l4_uint16_t device_notify_index
R: Event index to be used by the driver (driver to device).
Definition virtio.h:244
l4_uint64_t avail_addr
W: address of available ring.
Definition virtio.h:240
l4_uint64_t desc_addr
W: address of descriptor table.
Definition virtio.h:239
l4_uint64_t used_addr
W: address of used ring.
Definition virtio.h:241
l4_uint16_t ready
RW: queue ready flag (read-write).
Definition virtio.h:234
l4_uint16_t num_max
R: maximum number of descriptors supported by this queue.
Definition virtio.h:229
l4_uint16_t num
RW: number of descriptors configured for this queue.
Definition virtio.h:231
Capability allocator.
Shared_cap / Shared_del_cap.
Unique_cap / Unique_del_cap.