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