hrev45714 adds 3 changesets to branch 'master' old head: a5862816b1b28f8cf94e601057f8e4fb21969f17 new head: 78b461d6a11f062c7a61246f64d1e980612c8237 overview: http://cgit.haiku-os.org/haiku/log/?qt=range&q=78b461d+%5Ea586281 ---------------------------------------------------------------------------- 3aae21a: virtio: integrate into the build and image * device_manager: scans busses/virtio for network device types and scsi controllers. 6bbf9c9: Virtio: added drivers for PCI busses, bus manager and block device. * the Virtio PCI bus driver exposes a Virtio controller to the Virtio bus manager, which in turn exposes a Virtio device consumed by Virtio drivers. Drivers follow the new driver model. * virtio_block handles Virtio block devices under disk/virtual/virtio_block/x/raw. * Here is the Qemu command line option for Virtio disk devices: -drive file=haiku.image,if=virtio * the PCI bus driver currently supports only legacy interrupts (no MSI(-X) yet). * There is room for improvements in the bus manager: - it notifies the host for each queued request, which isn't optimal. - transfer descriptors should probably be simply preallocated (they are nicely leaked at the moment). - indirect descriptors are not supported yet. and in the block driver: - get the id of the disk. - implements flushing the cache. - improves dma restrictions. - do_io() should use a page for header descriptors instead of malloc(), which could cross boundaries. * The device manager tries to guess the driver based on the PCI device type, this implies having to declare the "busses/virtio" path for each possible type provided by Virtio. Thus future driver additions might require patching the device manager. * virtio.h is still private, the API is subject to changes. * virtio_pci.h, virtio_blk.h, virtio_ring.h are copied unchanged from FreeBSD. 78b461d: Merge branch 'virtio' [ Jérôme Duval <jerome.duval@xxxxxxxxx> ] ---------------------------------------------------------------------------- 18 files changed, 2623 insertions(+) build/jam/HaikuImage | 5 + headers/private/virtio/virtio.h | 120 ++++ src/add-ons/kernel/bus_managers/Jamfile | 1 + src/add-ons/kernel/bus_managers/virtio/Jamfile | 10 + .../kernel/bus_managers/virtio/VirtioDevice.cpp | 255 +++++++ .../kernel/bus_managers/virtio/VirtioModule.cpp | 258 ++++++++ .../kernel/bus_managers/virtio/VirtioPrivate.h | 146 ++++ .../kernel/bus_managers/virtio/VirtioQueue.cpp | 288 ++++++++ .../kernel/bus_managers/virtio/virtio_ring.h | 165 +++++ src/add-ons/kernel/busses/Jamfile | 1 + src/add-ons/kernel/busses/virtio/Jamfile | 16 + src/add-ons/kernel/busses/virtio/virtio_pci.cpp | 480 ++++++++++++++ src/add-ons/kernel/busses/virtio/virtio_pci.h | 87 +++ src/add-ons/kernel/drivers/disk/virtual/Jamfile | 1 + .../drivers/disk/virtual/virtio_block/Jamfile | 9 + .../disk/virtual/virtio_block/virtio_blk.h | 117 ++++ .../disk/virtual/virtio_block/virtio_block.cpp | 662 +++++++++++++++++++ .../kernel/device_manager/device_manager.cpp | 2 + ############################################################################ Commit: 3aae21ab63efe37cb6c8c94bf011fc3d36b58728 URL: http://cgit.haiku-os.org/haiku/commit/?id=3aae21a Author: Jérôme Duval <jerome.duval@xxxxxxxxx> Date: Mon May 20 11:14:07 2013 UTC virtio: integrate into the build and image * device_manager: scans busses/virtio for network device types and scsi controllers. ---------------------------------------------------------------------------- diff --git a/build/jam/HaikuImage b/build/jam/HaikuImage index 4a7e1f0..3f7f4fc 100644 --- a/build/jam/HaikuImage +++ b/build/jam/HaikuImage @@ -188,6 +188,7 @@ SYSTEM_ADD_ONS_DRIVERS_POWER = [ FFilterByBuildFeatures acpi_button@x86 ] ; SYSTEM_ADD_ONS_BUS_MANAGERS = [ FFilterByBuildFeatures ata@ata pci ps2@x86,x86_64 isa@x86,x86_64 ide@ide scsi config_manager agp_gart@x86 usb firewire@x86 acpi@x86 + virtio ] ; SYSTEM_ADD_ONS_FILE_SYSTEMS = bfs btrfs cdda exfat ext2 fat iso9660 nfs nfs4 attribute_overlay write_overlay ntfs reiserfs@x86 udf googlefs ; @@ -229,6 +230,8 @@ AddFilesToHaikuImage system add-ons kernel busses scsi : ahci ; AddFilesToHaikuImage system add-ons kernel busses usb : <usb>uhci <usb>ohci <usb>ehci ; +AddFilesToHaikuImage system add-ons kernel busses virtio + : virtio_pci@x86 ; AddFilesToHaikuImage system add-ons kernel console : vga_text ; AddFilesToHaikuImage system add-ons kernel debugger : <kdebug>demangle <kdebug>disasm@x86 <kdebug>hangman @@ -250,6 +253,7 @@ if $(TARGET_ARCH) = x86 || $(TARGET_ARCH) = x86_64 { # drivers AddNewDriversToHaikuImage disk scsi : scsi_cd scsi_disk ; +AddNewDriversToHaikuImage disk virtual : virtio_block ; AddNewDriversToHaikuImage power : enhanced_speedstep@x86 ; AddNewDriversToHaikuImage power : acpi_battery@x86 ; #AddNewDriversToHaikuImage display : display_controls@x86 ; @@ -607,6 +611,7 @@ AddBootModuleSymlinksToHaikuImage ide_isa@x86 <usb>uhci <usb>ohci <usb>ehci scsi_cd scsi_disk usb_disk + virtio virtio_pci virtio_block efi_gpt intel bfs diff --git a/src/add-ons/kernel/bus_managers/Jamfile b/src/add-ons/kernel/bus_managers/Jamfile index 319c631..f1de07b 100644 --- a/src/add-ons/kernel/bus_managers/Jamfile +++ b/src/add-ons/kernel/bus_managers/Jamfile @@ -12,3 +12,4 @@ SubInclude HAIKU_TOP src add-ons kernel bus_managers ps2 ; SubInclude HAIKU_TOP src add-ons kernel bus_managers scsi ; SubInclude HAIKU_TOP src add-ons kernel bus_managers tty ; SubInclude HAIKU_TOP src add-ons kernel bus_managers usb ; +SubInclude HAIKU_TOP src add-ons kernel bus_managers virtio ; diff --git a/src/add-ons/kernel/busses/Jamfile b/src/add-ons/kernel/busses/Jamfile index 7f3e607..2c9ee48 100644 --- a/src/add-ons/kernel/busses/Jamfile +++ b/src/add-ons/kernel/busses/Jamfile @@ -10,3 +10,4 @@ if $(HAIKU_ATA_STACK) = 1 { SubInclude HAIKU_TOP src add-ons kernel busses agp_gart ; SubInclude HAIKU_TOP src add-ons kernel busses scsi ; SubInclude HAIKU_TOP src add-ons kernel busses usb ; +SubInclude HAIKU_TOP src add-ons kernel busses virtio ; diff --git a/src/add-ons/kernel/drivers/disk/virtual/Jamfile b/src/add-ons/kernel/drivers/disk/virtual/Jamfile index 7952c77..6165da5 100644 --- a/src/add-ons/kernel/drivers/disk/virtual/Jamfile +++ b/src/add-ons/kernel/drivers/disk/virtual/Jamfile @@ -3,3 +3,4 @@ SubDir HAIKU_TOP src add-ons kernel drivers disk virtual ; #SubInclude HAIKU_TOP src add-ons kernel drivers disk virtual fmap ; SubInclude HAIKU_TOP src add-ons kernel drivers disk virtual nbd ; SubInclude HAIKU_TOP src add-ons kernel drivers disk virtual remote_disk ; +SubInclude HAIKU_TOP src add-ons kernel drivers disk virtual virtio_block ; diff --git a/src/system/kernel/device_manager/device_manager.cpp b/src/system/kernel/device_manager/device_manager.cpp index ec71aa1..27e0408 100644 --- a/src/system/kernel/device_manager/device_manager.cpp +++ b/src/system/kernel/device_manager/device_manager.cpp @@ -1552,6 +1552,7 @@ device_node::_GetNextDriverPath(void*& cookie, KPath& _path) switch (subType) { case PCI_scsi: _AddPath(*stack, "busses", "scsi"); + _AddPath(*stack, "busses", "virtio"); break; case PCI_ide: _AddPath(*stack, "busses", "ata"); @@ -1583,6 +1584,7 @@ device_node::_GetNextDriverPath(void*& cookie, KPath& _path) break; case PCI_network: _AddPath(*stack, "drivers", "net"); + _AddPath(*stack, "busses", "virtio"); break; case PCI_display: _AddPath(*stack, "drivers", "graphics"); ############################################################################ Commit: 6bbf9c9da977b2ea9d1caf36a867d320de81a836 URL: http://cgit.haiku-os.org/haiku/commit/?id=6bbf9c9 Author: Jérôme Duval <jerome.duval@xxxxxxxxx> Date: Sun May 26 14:08:18 2013 UTC Virtio: added drivers for PCI busses, bus manager and block device. * the Virtio PCI bus driver exposes a Virtio controller to the Virtio bus manager, which in turn exposes a Virtio device consumed by Virtio drivers. Drivers follow the new driver model. * virtio_block handles Virtio block devices under disk/virtual/virtio_block/x/raw. * Here is the Qemu command line option for Virtio disk devices: -drive file=haiku.image,if=virtio * the PCI bus driver currently supports only legacy interrupts (no MSI(-X) yet). * There is room for improvements in the bus manager: - it notifies the host for each queued request, which isn't optimal. - transfer descriptors should probably be simply preallocated (they are nicely leaked at the moment). - indirect descriptors are not supported yet. and in the block driver: - get the id of the disk. - implements flushing the cache. - improves dma restrictions. - do_io() should use a page for header descriptors instead of malloc(), which could cross boundaries. * The device manager tries to guess the driver based on the PCI device type, this implies having to declare the "busses/virtio" path for each possible type provided by Virtio. Thus future driver additions might require patching the device manager. * virtio.h is still private, the API is subject to changes. * virtio_pci.h, virtio_blk.h, virtio_ring.h are copied unchanged from FreeBSD. ---------------------------------------------------------------------------- diff --git a/headers/private/virtio/virtio.h b/headers/private/virtio/virtio.h new file mode 100644 index 0000000..8e446f5 --- /dev/null +++ b/headers/private/virtio/virtio.h @@ -0,0 +1,120 @@ +/* + * Copyright 2013, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _VIRTIO_H_ +#define _VIRTIO_H_ + + +#include <device_manager.h> +#include <KernelExport.h> + + +#define VIRTIO_DEVICE_ID_NETWORK 0x01 +#define VIRTIO_DEVICE_ID_BLOCK 0x02 +#define VIRTIO_DEVICE_ID_CONSOLE 0x03 +#define VIRTIO_DEVICE_ID_ENTROPY 0x04 +#define VIRTIO_DEVICE_ID_BALLOON 0x05 +#define VIRTIO_DEVICE_ID_IOMEMORY 0x06 +#define VIRTIO_DEVICE_ID_SCSI 0x08 +#define VIRTIO_DEVICE_ID_9P 0x09 + +#define VIRTIO_FEATURE_TRANSPORT_MASK ((1 << 28) - 1) + +#define VIRTIO_FEATURE_NOTIFY_ON_EMPTY (1 << 24) +#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_VIRTQUEUES_MAX_COUNT 8 + +#define VIRTIO_CONFIG_STATUS_RESET 0x00 +#define VIRTIO_CONFIG_STATUS_ACK 0x01 +#define VIRTIO_CONFIG_STATUS_DRIVER 0x02 +#define VIRTIO_CONFIG_STATUS_DRIVER_OK 0x04 +#define VIRTIO_CONFIG_STATUS_FAILED 0x80 + +// attributes: + +// node type +#define VIRTIO_BUS_TYPE_NAME "bus/virtio/v1" +// device type (uint16) +#define VIRTIO_DEVICE_TYPE_ITEM "virtio/type" +// alignment (uint16) +#define VIRTIO_VRING_ALIGNMENT_ITEM "virtio/vring_alignment" + +// sim cookie, issued by virtio bus manager +typedef void* virtio_sim; +// device cookie, issued by virtio bus manager +typedef void* virtio_device; +// queue cookie, issued by virtio bus manager +typedef void* virtio_queue; +// callback function for requests +typedef void (*virtio_callback_func)(void *cookie); +// callback function for interrupts +typedef void (*virtio_intr_func)(void *cookie); + +#define VIRTIO_DEVICE_MODULE_NAME "bus_managers/virtio/device/v1" + +typedef struct { + driver_module_info info; + + status_t (*queue_interrupt_handler)(virtio_sim sim, uint16 queue); + status_t (*config_interrupt_handler)(virtio_sim sim); +} virtio_for_controller_interface; + +#define VIRTIO_FOR_CONTROLLER_MODULE_NAME "bus_managers/virtio/controller/driver_v1" + +// Bus manager interface used by Virtio controller drivers. +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); + uint8 (*get_status)(void* cookie); + void (*set_status)(void* cookie, uint8 status); + status_t (*read_device_config)(void* cookie, uint8 offset, void* buffer, + size_t bufferSize); + status_t (*write_device_config)(void* cookie, uint8 offset, + 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_interrupt)(void* cookie); + void (*notify_queue)(void* cookie, uint16 queue); +} virtio_sim_interface; + + +// bus manager device interface for peripheral driver +typedef struct { + driver_module_info info; + + status_t (*negociate_features)(virtio_device cookie, uint32 supported, + uint32* negociated, const char* (*get_feature_name)(uint32)); + + status_t (*read_device_config)(virtio_device cookie, uint8 offset, + void* buffer, size_t bufferSize); + status_t (*write_device_config)(virtio_device cookie, uint8 offset, + const void* buffer, size_t bufferSize); + + status_t (*alloc_queues)(virtio_device cookie, size_t count, + virtio_queue *queues); + + status_t (*setup_interrupt)(virtio_device cookie, + virtio_intr_func config_handler, void* configCookie); + + status_t (*queue_request)(virtio_queue queue, + const physical_entry *readEntry, + const physical_entry *writtenEntry, virtio_callback_func callback, + void *callbackCookie); + + status_t (*queue_request_v)(virtio_queue queue, + const physical_entry* vector, + size_t readVectorCount, size_t writtenVectorCount, + virtio_callback_func callback, void *callbackCookie); + +} virtio_device_interface; + + +#endif /* _VIRTIO_H_ */ diff --git a/src/add-ons/kernel/bus_managers/virtio/Jamfile b/src/add-ons/kernel/bus_managers/virtio/Jamfile new file mode 100644 index 0000000..ef44b00 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/virtio/Jamfile @@ -0,0 +1,10 @@ +SubDir HAIKU_TOP src add-ons kernel bus_managers virtio ; + +UsePrivateHeaders virtio ; +UsePrivateKernelHeaders ; + +KernelAddon virtio : + VirtioDevice.cpp + VirtioModule.cpp + VirtioQueue.cpp + ; diff --git a/src/add-ons/kernel/bus_managers/virtio/VirtioDevice.cpp b/src/add-ons/kernel/bus_managers/virtio/VirtioDevice.cpp new file mode 100644 index 0000000..606c77e --- /dev/null +++ b/src/add-ons/kernel/bus_managers/virtio/VirtioDevice.cpp @@ -0,0 +1,255 @@ +/* + * Copyright 2013, Jérôme Duval, korli@xxxxxxxxxxxxxxxx. + * Distributed under the terms of the MIT License. + */ + + +#include "VirtioPrivate.h" + + +const char * +virtio_get_feature_name(uint32 feature) +{ + switch (feature) { + case VIRTIO_FEATURE_NOTIFY_ON_EMPTY: + return "notify on empty"; + case VIRTIO_FEATURE_RING_INDIRECT_DESC: + return "ring indirect"; + case VIRTIO_FEATURE_RING_EVENT_IDX: + return "ring event index"; + case VIRTIO_FEATURE_BAD_FEATURE: + return "bad feature"; + } + return NULL; +} + + +const char * +virtio_get_device_type_name(uint16 type) +{ + switch (type) { + case VIRTIO_DEVICE_ID_NETWORK: + return "network"; + case VIRTIO_DEVICE_ID_BLOCK: + return "block"; + case VIRTIO_DEVICE_ID_CONSOLE: + return "console"; + case VIRTIO_DEVICE_ID_ENTROPY: + return "entropy"; + case VIRTIO_DEVICE_ID_BALLOON: + return "balloon"; + case VIRTIO_DEVICE_ID_IOMEMORY: + return "io_memory"; + case VIRTIO_DEVICE_ID_SCSI: + return "scsi"; + case VIRTIO_DEVICE_ID_9P: + return "9p transport"; + default: + return "unknown"; + } +} + + +VirtioDevice::VirtioDevice(device_node *node) + : + fNode(node), + fID(0), + fController(NULL), + fCookie(NULL), + fStatus(B_NO_INIT), + fQueues(NULL), + fFeatures(0) +{ + device_node *parent = gDeviceManager->get_parent_node(node); + fStatus = gDeviceManager->get_driver(parent, + (driver_module_info **)&fController, &fCookie); + gDeviceManager->put_node(parent); + + if (fStatus != B_OK) + return; + + fStatus = gDeviceManager->get_attr_uint16(fNode, + VIRTIO_VRING_ALIGNMENT_ITEM, &fAlignment, true); + if (fStatus != B_OK) { + ERROR("alignment missing\n"); + return; + } + + fController->set_sim(fCookie, this); + + fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER); +} + + +VirtioDevice::~VirtioDevice() +{ + for (size_t index = 0; index < fQueueCount; index++) { + delete fQueues[index]; + } + delete fQueues; +} + + +status_t +VirtioDevice::InitCheck() +{ + return fStatus; +} + + +status_t +VirtioDevice::NegociateFeatures(uint32 supported, uint32* negociated, + const char* (*get_feature_name)(uint32)) +{ + fFeatures = 0; + status_t status = fController->read_host_features(fCookie, &fFeatures); + if (status != B_OK) + return status; + + DumpFeatures("read features", fFeatures, get_feature_name); + + fFeatures &= supported; + + // filter our own features + fFeatures &= (VIRTIO_FEATURE_TRANSPORT_MASK + /*| VIRTIO_FEATURE_RING_INDIRECT_DESC*/ | VIRTIO_FEATURE_RING_EVENT_IDX); + + *negociated = fFeatures; + + DumpFeatures("negociated features", fFeatures, get_feature_name); + + return fController->write_guest_features(fCookie, fFeatures); +} + + +status_t +VirtioDevice::ReadDeviceConfig(uint8 offset, void* buffer, size_t bufferSize) +{ + return fController->read_device_config(fCookie, offset, buffer, + bufferSize); +} + + +status_t +VirtioDevice::WriteDeviceConfig(uint8 offset, const void* buffer, + size_t bufferSize) +{ + return fController->write_device_config(fCookie, offset, buffer, + bufferSize); +} + + +status_t +VirtioDevice::AllocateQueues(size_t count, virtio_queue *queues) +{ + if (count > VIRTIO_VIRTQUEUES_MAX_COUNT || queues == NULL) + return B_BAD_VALUE; + + status_t status = B_OK; + fQueues = new(std::nothrow) VirtioQueue*[count]; + if (fQueues == NULL) { + status = B_NO_MEMORY; + goto err; + } + + fQueueCount = count; + for (size_t index = 0; index < count; index++) { + uint16 size = fController->get_queue_ring_size(fCookie, index); + fQueues[index] = new(std::nothrow) VirtioQueue(this, index, size); + queues[index] = fQueues[index]; + status = B_NO_MEMORY; + if (fQueues[index] != NULL) + status = fQueues[index]->InitCheck(); + if (status != B_OK) + goto err; + } + + return B_OK; + +err: + return status; +} + + +status_t +VirtioDevice::SetupInterrupt(virtio_intr_func configHandler, + void* configCookie) +{ + fConfigHandler = configHandler; + fConfigCookie = configCookie; + status_t status = fController->setup_interrupt(fCookie); + if (status != B_OK) + return status; + + // ready to go + fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER_OK); + + for (size_t index = 0; index < fQueueCount; index++) + fQueues[index]->EnableInterrupt(); + return B_OK; +} + + +status_t +VirtioDevice::SetupQueue(uint16 queueNumber, phys_addr_t physAddr) +{ + return fController->setup_queue(fCookie, queueNumber, physAddr); +} + + +void +VirtioDevice::NotifyQueue(uint16 queueNumber) +{ + fController->notify_queue(fCookie, queueNumber); +} + + +status_t +VirtioDevice::QueueInterrupt(uint16 queueNumber) +{ + if (queueNumber != INT16_MAX) { + if (queueNumber >= fQueueCount) + return B_BAD_VALUE; + return fQueues[queueNumber]->Interrupt(); + } + + status_t status = B_OK; + for (uint16 i = 0; i < fQueueCount; i++) { + status = fQueues[i]->Interrupt(); + if (status != B_OK) + break; + } + + return status; +} + + +status_t +VirtioDevice::ConfigInterrupt() +{ + if (fConfigHandler != NULL) + fConfigHandler(fConfigCookie); + return B_OK; +} + + +void +VirtioDevice::DumpFeatures(const char* title, uint32 features, + const char* (*get_feature_name)(uint32)) +{ + char features_string[512] = ""; + for (uint32 i = 0; i < 32; i++) { + uint32 feature = features & (1 << i); + if (feature == 0) + continue; + const char* name = virtio_get_feature_name(feature); + if (name == NULL) + name = get_feature_name(feature); + if (name != NULL) { + snprintf(features_string, sizeof(features_string), "%s[%s] ", + features_string, name); + } + } + TRACE("%s: %s\n", title, features_string); +} + diff --git a/src/add-ons/kernel/bus_managers/virtio/VirtioModule.cpp b/src/add-ons/kernel/bus_managers/virtio/VirtioModule.cpp new file mode 100644 index 0000000..8229fc7 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/virtio/VirtioModule.cpp @@ -0,0 +1,258 @@ +/* + * Copyright 2013, Jérôme Duval, korli@xxxxxxxxxxxxxxxx. + * Distributed under the terms of the MIT License. + */ + + +#include "VirtioPrivate.h" + + +device_manager_info *gDeviceManager = NULL; + + +// #pragma mark - + + +static status_t +virtio_device_init(device_node *node, void **cookie) +{ + CALLED(); + VirtioDevice *device = new(std::nothrow) VirtioDevice(node); + if (device == NULL) + return B_NO_MEMORY; + + status_t result = device->InitCheck(); + if (result != B_OK) { + ERROR("failed to set up virtio device object\n"); + return result; + } + + *cookie = device; + return B_OK; +} + + +static void +virtio_device_uninit(void *cookie) +{ + CALLED(); + VirtioDevice *device = (VirtioDevice *)cookie; + delete device; +} + + +static void +virtio_device_removed(void *cookie) +{ + CALLED(); + //VirtioDevice *device = (VirtioDevice *)cookie; +} + + +// #pragma mark - + + +status_t +virtio_negociate_features(void* cookie, uint32 supported, + uint32* negociated, const char* (*get_feature_name)(uint32)) +{ + CALLED(); + VirtioDevice *device = (VirtioDevice *)cookie; + + return device->NegociateFeatures(supported, negociated, get_feature_name); +} + + +status_t +virtio_read_device_config(void* cookie, uint8 offset, void* buffer, + size_t bufferSize) +{ + CALLED(); + VirtioDevice *device = (VirtioDevice *)cookie; + + return device->ReadDeviceConfig(offset, buffer, bufferSize); +} + + +status_t +virtio_write_device_config(void* cookie, uint8 offset, + const void* buffer, size_t bufferSize) +{ + CALLED(); + VirtioDevice *device = (VirtioDevice *)cookie; + + return device->WriteDeviceConfig(offset, buffer, bufferSize); +} + + +status_t +virtio_alloc_queues(virtio_device cookie, size_t count, virtio_queue *queues) +{ + CALLED(); + VirtioDevice *device = (VirtioDevice *)cookie; + return device->AllocateQueues(count, queues); +} + + +status_t +virtio_setup_interrupt(virtio_device cookie, virtio_intr_func config_handler, + void* configCookie) +{ + CALLED(); + VirtioDevice *device = (VirtioDevice *)cookie; + return device->SetupInterrupt(config_handler, configCookie); +} + + +status_t +virtio_queue_request_v(virtio_queue cookie, const physical_entry* vector, + size_t readVectorCount, size_t writtenVectorCount, + virtio_callback_func callback, void *callbackCookie) +{ + CALLED(); + VirtioQueue *queue = (VirtioQueue *)cookie; + return queue->QueueRequest(vector, readVectorCount, writtenVectorCount, + callback, callbackCookie); +} + + +status_t +virtio_queue_request(virtio_queue cookie, const physical_entry *readEntry, + const physical_entry *writtenEntry, virtio_callback_func callback, + void *callbackCookie) +{ + physical_entry entries[2]; + if (readEntry != NULL) { + entries[0] = *readEntry; + if (writtenEntry != NULL) + entries[1] = *writtenEntry; + } else if (writtenEntry != NULL) + entries[0] = *writtenEntry; + + return virtio_queue_request_v(cookie, entries, readEntry != NULL ? 1 : 0, + writtenEntry != NULL? 1 : 0, callback, callbackCookie); +} + + +// #pragma mark - + + +status_t +virtio_added_device(device_node *parent) +{ + CALLED(); + + uint16 deviceType; + if (gDeviceManager->get_attr_uint16(parent, + VIRTIO_DEVICE_TYPE_ITEM, &deviceType, true) != B_OK) { + ERROR("device type missing\n"); + return B_ERROR; + } + + device_attr attributes[] = { + // info about device + { B_DEVICE_BUS, B_STRING_TYPE, { string: "virtio" }}, + { VIRTIO_DEVICE_TYPE_ITEM, B_UINT16_TYPE, + { ui16: deviceType }}, + { NULL } + }; + + return gDeviceManager->register_node(parent, VIRTIO_DEVICE_MODULE_NAME, + attributes, NULL, NULL); +} + + +status_t +virtio_queue_interrupt_handler(virtio_sim sim, uint16 queue) +{ + VirtioDevice* device = (VirtioDevice*)sim; + return device->QueueInterrupt(queue); +} + + +status_t +virtio_config_interrupt_handler(virtio_sim sim) +{ + VirtioDevice* device = (VirtioDevice*)sim; + return device->ConfigInterrupt(); +} + + +static status_t +std_ops(int32 op, ...) +{ + switch (op) { + case B_MODULE_INIT: + case B_MODULE_UNINIT: + return B_OK; + + default: + break; + } + + return B_ERROR; +} + + +// #pragma mark - + + +virtio_device_interface virtio_device_module = { + { + { + VIRTIO_DEVICE_MODULE_NAME, + 0, + std_ops + }, + + NULL, // supported devices + NULL, // register node + virtio_device_init, + virtio_device_uninit, + NULL, // register child devices + NULL, // rescan + virtio_device_removed, + NULL, // suspend + NULL, // resume + }, + + virtio_negociate_features, + virtio_read_device_config, + virtio_write_device_config, + virtio_alloc_queues, + virtio_setup_interrupt, + virtio_queue_request, + virtio_queue_request_v +}; + +virtio_for_controller_interface virtio_for_controller_module = { + { + { + VIRTIO_FOR_CONTROLLER_MODULE_NAME, + 0, + &std_ops + }, + + NULL, // supported devices + virtio_added_device, + NULL, + NULL, + NULL + }, + + virtio_queue_interrupt_handler, + virtio_config_interrupt_handler +}; + + +module_dependency module_dependencies[] = { + { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&gDeviceManager }, + {} +}; + +module_info *modules[] = { + (module_info *)&virtio_for_controller_module, + (module_info *)&virtio_device_module, + NULL +}; + diff --git a/src/add-ons/kernel/bus_managers/virtio/VirtioPrivate.h b/src/add-ons/kernel/bus_managers/virtio/VirtioPrivate.h new file mode 100644 index 0000000..1b31201 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/virtio/VirtioPrivate.h @@ -0,0 +1,146 @@ +/* + * Copyright 2013, Jérôme Duval, korli@xxxxxxxxxxxxxxxx. + * Distributed under the terms of the MIT License. + */ +#ifndef VIRTIO_PRIVATE_H +#define VIRTIO_PRIVATE_H + + +#include <new> +#include <stdio.h> +#include <string.h> + +#include <lock.h> +#include <util/AutoLock.h> +#include <virtio.h> + +#include "virtio_ring.h" + + +//#define VIRTIO_TRACE +#ifdef VIRTIO_TRACE +# define TRACE(x...) dprintf("\33[33mvirtio:\33[0m " x) +#else +# define TRACE(x...) +#endif +#define ERROR(x...) dprintf("\33[33mvirtio:\33[0m " x) +#define CALLED() TRACE("CALLED %s\n", __PRETTY_FUNCTION__) + + +#define VIRTIO_SIM_MODULE_NAME "bus_managers/virtio/sim/driver_v1" + + +class VirtioDevice; +class VirtioQueue; + +extern device_manager_info *gDeviceManager; + + +class VirtioDevice { +public: + VirtioDevice(device_node *node); + ~VirtioDevice(); + + status_t InitCheck(); + uint32 ID() const { return fID; } + + status_t NegociateFeatures(uint32 supported, + uint32* negociated, + const char* (*get_feature_name)(uint32)); + + status_t ReadDeviceConfig(uint8 offset, void* buffer, + size_t bufferSize); + status_t WriteDeviceConfig(uint8 offset, + const void* buffer, size_t bufferSize); + + status_t AllocateQueues(size_t count, + virtio_queue *queues); + status_t SetupInterrupt(virtio_intr_func config_handler, + void* configCookie); + + uint16 Alignment() { return fAlignment; } + uint32 Features() { return fFeatures; } + + status_t SetupQueue(uint16 queueNumber, + phys_addr_t physAddr); + void NotifyQueue(uint16 queueNumber); + + status_t QueueInterrupt(uint16 queueNumber); + status_t ConfigInterrupt(); + +private: + void DumpFeatures(const char* title, + uint32 features, + const char* (*get_feature_name)(uint32)); + + + device_node * fNode; + uint32 fID; + virtio_sim_interface *fController; + void * fCookie; + status_t fStatus; + VirtioQueue** fQueues; + size_t fQueueCount; + uint32 fFeatures; + uint16 fAlignment; + + virtio_intr_func fConfigHandler; + void* fConfigCookie; +}; + + +class TransferDescriptor; + + +class VirtioQueue { +public: + VirtioQueue(VirtioDevice *device, + uint16 queueNumber, uint16 ringSize); + ~VirtioQueue(); + status_t InitCheck() { return fStatus; } + + void NotifyHost(); + status_t Interrupt(); + + bool IsFull() { return fRingFree == 0; } + bool IsEmpty() { return fRingFree == fRingSize; } + + status_t QueueRequest(const physical_entry* vector, + size_t readVectorCount, + size_t writtenVectorCount, + virtio_callback_func callback, + void *callbackCookie); + status_t QueueRequestIndirect( + const physical_entry* vector, + size_t readVectorCount, + size_t writtenVectorCount, + virtio_callback_func callback, + void *callbackCookie); + void EnableInterrupt(); + void DisableInterrupt(); + +private: + void UpdateAvailable(uint16 index); + uint16 QueueVector(uint16 insertIndex, + struct vring_desc *desc, + const physical_entry* vector, + size_t readVectorCount, + size_t writtenVectorCount); + void Finish(); + + VirtioDevice* fDevice; + uint16 fQueueNumber; + uint16 fRingSize; + uint16 fRingFree; + + struct vring fRing; + uint16 fRingHeadIndex; + uint16 fRingUsedIndex; + status_t fStatus; + size_t fAreaSize; + area_id fArea; + + TransferDescriptor** fDescriptors; +}; + +#endif // VIRTIO_PRIVATE_H diff --git a/src/add-ons/kernel/bus_managers/virtio/VirtioQueue.cpp b/src/add-ons/kernel/bus_managers/virtio/VirtioQueue.cpp new file mode 100644 index 0000000..6cff71d --- /dev/null +++ b/src/add-ons/kernel/bus_managers/virtio/VirtioQueue.cpp @@ -0,0 +1,288 @@ +/* + * Copyright 2013, Jérôme Duval, korli@xxxxxxxxxxxxxxxx. + * Distributed under the terms of the MIT License. + */ + + +#include "VirtioPrivate.h" + + +static inline uint32 +round_to_pagesize(uint32 size) +{ + return (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1); +} + + +area_id +alloc_mem(void **virt, phys_addr_t *phy, size_t size, uint32 protection, + const char *name) +{ + physical_entry pe; + void * virtadr; + area_id areaid; + status_t rv; + + TRACE("allocating %ld bytes for %s\n", size, name); + + size = round_to_pagesize(size); + areaid = create_area(name, &virtadr, B_ANY_KERNEL_ADDRESS, size, + B_CONTIGUOUS, protection); + if (areaid < B_OK) { + ERROR("couldn't allocate area %s\n", name); + return B_ERROR; + } + rv = get_memory_map(virtadr, size, &pe, 1); + if (rv < B_OK) { + delete_area(areaid); + ERROR("couldn't get mapping for %s\n", name); + return B_ERROR; + } + if (virt) + *virt = virtadr; + if (phy) + *phy = pe.address; + TRACE("area = %" B_PRId32 ", size = %ld, virt = %p, phy = %#" B_PRIxPHYSADDR "\n", + areaid, size, virtadr, pe.address); + return areaid; +} + + +class TransferDescriptor { +public: + TransferDescriptor(uint16 size, + virtio_callback_func callback, + void *callbackCookie); + ~TransferDescriptor(); + + void Callback(); + uint16 Size() { return fDescriptorCount; } +private: + void* fCookie; + virtio_callback_func fCallback; + struct vring_desc* fIndirect; + size_t fAreaSize; + area_id fArea; + uint16 fDescriptorCount; +}; + + +TransferDescriptor::TransferDescriptor(uint16 size, + virtio_callback_func callback, void *callbackCookie) + : fCookie(callbackCookie), + fCallback(callback), + fDescriptorCount(size) +{ +} + + +TransferDescriptor::~TransferDescriptor() +{ +} + + +void +TransferDescriptor::Callback() +{ + if (fCallback != NULL) + fCallback(fCookie); +} + + +// #pragma mark - + + +VirtioQueue::VirtioQueue(VirtioDevice* device, uint16 queueNumber, + uint16 ringSize) + : + fDevice(device), + fQueueNumber(queueNumber), + fRingSize(ringSize), + fRingFree(ringSize), + fRingHeadIndex(0), + fRingUsedIndex(0), + fStatus(B_OK) +{ + fDescriptors = new(std::nothrow) TransferDescriptor*[fRingSize]; + if (fDescriptors == NULL) { + fStatus = B_NO_MEMORY; + return; + } + + uint8* virtAddr; + phys_addr_t physAddr; + fAreaSize = vring_size(fRingSize, device->Alignment()); + fArea = alloc_mem((void **)&virtAddr, &physAddr, fAreaSize, 0, + "virtqueue"); + if (fArea < B_OK) { + fStatus = fArea; + return; + } + memset(virtAddr, 0, fAreaSize); + vring_init(&fRing, fRingSize, virtAddr, device->Alignment()); + + for (uint16 i = 0; i < fRingSize - 1; i++) + fRing.desc[i].next = i + 1; + fRing.desc[fRingSize - 1].next = UINT16_MAX; + + DisableInterrupt(); + + device->SetupQueue(fQueueNumber, physAddr); +} + + +VirtioQueue::~VirtioQueue() +{ + delete_area(fArea); +} + + +void +VirtioQueue::DisableInterrupt() +{ + /*if ((fDevice->Features() & VIRTIO_FEATURE_RING_EVENT_IDX) == 0) + fRing.avail->flags |= VRING_AVAIL_F_NO_INTERRUPT;*/ +} + + +void +VirtioQueue::EnableInterrupt() +{ + /*if ((fDevice->Features() & VIRTIO_FEATURE_RING_EVENT_IDX) == 0) + fRing.avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT;*/ +} + + +void +VirtioQueue::NotifyHost() +{ + fDevice->NotifyQueue(fQueueNumber); +} + + +status_t +VirtioQueue::Interrupt() +{ + CALLED(); + DisableInterrupt(); + + while (fRingUsedIndex != fRing.used->idx) + Finish(); + + EnableInterrupt(); + return B_OK; +} + + +void +VirtioQueue::Finish() +{ + TRACE("Finish() fRingUsedIndex: %u\n", fRingUsedIndex); + + uint16 usedIndex = fRingUsedIndex++ & (fRingSize - 1); + TRACE("Finish() usedIndex: %u\n", usedIndex); + struct vring_used_elem *element = &fRing.used->ring[usedIndex]; + uint16 descriptorIndex = element->id; + // uint32 length = element->len; + + fDescriptors[descriptorIndex]->Callback(); + uint16 size = fDescriptors[descriptorIndex]->Size(); + fRingFree += size; + size--; + + uint16 index = descriptorIndex; + while ((fRing.desc[index].flags & VRING_DESC_F_NEXT) != 0) { + index = fRing.desc[index].next; + size--; + } + + if (size > 0) + panic("VirtioQueue::Finish() descriptors left %d\n", size); + + // TODO TransferDescriptors are leaked, can't delete in interrupt handler. + + fRing.desc[index].next = fRingHeadIndex; + fRingHeadIndex = descriptorIndex; + TRACE("Finish() fRingHeadIndex: %u\n", fRingHeadIndex); +} + + +status_t +VirtioQueue::QueueRequest(const physical_entry* vector, size_t readVectorCount, + size_t writtenVectorCount, virtio_callback_func callback, + void *callbackCookie) +{ + CALLED(); + size_t count = readVectorCount + writtenVectorCount; + if (count < 1) + return B_BAD_VALUE; + if ((fDevice->Features() & VIRTIO_FEATURE_RING_INDIRECT_DESC) != 0) { + return QueueRequestIndirect(vector, readVectorCount, + writtenVectorCount, callback, callbackCookie); + } + + if (count > fRingFree) + return B_BUSY; + + uint16 insertIndex = fRingHeadIndex; + fDescriptors[insertIndex] = new(std::nothrow) TransferDescriptor(count, + callback, callbackCookie); + if (fDescriptors[insertIndex] == NULL) + return B_NO_MEMORY; + + // enqueue + uint16 index = QueueVector(insertIndex, fRing.desc, vector, + readVectorCount, writtenVectorCount); + + fRingHeadIndex = index; + fRingFree -= count; + + UpdateAvailable(insertIndex); + + NotifyHost(); + + return B_OK; +} + + +status_t +VirtioQueue::QueueRequestIndirect(const physical_entry* vector, + size_t readVectorCount, size_t writtenVectorCount, + virtio_callback_func callback, void *callbackCookie) +{ + // TODO + return B_OK; +} + + +void +VirtioQueue::UpdateAvailable(uint16 index) +{ + CALLED(); + uint16 available = fRing.avail->idx & (fRingSize - 1); + fRing.avail->ring[available] = index; + fRing.avail->idx++; +} + + +uint16 +VirtioQueue::QueueVector(uint16 insertIndex, struct vring_desc *desc, + const physical_entry* vector, size_t readVectorCount, + size_t writtenVectorCount) +{ + CALLED(); + uint16 index = insertIndex; + size_t total = readVectorCount + writtenVectorCount; + for (size_t i = 0; i < total; i++) { + desc[index].addr = vector[i].address; + desc[index].len = vector[i].size; + desc[index].flags = 0; + if (i >= readVectorCount) + desc[index].flags |= VRING_DESC_F_WRITE; + if (i < total - 1) + desc[index].flags |= VRING_DESC_F_NEXT; + index = desc[index].next; + } + + return index; +} diff --git a/src/add-ons/kernel/bus_managers/virtio/virtio_ring.h b/src/add-ons/kernel/bus_managers/virtio/virtio_ring.h new file mode 100644 index 0000000..c4f2b73 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/virtio/virtio_ring.h @@ -0,0 +1,165 @@ +/*- + * Copyright Rusty Russell IBM Corporation 2007. + * + * This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of IBM nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef VIRTIO_RING_H +#define VIRTIO_RING_H + +/* This marks a buffer as continuing via the next field. */ +#define VRING_DESC_F_NEXT 1 +/* This marks a buffer as write-only (otherwise read-only). */ +#define VRING_DESC_F_WRITE 2 +/* This means the buffer contains a list of buffer descriptors. */ +#define VRING_DESC_F_INDIRECT 4 + +/* The Host uses this in used->flags to advise the Guest: don't kick me + * when you add a buffer. It's unreliable, so it's simply an + * optimization. Guest will still kick if it's out of buffers. */ +#define VRING_USED_F_NO_NOTIFY 1 +/* The Guest uses this in avail->flags to advise the Host: don't + * interrupt me when you consume a buffer. It's unreliable, so it's + * simply an optimization. */ +#define VRING_AVAIL_F_NO_INTERRUPT 1 + +/* VirtIO ring descriptors: 16 bytes. + * These can chain together via "next". */ +struct vring_desc { + /* Address (guest-physical). */ + uint64_t addr; + /* Length. */ + uint32_t len; + /* The flags as indicated above. */ + uint16_t flags; + /* We chain unused descriptors via this, too. */ + uint16_t next; +}; + +struct vring_avail { + uint16_t flags; + uint16_t idx; + uint16_t ring[0]; +}; + +/* uint32_t is used here for ids for padding reasons. */ +struct vring_used_elem { + /* Index of start of used descriptor chain. */ + uint32_t id; + /* Total length of the descriptor chain which was written to. */ + uint32_t len; +}; + +struct vring_used { + uint16_t flags; + uint16_t idx; + struct vring_used_elem ring[0]; +}; + +struct vring { + unsigned int num; + + struct vring_desc *desc; + struct vring_avail *avail; + struct vring_used *used; +}; + +/* The standard layout for the ring is a continuous chunk of memory which + * looks like this. We assume num is a power of 2. + * + * struct vring { + * // The actual descriptors (16 bytes each) + * struct vring_desc desc[num]; + * + * // A ring of available descriptor heads with free-running index. + * __u16 avail_flags; + * __u16 avail_idx; + * __u16 available[num]; + * __u16 used_event_idx; + * + * // Padding to the next align boundary. + * char pad[]; + * + * // A ring of used descriptor heads with free-running index. + * __u16 used_flags; + * __u16 used_idx; + * struct vring_used_elem used[num]; + * __u16 avail_event_idx; + * }; + * + * NOTE: for VirtIO PCI, align is 4096. + */ + +/* + * We publish the used event index at the end of the available ring, and vice + * versa. They are at the end for backwards compatibility. + */ +#define vring_used_event(vr) ((vr)->avail->ring[(vr)->num]) +#define vring_avail_event(vr) (*(uint16_t *)&(vr)->used->ring[(vr)->num]) + +static inline int +vring_size(unsigned int num, unsigned long align) +{ + int size; + + size = num * sizeof(struct vring_desc); + size += sizeof(struct vring_avail) + (num * sizeof(uint16_t)) + + sizeof(uint16_t); + size = (size + align - 1) & ~(align - 1); + size += sizeof(struct vring_used) + + (num * sizeof(struct vring_used_elem)) + sizeof(uint16_t); + return (size); +} + +static inline void +vring_init(struct vring *vr, unsigned int num, uint8_t *p, + unsigned long align) +{ + vr->num = num; + vr->desc = (struct vring_desc *) p; + vr->avail = (struct vring_avail *) (p + + num * sizeof(struct vring_desc)); + vr->used = (struct vring_used *) + (((addr_t) &vr->avail->ring[num] + align-1) & ~(align-1)); +} + +/* + * The following is used with VIRTIO_RING_F_EVENT_IDX. + * + * Assuming a given event_idx value from the other size, if we have + * just incremented index from old to new_idx, should we trigger an + * event? + */ +static inline int +vring_need_event(uint16_t event_idx, uint16_t new_idx, uint16_t old) +{ + + return (uint16_t)(new_idx - event_idx - 1) < (uint16_t)(new_idx - old); +} +#endif /* VIRTIO_RING_H */ diff --git a/src/add-ons/kernel/busses/virtio/Jamfile b/src/add-ons/kernel/busses/virtio/Jamfile new file mode 100644 index 0000000..934cd68 --- /dev/null +++ b/src/add-ons/kernel/busses/virtio/Jamfile @@ -0,0 +1,16 @@ +SubDir HAIKU_TOP src add-ons kernel busses virtio ; + +SubDirC++Flags -fno-rtti ; + +UsePrivateHeaders kernel virtio ; + +KernelAddon virtio_pci : + virtio_pci.cpp + + kernel_cpp.cpp + ; + +SEARCH on [ FGristFiles + kernel_cpp.cpp + ] = [ FDirName $(HAIKU_TOP) src system kernel util ] ; + diff --git a/src/add-ons/kernel/busses/virtio/virtio_pci.cpp b/src/add-ons/kernel/busses/virtio/virtio_pci.cpp new file mode 100644 index 0000000..3c3a641 --- /dev/null +++ b/src/add-ons/kernel/busses/virtio/virtio_pci.cpp @@ -0,0 +1,480 @@ +/* + * Copyright 2013, Jérôme Duval, korli@xxxxxxxxxxxxxxxx. + * Distributed under the terms of the MIT License. + */ + + +#include <new> +#include <stdio.h> +#include <string.h> + +#include <bus/PCI.h> +#include <virtio.h> + +#include "virtio_pci.h" + + +//#define TRACE_VIRTIO +#ifdef TRACE_VIRTIO +# define TRACE(x...) dprintf("\33[33mvirtio_pci:\33[0m " x) +#else +# define TRACE(x...) ; +#endif +#define ERROR(x...) dprintf("\33[33mvirtio_pci:\33[0m " x) +#define CALLED() TRACE("CALLED %s\n", __PRETTY_FUNCTION__) + + +#define VIRTIO_PCI_DEVICE_MODULE_NAME "busses/virtio/virtio_pci/driver_v1" +#define VIRTIO_PCI_SIM_MODULE_NAME "busses/virtio/virtio_pci/device/v1" + +#define VIRTIO_PCI_CONTROLLER_TYPE_NAME "virtio pci controller" + + +typedef struct { + pci_device_module_info* pci; + pci_device* device; + uint16 config_base; + addr_t base_addr; + uint8 irq; + virtio_sim sim; + + device_node* node; +} virtio_pci_sim_info; + + +device_manager_info* gDeviceManager; +virtio_for_controller_interface* gVirtio; + + +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); + if (isr == 0) + return B_UNHANDLED_INTERRUPT; + + if (isr & VIRTIO_PCI_ISR_CONFIG) + gVirtio->config_interrupt_handler(bus->sim); + + if (isr & VIRTIO_PCI_ISR_INTR) + gVirtio->queue_interrupt_handler(bus->sim, INT16_MAX); + + return B_HANDLED_INTERRUPT; +} + + +static void +set_sim(void* cookie, virtio_sim sim) +{ + CALLED(); + virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie; + bus->sim = sim; +} + + +static status_t +read_host_features(void* cookie, uint32 *features) +{ + CALLED(); + virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie; + + 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); + return B_OK; +} + + +static status_t +write_guest_features(void* cookie, uint32 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); + return B_OK; +} + + +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); +} + + +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); +} + + +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 + bus->config_base + _offset; + uint8* buffer = (uint8*)_buffer; + while (bufferSize > 0) { + uint8 size = 4; + if (bufferSize == 1) { + size = 1; + *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); + } else { + *(uint32*)buffer = bus->pci->read_io_32(bus->device, + offset); + } + buffer += size; + bufferSize -= size; + offset += size; + } + + return B_OK; +} + + +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 + bus->config_base + _offset; + const uint8* buffer = (const uint8*)_buffer; + while (bufferSize > 0) { + uint8 size = 4; + if (bufferSize == 1) { + size = 1; + bus->pci->write_pci_config(bus->device, + offset, size, *buffer); + } else if (bufferSize <= 3) { + size = 2; + bus->pci->write_pci_config(bus->device, + offset, size, *(const uint16*)buffer); + } else { + bus->pci->write_pci_config(bus->device, + offset, size, *(const uint32*)buffer); + } + buffer += size; + bufferSize -= size; + offset += size; + } + return B_OK; +} + + +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); +} + + +status_t +setup_queue(void* cookie, uint16 queue, phys_addr_t phy) +{ + 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); + return B_OK; +} + + +status_t +setup_interrupt(void* cookie) +{ + CALLED(); + virtio_pci_sim_info* bus = (virtio_pci_sim_info*)cookie; + + // setup interrupt handler + status_t status = install_io_interrupt_handler(bus->irq, + virtio_pci_interrupt, bus, 0); + if (status != B_OK) { + ERROR("can't install interrupt handler\n"); + return status; + } + + return B_OK; +} + + +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); +} + + +// #pragma mark - + + +static status_t +init_bus(device_node* node, void** bus_cookie) +{ + CALLED(); + status_t status = B_OK; + + virtio_pci_sim_info* bus = new(std::nothrow) virtio_pci_sim_info; + if (bus == NULL) { + return B_NO_MEMORY; + } + + pci_device_module_info* pci; + pci_device* device; + + { + device_node* parent = gDeviceManager->get_parent_node(node); + device_node* pciParent = gDeviceManager->get_parent_node(parent); + gDeviceManager->get_driver(pciParent, (driver_module_info**)&pci, + (void**)&device); + gDeviceManager->put_node(pciParent); + gDeviceManager->put_node(parent); + } + + bus->node = node; + bus->pci = pci; + bus->device = device; + // TODO MSI implies 24 + bus->config_base = 20; + + pci_info pciInfo; + pci->get_pci_info(device, &pciInfo); + + // legacy interrupt + bus->base_addr = pciInfo.u.h0.base_registers[0]; + bus->irq = pciInfo.u.h0.interrupt_line; + if (bus->irq == 0 || bus->irq == 0xff) { + ERROR("PCI IRQ not assigned\n"); + return B_ERROR; + } + + // 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); + + TRACE("init_bus() %p node %p pci %p device %p\n", bus, node, + bus->pci, bus->device); + + *bus_cookie = bus; + return B_OK; +} + + +static void +uninit_bus(void* bus_cookie) +{ + virtio_pci_sim_info* bus = (virtio_pci_sim_info*)bus_cookie; + delete bus; +} + + +static void +bus_removed(void* bus_cookie) +{ + return; +} + + +// #pragma mark - + + +static status_t +register_child_devices(void* cookie) +{ + CALLED(); + device_node* node = (device_node*)cookie; + device_node* parent = gDeviceManager->get_parent_node(node); + pci_device_module_info* pci; + pci_device* device; + gDeviceManager->get_driver(parent, (driver_module_info**)&pci, + (void**)&device); + + uint16 pciSubDeviceId = pci->read_pci_config(device, PCI_subsystem_id, + 2); + + char prettyName[25]; + sprintf(prettyName, "Virtio Device %" B_PRIu16, pciSubDeviceId); + + device_attr attrs[] = { + // properties of this controller for virtio bus manager + { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, + { string: prettyName }}, + { B_DEVICE_FIXED_CHILD, B_STRING_TYPE, + { string: VIRTIO_FOR_CONTROLLER_MODULE_NAME }}, + + // private data to identify the device + { VIRTIO_DEVICE_TYPE_ITEM, B_UINT16_TYPE, + { ui16: pciSubDeviceId }}, + { VIRTIO_VRING_ALIGNMENT_ITEM, B_UINT16_TYPE, + { ui16: VIRTIO_PCI_VRING_ALIGN }}, + { NULL } + }; + + return gDeviceManager->register_node(node, VIRTIO_PCI_SIM_MODULE_NAME, + attrs, NULL, &node); +} + + +static status_t +init_device(device_node* node, void** device_cookie) +{ + CALLED(); + *device_cookie = node; + return B_OK; +} + + +static status_t +register_device(device_node* parent) +{ + device_attr attrs[] = { + {B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "Virtio PCI"}}, + {} + }; + + return gDeviceManager->register_node(parent, VIRTIO_PCI_DEVICE_MODULE_NAME, + attrs, NULL, NULL); +} + + +static float +supports_device(device_node* parent) +{ + CALLED(); + const char* bus; + uint16 vendorID, deviceID; + + // make sure parent is a PCI Virtio device node + if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false) != B_OK + || gDeviceManager->get_attr_uint16(parent, B_DEVICE_VENDOR_ID, + &vendorID, false) < B_OK + || gDeviceManager->get_attr_uint16(parent, B_DEVICE_ID, &deviceID, + false) < B_OK) + return -1; + + if (strcmp(bus, "pci") != 0) + return 0.0f; + + if (vendorID == VIRTIO_PCI_VENDORID) { + if (deviceID < VIRTIO_PCI_DEVICEID_MIN + && deviceID > VIRTIO_PCI_DEVICEID_MAX) { + return 0.0f; + } + + pci_device_module_info* pci; + pci_device* device; + gDeviceManager->get_driver(parent, (driver_module_info**)&pci, + (void**)&device); + uint8 pciSubDeviceId = pci->read_pci_config(device, PCI_revision, + 1); + if (pciSubDeviceId != VIRTIO_PCI_ABI_VERSION) + return 0.0f; + + TRACE("Virtio device found! vendor 0x%04x, device 0x%04x\n", vendorID, + deviceID); + return 0.8f; + } + + return 0.0f; +} + + +// #pragma mark - + + +module_dependency module_dependencies[] = { + { VIRTIO_FOR_CONTROLLER_MODULE_NAME, (module_info**)&gVirtio }, + { B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager }, + {} +}; + + +static virtio_sim_interface gVirtioPCIDeviceModule = { + { + { + VIRTIO_PCI_SIM_MODULE_NAME, + 0, + NULL + }, + + NULL, // supports device + NULL, // register device + init_bus, + uninit_bus, + NULL, // register child devices + NULL, // rescan + bus_removed, + }, + + set_sim, + read_host_features, + write_guest_features, + get_status, + set_status, + read_device_config, + write_device_config, + get_queue_ring_size, + setup_queue, + setup_interrupt, + notify_queue +}; + + +static driver_module_info sVirtioDevice = { + { + VIRTIO_PCI_DEVICE_MODULE_NAME, + 0, + NULL + }, + + supports_device, + register_device, + init_device, + NULL, // uninit + register_child_devices, + NULL, // rescan + NULL, // device removed +}; + +module_info* modules[] = { + (module_info* )&sVirtioDevice, + (module_info* )&gVirtioPCIDeviceModule, + NULL +}; + diff --git a/src/add-ons/kernel/busses/virtio/virtio_pci.h b/src/add-ons/kernel/busses/virtio/virtio_pci.h new file mode 100644 index 0000000..485cf4f --- /dev/null +++ b/src/add-ons/kernel/busses/virtio/virtio_pci.h @@ -0,0 +1,87 @@ +/*- + * Copyright IBM Corp. 2007 + * + * Authors: + * Anthony Liguori <aliguori@xxxxxxxxxx> + * + * This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of IBM nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _VIRTIO_PCI_H +#define _VIRTIO_PCI_H + +/* 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 + +/* + * VirtIO Header, located in BAR 0. + */ +#define VIRTIO_PCI_HOST_FEATURES 0 /* host's supported features (32bit, RO)*/ +#define VIRTIO_PCI_GUEST_FEATURES 4 /* guest's supported features (32, RW) */ +#define VIRTIO_PCI_QUEUE_PFN 8 /* physical address of VQ (32, RW) */ +#define VIRTIO_PCI_QUEUE_NUM 12 /* number of ring entries (16, RO) */ +#define VIRTIO_PCI_QUEUE_SEL 14 /* current VQ selection (16, RW) */ +#define VIRTIO_PCI_QUEUE_NOTIFY 16 /* notify host regarding VQ (16, RW) */ +#define VIRTIO_PCI_STATUS 18 /* device status register (8, RW) */ +#define VIRTIO_PCI_ISR 19 /* interrupt status register, reading + * also clears the register (8, RO) */ +/* Only if MSIX is enabled: */ +#define VIRTIO_MSI_CONFIG_VECTOR 20 /* configuration change vector (16, RW) */ +#define VIRTIO_MSI_QUEUE_VECTOR 22 /* vector for selected VQ notifications + (16, RW) */ + +/* The bit of the ISR which indicates a device has an interrupt. */ +#define VIRTIO_PCI_ISR_INTR 0x1 +/* The bit of the ISR which indicates a device configuration change. */ +#define VIRTIO_PCI_ISR_CONFIG 0x2 +/* Vector value used to disable MSI for queue. */ +#define VIRTIO_MSI_NO_VECTOR 0xFFFF + +/* + * The remaining space is defined by each driver as the per-driver + * configuration space. + */ +#define VIRTIO_PCI_CONFIG(sc) \ + (((sc)->vtpci_flags & VTPCI_FLAG_MSIX) ? 24 : 20) + +/* + * How many bits to shift physical queue address written to QUEUE_PFN. + * 12 is historical, and due to x86 page size. + */ +#define VIRTIO_PCI_QUEUE_ADDR_SHIFT 12 + +/* The alignment to use between consumer and producer parts of vring. */ +#define VIRTIO_PCI_VRING_ALIGN 4096 + +#endif /* _VIRTIO_PCI_H */ diff --git a/src/add-ons/kernel/drivers/disk/virtual/virtio_block/Jamfile b/src/add-ons/kernel/drivers/disk/virtual/virtio_block/Jamfile new file mode 100644 index 0000000..9bfba08 --- /dev/null +++ b/src/add-ons/kernel/drivers/disk/virtual/virtio_block/Jamfile @@ -0,0 +1,9 @@ +SubDir HAIKU_TOP src add-ons kernel drivers disk virtual virtio_block ; + +UsePrivateKernelHeaders ; +UsePrivateHeaders drivers virtio ; +SubDirHdrs $(HAIKU_TOP) src system kernel device_manager ; + +KernelAddon virtio_block : + virtio_block.cpp +; diff --git a/src/add-ons/kernel/drivers/disk/virtual/virtio_block/virtio_blk.h b/src/add-ons/kernel/drivers/disk/virtual/virtio_block/virtio_blk.h new file mode 100644 index 0000000..fdac9e5 --- /dev/null +++ b/src/add-ons/kernel/drivers/disk/virtual/virtio_block/virtio_blk.h @@ -0,0 +1,117 @@ +/*- + * This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of IBM nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _VIRTIO_BLK_H +#define _VIRTIO_BLK_H + +/* Feature bits */ +#define VIRTIO_BLK_F_BARRIER 0x0001 /* Does host support barriers? */ +#define VIRTIO_BLK_F_SIZE_MAX 0x0002 /* Indicates maximum segment size */ +#define VIRTIO_BLK_F_SEG_MAX 0x0004 /* Indicates maximum # of segments */ +#define VIRTIO_BLK_F_GEOMETRY 0x0010 /* Legacy geometry available */ +#define VIRTIO_BLK_F_RO 0x0020 /* Disk is read-only */ +#define VIRTIO_BLK_F_BLK_SIZE 0x0040 /* Block size of disk is available*/ +#define VIRTIO_BLK_F_SCSI 0x0080 /* Supports scsi command passthru */ +#define VIRTIO_BLK_F_FLUSH 0x0200 /* Cache flush command support */ +#define VIRTIO_BLK_F_TOPOLOGY 0x0400 /* Topology information is available */ + +#define VIRTIO_BLK_ID_BYTES 20 /* ID string length */ + +struct virtio_blk_config { + /* The capacity (in 512-byte sectors). */ + uint64_t capacity; + /* The maximum segment size (if VIRTIO_BLK_F_SIZE_MAX) */ + uint32_t size_max; + /* The maximum number of segments (if VIRTIO_BLK_F_SEG_MAX) */ + uint32_t seg_max; + /* geometry the device (if VIRTIO_BLK_F_GEOMETRY) */ + struct virtio_blk_geometry { + uint16_t cylinders; + uint8_t heads; + uint8_t sectors; + } geometry; + + /* block size of device (if VIRTIO_BLK_F_BLK_SIZE) */ + uint32_t blk_size; +} __packed; + +/* + * Command types + * + * Usage is a bit tricky as some bits are used as flags and some are not. + * + * Rules: + * VIRTIO_BLK_T_OUT may be combined with VIRTIO_BLK_T_SCSI_CMD or + * VIRTIO_BLK_T_BARRIER. VIRTIO_BLK_T_FLUSH is a command of its own + * and may not be combined with any of the other flags. + */ + +/* These two define direction. */ +#define VIRTIO_BLK_T_IN 0 +#define VIRTIO_BLK_T_OUT 1 + +/* This bit says it's a scsi command, not an actual read or write. */ +#define VIRTIO_BLK_T_SCSI_CMD 2 + +/* Cache flush command */ +#define VIRTIO_BLK_T_FLUSH 4 + +/* Get device ID command */ +#define VIRTIO_BLK_T_GET_ID 8 + +/* Barrier before this op. */ +#define VIRTIO_BLK_T_BARRIER 0x80000000 + +/* ID string length */ +#define VIRTIO_BLK_ID_BYTES 20 + +/* This is the first element of the read scatter-gather list. */ +struct virtio_blk_outhdr { + /* VIRTIO_BLK_T* */ + uint32_t type; + /* io priority. */ + uint32_t ioprio; + /* Sector (ie. 512 byte offset) */ + uint64_t sector; +}; + +struct virtio_scsi_inhdr { + uint32_t errors; + uint32_t data_len; + uint32_t sense_len; + uint32_t residual; +}; + +/* And this is the final byte of the write scatter-gather list. */ +#define VIRTIO_BLK_S_OK 0 +#define VIRTIO_BLK_S_IOERR 1 +#define VIRTIO_BLK_S_UNSUPP 2 + +#endif /* _VIRTIO_BLK_H */ diff --git a/src/add-ons/kernel/drivers/disk/virtual/virtio_block/virtio_block.cpp b/src/add-ons/kernel/drivers/disk/virtual/virtio_block/virtio_block.cpp new file mode 100644 index 0000000..9198214 --- /dev/null +++ b/src/add-ons/kernel/drivers/disk/virtual/virtio_block/virtio_block.cpp @@ -0,0 +1,662 @@ +/* + * Copyright 2013, Jérôme Duval, korli@xxxxxxxxxxxxxxxx. + * Distributed under the terms of the MIT License. + */ + + +#include <virtio.h> + +#include "virtio_blk.h" + + +struct DMAResource; +struct IOScheduler; + + +static const uint8 kDriveIcon[] = { + 0x6e, 0x63, 0x69, 0x66, 0x08, 0x03, 0x01, 0x00, 0x00, 0x02, 0x00, 0x16, + 0x02, 0x3c, 0xc7, 0xee, 0x38, 0x9b, 0xc0, 0xba, 0x16, 0x57, 0x3e, 0x39, + 0xb0, 0x49, 0x77, 0xc8, 0x42, 0xad, 0xc7, 0x00, 0xff, 0xff, 0xd3, 0x02, + 0x00, 0x06, 0x02, 0x3c, 0x96, 0x32, 0x3a, 0x4d, 0x3f, 0xba, 0xfc, 0x01, + 0x3d, 0x5a, 0x97, 0x4b, 0x57, 0xa5, 0x49, 0x84, 0x4d, 0x00, 0x47, 0x47, + 0x47, 0xff, 0xa5, 0xa0, 0xa0, 0x02, 0x00, 0x16, 0x02, 0xbc, 0x59, 0x2f, + 0xbb, 0x29, 0xa7, 0x3c, 0x0c, 0xe4, 0xbd, 0x0b, 0x7c, 0x48, 0x92, 0xc0, + 0x4b, 0x79, 0x66, 0x00, 0x7d, 0xff, 0xd4, 0x02, 0x00, 0x06, 0x02, 0x38, + 0xdb, 0xb4, 0x39, 0x97, 0x33, 0xbc, 0x4a, 0x33, 0x3b, 0xa5, 0x42, 0x48, + 0x6e, 0x66, 0x49, 0xee, 0x7b, 0x00, 0x59, 0x67, 0x56, 0xff, 0xeb, 0xb2, + 0xb2, 0x03, 0xa7, 0xff, 0x00, 0x03, 0xff, 0x00, 0x00, 0x04, 0x01, 0x80, + 0x07, 0x0a, 0x06, 0x22, 0x3c, 0x22, 0x49, 0x44, 0x5b, 0x5a, 0x3e, 0x5a, + 0x31, 0x39, 0x25, 0x0a, 0x04, 0x22, 0x3c, 0x44, 0x4b, 0x5a, 0x31, 0x39, + 0x25, 0x0a, 0x04, 0x44, 0x4b, 0x44, 0x5b, 0x5a, 0x3e, 0x5a, 0x31, 0x0a, + 0x04, 0x22, 0x3c, 0x22, 0x49, 0x44, 0x5b, 0x44, 0x4b, 0x08, 0x02, 0x27, + 0x43, 0xb8, 0x14, 0xc1, 0xf1, 0x08, 0x02, 0x26, 0x43, 0x29, 0x44, 0x0a, + 0x05, 0x44, 0x5d, 0x49, 0x5d, 0x60, 0x3e, 0x5a, 0x3b, 0x5b, 0x3f, 0x08, + 0x0a, 0x07, 0x01, 0x06, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x10, 0x01, 0x17, + 0x84, 0x00, 0x04, 0x0a, 0x01, 0x01, 0x01, 0x00, 0x0a, 0x02, 0x01, 0x02, + 0x00, 0x0a, 0x03, 0x01, 0x03, 0x00, 0x0a, 0x04, 0x01, 0x04, 0x10, 0x01, + 0x17, 0x85, 0x20, 0x04, 0x0a, 0x06, 0x01, 0x05, 0x30, 0x24, 0xb3, 0x99, + 0x01, 0x17, 0x82, 0x00, 0x04, 0x0a, 0x05, 0x01, 0x05, 0x30, 0x20, 0xb2, + 0xe6, 0x01, 0x17, 0x82, 0x00, 0x04 +}; + + +#define VIRTIO_BLOCK_DRIVER_MODULE_NAME "drivers/disk/virtual/virtio_block/driver_v1" +#define VIRTIO_BLOCK_DEVICE_MODULE_NAME "drivers/disk/virtual/virtio_block/device_v1" +#define VIRTIO_BLOCK_DEVICE_ID_GENERATOR "virtio_block/device_id" + + +typedef struct { + device_node* node; + ::virtio_device virtio_device; + virtio_device_interface* virtio; + ::virtio_queue virtio_queue; + IOScheduler* io_scheduler; + DMAResource* dma_resource; + + struct virtio_blk_config config; + + uint32 features; + uint64 capacity; + uint32 block_size; + + sem_id sem_cb; +} virtio_block_driver_info; + + +typedef struct { + virtio_block_driver_info* info; +} virtio_block_handle; + + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <fs/devfs.h> + +#include "dma_resources.h" +#include "IORequest.h" +#include "IOSchedulerSimple.h" + + +//#define TRACE_VIRTIO_BLOCK +#ifdef TRACE_VIRTIO_BLOCK +# define TRACE(x...) dprintf("virtio_block: " x) +#else +# define TRACE(x...) ; +#endif +#define ERROR(x...) dprintf("\33[33mvirtio_block:\33[0m " x) +#define CALLED() TRACE("CALLED %s\n", __PRETTY_FUNCTION__) + + +static device_manager_info* sDeviceManager; + + +void virtio_block_set_capacity(virtio_block_driver_info* info, uint64 capacity, + uint32 blockSize); + + +const char * +get_feature_name(uint32 feature) +{ + switch (feature) { + case VIRTIO_BLK_F_BARRIER: + return "host barrier"; + case VIRTIO_BLK_F_SIZE_MAX: + return "maximum segment size"; + case VIRTIO_BLK_F_SEG_MAX: + return "maximum segment count"; + case VIRTIO_BLK_F_GEOMETRY: + return "disk geometry"; + case VIRTIO_BLK_F_RO: + return "read only"; + case VIRTIO_BLK_F_BLK_SIZE: + return "block size"; + case VIRTIO_BLK_F_SCSI: + return "scsi commands"; + case VIRTIO_BLK_F_FLUSH: + return "flush command"; + case VIRTIO_BLK_F_TOPOLOGY: + return "topology"; + } + return NULL; +} + + +static status_t +get_geometry(virtio_block_handle* handle, device_geometry* geometry) +{ + virtio_block_driver_info* info = handle->info; + + devfs_compute_geometry_size(geometry, info->capacity, info->block_size); + + geometry->device_type = B_DISK; + geometry->removable = false; + + geometry->read_only = ((info->features & VIRTIO_BLK_F_RO) != 0); + geometry->write_once = false; + + TRACE("virtio_block: get_geometry(): %ld, %ld, %ld, %ld, %d, %d, %d, %d\n", + geometry->bytes_per_sector, geometry->sectors_per_track, + geometry->cylinder_count, geometry->head_count, geometry->device_type, + geometry->removable, geometry->read_only, geometry->write_once); + + return B_OK; +} + + +static int +log2(uint32 x) +{ + int y; + + for (y = 31; y >= 0; --y) { + if (x == ((uint32)1 << y)) + break; + } + + return y; +} + + +static void +virtio_block_callback(void* cookie) +{ + virtio_block_driver_info* info = (virtio_block_driver_info*)cookie; + + release_sem(info->sem_cb); +} + + +static status_t +do_io(void* cookie, IOOperation* operation) +{ + virtio_block_driver_info* info = (virtio_block_driver_info*)cookie; + + size_t bytesTransferred = 0; + status_t status = B_OK; + + physical_entry entries[operation->VecCount() + 2]; + + void *buffer = malloc(sizeof(struct virtio_blk_outhdr) + sizeof(uint8)); + struct virtio_blk_outhdr *header = (struct virtio_blk_outhdr*)buffer; + header->type = operation->IsWrite() ? VIRTIO_BLK_T_OUT : VIRTIO_BLK_T_IN; + header->sector = operation->Offset() / 512; + header->ioprio = 1; + + uint8* ack = (uint8*)buffer + sizeof(struct virtio_blk_outhdr); + *ack = 0xff; + + get_memory_map(buffer, sizeof(struct virtio_blk_outhdr) + sizeof(uint8), + &entries[0], 1); + entries[operation->VecCount() + 1].address = entries[0].address + + sizeof(struct virtio_blk_outhdr); + entries[operation->VecCount() + 1].size = sizeof(uint8); + entries[0].size = sizeof(struct virtio_blk_outhdr); + + memcpy(entries + 1, operation->Vecs(), operation->VecCount() + * sizeof(physical_entry)); + + info->virtio->queue_request_v(info->virtio_queue, entries, + 1 + (operation->IsWrite() ? operation->VecCount() : 0 ), + 1 + (operation->IsWrite() ? 0 : operation->VecCount()), + virtio_block_callback, info); + + acquire_sem(info->sem_cb); + + switch (*ack) { + case VIRTIO_BLK_S_OK: + status = B_OK; + bytesTransferred = operation->Length(); + break; + case VIRTIO_BLK_S_UNSUPP: + status = ENOTSUP; + break; + default: + status = EIO; + break; + } + free(buffer); + + info->io_scheduler->OperationCompleted(operation, status, + bytesTransferred); + return status; +} + + +// #pragma mark - device module API + + +static status_t +virtio_block_init_device(void* _info, void** _cookie) +{ + CALLED(); + virtio_block_driver_info* info = (virtio_block_driver_info*)_info; + + device_node* parent = sDeviceManager->get_parent_node(info->node); + sDeviceManager->get_driver(parent, (driver_module_info **)&info->virtio, + (void **)&info->virtio_device); + sDeviceManager->put_node(parent); + + info->virtio->negociate_features(info->virtio_device, + VIRTIO_BLK_F_BARRIER | VIRTIO_BLK_F_SIZE_MAX + | VIRTIO_BLK_F_SEG_MAX | VIRTIO_BLK_F_GEOMETRY + | VIRTIO_BLK_F_RO | VIRTIO_BLK_F_BLK_SIZE + | VIRTIO_BLK_F_FLUSH | VIRTIO_FEATURE_RING_INDIRECT_DESC, + &info->features, &get_feature_name); + + status_t status = info->virtio->read_device_config( + info->virtio_device, 0, &info->config, + sizeof(struct virtio_blk_config)); + if (status != B_OK) + return status; + + // and get (initial) capacity + uint32 block_size = 512; + if ((info->features & VIRTIO_BLK_F_BLK_SIZE) != 0) + block_size = info->config.blk_size; + uint64 capacity = info->config.capacity * 512 / block_size; + + virtio_block_set_capacity(info, capacity, block_size); + + TRACE("virtio_block: capacity: %" B_PRIu64 ", block_size %" B_PRIu32 "\n", + info->capacity, info->block_size); + + status = info->virtio->alloc_queues(info->virtio_device, 1, + &info->virtio_queue); + if (status != B_OK) { + ERROR("queue allocation failed (%s)\n", strerror(status)); + return status; + } + status = info->virtio->setup_interrupt(info->virtio_device, NULL, NULL); + + *_cookie = info; + return status; +} + + +static void +virtio_block_uninit_device(void* _cookie) +{ + CALLED(); + virtio_block_driver_info* info = (virtio_block_driver_info*)_cookie; + + delete info->io_scheduler; + delete info->dma_resource; +} + + +static status_t +virtio_block_open(void* _info, const char* path, int openMode, void** _cookie) +{ + CALLED(); + virtio_block_driver_info* info = (virtio_block_driver_info*)_info; + + virtio_block_handle* handle = (virtio_block_handle*)malloc( + sizeof(virtio_block_handle)); + if (handle == NULL) + return B_NO_MEMORY; + + handle->info = info; + + *_cookie = handle; + return B_OK; +} + + +static status_t +virtio_block_close(void* cookie) +{ + //virtio_block_handle* handle = (virtio_block_handle*)cookie; + CALLED(); + + return B_OK; +} + + +static status_t +virtio_block_free(void* cookie) +{ + CALLED(); + virtio_block_handle* handle = (virtio_block_handle*)cookie; + + free(handle); + return B_OK; +} + + +static status_t +virtio_block_read(void* cookie, off_t pos, void* buffer, size_t* _length) +{ + CALLED(); + virtio_block_handle* handle = (virtio_block_handle*)cookie; + size_t length = *_length; + + IORequest request; + status_t status = request.Init(pos, (addr_t)buffer, length, false, 0); + if (status != B_OK) + return status; + + status = handle->info->io_scheduler->ScheduleRequest(&request); + if (status != B_OK) + return status; + + status = request.Wait(0, 0); + if (status == B_OK) + *_length = length; + else + dprintf("read(): request.Wait() returned: %s\n", strerror(status)); + + return status; +} + + +static status_t +virtio_block_write(void* cookie, off_t pos, const void* buffer, + size_t* _length) +{ + CALLED(); + virtio_block_handle* handle = (virtio_block_handle*)cookie; + size_t length = *_length; + + IORequest request; + status_t status = request.Init(pos, (addr_t)buffer, length, true, 0); + if (status != B_OK) + return status; + + status = handle->info->io_scheduler->ScheduleRequest(&request); + if (status != B_OK) + return status; + + status = request.Wait(0, 0); + if (status == B_OK) + *_length = length; + else + dprintf("write(): request.Wait() returned: %s\n", strerror(status)); + + return status; +} + + +static status_t +virtio_block_io(void *cookie, io_request *request) +{ + CALLED(); + virtio_block_handle* handle = (virtio_block_handle*)cookie; + + return handle->info->io_scheduler->ScheduleRequest(request); [ *** diff truncated: 275 lines dropped *** ] ############################################################################ Revision: hrev45714 Commit: 78b461d6a11f062c7a61246f64d1e980612c8237 URL: http://cgit.haiku-os.org/haiku/commit/?id=78b461d Author: Jérôme Duval <jerome.duval@xxxxxxxxx> Date: Sun May 26 15:12:25 2013 UTC Merge branch 'virtio' ----------------------------------------------------------------------------