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