genode/repos/os/include/virtio/mmio_device.h

206 lines
5.4 KiB
C
Raw Normal View History

/*
* \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.
*/
struct Config_8 : Register_array<0x100, 8, 256, 8> { };
struct Config_16 : Register_array<0x100, 16, 128, 16> { };
struct Config_32 : Register_array<0x100, 32, 64, 32> { };
/*
* 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>();
}
uint32_t read_config(uint8_t offset, Access_size size)
{
switch (size) {
case ACCESS_8BIT: return read<Config_8>(offset);
case ACCESS_16BIT: return read<Config_16>(offset >> 1);
case ACCESS_32BIT: return read<Config_32>(offset >> 2);
}
return 0;
}
void write_config(uint8_t offset, Access_size size, uint32_t value)
{
switch (size) {
case ACCESS_8BIT: write<Config_8> ((uint8_t) value, offset); break;
case ACCESS_16BIT: write<Config_16>((uint16_t)value, (offset >> 1)); break;
case ACCESS_32BIT: write<Config_32>(value, (offset >> 2)); break;
}
}
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_ */