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  chksys(ds->info(&ds_info), "getting data-space infos");
413 
414  l4_addr_t ds_size = l4_round_page(ds_info.size);
415 
416  if (ds_size < L4_PAGESIZE)
417  chksys(-L4_EINVAL, "DS too small");
418 
419  if (_ds_offset >= ds_size)
420  chksys(-L4_ERANGE, "offset larger than DS size");
421 
422  size = l4_round_page(size);
423  if (size > ds_size)
424  chksys(-L4_EINVAL, "size larger than DS size");
425 
426  if (_ds_offset > ds_size - size)
427  chksys(-L4_EINVAL, "invalid offset or size");
428 
429  // overflow check
430  if ((ULLONG_MAX - size) < _drv_base)
431  chksys(-L4_EINVAL, "invalid size");
432 
433  _flags.rw() = ds_info.flags & L4Re::Dataspace::Map_rw;
434 
435  chksys(Env::env()->rm()->attach(&_local_base, size,
436  L4Re::Rm::Search_addr | (_flags.rw() ? 0 : L4Re::Rm::Read_only),
437  L4::Ipc::make_cap(ds.get(), _flags.rw()
438  ? L4_CAP_FPAGE_RW
439  : L4_CAP_FPAGE_RO), _ds_offset));
440 
441  _size = size;
442  _ds = cxx::move(ds);
443  _trans_offset = _drv_base - _local_base.get();
444  }
445 
446  /// \return True if the region is writable, false else.
447  bool is_writable() const { return _flags.rw(); }
448 
449  /// \return The flags for this region.
450  Flags flags() const { return _flags; }
451 
452  /// \return True if the region is empty (size == 0), false else.
453  bool empty() const
454  { return _size == 0; }
455 
456  /// \return The base address used by the driver.
457  l4_uint64_t drv_base() const { return _drv_base; }
458 
459  /// \return The local base address.
460  void *local_base() const { return (void*)_local_base.get(); }
461 
462  /// \return The size of the region in bytes.
463  l4_umword_t size() const { return _size; }
464 
465  /// \return The offset within the data space.
466  l4_addr_t ds_offset() const { return _ds_offset; }
467 
468  /// \return The data space capability for this region.
469  L4::Cap<L4Re::Dataspace> ds() const { return _ds.get(); }
470 
471  /**
472  * \brief Test if the given driver address range is within this region.
473  * \param base The driver base address.
474  * \param size The size of the region to lookup.
475  * \return true if the given driver address region is contained in
476  * this region, false else.
477  */
478  bool contains(l4_uint64_t base, l4_umword_t size) const
479  {
480  if (base < _drv_base)
481  return false;
482 
483  if (base > _drv_base + _size - 1)
484  return false;
485 
486  if (size > _size)
487  return false;
488 
489  if (base - _drv_base > _size - size)
490  return false;
491 
492  return true;
493  }
494 
495  /**
496  * \brief Get the local address for driver address \a p.
497  * \param p Driver address to translate.
498  * \pre \a p \em must be contained in this region.
499  * \return Local address for the given driver address \a p.
500  */
501  template<typename T>
502  T *local(Ptr<T> p) const
503  { return _local<T*>(p.get()); }
504 };
505 
506 typedef Driver_mem_region_t<No_custom_data> Driver_mem_region;
507 
508 /**
509  * List of driver memory regions assigned to a single L4-VIRTIO transport
510  * instance.
511  *
512  * \note The regions added to this list \em must never overlap.
513  */
514 template <typename DATA>
515 class Driver_mem_list_t
516 {
517 public:
518  typedef Driver_mem_region_t<DATA> Mem_region;
519 
520 private:
521  cxx::unique_ptr<Mem_region[]> _l;
522  unsigned _max;
523  unsigned _free;
524 
525 public:
526  /// type for storing a data-space capability internally
527  typedef L4Re::Util::Unique_cap<L4Re::Dataspace> Ds_cap;
528 
529  /// Make an empty, zero capacity list.
530  Driver_mem_list_t() : _max(0), _free(0) {}
531 
532  /**
533  * Make a fresh list with capacity \a max.
534  * \param max The capacity of this vector.
535  */
536  void init(unsigned max)
537  {
538  _l = cxx::make_unique<Driver_mem_region_t<DATA>[]>(max);
539  _max = max;
540  _free = 0;
541  }
542 
543  /// \return True if the remaining capacity is 0.
544  bool full() const
545  { return _free == _max; }
546 
547  /**
548  * \brief Add a new region to the list.
549  * \param drv_base Driver base address of the region.
550  * \param size Size of the region in bytes.
551  * \param offset Offset within the data space attached to drv_base.
552  * \param ds Data space backing the driver memory.
553  * \return A pointer to the new region.
554  */
555  Mem_region const *add(l4_uint64_t drv_base, l4_umword_t size,
556  l4_addr_t offset, Ds_cap &&ds)
557  {
558  if (full())
559  L4Re::chksys(-L4_ENOMEM);
560 
561  _l[_free++] = Mem_region(drv_base, size, offset, cxx::move(ds));
562  return &_l[_free - 1];
563  }
564 
565  /**
566  * \brief Remove the given region from the list.
567  * \param r The region to remove (result from add(), or find()).
568  */
569  void remove(Mem_region const *r)
570  {
571  if (r < &_l[0] || r >= &_l[_free])
572  L4Re::chksys(-L4_ERANGE);
573 
574  unsigned idx = r - &_l[0];
575 
576  for (unsigned i = idx + 1; i < _free - 1; ++i)
577  _l[i] = cxx::move(_l[i + 1]);
578 
579  _l[--_free] = Mem_region();
580  }
581 
582  /**
583  * \brief Find memory region containing the given driver address region.
584  * \param base Driver base address.
585  * \param size Size of the region.
586  * \return Pointer to the region containing the given region,
587  * NULL if none is found.
588  */
589  Mem_region *find(l4_uint64_t base, l4_umword_t size) const
590  {
591  return _find(base, size);
592  }
593 
594  /**
595  * Default implementation for loading an indirect descriptor.
596  *
597  * \param desc The descriptor to load
598  * \param p The request processor calling us
599  * \param[out] table Shall be set to the loaded descriptor table
600  *
601  * \throws Bad_descriptor The descriptor address could not be translated.
602  */
603  void load_desc(Virtqueue::Desc const &desc, Request_processor const *p,
604  Virtqueue::Desc const **table) const
605  {
606  Mem_region const *r = find(desc.addr.get(), desc.len);
607  if (L4_UNLIKELY(!r))
608  throw Bad_descriptor(p, Bad_descriptor::Bad_address);
609 
610  *table = static_cast<Virtqueue::Desc const *>(r->local(desc.addr));
611  }
612 
613  /**
614  * Default implementation returning the Driver_mem_region
615  *
616  * \param desc The descriptor to load
617  * \param p The request processor calling us
618  * \param[out] data Shall be set to a pointer to the Driver_mem_region
619  * that covers the descriptor.
620  *
621  * \throws Bad_descriptor The descriptor address could not be translated.
622  */
623  void load_desc(Virtqueue::Desc const &desc, Request_processor const *p,
624  Mem_region const **data) const
625  {
626  Mem_region const *r = find(desc.addr.get(), desc.len);
627  if (L4_UNLIKELY(!r))
628  throw Bad_descriptor(p, Bad_descriptor::Bad_address);
629 
630  *data = r;
631  }
632 
633  /**
634  * Default implementation returning generic information.
635  *
636  * \tparam ARG Abstract argument type used with
637  * Request_processor::start() and Request_processor::next()
638  * to deliver the result of loading a descriptor. This type
639  * must provide a constructor taking three arguments: (1)
640  * pointer to a Driver_mem_region, (2) the Virtqueue::Desc
641  * descriptor, and (3) a pointer to the calling
642  * Request_processor.
643  * \param desc The descriptor to load
644  * \param p The request processor calling us
645  * \param[out] data Shall be assigned to ARG(mem, desc, p)
646  *
647  * \throws Bad_descriptor The descriptor address could not be translated.
648  */
649  template<typename ARG>
650  void load_desc(Virtqueue::Desc const &desc, Request_processor const *p,
651  ARG *data) const
652  {
653  Mem_region *r = find(desc.addr.get(), desc.len);
654  if (L4_UNLIKELY(!r))
655  throw Bad_descriptor(p, Bad_descriptor::Bad_address);
656 
657  *data = ARG(r, desc, p);
658  }
659 
660 private:
661  Mem_region *_find(l4_uint64_t base, l4_umword_t size) const
662  {
663  for (unsigned i = 0; i < _free; ++i)
664  if (_l[i].contains(base, size))
665  return &_l[i];
666  return 0;
667  }
668 
669 
670 };
671 
672 typedef Driver_mem_list_t<No_custom_data> Driver_mem_list;
673 
674 /**
675  * \brief Server-side L4-VIRTIO device stub.
676  */
677 template<typename DATA>
678 class Device_t
679 {
680 public:
681  typedef Driver_mem_list_t<DATA> Mem_list;
682 
683 protected:
684  Mem_list _mem_info; ///< Memory region list
685 
686 private:
687  Dev_config *_device_config; ///< Device configuration space
688 
689 public:
690  L4_RPC_LEGACY_DISPATCH(L4virtio::Device);
691  template<typename IOS> int virtio_dispatch(unsigned r, IOS &ios)
692  { return dispatch(r, ios); }
693 
694  /// reset callback, called for doing a device reset
695  virtual void reset() = 0;
696 
697  /// callback for checking if the queues at DRIVER_OK transition
698  virtual bool check_queues() = 0;
699 
700  /// callback for client queue-config request
701  virtual int reconfig_queue(unsigned idx) = 0;
702 
703  /// callback for registering a single guest IRQ for all queues
704  virtual void register_single_driver_irq() = 0;
705 
706  /// callback to gather the device notification IRQ
707  virtual L4::Cap<L4::Irq> device_notify_irq() const = 0;
708 
709  virtual L4::Ipc_svr::Server_iface *server_iface() const = 0;
710 
711  /**
712  * \brief Make a device for the given config.
713  */
714  Device_t(Dev_config *dev_config)
715  : _device_config(dev_config)
716  {}
717 
718  /**
719  * \brief Get the memory region list used for this device.
720  */
721  Mem_list const *mem_info() const
722  { return &_mem_info; };
723 
724  long op_set_status(L4virtio::Device::Rights, unsigned status)
725  { return _set_status(status); }
726 
727  long op_config_queue(L4virtio::Device::Rights, unsigned queue)
728  {
729  Dev_config::Status status = _device_config->status();
730  if (status.failed())
731  return -L4_EIO;
732 
733  if (!status.acked() || !status.driver())
734  return -L4_EIO;
735 
736  return reconfig_queue(queue);
737  }
738 
739  long op_register_ds(L4virtio::Device::Rights,
740  L4::Ipc::Snd_fpage ds_cap_fp, l4_uint64_t ds_base,
741  l4_umword_t offset, l4_umword_t sz)
742  {
743  printf("Registering dataspace from 0x%llx with %lu KiB, offset 0x%lx\n",
744  ds_base, sz >> 10, offset);
745 
746  _check_n_init_shm(ds_cap_fp, ds_base, sz, offset);
747 
748  return 0;
749  }
750 
751  long op_register_iface(L4virtio::Device::Rights,
752  L4::Ipc::Snd_fpage irq_cap_fp,
753  L4::Ipc::Cap<L4::Triggerable> &host_irq,
754  L4::Ipc::Cap<L4Re::Dataspace> &config_ds)
755  {
756  if (!irq_cap_fp.cap_received())
757  return -L4_EINVAL;
758 
759  register_single_driver_irq();
760 
761  printf("register client: host IRQ: %lx config DS: %lx\n",
762  device_notify_irq().cap(), _device_config->ds().cap());
763 
764  host_irq = L4::Ipc::make_cap(device_notify_irq(), L4_CAP_FPAGE_RO);
765  config_ds = L4::Ipc::make_cap(_device_config->ds(), L4_CAP_FPAGE_RW);
766  return 0;
767  }
768 
769  /**
770  * \brief Trigger reset for the configuration space for queue \a idx.
771  * \param idx The queue index to reset.
772  * \param num_max Maximum number of entries in this queue.
773  * \param inc_generation The config generation will be incremented when
774  * this is true.
775  *
776  * This function resets the driver-readable configuration space for the
777  * queue with the given index. The queue configuration is reset to all 0,
778  * name num_max to the given value.
779  */
780  void reset_queue_config(unsigned idx, unsigned num_max,
781  bool inc_generation = false)
782  {
783  _device_config->reset_queue(idx, num_max, inc_generation);
784  }
785 
786  /**
787  * \brief Initialize the memory region list to the given maximum.
788  * \param num Maximum number of memory regions that can be managed.
789  */
790  void init_mem_info(unsigned num)
791  {
792  _mem_info.init(num);
793  }
794 
795  /**
796  * \brief Transition device into failed state.
797  * \note Callers should trigger a guest config IRQ
798  * after calling this function.
799  *
800  * This function does a full reset, (calls reset()) and sets the
801  * failed bit in the device status register.
802  */
803  void device_error()
804  {
805  reset();
806  _device_config->set_failed();
807  }
808 
809  /**
810  * \brief Enable/disable the specified queue.
811  * \param q Pointer to the ring that represents the
812  * virtqueue internally.
813  * \param qn Index of the queue.
814  * \param num_max Maximum number of supported entries in this queue.
815  * \return true for success.
816  * *
817  * This function calculates the parameters of the virtqueue from the
818  * clients configuration space values, checks the accessibility of the
819  * queue data structures and initializes \a q to ready state when all
820  * checks succeeded.
821  */
822  bool setup_queue(Virtqueue *q, unsigned qn, unsigned num_max)
823  {
824  l4virtio_config_queue_t volatile const *qc;
825  qc = _device_config->qconfig(qn);
826 
827  if (!qc->ready)
828  {
829  q->disable();
830  return true;
831  }
832 
833  // read to local variables before check
834  l4_uint32_t num = qc->num;
835  l4_uint64_t desc = qc->desc_addr;
836  l4_uint64_t avail = qc->avail_addr;
837  l4_uint64_t used = qc->used_addr;
838 
839  if (0)
840  printf("%p: setup queue: num=0x%x max_num=0x%x desc=0x%llx avail=0x%llx used=0x%llx\n",
841  this, num, num_max, desc, avail, used);
842 
843  if (!num || num > num_max)
844  return false;
845 
846  // num must be power of two
847  if (num & (num - 1))
848  return false;
849 
850  if (desc & 0xf)
851  return false;
852 
853  if (avail & 0x1)
854  return false;
855 
856  if (used & 0x3)
857  return false;
858 
859  auto const *desc_info = _mem_info.find(desc, Virtqueue::desc_size(num));
860  if (L4_UNLIKELY(!desc_info))
861  return false;
862 
863  auto const *avail_info = _mem_info.find(avail, Virtqueue::avail_size(num));
864  if (L4_UNLIKELY(!avail_info))
865  return false;
866 
867  auto const *used_info = _mem_info.find(used, Virtqueue::used_size(num));
868  if (L4_UNLIKELY(!used_info || !used_info->is_writable()))
869  return false;
870 
871  printf("shm=[%llx-%llx] local=[%lx-%lx] desc=[%llx-%llx] (%p-%p)\n",
872  desc_info->drv_base(), desc_info->drv_base() + desc_info->size() - 1,
873  (unsigned long)desc_info->local_base(),
874  (unsigned long)desc_info->local_base() + desc_info->size() - 1,
875  desc, desc + Virtqueue::desc_size(num),
876  desc_info->local(Ptr<char>(desc)),
877  desc_info->local(Ptr<char>(desc)) + Virtqueue::desc_size(num));
878 
879  printf("shm=[%llx-%llx] local=[%lx-%lx] avail=[%llx-%llx] (%p-%p)\n",
880  avail_info->drv_base(), avail_info->drv_base() + avail_info->size() - 1,
881  (unsigned long)avail_info->local_base(),
882  (unsigned long)avail_info->local_base() + avail_info->size() - 1,
883  avail, avail + Virtqueue::avail_size(num),
884  avail_info->local(Ptr<char>(avail)),
885  avail_info->local(Ptr<char>(avail)) + Virtqueue::avail_size(num));
886 
887  printf("shm=[%llx-%llx] local=[%lx-%lx] used=[%llx-%llx] (%p-%p)\n",
888  used_info->drv_base(), used_info->drv_base() + used_info->size() - 1,
889  (unsigned long)used_info->local_base(),
890  (unsigned long)used_info->local_base() + used_info->size() - 1,
891  used, used + Virtqueue::used_size(num),
892  used_info->local(Ptr<char>(used)),
893  used_info->local(Ptr<char>(used)) + Virtqueue::used_size(num));
894 
895  q->setup(num, desc_info->local(Ptr<void>(desc)),
896  avail_info->local(Ptr<void>(avail)),
897  used_info->local(Ptr<void>(used)));
898  return true;
899  }
900 
901  void check_n_init_shm(L4Re::Util::Unique_cap<L4Re::Dataspace> &&shm,
902  l4_uint64_t base, l4_umword_t size, l4_addr_t offset)
903  {
904  if (_mem_info.full())
905  L4Re::chksys(-L4_ENOMEM);
906 
907  auto const *i = _mem_info.add(base, size, offset, cxx::move(shm));
908  printf("PORT[%p]: DMA guest [%llx-%llx] local [%lx-%lx] offset %lx\n",
909  this, i->drv_base(), i->drv_base() + i->size() - 1,
910  (unsigned long)i->local_base(),
911  (unsigned long)i->local_base() + i->size() - 1,
912  i->ds_offset());
913  }
914 
915  /**
916  * Check for a value in the `cmd` register and handle a write.
917  *
918  * This function checks for a value in the `cmd` register and executes
919  * the command if there is any, or returns false if there was no command.
920  *
921  * Execution of the command is signaled by a zero in the `cmd` register.
922  */
923  bool handle_mem_cmd_write()
924  {
925  l4_uint32_t cmd = _device_config->get_cmd();
926  if (L4_LIKELY(!(cmd & L4VIRTIO_CMD_MASK)))
927  return false;
928 
929  switch (cmd & L4VIRTIO_CMD_MASK)
930  {
931  case L4VIRTIO_CMD_SET_STATUS:
932  _set_status(cmd & ~L4VIRTIO_CMD_MASK);
933  break;
934 
935  case L4VIRTIO_CMD_CFG_QUEUE:
936  reconfig_queue(cmd & ~L4VIRTIO_CMD_MASK);
937  break;
938 
939  default:
940  // unknown command
941  break;
942  }
943 
944  _device_config->reset_cmd();
945 
946  return true;
947  }
948 
949 private:
950  void _check_n_init_shm(L4::Ipc::Snd_fpage shm_cap_fp,
951  l4_uint64_t base, l4_umword_t size, l4_addr_t offset)
952  {
953  if (!shm_cap_fp.cap_received())
954  L4Re::chksys(-L4_EINVAL);
955 
956  L4Re::Util::Unique_cap<L4Re::Dataspace> ds(
957  L4Re::chkcap(server_iface()->template rcv_cap<L4Re::Dataspace>(0)));
958  L4Re::chksys(server_iface()->realloc_rcv_cap(0));
959 
960  check_n_init_shm(cxx::move(ds), base, size, offset);
961  }
962 
963  int _set_status(unsigned _status)
964  {
965  Dev_config::Status status(_status);
966 
967  if (_status == 0)
968  {
969  printf("Resetting device\n");
970  reset();
971  }
972 
973  // do nothing if 'failed' is set
974  if (status.failed())
975  return 0;
976 
977  if (status.running() && !check_queues())
978  status.failed() = 1;
979 
980  _device_config->set_status(status);
981  return 0;
982  }
983 
984 };
985 
986 typedef Device_t<No_custom_data> Device;
987 
988 } // namespace Svr
989 
990 }
991