[haiku-commits] haiku: hrev45714 - src/add-ons/kernel/bus_managers/virtio src/add-ons/kernel/drivers/disk/virtual/virtio_block src/add-ons/kernel/busses/virtio headers/private/virtio

  • From: korli@xxxxxxxxxxxxxxxx
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Sun, 26 May 2013 17:13:33 +0200 (CEST)

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'

----------------------------------------------------------------------------


Other related posts: