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-2022, 2024 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
95 void **userdata, Ptr<void> &user_devaddr,
97 l4_uint32_t fmask0 = -1U, l4_uint32_t fmask1 = -1U,
98 L4Re::Util::Dbg msg_dev = L4Re::Util::Dbg{2})
99 {
100 // Contact device.
101 driver_connect(srvcap);
102
103 if (_config->device != L4VIRTIO_ID_BLOCK)
104 L4Re::chksys(-L4_ENODEV, "Device is not a block device.");
105
106 if (_config->num_queues != 1)
107 L4Re::chksys(-L4_EINVAL, "Invalid number of queues reported.");
108
109 // Memory is shared in one large dataspace which contains queues,
110 // space for header/status and additional user-defined memory.
111 unsigned queuesz = max_queue_size(0);
112 l4_size_t totalsz = l4_round_page(usermem);
113
114 l4_uint64_t const header_offset =
115 l4_round_size(_queue.total_size(queuesz),
116 l4util_bsr(alignof(l4virtio_block_header_t)));
117 l4_uint64_t const status_offset = header_offset + queuesz * Header_size;
118 l4_uint64_t const usermem_offset = l4_round_page(status_offset + queuesz);
119
120 // reserve space for one header/status per descriptor
121 // TODO Should be reduced to 1/3 but this way no freelist is needed.
122 totalsz += usermem_offset;
123
124 auto *e = L4Re::Env::env();
125 if (!qds.is_valid())
126 {
128 "Allocate queue dataspace capability");
129 L4Re::chksys(e->mem_alloc()->alloc(totalsz, _ds.get(),
132 "Allocate memory for virtio structures");
133 _queue_ds = _ds.get();
134 }
135 else
136 {
137 if (qds->size() < totalsz)
138 L4Re::chksys(-L4_EINVAL, "External queue dataspace too small.");
139 _queue_ds = qds;
140 }
141
142 // Now sort out which region goes where in the dataspace.
143 L4Re::chksys(e->rm()->attach(&_queue_region, totalsz,
145 L4::Ipc::make_cap_rw(_queue_ds), 0,
147 "Attach dataspace for virtio structures");
148
149 l4_uint64_t devaddr;
150 L4Re::chksys(register_ds(_queue_ds, 0, totalsz, &devaddr),
151 "Register queue dataspace with device");
152
153 _queue.init_queue(queuesz, _queue_region.get(), msg_dev);
154
155 config_queue(0, queuesz, devaddr, devaddr + _queue.avail_offset(),
156 devaddr + _queue.used_offset());
157
158 _header_addr = devaddr + header_offset;
159 _headers = reinterpret_cast<l4virtio_block_header_t *>(_queue_region.get()
160 + header_offset);
161
162 _status_addr = devaddr + status_offset;
163 _status = _queue_region.get() + status_offset;
164
165 user_devaddr = Ptr<void>(devaddr + usermem_offset);
166 if (userdata)
167 *userdata = _queue_region.get() + usermem_offset;
168
169 // setup the callback mechanism
170 _pending.assign(queuesz, Request());
171
172 // Finish handshake with device.
173 _config->driver_features_map[0] = fmask0;
174 _config->driver_features_map[1] = fmask1;
176 }
177
182 {
183 return *_config->device_config<l4virtio_block_config_t>();
184 }
185
195 Callback callback)
196 {
197 l4_uint16_t descno = _queue.alloc_descriptor();
198 if (descno == Virtqueue::Eoq)
199 return Handle(Virtqueue::Eoq);
200
201 L4virtio::Virtqueue::Desc &desc = _queue.desc(descno);
202 Request &req = _pending[descno];
203
204 // setup the header
205 l4virtio_block_header_t &head = _headers[descno];
206 head.type = type;
207 head.ioprio = 0;
208 head.sector = sector;
209
210 // and put it in the descriptor
211 desc.addr = Ptr<void>(_header_addr + descno * Header_size);
212 desc.len = Header_size;
213 desc.flags.raw = 0; // no write, no indirect
214
215 req.tail = descno;
216 req.callback = callback;
217
218 return Handle(descno);
219 }
220
232 int add_block(Handle handle, Ptr<void> addr, l4_uint32_t size)
233 {
234 l4_uint16_t descno = _queue.alloc_descriptor();
235 if (descno == Virtqueue::Eoq)
236 return -L4_EAGAIN;
237
238 Request &req = _pending[handle.head];
239 L4virtio::Virtqueue::Desc &desc = _queue.desc(descno);
240 L4virtio::Virtqueue::Desc &prev = _queue.desc(req.tail);
241
242 prev.next = descno;
243 prev.flags.next() = true;
244
245 desc.addr = addr;
246 desc.len = size;
247 desc.flags.raw = 0;
248 /* A data buffer is device-writable exactly when the device writes into it:
249 * read (T_IN) and get-id requests. For write/flush/discard the device
250 * reads the buffer, so it stays device-readable. Strict virtio transports
251 * (e.g. a real virtio-blk device) honour this flag; L4virtio device
252 * backends key off the request type instead. */
253 auto type = _headers[handle.head].type;
254 if (type == L4VIRTIO_BLOCK_T_IN || type == L4VIRTIO_BLOCK_T_GET_ID)
255 desc.flags.write() = true;
256
257 req.tail = descno;
258
259 return L4_EOK;
260 }
261
275 {
276 // add the status bit
277 auto descno = _queue.alloc_descriptor();
278 if (descno == Virtqueue::Eoq)
279 return -L4_EAGAIN;
280
281 Request &req = _pending[handle.head];
282 L4virtio::Virtqueue::Desc &desc = _queue.desc(descno);
283 L4virtio::Virtqueue::Desc &prev = _queue.desc(req.tail);
284
285 prev.next = descno;
286 prev.flags.next() = true;
287
288 desc.addr = Ptr<void>(_status_addr + descno);
289 desc.len = 1;
290 desc.flags.raw = 0;
291 desc.flags.write() = true;
292
293 req.tail = descno;
294
295 send(_queue, handle.head);
296
297 return L4_EOK;
298 }
299
316 {
317 // add the status bit
318 auto descno = _queue.alloc_descriptor();
319 if (descno == Virtqueue::Eoq)
320 return -L4_EAGAIN;
321
322 L4virtio::Virtqueue::Desc &desc = _queue.desc(descno);
323 L4virtio::Virtqueue::Desc &prev = _queue.desc(_pending[handle.head].tail);
324
325 prev.next = descno;
326 prev.flags.next() = true;
327
328 desc.addr = Ptr<void>(_status_addr + descno);
329 desc.len = 1;
330 desc.flags.raw = 0;
331 desc.flags.write() = true;
332
333 _pending[handle.head].tail = descno;
334
335 int ret = send_and_wait(_queue, handle.head);
336 unsigned char status = _status[descno];
337 free_request(handle);
338
339 if (ret < 0)
340 return ret;
341
342 switch (status)
343 {
344 case L4VIRTIO_BLOCK_S_OK: return L4_EOK;
345 case L4VIRTIO_BLOCK_S_IOERR: return -L4_EIO;
347 }
348
349 return -L4_EINVAL;
350 }
351
352 void free_request(Handle handle)
353 {
354 if (handle.head != Virtqueue::Eoq
355 && _pending[handle.head].tail != Virtqueue::Eoq)
356 _queue.free_descriptor(handle.head, _pending[handle.head].tail);
357 _pending[handle.head].tail = Virtqueue::Eoq;
358 }
359
367 {
368 for (l4_uint16_t descno = _queue.find_next_used();
369 descno != Virtqueue::Eoq;
370 descno = _queue.find_next_used()
371 )
372 {
373 if (descno >= _queue.num() || _pending[descno].tail == Virtqueue::Eoq)
374 L4Re::chksys(-L4_ENOSYS, "Bad descriptor number");
375
376 unsigned char status = _status[descno];
377 free_request(Handle(descno));
378
379 if (_pending[descno].callback)
380 _pending[descno].callback(status);
381 }
382 }
383
384protected:
386 L4::Cap<L4Re::Dataspace> _queue_ds;
387
388private:
390 l4virtio_block_header_t *_headers;
391 unsigned char *_status;
392 l4_uint64_t _header_addr;
393 l4_uint64_t _status_addr;
394 Virtqueue _queue;
395 std::vector<Request> _pending;
396};
397
398} }
bit manipulation functions
static Env const * env() noexcept
Returns the initial environment for the current task.
Definition env:96
@ Continuous
Allocate physically contiguous memory.
Definition mem_alloc:64
@ Pinned
Deprecated, use L4Re::Dma_space instead.
Definition mem_alloc:65
Unique region.
Definition rm:432
C++ interface for capabilities.
Definition capability.h:249
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:274
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:232
int process_request(Handle handle)
Process request synchronously.
Definition virtio-block:315
l4virtio_block_config_t const & device_config() const
Return a reference to the device configuration.
Definition virtio-block:181
void process_used_queue()
Process and free all items in the used queue.
Definition virtio-block:366
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, L4Re::Util::Dbg msg_dev=L4Re::Util::Dbg{2})
Establish a connection to the device and set up shared memory.
Definition virtio-block:94
Handle start_request(l4_uint64_t sector, l4_uint32_t type, Callback callback)
Start the setup of a new request.
Definition virtio-block:194
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:490
void free_descriptor(l4_uint16_t head, l4_uint16_t tail)
Free a chained list of descriptors in the descriptor queue.
Definition virtqueue:665
void init_queue(unsigned num, void *desc, void *avail, void *used, L4Re::Util::Dbg msg_dev=L4Re::Util::Dbg{2})
Initialize this virtqueue.
Definition virtqueue:543
Pointer used in virtio descriptors.
Definition virtqueue:54
Descriptor in the descriptor table.
Definition virtqueue:93
l4_uint16_t next
Index of the next chained descriptor.
Definition virtqueue:117
l4_uint32_t len
Length of described buffer.
Definition virtqueue:115
Flags flags
Descriptor flags.
Definition virtqueue:116
Ptr< void > addr
Address stored in descriptor.
Definition virtqueue:114
unsigned long avail_offset() const
Get the offset of the available ring from the descriptor table.
Definition virtqueue:331
static unsigned long total_size(unsigned num)
Calculate the total size for a virtqueue of the given dimensions.
Definition virtqueue:253
unsigned long used_offset() const
Get the offset of the used ring from the descriptor table.
Definition virtqueue:337
T get() const noexcept
Return the address.
Definition rm:505
Dataspace interface.
Environment interface.
Error helper.
Common factory related definitions.
unsigned int l4_size_t
Unsigned size type.
Definition l4int.h:22
unsigned int l4_uint32_t
Unsigned 32bit value.
Definition l4int.h:29
unsigned short int l4_uint16_t
Unsigned 16bit value.
Definition l4int.h:27
unsigned long long l4_uint64_t
Unsigned 64bit value.
Definition l4int.h:31
@ L4_ENOSYS
No sys.
Definition err.h:52
@ L4_EINVAL
Invalid argument.
Definition err.h:47
@ L4_ENODEV
No such thing.
Definition err.h:45
@ L4_EIO
I/O error.
Definition err.h:36
@ L4_EOK
Ok.
Definition err.h:33
@ L4_EAGAIN
Try again.
Definition err.h:39
l4_addr_t l4_round_page(l4_addr_t address) L4_NOTHROW
Round address up to the next page.
Definition consts.h:484
#define L4_PAGESHIFT
Size of a page, log2-based.
Definition consts.h:26
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:495
@ L4VIRTIO_BLOCK_T_GET_ID
Get device ID.
@ L4VIRTIO_BLOCK_T_IN
Read from device.
@ 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:65
Common constants.
atomic operations header and generic implementations
Unique_cap< T > make_unique_cap()
Allocate a capability slot and wrap it in an Unique_cap.
Definition unique_cap:56
L4::Detail::Unique_cap_impl< T, L4Re::Util::Smart_cap_auto< L4_FP_ALL_SPACES > > Unique_cap
Unique capability that implements automatic free and unmap of the capability selector.
Definition unique_cap:43
T chkcap(T &&cap, char const *extra="", l4_ret_t err=-L4_ENOMEM)
Check for valid capability or raise C++ exception.
Definition error_helper:149
l4_ret_t chksys(l4_ret_t err, char const *extra="", l4_ret_t ret=0)
Generate C++ exception on error.
Definition error_helper:72
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:855
L4-VIRTIO Transport C++ API.
Definition l4virtio:26
Semaphore class definition.
@ RW
Readable and writable region.
Definition rm:141
@ Search_addr
Search for a suitable address range.
Definition rm:115
constexpr next_bfm_t::Val next() const
Get the next bits (0 to 0) of raw.
Definition virtqueue:107
constexpr write_bfm_t::Val write() const
Get the write bits (1 to 1) of raw.
Definition virtqueue:109
l4_uint16_t raw
raw flags value of a virtio descriptor.
Definition virtqueue:100
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.
l4_uint32_t device
device ID
Definition virtio.h:140
l4_uint32_t num_queues
number of virtqueues
Definition virtio.h:153
Unique_cap / Unique_del_cap.