virtio_pci: use generic platform API

Ref genodelabs/genode#4578
This commit is contained in:
Stefan Kalkowski 2022-09-20 12:02:32 +02:00 committed by Christian Helmuth
parent fdba7259ab
commit ec6f89111e
6 changed files with 112 additions and 161 deletions

View File

@ -14,10 +14,7 @@
#ifndef _INCLUDE__VIRTIO__PCI_DEVICE_H_
#define _INCLUDE__VIRTIO__PCI_DEVICE_H_
#include <os/attached_mmio.h>
#include <irq_session/client.h>
#include <legacy/x86/platform_device/client.h>
#include <legacy/x86/platform_session/connection.h>
#include <platform_session/device.h>
#include <virtio/queue.h>
namespace Virtio {
@ -26,7 +23,7 @@ namespace Virtio {
class Device;
}
struct Virtio::Device_mmio : public Attached_mmio
struct Virtio::Device_mmio : public Genode::Mmio
{
struct DeviceFeatureSelect : Register<0x00, 32> { };
struct DeviceFeature : Register<0x04, 32> { };
@ -54,10 +51,7 @@ struct Virtio::Device_mmio : public Attached_mmio
struct IrqReason : Register<0x0, 32> { };
Device_mmio(Genode::Env &env,
Genode::addr_t base,
Genode::size_t size)
: Attached_mmio(env, base, size, false) { }
using Mmio::Mmio;
};
class Virtio::Device
@ -93,133 +87,112 @@ class Virtio::Device
enum {
VIRTIO_PCI_BASE_ID = 0x1040,
VIRTIO_MSI_NO_VECTOR = 0xffff
VIRTIO_MSI_NO_VECTOR = 0xffff,
MMIO_MAX = 6U
};
Genode::Env &_env;
Platform::Device_client &_device;
Genode::Irq_session_client _irq { _device.irq(0) };
uint32_t _notify_offset_multiplier = 0;
Genode::Constructible<Device_mmio> _cfg_common { };
Genode::Constructible<Device_mmio> _dev_config { };
Genode::Constructible<Device_mmio> _notify { };
Genode::Constructible<Device_mmio> _isr { };
Env & _env;
Platform::Connection & _plat;
Platform::Device _device { _plat };
Platform::Device::Irq _irq { _device, { 0 } };
Constructible<Platform::Device::Mmio> _mmio[MMIO_MAX] { };
void _configure()
Mmio _cfg_common { _bar_offset("common") };
Mmio _dev_config { _bar_offset("device") };
Mmio _notify { _bar_offset("notify") };
Mmio _isr { _bar_offset("irq_status") };
size_t _notify_offset_multiplier { 0 };
template <typename FN>
void with_virtio_range(String<16> type, FN const & fn)
{
typedef Platform::Device Pdev;
_plat.update();
_plat.with_xml([&] (Xml_node xml) {
xml.with_optional_sub_node("device", [&] (Xml_node xml) {
xml.with_optional_sub_node("pci-config",
[&] (Xml_node xml) {
xml.for_each_sub_node("virtio_range",
[&] (Xml_node xml) {
if (xml.attribute_value("type", String<16>()) ==
type)
fn(xml);
});
});
});
});
}
enum { PCI_STATUS = 0x6, PCI_CAPABILITIES = 0x34, };
addr_t _bar_offset(String<16> type)
{
unsigned idx = MMIO_MAX;
addr_t off = ~0UL;
with_virtio_range(type, [&] (Xml_node xml) {
idx = xml.attribute_value<unsigned>("index", MMIO_MAX);
off = xml.attribute_value("offset", ~0UL);
});
auto status = _device.config_read(PCI_STATUS, Pdev::ACCESS_16BIT);
if (!(status & 0x10)) {
error("PCI capabilities missing according to device status!");
throw Configuration_failed();
}
auto addr = _device.config_read(PCI_CAPABILITIES, Pdev::ACCESS_8BIT);
addr &= 0xFC;
while (addr) {
enum { ID_VNDR = 0x09 };
enum { CAP_ID = 0, CAP_LIST_NEXT = 1 };
auto const cap_id = _device.config_read(addr + CAP_ID, Pdev::ACCESS_8BIT);
auto const cap_next = _device.config_read(addr + CAP_LIST_NEXT, Pdev::ACCESS_8BIT);
if (cap_id == ID_VNDR) {
enum { CFG_TYPE = 0x3, BAR = 0x4, OFFSET = 0x8,
LENGTH = 0xC, NOTIFY_OFFSET_MULT = 0x10 };
enum { COMMON_CFG = 1, NOTIFY_CFG = 2, ISR_CFG = 3,
DEVICE_CFG = 4, PCI_CFG = 5 };
auto const cfg_type = _device.config_read(addr + CFG_TYPE, Pdev::ACCESS_8BIT);
auto const bar = _device.config_read(addr + BAR, Pdev::ACCESS_8BIT);
auto const off = _device.config_read(addr + OFFSET, Pdev::ACCESS_32BIT);
auto const len = _device.config_read(addr + LENGTH, Pdev::ACCESS_32BIT);
if (cfg_type == COMMON_CFG) {
auto const r = _device.resource(bar);
_cfg_common.construct(_env, r.base() + off, len);
} else if (cfg_type == DEVICE_CFG) {
auto const r = _device.resource(bar);
_dev_config.construct(_env, r.base() + off, len);
} else if (cfg_type == NOTIFY_CFG) {
_notify_offset_multiplier = _device.config_read(
addr + NOTIFY_OFFSET_MULT, Pdev::ACCESS_32BIT);
auto const r = _device.resource(bar);
_notify.construct(_env, r.base() + off, len);
} else if (cfg_type == ISR_CFG) {
auto const r = _device.resource(bar);
_isr.construct(_env, r.base() + off, len);
}
}
addr = cap_next;
}
if (!_cfg_common.constructed() || !_dev_config.constructed() ||
!_notify.constructed() || !_isr.constructed()) {
error("Required VirtIO PCI capabilities not found!");
if (idx >= MMIO_MAX || off == ~0UL)
throw Configuration_failed();
}
_cfg_common->write<Device_mmio::MsiXConfig>(VIRTIO_MSI_NO_VECTOR);
if (!_mmio[idx].constructed())
_mmio[idx].construct(_device,
Platform::Device::Mmio::Index{idx});
return _mmio[idx]->base() + off;
}
public:
Device(Genode::Env &env,
Platform::Device_client device)
: _env(env), _device(device)
Device(Genode::Env & env,
Platform::Connection & plat)
: _env(env), _plat(plat)
{
_configure();
with_virtio_range("notify", [&] (Xml_node xml) {
_notify_offset_multiplier = xml.attribute_value("factor", 0UL);
});
_cfg_common.write<Device_mmio::MsiXConfig>(VIRTIO_MSI_NO_VECTOR);
}
uint32_t vendor_id() { return _device.vendor_id(); }
uint32_t device_id() {
return _device.device_id() - VIRTIO_PCI_BASE_ID; }
uint8_t get_status() {
return _cfg_common->read<Device_mmio::DeviceStatus>(); }
return _cfg_common.read<Device_mmio::DeviceStatus>(); }
bool set_status(uint8_t status)
{
_cfg_common->write<Device_mmio::DeviceStatus>(status);
return _cfg_common->read<Device_mmio::DeviceStatus>() == status;
_cfg_common.write<Device_mmio::DeviceStatus>(status);
return _cfg_common.read<Device_mmio::DeviceStatus>() == status;
}
uint32_t get_features(uint32_t selection)
{
_cfg_common->write<Device_mmio::DeviceFeatureSelect>(selection);
return _cfg_common->read<Device_mmio::DeviceFeature>();
_cfg_common.write<Device_mmio::DeviceFeatureSelect>(selection);
return _cfg_common.read<Device_mmio::DeviceFeature>();
}
void set_features(uint32_t selection, uint32_t features)
{
_cfg_common->write<Device_mmio::DriverFeatureSelect>(selection);
_cfg_common->write<Device_mmio::DriverFeature>(features);
_cfg_common.write<Device_mmio::DriverFeatureSelect>(selection);
_cfg_common.write<Device_mmio::DriverFeature>(features);
}
uint8_t get_config_generation() {
return _cfg_common->read<Device_mmio::ConfigGeneration>(); }
return _cfg_common.read<Device_mmio::ConfigGeneration>(); }
uint16_t get_max_queue_size(uint16_t queue_index)
{
_cfg_common->write<Device_mmio::QueueSelect>(queue_index);
return _cfg_common->read<Device_mmio::QueueSize>();
_cfg_common.write<Device_mmio::QueueSelect>(queue_index);
return _cfg_common.read<Device_mmio::QueueSize>();
}
uint32_t read_config(uint8_t offset, Access_size size)
{
switch (size) {
case Device::ACCESS_8BIT:
return _dev_config->read<Device_mmio::Config_8>(offset);
return _dev_config.read<Device_mmio::Config_8>(offset);
case Device::ACCESS_16BIT:
return _dev_config->read<Device_mmio::Config_16>(offset >> 1);
return _dev_config.read<Device_mmio::Config_16>(offset >> 1);
case Device::ACCESS_32BIT:
return _dev_config->read<Device_mmio::Config_32>(offset >> 2);
return _dev_config.read<Device_mmio::Config_32>(offset >> 2);
}
return 0;
}
@ -228,63 +201,63 @@ class Virtio::Device
{
switch (size) {
case Device::ACCESS_8BIT:
_dev_config->write<Device_mmio::Config_8>(value, offset);
_dev_config.write<Device_mmio::Config_8>(value, offset);
break;
case Device::ACCESS_16BIT:
_dev_config->write<Device_mmio::Config_16>(value, offset >> 1);
_dev_config.write<Device_mmio::Config_16>(value, offset >> 1);
break;
case Device::ACCESS_32BIT:
_dev_config->write<Device_mmio::Config_32>(value, offset >> 2);
_dev_config.write<Device_mmio::Config_32>(value, offset >> 2);
break;
}
}
bool configure_queue(uint16_t queue_index, Virtio::Queue_description desc)
{
_cfg_common->write<Device_mmio::QueueSelect>(queue_index);
_cfg_common.write<Device_mmio::QueueSelect>(queue_index);
if (_cfg_common->read<Device_mmio::QueueEnable>()) {
if (_cfg_common.read<Device_mmio::QueueEnable>()) {
warning("VirtIO queues can't be re-configured after being enabled!");
return false;
}
_cfg_common->write<Device_mmio::QueueMsixVector>(VIRTIO_MSI_NO_VECTOR);
if (_cfg_common->read<Device_mmio::QueueMsixVector>() != VIRTIO_MSI_NO_VECTOR) {
_cfg_common.write<Device_mmio::QueueMsixVector>(VIRTIO_MSI_NO_VECTOR);
if (_cfg_common.read<Device_mmio::QueueMsixVector>() != VIRTIO_MSI_NO_VECTOR) {
error("Failed to disable MSI-X for queue ", queue_index);
return false;
}
_cfg_common->write<Device_mmio::QueueSize>(desc.size);
_cfg_common.write<Device_mmio::QueueSize>(desc.size);
uint64_t addr = desc.desc;
_cfg_common->write<Device_mmio::QueueDescLow>((uint32_t)addr);
_cfg_common->write<Device_mmio::QueueDescHigh>((uint32_t)(addr >> 32));
_cfg_common.write<Device_mmio::QueueDescLow>((uint32_t)addr);
_cfg_common.write<Device_mmio::QueueDescHigh>((uint32_t)(addr >> 32));
addr = desc.avail;
_cfg_common->write<Device_mmio::QueueAvailLow>((uint32_t)addr);
_cfg_common->write<Device_mmio::QueueAvailHigh>((uint32_t)(addr >> 32));
_cfg_common.write<Device_mmio::QueueAvailLow>((uint32_t)addr);
_cfg_common.write<Device_mmio::QueueAvailHigh>((uint32_t)(addr >> 32));
addr = desc.used;
_cfg_common->write<Device_mmio::QueueUsedLow>((uint32_t)addr);
_cfg_common->write<Device_mmio::QueueUsedHigh>((uint32_t)(addr >> 32));
_cfg_common->write<Device_mmio::QueueEnable>(1);
return _cfg_common->read<Device_mmio::QueueEnable>() != 0;
_cfg_common.write<Device_mmio::QueueUsedLow>((uint32_t)addr);
_cfg_common.write<Device_mmio::QueueUsedHigh>((uint32_t)(addr >> 32));
_cfg_common.write<Device_mmio::QueueEnable>(1);
return _cfg_common.read<Device_mmio::QueueEnable>() != 0;
}
void notify_buffers_available(uint16_t queue_index) {
_cfg_common->write<Device_mmio::QueueSelect>(queue_index);
auto const offset = _cfg_common->read<Device_mmio::QueueNotifyOff>();
_cfg_common.write<Device_mmio::QueueSelect>(queue_index);
auto const offset = _cfg_common.read<Device_mmio::QueueNotifyOff>();
auto const addr = (offset * _notify_offset_multiplier >> 1) + 1;
_notify->local_addr<uint16_t>()[addr] = queue_index;
*(uint16_t*)(_notify.base() + addr) = queue_index;
}
uint32_t read_isr() {
return _isr->read<Device_mmio::IrqReason>(); }
return _isr.read<Device_mmio::IrqReason>(); }
void irq_sigh(Signal_context_capability cap) {
_irq.sigh(cap); }
void irq_ack() { _irq.ack_irq(); }
void irq_ack() { _irq.ack(); }
};
#endif /* _INCLUDE__VIRTIO__PCI_DEVICE_H_ */

View File

@ -14,9 +14,8 @@
#ifndef _INCLUDE__VIRTIO__QUEUE_H_
#define _INCLUDE__VIRTIO__QUEUE_H_
#include <base/attached_dataspace.h>
#include <platform_session/dma_buffer.h>
#include <base/stdint.h>
#include <dataspace/client.h>
#include <util/misc_math.h>
namespace Virtio
@ -138,7 +137,7 @@ class Virtio::Queue
Buffer_pool(Buffer_pool const &) = delete;
Buffer_pool &operator = (Buffer_pool const &) = delete;
Attached_dataspace _ds;
Platform::Dma_buffer _ds;
uint16_t const _buffer_count;
uint16_t const _buffer_size;
addr_t const _phys_base;
@ -156,16 +155,14 @@ class Virtio::Queue
};
Buffer_pool(Platform::Connection & plat,
Region_map & rm,
uint16_t const buffer_count,
uint16_t const buffer_size)
:
_ds(rm, plat.alloc_dma_buffer(buffer_count *
align_natural(buffer_size),
CACHED)),
_ds(plat, buffer_count * align_natural(buffer_size),
CACHED),
_buffer_count(buffer_count),
_buffer_size(buffer_size),
_phys_base(_dma_addr(plat, _ds.cap())) {}
_phys_base(_ds.dma_addr()) {}
const Buffer get(uint16_t descriptor_idx) const
{
@ -211,7 +208,7 @@ class Virtio::Queue
uint16_t const _queue_size;
Attached_dataspace _ds;
Platform::Dma_buffer _ds;
Buffer_pool _buffers;
Avail volatile * const _avail;
Used volatile * const _used;
@ -565,18 +562,16 @@ class Virtio::Queue
print(output, _queue_size);
}
Queue(Region_map & rm,
Platform::Connection & plat,
Queue(Platform::Connection & plat,
uint16_t queue_size,
uint16_t buffer_size)
: _queue_size(queue_size),
_ds(rm, plat.alloc_dma_buffer(_ds_size(queue_size), UNCACHED)),
_buffers(plat, rm, queue_size, _check_buffer_size(buffer_size)),
_ds(plat, _ds_size(queue_size), UNCACHED),
_buffers(plat, queue_size, _check_buffer_size(buffer_size)),
_avail(_init_avail(_ds.local_addr<uint8_t>(), queue_size)),
_used(_init_used(_ds.local_addr<uint8_t>(), queue_size)),
_descriptors(_ds.local_addr<uint8_t>(), queue_size),
_description(_init_description(queue_size,
_dma_addr(plat, _ds.cap())))
_description(_init_description(queue_size, _ds.dma_addr()))
{
_fill_descriptor_table();
}

View File

@ -203,7 +203,7 @@ class Virtio_fb::Driver
Env &_env;
Platform::Connection &_platform;
Virtio::Device &_device;
Control_queue _ctrl_vq { _env.rm(), _platform, 4, 512 };
Control_queue _ctrl_vq { _platform, 4, 512 };
uint32_t const _num_scanouts;
Signal_handler<Driver> _irq_handler { _env.ep(), *this, &Driver::_handle_irq };

View File

@ -137,10 +137,8 @@ class Virtio_input::Driver
Input::Absolute_motion _abs_motion { -1, -1 };
Abs_config _abs_config { { 0, 0 }, { 0, 0 }, 0, 0 };
Signal_handler<Driver> _irq_handler {_env.ep(), *this, &Driver::_handle_irq};
Events_virtqueue _events_vq { _env.rm(), _plat,
QUEUE_SIZE, QUEUE_ELM_SIZE };
Status_virtqueue _status_vq { _env.rm(), _plat,
QUEUE_SIZE, QUEUE_ELM_SIZE };
Events_virtqueue _events_vq { _plat, QUEUE_SIZE, QUEUE_ELM_SIZE };
Status_virtqueue _status_vq { _plat, QUEUE_SIZE, QUEUE_ELM_SIZE };
void _handle_event(::Event::Session_client::Batch &batch, const Event &evt)

View File

@ -288,18 +288,17 @@ class Virtio_nic::Device : Noncopyable
public:
Device(Genode::Env &env,
Virtio::Device &device,
Device(Virtio::Device &device,
Platform::Connection &plat,
Genode::Xml_node const &xml)
try :
_verbose { xml.attribute_value("verbose", false) },
_device { device },
_hw_features { _init_hw_features(xml) },
_rx_vq { env.rm(), plat,
_rx_vq { plat,
_vq_size(RX_VQ, xml, "rx_queue_size"),
_buf_size(RX_VQ, xml, "rx_buffer_size") },
_tx_vq { env.rm(), plat,
_tx_vq { plat,
_vq_size(TX_VQ, xml, "tx_queue_size"),
_buf_size(TX_VQ, xml, "tx_buffer_size") }
{ }
@ -496,7 +495,7 @@ class Virtio_nic::Uplink_client : public Virtio_nic::Device,
Platform::Connection &plat,
Genode::Xml_node const &xml)
:
Device { env, device, plat, xml },
Device { device, plat, xml },
Uplink_client_base { env, alloc, read_mac_address() },
_irq_handler { env.ep(), *this, &Uplink_client::_handle_irq }
{

View File

@ -14,7 +14,7 @@
/* Genode includes */
#include <base/component.h>
#include <base/heap.h>
#include <legacy/x86/platform_session/connection.h>
#include <platform_session/connection.h>
#include <virtio/pci_device.h>
/* local includes */
@ -30,29 +30,15 @@ struct Virtio_pci_nic::Main
struct Device_not_found : Genode::Exception { };
Genode::Env & env;
Genode::Heap heap { env.ram(), env.rm() };
Platform::Connection pci { env };
Platform::Device_client platform_device;
Virtio::Device virtio_device { env, platform_device };
Attached_rom_dataspace config_rom { env, "config" };
Genode::Heap heap { env.ram(), env.rm() };
Platform::Connection pci { env };
Virtio::Device virtio_device { env, pci };
Attached_rom_dataspace config_rom { env, "config" };
Virtio_nic::Uplink_client uplink_client { env, heap, virtio_device,
pci, config_rom.xml() };
Platform::Device_capability find_platform_device()
{
Platform::Device_capability device_cap;
pci.with_upgrade([&] () { device_cap = pci.first_device(); });
if (!device_cap.valid()) throw Device_not_found();
return device_cap;
}
Main(Env &env)
: env(env), platform_device(find_platform_device())
{
log("--- VirtIO PCI driver started ---");
}
Main(Env &env) : env(env) {
log("--- VirtIO PCI driver started ---"); }
};