L4Re Operating System Framework – Interface and Usage Documentation
Loading...
Searching...
No Matches
virtio-block
1// vi:ft=cpp
2/* SPDX-License-Identifier: GPL-2.0-only or License-Ref-kk-custom */
3/*
4 * Copyright (C) 2015-2020, 2022 Kernkonzept GmbH.
5 * Author(s): Sarah Hoffmann <sarah.hoffmann@kernkonzept.com>
6 * Manuel von Oltersdorff-Kalettka <manuel.kalettka@kernkonzept.com>
7 *
8 */
9#pragma once
10
11#include <l4/sys/factory>
12#include <l4/sys/semaphore>
13#include <l4/re/dataspace>
14#include <l4/re/env>
15#include <l4/re/util/unique_cap>
16#include <l4/re/util/object_registry>
17#include <l4/re/error_helper>
18
19#include <l4/util/atomic.h>
20#include <l4/util/bitops.h>
21#include <l4/l4virtio/client/l4virtio>
22#include <l4/l4virtio/l4virtio>
23#include <l4/l4virtio/virtqueue>
24#include <l4/l4virtio/virtio_block.h>
25#include <l4/sys/consts.h>
26
27#include <cstring>
28#include <vector>
29#include <functional>
30
31namespace L4virtio { namespace Driver {
32
36class Block_device : public Device
37{
38public:
39 typedef std::function<void(unsigned char)> Callback;
40
41private:
42 enum { Header_size = sizeof(l4virtio_block_header_t) };
43
44 struct Request
45 {
46 l4_uint16_t tail;
47 Callback callback;
48
49 Request() : tail(Virtqueue::Eoq), callback(0) {}
50 };
51
52public:
56 class Handle
57 {
58 friend Block_device;
59 l4_uint16_t head;
60
61 explicit Handle(l4_uint16_t descno) : head(descno) {}
62
63 public:
64 Handle() : head(Virtqueue::Eoq) {}
65 bool valid() const { return head != Virtqueue::Eoq; }
66 };
67
92 void **userdata, Ptr<void> &user_devaddr,
94 l4_uint32_t fmask0 = -1U, l4_uint32_t fmask1 = -1U)
95 {
96 // Contact device.
97 driver_connect(srvcap);
98
99 if (_config->device != L4VIRTIO_ID_BLOCK)
100 L4Re::chksys(-L4_ENODEV, "Device is not a block device.");
101
102 if (_config->num_queues != 1)
103 L4Re::chksys(-L4_EINVAL, "Invalid number of queues reported.");
104
105 // Memory is shared in one large dataspace which contains queues,
106 // space for header/status and additional user-defined memory.
107 unsigned queuesz = max_queue_size(0);
108 l4_size_t totalsz = l4_round_page(usermem);
109
110 l4_uint64_t const header_offset =
111 l4_round_size(_queue.total_size(queuesz),
112 l4util_bsr(alignof(l4virtio_block_header_t)));
113 l4_uint64_t const status_offset = header_offset + queuesz * Header_size;
114 l4_uint64_t const usermem_offset = l4_round_page(status_offset + queuesz);
115
116 // reserve space for one header/status per descriptor
117 // TODO Should be reduced to 1/3 but this way no freelist is needed.
118 totalsz += usermem_offset;
119
120 auto *e = L4Re::Env::env();
121 if (!qds.is_valid())
122 {
123 _ds = L4Re::chkcap(L4Re::Util::make_unique_cap<L4Re::Dataspace>(),
124 "Allocate queue dataspace capability");
125 L4Re::chksys(e->mem_alloc()->alloc(totalsz, _ds.get(),
128 "Allocate memory for virtio structures");
129 _queue_ds = _ds.get();
130 }
131 else
132 {
133 if (qds->size() < totalsz)
134 L4Re::chksys(-L4_EINVAL, "External queue dataspace too small.");
135 _queue_ds = qds;
136 }
137
138 // Now sort out which region goes where in the dataspace.
139 L4Re::chksys(e->rm()->attach(&_queue_region, totalsz,
141 L4::Ipc::make_cap_rw(_queue_ds), 0,
143 "Attach dataspace for virtio structures");
144
145 l4_uint64_t devaddr;
146 L4Re::chksys(register_ds(_queue_ds, 0, totalsz, &devaddr),
147 "Register queue dataspace with device");
148
149 _queue.init_queue(queuesz, _queue_region.get());
150
151 config_queue(0, queuesz, devaddr, devaddr + _queue.avail_offset(),
152 devaddr + _queue.used_offset());
153
154 _header_addr = devaddr + header_offset;
155 _headers = reinterpret_cast<l4virtio_block_header_t *>(_queue_region.get()
156 + header_offset);
157
158 _status_addr = devaddr + status_offset;
159 _status = _queue_region.get() + status_offset;
160
161 user_devaddr = Ptr<void>(devaddr + usermem_offset);
162 if (userdata)
163 *userdata = _queue_region.get() + usermem_offset;
164
165 // setup the callback mechanism
166 _pending.assign(queuesz, Request());
167
168 // Finish handshake with device.
169 _config->driver_features_map[0] = fmask0;
170 _config->driver_features_map[1] = fmask1;
172 }
173
178 {
179 return *_config->device_config<l4virtio_block_config_t>();
180 }
181
191 Callback callback)
192 {
193 l4_uint16_t descno = _queue.alloc_descriptor();
194 if (descno == Virtqueue::Eoq)
195 return Handle(Virtqueue::Eoq);
196
197 L4virtio::Virtqueue::Desc &desc = _queue.desc(descno);
198 Request &req = _pending[descno];
199
200 // setup the header
201 l4virtio_block_header_t &head = _headers[descno];
202 head.type = type;
203 head.ioprio = 0;
204 head.sector = sector;
205
206 // and put it in the descriptor
207 desc.addr = Ptr<void>(_header_addr + descno * Header_size);
208 desc.len = Header_size;
209 desc.flags.raw = 0; // no write, no indirect
210
211 req.tail = descno;
212 req.callback = callback;
213
214 return Handle(descno);
215 }
216
228 int add_block(Handle handle, Ptr<void> addr, l4_uint32_t size)
229 {
230 l4_uint16_t descno = _queue.alloc_descriptor();
231 if (descno == Virtqueue::Eoq)
232 return -L4_EAGAIN;
233
234 Request &req = _pending[handle.head];
235 L4virtio::Virtqueue::Desc &desc = _queue.desc(descno);
236 L4virtio::Virtqueue::Desc &prev = _queue.desc(req.tail);
237
238 prev.next = descno;
239 prev.flags.next() = true;
240
241 desc.addr = addr;
242 desc.len = size;
243 desc.flags.raw = 0;
244 if (_headers[handle.head].type > 0) // write or flush request
245 desc.flags.write() = true;
246
247 req.tail = descno;
248
249 return L4_EOK;
250 }
251
265 {
266 // add the status bit
267 auto descno = _queue.alloc_descriptor();
268 if (descno == Virtqueue::Eoq)
269 return -L4_EAGAIN;
270
271 Request &req = _pending[handle.head];
272 L4virtio::Virtqueue::Desc &desc = _queue.desc(descno);
273 L4virtio::Virtqueue::Desc &prev = _queue.desc(req.tail);
274
275 prev.next = descno;
276 prev.flags.next() = true;
277
278 desc.addr = Ptr<void>(_status_addr + descno);
279 desc.len = 1;
280 desc.flags.raw = 0;
281 desc.flags.write() = true;
282
283 req.tail = descno;
284
285 send(_queue, handle.head);
286
287 return L4_EOK;
288 }
289
300 {
301 // add the status bit
302 auto descno = _queue.alloc_descriptor();
303 if (descno == Virtqueue::Eoq)
304 return descno;
305
306 L4virtio::Virtqueue::Desc &desc = _queue.desc(descno);
307 L4virtio::Virtqueue::Desc &prev = _queue.desc(_pending[handle.head].tail);
308
309 prev.next = descno;
310 prev.flags.next() = true;
311
312 desc.addr = Ptr<void>(_status_addr + descno);
313 desc.len = 1;
314 desc.flags.raw = 0;
315 desc.flags.write() = true;
316
317 _pending[handle.head].tail = descno;
318
319 int ret = send_and_wait(_queue, handle.head);
320 unsigned char status = _status[descno];
321 free_request(handle);
322
323 if (ret < 0)
324 return ret;
325
326 switch (status)
327 {
328 case L4VIRTIO_BLOCK_S_OK: return L4_EOK;
329 case L4VIRTIO_BLOCK_S_IOERR: return -L4_EIO;
331 }
332
333 return -L4_EINVAL;
334 }
335
336 void free_request(Handle handle)
337 {
338 if (handle.head != Virtqueue::Eoq
339 && _pending[handle.head].tail != Virtqueue::Eoq)
340 _queue.free_descriptor(handle.head, _pending[handle.head].tail);
341 _pending[handle.head].tail = Virtqueue::Eoq;
342 }
343
351 {
352 for (l4_uint16_t descno = _queue.find_next_used();
353 descno != Virtqueue::Eoq;
354 descno = _queue.find_next_used()
355 )
356 {
357 if (descno >= _queue.num() || _pending[descno].tail == Virtqueue::Eoq)
358 L4Re::chksys(-L4_ENOSYS, "Bad descriptor number");
359
360 unsigned char status = _status[descno];
361 free_request(Handle(descno));
362
363 if (_pending[descno].callback)
364 _pending[descno].callback(status);
365 }
366 }
367
368protected:
370 L4::Cap<L4Re::Dataspace> _queue_ds;
371
372private:
374 l4virtio_block_header_t *_headers;
375 unsigned char *_status;
376 l4_uint64_t _header_addr;
377 l4_uint64_t _status_addr;
378 Virtqueue _queue;
379 std::vector<Request> _pending;
380};
381
382} }
bit manipulation functions
static Env const * env() noexcept
Returns the initial environment for the current task.
Definition env:103
@ Continuous
Allocate physically contiguous memory.
Definition mem_alloc:73
@ Pinned
Deprecated, use L4Re::Dma_space instead.
Definition mem_alloc:74
Unique region.
Definition rm:413
T get() const noexcept
Return the address.
Definition rm:486
C++ interface for capabilities.
Definition capability.h:222
Handle to an ongoing request.
Definition virtio-block:57
Simple class for accessing a virtio block device synchronously.
Definition virtio-block:37
int send_request(Handle handle)
Process request asynchronously.
Definition virtio-block:264
int add_block(Handle handle, Ptr< void > addr, l4_uint32_t size)
Add a data block to a request that has already been set up.
Definition virtio-block:228
int process_request(Handle handle)
Process request synchronously.
Definition virtio-block:299
l4virtio_block_config_t const & device_config() const
Return a reference to the device configuration.
Definition virtio-block:177
void process_used_queue()
Process and free all items in the used queue.
Definition virtio-block:350
void setup_device(L4::Cap< L4virtio::Device > srvcap, l4_size_t usermem, void **userdata, Ptr< void > &user_devaddr, L4::Cap< L4Re::Dataspace > qds=L4::Cap< L4Re::Dataspace >(), l4_uint32_t fmask0=-1U, l4_uint32_t fmask1=-1U)
Establish a connection to the device and set up shared memory.
Definition virtio-block:91
Handle start_request(l4_uint64_t sector, l4_uint32_t type, Callback callback)
Start the setup of a new request.
Definition virtio-block:190
Client-side implementation for a general virtio device.
Definition l4virtio:32
int max_queue_size(int num) const
Maximum queue size allowed by the device.
Definition l4virtio:230
void driver_connect(L4::Cap< L4virtio::Device > srvcap, bool manage_notify=true)
Contacts the device and starts the initial handshake.
Definition l4virtio:56
void send(Virtqueue &queue, l4_uint16_t descno)
Send a request to the device.
Definition l4virtio:312
int register_ds(L4::Cap< L4Re::Dataspace > ds, l4_umword_t offset, l4_umword_t size, l4_uint64_t *devaddr)
Share a dataspace with the device.
Definition l4virtio:196
int config_queue(int num, unsigned size, l4_uint64_t desc_addr, l4_uint64_t avail_addr, l4_uint64_t used_addr)
Send the virtqueue configuration to the device.
Definition l4virtio:212
int driver_acknowledge()
Finalize handshake with the device.
Definition l4virtio:156
int send_and_wait(Virtqueue &queue, l4_uint16_t descno)
Send a request to the device and wait for it to be processed.
Definition l4virtio:247
Driver-side implementation of a Virtqueue.
Definition virtqueue:483
void free_descriptor(l4_uint16_t head, l4_uint16_t tail)
Free a chained list of descriptors in the descriptor queue.
Definition virtqueue:643
l4_uint16_t alloc_descriptor()
Allocate and return an unused descriptor from the descriptor table.
Definition virtqueue:570
l4_uint16_t find_next_used(l4_uint32_t *len=nullptr)
Return the next finished block.
Definition virtqueue:621
void init_queue(unsigned num, void *desc, void *avail, void *used)
Initialize this virtqueue.
Definition virtqueue:534
Desc & desc(l4_uint16_t descno)
Return a reference to a descriptor in the descriptor table.
Definition virtqueue:602
Pointer used in virtio descriptors.
Definition virtqueue:64
Descriptor in the descriptor table.
Definition virtqueue:103
l4_uint16_t next
Index of the next chained descriptor.
Definition virtqueue:127
l4_uint32_t len
Length of described buffer.
Definition virtqueue:125
Flags flags
Descriptor flags.
Definition virtqueue:126
Ptr< void > addr
Address stored in descriptor.
Definition virtqueue:124
unsigned long avail_offset() const
Get the offset of the available ring from the descriptor table.
Definition virtqueue:336
static unsigned long total_size(unsigned num)
Calculate the total size for a virtqueue of the given dimensions.
Definition virtqueue:258
unsigned long used_offset() const
Get the offset of the used ring from the descriptor table.
Definition virtqueue:342
unsigned num() const
Definition virtqueue:416
Dataspace interface.
Environment interface.
Error helper.
Common factory related definitions.
unsigned int l4_size_t
Unsigned size type.
Definition l4int.h:35
unsigned int l4_uint32_t
Unsigned 32bit value.
Definition l4int.h:40
unsigned short int l4_uint16_t
Unsigned 16bit value.
Definition l4int.h:38
unsigned long long l4_uint64_t
Unsigned 64bit value.
Definition l4int.h:42
@ L4_ENOSYS
No sys.
Definition err.h:60
@ L4_EINVAL
Invalid argument.
Definition err.h:56
@ L4_ENODEV
No such thing.
Definition err.h:55
@ L4_EIO
I/O error.
Definition err.h:46
@ L4_EOK
Ok.
Definition err.h:43
@ L4_EAGAIN
Try again.
Definition err.h:49
l4_addr_t l4_round_page(l4_addr_t address) L4_NOTHROW
Round address up to the next page.
Definition consts.h:462
#define L4_PAGESHIFT
Size of a page, log2-based.
Definition consts.h:37
l4_addr_t l4_round_size(l4_addr_t value, unsigned char bits) L4_NOTHROW
Round value up to the next alignment with bits size.
Definition consts.h:473
@ L4VIRTIO_BLOCK_S_IOERR
IO error on device.
@ L4VIRTIO_BLOCK_S_UNSUPP
Operation is not supported.
@ L4VIRTIO_BLOCK_S_OK
Request finished successfully.
@ L4VIRTIO_ID_BLOCK
General block device.
Definition virtio.h:64
atomic operations header and generic implementations
L4::Detail::Unique_cap_impl< T, Smart_cap_auto< L4_FP_ALL_SPACES > > Unique_cap
Unique capability that implements automatic free and unmap of the capability selector.
Definition unique_cap:54
long chksys(long err, char const *extra="", long ret=0)
Generate C++ exception on error.
Definition error_helper:68
T chkcap(T &&cap, char const *extra="", long err=-L4_ENOMEM)
Check for valid capability or raise C++ exception.
Definition error_helper:145
Cap< T > make_cap_rw(L4::Cap< T > cap) noexcept
Make an L4::Ipc::Cap<T> for the given capability with L4_CAP_FPAGE_RW rights.
Definition ipc_types:638
L4-VIRTIO Transport C++ API.
Definition l4virtio:26
Semaphore class definition.
@ RW
Readable and writable region.
Definition rm:140
@ Search_addr
Search for a suitable address range.
Definition rm:118
write_bfm_t::Val write() const
Get the write bits ( 1 to 1 ) of raw.
Definition virtqueue:119
next_bfm_t::Val next() const
Get the next bits ( 0 to 0 ) of raw.
Definition virtqueue:117
l4_uint16_t raw
raw flags value of a virtio descriptor.
Definition virtqueue:110
Device configuration for block devices.
Header structure of a request for a block device.
l4_uint32_t type
Kind of request, see L4virtio_block_operations.
l4_uint32_t ioprio
Priority (unused)
l4_uint64_t sector
First sector to read/write.
Unique_cap / Unique_del_cap.