L4Re - L4 Runtime Environment
virtio-block
1 // vi:ft=cpp
2 /* SPDX-License-Identifier: GPL-2.0-only or License-Ref-kk-custom */
3 /*
4  * Copyright (C) 2017-2020 Kernkonzept GmbH.
5  * Author(s): Sarah Hoffmann <sarah.hoffmann@kernkonzept.com>
6  *
7  */
8 #pragma once
9 
10 #include <l4/cxx/unique_ptr>
11 #include <l4/re/util/unique_cap>
12 
13 #include <climits>
14 
15 #include <l4/l4virtio/virtio.h>
16 #include <l4/l4virtio/virtio_block.h>
17 #include <l4/l4virtio/server/l4virtio>
18 #include <l4/sys/cxx/ipc_epiface>
19 
20 namespace L4virtio { namespace Svr {
21 
22 template <typename Ds_data> class Block_dev_base;
23 
27 template<typename Ds_data>
29 {
30  friend class Block_dev_base<Ds_data>;
31  enum { Header_size = sizeof(l4virtio_block_header_t) };
32 
33 public:
34  struct Data_block
35  {
39  void *addr;
41  l4_uint32_t len;
42 
43  Data_block() = default;
44 
45  Data_block(Driver_mem_region_t<Ds_data> *m, Virtqueue::Desc const &desc,
46  Request_processor const *)
47  : mem(m), addr(m->local(desc.addr)), len(desc.len)
48  {}
49  };
50 
51 
52 
63  unsigned data_size() const
64  {
66  Data_block data;
67 
68  rp.start(_mem_list, _request, &data);
69 
70  unsigned total = data.len;
71 
72  try
73  {
74  while (rp.has_more())
75  {
76  rp.next(_mem_list, &data);
77  total += data.len;
78  }
79  }
80  catch (Bad_descriptor const &e)
81  {
82  // need to convert the exception because e contains a raw pointer to rp
83  throw L4::Runtime_error(-L4_EIO, "bad virtio descriptor");
84  }
85 
86  if (total < Header_size + 1)
87  throw L4::Runtime_error(-L4_EIO, "virtio request too short");
88 
89  return total - Header_size - 1;
90  }
91 
95  bool has_more()
96  {
97  // peek into the remaining data
98  while (_data.len == 0 && _rp.has_more())
99  _rp.next(_mem_list, &_data);
100 
101  // there always must be one byte left for status
102  return (_data.len > 1 || _rp.has_more());
103  }
104 
113  Data_block next_block()
114  {
115  Data_block out;
116 
117  if (_data.len == 0)
118  {
119  if (!_rp.has_more())
121  "No more data blocks in virtio request");
122 
123  if (_todo_blocks == 0)
125  --_todo_blocks;
126 
127  _rp.next(_mem_list, &_data);
128  }
129 
130  if (_data.len > _max_block_size)
132 
133  out = _data;
134 
135  if (!_rp.has_more())
136  {
137  --(out.len);
138  _data.len = 1;
139  _data.addr = static_cast<char *>(_data.addr) + out.len;
140  }
141  else
142  _data.len = 0; // is consumed
143 
144  return out;
145  }
146 
149  { return _header; }
150 
151 private:
152  Block_request(Virtqueue::Request req, Driver_mem_list_t<Ds_data> *mem_list,
153  unsigned max_blocks, l4_uint32_t max_block_size)
154  : _mem_list(mem_list),
155  _request(req),
156  _todo_blocks(max_blocks),
157  _max_block_size(max_block_size)
158  {
159  // read header which should be in the first block
160  _rp.start(mem_list, _request, &_data);
161  --_todo_blocks;
162 
163  if (_data.len < Header_size)
165 
166  _header = *(static_cast<l4virtio_block_header_t *>(_data.addr));
167 
168  _data.addr = static_cast<char *>(_data.addr) + Header_size;
169  _data.len -= Header_size;
170 
171  // if there is no space for status bit we cannot really recover
172  if (!_rp.has_more() && _data.len == 0)
174  }
175 
176  int release_request(Virtqueue *queue, l4_uint8_t status, unsigned sz)
177  {
178  // write back status
179  // If there was an error on the way or the status byte is in its
180  // own block, fast-forward to the last block.
181  if (_rp.has_more())
182  {
183  while (_rp.next(_mem_list, &_data) && _todo_blocks > 0)
184  --_todo_blocks;
185 
186  if (_todo_blocks > 0 && _data.len > 0)
187  *(static_cast<l4_uint8_t *>(_data.addr) + _data.len - 1) = status;
188  else
189  return -L4_EIO; // too many data blocks
190  }
191  else if (_data.len > 0)
192  *(static_cast<l4_uint8_t *>(_data.addr)) = status;
193  else
194  return -L4_EIO; // no space for final status byte
195 
196  // now release the head
197  queue->consumed(_request, sz);
198 
199  return L4_EOK;
200  }
201 
207  Driver_mem_list_t<Ds_data> *_mem_list;
209  l4virtio_block_header_t _header;
211  Request_processor _rp;
213  Data_block _data;
214 
216  Virtqueue::Request _request;
218  unsigned _todo_blocks;
220  l4_uint32_t _max_block_size;
221 };
222 
223 struct Block_features : public Dev_config::Features
224 {
225  Block_features() = default;
226  Block_features(l4_uint32_t raw) : Dev_config::Features(raw) {}
227 
229  CXX_BITFIELD_MEMBER( 1, 1, size_max, raw);
231  CXX_BITFIELD_MEMBER( 2, 2, seg_max, raw);
233  CXX_BITFIELD_MEMBER( 4, 4, geometry, raw);
235  CXX_BITFIELD_MEMBER( 5, 5, ro, raw);
237  CXX_BITFIELD_MEMBER( 6, 6, blk_size, raw);
239  CXX_BITFIELD_MEMBER( 9, 9, flush, raw);
241  CXX_BITFIELD_MEMBER(10, 10, topology, raw);
243  CXX_BITFIELD_MEMBER(11, 11, config_wce, raw);
245  CXX_BITFIELD_MEMBER(13, 13, discard, raw);
247  CXX_BITFIELD_MEMBER(14, 14, write_zeroes, raw);
248 };
249 
250 
256 template <typename Ds_data>
257 class Block_dev_base : public L4virtio::Svr::Device_t<Ds_data>
258 {
259 private:
260  class Irq_object : public L4::Irqep_t<Irq_object>
261  {
262  public:
263  Irq_object(Block_dev_base<Ds_data> *parent) : _parent(parent) {}
264 
265  void handle_irq()
266  {
267  _parent->kick();
268  }
269 
270  private:
271  Block_dev_base<Ds_data> *_parent;
272  };
273  Irq_object _irq_handler;
274 
275 protected:
276  L4::Epiface *irq_iface()
277  { return &_irq_handler; }
278 
279 private:
280  L4Re::Util::Unique_cap<L4::Irq> _kick_guest_irq;
281  Virtqueue _queue;
282  unsigned _vq_max;
283  l4_uint32_t _max_block_size = UINT_MAX;
284  Dev_config_t<l4virtio_block_config_t> _dev_config;
285 
286 public:
287  typedef Block_request<Ds_data> Request;
288 
289 protected:
290  Block_features negotiated_features() const
291  { return _dev_config.negotiated_features(0); }
292 
293  Block_features device_features() const
294  { return _dev_config.host_features(0); }
295 
296  void set_device_features(Block_features df)
297  { _dev_config.host_features(0) = df.raw; }
298 
309  {
310  _dev_config.priv_config()->size_max = sz;
311  Block_features df = device_features();
312  df.size_max() = true;
313  set_device_features(df);
314 
315  _max_block_size = sz;
316  }
317 
323  {
324  _dev_config.priv_config()->seg_max = sz;
325  Block_features df = device_features();
326  df.seg_max() = true;
327  set_device_features(df);
328  }
329 
333  void set_geometry(l4_uint16_t cylinders, l4_uint8_t heads, l4_uint8_t sectors)
334  {
335  l4virtio_block_config_t volatile *pc = _dev_config.priv_config();
336  pc->geometry.cylinders = cylinders;
337  pc->geometry.heads = heads;
338  pc->geometry.sectors = sectors;
339  Block_features df = device_features();
340  df.geometry() = true;
341  set_device_features(df);
342  }
343 
351  {
352  _dev_config.priv_config()->blk_size = sz;
353  Block_features df = device_features();
354  df.blk_size() = true;
355  set_device_features(df);
356  }
357 
366  void set_topology(l4_uint8_t physical_block_exp,
367  l4_uint8_t alignment_offset,
368  l4_uint32_t min_io_size,
369  l4_uint32_t opt_io_size)
370  {
371  l4virtio_block_config_t volatile *pc = _dev_config.priv_config();
372  pc->topology.physical_block_exp = physical_block_exp;
373  pc->topology.alignment_offset = alignment_offset;
374  pc->topology.min_io_size = min_io_size;
375  pc->topology.opt_io_size = opt_io_size;
376  Block_features df = device_features();
377  df.topology() = true;
378  set_device_features(df);
379  }
380 
382  void set_flush()
383  {
384  Block_features df = device_features();
385  df.flush() = true;
386  set_device_features(df);
387  }
388 
393  void set_config_wce(l4_uint8_t writeback)
394  {
395  l4virtio_block_config_t volatile *pc = _dev_config.priv_config();
396  pc->writeback = writeback;
397  Block_features df = device_features();
398  df.config_wce() = true;
399  set_device_features(df);
400  }
401 
407  {
408  l4virtio_block_config_t volatile *pc = _dev_config.priv_config();
409  return pc->writeback;
410  }
411 
420  void set_discard(l4_uint32_t max_discard_sectors, l4_uint32_t max_discard_seg,
421  l4_uint32_t discard_sector_alignment)
422  {
423  l4virtio_block_config_t volatile *pc = _dev_config.priv_config();
424  pc->max_discard_sectors = max_discard_sectors;
425  pc->max_discard_seg = max_discard_seg;
426  pc->discard_sector_alignment = discard_sector_alignment;
427  Block_features df = device_features();
428  df.discard() = true;
429  set_device_features(df);
430  }
431 
440  void set_write_zeroes(l4_uint32_t max_write_zeroes_sectors,
441  l4_uint32_t max_write_zeroes_seg,
442  l4_uint8_t write_zeroes_may_unmap)
443  {
444  l4virtio_block_config_t volatile *pc = _dev_config.priv_config();
445  pc->max_write_zeroes_sectors = max_write_zeroes_sectors;
446  pc->max_write_zeroes_seg = max_write_zeroes_seg;
447  pc->write_zeroes_may_unmap = write_zeroes_may_unmap;
448  Block_features df = device_features();
449  df.write_zeroes() = true;
450  set_device_features(df);
451  }
452 
453 public:
462  Block_dev_base(l4_uint32_t vendor, unsigned queue_size, l4_uint64_t capacity,
463  bool read_only)
464  : L4virtio::Svr::Device_t<Ds_data>(&_dev_config), _irq_handler(this),
465  _vq_max(queue_size), _dev_config(vendor, L4VIRTIO_ID_BLOCK, 1)
466  {
467  this->reset_queue_config(0, queue_size);
468 
469  Block_features df(0);
470  df.ring_indirect_desc() = true;
471  df.ro() = read_only;
472  set_device_features(df);
473 
474  _dev_config.priv_config()->capacity = capacity;
475  }
476 
477 
492  virtual bool process_request(cxx::unique_ptr<Request> &&req) = 0;
493 
497  virtual void reset_device() = 0;
498 
506  virtual bool reset_client() { return false; }
507 
511  virtual bool queue_stopped() = 0;
512 
524  void finalize_request(cxx::unique_ptr<Request> req, unsigned sz,
526  {
527  if (_dev_config.status().fail_state() || !_queue.ready())
528  return;
529 
530  if (req->release_request(&_queue, status, sz) < 0)
531  this->device_error();
532 
533  // XXX not implemented
534  // _dev_config->irq_status |= 1;
535  _kick_guest_irq->trigger();
536 
537  // Request can be dropped here.
538  }
539 
540  int reconfig_queue(unsigned idx)
541  {
542  if (idx == 0 && this->setup_queue(&_queue, 0, _vq_max))
543  return 0;
544 
545  return -L4_EINVAL;
546  }
547 
548  void reset()
549  {
550  _queue.disable();
551  _dev_config.reset_queue(0, _vq_max);
552  _dev_config.reset_hdr();
553  reset_device();
554  }
555 
556 protected:
557  void kick()
558  {
559  if (!_queue.ready() || queue_stopped())
560  return;
561 
562  while (!_dev_config.status().fail_state())
563  {
564  auto r = _queue.next_avail();
565  if (!r)
566  return;
567 
568  try
569  {
570  cxx::unique_ptr<Request>
571  cur{new Request(r, &(this->_mem_info), _vq_max, _max_block_size)};
572 
573  if (!process_request(cxx::move(cur)))
574  return;
575  }
576  catch (Bad_descriptor const &e)
577  {
578  this->device_error();
579  return;
580  }
581  }
582  }
583 
584 private:
585  L4::Cap<L4::Irq> device_notify_irq() const
586  {
587  return L4::cap_cast<L4::Irq>(_irq_handler.obj_cap());
588  }
589 
590  void register_single_driver_irq()
591  {
592  if (_kick_guest_irq)
593  {
594  // client has changed, purge all old state
595  reset();
596  if (!reset_client())
597  L4Re::chksys(-L4_EINVAL, "Client already connected.");
598  }
599 
600  _kick_guest_irq = L4Re::Util::Unique_cap<L4::Irq>(
601  L4Re::chkcap(this->server_iface()->template rcv_cap<L4::Irq>(0)));
602 
603  L4Re::chksys(this->server_iface()->realloc_rcv_cap(0));
604  }
605 
606  void trigger_driver_config_irq() const override
607  {
608  _kick_guest_irq->trigger();
609  }
610 
611  bool check_queues()
612  {
613  if (!_queue.ready())
614  {
615  reset();
616  return false;
617  }
618 
619  return true;
620  }
621 };
622 
623 template <typename Ds_data>
624 struct Block_dev
625 : Block_dev_base<Ds_data>,
626  L4::Epiface_t<Block_dev<Ds_data>, L4virtio::Device>
627 {
629 
640  L4::Cap<void> register_obj(L4::Registry_iface *registry,
641  char const *service = 0)
642  {
643  L4Re::chkcap(registry->register_irq_obj(this->irq_iface()));
644  L4::Cap<void> ret;
645  if (service)
646  ret = registry->register_obj(this, service);
647  else
648  ret = registry->register_obj(this);
649  L4Re::chkcap(ret);
650 
651  return ret;
652  }
653 
654  L4::Cap<void> register_obj(L4::Registry_iface *registry,
656  {
657  L4Re::chkcap(registry->register_irq_obj(this->irq_iface()));
658 
659  return L4Re::chkcap(registry->register_obj(this, ep));
660  }
661 
662 protected:
663  L4::Ipc_svr::Server_iface *server_iface() const override
664  {
665  return this->L4::Epiface::server_iface();
666  }
667 };
668 
669 } }
Abstract interface for object registries.
Definition: ipc_epiface:330
bool has_more() const
Are there more chained descriptors?
Definition: virtio:475
virtual bool reset_client()
The client requests reinitialisation of the connection.
Definition: virtio-block:506
Descriptor in the descriptor table.
Definition: virtqueue:95
Block_dev_base(l4_uint32_t vendor, unsigned queue_size, l4_uint64_t capacity, bool read_only)
Create a new virtio block device.
Definition: virtio-block:462
void set_config_wce(l4_uint8_t writeback)
Sets cache mode and enables the the writeback toggle.
Definition: virtio-block:393
Virtqueue implementation for the device.
Definition: virtio:87
Invalid argument.
Definition: err.h:56
Server-side L4-VIRTIO device stub.
Definition: l4virtio:782
General block device.
Definition: virtio.h:61
Unique_cap / Unique_del_cap.
l4virtio_block_header_t const & header() const
Return the block request header.
Definition: virtio-block:148
Interface for server-loop related functions.
Definition: ipc_epiface:47
Request finished successfully.
Definition: virtio_block.h:46
unsigned short int l4_uint16_t
Unsigned 16bit value.
Definition: l4int.h:38
Epiface implementation for interrupt handlers.
Definition: ipc_epiface:290
C++ Irq interface.
Definition: irq:117
Exception used by Queue to indicate descriptor errors.
Definition: virtio:325
void set_flush()
Enables the flush command.
Definition: virtio-block:382
Header structure of a request for a block device.
Definition: virtio_block.h:54
bool ready() const
Test if this queue is in working state.
Definition: virtqueue:405
Device configuration for block devices.
Definition: virtio_block.h:80
void start(DESC_MAN *dm, Virtqueue *ring, Virtqueue::Head_desc const &request, ARGS... args)
Start processing a new request.
Definition: virtio:427
unsigned data_size() const
Compute the total size of the data in the request.
Definition: virtio-block:63
void consumed(Head_desc const &r, l4_uint32_t len=0)
Put the given descriptor into the used ring.
Definition: virtio:166
void disable()
Completely disable the queue.
Definition: virtqueue:230
Epiface implementation for Kobject-based interface implementations.
Definition: ipc_epiface:515
Request next_avail()
Get the next available descriptor from the available ring.
Definition: virtio:137
void set_geometry(l4_uint16_t cylinders, l4_uint8_t heads, l4_uint8_t sectors)
Set disk geometry that is reported to the client.
Definition: virtio-block:333
void set_blk_size(l4_uint32_t sz)
Sets block disk size to be reported to the client.
Definition: virtio-block:350
Encapsulate the state for processing a VIRTIO request.
Definition: virtio:398
Exception for an abstract runtime error.
Definition: exceptions:139
Base class for virtio block devices.
Definition: virtio-block:22
void reset()
reset callback, called for doing a device reset
Definition: virtio-block:548
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
void set_write_zeroes(l4_uint32_t max_write_zeroes_sectors, l4_uint32_t max_write_zeroes_seg, l4_uint8_t write_zeroes_may_unmap)
Sets constraints for and enables the write zeroes command.
Definition: virtio-block:440
Data_block next_block()
Return next block in scatter-gather list.
Definition: virtio-block:113
void set_size_max(l4_uint32_t sz)
Sets the maximum size of any single segment reported to client.
Definition: virtio-block:308
Ok.
Definition: err.h:43
Base class for interface implementations.
Definition: ipc_epiface:153
bool next(DESC_MAN *dm, ARGS... args)
Switch to the next descriptor in a descriptor chain.
Definition: virtio:492
Ptr< void > addr
Address stored in descriptor.
Definition: virtqueue:117
Server_iface * server_iface() const
Get pointer to server interface at which the object is currently registered.
Definition: ipc_epiface:221
T chkcap(T &&cap, char const *extra="", long err=-L4_ENOMEM)
Check for valid capability or raise C++ exception.
Definition: error_helper:145
int reconfig_queue(unsigned idx)
callback for client queue-config request
Definition: virtio-block:540
void set_discard(l4_uint32_t max_discard_sectors, l4_uint32_t max_discard_seg, l4_uint32_t discard_sector_alignment)
Sets constraints for and enables the discard command.
Definition: virtio-block:420
I/O error.
Definition: err.h:46
A request to read or write data.
Definition: virtio-block:28
T * local(Ptr< T > p) const
Get the local address for driver address p.
Definition: l4virtio:591
long chksys(long err, char const *extra="", long ret=0)
Generate C++ exception on error.
Definition: error_helper:68
unsigned char l4_uint8_t
Unsigned 8bit value.
Definition: l4int.h:36
void set_topology(l4_uint8_t physical_block_exp, l4_uint8_t alignment_offset, l4_uint32_t min_io_size, l4_uint32_t opt_io_size)
Sets the I/O alignment information reported back to the client.
Definition: virtio-block:366
Type for device feature bitmap.
Definition: virtio:66
virtual L4::Cap< L4::Irq > register_irq_obj(L4::Epiface *o)=0
Register o as server-side object for asynchronous IRQs.
l4_uint8_t get_writeback()
Get the writeback field from the configuration space.
Definition: virtio-block:406
void finalize_request(cxx::unique_ptr< Request > req, unsigned sz, l4_uint8_t status=L4VIRTIO_BLOCK_S_OK)
Releases resources related to a request and notifies the client.
Definition: virtio-block:524
bool has_more()
Check if the request contains more data blocks.
Definition: virtio-block:95
Invalid size of memory block.
Definition: virtio:334
unsigned long long l4_uint64_t
Unsigned 64bit value.
Definition: l4int.h:42
L4-VIRTIO Transport C++ API.
Definition: l4virtio:18
void set_seg_max(l4_uint32_t sz)
Sets the maximum number of segments in a request that is reported to client.
Definition: virtio-block:322
l4_uint32_t len
Length of described buffer.
Definition: virtqueue:118
virtual L4::Cap< void > register_obj(L4::Epiface *o, char const *service)=0
Register an L4::Epiface for an IPC gate available in the applications environment under the name serv...
Cap< T > cap_cast(Cap< F > const &c) noexcept
static_cast for capabilities.
Definition: capability.h:379
struct l4virtio_block_header_t l4virtio_block_header_t
Header structure of a request for a block device.
unsigned int l4_uint32_t
Unsigned 32bit value.
Definition: l4int.h:40
Already exists.
Definition: err.h:54