17#include <l4/re/util/debug>
26#include <l4/re/util/meta>
28#include <l4/cxx/bitfield>
29#include <l4/cxx/utils>
30#include <l4/cxx/unique_ptr>
32#include <l4/sys/cxx/ipc_legacy>
63 l4_uint32_t _host_features[
sizeof(l4virtio_config_hdr_t::dev_features_map)
72 {
return (x + 0xfU) & ~0xfU; }
74 void attach_n_init_cfg(Cfg_cap
const &cfg,
l4_addr_t offset)
80 "Attach config space to local address space.");
82 _config->generation = 0;
83 memset(_config->driver_features_map, 0,
sizeof(_config->driver_features_map));
84 memset(_host_features, 0,
sizeof(_host_features));
93 void volatile *get_priv_config()
const
114 : _vendor(vendor), _device(device),
115 _qoffset(0x100 + align(cfg_size)),
129 auto cfg = chkcap(L4Re::Util::make_shared_cap<Dataspace>());
132 attach_n_init_cfg(cfg, 0);
149 : _vendor(vendor), _device(device),
150 _qoffset(0x100 + align(cfg_size)),
160 attach_n_init_cfg(cfg, cfg_offset);
163 void set_host_feature(
unsigned feature)
166 void clear_host_feature(
unsigned feature)
169 bool get_host_feature(
unsigned feature)
172 bool get_guest_feature(
unsigned feature)
176 {
return _host_features[idx]; }
179 {
return _host_features[idx]; }
199 {
return _config->driver_features_map[idx]; }
213 {
return _config->driver_features_map[idx] & _host_features[idx]; }
312 (
reinterpret_cast<char *
>(_config.
get()) + _qoffset) + index;
320 _config->
magic = L4VIRTIO_MAGIC;
322 _config->
device = _device;
323 _config->
vendor = _vendor;
325 _config->irq_status = 0;
329 memcpy(_config->dev_features_map, _host_features,
330 sizeof(_config->dev_features_map));
333 ++_config->generation;
346 bool inc_generation =
false)
const
359 ++_config->generation;
369 {
return _config.
get(); }
382 {
return _ds_offset; }
386template<
typename PRIV_CONFIG>
387class Dev_config_t :
public Dev_config
391 typedef PRIV_CONFIG Priv_config;
406 : Dev_config(vendor, device, sizeof(PRIV_CONFIG),
num_queues)
422 : Dev_config(cfg, cfg_offset, vendor, device, sizeof(PRIV_CONFIG),
435 Priv_config
volatile *priv_config()
const
437 return static_cast<Priv_config
volatile *
>(get_priv_config());
442struct No_custom_data {};
449template <
typename DATA>
459 CXX_BITFIELD_MEMBER(0, 0, rw, raw);
480 return reinterpret_cast<T
>(addr - _trans_offset);
511 long err =
ds->
info(&ds_info);
519 if (_ds_offset >= ds_size)
520 chksys(-
L4_ERANGE,
"offset larger than DS size");
524 chksys(-
L4_EINVAL,
"size larger than DS size");
526 if (_ds_offset > ds_size -
size)
527 chksys(-
L4_EINVAL,
"invalid offset or size");
530 if ((ULLONG_MAX -
size) < _drv_base)
541 chksys(err,
"getting data-space infos");
549 chksys(Env::env()->rm()->attach(&_local_base,
size, f,
557 _trans_offset = _drv_base - _local_base.
get();
564 Flags
flags()
const {
return _flags; }
568 {
return _size == 0; }
594 if (base < _drv_base)
597 if (base > _drv_base + _size - 1)
603 if (base - _drv_base > _size -
size)
617 {
return _local<T*>(p.
get()); }
620typedef Driver_mem_region_t<No_custom_data> Driver_mem_region;
628template <
typename DATA>
635 cxx::unique_ptr<Mem_region[]> _l;
652 _l = cxx::make_unique<Driver_mem_region_t<DATA>[]>(max);
659 {
return _free == _max; }
675 _l[_free++] = Mem_region(drv_base, size, offset, cxx::move(ds));
676 return &_l[_free - 1];
685 if (r < &_l[0] || r >= &_l[_free])
688 unsigned idx = r - &_l[0];
690 for (
unsigned i = idx + 1; i < _free - 1; ++i)
691 _l[i] = cxx::move(_l[i + 1]);
693 _l[--_free] = Mem_region();
705 return _find(base, size);
738 Mem_region
const **data)
const
763 template<
typename ARG>
771 *data = ARG(r, desc, p);
777 for (
unsigned i = 0; i < _free; ++i)
778 if (_l[i].contains(base, size))
786typedef Driver_mem_list_t<No_custom_data> Driver_mem_list;
794template<
typename DATA>
806 using Ds_vector = std::vector<L4::Cap<L4Re::Dataspace>>;
808 std::shared_ptr<Ds_vector const> _trusted_ds_caps;
811 bool _trusted_ds_validation_enabled =
false;
815 template<
typename IOS>
int virtio_dispatch(
unsigned r, IOS &ios)
816 {
return dispatch(r, ios); }
886 : _device_config(dev_config)
895 long op_set_status(L4virtio::Device::Rights,
unsigned status)
896 {
return _set_status(status); }
898 long op_config_queue(L4virtio::Device::Rights,
unsigned queue)
900 Dev_config::Status status = _device_config->
status();
901 if (status.fail_state() || !status.acked() || !status.driver())
907 long op_register_ds(L4virtio::Device::Rights,
912 .printf(
"Registering dataspace from 0x%llx with %lu KiB, offset 0x%lx\n",
913 ds_base, sz >> 10, offset);
915 _check_n_init_shm(ds_cap_fp, ds_base, sz, offset);
920 long op_device_config(L4virtio::Device::Rights,
925 .printf(
"register client: host IRQ: %lx config DS: %lx\n",
933 long op_device_notification_irq(L4virtio::Device::Rights,
964 int op_info(L4::Icu::Rights, L4::Icu::_Info &info)
997 bool inc_generation =
false)
999 _device_config->
reset_queue(idx, num_max, inc_generation);
1044 qc = _device_config->
qconfig(qn);
1061 printf(
"%p: setup queue: num=0x%x max_num=0x%x desc=0x%llx avail=0x%llx used=0x%llx\n",
1062 this, num, num_max, desc, avail, used);
1064 if (!num || num > num_max)
1068 if (num & (num - 1))
1089 if (
L4_UNLIKELY(!used_info || !used_info->is_writable()))
1093 .printf(
"shm=[%llx-%llx] local=[%lx-%lx] desc=[%llx-%llx] (%p-%p)\n",
1094 desc_info->drv_base(), desc_info->drv_base() + desc_info->size() - 1,
1095 desc_info->local_base(),
1096 desc_info->local_base() + desc_info->size() - 1,
1102 .printf(
"shm=[%llx-%llx] local=[%lx-%lx] avail=[%llx-%llx] (%p-%p)\n",
1103 avail_info->drv_base(), avail_info->drv_base() + avail_info->size() - 1,
1104 avail_info->local_base(),
1105 avail_info->local_base() + avail_info->size() - 1,
1111 .printf(
"shm=[%llx-%llx] local=[%lx-%lx] used=[%llx-%llx] (%p-%p)\n",
1112 used_info->drv_base(), used_info->drv_base() + used_info->size() - 1,
1113 used_info->local_base(),
1114 used_info->local_base() + used_info->size() - 1,
1131 auto const *i =
_mem_info.add(base, size, offset, cxx::move(shm));
1133 .printf(
"PORT[%p]: DMA guest [%llx-%llx] local [%lx-%lx] offset %lx\n",
1134 this, i->drv_base(), i->drv_base() + i->size() - 1,
1136 i->local_base() + i->size() - 1,
1183 _trusted_ds_validation_enabled =
true;
1194 _trusted_ds_caps = ds;
1212 if (!_trusted_ds_caps)
1214 if (std::any_of(_trusted_ds_caps->cbegin(), _trusted_ds_caps->cend(),
1217 return L4Re::Env::env()->task()
1218 ->cap_equal(ds, cap).label() == 1;
1234 L4Re::chkcap(server_iface()->
template rcv_cap<L4Re::Dataspace>(0)));
1237 if (_trusted_ds_validation_enabled)
1238 L4Re::chksys(validate_ds(ds.get()),
"Validating the dataspace.");
1240 check_n_init_shm(cxx::move(ds), base, size, offset);
1243 bool check_features_internal()
1245 static_assert(
sizeof(l4virtio_config_hdr_t::driver_features_map)
1246 ==
sizeof(l4virtio_config_hdr_t::dev_features_map),
1247 "Driver and device feature maps must be of the same size");
1260 i <
sizeof(l4virtio_config_hdr_t::driver_features_map)
1261 /
sizeof(l4virtio_config_hdr_t::driver_features_map[0]);
1266 & ~_device_config->host_features(i))
1293 void check_and_update_status(Dev_config::Status status)
1296 Dev_config::Status current_status = _device_config->
status();
1308 if (current_status.fail_state() || status.fail_state())
1310 if (current_status.fail_state() != status.fail_state())
1312 current_status.fail_state() =
1313 current_status.fail_state() | status.fail_state();
1321 if ((!status.acked() && status.driver())
1322 || (!status.driver() && status.features_ok())
1323 || (!status.features_ok() && status.driver_ok()))
1325 current_status.device_needs_reset() = 1;
1331 if (status.features_ok() && !status.driver_ok()
1332 && !check_features_internal())
1333 status.features_ok() = 0;
1340 current_status.device_needs_reset() = 1;
1360 int _set_status(
unsigned new_status)
1362 if (new_status == 0)
1364 L4Re::Util::Dbg().printf(
"Resetting device\n");
1369 Dev_config::Status status(new_status);
1370 check_and_update_status(status);
1377typedef Device_t<No_custom_data> Device;
Interface for memory-like objects.
long info(Stats *stats)
Get information on the dataspace.
C++ interface of the initial environment that is provided to an L4 task.
static Env const * env() noexcept
Returns the initial environment for the current task.
T get() const noexcept
Return the address.
l4_cap_idx_t cap() const noexcept
Return capability selector.
C++ interface for capabilities.
Capability type for RPC interfaces (see L4::Cap<T>).
bool cap_received() const noexcept
Check if at least one capability has been mapped.
Interface for server-loop related functions.
IPC interface for virtio over L4 IPC.
Pointer used in virtio descriptors.
Abstraction for L4-Virtio device config memory.
void set_status(Status status)
Set device status register.
bool reset_queue(unsigned index, unsigned num_max, bool inc_generation=false) const
Reset queue config for the given queue.
l4virtio_config_hdr_t const volatile * hdr() const
Get a read-only pointer to the config header.
l4_uint32_t get_cmd() const
Get the value from the cmd register.
Dev_config(l4_uint32_t vendor, l4_uint32_t device, unsigned cfg_size, l4_uint32_t num_queues=0)
Create a L4-Virtio config data space.
bool change_queue_config(l4_uint32_t num_queues)
Setup new queue configuration.
void add_irq_status(l4_uint32_t status)
Adds irq status bit.
L4::Cap< L4Re::Dataspace > ds() const
Get data-space capability for the shared config data space.
Status status() const
Get current device status (trusted).
void reset_cmd()
Reset the cmd register after execution of a command.
l4_uint32_t num_queues() const
Return the number of queues currently usable.
l4virtio_config_queue_t volatile const * qconfig(unsigned index) const
Get queue read-only config data for queue with the given index.
void reset_hdr(bool inc_generation=false) const
Reset the config header to the initial contents.
Dev_config(Cfg_cap const &cfg, l4_addr_t cfg_offset, l4_uint32_t vendor, l4_uint32_t device, unsigned cfg_size, l4_uint32_t num_queues=0)
Setup an L4-Virtio config space in an existing data space.
l4_uint32_t guest_features(unsigned idx) const
Return a specific set of guest features.
l4_uint32_t negotiated_features(unsigned idx) const
Compute a specific set of negotiated features.
l4_addr_t ds_offset() const
Return the offset into the config dataspace where the device configuration starts.
void set_device_needs_reset()
Set DEVICE_NEEDS_RESET bit in device status register.
Server-side L4-VIRTIO device stub.
void device_error()
Transition device into DEVICE_NEEDS_RESET state.
virtual void cfg_changed(unsigned)
callback for client device configuration changes
void add_trusted_dataspaces(std::shared_ptr< Ds_vector const > ds)
Provide a list of trusted dataspaces that can be used for validation.
virtual void reset()=0
reset callback, called for doing a device reset
virtual void trigger_driver_config_irq()=0
callback for triggering configuration change notification IRQ
bool handle_mem_cmd_write()
Check for a value in the cmd register and handle a write.
Mem_list _mem_info
Memory region list.
virtual void register_driver_irq(unsigned idx)
Callback for registering an notification IRQ (multi IRQ).
Device_t(Dev_config *dev_config)
Make a device for the given config.
virtual void register_single_driver_irq()
callback for registering a single guest IRQ for all queues (old-style)
virtual bool check_features()
callback for checking the subset of accepted features
virtual unsigned num_events_supported() const
Return the highest notification index supported.
bool setup_queue(Virtqueue *q, unsigned qn, unsigned num_max)
Enable/disable the specified queue.
void init_mem_info(unsigned num)
Initialize the memory region list to the given maximum.
Mem_list const * mem_info() const
Get the memory region list used for this device.
virtual int reconfig_queue(unsigned idx)=0
callback for client queue-config request
void enable_trusted_ds_validation()
Enable trusted dataspace validation.
virtual L4::Cap< L4::Irq > device_notify_irq(unsigned idx)
Callback to gather the device notification IRQ (multi IRQ).
virtual L4::Cap< L4::Irq > device_notify_irq() const
callback to gather the device notification IRQ (old-style)
void reset_queue_config(unsigned idx, unsigned num_max, bool inc_generation=false)
Trigger reset for the configuration space for queue idx.
virtual bool check_queues()=0
callback for checking if the queues at DRIVER_OK transition
List of driver memory regions assigned to a single L4-VIRTIO transport instance.
Mem_region * find(l4_uint64_t base, l4_umword_t size) const
Find memory region containing the given driver address region.
void load_desc(Virtqueue::Desc const &desc, Request_processor const *p, Virtqueue::Desc const **table) const
Default implementation for loading an indirect descriptor.
void remove(Mem_region const *r)
Remove the given region from the list.
Mem_region const * add(l4_uint64_t drv_base, l4_umword_t size, l4_addr_t offset, Ds_cap &&ds)
Add a new region to the list.
L4Re::Util::Unique_cap< L4Re::Dataspace > Ds_cap
type for storing a data-space capability internally
void init(unsigned max)
Make a fresh list with capacity max.
void load_desc(Virtqueue::Desc const &desc, Request_processor const *p, Mem_region const **data) const
Default implementation returning the Driver_mem_region.
void load_desc(Virtqueue::Desc const &desc, Request_processor const *p, ARG *data) const
Default implementation returning generic information.
Driver_mem_list_t()
Make an empty, zero capacity list.
Region of driver memory, that shall be managed locally.
bool contains(l4_uint64_t base, l4_umword_t size) const
Test if the given driver address range is within this region.
l4_addr_t ds_offset() const
Driver_mem_region_t()
Make default empty memory region.
l4_addr_t local_base() const
Driver_mem_region_t(l4_uint64_t drv_base, l4_umword_t size, l4_addr_t offset, Ds_cap &&ds)
Make a local memory region for the given driver values.
L4::Cap< L4Re::Dataspace > ds() const
l4_uint64_t drv_base() const
T * local(Ptr< T > p) const
Get the local address for driver address p.
Encapsulate the state for processing a VIRTIO request.
Virtqueue implementation for the device.
Descriptor in the descriptor table.
l4_uint32_t len
Length of described buffer.
Ptr< void > addr
Address stored in descriptor.
void disable()
Completely disable the queue.
void setup(unsigned num, void *desc, void *avail, void *used)
Enable this queue.
static unsigned long desc_size(unsigned num)
Calculate the size of the descriptor table for num entries.
static unsigned long used_size(unsigned num)
Calculate the size of the used ring for num entries.
static unsigned long avail_size(unsigned num)
Calculate the size of the available ring for num entries.
unsigned long l4_umword_t
Unsigned machine word.
unsigned long l4_addr_t
Address type.
unsigned int l4_uint32_t
Unsigned 32bit value.
unsigned long long l4_uint64_t
Unsigned 64bit value.
@ L4_EINVAL
Invalid argument.
@ L4_CAP_FPAGE_RO
Read right for capability flex-pages.
@ L4_CAP_FPAGE_RW
Read and interface specific 'W' right for capability flex-pages.
#define L4_SUPERPAGESHIFT
Size of a large page, log2-based.
l4_addr_t l4_trunc_page(l4_addr_t address) L4_NOTHROW
Round an address down to the next lower page boundary.
l4_addr_t l4_round_page(l4_addr_t address) L4_NOTHROW
Round address up to the next page.
#define L4_PAGESIZE
Minimal page size (in bytes).
#define L4_UNLIKELY(x)
Expression is unlikely to execute.
#define L4_LIKELY(x)
Expression is likely to execute.
void * l4virtio_device_config(l4virtio_config_hdr_t const *cfg)
Get the pointer to the device configuration.
void l4virtio_clear_feature(l4_uint32_t *feature_map, unsigned feat)
Clear the given feature bit in a feature map.
void l4virtio_set_feature(l4_uint32_t *feature_map, unsigned feat)
Set the given feature bit in a feature map.
unsigned l4virtio_get_feature(l4_uint32_t *feature_map, unsigned feat)
Check if the given bit in a feature map is set.
@ L4VIRTIO_FEATURE_VERSION_1
Virtio protocol version 1 supported. Must be 1 for L4virtio.
@ L4VIRTIO_CMD_SET_STATUS
Set the status register.
@ L4VIRTIO_CMD_CFG_QUEUE
Configure a queue.
@ L4VIRTIO_CMD_MASK
Mask to get command bits.
@ L4VIRTIO_CMD_CFG_CHANGED
Device config changed.
Common L4 ABI Data Types.
L4::Detail::Shared_cap_impl< T, Smart_count_cap< L4_FP_ALL_SPACES > > Shared_cap
Shared capability that implements automatic free and unmap of the capability selector.
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.
long chksys(long err, char const *extra="", long ret=0)
Generate C++ exception on error.
T chkcap(T &&cap, char const *extra="", long err=-L4_ENOMEM)
Check for valid capability or raise C++ exception.
Cap< T > make_cap(L4::Cap< T > cap, unsigned rights) noexcept
Make an L4::Ipc::Cap<T> for the given capability and rights.
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.
L4-VIRTIO Transport C++ API.
@ W
Request write-only mapping.
Information about the dataspace.
@ RW
Readable and writable region.
@ Search_addr
Search for a suitable address range.
Exception used by Queue to indicate descriptor errors.
@ Bad_address
Address cannot be translated.
Type for device feature bitmap.
Type of the device status register.
constexpr device_needs_reset_bfm_t::Val device_needs_reset() const
Get the device_needs_reset bits ( 6 to 6 ) of raw.
constexpr driver_ok_bfm_t::Val driver_ok() const
Get the driver_ok bits ( 2 to 2 ) of raw.
unsigned char raw
Raw value of the VIRTIO device status register.
Info to use for a specific MSI.
L4-VIRTIO config header, provided in shared data space.
l4_uint32_t vendor
vendor ID
l4_uint32_t magic
magic value (must be 'virt').
l4_uint32_t queues_offset
offset of virtqueue config array
l4_uint32_t device
device ID
l4_uint32_t num_queues
number of virtqueues
l4_uint32_t version
VIRTIO version.
Queue configuration entry.
l4_uint64_t avail_addr
W: address of available ring.
l4_uint64_t desc_addr
W: address of descriptor table.
l4_uint64_t used_addr
W: address of used ring.
l4_uint16_t ready
RW: queue ready flag (read-write)
l4_uint16_t num_max
R: maximum number of descriptors supported by this queue.
l4_uint16_t num
RW: number of descriptors configured for this queue.
Shared_cap / Shared_del_cap.
Unique_cap / Unique_del_cap.