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 <[email protected]>
This commit is contained in:
Jérôme Duval
2023-10-15 13:02:41 +00:00
parent 518e9cb9f2
commit 6d42b430d1
19 changed files with 507 additions and 139 deletions
+13 -7
View File
@@ -26,13 +26,14 @@
#define VIRTIO_DEVICE_ID_VSOCK 19 #define VIRTIO_DEVICE_ID_VSOCK 19
#define VIRTIO_DEVICE_ID_CRYPTO 20 #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_NOTIFY_ON_EMPTY (1 << 24)
#define VIRTIO_FEATURE_ANY_LAYOUT (1 << 27) #define VIRTIO_FEATURE_ANY_LAYOUT (1 << 27)
#define VIRTIO_FEATURE_RING_INDIRECT_DESC (1 << 28) #define VIRTIO_FEATURE_RING_INDIRECT_DESC (1 << 28)
#define VIRTIO_FEATURE_RING_EVENT_IDX (1 << 29) #define VIRTIO_FEATURE_RING_EVENT_IDX (1 << 29)
#define VIRTIO_FEATURE_BAD_FEATURE (1 << 30) #define VIRTIO_FEATURE_BAD_FEATURE (1 << 30)
#define VIRTIO_FEATURE_VERSION_1 (1ULL << 32)
#define VIRTIO_VIRTQUEUES_MAX_COUNT 8 #define VIRTIO_VIRTQUEUES_MAX_COUNT 8
@@ -40,6 +41,8 @@
#define VIRTIO_CONFIG_STATUS_ACK 0x01 #define VIRTIO_CONFIG_STATUS_ACK 0x01
#define VIRTIO_CONFIG_STATUS_DRIVER 0x02 #define VIRTIO_CONFIG_STATUS_DRIVER 0x02
#define VIRTIO_CONFIG_STATUS_DRIVER_OK 0x04 #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 #define VIRTIO_CONFIG_STATUS_FAILED 0x80
// attributes: // attributes:
@@ -50,6 +53,8 @@
#define VIRTIO_DEVICE_TYPE_ITEM "virtio/type" #define VIRTIO_DEVICE_TYPE_ITEM "virtio/type"
// alignment (uint16) // alignment (uint16)
#define VIRTIO_VRING_ALIGNMENT_ITEM "virtio/vring_alignment" #define VIRTIO_VRING_ALIGNMENT_ITEM "virtio/vring_alignment"
// version (uint8)
#define VIRTIO_VERSION_ITEM "virtio/version"
// sim cookie, issued by virtio bus manager // sim cookie, issued by virtio bus manager
typedef void* virtio_sim; typedef void* virtio_sim;
@@ -78,8 +83,8 @@ typedef struct {
driver_module_info info; driver_module_info info;
void (*set_sim)(void* cookie, virtio_sim sim); void (*set_sim)(void* cookie, virtio_sim sim);
status_t (*read_host_features)(void* cookie, uint32* features); status_t (*read_host_features)(void* cookie, uint64* features);
status_t (*write_guest_features)(void* cookie, uint32 features); status_t (*write_guest_features)(void* cookie, uint64 features);
uint8 (*get_status)(void* cookie); uint8 (*get_status)(void* cookie);
void (*set_status)(void* cookie, uint8 status); void (*set_status)(void* cookie, uint8 status);
status_t (*read_device_config)(void* cookie, uint8 offset, void* buffer, status_t (*read_device_config)(void* cookie, uint8 offset, void* buffer,
@@ -88,7 +93,8 @@ typedef struct {
const void* buffer, size_t bufferSize); const void* buffer, size_t bufferSize);
uint16 (*get_queue_ring_size)(void* cookie, uint16 queue); 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 (*setup_interrupt)(void* cookie, uint16 queueCount);
status_t (*free_interrupt)(void* cookie); status_t (*free_interrupt)(void* cookie);
void (*notify_queue)(void* cookie, uint16 queue); void (*notify_queue)(void* cookie, uint16 queue);
@@ -99,10 +105,10 @@ typedef struct {
typedef struct { typedef struct {
driver_module_info info; driver_module_info info;
status_t (*negotiate_features)(virtio_device cookie, uint32 supported, status_t (*negotiate_features)(virtio_device cookie, uint64 supported,
uint32* negotiated, const char* (*get_feature_name)(uint32)); 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, status_t (*read_device_config)(virtio_device cookie, uint8 offset,
void* buffer, size_t bufferSize); void* buffer, size_t bufferSize);
@@ -17,7 +17,7 @@
const char* const char*
get_feature_name(uint32 feature) get_feature_name(uint64 feature)
{ {
switch (feature) { switch (feature) {
case VIRTIO_BALLOON_F_MUST_TELL_HOST: case VIRTIO_BALLOON_F_MUST_TELL_HOST:
@@ -61,7 +61,7 @@ private:
virtio_device* fVirtioDevice; virtio_device* fVirtioDevice;
status_t fStatus; status_t fStatus;
uint32 fFeatures; uint64 fFeatures;
::virtio_queue fVirtioQueues[2]; ::virtio_queue fVirtioQueues[2];
physical_entry fEntry; physical_entry fEntry;
@@ -8,7 +8,7 @@
const char * const char *
virtio_get_feature_name(uint32 feature) virtio_get_feature_name(uint64 feature)
{ {
switch (feature) { switch (feature) {
case VIRTIO_FEATURE_NOTIFY_ON_EMPTY: case VIRTIO_FEATURE_NOTIFY_ON_EMPTY:
@@ -62,6 +62,7 @@ VirtioDevice::VirtioDevice(device_node *node)
fQueues(NULL), fQueues(NULL),
fFeatures(0), fFeatures(0),
fAlignment(0), fAlignment(0),
fVirtio1(false),
fConfigHandler(NULL), fConfigHandler(NULL),
fDriverCookie(NULL) fDriverCookie(NULL)
{ {
@@ -80,6 +81,9 @@ VirtioDevice::VirtioDevice(device_node *node)
ERROR("alignment missing\n"); ERROR("alignment missing\n");
return; 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); fController->set_sim(fCookie, this);
@@ -104,8 +108,8 @@ VirtioDevice::InitCheck()
status_t status_t
VirtioDevice::NegotiateFeatures(uint32 supported, uint32* negotiated, VirtioDevice::NegotiateFeatures(uint64 supported, uint64* negotiated,
const char* (*get_feature_name)(uint32)) const char* (*get_feature_name)(uint64))
{ {
fFeatures = 0; fFeatures = 0;
status_t status = fController->read_host_features(fCookie, &fFeatures); 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); _DumpFeatures("read features", fFeatures, get_feature_name);
if (fVirtio1) {
supported |= VIRTIO_FEATURE_VERSION_1;
supported &= ~VIRTIO_FEATURE_NOTIFY_ON_EMPTY;
}
fFeatures &= supported; fFeatures &= supported;
// filter our own features // filter our own features
fFeatures &= (VIRTIO_FEATURE_TRANSPORT_MASK fFeatures &= (VIRTIO_FEATURE_TRANSPORT_MASK
| VIRTIO_FEATURE_RING_INDIRECT_DESC | VIRTIO_FEATURE_RING_EVENT_IDX); | VIRTIO_FEATURE_RING_INDIRECT_DESC | VIRTIO_FEATURE_RING_EVENT_IDX
| VIRTIO_FEATURE_VERSION_1);
*negotiated = fFeatures;
_DumpFeatures("negotiated features", fFeatures, get_feature_name); _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 status_t
VirtioDevice::ClearFeature(uint32 feature) VirtioDevice::ClearFeature(uint64 feature)
{ {
fFeatures &= ~feature; fFeatures &= ~feature;
return fController->write_guest_features(fCookie, fFeatures); return fController->write_guest_features(fCookie, fFeatures);
@@ -224,9 +249,10 @@ VirtioDevice::FreeInterrupts()
status_t 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 void
VirtioDevice::_DumpFeatures(const char* title, uint32 features, VirtioDevice::_DumpFeatures(const char* title, uint64 features,
const char* (*get_feature_name)(uint32)) const char* (*get_feature_name)(uint64))
{ {
char features_string[512] = ""; char features_string[512] = "";
for (uint32 i = 0; i < 32; i++) { for (uint32 i = 0; i < 64; i++) {
uint32 feature = features & (1 << i); uint64 feature = features & (1ULL << i);
if (feature == 0) if (feature == 0)
continue; continue;
const char* name = virtio_get_feature_name(feature); const char* name = virtio_get_feature_name(feature);
@@ -54,8 +54,8 @@ virtio_device_removed(void *_device)
status_t status_t
virtio_negotiate_features(void* _device, uint32 supported, virtio_negotiate_features(void* _device, uint64 supported,
uint32* negotiated, const char* (*get_feature_name)(uint32)) uint64* negotiated, const char* (*get_feature_name)(uint64))
{ {
CALLED(); CALLED();
VirtioDevice *device = (VirtioDevice *)_device; VirtioDevice *device = (VirtioDevice *)_device;
@@ -65,7 +65,7 @@ virtio_negotiate_features(void* _device, uint32 supported,
status_t status_t
virtio_clear_feature(void* _device, uint32 feature) virtio_clear_feature(void* _device, uint64 feature)
{ {
CALLED(); CALLED();
VirtioDevice *device = (VirtioDevice *)_device; VirtioDevice *device = (VirtioDevice *)_device;
@@ -45,10 +45,10 @@ public:
status_t InitCheck(); status_t InitCheck();
uint32 ID() const { return fID; } uint32 ID() const { return fID; }
status_t NegotiateFeatures(uint32 supported, status_t NegotiateFeatures(uint64 supported,
uint32* negotiated, uint64* negotiated,
const char* (*get_feature_name)(uint32)); const char* (*get_feature_name)(uint64));
status_t ClearFeature(uint32 feature); status_t ClearFeature(uint64 feature);
status_t ReadDeviceConfig(uint8 offset, void* buffer, status_t ReadDeviceConfig(uint8 offset, void* buffer,
size_t bufferSize); size_t bufferSize);
@@ -63,12 +63,13 @@ public:
status_t FreeInterrupts(); status_t FreeInterrupts();
uint16 Alignment() const { return fAlignment; } uint16 Alignment() const { return fAlignment; }
uint32 Features() const { return fFeatures; } uint64 Features() const { return fFeatures; }
void* DriverCookie() { return fDriverCookie; } void* DriverCookie() { return fDriverCookie; }
status_t SetupQueue(uint16 queueNumber, 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); void NotifyQueue(uint16 queueNumber);
status_t QueueInterrupt(uint16 queueNumber); status_t QueueInterrupt(uint16 queueNumber);
@@ -76,8 +77,8 @@ public:
private: private:
void _DumpFeatures(const char* title, void _DumpFeatures(const char* title,
uint32 features, uint64 features,
const char* (*get_feature_name)(uint32)); const char* (*get_feature_name)(uint64));
void _DestroyQueues(size_t count); void _DestroyQueues(size_t count);
@@ -89,8 +90,9 @@ private:
status_t fStatus; status_t fStatus;
VirtioQueue** fQueues; VirtioQueue** fQueues;
size_t fQueueCount; size_t fQueueCount;
uint32 fFeatures; uint64 fFeatures;
uint16 fAlignment; uint16 fAlignment;
bool fVirtio1;
virtio_intr_func fConfigHandler; virtio_intr_func fConfigHandler;
void* fDriverCookie; void* fDriverCookie;
@@ -182,7 +182,9 @@ VirtioQueue::VirtioQueue(VirtioDevice* device, uint16 queueNumber,
DisableInterrupt(); 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));
} }
@@ -14,7 +14,7 @@
const char * const char *
get_feature_name(uint32 feature) get_feature_name(uint64 feature)
{ {
switch (feature) { switch (feature) {
} }
@@ -49,7 +49,7 @@ private:
virtio_device* fVirtioDevice; virtio_device* fVirtioDevice;
status_t fStatus; status_t fStatus;
uint32 fFeatures; uint64 fFeatures;
::virtio_queue fVirtioQueue; ::virtio_queue fVirtioQueue;
spinlock fInterruptLock; spinlock fInterruptLock;
@@ -16,7 +16,7 @@
const char * const char *
get_feature_name(uint32 feature) get_feature_name(uint64 feature)
{ {
switch (feature) { switch (feature) {
case VIRTIO_SCSI_F_INOUT: case VIRTIO_SCSI_F_INOUT:
@@ -81,7 +81,7 @@ private:
status_t fStatus; status_t fStatus;
struct virtio_scsi_config fConfig; struct virtio_scsi_config fConfig;
uint32 fFeatures; uint64 fFeatures;
::virtio_queue fControlVirtioQueue; ::virtio_queue fControlVirtioQueue;
::virtio_queue fEventVirtioQueue; ::virtio_queue fEventVirtioQueue;
::virtio_queue fRequestVirtioQueue; ::virtio_queue fRequestVirtioQueue;
@@ -33,7 +33,7 @@ device_manager_info* gDeviceManager;
static const char * static const char *
virtio_get_feature_name(uint32 feature) virtio_get_feature_name(uint64 feature)
{ {
switch (feature) { switch (feature) {
case VIRTIO_FEATURE_NOTIFY_ON_EMPTY: case VIRTIO_FEATURE_NOTIFY_ON_EMPTY:
@@ -52,12 +52,12 @@ virtio_get_feature_name(uint32 feature)
static void static void
virtio_dump_features(const char* title, uint32 features, virtio_dump_features(const char* title, uint64 features,
const char* (*get_feature_name)(uint32)) const char* (*get_feature_name)(uint64))
{ {
char features_string[512] = ""; char features_string[512] = "";
for (uint32 i = 0; i < 32; i++) { for (uint64 i = 0; i < 32; i++) {
uint32 feature = features & (1 << i); uint64 feature = features & (1 << i);
if (feature == 0) if (feature == 0)
continue; continue;
const char* name = virtio_get_feature_name(feature); const char* name = virtio_get_feature_name(feature);
@@ -365,8 +365,8 @@ virtio_device_register_child_devices(void* cookie)
static status_t static status_t
virtio_device_negotiate_features(virtio_device cookie, uint32 supported, virtio_device_negotiate_features(virtio_device cookie, uint64 supported,
uint32* negotiated, const char* (*get_feature_name)(uint32)) uint64* negotiated, const char* (*get_feature_name)(uint64))
{ {
TRACE("virtio_device_negotiate_features(%p)\n", cookie); TRACE("virtio_device_negotiate_features(%p)\n", cookie);
VirtioDevice* dev = (VirtioDevice*)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 |= kVirtioConfigSAcknowledge;
dev->fRegs->status |= kVirtioConfigSDriver; dev->fRegs->status |= kVirtioConfigSDriver;
uint32 features = dev->fRegs->deviceFeatures; uint64 features = dev->fRegs->deviceFeatures;
virtio_dump_features("read features", features, get_feature_name); virtio_dump_features("read features", features, get_feature_name);
features &= supported; features &= supported;
@@ -395,7 +395,7 @@ virtio_device_negotiate_features(virtio_device cookie, uint32 supported,
static status_t static status_t
virtio_device_clear_feature(virtio_device cookie, uint32 feature) virtio_device_clear_feature(virtio_device cookie, uint64 feature)
{ {
panic("not implemented"); panic("not implemented");
return B_ERROR; return B_ERROR;
@@ -2,7 +2,8 @@ SubDir HAIKU_TOP src add-ons kernel busses virtio virtio_pci ;
SubDirC++Flags -fno-rtti ; SubDirC++Flags -fno-rtti ;
UsePrivateHeaders kernel virtio ; UsePrivateHeaders virtio ;
UsePrivateKernelHeaders ;
KernelAddon virtio_pci : KernelAddon virtio_pci :
virtio_pci.cpp virtio_pci.cpp
@@ -9,6 +9,9 @@
#include <string.h> #include <string.h>
#include <bus/PCI.h> #include <bus/PCI.h>
#include <SupportDefs.h>
#include <kernel.h>
#include <virtio.h> #include <virtio.h>
#include "virtio_pci.h" #include "virtio_pci.h"
@@ -45,11 +48,18 @@ typedef struct {
typedef struct { typedef struct {
pci_device_module_info* pci; pci_device_module_info* pci;
pci_device* device; pci_device* device;
bool virtio1;
addr_t base_addr; addr_t base_addr;
area_id registersArea[4];
addr_t commonCfgAddr;
addr_t isrAddr;
addr_t notifyAddr;
uint32 notifyOffsetMultiplier;
uint8 irq; uint8 irq;
virtio_irq_type irq_type; virtio_irq_type irq_type;
virtio_sim sim; virtio_sim sim;
uint16 queue_count; uint16 queue_count;
addr_t* notifyOffsets;
device_node* node; device_node* node;
pci_info info; pci_info info;
@@ -62,12 +72,53 @@ device_manager_info* gDeviceManager;
virtio_for_controller_interface* gVirtio; 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_interrupt(void *data)
{ {
virtio_pci_sim_info* bus = (virtio_pci_sim_info*)data; virtio_pci_sim_info* bus = (virtio_pci_sim_info*)data;
uint8 isr = bus->pci->read_io_8(bus->device, uint8 isr;
bus->base_addr + VIRTIO_PCI_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) if (isr == 0)
return B_UNHANDLED_INTERRUPT; return B_UNHANDLED_INTERRUPT;
@@ -81,7 +132,7 @@ virtio_pci_interrupt(void *data)
} }
int32 static int32
virtio_pci_config_interrupt(void *data) virtio_pci_config_interrupt(void *data)
{ {
virtio_pci_sim_info* bus = (virtio_pci_sim_info*)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_interrupt(void *data)
{ {
virtio_pci_queue_cookie* cookie = (virtio_pci_queue_cookie*)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) virtio_pci_setup_msix_interrupts(virtio_pci_sim_info* bus)
{ {
CALLED(); CALLED();
uint8 irq = 0; // first irq slot uint8 irq = 0; // first irq slot
bus->pci->write_io_16(bus->device, bus->base_addr if (bus->virtio1) {
+ VIRTIO_MSI_CONFIG_VECTOR, irq); volatile uint16 *msixVector = (uint16*)(bus->commonCfgAddr
if (bus->pci->read_io_16(bus->device, bus->base_addr + offsetof(struct virtio_pci_common_cfg, config_msix_vector));
+ VIRTIO_MSI_CONFIG_VECTOR) == VIRTIO_MSI_NO_VECTOR) { *msixVector = irq;
ERROR("msix config vector incorrect\n"); } else {
return B_BAD_VALUE; 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) if (bus->irq_type == VIRTIO_IRQ_MSI_X)
irq++; irq++;
for (uint16 queue = 0; queue < bus->queue_count; queue++) { for (uint16 queue = 0; queue < bus->queue_count; queue++) {
bus->pci->write_io_16(bus->device, bus->base_addr if (bus->virtio1) {
+ VIRTIO_PCI_QUEUE_SEL, queue); volatile uint16* queueSelect = (uint16*)(bus->commonCfgAddr
bus->pci->write_io_16(bus->device, bus->base_addr + offsetof(struct virtio_pci_common_cfg, queue_select));
+ VIRTIO_MSI_QUEUE_VECTOR, irq); *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 if (bus->pci->read_io_16(bus->device, bus->base_addr
+ VIRTIO_MSI_QUEUE_VECTOR) == VIRTIO_MSI_NO_VECTOR) { + VIRTIO_MSI_QUEUE_VECTOR) == VIRTIO_MSI_NO_VECTOR) {
ERROR("msix queue vector incorrect\n"); ERROR("msix queue vector incorrect\n");
return B_BAD_VALUE; return B_BAD_VALUE;
}
} }
if (bus->irq_type == VIRTIO_IRQ_MSI_X) if (bus->irq_type == VIRTIO_IRQ_MSI_X)
irq++; irq++;
} }
@@ -146,7 +211,7 @@ set_sim(void* cookie, virtio_sim sim)
static status_t static status_t
read_host_features(void* cookie, uint32 *features) read_host_features(void* cookie, uint64 *features)
{ {
CALLED(); CALLED();
virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie; 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, TRACE("read_host_features() %p node %p pci %p device %p\n", bus,
bus->node, bus->pci, bus->device); bus->node, bus->pci, bus->device);
*features = bus->pci->read_io_32(bus->device, if (bus->virtio1) {
bus->base_addr + VIRTIO_PCI_HOST_FEATURES); 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; return B_OK;
} }
static status_t static status_t
write_guest_features(void* cookie, uint32 features) write_guest_features(void* cookie, uint64 features)
{ {
CALLED(); CALLED();
virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie; virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie;
bus->pci->write_io_32(bus->device, bus->base_addr if (bus->virtio1) {
+ VIRTIO_PCI_GUEST_FEATURES, features); 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; return B_OK;
} }
uint8 static uint8
get_status(void* cookie) get_status(void* cookie)
{ {
CALLED(); CALLED();
virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie; virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie;
return bus->pci->read_io_8(bus->device, bus->base_addr if (bus->virtio1) {
+ VIRTIO_PCI_STATUS); 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) set_status(void* cookie, uint8 status)
{ {
CALLED(); CALLED();
virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie; virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie;
bus->pci->write_io_8(bus->device, bus->base_addr + VIRTIO_PCI_STATUS, if (bus->virtio1) {
status); 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, read_device_config(void* cookie, uint8 _offset, void* _buffer,
size_t bufferSize) size_t bufferSize)
{ {
CALLED(); CALLED();
virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie; 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; uint8* buffer = (uint8*)_buffer;
while (bufferSize > 0) { while (bufferSize > 0) {
uint8 size = 4; uint8 size = 4;
if (bufferSize == 1) { if (bufferSize == 1) {
size = 1; size = 1;
*buffer = bus->pci->read_io_8(bus->device, if (bus->virtio1) {
offset); *buffer = *(uint8*)offset;
} else {
*buffer = bus->pci->read_io_8(bus->device, offset);
}
} else if (bufferSize <= 3) { } else if (bufferSize <= 3) {
size = 2; size = 2;
*(uint16*)buffer = bus->pci->read_io_16(bus->device, if (bus->virtio1) {
offset); *(uint16*)buffer = *(uint16*)offset;
} else {
*(uint16*)buffer = bus->pci->read_io_16(bus->device, offset);
}
} else { } else {
*(uint32*)buffer = bus->pci->read_io_32(bus->device, if (bus->virtio1) {
offset); *(uint32*)buffer = *(uint32*)offset;
} else {
*(uint32*)buffer = bus->pci->read_io_32(bus->device,
offset);
}
} }
buffer += size; buffer += size;
bufferSize -= 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, write_device_config(void* cookie, uint8 _offset, const void* _buffer,
size_t bufferSize) size_t bufferSize)
{ {
CALLED(); CALLED();
virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie; 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; const uint8* buffer = (const uint8*)_buffer;
while (bufferSize > 0) { while (bufferSize > 0) {
uint8 size = 4; uint8 size = 4;
if (bufferSize == 1) { if (bufferSize == 1) {
size = 1; size = 1;
bus->pci->write_io_8(bus->device, if (bus->virtio1) {
offset, *buffer); *(uint8*)offset = *buffer;
} else {
bus->pci->write_io_8(bus->device, offset, *buffer);
}
} else if (bufferSize <= 3) { } else if (bufferSize <= 3) {
size = 2; size = 2;
bus->pci->write_io_16(bus->device, if (bus->virtio1) {
offset, *(const uint16*)buffer); *(uint16*)offset = *(uint16*)buffer;
} else {
bus->pci->write_io_16(bus->device, offset, *(const uint16*)buffer);
}
} else { } else {
bus->pci->write_io_32(bus->device, if (bus->virtio1) {
offset, *(const uint32*)buffer); *(uint32*)offset = *(uint32*)buffer;
} else {
bus->pci->write_io_32(bus->device, offset, *(const uint32*)buffer);
}
} }
buffer += size; buffer += size;
bufferSize -= 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) get_queue_ring_size(void* cookie, uint16 queue)
{ {
CALLED(); CALLED();
virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie; virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie;
bus->pci->write_io_16(bus->device, bus->base_addr + VIRTIO_PCI_QUEUE_SEL, if (bus->virtio1) {
queue); volatile uint16* queueSelect = (uint16*)(bus->commonCfgAddr
return bus->pci->read_io_16(bus->device, bus->base_addr + offsetof(struct virtio_pci_common_cfg, queue_select));
+ VIRTIO_PCI_QUEUE_NUM); *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 static status_t
setup_queue(void* cookie, uint16 queue, phys_addr_t phy) setup_queue(void* cookie, uint16 queue, phys_addr_t phy, phys_addr_t phyAvail,
phys_addr_t phyUsed)
{ {
CALLED(); CALLED();
virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie; virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie;
bus->pci->write_io_16(bus->device, bus->base_addr + VIRTIO_PCI_QUEUE_SEL, if (queue >= bus->queue_count)
queue); return B_BAD_VALUE;
bus->pci->write_io_32(bus->device, bus->base_addr + VIRTIO_PCI_QUEUE_PFN,
(uint32)phy >> VIRTIO_PCI_QUEUE_ADDR_SHIFT); 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; return B_OK;
} }
status_t static status_t
setup_interrupt(void* cookie, uint16 queueCount) setup_interrupt(void* cookie, uint16 queueCount)
{ {
CALLED(); CALLED();
@@ -308,7 +470,7 @@ setup_interrupt(void* cookie, uint16 queueCount)
} }
} else { } else {
uint8 vector; 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) { && bus->pci->enable_msix(bus->device) == B_OK) {
TRACE_ALWAYS("using MSI-X vector shared %u\n", 1); TRACE_ALWAYS("using MSI-X vector shared %u\n", 1);
bus->irq = vector; bus->irq = vector;
@@ -375,7 +537,7 @@ setup_interrupt(void* cookie, uint16 queueCount)
} }
status_t static status_t
free_interrupt(void* cookie) free_interrupt(void* cookie)
{ {
CALLED(); CALLED();
@@ -402,13 +564,20 @@ free_interrupt(void* cookie)
} }
void static void
notify_queue(void* cookie, uint16 queue) notify_queue(void* cookie, uint16 queue)
{ {
CALLED(); CALLED();
virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie; virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie;
bus->pci->write_io_16(bus->device, bus->base_addr if (queue >= bus->queue_count)
+ VIRTIO_PCI_QUEUE_NOTIFY, queue); 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_info *pciInfo = &bus->info;
pci->get_pci_info(device, pciInfo); pci->get_pci_info(device, pciInfo);
// legacy interrupt bus->virtio1 = pciInfo->revision == 1;
bus->base_addr = pciInfo->u.h0.base_registers[0];
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_RESET);
set_status(bus, VIRTIO_CONFIG_STATUS_ACK); set_status(bus, VIRTIO_CONFIG_STATUS_ACK);
@@ -488,6 +732,16 @@ uninit_bus(void* bus_cookie)
} else } else
remove_io_interrupt_handler(bus->irq, virtio_pci_interrupt, bus); 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->cookies;
delete bus; delete bus;
} }
@@ -514,11 +768,16 @@ register_child_devices(void* cookie)
gDeviceManager->get_driver(parent, (driver_module_info**)&pci, gDeviceManager->get_driver(parent, (driver_module_info**)&pci,
(void**)&device); (void**)&device);
uint16 pciSubDeviceId = pci->read_pci_config(device, PCI_subsystem_id, uint16 pciSubDeviceId = pci->read_pci_config(device, PCI_subsystem_id, 2);
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]; char prettyName[25];
sprintf(prettyName, "Virtio Device %" B_PRIu16, pciSubDeviceId); sprintf(prettyName, "Virtio Device %" B_PRIu16, virtioDeviceId);
device_attr attrs[] = { device_attr attrs[] = {
// properties of this controller for virtio bus manager // properties of this controller for virtio bus manager
@@ -529,9 +788,11 @@ register_child_devices(void* cookie)
// private data to identify the device // private data to identify the device
{ VIRTIO_DEVICE_TYPE_ITEM, B_UINT16_TYPE, { VIRTIO_DEVICE_TYPE_ITEM, B_UINT16_TYPE,
{ .ui16 = pciSubDeviceId }}, { .ui16 = virtioDeviceId }},
{ VIRTIO_VRING_ALIGNMENT_ITEM, B_UINT16_TYPE, { VIRTIO_VRING_ALIGNMENT_ITEM, B_UINT16_TYPE,
{ .ui16 = VIRTIO_PCI_VRING_ALIGN }}, { .ui16 = VIRTIO_PCI_VRING_ALIGN }},
{ VIRTIO_VERSION_ITEM, B_UINT8_TYPE,
{ .ui8 = pciRevision }},
{ NULL } { NULL }
}; };
@@ -583,7 +844,7 @@ supports_device(device_node* parent)
if (vendorID == VIRTIO_PCI_VENDORID) { if (vendorID == VIRTIO_PCI_VENDORID) {
if (deviceID < VIRTIO_PCI_DEVICEID_MIN if (deviceID < VIRTIO_PCI_DEVICEID_MIN
|| deviceID > VIRTIO_PCI_DEVICEID_MAX) { || deviceID > VIRTIO_PCI_DEVICEID_MODERN_MAX) {
return 0.0f; return 0.0f;
} }
@@ -591,9 +852,16 @@ supports_device(device_node* parent)
pci_device* device; pci_device* device;
gDeviceManager->get_driver(parent, (driver_module_info**)&pci, gDeviceManager->get_driver(parent, (driver_module_info**)&pci,
(void**)&device); (void**)&device);
uint8 pciSubDeviceId = pci->read_pci_config(device, PCI_revision, uint8 pciRevision = pci->read_pci_config(device, PCI_revision,
1); 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; return 0.0f;
TRACE("Virtio device found! vendor 0x%04x, device 0x%04x\n", vendorID, TRACE("Virtio device found! vendor 0x%04x, device 0x%04x\n", vendorID,
@@ -39,10 +39,9 @@
/* VirtIO PCI vendor/device ID. */ /* VirtIO PCI vendor/device ID. */
#define VIRTIO_PCI_VENDORID 0x1AF4 #define VIRTIO_PCI_VENDORID 0x1AF4
#define VIRTIO_PCI_DEVICEID_MIN 0x1000 #define VIRTIO_PCI_DEVICEID_MIN 0x1000
#define VIRTIO_PCI_DEVICEID_MAX 0x103F #define VIRTIO_PCI_DEVICEID_LEGACY_MAX 0x103F
#define VIRTIO_PCI_DEVICEID_MODERN_MIN 0x1040
/* VirtIO ABI version, this must match exactly. */ #define VIRTIO_PCI_DEVICEID_MODERN_MAX 0x107F
#define VIRTIO_PCI_ABI_VERSION 0
/* /*
* VirtIO Header, located in BAR 0. * VirtIO Header, located in BAR 0.
@@ -84,4 +83,63 @@
/* The alignment to use between consumer and producer parts of vring. */ /* The alignment to use between consumer and producer parts of vring. */
#define VIRTIO_PCI_VRING_ALIGN 4096 #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 */ #endif /* _VIRTIO_PCI_H */
@@ -55,7 +55,7 @@ typedef struct {
struct virtio_blk_config config; struct virtio_blk_config config;
uint32 features; uint64 features;
uint64 capacity; uint64 capacity;
uint32 block_size; uint32 block_size;
uint32 physical_block_size; uint32 physical_block_size;
@@ -98,7 +98,7 @@ bool virtio_block_set_capacity(virtio_block_driver_info* info);
const char * const char *
get_feature_name(uint32 feature) get_feature_name(uint64 feature)
{ {
switch (feature) { switch (feature) {
case VIRTIO_BLK_F_BARRIER: case VIRTIO_BLK_F_BARRIER:
@@ -51,7 +51,7 @@ struct VirtioInputDevice {
virtio_device_interface* virtio; virtio_device_interface* virtio;
::virtio_queue virtio_queue; ::virtio_queue virtio_queue;
uint32 features; uint64 features;
uint32 packetCnt; uint32 packetCnt;
int32 freePackets; int32 freePackets;
@@ -57,7 +57,7 @@ typedef struct {
::virtio_device virtio_device; ::virtio_device virtio_device;
virtio_device_interface* virtio; virtio_device_interface* virtio;
uint32 features; uint64 features;
uint32 pairsCount; uint32 pairsCount;
@@ -123,7 +123,7 @@ static void virtio_net_txDone(void* driverCookie, void* cookie);
const char* const char*
get_feature_name(uint32 feature) get_feature_name(uint64 feature)
{ {
switch (feature) { switch (feature) {
case VIRTIO_NET_F_CSUM: case VIRTIO_NET_F_CSUM:
@@ -195,6 +195,7 @@ virtio_net_drain_queues(virtio_net_driver_info* info)
static status_t static status_t
virtio_net_rx_enqueue_buf(virtio_net_driver_info* info, BufInfo* buf) virtio_net_rx_enqueue_buf(virtio_net_driver_info* info, BufInfo* buf)
{ {
CALLED();
physical_entry entries[2]; physical_entry entries[2];
entries[0] = buf->hdrEntry; entries[0] = buf->hdrEntry;
entries[1] = buf->entry; entries[1] = buf->entry;
@@ -54,8 +54,10 @@
#define VIRTIO_NET_F_GUEST_ANNOUNCE 0x200000 /* Announce device on network */ #define VIRTIO_NET_F_GUEST_ANNOUNCE 0x200000 /* Announce device on network */
#define VIRTIO_NET_F_MQ 0x400000 /* Device supports RFS */ #define VIRTIO_NET_F_MQ 0x400000 /* Device supports RFS */
#define VIRTIO_NET_F_CTRL_MAC_ADDR 0x800000 /* Set MAC address */ #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_LINK_UP 1 /* Link is up */
#define VIRTIO_NET_S_ANNOUNCE 2 /* Announcement is needed */
struct virtio_net_config { struct virtio_net_config {
/* The config defining mac address (if VIRTIO_NET_F_MAC) */ /* 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 max_virtqueue_pairs;
uint16_t mtu; uint16_t mtu;
uint32_t speed;
uint8_t duplex;
} _PACKED; } _PACKED;
/* /*