mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-24 07:46:42 +00:00
adc594a7e6
The read_config and write_config functions in the generic virtio headers used by all drivers lead to compiler warnings resp. errors if effective-c++ switch is enabled. Moreover, the functions require to define the access width as parameter. We can better turn them into template functions using the value type to read resp. write to derive the access width. Ref genodelabs/genode#4344
201 lines
5.1 KiB
C++
201 lines
5.1 KiB
C++
/*
|
|
* \brief VirtIO MMIO device
|
|
* \author Piotr Tworek
|
|
* \date 2019-09-27
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2020 Genode Labs GmbH
|
|
*
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
* under the terms of the GNU Affero General Public License version 3.
|
|
*/
|
|
|
|
#ifndef _INCLUDE__VIRTIO__MMIO_DEVICE_H_
|
|
#define _INCLUDE__VIRTIO__MMIO_DEVICE_H_
|
|
|
|
#include <platform_session/connection.h>
|
|
#include <platform_session/device.h>
|
|
#include <virtio/queue.h>
|
|
|
|
|
|
namespace Virtio {
|
|
using namespace Genode;
|
|
class Device;
|
|
}
|
|
|
|
|
|
class Virtio::Device : Platform::Device::Mmio
|
|
{
|
|
public:
|
|
|
|
struct Invalid_device : Genode::Exception { };
|
|
|
|
enum Status : uint8_t
|
|
{
|
|
RESET = 0,
|
|
ACKNOWLEDGE = 1 << 0,
|
|
DRIVER = 1 << 1,
|
|
DRIVER_OK = 1 << 2,
|
|
FEATURES_OK = 1 << 3,
|
|
FAILED = 1 << 7,
|
|
};
|
|
|
|
enum Access_size : uint8_t
|
|
{
|
|
ACCESS_8BIT,
|
|
ACCESS_16BIT,
|
|
ACCESS_32BIT,
|
|
};
|
|
|
|
private:
|
|
|
|
enum { VIRTIO_MMIO_MAGIC = 0x74726976 };
|
|
|
|
/**
|
|
* Some of the registers are actually 8 bits wide, but according to
|
|
* section 4.2.2.2 of VIRTIO 1.0 spec "The driver MUST use only 32 bit
|
|
* wide and aligned reads and writes".
|
|
*/
|
|
struct Magic : Register<0x000, 32> { };
|
|
struct Version : Register<0x004, 32> { };
|
|
struct DeviceID : Register<0x008, 32> { };
|
|
struct VendorID : Register<0x00C, 32> { };
|
|
struct DeviceFeatures : Register<0x010, 32> { };
|
|
struct DeviceFeaturesSel : Register<0x014, 32> { };
|
|
struct DriverFeatures : Register<0x020, 32> { };
|
|
struct DriverFeaturesSel : Register<0x024, 32> { };
|
|
struct QueueSel : Register<0x030, 32> { };
|
|
struct QueueNumMax : Register<0x034, 32> { };
|
|
struct QueueNum : Register<0x038, 32> { };
|
|
struct QueueReady : Register<0x044, 32> { };
|
|
struct QueueNotify : Register<0x050, 32> { };
|
|
struct InterruptStatus : Register<0x060, 32> { };
|
|
struct InterruptAck : Register<0x064, 32> { };
|
|
struct StatusReg : Register<0x070, 32> { };
|
|
struct QueueDescLow : Register<0x080, 32> { };
|
|
struct QueueDescHigh : Register<0x084, 32> { };
|
|
struct QueueAvailLow : Register<0x090, 32> { };
|
|
struct QueueAvailHigh : Register<0x094, 32> { };
|
|
struct QueueUsedLow : Register<0x0A0, 32> { };
|
|
struct QueueUsedHigh : Register<0x0A4, 32> { };
|
|
struct ConfigGeneration : Register<0x0FC, 32> { };
|
|
|
|
/**
|
|
* Different views on device configuration space. According to the
|
|
* VIRTIO 1.0 spec 64 bit wide registers are supposed to be read as
|
|
* two 32 bit values.
|
|
*/
|
|
template <typename T> class Config :
|
|
public Register_array<0x100, sizeof(T)*8,
|
|
256/sizeof(T), sizeof(T)*8> {};
|
|
|
|
/*
|
|
* Noncopyable
|
|
*/
|
|
Device(Device const &) = delete;
|
|
Device &operator = (Device const &) = delete;
|
|
|
|
Platform::Device::Irq _irq;
|
|
|
|
public:
|
|
|
|
Device(Platform::Device & device)
|
|
:
|
|
Platform::Device::Mmio(device), _irq(device, {0})
|
|
{
|
|
if (read<Magic>() != VIRTIO_MMIO_MAGIC) {
|
|
throw Invalid_device(); }
|
|
}
|
|
|
|
uint32_t vendor_id() { return read<VendorID>(); }
|
|
uint32_t device_id() { return read<DeviceID>(); }
|
|
uint8_t get_status() { return read<StatusReg>() & 0xff; }
|
|
|
|
bool set_status(uint8_t status)
|
|
{
|
|
write<StatusReg>(status);
|
|
return read<StatusReg>() == status;
|
|
}
|
|
|
|
uint32_t get_features(uint32_t selection)
|
|
{
|
|
write<DeviceFeaturesSel>(selection);
|
|
return read<DeviceFeatures>();
|
|
}
|
|
|
|
void set_features(uint32_t selection, uint32_t features)
|
|
{
|
|
write<DriverFeaturesSel>(selection);
|
|
write<DriverFeatures>(features);
|
|
}
|
|
|
|
uint8_t get_config_generation() {
|
|
return read<ConfigGeneration>() & 0xff; }
|
|
|
|
uint16_t get_max_queue_size(uint16_t queue_index)
|
|
{
|
|
write<QueueSel>(queue_index);
|
|
if (read<QueueReady>() != 0) {
|
|
return 0; }
|
|
return (uint16_t)read<QueueNumMax>();
|
|
}
|
|
|
|
template <typename T>
|
|
T read_config(const uint8_t offset)
|
|
{
|
|
static_assert(sizeof(T) <= 4);
|
|
return read<Config<T>>(offset >> log2(sizeof(T)));
|
|
}
|
|
|
|
template <typename T>
|
|
void write_config(const uint8_t offset, const T value)
|
|
{
|
|
static_assert(sizeof(T) <= 4);
|
|
write<Config<T>>(value, (offset >> log2(sizeof(T))));
|
|
}
|
|
|
|
bool configure_queue(uint16_t queue_index,
|
|
Virtio::Queue_description desc)
|
|
{
|
|
write<QueueSel>(queue_index);
|
|
|
|
if (read<QueueReady>() != 0)
|
|
return false;
|
|
|
|
write<QueueNum>(desc.size);
|
|
|
|
uint64_t addr = desc.desc;
|
|
write<QueueDescLow>((uint32_t)addr);
|
|
write<QueueDescHigh>((uint32_t)(addr >> 32));
|
|
|
|
addr = desc.avail;
|
|
write<QueueAvailLow>((uint32_t)addr);
|
|
write<QueueAvailHigh>((uint32_t)(addr >> 32));
|
|
|
|
addr = desc.used;
|
|
write<QueueUsedLow>((uint32_t)addr);
|
|
write<QueueUsedHigh>((uint32_t)(addr >> 32));
|
|
|
|
write<QueueReady>(1);
|
|
return read<QueueReady>() != 0;
|
|
}
|
|
|
|
void notify_buffers_available(uint16_t queue_index) {
|
|
write<QueueNotify>(queue_index); }
|
|
|
|
uint32_t read_isr()
|
|
{
|
|
uint32_t isr = read<InterruptStatus>();
|
|
write<InterruptAck>(isr);
|
|
return isr;
|
|
}
|
|
|
|
void irq_sigh(Signal_context_capability cap) {
|
|
_irq.sigh(cap); }
|
|
|
|
void irq_ack() { _irq.ack(); }
|
|
};
|
|
|
|
#endif /* _INCLUDE__VIRTIO__MMIO_DEVICE_H_ */
|