L4Re Operating System Framework
Interface and Usage Documentation
Loading...
Searching...
No Matches
virtio-block
1// vi:ft=cpp
2/* SPDX-License-Identifier: MIT */
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
93 void **userdata, Ptr<void> &user_devaddr,
95 l4_uint32_t fmask0 = -1U, l4_uint32_t fmask1 = -1U)
96 {
97 // Contact device.
98 driver_connect(srvcap);
99
100 if (_config->device != L4VIRTIO_ID_BLOCK)
101 L4Re::chksys(-L4_ENODEV, "Device is not a block device.");
102
103 if (_config->num_queues != 1)
104 L4Re::chksys(-L4_EINVAL, "Invalid number of queues reported.");
105
106 // Memory is shared in one large dataspace which contains queues,
107 // space for header/status and additional user-defined memory.
108 unsigned queuesz = max_queue_size(0);
109 l4_size_t totalsz = l4_round_page(usermem);
110
111 l4_uint64_t const header_offset =
112 l4_round_size(_queue.total_size(queuesz),
113 l4util_bsr(alignof(l4virtio_block_header_t)));
114 l4_uint64_t const status_offset = header_offset + queuesz * Header_size;
115 l4_uint64_t const usermem_offset = l4_round_page(status_offset + queuesz);
116
117 // reserve space for one header/status per descriptor
118 // TODO Should be reduced to 1/3 but this way no freelist is needed.
119 totalsz += usermem_offset;
120
121 auto *e = L4Re::Env::env();
122 if (!qds.is_valid())
123 {
124 _ds = L4Re::chkcap(L4Re::Util::make_unique_cap<L4Re::Dataspace>(),
125 "Allocate queue dataspace capability");
126 L4Re::chksys(e->mem_alloc()->alloc(totalsz, _ds.get(),
129 "Allocate memory for virtio structures");
130 _queue_ds = _ds.get();
131 }
132 else
133 {
134 if (qds->size() < totalsz)
135 L4Re::chksys(-L4_EINVAL, "External queue dataspace too small.");
136 _queue_ds = qds;
137 }
138
139 // Now sort out which region goes where in the dataspace.
140 L4Re::chksys(e->rm()->attach(&_queue_region, totalsz,
142 L4::Ipc::make_cap_rw(_queue_ds), 0,
144 "Attach dataspace for virtio structures");
145
146 l4_uint64_t devaddr;
147 L4Re::chksys(register_ds(_queue_ds, 0, totalsz, &devaddr),
148 "Register queue dataspace with device");
149
150 _queue.init_queue(queuesz, _queue_region.get());
151
152 config_queue(0, queuesz, devaddr, devaddr + _queue.avail_offset(),
153 devaddr + _queue.used_offset());
154
155 _header_addr = devaddr + header_offset;
156 _headers = reinterpret_cast<l4virtio_block_header_t *>(_queue_region.get()
157 + header_offset);
158
159 _status_addr = devaddr + status_offset;
160 _status = _queue_region.get() + status_offset;
161
162 user_devaddr = Ptr<void>(devaddr + usermem_offset);
163 if (userdata)
164 *userdata = _queue_region.get() + usermem_offset;
165
166 // setup the callback mechanism
167 _pending.assign(queuesz, Request());
168
169 // Finish handshake with device.
170 _config->driver_features_map[0] = fmask0;
171 _config->driver_features_map[1] = fmask1;
173 }
174
179 {
180 return *_config->device_config<l4virtio_block_config_t>();
181 }
182
192 Callback callback)
193 {
194 l4_uint16_t descno = _queue.alloc_descriptor();
195 if (descno == Virtqueue::Eoq)
196 return Handle(Virtqueue::Eoq);
197
198 L4virtio::Virtqueue::Desc &desc = _queue.desc(descno);
199 Request &req = _pending[descno];
200
201 // setup the header
202 l4virtio_block_header_t &head = _headers[descno];
203 head.type = type;
204 head.ioprio = 0;
205 head.sector = sector;
206
207 // and put it in the descriptor
208 desc.addr = Ptr<void>(_header_addr + descno * Header_size);
209 desc.len = Header_size;
210 desc.flags.raw = 0; // no write, no indirect
211
212 req.tail = descno;
213 req.callback = callback;
214
215 return Handle(descno);
216 }
217
229 int add_block(Handle handle, Ptr<void> addr, l4_uint32_t size)
230 {
231 l4_uint16_t descno = _queue.alloc_descriptor();
232 if (descno == Virtqueue::Eoq)
233 return -L4_EAGAIN;
234
235 Request &req = _pending[handle.head];
236 L4virtio::Virtqueue::Desc &desc = _queue.desc(descno);
237 L4virtio::Virtqueue::Desc &prev = _queue.desc(req.tail);
238
239 prev.next = descno;
240 prev.flags.next() = true;
241
242 desc.addr = addr;
243 desc.len = size;
244 desc.flags.raw = 0;
245 if (_headers[handle.head].type > 0) // write or flush request
246 desc.flags.write() = true;
247
248 req.tail = descno;
249
250 return L4_EOK;
251 }
252
266 {
267 // add the status bit
268 auto descno = _queue.alloc_descriptor();
269 if (descno == Virtqueue::Eoq)
270 return -L4_EAGAIN;
271
272 Request &req = _pending[handle.head];
273 L4virtio::Virtqueue::Desc &desc = _queue.desc(descno);
274 L4virtio::Virtqueue::Desc &prev = _queue.desc(req.tail);
275
276 prev.next = descno;
277 prev.flags.next() = true;
278
279 desc.addr = Ptr<void>(_status_addr + descno);
280 desc.len = 1;
281 desc.flags.raw = 0;
282 desc.flags.write() = true;
283
284 req.tail = descno;
285
286 send(_queue, handle.head);
287
288 return L4_EOK;
289 }
290
307 {
308 // add the status bit
309 auto descno = _queue.alloc_descriptor();
310 if (descno == Virtqueue::Eoq)
311 return -L4_EAGAIN;
312
313 L4virtio::Virtqueue::Desc &desc = _queue.desc(descno);
314 L4virtio::Virtqueue::Desc &prev = _queue.desc(_pending[handle.head].tail);
315
316 prev.next = descno;
317 prev.flags.next() = true;
318
319 desc.addr = Ptr<void>(_status_addr + descno);
320 desc.len = 1;
321 desc.flags.raw = 0;
322 desc.flags.write() = true;
323
324 _pending[handle.head].tail = descno;
325
326 int ret = send_and_wait(_queue, handle.head);
327 unsigned char status = _status[descno];
328 free_request(handle);
329
330 if (ret < 0)
331 return ret;
332
333 switch (status)
334 {
335 case L4VIRTIO_BLOCK_S_OK: return L4_EOK;
336 case L4VIRTIO_BLOCK_S_IOERR: return -L4_EIO;
338 }
339
340 return -L4_EINVAL;
341 }
342
343 void free_request(Handle handle)
344 {
345 if (handle.head != Virtqueue::Eoq
346 && _pending[handle.head].tail != Virtqueue::Eoq)
347 _queue.free_descriptor(handle.head, _pending[handle.head].tail);
348 _pending[handle.head].tail = Virtqueue::Eoq;
349 }
350
358 {
359 for (l4_uint16_t descno = _queue.find_next_used();
360 descno != Virtqueue::Eoq;
361 descno = _queue.find_next_used()
362 )
363 {
364 if (descno >= _queue.num() || _pending[descno].tail == Virtqueue::Eoq)
365 L4Re::chksys(-L4_ENOSYS, "Bad descriptor number");
366
367 unsigned char status = _status[descno];
368 free_request(Handle(descno));
369
370 if (_pending[descno].callback)
371 _pending[descno].callback(status);
372 }
373 }
374
375protected:
377 L4::Cap<L4Re::Dataspace> _queue_ds;
378
379private:
381 l4virtio_block_header_t *_headers;
382 unsigned char *_status;
383 l4_uint64_t _header_addr;
384 l4_uint64_t _status_addr;
385 Virtqueue _queue;
386 std::vector<Request> _pending;
387};
388
389} }
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:435
T get() const noexcept
Return the address.
Definition rm:508
C++ interface for capabilities.
Definition capability.h:219
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:265
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:229
int process_request(Handle handle)
Process request synchronously.
Definition virtio-block:306
l4virtio_block_config_t const & device_config() const
Return a reference to the device configuration.
Definition virtio-block:178
void process_used_queue()
Process and free all items in the used queue.
Definition virtio-block:357
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:92
Handle start_request(l4_uint64_t sector, l4_uint32_t type, Callback callback)
Start the setup of a new request.
Definition virtio-block:191
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:477
void free_descriptor(l4_uint16_t head, l4_uint16_t tail)
Free a chained list of descriptors in the descriptor queue.
Definition virtqueue:637
l4_uint16_t alloc_descriptor()
Allocate and return an unused descriptor from the descriptor table.
Definition virtqueue:564
l4_uint16_t find_next_used(l4_uint32_t *len=nullptr)
Return the next finished block.
Definition virtqueue:615
void init_queue(unsigned num, void *desc, void *avail, void *used)
Initialize this virtqueue.
Definition virtqueue:528
Desc & desc(l4_uint16_t descno)
Return a reference to a descriptor in the descriptor table.
Definition virtqueue:596
Pointer used in virtio descriptors.
Definition virtqueue:55
Descriptor in the descriptor table.
Definition virtqueue:94
l4_uint16_t next
Index of the next chained descriptor.
Definition virtqueue:118
l4_uint32_t len
Length of described buffer.
Definition virtqueue:116
Flags flags
Descriptor flags.
Definition virtqueue:117
Ptr< void > addr
Address stored in descriptor.
Definition virtqueue:115
unsigned long avail_offset() const
Get the offset of the available ring from the descriptor table.
Definition virtqueue:329
static unsigned long total_size(unsigned num)
Calculate the total size for a virtqueue of the given dimensions.
Definition virtqueue:251
unsigned long used_offset() const
Get the offset of the used ring from the descriptor table.
Definition virtqueue:335
unsigned num() const
Definition virtqueue:410
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:61
@ L4_EINVAL
Invalid argument.
Definition err.h:57
@ 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:659
L4-VIRTIO Transport C++ API.
Definition l4virtio:26
Semaphore class definition.
@ RW
Readable and writable region.
Definition rm:150
@ Search_addr
Search for a suitable address range.
Definition rm:125
constexpr next_bfm_t::Val next() const
Get the next bits ( 0 to 0 ) of raw.
Definition virtqueue:108
constexpr write_bfm_t::Val write() const
Get the write bits ( 1 to 1 ) of raw.
Definition virtqueue:110
l4_uint16_t raw
raw flags value of a virtio descriptor.
Definition virtqueue:101
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.