L4Re – L4 Runtime Environment
l4virtio
1 // vi:ft=cpp
2 /* SPDX-License-Identifier: GPL-2.0-only or License-Ref-kk-custom */
3 /*
4  * Copyright (C) 2014-2020 Kernkonzept GmbH.
5  * Author(s): Alexander Warg <alexander.warg@kernkonzept.com>
6  *
7  */
8 #pragma once
9 
10 #include <limits.h>
11 
12 #include <l4/re/dataspace>
13 #include <l4/re/util/debug>
14 #include <l4/re/env>
15 #include <l4/re/error_helper>
16 #include <l4/re/rm>
17 #include <l4/re/util/cap_alloc>
18 #include <l4/re/util/shared_cap>
19 #include <l4/re/util/unique_cap>
20 
21 #include <l4/sys/types.h>
22 #include <l4/re/util/meta>
23 
24 #include <l4/cxx/bitfield>
25 #include <l4/cxx/utils>
26 #include <l4/cxx/unique_ptr>
27 
28 #include <l4/sys/cxx/ipc_legacy>
29 
30 #include "../l4virtio"
31 #include "virtio"
32 
36 namespace L4virtio {
37 namespace Svr {
38 
49 {
50 public:
51  typedef Dev_status Status;
52  typedef Dev_features Features;
53 
54 private:
55  typedef L4Re::Rm::Unique_region< l4virtio_config_hdr_t*> Cfg_region;
57 
58  l4_uint32_t _vendor, _device, _qoffset, _nqueues;
59  l4_uint32_t _host_features[sizeof(l4virtio_config_hdr_t::dev_features_map)
60  / sizeof(l4_uint32_t)];
61  Cfg_cap _ds;
62  Cfg_region _config;
63  l4_addr_t _ds_offset = 0;
64 
65  Status _status{0}; // status shadow, can be trusted by the device model
66 
67  static l4_uint32_t align(l4_uint32_t x)
68  { return (x + 0xfU) & ~0xfU; }
69 
70  void attach_n_init_cfg(Cfg_cap const &cfg, l4_addr_t offset)
71  {
72  L4Re::chksys(L4Re::Env::env()->rm()->attach(&_config, L4_PAGESIZE,
73  L4Re::Rm::F::Search_addr | L4Re::Rm::F::RW,
74  L4::Ipc::make_cap_rw(cfg.get()),
75  offset),
76  "Attach config space to local address space.");
77 
78  _config->generation = 0;
79  memset(_config->driver_features_map, 0, sizeof(_config->driver_features_map));
80  memset(_host_features, 0, sizeof(_host_features));
81  set_host_feature(L4VIRTIO_FEATURE_VERSION_1);
82  reset_hdr();
83 
84  _ds = cfg;
85  _ds_offset = offset;
86  }
87 
88 protected:
89  void volatile *get_priv_config() const
90  {
91  return l4virtio_device_config(_config.get());
92  }
93 
94 public:
95 
109  unsigned cfg_size, l4_uint32_t num_queues = 0)
110  : _vendor(vendor), _device(device),
111  _qoffset(0x100 + align(cfg_size)),
112  _nqueues(num_queues)
113  {
114  using L4Re::Dataspace;
115  using L4Re::chkcap;
116  using L4Re::chksys;
117 
118  if (sizeof(l4virtio_config_queue_t) * _nqueues + _qoffset > L4_PAGESIZE)
119  {
120  // too many queues does not fit into our page
121  _qoffset = 0;
122  _nqueues = 0;
123  }
124 
125  auto cfg = chkcap(L4Re::Util::make_shared_cap<Dataspace>());
126  chksys(L4Re::Env::env()->mem_alloc()->alloc(L4_PAGESIZE, cfg.get()));
127 
128  attach_n_init_cfg(cfg, 0);
129  }
130 
142  Dev_config(Cfg_cap const &cfg, l4_addr_t cfg_offset,
143  l4_uint32_t vendor, l4_uint32_t device,
144  unsigned cfg_size, l4_uint32_t num_queues = 0)
145  : _vendor(vendor), _device(device),
146  _qoffset(0x100 + align(cfg_size)),
147  _nqueues(num_queues)
148  {
149  if (sizeof(l4virtio_config_queue_t) * _nqueues + _qoffset > L4_PAGESIZE)
150  {
151  // too many queues does not fit into our page
152  _qoffset = 0;
153  _nqueues = 0;
154  }
155 
156  attach_n_init_cfg(cfg, cfg_offset);
157  }
158 
159  void set_host_feature(unsigned feature)
160  { l4virtio_set_feature(_host_features, feature); }
161 
162  void clear_host_feature(unsigned feature)
163  { l4virtio_clear_feature(_host_features, feature); }
164 
165  bool get_host_feature(unsigned feature)
166  { return l4virtio_get_feature(_host_features, feature); }
167 
168  bool get_guest_feature(unsigned feature)
169  { return l4virtio_get_feature(_config->driver_features_map, feature); }
170 
171  l4_uint32_t &host_features(unsigned idx)
172  { return _host_features[idx]; }
173 
174  l4_uint32_t host_features(unsigned idx) const
175  { return _host_features[idx]; }
176 
188  l4_uint32_t guest_features(unsigned idx) const
189  { return _config->driver_features_map[idx]; }
190 
202  l4_uint32_t negotiated_features(unsigned idx) const
203  { return _config->driver_features_map[idx] & _host_features[idx]; }
204 
212  Status status() const { return _status; }
213 
221  {
222  return hdr()->cmd;
223  }
224 
231  void reset_cmd()
232  {
233  const_cast<l4_uint32_t volatile &>(hdr()->cmd) = 0;
234  }
235 
244  {
245  _status = status;
246  const_cast<l4_uint32_t volatile &>(hdr()->status) = status.raw;
247  }
248 
256  {
257  _status.device_needs_reset() = 1;
258  const_cast<l4_uint32_t volatile &>(hdr()->status) = _status.raw;
259  }
260 
266  {
267  if (sizeof(l4virtio_config_queue_t) * num_queues + _qoffset > L4_PAGESIZE)
268  // too many queues does not fit into our page
269  return false;
270 
271  _nqueues = num_queues;
272  reset_hdr(true);
273  return true;
274  }
275 
282  l4virtio_config_queue_t volatile const *qconfig(unsigned index) const
283  {
284  if (L4_UNLIKELY(_qoffset < sizeof (l4virtio_config_hdr_t)))
285  return 0;
286 
287  if (L4_UNLIKELY(index >= _nqueues))
288  return 0;
289 
290  return reinterpret_cast<l4virtio_config_queue_t const *>
291  (reinterpret_cast<char *>(_config.get()) + _qoffset) + index;
292  }
293 
297  void reset_hdr(bool inc_generation = false) const
298  {
299  _config->magic = L4VIRTIO_MAGIC;
300  _config->version = 2;
301  _config->device = _device;
302  _config->vendor = _vendor;
303  _config->status = 0;
304  _config->irq_status = 0;
305  _config->num_queues = _nqueues;
306  _config->queues_offset = _qoffset;
307 
308  memcpy(_config->dev_features_map, _host_features,
309  sizeof(_config->dev_features_map));
310  wmb();
311  if (inc_generation)
312  ++_config->generation;
313 
314  }
315 
324  bool reset_queue(unsigned index, unsigned num_max,
325  bool inc_generation = false) const
326  {
327  l4virtio_config_queue_t volatile *qc;
328  // this function is allowed to write to the device config
329  qc = const_cast<l4virtio_config_queue_t volatile *>(qconfig(index));
330  if (L4_UNLIKELY(qc == 0))
331  return false;
332 
333  qc->num_max = num_max;
334  qc->num = 0;
335  qc->ready = 0;
336  wmb();
337  if (inc_generation)
338  ++_config->generation;
339 
340  return true;
341  }
342 
347  l4virtio_config_hdr_t const volatile *hdr() const
348  { return _config.get(); }
349 
354  L4::Cap<L4Re::Dataspace> ds() const { return _ds.get(); }
355 
361  { return _ds_offset; }
362 };
363 
364 
365 template<typename PRIV_CONFIG>
366 class Dev_config_t : public Dev_config
367 {
368 public:
370  typedef PRIV_CONFIG Priv_config;
371 
383  Dev_config_t(l4_uint32_t vendor, l4_uint32_t device,
384  l4_uint32_t num_queues = 0)
385  : Dev_config(vendor, device, sizeof(PRIV_CONFIG), num_queues)
386  {}
387 
398  Dev_config_t(L4Re::Util::Shared_cap<L4Re::Dataspace> const &cfg,
399  l4_addr_t cfg_offset, l4_uint32_t vendor, l4_uint32_t device,
400  l4_uint32_t num_queues = 0)
401  : Dev_config(cfg, cfg_offset, vendor, device, sizeof(PRIV_CONFIG),
402  num_queues)
403  {}
404 
414  Priv_config volatile *priv_config() const
415  {
416  return static_cast<Priv_config volatile *>(get_priv_config());
417  }
418 
419 };
420 
421 struct No_custom_data {};
422 
428 template <typename DATA>
429 class Driver_mem_region_t : public DATA
430 {
431 public:
432  struct Flags
433  {
434  l4_uint32_t raw;
435  CXX_BITFIELD_MEMBER(0, 0, rw, raw);
436  };
437 
438 private:
441 
442  l4_uint64_t _drv_base;
443  l4_uint64_t _trans_offset;
444  l4_umword_t _size;
445  Flags _flags;
446 
447  Ds_cap _ds;
448  l4_addr_t _ds_offset;
449 
451  L4Re::Rm::Unique_region<l4_addr_t> _local_base;
452 
453  template<typename T>
454  T _local(l4_uint64_t addr) const
455  {
456  return (T)(addr - _trans_offset);
457  }
458 
459 public:
461  Driver_mem_region_t() : _size(0) {}
462 
475  l4_addr_t offset, Ds_cap &&ds)
476  : _drv_base(l4_trunc_page(drv_base)), _size(0),
477  _ds_offset(l4_trunc_page(offset))
478  {
479  using L4Re::chksys;
480  using L4Re::Env;
481 
483  // Sometimes we work with dataspaces that do not implement all dataspace
484  // methods and return an error instead. An example of such a dataspace is
485  // io's Vi::System_bus. We detect this case when the info method returns
486  // -L4_ENOSYS and simply assume the dataspace is good for us.
487  long err = ds->info(&ds_info);
488  if (err >= 0)
489  {
490  l4_addr_t ds_size = l4_round_page(ds_info.size);
491 
492  if (ds_size < L4_PAGESIZE)
493  chksys(-L4_EINVAL, "DS too small");
494 
495  if (_ds_offset >= ds_size)
496  chksys(-L4_ERANGE, "offset larger than DS size");
497 
499  if (size > ds_size)
500  chksys(-L4_EINVAL, "size larger than DS size");
501 
502  if (_ds_offset > ds_size - size)
503  chksys(-L4_EINVAL, "invalid offset or size");
504 
505  // overflow check
506  if ((ULLONG_MAX - size) < _drv_base)
507  chksys(-L4_EINVAL, "invalid size");
508 
509  _flags.rw() = (ds_info.flags & L4Re::Dataspace::F::W).raw != 0;
510  }
511  else if (err == -L4_ENOSYS)
512  {
513  _flags.rw() = true;
514  }
515  else
516  {
517  chksys(err, "getting data-space infos");
518  }
519 
520  auto f = L4Re::Rm::F::Search_addr | L4Re::Rm::F::R;
521  if (_flags.rw())
522  f |= L4Re::Rm::F::W;
523 
524  // use a big alignment to save PT/TLB entries and kernel memory resources!
525  chksys(Env::env()->rm()->attach(&_local_base, size, f,
526  L4::Ipc::make_cap(ds.get(), _flags.rw()
528  : L4_CAP_FPAGE_RO),
529  _ds_offset, L4_SUPERPAGESHIFT));
530 
531  _size = size;
532  _ds = cxx::move(ds);
533  _trans_offset = _drv_base - _local_base.get();
534  }
535 
537  bool is_writable() const { return _flags.rw(); }
538 
540  Flags flags() const { return _flags; }
541 
543  bool empty() const
544  { return _size == 0; }
545 
547  l4_uint64_t drv_base() const { return _drv_base; }
548 
550  void *local_base() const { return (void*)_local_base.get(); }
551 
553  l4_umword_t size() const { return _size; }
554 
556  l4_addr_t ds_offset() const { return _ds_offset; }
557 
559  L4::Cap<L4Re::Dataspace> ds() const { return _ds.get(); }
560 
569  {
570  if (base < _drv_base)
571  return false;
572 
573  if (base > _drv_base + _size - 1)
574  return false;
575 
576  if (size > _size)
577  return false;
578 
579  if (base - _drv_base > _size - size)
580  return false;
581 
582  return true;
583  }
584 
591  template<typename T>
592  T *local(Ptr<T> p) const
593  { return _local<T*>(p.get()); }
594 };
595 
596 typedef Driver_mem_region_t<No_custom_data> Driver_mem_region;
597 
604 template <typename DATA>
606 {
607 public:
609 
610 private:
611  cxx::unique_ptr<Mem_region[]> _l;
612  unsigned _max;
613  unsigned _free;
614 
615 public:
618 
620  Driver_mem_list_t() : _max(0), _free(0) {}
621 
626  void init(unsigned max)
627  {
628  _l = cxx::make_unique<Driver_mem_region_t<DATA>[]>(max);
629  _max = max;
630  _free = 0;
631  }
632 
634  bool full() const
635  { return _free == _max; }
636 
645  Mem_region const *add(l4_uint64_t drv_base, l4_umword_t size,
646  l4_addr_t offset, Ds_cap &&ds)
647  {
648  if (full())
650 
651  _l[_free++] = Mem_region(drv_base, size, offset, cxx::move(ds));
652  return &_l[_free - 1];
653  }
654 
659  void remove(Mem_region const *r)
660  {
661  if (r < &_l[0] || r >= &_l[_free])
663 
664  unsigned idx = r - &_l[0];
665 
666  for (unsigned i = idx + 1; i < _free - 1; ++i)
667  _l[i] = cxx::move(_l[i + 1]);
668 
669  _l[--_free] = Mem_region();
670  }
671 
680  {
681  return _find(base, size);
682  }
683 
693  void load_desc(Virtqueue::Desc const &desc, Request_processor const *p,
694  Virtqueue::Desc const **table) const
695  {
696  Mem_region const *r = find(desc.addr.get(), desc.len);
697  if (L4_UNLIKELY(!r))
699 
700  *table = static_cast<Virtqueue::Desc const *>(r->local(desc.addr));
701  }
702 
713  void load_desc(Virtqueue::Desc const &desc, Request_processor const *p,
714  Mem_region const **data) const
715  {
716  Mem_region const *r = find(desc.addr.get(), desc.len);
717  if (L4_UNLIKELY(!r))
719 
720  *data = r;
721  }
722 
739  template<typename ARG>
740  void load_desc(Virtqueue::Desc const &desc, Request_processor const *p,
741  ARG *data) const
742  {
743  Mem_region *r = find(desc.addr.get(), desc.len);
744  if (L4_UNLIKELY(!r))
746 
747  *data = ARG(r, desc, p);
748  }
749 
750 private:
751  Mem_region *_find(l4_uint64_t base, l4_umword_t size) const
752  {
753  for (unsigned i = 0; i < _free; ++i)
754  if (_l[i].contains(base, size))
755  return &_l[i];
756  return 0;
757  }
758 
759 
760 };
761 
762 typedef Driver_mem_list_t<No_custom_data> Driver_mem_list;
763 
782 template<typename DATA>
783 class Device_t
784 {
785 public:
787 
788 protected:
790 
791 private:
792  Dev_config *_device_config;
793 
794 public:
795  L4_RPC_LEGACY_DISPATCH(L4virtio::Device);
796  template<typename IOS> int virtio_dispatch(unsigned r, IOS &ios)
797  { return dispatch(r, ios); }
798 
800  virtual void reset() = 0;
801 
803  virtual bool check_features()
804  { return true; }
805 
807  virtual bool check_queues() = 0;
808 
810  virtual int reconfig_queue(unsigned idx) = 0;
811 
814  { L4Re::chksys(-L4_ENOSYS, "Legacy single IRQ interface not implemented."); }
815 
817  virtual void trigger_driver_config_irq() const = 0;
818 
821  {
822  L4Re::chksys(-L4_ENOSYS, "Legacy single IRQ interface not implemented.");
823  return L4::Cap<L4::Irq>();
824  }
825 
832  virtual void register_driver_irq(unsigned idx)
833  {
834  if (idx != 0)
835  L4Re::chksys(-L4_ENOSYS, "Multi IRQ interface not implemented.");
836 
838  }
839 
846  virtual L4::Cap<L4::Irq> device_notify_irq(unsigned idx)
847  {
848  if (idx != 0)
849  L4Re::chksys(-L4_ENOSYS, "Multi IRQ interface not implemented.");
850 
851  return device_notify_irq();
852  }
853 
855  virtual unsigned num_events_supported() const
856  { return 1; }
857 
858  virtual L4::Ipc_svr::Server_iface *server_iface() const = 0;
859 
863  Device_t(Dev_config *dev_config)
864  : _device_config(dev_config)
865  {}
866 
870  Mem_list const *mem_info() const
871  { return &_mem_info; };
872 
873  long op_set_status(L4virtio::Device::Rights, unsigned status)
874  { return _set_status(status); }
875 
876  long op_config_queue(L4virtio::Device::Rights, unsigned queue)
877  {
878  Dev_config::Status status = _device_config->status();
879  if (status.fail_state() || !status.acked() || !status.driver())
880  return -L4_EIO;
881 
882  return reconfig_queue(queue);
883  }
884 
885  long op_register_ds(L4virtio::Device::Rights,
886  L4::Ipc::Snd_fpage ds_cap_fp, l4_uint64_t ds_base,
887  l4_umword_t offset, l4_umword_t sz)
888  {
889  L4Re::Util::Dbg()
890  .printf("Registering dataspace from 0x%llx with %lu KiB, offset 0x%lx\n",
891  ds_base, sz >> 10, offset);
892 
893  _check_n_init_shm(ds_cap_fp, ds_base, sz, offset);
894 
895  return 0;
896  }
897 
898  long op_register_iface(L4virtio::Device::Rights,
899  L4::Ipc::Snd_fpage irq_cap_fp,
902  {
903  // If a dataspace with offset is used, the old-style registration
904  // interface cannot be supported.
905  if (_device_config->ds_offset() != 0)
906  return -L4_ENOSYS;
907 
908  if (!irq_cap_fp.cap_received())
909  return -L4_EINVAL;
910 
912 
913  L4Re::Util::Dbg()
914  .printf("register client: host IRQ: %lx config DS: %lx\n",
915  device_notify_irq().cap(), _device_config->ds().cap());
916 
918  config_ds = L4::Ipc::make_cap(_device_config->ds(), L4_CAP_FPAGE_RW);
919  return 0;
920  }
921 
922  long op_device_config(L4virtio::Device::Rights,
924  l4_addr_t &ds_offset)
925  {
926  L4Re::Util::Dbg()
927  .printf("register client: host IRQ: %lx config DS: %lx\n",
928  device_notify_irq().cap(), _device_config->ds().cap());
929 
930  config_ds = L4::Ipc::make_cap(_device_config->ds(), L4_CAP_FPAGE_RW);
931  ds_offset = _device_config->ds_offset();
932  return 0;
933  }
934 
935  long op_device_notification_irq(L4virtio::Device::Rights,
936  unsigned idx,
938  {
939  auto cap = device_notify_irq(idx);
940 
941  if (!cap.is_valid())
942  return -L4_EINVAL;
943 
945  return L4_EOK;
946  }
947 
948  int op_bind(L4::Icu::Rights, l4_umword_t idx, L4::Ipc::Snd_fpage irq_cap_fp)
949  {
950  if (idx >= num_events_supported())
951  return -L4_ERANGE;
952 
953  if (!irq_cap_fp.cap_received())
954  return -L4_EINVAL;
955 
956  register_driver_irq(idx);
957 
958  return L4_EOK;
959  }
960 
961  int op_unbind(L4::Icu::Rights, l4_umword_t, L4::Ipc::Snd_fpage)
962  {
963  return -L4_ENOSYS;
964  }
965 
966  int op_info(L4::Icu::Rights, L4::Icu::_Info &info)
967  {
968  info.features = 0;
969  info.nr_irqs = num_events_supported();
970  info.nr_msis = 0;
971 
972  return L4_EOK;
973  }
974 
975  int op_msi_info(L4::Icu::Rights, l4_umword_t, l4_uint64_t, l4_icu_msi_info_t &)
976  { return -L4_ENOSYS; }
977 
978  int op_mask(L4::Icu::Rights, l4_umword_t)
979  { return -L4_ENOSYS; }
980 
981  int op_unmask(L4::Icu::Rights, l4_umword_t)
982  { return -L4_ENOREPLY; }
983 
984  int op_set_mode(L4::Icu::Rights, l4_umword_t, l4_umword_t)
985  { return -L4_ENOSYS; }
986 
998  void reset_queue_config(unsigned idx, unsigned num_max,
999  bool inc_generation = false)
1000  {
1001  _device_config->reset_queue(idx, num_max, inc_generation);
1002  }
1003 
1008  void init_mem_info(unsigned num)
1009  {
1010  _mem_info.init(num);
1011  }
1012 
1021  {
1022  reset();
1023  _device_config->set_device_needs_reset();
1024 
1025  // the device MUST NOT notify the driver before DRIVER_OK.
1026  if (_device_config->status().driver_ok())
1027  {
1028  // we do not care about this anywhere, so skip
1029  // _dev_config->irq_status |= 1;
1031  }
1032  }
1033 
1047  bool setup_queue(Virtqueue *q, unsigned qn, unsigned num_max)
1048  {
1049  l4virtio_config_queue_t volatile const *qc;
1050  qc = _device_config->qconfig(qn);
1051  if (L4_UNLIKELY(qc == 0))
1052  return false;
1053 
1054  if (!qc->ready)
1055  {
1056  q->disable();
1057  return true;
1058  }
1059 
1060  // read to local variables before check
1061  l4_uint32_t num = qc->num;
1062  l4_uint64_t desc = qc->desc_addr;
1063  l4_uint64_t avail = qc->avail_addr;
1064  l4_uint64_t used = qc->used_addr;
1065 
1066  if (0)
1067  printf("%p: setup queue: num=0x%x max_num=0x%x desc=0x%llx avail=0x%llx used=0x%llx\n",
1068  this, num, num_max, desc, avail, used);
1069 
1070  if (!num || num > num_max)
1071  return false;
1072 
1073  // num must be power of two
1074  if (num & (num - 1))
1075  return false;
1076 
1077  if (desc & 0xf)
1078  return false;
1079 
1080  if (avail & 0x1)
1081  return false;
1082 
1083  if (used & 0x3)
1084  return false;
1085 
1086  auto const *desc_info = _mem_info.find(desc, Virtqueue::desc_size(num));
1087  if (L4_UNLIKELY(!desc_info))
1088  return false;
1089 
1090  auto const *avail_info = _mem_info.find(avail, Virtqueue::avail_size(num));
1091  if (L4_UNLIKELY(!avail_info))
1092  return false;
1093 
1094  auto const *used_info = _mem_info.find(used, Virtqueue::used_size(num));
1095  if (L4_UNLIKELY(!used_info || !used_info->is_writable()))
1096  return false;
1097 
1098  L4Re::Util::Dbg()
1099  .printf("shm=[%llx-%llx] local=[%lx-%lx] desc=[%llx-%llx] (%p-%p)\n",
1100  desc_info->drv_base(), desc_info->drv_base() + desc_info->size() - 1,
1101  (unsigned long)desc_info->local_base(),
1102  (unsigned long)desc_info->local_base() + desc_info->size() - 1,
1103  desc, desc + Virtqueue::desc_size(num),
1104  desc_info->local(Ptr<char>(desc)),
1105  desc_info->local(Ptr<char>(desc)) + Virtqueue::desc_size(num));
1106 
1107  L4Re::Util::Dbg()
1108  .printf("shm=[%llx-%llx] local=[%lx-%lx] avail=[%llx-%llx] (%p-%p)\n",
1109  avail_info->drv_base(), avail_info->drv_base() + avail_info->size() - 1,
1110  (unsigned long)avail_info->local_base(),
1111  (unsigned long)avail_info->local_base() + avail_info->size() - 1,
1112  avail, avail + Virtqueue::avail_size(num),
1113  avail_info->local(Ptr<char>(avail)),
1114  avail_info->local(Ptr<char>(avail)) + Virtqueue::avail_size(num));
1115 
1116  L4Re::Util::Dbg()
1117  .printf("shm=[%llx-%llx] local=[%lx-%lx] used=[%llx-%llx] (%p-%p)\n",
1118  used_info->drv_base(), used_info->drv_base() + used_info->size() - 1,
1119  (unsigned long)used_info->local_base(),
1120  (unsigned long)used_info->local_base() + used_info->size() - 1,
1121  used, used + Virtqueue::used_size(num),
1122  used_info->local(Ptr<char>(used)),
1123  used_info->local(Ptr<char>(used)) + Virtqueue::used_size(num));
1124 
1125  q->setup(num, desc_info->local(Ptr<void>(desc)),
1126  avail_info->local(Ptr<void>(avail)),
1127  used_info->local(Ptr<void>(used)));
1128  return true;
1129  }
1130 
1131  void check_n_init_shm(L4Re::Util::Unique_cap<L4Re::Dataspace> &&shm,
1132  l4_uint64_t base, l4_umword_t size, l4_addr_t offset)
1133  {
1134  if (_mem_info.full())
1136 
1137  auto const *i = _mem_info.add(base, size, offset, cxx::move(shm));
1138  L4Re::Util::Dbg()
1139  .printf("PORT[%p]: DMA guest [%llx-%llx] local [%lx-%lx] offset %lx\n",
1140  this, i->drv_base(), i->drv_base() + i->size() - 1,
1141  (unsigned long)i->local_base(),
1142  (unsigned long)i->local_base() + i->size() - 1,
1143  i->ds_offset());
1144  }
1145 
1155  {
1156  l4_uint32_t cmd = _device_config->get_cmd();
1157  if (L4_LIKELY(!(cmd & L4VIRTIO_CMD_MASK)))
1158  return false;
1159 
1160  switch (cmd & L4VIRTIO_CMD_MASK)
1161  {
1163  _set_status(cmd & ~L4VIRTIO_CMD_MASK);
1164  break;
1165 
1168  break;
1169 
1170  default:
1171  // unknown command
1172  break;
1173  }
1174 
1175  _device_config->reset_cmd();
1176 
1177  return true;
1178  }
1179 
1180 private:
1181  void _check_n_init_shm(L4::Ipc::Snd_fpage shm_cap_fp,
1182  l4_uint64_t base, l4_umword_t size, l4_addr_t offset)
1183  {
1184  if (!shm_cap_fp.cap_received())
1186 
1188  L4Re::chkcap(server_iface()->template rcv_cap<L4Re::Dataspace>(0)));
1189  L4Re::chksys(server_iface()->realloc_rcv_cap(0));
1190 
1191  check_n_init_shm(cxx::move(ds), base, size, offset);
1192  }
1193 
1194  bool check_features_internal()
1195  {
1196  static_assert(sizeof(l4virtio_config_hdr_t::driver_features_map)
1197  == sizeof(l4virtio_config_hdr_t::dev_features_map),
1198  "Driver and device feature maps must be of the same size");
1199 
1200  // From the Virtio 1.0 specification 6.1 Driver Requirements and 6.2 Device
1201  // Requirements: A driver MUST accept VIRTIO_F_VERSION_1 if it is offered.
1202  // A device MUST offer VIRTIO_F_VERSION_1. A device MAY fail to operate
1203  // further if VIRTIO_F_VERSION_1 is not accepted.
1204  //
1205  // The L4virtio implementation does not support legacy interfaces so we
1206  // fail here if the Virtio 1.0 feature was not accepted.
1207  if (!_device_config->get_guest_feature(L4VIRTIO_FEATURE_VERSION_1))
1208  return false;
1209 
1210  for (auto i = 0u;
1211  i < sizeof(l4virtio_config_hdr_t::driver_features_map)
1212  / sizeof(l4virtio_config_hdr_t::driver_features_map[0]);
1213  i++)
1214  {
1215  // Driver must not accept features that were not offered by device
1216  if (_device_config->guest_features(i)
1217  & ~_device_config->host_features(i))
1218  return false;
1219  }
1220  return check_features();
1221  }
1222 
1244  void check_and_update_status(Dev_config::Status status)
1245  {
1246  // snapshot of current status
1247  Dev_config::Status current_status = _device_config->status();
1248 
1249  // handle reset
1250  if (!status.raw)
1251  {
1252  _device_config->set_status(status);
1253  return;
1254  }
1255 
1256  // Do no further processing in case of driver or device failure. If FAILED
1257  // or DEVICE_NEEDS_RESET are set only these fail_state bits will be set in
1258  // addition to the current status bits already set.
1259  if (current_status.fail_state() || status.fail_state())
1260  {
1261  if (current_status.fail_state() != status.fail_state())
1262  {
1263  current_status.fail_state() =
1264  current_status.fail_state() | status.fail_state();
1265  _device_config->set_status(current_status);
1266  }
1267  return;
1268  }
1269 
1270  // Enforce init sequence ACKNOWLEDGE, DRIVER, FEATURES_OK, DRIVER_OK.
1271  // We do not enforce that only one additional new bit is set per call.
1272  if ((!status.acked() && status.driver())
1273  || (!status.driver() && status.features_ok())
1274  || (!status.features_ok() && status.driver_ok()))
1275  {
1276  current_status.device_needs_reset() = 1;
1277  _device_config->set_status(current_status);
1278  return;
1279  }
1280 
1281  // only check feature compatibility before DRIVER_OK is set
1282  if (status.features_ok() && !status.driver_ok()
1283  && !check_features_internal())
1284  status.features_ok() = 0;
1285 
1286  // Note that if FEATURES_OK and DRIVER_OK are both updated to being set
1287  // at the same time the above check_features_internal() is skipped; this is
1288  // considered undefined behaviour but it is not prevented.
1289  if (status.running() && !check_queues())
1290  {
1291  current_status.device_needs_reset() = 1;
1292  _device_config->set_status(current_status);
1293  return;
1294  }
1295 
1296  _device_config->set_status(status);
1297  }
1298 
1311  int _set_status(unsigned new_status)
1312  {
1313  if (new_status == 0)
1314  {
1315  L4Re::Util::Dbg().printf("Resetting device\n");
1316  reset();
1317  _device_config->reset_hdr(true);
1318  }
1319 
1320  Dev_config::Status status(new_status);
1321  check_and_update_status(status);
1322 
1323  return 0;
1324  }
1325 
1326 };
1327 
1328 typedef Device_t<No_custom_data> Device;
1329 
1330 } // namespace Svr
1331 
1332 }
1333 
#define L4_SUPERPAGESHIFT
Size of a large page, log2-based.
Definition: consts.h:42
Interface for memory-like objects.
Definition: dataspace:63
long info(Stats *stats)
Get information on the dataspace.
C++ interface of the initial environment that is provided to an L4 task.
Definition: env:86
static Env const * env() noexcept
Returns the initial environment for the current task.
Definition: env:103
l4_cap_idx_t cap() const noexcept
Return capability selector.
Definition: capability.h:52
Capability type for RPC interfaces (see L4::Cap<T>).
Definition: ipc_types:542
Generic RPC wrapper for L4 flex-pages.
Definition: ipc_types:322
bool cap_received() const noexcept
Check if the capability has been mapped.
Definition: ipc_types:438
Interface for server-loop related functions.
Definition: ipc_epiface:48
IPC interface for virtio over L4 IPC.
Definition: l4virtio:42
Pointer used in virtio descriptors.
Definition: virtqueue:57
l4_uint64_t get() const
Definition: virtqueue:71
Abstraction for L4-Virtio device config memory.
Definition: l4virtio:49
void set_status(Status status)
Set device status register.
Definition: l4virtio:243
bool reset_queue(unsigned index, unsigned num_max, bool inc_generation=false) const
Reset queue config for the given queue.
Definition: l4virtio:324
l4virtio_config_hdr_t const volatile * hdr() const
Get a read-only pointer to the config header.
Definition: l4virtio:347
l4_uint32_t get_cmd() const
Get the value from the cmd register.
Definition: l4virtio:220
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:108
bool change_queue_config(l4_uint32_t num_queues)
Setup new queue configuration.
Definition: l4virtio:265
l4virtio_config_queue_t volatile const * qconfig(unsigned index) const
Get queue read-only config data for queue with the given index.
Definition: l4virtio:282
Status status() const
Get current device status (trusted).
Definition: l4virtio:212
void reset_cmd()
Reset the cmd register after execution of a command.
Definition: l4virtio:231
void reset_hdr(bool inc_generation=false) const
Reset the config header to the initial contents.
Definition: l4virtio:297
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:142
l4_uint32_t guest_features(unsigned idx) const
Return a specific set of guest features.
Definition: l4virtio:188
L4::Cap< L4Re::Dataspace > ds() const
Get data-space capability for the shared config data space.
Definition: l4virtio:354
l4_uint32_t negotiated_features(unsigned idx) const
Compute a specific set of negotiated features.
Definition: l4virtio:202
l4_addr_t ds_offset() const
Return the offset into the config dataspace where the device configuration starts.
Definition: l4virtio:360
void set_device_needs_reset()
Set DEVICE_NEEDS_RESET bit in device status register.
Definition: l4virtio:255
Server-side L4-VIRTIO device stub.
Definition: l4virtio:784
void device_error()
Transition device into DEVICE_NEEDS_RESET state.
Definition: l4virtio:1020
Mem_list const * mem_info() const
Get the memory region list used for this device.
Definition: l4virtio:870
virtual void reset()=0
reset callback, called for doing a device reset
bool handle_mem_cmd_write()
Check for a value in the cmd register and handle a write.
Definition: l4virtio:1154
Mem_list _mem_info
Memory region list.
Definition: l4virtio:789
virtual void trigger_driver_config_irq() const =0
callback for triggering configuration change notification IRQ
virtual void register_driver_irq(unsigned idx)
Callback for registering an notification IRQ (multi IRQ).
Definition: l4virtio:832
virtual L4::Cap< L4::Irq > device_notify_irq(unsigned idx)
Callback to gather the device notification IRQ (multi IRQ).
Definition: l4virtio:846
Device_t(Dev_config *dev_config)
Make a device for the given config.
Definition: l4virtio:863
virtual void register_single_driver_irq()
callback for registering a single guest IRQ for all queues (old-style)
Definition: l4virtio:813
virtual bool check_features()
callback for checking the subset of accepted features
Definition: l4virtio:803
virtual unsigned num_events_supported() const
Return the highest notification index supported.
Definition: l4virtio:855
bool setup_queue(Virtqueue *q, unsigned qn, unsigned num_max)
Enable/disable the specified queue.
Definition: l4virtio:1047
void init_mem_info(unsigned num)
Initialize the memory region list to the given maximum.
Definition: l4virtio:1008
virtual int reconfig_queue(unsigned idx)=0
callback for client queue-config request
virtual L4::Cap< L4::Irq > device_notify_irq() const
callback to gather the device notification IRQ (old-style)
Definition: l4virtio:820
void reset_queue_config(unsigned idx, unsigned num_max, bool inc_generation=false)
Trigger reset for the configuration space for queue idx.
Definition: l4virtio:998
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:606
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:693
void remove(Mem_region const *r)
Remove the given region from the list.
Definition: l4virtio:659
L4Re::Util::Unique_cap< L4Re::Dataspace > Ds_cap
type for storing a data-space capability internally
Definition: l4virtio:617
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:645
void init(unsigned max)
Make a fresh list with capacity max.
Definition: l4virtio:626
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:713
void load_desc(Virtqueue::Desc const &desc, Request_processor const *p, ARG *data) const
Default implementation returning generic information.
Definition: l4virtio:740
Mem_region * find(l4_uint64_t base, l4_umword_t size) const
Find memory region containing the given driver address region.
Definition: l4virtio:679
Driver_mem_list_t()
Make an empty, zero capacity list.
Definition: l4virtio:620
Region of driver memory, that shall be managed locally.
Definition: l4virtio:430
bool contains(l4_uint64_t base, l4_umword_t size) const
Test if the given driver address range is within this region.
Definition: l4virtio:568
l4_addr_t ds_offset() const
Definition: l4virtio:556
T * local(Ptr< T > p) const
Get the local address for driver address p.
Definition: l4virtio:592
Driver_mem_region_t()
Make default empty memroy region.
Definition: l4virtio:461
l4_umword_t size() const
Definition: l4virtio:553
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:474
l4_uint64_t drv_base() const
Definition: l4virtio:547
L4::Cap< L4Re::Dataspace > ds() const
Definition: l4virtio:559
Encapsulate the state for processing a VIRTIO request.
Definition: virtio:399
Virtqueue implementation for the device.
Definition: virtio:88
Descriptor in the descriptor table.
Definition: virtqueue:96
l4_uint32_t len
Length of described buffer.
Definition: virtqueue:118
Ptr< void > addr
Address stored in descriptor.
Definition: virtqueue:117
void disable()
Completely disable the queue.
Definition: virtqueue:230
void setup(unsigned num, void *desc, void *avail, void *used)
Enable this queue.
Definition: virtqueue:355
static unsigned long desc_size(unsigned num)
Calculate the size of the descriptor table for num entries.
Definition: virtqueue:267
static unsigned long used_size(unsigned num)
Calculate the size of the used ring for num entries.
Definition: virtqueue:304
static unsigned long avail_size(unsigned num)
Calculate the size of the available ring for num entries.
Definition: virtqueue:285
#define L4_UNLIKELY(x)
Expression is unlikely to execute.
Definition: compiler.h:238
#define L4_LIKELY(x)
Expression is likely to execute.
Definition: compiler.h:237
Dataspace interface.
Environment interface.
Error helper.
T1 max(T1 a, T1 b)
Get the maximum of a and b.
Definition: minmax:46
unsigned long l4_umword_t
Unsigned machine word.
Definition: l4int.h:52
unsigned long l4_addr_t
Address type.
Definition: l4int.h:45
unsigned int l4_uint32_t
Unsigned 32bit value.
Definition: l4int.h:40
unsigned long long l4_uint64_t
Unsigned 64bit value.
Definition: l4int.h:42
@ L4_ERANGE
Range error.
Definition: err.h:58
@ L4_ENOSYS
No sys.
Definition: err.h:60
@ L4_EINVAL
Invalid argument.
Definition: err.h:56
@ L4_ENOREPLY
No reply.
Definition: err.h:65
@ L4_EIO
I/O error.
Definition: err.h:46
@ L4_EOK
Ok.
Definition: err.h:43
@ L4_ENOMEM
No memory.
Definition: err.h:50
@ L4_CAP_FPAGE_RO
Read right for capability flex-pages.
Definition: __l4_fpage.h:162
@ L4_CAP_FPAGE_RW
Read and interface specific 'W' right for capability flex-pages.
Definition: __l4_fpage.h:178
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:364
l4_addr_t l4_round_page(l4_addr_t address) L4_NOTHROW
Round address up to the next page.
Definition: consts.h:389
#define L4_PAGESIZE
Minimal page size (in bytes).
Definition: consts.h:307
void l4virtio_clear_feature(l4_uint32_t *feature_map, unsigned feat)
Clear the given feature bit in a feature map.
Definition: virtio.h:264
void l4virtio_set_feature(l4_uint32_t *feature_map, unsigned feat)
Set the given feature bit in a feature map.
Definition: virtio.h:252
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:276
void * l4virtio_device_config(l4virtio_config_hdr_t const *cfg)
Get the pointer to the device configuration.
Definition: virtio.h:243
@ L4VIRTIO_FEATURE_VERSION_1
Virtio protocol version 1 supported. Must be 1 for L4virtio.
Definition: virtio.h:93
@ L4VIRTIO_CMD_SET_STATUS
Set the status register.
Definition: virtio.h:114
@ L4VIRTIO_CMD_CFG_QUEUE
Configure a queue.
Definition: virtio.h:115
@ L4VIRTIO_CMD_MASK
Mask to get command bits.
Definition: virtio.h:116
Common L4 ABI Data Types.
L4::Detail::Shared_cap_impl< T, Smart_count_cap< L4_FP_ALL_SPACES > > Shared_cap
Shared capability that implements automatic free and unmap of the capability selector.
Definition: shared_cap:59
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
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:624
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:634
L4-VIRTIO Transport C++ API.
Definition: virtio-block:29
Region mapper interface.
@ W
Request write-only memory.
Definition: dataspace:86
Information about the dataspace.
Definition: dataspace:130
Flags flags
flags
Definition: dataspace:132
@ Search_addr
Search for a suitable address range.
Definition: rm:105
Exception used by Queue to indicate descriptor errors.
Definition: virtio:326
@ Bad_address
Address cannot be translated.
Definition: virtio:330
Type for device feature bitmap.
Definition: virtio:67
Type of the device status register.
Definition: virtio:33
device_needs_reset_bfm_t::Val device_needs_reset() const
Get the device_needs_reset bits ( 6 to 6 ) of raw.
Definition: virtio:45
driver_ok_bfm_t::Val driver_ok() const
Get the driver_ok bits ( 2 to 2 ) of raw.
Definition: virtio:42
unsigned char raw
Raw value of the VIRTIO device status register.
Definition: virtio:34
Info to use for a specific MSI.
Definition: icu.h:181
L4-VIRTIO config header, provided in shared data space.
Definition: virtio.h:123
l4_uint32_t cmd
L4 specific command register polled by the driver iff supported.
Definition: virtio.h:182
l4_uint32_t status
Device status register (read-only).
Definition: virtio.h:161
Queue configuration entry.
Definition: virtio.h:204
l4_uint64_t avail_addr
W: address of available ring.
Definition: virtio.h:217
l4_uint64_t desc_addr
W: address of descriptor table.
Definition: virtio.h:216
l4_uint64_t used_addr
W: address of used ring.
Definition: virtio.h:218
l4_uint16_t ready
RW: queue ready flag (read-write)
Definition: virtio.h:211
l4_uint16_t num_max
R: maximum number of descriptors supported by this queue.
Definition: virtio.h:206
l4_uint16_t num
RW: number of descriptors configured for this queue.
Definition: virtio.h:208
Capability allocator.
Shared_cap / Shared_del_cap.
Unique_cap / Unique_del_cap.