virtio: support modern devices

fixes #17239 #17238

Change-Id: Ia5b6347110a60fab18852079b30dca6301010474
Reviewed-on: https://review.haiku-os.org/c/haiku/+/6995
Reviewed-by: Jérôme Duval <jerome.duval@gmail.com>
This commit is contained in:
Jérôme Duval 2023-10-03 18:50:18 +02:00
parent 518e9cb9f2
commit 6d42b430d1
19 changed files with 507 additions and 139 deletions

View File

@ -26,13 +26,14 @@
#define VIRTIO_DEVICE_ID_VSOCK 19
#define VIRTIO_DEVICE_ID_CRYPTO 20
#define VIRTIO_FEATURE_TRANSPORT_MASK ((1 << 28) - 1)
#define VIRTIO_FEATURE_TRANSPORT_MASK ((1ULL << 28) - 1)
#define VIRTIO_FEATURE_NOTIFY_ON_EMPTY (1 << 24)
#define VIRTIO_FEATURE_ANY_LAYOUT (1 << 27)
#define VIRTIO_FEATURE_RING_INDIRECT_DESC (1 << 28)
#define VIRTIO_FEATURE_RING_EVENT_IDX (1 << 29)
#define VIRTIO_FEATURE_BAD_FEATURE (1 << 30)
#define VIRTIO_FEATURE_VERSION_1 (1ULL << 32)
#define VIRTIO_VIRTQUEUES_MAX_COUNT 8
@ -40,6 +41,8 @@
#define VIRTIO_CONFIG_STATUS_ACK 0x01
#define VIRTIO_CONFIG_STATUS_DRIVER 0x02
#define VIRTIO_CONFIG_STATUS_DRIVER_OK 0x04
#define VIRTIO_CONFIG_STATUS_FEATURES_OK 0x08
#define VIRTIO_CONFIG_STATUS_DEVICE_NEEDS_RESET 0x40
#define VIRTIO_CONFIG_STATUS_FAILED 0x80
// attributes:
@ -50,6 +53,8 @@
#define VIRTIO_DEVICE_TYPE_ITEM "virtio/type"
// alignment (uint16)
#define VIRTIO_VRING_ALIGNMENT_ITEM "virtio/vring_alignment"
// version (uint8)
#define VIRTIO_VERSION_ITEM "virtio/version"
// sim cookie, issued by virtio bus manager
typedef void* virtio_sim;
@ -78,8 +83,8 @@ typedef struct {
driver_module_info info;
void (*set_sim)(void* cookie, virtio_sim sim);
status_t (*read_host_features)(void* cookie, uint32* features);
status_t (*write_guest_features)(void* cookie, uint32 features);
status_t (*read_host_features)(void* cookie, uint64* features);
status_t (*write_guest_features)(void* cookie, uint64 features);
uint8 (*get_status)(void* cookie);
void (*set_status)(void* cookie, uint8 status);
status_t (*read_device_config)(void* cookie, uint8 offset, void* buffer,
@ -88,7 +93,8 @@ typedef struct {
const void* buffer, size_t bufferSize);
uint16 (*get_queue_ring_size)(void* cookie, uint16 queue);
status_t (*setup_queue)(void* cookie, uint16 queue, phys_addr_t phy);
status_t (*setup_queue)(void* cookie, uint16 queue, phys_addr_t phy, phys_addr_t phyAvail,
phys_addr_t phyUsed);
status_t (*setup_interrupt)(void* cookie, uint16 queueCount);
status_t (*free_interrupt)(void* cookie);
void (*notify_queue)(void* cookie, uint16 queue);
@ -99,10 +105,10 @@ typedef struct {
typedef struct {
driver_module_info info;
status_t (*negotiate_features)(virtio_device cookie, uint32 supported,
uint32* negotiated, const char* (*get_feature_name)(uint32));
status_t (*negotiate_features)(virtio_device cookie, uint64 supported,
uint64* negotiated, const char* (*get_feature_name)(uint64));
status_t (*clear_feature)(virtio_device cookie, uint32 feature);
status_t (*clear_feature)(virtio_device cookie, uint64 feature);
status_t (*read_device_config)(virtio_device cookie, uint8 offset,
void* buffer, size_t bufferSize);

View File

@ -17,7 +17,7 @@
const char*
get_feature_name(uint32 feature)
get_feature_name(uint64 feature)
{
switch (feature) {
case VIRTIO_BALLOON_F_MUST_TELL_HOST:

View File

@ -61,7 +61,7 @@ private:
virtio_device* fVirtioDevice;
status_t fStatus;
uint32 fFeatures;
uint64 fFeatures;
::virtio_queue fVirtioQueues[2];
physical_entry fEntry;

View File

@ -8,7 +8,7 @@
const char *
virtio_get_feature_name(uint32 feature)
virtio_get_feature_name(uint64 feature)
{
switch (feature) {
case VIRTIO_FEATURE_NOTIFY_ON_EMPTY:
@ -62,6 +62,7 @@ VirtioDevice::VirtioDevice(device_node *node)
fQueues(NULL),
fFeatures(0),
fAlignment(0),
fVirtio1(false),
fConfigHandler(NULL),
fDriverCookie(NULL)
{
@ -80,6 +81,9 @@ VirtioDevice::VirtioDevice(device_node *node)
ERROR("alignment missing\n");
return;
}
uint8 version = 0;
if (gDeviceManager->get_attr_uint8(fNode, VIRTIO_VERSION_ITEM, &version, true) == B_OK)
fVirtio1 = version == 1;
fController->set_sim(fCookie, this);
@ -104,8 +108,8 @@ VirtioDevice::InitCheck()
status_t
VirtioDevice::NegotiateFeatures(uint32 supported, uint32* negotiated,
const char* (*get_feature_name)(uint32))
VirtioDevice::NegotiateFeatures(uint64 supported, uint64* negotiated,
const char* (*get_feature_name)(uint64))
{
fFeatures = 0;
status_t status = fController->read_host_features(fCookie, &fFeatures);
@ -114,22 +118,43 @@ VirtioDevice::NegotiateFeatures(uint32 supported, uint32* negotiated,
_DumpFeatures("read features", fFeatures, get_feature_name);
if (fVirtio1) {
supported |= VIRTIO_FEATURE_VERSION_1;
supported &= ~VIRTIO_FEATURE_NOTIFY_ON_EMPTY;
}
fFeatures &= supported;
// filter our own features
fFeatures &= (VIRTIO_FEATURE_TRANSPORT_MASK
| VIRTIO_FEATURE_RING_INDIRECT_DESC | VIRTIO_FEATURE_RING_EVENT_IDX);
*negotiated = fFeatures;
| VIRTIO_FEATURE_RING_INDIRECT_DESC | VIRTIO_FEATURE_RING_EVENT_IDX
| VIRTIO_FEATURE_VERSION_1);
_DumpFeatures("negotiated features", fFeatures, get_feature_name);
return fController->write_guest_features(fCookie, fFeatures);
status = fController->write_guest_features(fCookie, fFeatures);
if (status != B_OK)
return status;
if (fVirtio1) {
fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_FEATURES_OK);
if ((fController->get_status(fCookie) & VIRTIO_CONFIG_STATUS_FEATURES_OK) == 0) {
fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_FAILED);
return B_BAD_VALUE;
}
if ((fFeatures & VIRTIO_FEATURE_VERSION_1) == 0) {
fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_FAILED);
return B_BAD_VALUE;
}
}
*negotiated = fFeatures;
return B_OK;
}
status_t
VirtioDevice::ClearFeature(uint32 feature)
VirtioDevice::ClearFeature(uint64 feature)
{
fFeatures &= ~feature;
return fController->write_guest_features(fCookie, fFeatures);
@ -224,9 +249,10 @@ VirtioDevice::FreeInterrupts()
status_t
VirtioDevice::SetupQueue(uint16 queueNumber, phys_addr_t physAddr)
VirtioDevice::SetupQueue(uint16 queueNumber, phys_addr_t physAddr, phys_addr_t phyAvail,
phys_addr_t phyUsed)
{
return fController->setup_queue(fCookie, queueNumber, physAddr);
return fController->setup_queue(fCookie, queueNumber, physAddr, phyAvail, phyUsed);
}
@ -278,12 +304,12 @@ VirtioDevice::_DestroyQueues(size_t count)
void
VirtioDevice::_DumpFeatures(const char* title, uint32 features,
const char* (*get_feature_name)(uint32))
VirtioDevice::_DumpFeatures(const char* title, uint64 features,
const char* (*get_feature_name)(uint64))
{
char features_string[512] = "";
for (uint32 i = 0; i < 32; i++) {
uint32 feature = features & (1 << i);
for (uint32 i = 0; i < 64; i++) {
uint64 feature = features & (1ULL << i);
if (feature == 0)
continue;
const char* name = virtio_get_feature_name(feature);

View File

@ -54,8 +54,8 @@ virtio_device_removed(void *_device)
status_t
virtio_negotiate_features(void* _device, uint32 supported,
uint32* negotiated, const char* (*get_feature_name)(uint32))
virtio_negotiate_features(void* _device, uint64 supported,
uint64* negotiated, const char* (*get_feature_name)(uint64))
{
CALLED();
VirtioDevice *device = (VirtioDevice *)_device;
@ -65,7 +65,7 @@ virtio_negotiate_features(void* _device, uint32 supported,
status_t
virtio_clear_feature(void* _device, uint32 feature)
virtio_clear_feature(void* _device, uint64 feature)
{
CALLED();
VirtioDevice *device = (VirtioDevice *)_device;

View File

@ -45,10 +45,10 @@ public:
status_t InitCheck();
uint32 ID() const { return fID; }
status_t NegotiateFeatures(uint32 supported,
uint32* negotiated,
const char* (*get_feature_name)(uint32));
status_t ClearFeature(uint32 feature);
status_t NegotiateFeatures(uint64 supported,
uint64* negotiated,
const char* (*get_feature_name)(uint64));
status_t ClearFeature(uint64 feature);
status_t ReadDeviceConfig(uint8 offset, void* buffer,
size_t bufferSize);
@ -63,12 +63,13 @@ public:
status_t FreeInterrupts();
uint16 Alignment() const { return fAlignment; }
uint32 Features() const { return fFeatures; }
uint64 Features() const { return fFeatures; }
void* DriverCookie() { return fDriverCookie; }
status_t SetupQueue(uint16 queueNumber,
phys_addr_t physAddr);
phys_addr_t physAddr, phys_addr_t phyAvail,
phys_addr_t phyUsed);
void NotifyQueue(uint16 queueNumber);
status_t QueueInterrupt(uint16 queueNumber);
@ -76,8 +77,8 @@ public:
private:
void _DumpFeatures(const char* title,
uint32 features,
const char* (*get_feature_name)(uint32));
uint64 features,
const char* (*get_feature_name)(uint64));
void _DestroyQueues(size_t count);
@ -89,8 +90,9 @@ private:
status_t fStatus;
VirtioQueue** fQueues;
size_t fQueueCount;
uint32 fFeatures;
uint64 fFeatures;
uint16 fAlignment;
bool fVirtio1;
virtio_intr_func fConfigHandler;
void* fDriverCookie;

View File

@ -182,7 +182,9 @@ VirtioQueue::VirtioQueue(VirtioDevice* device, uint16 queueNumber,
DisableInterrupt();
device->SetupQueue(fQueueNumber, physAddr);
device->SetupQueue(fQueueNumber, physAddr,
physAddr + ((addr_t)fRing.avail - (addr_t)fRing.desc),
physAddr + ((addr_t)fRing.used - (addr_t)fRing.desc));
}

View File

@ -14,7 +14,7 @@
const char *
get_feature_name(uint32 feature)
get_feature_name(uint64 feature)
{
switch (feature) {
}

View File

@ -49,7 +49,7 @@ private:
virtio_device* fVirtioDevice;
status_t fStatus;
uint32 fFeatures;
uint64 fFeatures;
::virtio_queue fVirtioQueue;
spinlock fInterruptLock;

View File

@ -16,7 +16,7 @@
const char *
get_feature_name(uint32 feature)
get_feature_name(uint64 feature)
{
switch (feature) {
case VIRTIO_SCSI_F_INOUT:

View File

@ -81,7 +81,7 @@ private:
status_t fStatus;
struct virtio_scsi_config fConfig;
uint32 fFeatures;
uint64 fFeatures;
::virtio_queue fControlVirtioQueue;
::virtio_queue fEventVirtioQueue;
::virtio_queue fRequestVirtioQueue;

View File

@ -33,7 +33,7 @@ device_manager_info* gDeviceManager;
static const char *
virtio_get_feature_name(uint32 feature)
virtio_get_feature_name(uint64 feature)
{
switch (feature) {
case VIRTIO_FEATURE_NOTIFY_ON_EMPTY:
@ -52,12 +52,12 @@ virtio_get_feature_name(uint32 feature)
static void
virtio_dump_features(const char* title, uint32 features,
const char* (*get_feature_name)(uint32))
virtio_dump_features(const char* title, uint64 features,
const char* (*get_feature_name)(uint64))
{
char features_string[512] = "";
for (uint32 i = 0; i < 32; i++) {
uint32 feature = features & (1 << i);
for (uint64 i = 0; i < 32; i++) {
uint64 feature = features & (1 << i);
if (feature == 0)
continue;
const char* name = virtio_get_feature_name(feature);
@ -365,8 +365,8 @@ virtio_device_register_child_devices(void* cookie)
static status_t
virtio_device_negotiate_features(virtio_device cookie, uint32 supported,
uint32* negotiated, const char* (*get_feature_name)(uint32))
virtio_device_negotiate_features(virtio_device cookie, uint64 supported,
uint64* negotiated, const char* (*get_feature_name)(uint64))
{
TRACE("virtio_device_negotiate_features(%p)\n", cookie);
VirtioDevice* dev = (VirtioDevice*)cookie;
@ -374,7 +374,7 @@ virtio_device_negotiate_features(virtio_device cookie, uint32 supported,
dev->fRegs->status |= kVirtioConfigSAcknowledge;
dev->fRegs->status |= kVirtioConfigSDriver;
uint32 features = dev->fRegs->deviceFeatures;
uint64 features = dev->fRegs->deviceFeatures;
virtio_dump_features("read features", features, get_feature_name);
features &= supported;
@ -395,7 +395,7 @@ virtio_device_negotiate_features(virtio_device cookie, uint32 supported,
static status_t
virtio_device_clear_feature(virtio_device cookie, uint32 feature)
virtio_device_clear_feature(virtio_device cookie, uint64 feature)
{
panic("not implemented");
return B_ERROR;

View File

@ -2,7 +2,8 @@ SubDir HAIKU_TOP src add-ons kernel busses virtio virtio_pci ;
SubDirC++Flags -fno-rtti ;
UsePrivateHeaders kernel virtio ;
UsePrivateHeaders virtio ;
UsePrivateKernelHeaders ;
KernelAddon virtio_pci :
virtio_pci.cpp

View File

@ -9,6 +9,9 @@
#include <string.h>
#include <bus/PCI.h>
#include <SupportDefs.h>
#include <kernel.h>
#include <virtio.h>
#include "virtio_pci.h"
@ -45,11 +48,18 @@ typedef struct {
typedef struct {
pci_device_module_info* pci;
pci_device* device;
bool virtio1;
addr_t base_addr;
area_id registersArea[4];
addr_t commonCfgAddr;
addr_t isrAddr;
addr_t notifyAddr;
uint32 notifyOffsetMultiplier;
uint8 irq;
virtio_irq_type irq_type;
virtio_sim sim;
uint16 queue_count;
addr_t* notifyOffsets;
device_node* node;
pci_info info;
@ -62,12 +72,53 @@ device_manager_info* gDeviceManager;
virtio_for_controller_interface* gVirtio;
int32
static status_t
virtio_pci_find_capability(virtio_pci_sim_info* bus, uint8 cfgType,
void* buffer, size_t size)
{
uint8 capabilityOffset;
if (bus->pci->find_pci_capability(bus->device, PCI_cap_id_vendspec, &capabilityOffset) != B_OK)
return B_ENTRY_NOT_FOUND;
if (size < sizeof(virtio_pci_cap))
return B_RESULT_NOT_REPRESENTABLE;
union regs {
uint32 reg[8];
struct virtio_pci_cap capability;
} * v = (union regs*)buffer;
while (capabilityOffset != 0) {
for (int i = 0; i < 4; i++) {
v->reg[i] = bus->pci->read_pci_config(bus->device, capabilityOffset + i * 4, 4);
}
if (v->capability.cfg_type == cfgType)
break;
capabilityOffset = v->capability.cap_next;
}
if (capabilityOffset == 0)
return B_ENTRY_NOT_FOUND;
if (v->capability.length > sizeof(virtio_pci_cap)) {
size_t length = min_c(ROUNDUP(v->capability.length, sizeof(uint32)), size);
for (size_t i = 4; i < length / sizeof(uint32); i++)
v->reg[i] = bus->pci->read_pci_config(bus->device, capabilityOffset + i * 4, 4);
}
return B_OK;
}
static int32
virtio_pci_interrupt(void *data)
{
virtio_pci_sim_info* bus = (virtio_pci_sim_info*)data;
uint8 isr = bus->pci->read_io_8(bus->device,
bus->base_addr + VIRTIO_PCI_ISR);
uint8 isr;
if (bus->virtio1) {
uint8* isrAddr = (uint8*)bus->isrAddr;
isr = *isrAddr;
} else {
isr = bus->pci->read_io_8(bus->device,
bus->base_addr + VIRTIO_PCI_ISR);
}
if (isr == 0)
return B_UNHANDLED_INTERRUPT;
@ -81,7 +132,7 @@ virtio_pci_interrupt(void *data)
}
int32
static int32
virtio_pci_config_interrupt(void *data)
{
virtio_pci_sim_info* bus = (virtio_pci_sim_info*)data;
@ -91,7 +142,7 @@ virtio_pci_config_interrupt(void *data)
}
int32
static int32
virtio_pci_queue_interrupt(void *data)
{
virtio_pci_queue_cookie* cookie = (virtio_pci_queue_cookie*)data;
@ -101,33 +152,47 @@ virtio_pci_queue_interrupt(void *data)
}
status_t
static status_t
virtio_pci_setup_msix_interrupts(virtio_pci_sim_info* bus)
{
CALLED();
uint8 irq = 0; // first irq slot
bus->pci->write_io_16(bus->device, bus->base_addr
+ VIRTIO_MSI_CONFIG_VECTOR, irq);
if (bus->pci->read_io_16(bus->device, bus->base_addr
+ VIRTIO_MSI_CONFIG_VECTOR) == VIRTIO_MSI_NO_VECTOR) {
ERROR("msix config vector incorrect\n");
return B_BAD_VALUE;
if (bus->virtio1) {
volatile uint16 *msixVector = (uint16*)(bus->commonCfgAddr
+ offsetof(struct virtio_pci_common_cfg, config_msix_vector));
*msixVector = irq;
} else {
bus->pci->write_io_16(bus->device, bus->base_addr
+ VIRTIO_MSI_CONFIG_VECTOR, irq);
if (bus->pci->read_io_16(bus->device, bus->base_addr
+ VIRTIO_MSI_CONFIG_VECTOR) == VIRTIO_MSI_NO_VECTOR) {
ERROR("msix config vector incorrect\n");
return B_BAD_VALUE;
}
}
if (bus->irq_type == VIRTIO_IRQ_MSI_X)
irq++;
for (uint16 queue = 0; queue < bus->queue_count; queue++) {
bus->pci->write_io_16(bus->device, bus->base_addr
+ VIRTIO_PCI_QUEUE_SEL, queue);
bus->pci->write_io_16(bus->device, bus->base_addr
+ VIRTIO_MSI_QUEUE_VECTOR, irq);
if (bus->virtio1) {
volatile uint16* queueSelect = (uint16*)(bus->commonCfgAddr
+ offsetof(struct virtio_pci_common_cfg, queue_select));
*queueSelect = queue;
volatile uint16* msixVector = (uint16*)(bus->commonCfgAddr
+ offsetof(struct virtio_pci_common_cfg, queue_msix_vector));
*msixVector = irq;
} else {
bus->pci->write_io_16(bus->device, bus->base_addr
+ VIRTIO_PCI_QUEUE_SEL, queue);
bus->pci->write_io_16(bus->device, bus->base_addr
+ VIRTIO_MSI_QUEUE_VECTOR, irq);
if (bus->pci->read_io_16(bus->device, bus->base_addr
+ VIRTIO_MSI_QUEUE_VECTOR) == VIRTIO_MSI_NO_VECTOR) {
ERROR("msix queue vector incorrect\n");
return B_BAD_VALUE;
if (bus->pci->read_io_16(bus->device, bus->base_addr
+ VIRTIO_MSI_QUEUE_VECTOR) == VIRTIO_MSI_NO_VECTOR) {
ERROR("msix queue vector incorrect\n");
return B_BAD_VALUE;
}
}
if (bus->irq_type == VIRTIO_IRQ_MSI_X)
irq++;
}
@ -146,7 +211,7 @@ set_sim(void* cookie, virtio_sim sim)
static status_t
read_host_features(void* cookie, uint32 *features)
read_host_features(void* cookie, uint64 *features)
{
CALLED();
virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie;
@ -154,65 +219,116 @@ read_host_features(void* cookie, uint32 *features)
TRACE("read_host_features() %p node %p pci %p device %p\n", bus,
bus->node, bus->pci, bus->device);
*features = bus->pci->read_io_32(bus->device,
bus->base_addr + VIRTIO_PCI_HOST_FEATURES);
if (bus->virtio1) {
volatile uint32 *select = (uint32*)(bus->commonCfgAddr
+ offsetof(struct virtio_pci_common_cfg, device_feature_select));
volatile uint32 *feature = (uint32*)(bus->commonCfgAddr
+ offsetof(struct virtio_pci_common_cfg, device_feature));
*select = 0;
*features = *feature;
*select = 1;
*features |= ((uint64)*feature << 32) ;
TRACE("read_host_features() %" B_PRIx64 "\n", *features);
} else {
*features = bus->pci->read_io_32(bus->device,
bus->base_addr + VIRTIO_PCI_HOST_FEATURES);
}
return B_OK;
}
static status_t
write_guest_features(void* cookie, uint32 features)
write_guest_features(void* cookie, uint64 features)
{
CALLED();
virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie;
bus->pci->write_io_32(bus->device, bus->base_addr
+ VIRTIO_PCI_GUEST_FEATURES, features);
if (bus->virtio1) {
volatile uint32 *select = (uint32*)(bus->commonCfgAddr
+ offsetof(struct virtio_pci_common_cfg, device_feature_select));
volatile uint32 *feature = (uint32*)(bus->commonCfgAddr
+ offsetof(struct virtio_pci_common_cfg, device_feature));
*select = 0;
*feature = features & 0xffffffff;
*select = 1;
*feature = (features >> 32) ;
} else {
bus->pci->write_io_32(bus->device, bus->base_addr
+ VIRTIO_PCI_GUEST_FEATURES, features);
}
return B_OK;
}
uint8
static uint8
get_status(void* cookie)
{
CALLED();
virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie;
return bus->pci->read_io_8(bus->device, bus->base_addr
+ VIRTIO_PCI_STATUS);
if (bus->virtio1) {
uint8 *addr = (uint8*)(bus->commonCfgAddr
+ offsetof(struct virtio_pci_common_cfg, device_status));
return *addr;
} else {
return bus->pci->read_io_8(bus->device, bus->base_addr + VIRTIO_PCI_STATUS);
}
}
void
static void
set_status(void* cookie, uint8 status)
{
CALLED();
virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie;
bus->pci->write_io_8(bus->device, bus->base_addr + VIRTIO_PCI_STATUS,
status);
if (bus->virtio1) {
uint8 *addr = (uint8*)(bus->commonCfgAddr
+ offsetof(struct virtio_pci_common_cfg, device_status));
uint8 old = 0;
if (status != 0)
old = *addr;
*addr = status | old;
} else {
uint8 old = 0;
if (status != 0)
old = bus->pci->read_io_8(bus->device, bus->base_addr + VIRTIO_PCI_STATUS);
bus->pci->write_io_8(bus->device, bus->base_addr + VIRTIO_PCI_STATUS, status | old);
}
}
status_t
static status_t
read_device_config(void* cookie, uint8 _offset, void* _buffer,
size_t bufferSize)
{
CALLED();
virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie;
addr_t offset = bus->base_addr + VIRTIO_PCI_CONFIG(bus) + _offset;
addr_t offset = bus->base_addr + _offset;
if (!bus->virtio1)
offset += VIRTIO_PCI_CONFIG(bus);
uint8* buffer = (uint8*)_buffer;
while (bufferSize > 0) {
uint8 size = 4;
if (bufferSize == 1) {
size = 1;
*buffer = bus->pci->read_io_8(bus->device,
offset);
if (bus->virtio1) {
*buffer = *(uint8*)offset;
} else {
*buffer = bus->pci->read_io_8(bus->device, offset);
}
} else if (bufferSize <= 3) {
size = 2;
*(uint16*)buffer = bus->pci->read_io_16(bus->device,
offset);
if (bus->virtio1) {
*(uint16*)buffer = *(uint16*)offset;
} else {
*(uint16*)buffer = bus->pci->read_io_16(bus->device, offset);
}
} else {
*(uint32*)buffer = bus->pci->read_io_32(bus->device,
offset);
if (bus->virtio1) {
*(uint32*)buffer = *(uint32*)offset;
} else {
*(uint32*)buffer = bus->pci->read_io_32(bus->device,
offset);
}
}
buffer += size;
bufferSize -= size;
@ -223,28 +339,39 @@ read_device_config(void* cookie, uint8 _offset, void* _buffer,
}
status_t
static status_t
write_device_config(void* cookie, uint8 _offset, const void* _buffer,
size_t bufferSize)
{
CALLED();
virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie;
addr_t offset = bus->base_addr + VIRTIO_PCI_CONFIG(bus) + _offset;
addr_t offset = bus->base_addr + _offset;
if (!bus->virtio1)
offset += VIRTIO_PCI_CONFIG(bus);
const uint8* buffer = (const uint8*)_buffer;
while (bufferSize > 0) {
uint8 size = 4;
if (bufferSize == 1) {
size = 1;
bus->pci->write_io_8(bus->device,
offset, *buffer);
if (bus->virtio1) {
*(uint8*)offset = *buffer;
} else {
bus->pci->write_io_8(bus->device, offset, *buffer);
}
} else if (bufferSize <= 3) {
size = 2;
bus->pci->write_io_16(bus->device,
offset, *(const uint16*)buffer);
if (bus->virtio1) {
*(uint16*)offset = *(uint16*)buffer;
} else {
bus->pci->write_io_16(bus->device, offset, *(const uint16*)buffer);
}
} else {
bus->pci->write_io_32(bus->device,
offset, *(const uint32*)buffer);
if (bus->virtio1) {
*(uint32*)offset = *(uint32*)buffer;
} else {
bus->pci->write_io_32(bus->device, offset, *(const uint32*)buffer);
}
}
buffer += size;
bufferSize -= size;
@ -254,32 +381,67 @@ write_device_config(void* cookie, uint8 _offset, const void* _buffer,
}
uint16
static uint16
get_queue_ring_size(void* cookie, uint16 queue)
{
CALLED();
virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie;
bus->pci->write_io_16(bus->device, bus->base_addr + VIRTIO_PCI_QUEUE_SEL,
queue);
return bus->pci->read_io_16(bus->device, bus->base_addr
+ VIRTIO_PCI_QUEUE_NUM);
if (bus->virtio1) {
volatile uint16* queueSelect = (uint16*)(bus->commonCfgAddr
+ offsetof(struct virtio_pci_common_cfg, queue_select));
*queueSelect = queue;
volatile uint16* ringSize = (volatile uint16*)(bus->commonCfgAddr
+ offsetof(struct virtio_pci_common_cfg, queue_size));
return *ringSize;
} else {
bus->pci->write_io_16(bus->device, bus->base_addr + VIRTIO_PCI_QUEUE_SEL,
queue);
return bus->pci->read_io_16(bus->device, bus->base_addr
+ VIRTIO_PCI_QUEUE_NUM);
}
}
status_t
setup_queue(void* cookie, uint16 queue, phys_addr_t phy)
static status_t
setup_queue(void* cookie, uint16 queue, phys_addr_t phy, phys_addr_t phyAvail,
phys_addr_t phyUsed)
{
CALLED();
virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie;
bus->pci->write_io_16(bus->device, bus->base_addr + VIRTIO_PCI_QUEUE_SEL,
queue);
bus->pci->write_io_32(bus->device, bus->base_addr + VIRTIO_PCI_QUEUE_PFN,
(uint32)phy >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
if (queue >= bus->queue_count)
return B_BAD_VALUE;
if (bus->virtio1) {
volatile uint16* queueSelect = (uint16*)(bus->commonCfgAddr
+ offsetof(struct virtio_pci_common_cfg, queue_select));
*queueSelect = queue;
volatile uint64* queueDesc = (volatile uint64*)(bus->commonCfgAddr
+ offsetof(struct virtio_pci_common_cfg, queue_desc));
*queueDesc = phy;
volatile uint64* queueAvail = (volatile uint64*)(bus->commonCfgAddr
+ offsetof(struct virtio_pci_common_cfg, queue_avail));
*queueAvail = phyAvail;
volatile uint64* queueUsed = (volatile uint64*)(bus->commonCfgAddr
+ offsetof(struct virtio_pci_common_cfg, queue_used));
*queueUsed = phyUsed;
volatile uint16* queueEnable = (volatile uint16*)(bus->commonCfgAddr
+ offsetof(struct virtio_pci_common_cfg, queue_enable));
*queueEnable = 1;
volatile uint16* queueNotifyOffset = (volatile uint16*)(bus->commonCfgAddr
+ offsetof(struct virtio_pci_common_cfg, queue_notify_off));
bus->notifyOffsets[queue] = *queueNotifyOffset * bus->notifyOffsetMultiplier;
} else {
bus->pci->write_io_16(bus->device, bus->base_addr + VIRTIO_PCI_QUEUE_SEL, queue);
bus->pci->write_io_32(bus->device, bus->base_addr + VIRTIO_PCI_QUEUE_PFN,
(uint32)phy >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
}
return B_OK;
}
status_t
static status_t
setup_interrupt(void* cookie, uint16 queueCount)
{
CALLED();
@ -308,7 +470,7 @@ setup_interrupt(void* cookie, uint16 queueCount)
}
} else {
uint8 vector;
if (bus->pci->configure_msix(bus->device, queueCount + 1, &vector) == B_OK
if (bus->pci->configure_msix(bus->device, 1, &vector) == B_OK
&& bus->pci->enable_msix(bus->device) == B_OK) {
TRACE_ALWAYS("using MSI-X vector shared %u\n", 1);
bus->irq = vector;
@ -375,7 +537,7 @@ setup_interrupt(void* cookie, uint16 queueCount)
}
status_t
static status_t
free_interrupt(void* cookie)
{
CALLED();
@ -402,13 +564,20 @@ free_interrupt(void* cookie)
}
void
static void
notify_queue(void* cookie, uint16 queue)
{
CALLED();
virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie;
bus->pci->write_io_16(bus->device, bus->base_addr
+ VIRTIO_PCI_QUEUE_NOTIFY, queue);
if (queue >= bus->queue_count)
return;
if (bus->virtio1) {
volatile uint16* notifyAddr = (volatile uint16*)(bus->notifyAddr + bus->notifyOffsets[queue]);
*notifyAddr = queue;
} else {
bus->pci->write_io_16(bus->device, bus->base_addr
+ VIRTIO_PCI_QUEUE_NOTIFY, queue);
}
}
@ -446,14 +615,89 @@ init_bus(device_node* node, void** bus_cookie)
pci_info *pciInfo = &bus->info;
pci->get_pci_info(device, pciInfo);
// legacy interrupt
bus->base_addr = pciInfo->u.h0.base_registers[0];
bus->virtio1 = pciInfo->revision == 1;
if (bus->virtio1) {
struct virtio_pci_cap common, isr, deviceCap;
struct virtio_pci_notify_cap notify;
bool deviceCfgFound = false;
if (virtio_pci_find_capability(bus, VIRTIO_PCI_CAP_COMMON_CFG, &common,
sizeof(common)) != B_OK) {
return B_DEVICE_NOT_FOUND;
}
if (virtio_pci_find_capability(bus, VIRTIO_PCI_CAP_ISR_CFG, &isr,
sizeof(isr)) != B_OK) {
return B_DEVICE_NOT_FOUND;
}
if (virtio_pci_find_capability(bus, VIRTIO_PCI_CAP_DEVICE_CFG, &deviceCap,
sizeof(deviceCap)) != B_OK) {
memset(&deviceCap, 0, sizeof(deviceCap));
} else {
deviceCfgFound = true;
}
if (virtio_pci_find_capability(bus, VIRTIO_PCI_CAP_NOTIFY_CFG, &notify,
sizeof(notify)) != B_OK) {
return B_DEVICE_NOT_FOUND;
}
size_t bars[6] = {0};
if (common.length > 0)
bars[common.bar] = common.offset + common.length;
if (isr.length > 0)
bars[isr.bar] = max_c(bars[isr.bar], isr.offset + isr.length);
if (notify.cap.length > 0) {
bars[notify.cap.bar] = max_c(bars[notify.cap.bar], notify.cap.offset
+ notify.cap.length);
}
if (deviceCfgFound && deviceCap.length > 0)
bars[deviceCap.bar] = max_c(bars[deviceCap.bar], deviceCap.offset + deviceCap.length);
int index = 0;
addr_t registers[6] = {0};
for (int i = 0; i < 6; i++) {
if (bars[i] == 0)
continue;
phys_addr_t barAddr = pciInfo->u.h0.base_registers[i];
size_t barSize = pciInfo->u.h0.base_register_sizes[i];
if ((pciInfo->u.h0.base_register_flags[i] & PCI_address_type) == PCI_address_type_64) {
barAddr |= (uint64)pciInfo->u.h0.base_registers[i + 1] << 32;
barSize |= (uint64)pciInfo->u.h0.base_register_sizes[i + 1] << 32;
}
bus->registersArea[i] = map_physical_memory("Virtio PCI memory mapped registers",
barAddr, barSize, B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
(void **)&registers[i]);
index++;
}
bus->commonCfgAddr = registers[common.bar] + common.offset;
bus->isrAddr = registers[isr.bar] + isr.offset;
bus->notifyAddr = registers[notify.cap.bar] + notify.cap.offset;
bus->notifyOffsetMultiplier = notify.notify_off_multiplier;
if (deviceCfgFound)
bus->base_addr = registers[deviceCap.bar] + deviceCap.offset;
// enable bus master and memory
uint16 pcicmd = pci->read_pci_config(device, PCI_command, 2);
pcicmd &= ~(PCI_command_int_disable | PCI_command_io);
pci->write_pci_config(device, PCI_command, 2,
pcicmd | PCI_command_master | PCI_command_memory);
volatile uint16 *queueCount = (uint16*)(bus->commonCfgAddr
+ offsetof(struct virtio_pci_common_cfg, num_queues));
bus->notifyOffsets = new addr_t[*queueCount];
} else {
// legacy interrupt
bus->base_addr = pciInfo->u.h0.base_registers[0];
// enable bus master and io
uint16 pcicmd = pci->read_pci_config(device, PCI_command, 2);
pcicmd &= ~(PCI_command_memory | PCI_command_int_disable);
pcicmd |= PCI_command_master | PCI_command_io;
pci->write_pci_config(device, PCI_command, 2, pcicmd);
}
// enable bus master and io
uint16 pcicmd = pci->read_pci_config(device, PCI_command, 2);
pcicmd &= ~(PCI_command_memory | PCI_command_int_disable);
pcicmd |= PCI_command_master | PCI_command_io;
pci->write_pci_config(device, PCI_command, 2, pcicmd);
set_status(bus, VIRTIO_CONFIG_STATUS_RESET);
set_status(bus, VIRTIO_CONFIG_STATUS_ACK);
@ -488,6 +732,16 @@ uninit_bus(void* bus_cookie)
} else
remove_io_interrupt_handler(bus->irq, virtio_pci_interrupt, bus);
if (bus->virtio1) {
for (int i = 0; i < 6; i++) {
if (bus->registersArea[i] >= 0)
delete_area(bus->registersArea[i]);
else
break;
}
}
delete[] bus->notifyOffsets;
delete[] bus->cookies;
delete bus;
}
@ -514,11 +768,16 @@ register_child_devices(void* cookie)
gDeviceManager->get_driver(parent, (driver_module_info**)&pci,
(void**)&device);
uint16 pciSubDeviceId = pci->read_pci_config(device, PCI_subsystem_id,
2);
uint16 pciSubDeviceId = pci->read_pci_config(device, PCI_subsystem_id, 2);
uint8 pciRevision = pci->read_pci_config(device, PCI_revision, 1);
uint16 pciDeviceId = pci->read_pci_config(device, PCI_device_id, 2);
uint16 virtioDeviceId = pciSubDeviceId;
if (pciDeviceId >= VIRTIO_PCI_DEVICEID_MODERN_MIN)
virtioDeviceId = pciDeviceId - VIRTIO_PCI_DEVICEID_MODERN_MIN;
char prettyName[25];
sprintf(prettyName, "Virtio Device %" B_PRIu16, pciSubDeviceId);
sprintf(prettyName, "Virtio Device %" B_PRIu16, virtioDeviceId);
device_attr attrs[] = {
// properties of this controller for virtio bus manager
@ -529,9 +788,11 @@ register_child_devices(void* cookie)
// private data to identify the device
{ VIRTIO_DEVICE_TYPE_ITEM, B_UINT16_TYPE,
{ .ui16 = pciSubDeviceId }},
{ .ui16 = virtioDeviceId }},
{ VIRTIO_VRING_ALIGNMENT_ITEM, B_UINT16_TYPE,
{ .ui16 = VIRTIO_PCI_VRING_ALIGN }},
{ VIRTIO_VERSION_ITEM, B_UINT8_TYPE,
{ .ui8 = pciRevision }},
{ NULL }
};
@ -583,7 +844,7 @@ supports_device(device_node* parent)
if (vendorID == VIRTIO_PCI_VENDORID) {
if (deviceID < VIRTIO_PCI_DEVICEID_MIN
|| deviceID > VIRTIO_PCI_DEVICEID_MAX) {
|| deviceID > VIRTIO_PCI_DEVICEID_MODERN_MAX) {
return 0.0f;
}
@ -591,9 +852,16 @@ supports_device(device_node* parent)
pci_device* device;
gDeviceManager->get_driver(parent, (driver_module_info**)&pci,
(void**)&device);
uint8 pciSubDeviceId = pci->read_pci_config(device, PCI_revision,
uint8 pciRevision = pci->read_pci_config(device, PCI_revision,
1);
if (pciSubDeviceId != VIRTIO_PCI_ABI_VERSION)
if (deviceID >= VIRTIO_PCI_DEVICEID_MIN
&& deviceID <= VIRTIO_PCI_DEVICEID_LEGACY_MAX
&& pciRevision != 0) {
return 0.0f;
}
if (deviceID >= VIRTIO_PCI_DEVICEID_MODERN_MIN
&& deviceID <= VIRTIO_PCI_DEVICEID_MODERN_MAX
&& pciRevision != 1)
return 0.0f;
TRACE("Virtio device found! vendor 0x%04x, device 0x%04x\n", vendorID,

View File

@ -39,10 +39,9 @@
/* VirtIO PCI vendor/device ID. */
#define VIRTIO_PCI_VENDORID 0x1AF4
#define VIRTIO_PCI_DEVICEID_MIN 0x1000
#define VIRTIO_PCI_DEVICEID_MAX 0x103F
/* VirtIO ABI version, this must match exactly. */
#define VIRTIO_PCI_ABI_VERSION 0
#define VIRTIO_PCI_DEVICEID_LEGACY_MAX 0x103F
#define VIRTIO_PCI_DEVICEID_MODERN_MIN 0x1040
#define VIRTIO_PCI_DEVICEID_MODERN_MAX 0x107F
/*
* VirtIO Header, located in BAR 0.
@ -84,4 +83,63 @@
/* The alignment to use between consumer and producer parts of vring. */
#define VIRTIO_PCI_VRING_ALIGN 4096
/*
* Virtio 1.0 specific
*/
struct virtio_pci_cap {
uint8_t cap_vndr; /* Generic PCI field: PCI_CAP_ID_VNDR */
uint8_t cap_next; /* Generic PCI field: next ptr. */
uint8_t cap_len; /* Generic PCI field: capability length */
uint8_t cfg_type; /* Identifies the structure. */
uint8_t bar; /* Where to find it. */
uint8_t padding[3]; /* Pad to full dword. */
uint32_t offset; /* Offset within bar. */
uint32_t length; /* Length of the structure, in bytes. */
} _PACKED;
/* Common configuration */
#define VIRTIO_PCI_CAP_COMMON_CFG 1
/* Notifications */
#define VIRTIO_PCI_CAP_NOTIFY_CFG 2
/* ISR Status */
#define VIRTIO_PCI_CAP_ISR_CFG 3
/* Device specific configuration */
#define VIRTIO_PCI_CAP_DEVICE_CFG 4
/* PCI configuration access */
#define VIRTIO_PCI_CAP_PCI_CFG 5
struct virtio_pci_notify_cap {
struct virtio_pci_cap cap;
uint32_t notify_off_multiplier; /* Multiplier for queue_notify_off. */
} _PACKED;
struct virtio_pci_cfg_cap {
struct virtio_pci_cap cap;
uint8_t pci_cfg_data[4]; /* Data for BAR access. */
} _PACKED;
struct virtio_pci_common_cfg {
/* About the whole device. */
uint32_t device_feature_select; /* read-write */
uint32_t device_feature; /* read-only for driver */
uint32_t driver_feature_select; /* read-write */
uint32_t driver_feature; /* read-write */
uint16_t config_msix_vector; /* read-write */
uint16_t num_queues; /* read-only for driver */
uint8_t device_status; /* read-write */
uint8_t config_generation; /* read-only for driver */
/* About a specific virtqueue. */
uint16_t queue_select; /* read-write */
uint16_t queue_size; /* read-write, power of 2, or 0. */
uint16_t queue_msix_vector; /* read-write */
uint16_t queue_enable; /* read-write */
uint16_t queue_notify_off; /* read-only for driver */
uint64_t queue_desc; /* read-write */
uint64_t queue_avail; /* read-write */
uint64_t queue_used; /* read-write */
} _PACKED;
#endif /* _VIRTIO_PCI_H */

View File

@ -55,7 +55,7 @@ typedef struct {
struct virtio_blk_config config;
uint32 features;
uint64 features;
uint64 capacity;
uint32 block_size;
uint32 physical_block_size;
@ -98,7 +98,7 @@ bool virtio_block_set_capacity(virtio_block_driver_info* info);
const char *
get_feature_name(uint32 feature)
get_feature_name(uint64 feature)
{
switch (feature) {
case VIRTIO_BLK_F_BARRIER:

View File

@ -51,7 +51,7 @@ struct VirtioInputDevice {
virtio_device_interface* virtio;
::virtio_queue virtio_queue;
uint32 features;
uint64 features;
uint32 packetCnt;
int32 freePackets;

View File

@ -57,7 +57,7 @@ typedef struct {
::virtio_device virtio_device;
virtio_device_interface* virtio;
uint32 features;
uint64 features;
uint32 pairsCount;
@ -123,7 +123,7 @@ static void virtio_net_txDone(void* driverCookie, void* cookie);
const char*
get_feature_name(uint32 feature)
get_feature_name(uint64 feature)
{
switch (feature) {
case VIRTIO_NET_F_CSUM:
@ -195,6 +195,7 @@ virtio_net_drain_queues(virtio_net_driver_info* info)
static status_t
virtio_net_rx_enqueue_buf(virtio_net_driver_info* info, BufInfo* buf)
{
CALLED();
physical_entry entries[2];
entries[0] = buf->hdrEntry;
entries[1] = buf->entry;

View File

@ -54,8 +54,10 @@
#define VIRTIO_NET_F_GUEST_ANNOUNCE 0x200000 /* Announce device on network */
#define VIRTIO_NET_F_MQ 0x400000 /* Device supports RFS */
#define VIRTIO_NET_F_CTRL_MAC_ADDR 0x800000 /* Set MAC address */
#define VIRTIO_NET_F_SPEED_DUPLEX (1ULL << 63) /* Device set linkspeed and duplex */
#define VIRTIO_NET_S_LINK_UP 1 /* Link is up */
#define VIRTIO_NET_S_ANNOUNCE 2 /* Announcement is needed */
struct virtio_net_config {
/* The config defining mac address (if VIRTIO_NET_F_MAC) */
@ -68,6 +70,8 @@ struct virtio_net_config {
*/
uint16_t max_virtqueue_pairs;
uint16_t mtu;
uint32_t speed;
uint8_t duplex;
} _PACKED;
/*