30 #include <l4/re/util/meta> 32 #include <l4/cxx/bitfield> 33 #include <l4/cxx/utils> 34 #include <l4/cxx/unique_ptr> 36 #include <l4/sys/cxx/ipc_legacy> 38 #include "../l4virtio" 63 typedef L4Re::Rm::Unique_region< l4virtio_config_hdr_t*> Cfg_region;
67 l4_uint32_t _host_features[
sizeof(l4virtio_config_hdr_t::dev_features_map)
76 {
return (x + 0xfU) & ~0xfU; }
79 void volatile *get_priv_config()
const 99 : _vendor(vendor), _device(device),
100 _qoffset(0x100 + align(cfg_size)),
118 Cfg_cap cfg =
chkcap(L4Re::Util::make_unique_cap<Dataspace>());
123 _ds = cxx::move(cfg);
124 _config->generation = 0;
125 memset(_config->driver_features_map, 0, sizeof (_config->driver_features_map));
126 memset(_host_features, 0,
sizeof(_host_features));
127 _host_features[1] = 1;
131 void set_host_feature(
unsigned feature)
134 void clear_host_feature(
unsigned feature)
137 bool get_host_feature(
unsigned feature)
141 {
return _host_features[idx]; }
144 {
return _host_features[idx]; }
158 {
return _config->driver_features_map[idx]; }
172 {
return _config->driver_features_map[idx] & _host_features[idx]; }
181 Status
status()
const {
return _status; }
240 _nqueues = num_queues;
260 (
reinterpret_cast<char *
>(_config.get()) + _qoffset) + index;
268 _config->magic = L4VIRTIO_MAGIC;
269 _config->version = 2;
270 _config->device = _device;
271 _config->vendor = _vendor;
273 _config->irq_status = 0;
274 _config->num_queues = _nqueues;
275 _config->queues_offset = _qoffset;
277 memcpy(_config->dev_features_map, _host_features,
278 sizeof(_config->dev_features_map));
281 ++_config->generation;
294 bool inc_generation =
false)
const 307 ++_config->generation;
317 {
return _config.get(); }
330 {
return _ds_offset; }
334 template<
typename PRIV_CONFIG>
339 typedef PRIV_CONFIG Priv_config;
353 :
Dev_config(vendor, device,
sizeof(PRIV_CONFIG), num_queues)
365 Priv_config
volatile *priv_config()
const 367 return static_cast<Priv_config
volatile *
>(get_priv_config());
372 struct No_custom_data {};
379 template <
typename DATA>
386 CXX_BITFIELD_MEMBER(0, 0, rw, raw);
402 L4Re::Rm::Unique_region<l4_addr_t> _local_base;
407 return (T)(addr - _trans_offset);
438 long err =
ds->
info(&ds_info);
446 if (_ds_offset >= ds_size)
453 if (_ds_offset > ds_size - size)
457 if ((ULLONG_MAX - size) < _drv_base)
468 chksys(err,
"getting data-space infos");
472 chksys(Env::env()->rm()->attach(&_local_base, size,
481 _trans_offset = _drv_base - _local_base.get();
488 Flags
flags()
const {
return _flags; }
492 {
return _size == 0; }
498 void *
local_base()
const {
return (
void*)_local_base.get(); }
518 if (base < _drv_base)
521 if (base > _drv_base + _size - 1)
527 if (base - _drv_base > _size - size)
541 {
return _local<T*>(p.
get()); }
552 template <
typename DATA>
559 cxx::unique_ptr<Mem_region[]> _l;
576 _l = cxx::make_unique<Driver_mem_region_t<DATA>[]>(
max);
583 {
return _free == _max; }
599 _l[_free++] = Mem_region(drv_base, size, offset, cxx::move(
ds));
600 return &_l[_free - 1];
607 void remove(Mem_region
const *r)
609 if (r < &_l[0] || r >= &_l[_free])
612 unsigned idx = r - &_l[0];
614 for (
unsigned i = idx + 1; i < _free - 1; ++i)
615 _l[i] = cxx::move(_l[i + 1]);
617 _l[--_free] = Mem_region();
629 return _find(base, size);
644 Mem_region
const *r = find(desc.
addr.
get(), desc.
len);
662 Mem_region
const **data)
const 664 Mem_region
const *r = find(desc.
addr.
get(), desc.
len);
687 template<
typename ARG>
691 Mem_region *r = find(desc.
addr.
get(), desc.
len);
695 *data = ARG(r, desc, p);
701 for (
unsigned i = 0; i < _free; ++i)
702 if (_l[i].contains(base, size))
730 template<
typename DATA>
744 template<
typename IOS>
int virtio_dispatch(
unsigned r, IOS &ios)
745 {
return dispatch(r, ios); }
748 virtual void reset() = 0;
755 virtual bool check_queues() = 0;
758 virtual int reconfig_queue(
unsigned idx) = 0;
782 register_single_driver_irq();
796 return device_notify_irq();
809 : _device_config(dev_config)
816 {
return &_mem_info; };
818 long op_set_status(L4virtio::Device::Rights,
unsigned status)
819 {
return _set_status(status); }
821 long op_config_queue(L4virtio::Device::Rights,
unsigned queue)
830 return reconfig_queue(queue);
833 long op_register_ds(L4virtio::Device::Rights,
837 printf(
"Registering dataspace from 0x%llx with %lu KiB, offset 0x%lx\n",
838 ds_base, sz >> 10, offset);
840 _check_n_init_shm(ds_cap_fp, ds_base, sz, offset);
845 long op_register_iface(L4virtio::Device::Rights,
858 register_single_driver_irq();
860 printf(
"register client: host IRQ: %lx config DS: %lx\n",
861 device_notify_irq().cap(), _device_config->
ds().
cap());
868 long op_device_config(L4virtio::Device::Rights,
872 printf(
"register client: host IRQ: %lx config DS: %lx\n",
873 device_notify_irq().cap(), _device_config->
ds().
cap());
880 long op_device_notification_irq(L4virtio::Device::Rights,
884 auto cap = device_notify_irq(idx);
895 if (idx >= num_events_supported())
901 register_driver_irq(idx);
911 int op_info(L4::Icu::Rights, L4::Icu::_Info &info)
914 info.nr_irqs = num_events_supported();
944 bool inc_generation =
false)
946 _device_config->
reset_queue(idx, num_max, inc_generation);
988 qc = _device_config->
qconfig(qn);
1003 printf(
"%p: setup queue: num=0x%x max_num=0x%x desc=0x%llx avail=0x%llx used=0x%llx\n",
1004 this, num, num_max, desc, avail, used);
1006 if (!num || num > num_max)
1010 if (num & (num - 1))
1031 if (
L4_UNLIKELY(!used_info || !used_info->is_writable()))
1034 printf(
"shm=[%llx-%llx] local=[%lx-%lx] desc=[%llx-%llx] (%p-%p)\n",
1035 desc_info->drv_base(), desc_info->drv_base() + desc_info->size() - 1,
1036 (
unsigned long)desc_info->local_base(),
1037 (
unsigned long)desc_info->local_base() + desc_info->size() - 1,
1042 printf(
"shm=[%llx-%llx] local=[%lx-%lx] avail=[%llx-%llx] (%p-%p)\n",
1043 avail_info->drv_base(), avail_info->drv_base() + avail_info->size() - 1,
1044 (
unsigned long)avail_info->local_base(),
1045 (
unsigned long)avail_info->local_base() + avail_info->size() - 1,
1050 printf(
"shm=[%llx-%llx] local=[%lx-%lx] used=[%llx-%llx] (%p-%p)\n",
1051 used_info->drv_base(), used_info->drv_base() + used_info->size() - 1,
1052 (
unsigned long)used_info->local_base(),
1053 (
unsigned long)used_info->local_base() + used_info->size() - 1,
1067 if (_mem_info.
full())
1070 auto const *i = _mem_info.
add(base, size, offset, cxx::move(shm));
1071 printf(
"PORT[%p]: DMA guest [%llx-%llx] local [%lx-%lx] offset %lx\n",
1072 this, i->drv_base(), i->drv_base() + i->size() - 1,
1073 (
unsigned long)i->local_base(),
1074 (
unsigned long)i->local_base() + i->size() - 1,
1092 switch (cmd & L4VIRTIO_CMD_MASK)
1095 _set_status(cmd & ~L4VIRTIO_CMD_MASK);
1099 reconfig_queue(cmd & ~L4VIRTIO_CMD_MASK);
1120 L4Re::chkcap(server_iface()->
template rcv_cap<L4Re::Dataspace>(0)));
1123 check_n_init_shm(cxx::move(ds), base, size, offset);
1126 bool check_features_internal()
1128 static_assert(
sizeof(l4virtio_config_hdr_t::driver_features_map)
1129 ==
sizeof(l4virtio_config_hdr_t::dev_features_map),
1130 "Driver and device feature maps must be of the same size");
1133 i <
sizeof(l4virtio_config_hdr_t::driver_features_map)
1134 /
sizeof(l4virtio_config_hdr_t::driver_features_map[0]);
1139 & ~_device_config->host_features(i))
1142 return check_features();
1145 int _set_status(
unsigned _status)
1151 printf(
"Resetting device\n");
1160 && !check_features_internal())
1163 if (status.
running() && !check_queues())
l4_uint32_t get_cmd() const
Get the value from the cmd register.
Request writable mapping.
Capability type for RPC interfaces (see L4::Cap<T>).
Descriptor in the descriptor table.
Pointer used in virtio descriptors.
virtual void register_single_driver_irq()
callback for registering a single guest IRQ for all queues (old-style)
Read and interface specific 'W' right for capability flex-pages.
l4_uint32_t status
Device status register (read-only).
Virtqueue implementation for the device.
Abstraction for L4-Virtio device config memory.
Status status() const
Get current device status (trusted).
Server-side L4-VIRTIO device stub.
void l4virtio_set_feature(l4_uint32_t *feature_map, unsigned feat)
Set the given feature bit in a feature map.
Unique_cap / Unique_del_cap.
bool handle_mem_cmd_write()
Check for a value in the cmd register and handle a write.
Dev_config(l4_uint32_t vendor, l4_uint32_t device, unsigned cfg_size, l4_uint32_t num_queues=0)
Setup/Create a L4-Virtio config data space.
bool change_queue_config(l4_uint32_t num_queues)
Setup new queue configuration.
Interface for server-loop related functions.
bool cap_received() const
Check if the capability has been mapped.
l4virtio_config_queue_t volatile const * qconfig(unsigned index) const
Get queue read-only config data for queue with the given index.
l4_addr_t l4_round_page(l4_addr_t address) L4_NOTHROW
Round address up to the next page.
Info to use for a specific MSI.
void reset_hdr(bool inc_generation=false) const
Reset the config header to the initial contents.
void load_desc(Virtqueue::Desc const &desc, Request_processor const *p, Mem_region const **data) const
Default implementation returning the Driver_mem_region.
Common L4 ABI Data Types.
Driver_mem_list_t()
Make an empty, zero capacity list.
l4_uint32_t cmd
L4 specific command register polled by the driver iff supported.
Address cannot be translated.
Information about the dataspace.
void reset_queue_config(unsigned idx, unsigned num_max, bool inc_generation=false)
Trigger reset for the configuration space for queue idx.
Cap< T > make_cap(L4::Cap< T > cap, unsigned rights)
Make an L4::Ipc::Cap<T> for the given capability and rights.
L4::Cap< L4Re::Dataspace > ds() const
Get data-space capability for the shared config data space.
C++ interface of the initial environment that is provided to an L4 task.
Exception used by Queue to indicate descriptor errors.
l4_addr_t l4_trunc_page(l4_addr_t address) L4_NOTHROW
Round an address down to the next lower page boundary.
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.
Type of the device status register.
l4_uint64_t used_addr
W: address of used ring.
void disable()
Completely disable the queue.
l4_uint64_t drv_base() const
Search for a suitable address range.
Mem_region * find(l4_uint64_t base, l4_umword_t size) const
Find memory region containing the given driver address region.
void device_error()
Transition device into failed state.
Mem_list _mem_info
Memory region list.
Encapsulate the state for processing a VIRTIO request.
virtual L4::Cap< L4::Irq > device_notify_irq() const
callback to gather the device notification IRQ (old-style)
#define L4_SUPERPAGESHIFT
Size of a large page, log2-based.
l4_uint16_t num
RW: number of descriptors configured for this queue.
Read right for capability flex-pages.
Interface for memory-like objects.
T1 max(T1 a, T1 b)
Get the maximum of a and b.
l4virtio_config_hdr_t const volatile * hdr() const
Get a read-only pointer to the config header.
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 info(Stats *stats)
Get information on the dataspace.
#define L4_PAGESIZE
Minimal page size (in bytes).
driver_bfm_t::Val driver() const
Get the driver bits ( 1 to 1 ) of raw .
void * l4virtio_device_config(l4virtio_config_hdr_t const *cfg)
Get the pointer to the device configuration.
bool contains(l4_uint64_t base, l4_umword_t size) const
Test if the given driver address range is within this region.
bool running() const
Check if the device is in running state.
virtual unsigned num_events_supported() const
Return the highest notification index supported.
IPC interface for virtio over L4 IPC.
#define L4_UNLIKELY(x)
Expression is unlikely to execute.
static Env const * env()
Returns the initial environment for the current task.
L4-VIRTIO config header, provided in shared data space.
unsigned l4virtio_get_feature(l4_uint32_t *feature_map, unsigned feat)
Check if the given bit in a feature map is set.
l4_cap_idx_t cap() const
Return capability selector.
features_ok_bfm_t::Val features_ok() const
Get the features_ok bits ( 3 to 3 ) of raw .
Cap< T > make_cap_rw(L4::Cap< T > cap)
Make an L4::Ipc::Cap<T> for the given capability with L4_CAP_FPAGE_RW rights.
unsigned long l4_umword_t
Unsigned machine word.
l4_uint16_t num_max
R: maximum number of descriptors supported by this queue.
driver_ok_bfm_t::Val driver_ok() const
Get the driver_ok bits ( 2 to 2 ) of raw .
Ptr< void > addr
Address stored in descriptor.
virtual L4::Cap< L4::Irq > device_notify_irq(unsigned idx)
Callback to gather the device notification IRQ (multi IRQ).
T chkcap(T &&cap, char const *extra="", long err=-L4_ENOMEM)
Check for valid capability or raise C++ exception.
List of driver memory regions assigned to a single L4-VIRTIO transport instance.
void init(unsigned max)
Make a fresh list with capacity max.
L4::Cap< L4Re::Dataspace > ds() const
#define L4_LIKELY(x)
Expression is likely to execute.
unsigned char raw
Raw value of the VIRTIO device status register.
l4_uint16_t ready
RW: queue ready flag (read-write)
void set_status(Status status)
Set device status register.
virtual void register_driver_irq(unsigned idx)
Callback for registering an notification IRQ (multi IRQ).
void setup(unsigned num, void *desc, void *avail, void *used)
Enable this queue.
T * local(Ptr< T > p) const
Get the local address for driver address p.
void set_failed()
Set device status failed bit.
Mem_list const * mem_info() const
Get the memory region list used for this device.
failed_bfm_t::Val failed() const
Get the failed bits ( 7 to 7 ) of raw .
long chksys(long err, char const *extra="", long ret=0)
Generate C++ exception on error.
virtual bool check_features()
callback for checking the subset of accepted features
Type for device feature bitmap.
static unsigned long avail_size(unsigned num)
Calculate the size of the available ring for num entries.
Queue configuration entry.
Region of driver memory, that shall be managed locally.
void init_mem_info(unsigned num)
Initialize the memory region list to the given maximum.
void reset_cmd()
Reset the cmd register after execution of a command.
void load_desc(Virtqueue::Desc const &desc, Request_processor const *p, ARG *data) const
Default implementation returning generic information.
bool reset_queue(unsigned index, unsigned num_max, bool inc_generation=false) const
Reset queue config for the given queue.
void load_desc(Virtqueue::Desc const &desc, Request_processor const *p, Virtqueue::Desc const **table) const
Default implementation for loading an indirect descriptor.
Device_t(Dev_config *dev_config)
Make a device for the given config.
static unsigned long desc_size(unsigned num)
Calculate the size of the descriptor table for num entries.
unsigned long long l4_uint64_t
Unsigned 64bit value.
l4_uint32_t negotiated_features(unsigned idx) const
Compute a specific set of negotiated features.
bool setup_queue(Virtqueue *q, unsigned qn, unsigned num_max)
Enable/disable the specified queue.
L4-VIRTIO Transport C++ API.
void * local_base() const
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.
l4_uint32_t len
Length of described buffer.
l4_uint32_t guest_features(unsigned idx) const
Return a specific set of guest features.
Mask to get command bits.
L4Re::Util::Unique_cap< L4Re::Dataspace > Ds_cap
type for storing a data-space capability internally
unsigned long l4_addr_t
Address type.
l4_addr_t ds_offset() const
Return the offset into the config dataspace where the device configuration starts.
l4_uint64_t desc_addr
W: address of descriptor table.
acked_bfm_t::Val acked() const
Get the acked bits ( 0 to 0 ) of raw .
void l4virtio_clear_feature(l4_uint32_t *feature_map, unsigned feat)
Clear the given feature bit in a feature map.
l4_uint64_t avail_addr
W: address of available ring.
unsigned int l4_uint32_t
Unsigned 32bit value.
Driver_mem_region_t()
Make default empty memroy region.
Generic RPC wrapper for L4 flex-pages.
static unsigned long used_size(unsigned num)
Calculate the size of the used ring for num entries.
l4_addr_t ds_offset() const