L4Re - L4 Runtime Environment
l4virtio
1 // vi:ft=cpp
2 /*
3  * (c) 2014 Alexander Warg <warg@os.inf.tu-dresden.de>
4  *
5  * This file is part of TUD:OS and distributed under the terms of the
6  * GNU General Public License 2.
7  * Please see the COPYING-GPL-2 file for details.
8  *
9  * As a special exception, you may use this file as part of a free software
10  * library without restriction. Specifically, if other files instantiate
11  * templates or use macros or inline functions from this file, or you compile
12  * this file and link it with other files to produce an executable, this
13  * file does not by itself cause the resulting executable to be covered by
14  * the GNU General Public License. This exception does not however
15  * invalidate any other reasons why the executable file might be covered by
16  * the GNU General Public License.
17  */
18 #pragma once
19 
20 #include <limits.h>
21 
22 #include <l4/re/dataspace>
23 #include <l4/re/env>
24 #include <l4/re/error_helper>
25 #include <l4/re/rm>
26 #include <l4/re/util/cap_alloc>
27 #include <l4/re/util/unique_cap>
28 
29 #include <l4/sys/types.h>
30 #include <l4/re/util/meta>
31 
32 #include <l4/cxx/bitfield>
33 #include <l4/cxx/utils>
34 #include <l4/cxx/unique_ptr>
35 
36 #include <l4/sys/cxx/ipc_legacy>
37 
38 #include "../l4virtio"
39 #include "virtio"
40 
41 /**
42  * \ingroup l4virtio_transport
43  */
44 namespace L4virtio {
45 namespace Svr {
46 
47 /**
48  * \brief Abstraction for L4-Virtio device config memory.
49  *
50  * Virtio defines a device configuration mechanism, L4-Virtio implements this
51  * mechanism based on shared memory a set_status() and a config_queue() call.
52  * This class provides an abstraction for L4-Virtio host implementations to
53  * establish such a shared memory data space and providing the necessary
54  * contents and access functions.
55  */
56 class Dev_config
57 {
58 public:
59  typedef Dev_status Status;
60  typedef Dev_features Features;
61 
62 private:
63  typedef L4Re::Rm::Unique_region< l4virtio_config_hdr_t*> Cfg_region;
64  typedef L4Re::Util::Unique_cap<L4Re::Dataspace> Cfg_cap;
65 
66  l4_uint32_t _vendor, _device, _qoffset, _nqueues;
67  l4_uint32_t _host_features[sizeof(l4virtio_config_hdr_t::dev_features_map)
68  / sizeof(l4_uint32_t)];
69  Cfg_cap _ds;
70  Cfg_region _config;
71 
72  Status _status; // status shadow, can be trusted by the device model
73 
74  static l4_uint32_t align(l4_uint32_t x)
75  { return (x + 0xfU) & ~0xfU; }
76 
77 protected:
78  void volatile *get_priv_config() const
79  {
80  return l4virtio_device_config(_config.get());
81  }
82 
83 public:
84 
85  /**
86  * \brief Setup/Create a L4-Virtio config data space.
87  * \param vendor The vendor ID to store in config header.
88  * \param device The device ID to store in config header.
89  * \param cfg_size The size of the device-specific config data in bytes.
90  * \param num_queues The number of queues provided by the device.
91  *
92  * This constructor allocates a data space used for L4-virtio config attaches
93  * the data space to the local address space and writes the initial contents
94  * to the config header.
95  */
96  Dev_config(l4_uint32_t vendor, l4_uint32_t device,
97  unsigned cfg_size, l4_uint32_t num_queues = 0)
98  : _vendor(vendor), _device(device),
99  _qoffset(0x100 + align(cfg_size)),
100  _nqueues(num_queues)
101  {
102  using L4Re::Dataspace;
103  using L4Re::chkcap;
104  using L4Re::chksys;
105 
106  //if (_cfg_offset + align(cfg_size) >= L4_PAGESIZE)
107  // hm what to do
108 
109  if (sizeof(l4virtio_config_queue_t) * _nqueues + _qoffset > L4_PAGESIZE)
110  {
111  // too many queues does not fit into our page
112  _qoffset = 0;
113  _nqueues = 0;
114  }
115 
116  typedef L4Re::Util::Unique_cap<L4Re::Dataspace> Cfg_cap;
117  Cfg_cap cfg = chkcap(L4Re::Util::make_unique_cap<Dataspace>());
118  chksys(L4Re::Env::env()->mem_alloc()->alloc(L4_PAGESIZE, cfg.get()));
119  chksys(L4Re::Env::env()->rm()->attach(&_config, L4_PAGESIZE,
120  L4Re::Rm::Search_addr,
121  L4::Ipc::make_cap_rw(cfg.get())));
122  _ds = cxx::move(cfg);
123  _config->generation = 0;
124  memset(_config->driver_features_map, 0, sizeof (_config->driver_features_map));
125  memset(_host_features, 0, sizeof(_host_features));
126  _host_features[1] = 1; // virtio 1
127  reset_hdr();
128  }
129 
130  void set_host_feature(unsigned feature)
131  { l4virtio_set_feature(_host_features, feature); }
132 
133  void clear_host_feature(unsigned feature)
134  { l4virtio_clear_feature(_host_features, feature); }
135 
136  bool get_host_feature(unsigned feature)
137  { return l4virtio_get_feature(_host_features, feature); }
138 
139  l4_uint32_t &host_features(unsigned idx)
140  { return _host_features[idx]; }
141 
142  l4_uint32_t host_features(unsigned idx) const
143  { return _host_features[idx]; }
144 
145  /**
146  * Return a specific set of guest features.
147  *
148  * \param idx Index into the guest features array.
149  *
150  * \retval The selected set of guest features.
151  *
152  * This function returns a specific 32bit set of features enabled by the
153  * guest/driver. `idx` is the index in the guest features array, resp. the 32
154  * bit set to return.
155  */
156  l4_uint32_t guest_features(unsigned idx) const
157  { return _config->driver_features_map[idx]; }
158 
159  /**
160  * \brief Get current device status (trusted).
161  * \return Current device status register (trusted).
162  *
163  * The status returned by this function is value stored internally and cannot
164  * be written by the guest (i.e., the value can be taken as trusted.)
165  */
166  Status status() const { return _status; }
167 
168  /**
169  * Get the value from the `cmd` register.
170  *
171  * Note, the most significant eight bits are the command (0 is nothing to
172  * do). The upper eight bit are reset to zero after the command was handled.
173  */
174  l4_uint32_t get_cmd() const
175  {
176  return hdr()->cmd;
177  }
178 
179  /**
180  * Reset the `cmd` register after execution of a command.
181  *
182  * This function resets the `cmd` register in order for the client to
183  * detect that the command was executed by the device.
184  */
185  void reset_cmd()
186  {
187  const_cast<l4_uint32_t volatile &>(hdr()->cmd) = 0;
188  }
189 
190  /**
191  * \brief Set device status register.
192  * \param status The new value for the device status register.
193  *
194  * This function sets the internal status register and also the status
195  * register in the shared memory to \a status.
196  */
197  void set_status(Status status)
198  {
199  _status = status;
200  const_cast<l4_uint32_t volatile &>(hdr()->status) = status.raw;
201  }
202 
203  /**
204  * \brief Set device status failed bit.
205  *
206  * This function sets the internal status register and also the status
207  * register in the shared memory to \a status.
208  */
209  void set_failed()
210  {
211  _status.failed() = 1;
212  const_cast<l4_uint32_t volatile &>(hdr()->status) = _status.raw;
213  }
214 
215  /**
216  * \brief Setup new queue configuration.
217  * \param num_queues The number of queues provided by the device.
218  */
219  bool change_queue_config(l4_uint32_t num_queues)
220  {
221  if (sizeof(l4virtio_config_queue_t) * num_queues + _qoffset > L4_PAGESIZE)
222  // too many queues does not fit into our page
223  return false;
224 
225  _nqueues = num_queues;
226  reset_hdr(true);
227  return true;
228  }
229 
230  /**
231  * \brief Get queue read-only config data for queue with the given \a index.
232  * \param index The index of the queue.
233  * \return Read-only pointer to the config of the queue with the given
234  * \a index, or NULL if \a index is out of range.
235  */
236  l4virtio_config_queue_t volatile const *qconfig(unsigned index) const
237  {
238  if (L4_UNLIKELY(_qoffset < sizeof (l4virtio_config_hdr_t)))
239  return 0;
240 
241  if (L4_UNLIKELY(index >= _nqueues))
242  return 0;
243 
244  return reinterpret_cast<l4virtio_config_queue_t const *>
245  (reinterpret_cast<char *>(_config.get()) + _qoffset) + index;
246  }
247 
248  /**
249  * \brief Reset the config header to the initial contents.
250  */
251  void reset_hdr(bool inc_generation = false) const
252  {
253  _config->magic = L4VIRTIO_MAGIC;
254  _config->version = 2;
255  _config->device = _device;
256  _config->vendor = _vendor;
257  _config->status = 0;
258  _config->irq_status = 0;
259  _config->num_queues = _nqueues;
260  _config->queues_offset = _qoffset;
261 
262  memcpy(_config->dev_features_map, _host_features,
263  sizeof(_config->dev_features_map));
264  wmb();
265  if (inc_generation)
266  ++_config->generation;
267 
268  }
269 
270  /**
271  * \brief Reset queue config for the given queue.
272  * \param index The index of the queue to reset.
273  * \param num_max The maximum number of descriptor supported by this queue.
274  * \param inc_generation The config generation will be incremented when
275  * this is true.
276  * \return true on success, or false when \a index is out of range.
277  */
278  bool reset_queue(unsigned index, unsigned num_max,
279  bool inc_generation = false) const
280  {
281  l4virtio_config_queue_t volatile *qc;
282  // this function is allowed to write to the device config
283  qc = const_cast<l4virtio_config_queue_t volatile *>(qconfig(index));
284  if (L4_UNLIKELY(qc == 0))
285  return false;
286 
287  qc->num_max = num_max;
288  qc->num = 0;
289  qc->ready = 0;
290  wmb();
291  if (inc_generation)
292  ++_config->generation;
293 
294  return true;
295  }
296 
297  /**
298  * \brief Get a read-only pointer to the config header.
299  * \return Read-only pointer to the shared config header.
300  */
301  l4virtio_config_hdr_t const volatile *hdr() const
302  { return _config.get(); }
303 
304  /**
305  * \brief Get data-space capability for the shared config data space.
306  * \return Capability for the shared config data space.
307  */
308  L4::Cap<L4Re::Dataspace> ds() const { return _ds.get(); }
309 };
310 
311 
312 template<typename PRIV_CONFIG>
313 class Dev_config_t : public Dev_config
314 {
315 public:
316  /// Type for device private configuration space
317  typedef PRIV_CONFIG Priv_config;
318 
319  /**
320  * \brief Setup/Create a L4-Virtio config data space.
321  * \param vendor The vendor ID to store in config header.
322  * \param device The device ID to store in config header.
323  * \param num_queues The number of queues provided by the device.
324  *
325  * This constructor allocates a data space used for L4-virtio config attaches
326  * the data space to the local address space and writes the initial contents
327  * to the config header.
328  */
329  Dev_config_t(l4_uint32_t vendor, l4_uint32_t device,
330  l4_uint32_t num_queues = 0)
331  : Dev_config(vendor, device, sizeof(PRIV_CONFIG), num_queues)
332  {}
333 
334  /**
335  * \brief Access the device private config structure.
336  * \return Pointer to the device private config structure within the shared
337  * configuration space.
338  *
339  * You have to be very careful in reading and checking the contents of this
340  * data structure because it is prone to race conditions and arbitrary
341  * modifications by the guest.
342  */
343  Priv_config volatile *priv_config() const
344  {
345  return static_cast<Priv_config volatile *>(get_priv_config());
346  }
347 
348 };
349 
350 struct No_custom_data {};
351 
352 /**
353  * Region of driver memory, that shall be managed locally.
354  *
355  * \tparam DATA Class defining additional information
356  */
357 template <typename DATA>
358 class Driver_mem_region_t : public DATA
359 {
360 public:
361  struct Flags
362  {
363  l4_uint32_t raw; ///< raw flags value
364  CXX_BITFIELD_MEMBER(0, 0, rw, raw); ///< read-write flag
365  };
366 
367 private:
368  /// type for storing a data-space capability internally
369  typedef L4Re::Util::Unique_cap<L4Re::Dataspace> Ds_cap;
370 
371  l4_uint64_t _drv_base; ///< base address used by the driver
372  l4_uint64_t _trans_offset; ///< offset for fast translation
373  l4_umword_t _size; ///< size of the region in bytes
374  Flags _flags; ///< flags attached to this region
375 
376  Ds_cap _ds; ///< data space capability backing this region
377  l4_addr_t _ds_offset;
378 
379  /// local mapping of the region
380  L4Re::Rm::Unique_region<l4_addr_t> _local_base;
381 
382  template<typename T>
383  T _local(l4_uint64_t addr) const
384  {
385  return (T)(addr - _trans_offset);
386  }
387 
388 public:
389  /// Make default empty memroy region
390  Driver_mem_region_t() : _size(0) {}
391 
392  /**
393  * \brief Make a local memory region for the given driver values.
394  * \param drv_base Base address of the memory region used by the driver.
395  * \param size Size of the memory region.
396  * \param offset Offset within the data space that is mapped to \a
397  * drv_base within the driver.
398  * \param ds Data space capability backing the memory.
399  *
400  * This constructor attaches the region of given data space to the
401  * local address space and stores the corresponding data for later reference.
402  */
403  Driver_mem_region_t(l4_uint64_t drv_base, l4_umword_t size,
404  l4_addr_t offset, Ds_cap &&ds)
405  : _drv_base(l4_trunc_page(drv_base)), _size(0),
406  _ds_offset(l4_trunc_page(offset))
407  {
408  using L4Re::chksys;
409  using L4Re::Env;
410 
411  L4Re::Dataspace::Stats ds_info = L4Re::Dataspace::Stats();
412  // Sometimes we work with dataspaces that do not implement all dataspace
413  // methods and return an error instead. An example of such a dataspace is
414  // io's Vi::System_bus. We detect this case when the info method returns
415  // -L4_ENOSYS and simply assume the dataspace is good for us.
416  long err = ds->info(&ds_info);
417  if (err >= 0)
418  {
419  l4_addr_t ds_size = l4_round_page(ds_info.size);
420 
421  if (ds_size < L4_PAGESIZE)
422  chksys(-L4_EINVAL, "DS too small");
423 
424  if (_ds_offset >= ds_size)
425  chksys(-L4_ERANGE, "offset larger than DS size");
426 
427  size = l4_round_page(size);
428  if (size > ds_size)
429  chksys(-L4_EINVAL, "size larger than DS size");
430 
431  if (_ds_offset > ds_size - size)
432  chksys(-L4_EINVAL, "invalid offset or size");
433 
434  // overflow check
435  if ((ULLONG_MAX - size) < _drv_base)
436  chksys(-L4_EINVAL, "invalid size");
437 
438  _flags.rw() = ds_info.flags & L4Re::Dataspace::Map_rw;
439  }
440  else if (err == -L4_ENOSYS)
441  {
442  _flags.rw() = true;
443  }
444  else
445  {
446  chksys(err, "getting data-space infos");
447  }
448 
449  // use a big alignment to save PT/TLB entries and kernel memory resources!
450  chksys(Env::env()->rm()->attach(&_local_base, size,
451  L4Re::Rm::Search_addr | (_flags.rw() ? 0 : L4Re::Rm::Read_only),
452  L4::Ipc::make_cap(ds.get(), _flags.rw()
453  ? L4_CAP_FPAGE_RW
454  : L4_CAP_FPAGE_RO),
455  _ds_offset, L4_SUPERPAGESHIFT));
456 
457  _size = size;
458  _ds = cxx::move(ds);
459  _trans_offset = _drv_base - _local_base.get();
460  }
461 
462  /// \return True if the region is writable, false else.
463  bool is_writable() const { return _flags.rw(); }
464 
465  /// \return The flags for this region.
466  Flags flags() const { return _flags; }
467 
468  /// \return True if the region is empty (size == 0), false else.
469  bool empty() const
470  { return _size == 0; }
471 
472  /// \return The base address used by the driver.
473  l4_uint64_t drv_base() const { return _drv_base; }
474 
475  /// \return The local base address.
476  void *local_base() const { return (void*)_local_base.get(); }
477 
478  /// \return The size of the region in bytes.
479  l4_umword_t size() const { return _size; }
480 
481  /// \return The offset within the data space.
482  l4_addr_t ds_offset() const { return _ds_offset; }
483 
484  /// \return The data space capability for this region.
485  L4::Cap<L4Re::Dataspace> ds() const { return _ds.get(); }
486 
487  /**
488  * \brief Test if the given driver address range is within this region.
489  * \param base The driver base address.
490  * \param size The size of the region to lookup.
491  * \return true if the given driver address region is contained in
492  * this region, false else.
493  */
494  bool contains(l4_uint64_t base, l4_umword_t size) const
495  {
496  if (base < _drv_base)
497  return false;
498 
499  if (base > _drv_base + _size - 1)
500  return false;
501 
502  if (size > _size)
503  return false;
504 
505  if (base - _drv_base > _size - size)
506  return false;
507 
508  return true;
509  }
510 
511  /**
512  * \brief Get the local address for driver address \a p.
513  * \param p Driver address to translate.
514  * \pre \a p \em must be contained in this region.
515  * \return Local address for the given driver address \a p.
516  */
517  template<typename T>
518  T *local(Ptr<T> p) const
519  { return _local<T*>(p.get()); }
520 };
521 
522 typedef Driver_mem_region_t<No_custom_data> Driver_mem_region;
523 
524 /**
525  * List of driver memory regions assigned to a single L4-VIRTIO transport
526  * instance.
527  *
528  * \note The regions added to this list \em must never overlap.
529  */
530 template <typename DATA>
531 class Driver_mem_list_t
532 {
533 public:
534  typedef Driver_mem_region_t<DATA> Mem_region;
535 
536 private:
537  cxx::unique_ptr<Mem_region[]> _l;
538  unsigned _max;
539  unsigned _free;
540 
541 public:
542  /// type for storing a data-space capability internally
543  typedef L4Re::Util::Unique_cap<L4Re::Dataspace> Ds_cap;
544 
545  /// Make an empty, zero capacity list.
546  Driver_mem_list_t() : _max(0), _free(0) {}
547 
548  /**
549  * Make a fresh list with capacity \a max.
550  * \param max The capacity of this vector.
551  */
552  void init(unsigned max)
553  {
554  _l = cxx::make_unique<Driver_mem_region_t<DATA>[]>(max);
555  _max = max;
556  _free = 0;
557  }
558 
559  /// \return True if the remaining capacity is 0.
560  bool full() const
561  { return _free == _max; }
562 
563  /**
564  * \brief Add a new region to the list.
565  * \param drv_base Driver base address of the region.
566  * \param size Size of the region in bytes.
567  * \param offset Offset within the data space attached to drv_base.
568  * \param ds Data space backing the driver memory.
569  * \return A pointer to the new region.
570  */
571  Mem_region const *add(l4_uint64_t drv_base, l4_umword_t size,
572  l4_addr_t offset, Ds_cap &&ds)
573  {
574  if (full())
575  L4Re::chksys(-L4_ENOMEM);
576 
577  _l[_free++] = Mem_region(drv_base, size, offset, cxx::move(ds));
578  return &_l[_free - 1];
579  }
580 
581  /**
582  * \brief Remove the given region from the list.
583  * \param r The region to remove (result from add(), or find()).
584  */
585  void remove(Mem_region const *r)
586  {
587  if (r < &_l[0] || r >= &_l[_free])
588  L4Re::chksys(-L4_ERANGE);
589 
590  unsigned idx = r - &_l[0];
591 
592  for (unsigned i = idx + 1; i < _free - 1; ++i)
593  _l[i] = cxx::move(_l[i + 1]);
594 
595  _l[--_free] = Mem_region();
596  }
597 
598  /**
599  * \brief Find memory region containing the given driver address region.
600  * \param base Driver base address.
601  * \param size Size of the region.
602  * \return Pointer to the region containing the given region,
603  * NULL if none is found.
604  */
605  Mem_region *find(l4_uint64_t base, l4_umword_t size) const
606  {
607  return _find(base, size);
608  }
609 
610  /**
611  * Default implementation for loading an indirect descriptor.
612  *
613  * \param desc The descriptor to load
614  * \param p The request processor calling us
615  * \param[out] table Shall be set to the loaded descriptor table
616  *
617  * \throws Bad_descriptor The descriptor address could not be translated.
618  */
619  void load_desc(Virtqueue::Desc const &desc, Request_processor const *p,
620  Virtqueue::Desc const **table) const
621  {
622  Mem_region const *r = find(desc.addr.get(), desc.len);
623  if (L4_UNLIKELY(!r))
624  throw Bad_descriptor(p, Bad_descriptor::Bad_address);
625 
626  *table = static_cast<Virtqueue::Desc const *>(r->local(desc.addr));
627  }
628 
629  /**
630  * Default implementation returning the Driver_mem_region
631  *
632  * \param desc The descriptor to load
633  * \param p The request processor calling us
634  * \param[out] data Shall be set to a pointer to the Driver_mem_region
635  * that covers the descriptor.
636  *
637  * \throws Bad_descriptor The descriptor address could not be translated.
638  */
639  void load_desc(Virtqueue::Desc const &desc, Request_processor const *p,
640  Mem_region const **data) const
641  {
642  Mem_region const *r = find(desc.addr.get(), desc.len);
643  if (L4_UNLIKELY(!r))
644  throw Bad_descriptor(p, Bad_descriptor::Bad_address);
645 
646  *data = r;
647  }
648 
649  /**
650  * Default implementation returning generic information.
651  *
652  * \tparam ARG Abstract argument type used with
653  * Request_processor::start() and Request_processor::next()
654  * to deliver the result of loading a descriptor. This type
655  * must provide a constructor taking three arguments: (1)
656  * pointer to a Driver_mem_region, (2) the Virtqueue::Desc
657  * descriptor, and (3) a pointer to the calling
658  * Request_processor.
659  * \param desc The descriptor to load
660  * \param p The request processor calling us
661  * \param[out] data Shall be assigned to ARG(mem, desc, p)
662  *
663  * \throws Bad_descriptor The descriptor address could not be translated.
664  */
665  template<typename ARG>
666  void load_desc(Virtqueue::Desc const &desc, Request_processor const *p,
667  ARG *data) const
668  {
669  Mem_region *r = find(desc.addr.get(), desc.len);
670  if (L4_UNLIKELY(!r))
671  throw Bad_descriptor(p, Bad_descriptor::Bad_address);
672 
673  *data = ARG(r, desc, p);
674  }
675 
676 private:
677  Mem_region *_find(l4_uint64_t base, l4_umword_t size) const
678  {
679  for (unsigned i = 0; i < _free; ++i)
680  if (_l[i].contains(base, size))
681  return &_l[i];
682  return 0;
683  }
684 
685 
686 };
687 
688 typedef Driver_mem_list_t<No_custom_data> Driver_mem_list;
689 
690 /**
691  * \brief Server-side L4-VIRTIO device stub.
692  */
693 template<typename DATA>
694 class Device_t
695 {
696 public:
697  typedef Driver_mem_list_t<DATA> Mem_list;
698 
699 protected:
700  Mem_list _mem_info; ///< Memory region list
701 
702 private:
703  Dev_config *_device_config; ///< Device configuration space
704 
705 public:
706  L4_RPC_LEGACY_DISPATCH(L4virtio::Device);
707  template<typename IOS> int virtio_dispatch(unsigned r, IOS &ios)
708  { return dispatch(r, ios); }
709 
710  /// reset callback, called for doing a device reset
711  virtual void reset() = 0;
712 
713  /// callback for checking if the queues at DRIVER_OK transition
714  virtual bool check_queues() = 0;
715 
716  /// callback for client queue-config request
717  virtual int reconfig_queue(unsigned idx) = 0;
718 
719  /// callback for registering a single guest IRQ for all queues
720  virtual void register_single_driver_irq() = 0;
721 
722  /// callback to gather the device notification IRQ
723  virtual L4::Cap<L4::Irq> device_notify_irq() const = 0;
724 
725  virtual L4::Ipc_svr::Server_iface *server_iface() const = 0;
726 
727  /**
728  * \brief Make a device for the given config.
729  */
730  Device_t(Dev_config *dev_config)
731  : _device_config(dev_config)
732  {}
733 
734  /**
735  * \brief Get the memory region list used for this device.
736  */
737  Mem_list const *mem_info() const
738  { return &_mem_info; };
739 
740  long op_set_status(L4virtio::Device::Rights, unsigned status)
741  { return _set_status(status); }
742 
743  long op_config_queue(L4virtio::Device::Rights, unsigned queue)
744  {
745  Dev_config::Status status = _device_config->status();
746  if (status.failed())
747  return -L4_EIO;
748 
749  if (!status.acked() || !status.driver())
750  return -L4_EIO;
751 
752  return reconfig_queue(queue);
753  }
754 
755  long op_register_ds(L4virtio::Device::Rights,
756  L4::Ipc::Snd_fpage ds_cap_fp, l4_uint64_t ds_base,
757  l4_umword_t offset, l4_umword_t sz)
758  {
759  printf("Registering dataspace from 0x%llx with %lu KiB, offset 0x%lx\n",
760  ds_base, sz >> 10, offset);
761 
762  _check_n_init_shm(ds_cap_fp, ds_base, sz, offset);
763 
764  return 0;
765  }
766 
767  long op_register_iface(L4virtio::Device::Rights,
768  L4::Ipc::Snd_fpage irq_cap_fp,
769  L4::Ipc::Cap<L4::Triggerable> &host_irq,
770  L4::Ipc::Cap<L4Re::Dataspace> &config_ds)
771  {
772  if (!irq_cap_fp.cap_received())
773  return -L4_EINVAL;
774 
775  register_single_driver_irq();
776 
777  printf("register client: host IRQ: %lx config DS: %lx\n",
778  device_notify_irq().cap(), _device_config->ds().cap());
779 
780  host_irq = L4::Ipc::make_cap(device_notify_irq(), L4_CAP_FPAGE_RO);
781  config_ds = L4::Ipc::make_cap(_device_config->ds(), L4_CAP_FPAGE_RW);
782  return 0;
783  }
784 
785  /**
786  * \brief Trigger reset for the configuration space for queue \a idx.
787  * \param idx The queue index to reset.
788  * \param num_max Maximum number of entries in this queue.
789  * \param inc_generation The config generation will be incremented when
790  * this is true.
791  *
792  * This function resets the driver-readable configuration space for the
793  * queue with the given index. The queue configuration is reset to all 0,
794  * name num_max to the given value.
795  */
796  void reset_queue_config(unsigned idx, unsigned num_max,
797  bool inc_generation = false)
798  {
799  _device_config->reset_queue(idx, num_max, inc_generation);
800  }
801 
802  /**
803  * \brief Initialize the memory region list to the given maximum.
804  * \param num Maximum number of memory regions that can be managed.
805  */
806  void init_mem_info(unsigned num)
807  {
808  _mem_info.init(num);
809  }
810 
811  /**
812  * \brief Transition device into failed state.
813  * \note Callers should trigger a guest config IRQ
814  * after calling this function.
815  *
816  * This function does a full reset, (calls reset()) and sets the
817  * failed bit in the device status register.
818  */
819  void device_error()
820  {
821  reset();
822  _device_config->set_failed();
823  }
824 
825  /**
826  * \brief Enable/disable the specified queue.
827  * \param q Pointer to the ring that represents the
828  * virtqueue internally.
829  * \param qn Index of the queue.
830  * \param num_max Maximum number of supported entries in this queue.
831  * \return true for success.
832  * *
833  * This function calculates the parameters of the virtqueue from the
834  * clients configuration space values, checks the accessibility of the
835  * queue data structures and initializes \a q to ready state when all
836  * checks succeeded.
837  */
838  bool setup_queue(Virtqueue *q, unsigned qn, unsigned num_max)
839  {
840  l4virtio_config_queue_t volatile const *qc;
841  qc = _device_config->qconfig(qn);
842 
843  if (!qc->ready)
844  {
845  q->disable();
846  return true;
847  }
848 
849  // read to local variables before check
850  l4_uint32_t num = qc->num;
851  l4_uint64_t desc = qc->desc_addr;
852  l4_uint64_t avail = qc->avail_addr;
853  l4_uint64_t used = qc->used_addr;
854 
855  if (0)
856  printf("%p: setup queue: num=0x%x max_num=0x%x desc=0x%llx avail=0x%llx used=0x%llx\n",
857  this, num, num_max, desc, avail, used);
858 
859  if (!num || num > num_max)
860  return false;
861 
862  // num must be power of two
863  if (num & (num - 1))
864  return false;
865 
866  if (desc & 0xf)
867  return false;
868 
869  if (avail & 0x1)
870  return false;
871 
872  if (used & 0x3)
873  return false;
874 
875  auto const *desc_info = _mem_info.find(desc, Virtqueue::desc_size(num));
876  if (L4_UNLIKELY(!desc_info))
877  return false;
878 
879  auto const *avail_info = _mem_info.find(avail, Virtqueue::avail_size(num));
880  if (L4_UNLIKELY(!avail_info))
881  return false;
882 
883  auto const *used_info = _mem_info.find(used, Virtqueue::used_size(num));
884  if (L4_UNLIKELY(!used_info || !used_info->is_writable()))
885  return false;
886 
887  printf("shm=[%llx-%llx] local=[%lx-%lx] desc=[%llx-%llx] (%p-%p)\n",
888  desc_info->drv_base(), desc_info->drv_base() + desc_info->size() - 1,
889  (unsigned long)desc_info->local_base(),
890  (unsigned long)desc_info->local_base() + desc_info->size() - 1,
891  desc, desc + Virtqueue::desc_size(num),
892  desc_info->local(Ptr<char>(desc)),
893  desc_info->local(Ptr<char>(desc)) + Virtqueue::desc_size(num));
894 
895  printf("shm=[%llx-%llx] local=[%lx-%lx] avail=[%llx-%llx] (%p-%p)\n",
896  avail_info->drv_base(), avail_info->drv_base() + avail_info->size() - 1,
897  (unsigned long)avail_info->local_base(),
898  (unsigned long)avail_info->local_base() + avail_info->size() - 1,
899  avail, avail + Virtqueue::avail_size(num),
900  avail_info->local(Ptr<char>(avail)),
901  avail_info->local(Ptr<char>(avail)) + Virtqueue::avail_size(num));
902 
903  printf("shm=[%llx-%llx] local=[%lx-%lx] used=[%llx-%llx] (%p-%p)\n",
904  used_info->drv_base(), used_info->drv_base() + used_info->size() - 1,
905  (unsigned long)used_info->local_base(),
906  (unsigned long)used_info->local_base() + used_info->size() - 1,
907  used, used + Virtqueue::used_size(num),
908  used_info->local(Ptr<char>(used)),
909  used_info->local(Ptr<char>(used)) + Virtqueue::used_size(num));
910 
911  q->setup(num, desc_info->local(Ptr<void>(desc)),
912  avail_info->local(Ptr<void>(avail)),
913  used_info->local(Ptr<void>(used)));
914  return true;
915  }
916 
917  void check_n_init_shm(L4Re::Util::Unique_cap<L4Re::Dataspace> &&shm,
918  l4_uint64_t base, l4_umword_t size, l4_addr_t offset)
919  {
920  if (_mem_info.full())
921  L4Re::chksys(-L4_ENOMEM);
922 
923  auto const *i = _mem_info.add(base, size, offset, cxx::move(shm));
924  printf("PORT[%p]: DMA guest [%llx-%llx] local [%lx-%lx] offset %lx\n",
925  this, i->drv_base(), i->drv_base() + i->size() - 1,
926  (unsigned long)i->local_base(),
927  (unsigned long)i->local_base() + i->size() - 1,
928  i->ds_offset());
929  }
930 
931  /**
932  * Check for a value in the `cmd` register and handle a write.
933  *
934  * This function checks for a value in the `cmd` register and executes
935  * the command if there is any, or returns false if there was no command.
936  *
937  * Execution of the command is signaled by a zero in the `cmd` register.
938  */
939  bool handle_mem_cmd_write()
940  {
941  l4_uint32_t cmd = _device_config->get_cmd();
942  if (L4_LIKELY(!(cmd & L4VIRTIO_CMD_MASK)))
943  return false;
944 
945  switch (cmd & L4VIRTIO_CMD_MASK)
946  {
947  case L4VIRTIO_CMD_SET_STATUS:
948  _set_status(cmd & ~L4VIRTIO_CMD_MASK);
949  break;
950 
951  case L4VIRTIO_CMD_CFG_QUEUE:
952  reconfig_queue(cmd & ~L4VIRTIO_CMD_MASK);
953  break;
954 
955  default:
956  // unknown command
957  break;
958  }
959 
960  _device_config->reset_cmd();
961 
962  return true;
963  }
964 
965 private:
966  void _check_n_init_shm(L4::Ipc::Snd_fpage shm_cap_fp,
967  l4_uint64_t base, l4_umword_t size, l4_addr_t offset)
968  {
969  if (!shm_cap_fp.cap_received())
970  L4Re::chksys(-L4_EINVAL);
971 
972  L4Re::Util::Unique_cap<L4Re::Dataspace> ds(
973  L4Re::chkcap(server_iface()->template rcv_cap<L4Re::Dataspace>(0)));
974  L4Re::chksys(server_iface()->realloc_rcv_cap(0));
975 
976  check_n_init_shm(cxx::move(ds), base, size, offset);
977  }
978 
979  int _set_status(unsigned _status)
980  {
981  Dev_config::Status status(_status);
982 
983  if (_status == 0)
984  {
985  printf("Resetting device\n");
986  reset();
987  }
988 
989  // do nothing if 'failed' is set
990  if (status.failed())
991  return 0;
992 
993  if (status.running() && !check_queues())
994  status.failed() = 1;
995 
996  _device_config->set_status(status);
997  return 0;
998  }
999 
1000 };
1001 
1002 typedef Device_t<No_custom_data> Device;
1003 
1004 } // namespace Svr
1005 
1006 }
1007