L4Re - L4 Runtime Environment
l4virtio
1 // vi:ft=cpp
2 /*
3  * (c) 2013-2014 Alexander Warg <warg@os.inf.tu-dresden.de>
4  * Matthias Lange <matthias.lange@kernkonzept.com>
5  *
6  * This file is part of TUD:OS and distributed under the terms of the
7  * GNU General Public License 2.
8  * Please see the COPYING-GPL-2 file for details.
9  *
10  * As a special exception, you may use this file as part of a free software
11  * library without restriction. Specifically, if other files instantiate
12  * templates or use macros or inline functions from this file, or you compile
13  * this file and link it with other files to produce an executable, this
14  * file does not by itself cause the resulting executable to be covered by
15  * the GNU General Public License. This exception does not however
16  * invalidate any other reasons why the executable file might be covered by
17  * the GNU General Public License.
18  */
19 
20 #pragma once
21 
22 #include "virtio.h"
23 #include <l4/sys/capability>
24 #include <l4/sys/cxx/ipc_client>
25 #include <l4/re/dataspace>
26 #include <l4/sys/irq>
27 #include <l4/cxx/utils>
28 
29 namespace L4virtio {
30 /**
31  * IPC interface for virtio over L4 IPC.
32  *
33  * The L4virtio protocol is an adaption of the mmio virtio transport 1.0(4).
34  * This interface allows to exchange the necessary resources: device
35  * configuration page, notification interrupts and dataspaces for payload.
36  *
37  * Notification interrupts can be configured independently for changes to
38  * the configuration space and each queue through special L4virtio-specific
39  * notify_index fields in the config page and queue configuration. The
40  * interface distinguishes between device-to-driver and driver-to-device
41  * notification interrupts.
42  *
43  * Device-to-driver interrupts are configured via the ICU interface. The
44  * device announces the maximum number of supported interrupts via Icu::info().
45  * The driver can then bind interrupts using Icu::bind().
46  *
47  * Driver-to-device interrupts must be requested from the device through
48  * device_notification_irq().
49  */
50 class Device :
51  public L4::Kobject_t<Device, L4::Icu, L4VIRTIO_PROTOCOL,
52  L4::Type_info::Demand_t<1> >
53 {
54 public:
55  typedef l4virtio_config_queue_t Config_queue;
56  struct Config_hdr : l4virtio_config_hdr_t
57  {
58  Config_queue *queues() const
59  { return l4virtio_config_queues(this); }
60 
61  template <typename T>
62  T *device_config() const
63  {
64  return static_cast<T*>(l4virtio_device_config(this));
65  }
66 
67  int config_queue(unsigned num, L4::Cap<L4::Triggerable> out_notify,
68  L4::Cap<L4::Triggerable> in_notify,
69  l4_timeout_s to = L4_IPC_TIMEOUT_NEVER)
70  {
71  return send_cmd(L4VIRTIO_CMD_CFG_QUEUE | num,
72  out_notify, in_notify, to);
73  }
74 
75  int set_status(unsigned new_status, L4::Cap<L4::Triggerable> out_notify,
76  L4::Cap<L4::Triggerable> in_notify,
77  l4_timeout_s to = L4_IPC_TIMEOUT_NEVER)
78  {
79  return send_cmd(L4VIRTIO_CMD_SET_STATUS | new_status,
80  out_notify, in_notify, to);
81  }
82 
83  int send_cmd(unsigned command, L4::Cap<L4::Triggerable> out_notify,
84  L4::Cap<L4::Triggerable> in_notify,
85  l4_timeout_s to = L4_IPC_TIMEOUT_NEVER)
86  {
87  cxx::write_now(&cmd, command);
88 
89  if (out_notify)
90  out_notify->trigger();
91 
92  auto utcb = l4_utcb();
93  auto ipc_to = l4_timeout(L4_IPC_TIMEOUT_0, to);
94 
95  do
96  {
97  if (in_notify)
98  if (l4_ipc_error(l4_ipc_receive(in_notify.cap(), utcb, ipc_to),
99  utcb) == L4_IPC_RETIMEOUT)
100  break;
101  }
102  while (cxx::access_once(&cmd));
103 
104  return cxx::access_once(&cmd) ? -L4_EBUSY : L4_EOK;
105  }
106  };
107 
108  /**
109  * Write the VIRTIO status register.
110  *
111  * \param status Status word to write to the VIRTIO status.
112  *
113  * \return 0 on success, <0 on error.
114  *
115  * \note All other registers are accessed via shared memory.
116  */
117  L4_INLINE_RPC(long, set_status, (unsigned status));
118 
119  /**
120  * Trigger queue configuration of the given queue.
121  *
122  * Usually all queues are configured when the status is written to running.
123  * However, in some cases queues shall be disabled or enabled dynamically, in
124  * this case this function triggers a reconfiguration from the shared memory
125  * register of the queue config.
126  *
127  * \param queue Queue index for the queue to be configured.
128  *
129  * \retval 0 on success.
130  * \retval -L4_EIO The queue's status is invalid.
131  * \retval -L4_ERANGE The queue index exceeds the number of queues.
132  * \retval -L4_EINVAL Otherwise.
133  */
134  L4_INLINE_RPC(long, config_queue, (unsigned queue));
135 
136  /**
137  * Register a shared data space with VIRTIO host
138  *
139  * \param ds_cap Data-space capability to register. The lower 8 bits determine
140  * the rights mask with which the guest's rights are masked during
141  * the registration of the dataspace at the VIRTIO host.
142  * \param base VIRTIO guest physical start address of shared memory region
143  * \param offset Offset within the data space that is attached to the
144  * given `base` in the guest physical memory.
145  * \param size Size of the memory region in the guest
146  *
147  * \return 0 on success, < 0 on error
148  */
149  L4_INLINE_RPC(long, register_ds, (L4::Ipc::Cap<L4Re::Dataspace> ds_cap,
150  l4_uint64_t base, l4_umword_t offset,
151  l4_umword_t size));
152 
153  /**
154  * Register client to the L4-VIRTIO device.
155  *
156  * \param guest_irq IRQ capability for valid IRQ object for device-to-driver
157  * notifications.
158  * \param host_irq Capability selector for receiving the driver-to-device
159  * notifications IRQ capability.
160  * \param config_ds Capability for receiving the dataspace capability for
161  * the shared L4-VIRTIO config data space.
162  *
163  * \retval L4_EOK Success.
164  * \retval -L4_ENOSYS This interface is no longer supported by the server.
165  * Use get_device_config() etc. instead.
166  *
167  * \deprecated Use device_config(), device_notification_irq() and
168  * Icu::bind() instead.
169  */
170  L4_INLINE_RPC(long, register_iface, (L4::Ipc::Cap<L4::Triggerable> guest_irq,
171  L4::Ipc::Out<L4::Cap<L4::Triggerable> > host_irq,
172  L4::Ipc::Out<L4::Cap<L4Re::Dataspace> > config_ds));
173 
174  /**
175  * Get the dataspace with the L4virtio configuration page.
176  *
177  * \param config_ds Capability for receiving the dataspace capability for
178  * the shared L4-VIRTIO config data space.
179  * \param ds_offset Offset into the dataspace where the device configuration
180  * structure starts.
181  */
182  L4_INLINE_RPC(long, device_config, (L4::Ipc::Out<L4::Cap<L4Re::Dataspace> > config_ds,
183  l4_addr_t *ds_offset));
184 
185  /**
186  * Get the notification interrupt corresponding to the given index.
187  *
188  * \param index Index of the interrupt.
189  * \param[out] irq Triggerable for the given index.
190  *
191  * \retval L4_EOK Success.
192  * \retval L4_ENOSYS IRQ notification not supported by device.
193  * \retval <0 Other error.
194  *
195  * An index is only guaranteed to return an IRQ object when the index is
196  * set in one of the device notify index fields. The device must return
197  * the same interrupt for a given index as long as the index is in use.
198  * If an index disappears as a result of a configuration change and then is
199  * reused later, the interrupt is not guaranteed to be the same.
200  *
201  * Interrupts must always be rerequested after a device reset.
202  */
203  L4_INLINE_RPC(long, device_notification_irq,
204  (unsigned index, L4::Ipc::Out<L4::Cap<L4::Triggerable> > irq));
205 
206 
207  typedef L4::Typeid::Rpcs<set_status_t, config_queue_t, register_iface_t,
208  register_ds_t, device_config_t,
209  device_notification_irq_t> Rpcs;
210 };
211 
212 }