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