2 * (c) 2013-2014 Alexander Warg <warg@os.inf.tu-dresden.de>
3 * economic rights: Technische Universität Dresden (Germany)
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.
11 #include <l4/sys/capability>
12 #include <l4/sys/meta>
14 #include <l4/re/dataspace>
15 #include <l4/re/error_helper>
16 #include <l4/re/util/cap_alloc>
21 #include <l4/cxx/ipc_stream>
22 #include <l4/cxx/ipc_server>
24 #include <l4/l4virtio/l4virtio>
25 #include <l4/l4virtio/virtqueue>
26 #include <l4/l4virtio/virtio_block.h>
30 #include "irq_emitter.h"
33 namespace L4virtio { namespace Driver {
36 * \brief Client-side implementation for a general virtio device.
42 * Contacts the device and sets up the config page.
44 * \param srvcap Capability for device communication.
45 * \param guest_irq Irq capability to send to device.
47 * \throws L4::Runtime_error if the initialisation fails
49 * This function contacts the server, sets up the notification
50 * channels and the configuration dataspace. After this is done,
51 * the caller can set up any dataspaces it needs.
53 void driver_connect(L4::Cap<L4virtio::Device> srvcap,
54 L4::Cap<L4::Irq> guest_irq)
58 _host_irq = L4Re::chkcap(L4Re::Util::cap_alloc.alloc<L4::Irq>(),
59 "Cannot allocate host irq");
61 _config_cap = L4Re::chkcap(L4Re::Util::cap_alloc.alloc<L4Re::Dataspace>(),
62 "Cannot allocate cap for config dataspace");
64 auto *e = L4Re::Env::env();
65 L4Re::chksys(e->rm()->attach(&_config, L4_PAGESIZE, L4Re::Rm::Search_addr,
66 L4::Ipc::make_cap_rw(_config_cap.get()), 0,
68 "Cannot attach config dataspace");
70 L4Re::chksys(_device->register_iface(guest_irq, _host_irq.get(),
72 "Error registering interface with device");
74 if (memcmp(&_config->magic, "virt", 4) != 0)
75 L4Re::chksys(-L4_ENODEV, "Device config has wrong magic value");
77 if (_config->version != 1)
78 L4Re::chksys(-L4_ENODEV, "Invalid virtio version, must be 1");
83 * Share a dataspace with the device.
85 * \param ds Dataspace to share with the device.
86 * \param offset Offset in dataspace where the shared part starts.
87 * \param size Total size in bytes of the shared space.
88 * \param devaddr Start of shared space in the device address space.
90 * Although this function allows to share only a part of the given dataspace
91 * for convenience, the granularity of sharing is always the dataspace level.
92 * Thus, the remainder of the dataspace is not protected from the device.
94 int register_ds(L4::Cap<L4Re::Dataspace> ds, l4_umword_t offset,
95 l4_umword_t size, l4_uint64_t devaddr)
97 return _device->register_ds(L4::Ipc::make_cap_rw(ds), devaddr, offset, size);
100 int config_queue(int num)
102 return _device->config_queue(num);
105 L4virtio::Device::Config_hdr *device_config() const
106 { return _config.get(); }
108 unsigned selected_queue() const
109 { return _queue_sel; }
111 l4_uint32_t page_size() const
112 { return _config->guest_page_size; }
114 L4virtio::Device::Config_queue *queue_config(int num) const
116 return &_config->queues()[num];
119 l4_uint32_t read(unsigned reg)
123 case 0: return *reinterpret_cast<l4_uint32_t const *>("virt");
125 case 2: return _config->device;
126 case 3: return _config->vendor;
127 case 4: return (_host_feat_sel < 8) ?
128 _config->host_features[_host_feat_sel] : 0;
129 case 13: return (_queue_sel < _config->num_queues) ?
130 _config->queues()[_queue_sel].num_max : 0;
131 case 16: return (_queue_sel < _config->num_queues) ?
132 _config->queues()[_queue_sel].pfn : 0;
133 case 24: return 1; // currently unused: _config->irq_status;
134 case 28: return _config->status;
139 void write(unsigned reg, l4_uint32_t value)
141 // XXX make sure we don't write outside config page because the
142 // driver side screwed up
146 _host_irq->trigger();
149 _host_feat_sel = value;
152 if (_guest_feat_sel < 8)
153 _config->guest_features[_guest_feat_sel] = value;
156 _guest_feat_sel = value;
159 _config->guest_page_size = value;
165 if (_queue_sel < _config->num_queues)
166 _config->queues()[_queue_sel].num = value;
169 if (_queue_sel < _config->num_queues)
170 _config->queues()[_queue_sel].align = value;
173 if (_queue_sel < _config->num_queues)
175 _config->queues()[_queue_sel].pfn = value;
176 _device->config_queue(_queue_sel);
180 _device->set_status(value);
187 L4::Cap<L4virtio::Device> _device;
188 L4Re::Rm::Auto_region<L4virtio::Device::Config_hdr *> _config;
189 L4Re::Util::Auto_cap<L4::Irq>::Cap _guest_irq;
192 L4Re::Util::Auto_cap<L4::Irq>::Cap _host_irq;
193 L4Re::Util::Auto_cap<L4Re::Dataspace>::Cap _config_cap;
195 unsigned _queue_sel = 0;
196 unsigned _host_feat_sel = 0;
197 unsigned _guest_feat_sel = 0;
205 class Proxy_dev : public L4::Irqep_t<Proxy_dev>
208 // -- network specific
209 L4virtio::Driver::Virtqueue _txq;
211 // -------------------
213 Proxy_dev(Vmm::Vm_ram *iommu, Vmm::Irq_emitter const &irq)
214 : _iommu(iommu), _irq(irq) {}
216 l4_uint32_t read(unsigned reg, char, unsigned)
217 { return _dev.read(reg); }
219 void write(unsigned reg, char, l4_uint32_t value, unsigned)
221 // -- network specific
223 _txq.no_notify_host(true);
225 _dev.write(reg, value);
227 if (reg >> 2 == 16 && _dev.selected_queue() == 1)
229 auto *q = _dev.queue_config(1);
230 _txq.setup(q->num, q->align,
231 _iommu->access(L4virtio::Ptr<void>(value * _dev.page_size())));
235 template<typename REG>
236 void register_obj(REG *registry, L4::Cap<L4virtio::Device> host,
237 L4::Cap<L4Re::Dataspace> ram, l4_addr_t ram_base)
239 L4::Cap<L4::Irq> guest_irq = L4Re::chkcap(registry->register_irq_obj(this));
241 _dev.driver_connect(host, guest_irq);
242 L4Re::chksys(_dev.register_ds(ram, 0, ram->size(), ram_base));
246 { _irq.inject(vmm_current_cpu_id); }
249 Vmm::Irq_emitter _irq;
251 L4virtio::Driver::Device _dev;