Merge branch 'virtio'
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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_ */
|
||||
@@ -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 ;
|
||||
|
||||
@@ -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
|
||||
;
|
||||
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Copyright 2013, Jérôme Duval, [email protected].
|
||||
* 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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Copyright 2013, Jérôme Duval, [email protected].
|
||||
* 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
|
||||
};
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright 2013, Jérôme Duval, korli@users.berlios.de.
|
||||
* 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
|
||||
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* Copyright 2013, Jérôme Duval, korli@users.berlios.de.
|
||||
* 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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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 ;
|
||||
|
||||
@@ -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 ] ;
|
||||
|
||||
@@ -0,0 +1,480 @@
|
||||
/*
|
||||
* Copyright 2013, Jérôme Duval, korli@users.berlios.de.
|
||||
* 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
|
||||
};
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*-
|
||||
* Copyright IBM Corp. 2007
|
||||
*
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.com>
|
||||
*
|
||||
* 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 */
|
||||
@@ -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 ;
|
||||
|
||||
@@ -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
|
||||
;
|
||||
@@ -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 */
|
||||
@@ -0,0 +1,662 @@
|
||||
/*
|
||||
* Copyright 2013, Jérôme Duval, korli@users.berlios.de.
|
||||
* 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);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
virtio_block_ioctl(void* cookie, uint32 op, void* buffer, size_t length)
|
||||
{
|
||||
CALLED();
|
||||
virtio_block_handle* handle = (virtio_block_handle*)cookie;
|
||||
virtio_block_driver_info* info = handle->info;
|
||||
|
||||
TRACE("ioctl(op = %ld)\n", op);
|
||||
|
||||
switch (op) {
|
||||
case B_GET_DEVICE_SIZE:
|
||||
{
|
||||
size_t size = info->capacity * info->block_size;
|
||||
return user_memcpy(buffer, &size, sizeof(size_t));
|
||||
}
|
||||
|
||||
case B_GET_GEOMETRY:
|
||||
{
|
||||
if (buffer == NULL /*|| length != sizeof(device_geometry)*/)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
device_geometry geometry;
|
||||
status_t status = get_geometry(handle, &geometry);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
return user_memcpy(buffer, &geometry, sizeof(device_geometry));
|
||||
}
|
||||
|
||||
case B_GET_ICON_NAME:
|
||||
return user_strlcpy((char*)buffer, "devices/drive-harddisk",
|
||||
B_FILE_NAME_LENGTH);
|
||||
|
||||
case B_GET_VECTOR_ICON:
|
||||
{
|
||||
// TODO: take device type into account!
|
||||
device_icon iconData;
|
||||
if (length != sizeof(device_icon))
|
||||
return B_BAD_VALUE;
|
||||
if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK)
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
if (iconData.icon_size >= (int32)sizeof(kDriveIcon)) {
|
||||
if (user_memcpy(iconData.icon_data, kDriveIcon,
|
||||
sizeof(kDriveIcon)) != B_OK)
|
||||
return B_BAD_ADDRESS;
|
||||
}
|
||||
|
||||
iconData.icon_size = sizeof(kDriveIcon);
|
||||
return user_memcpy(buffer, &iconData, sizeof(device_icon));
|
||||
}
|
||||
|
||||
/*case B_FLUSH_DRIVE_CACHE:
|
||||
return synchronize_cache(info);*/
|
||||
}
|
||||
|
||||
return B_DEV_INVALID_IOCTL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
virtio_block_set_capacity(virtio_block_driver_info* info, uint64 capacity,
|
||||
uint32 blockSize)
|
||||
{
|
||||
TRACE("set_capacity(device = %p, capacity = %Ld, blockSize = %ld)\n",
|
||||
info, capacity, blockSize);
|
||||
|
||||
// get log2, if possible
|
||||
uint32 blockShift = log2(blockSize);
|
||||
|
||||
if ((1UL << blockShift) != blockSize)
|
||||
blockShift = 0;
|
||||
|
||||
info->capacity = capacity;
|
||||
|
||||
if (info->block_size != blockSize) {
|
||||
if (info->block_size != 0) {
|
||||
ERROR("old %" B_PRId32 ", new %" B_PRId32 "\n", info->block_size,
|
||||
blockSize);
|
||||
panic("updating DMAResource not yet implemented...");
|
||||
}
|
||||
|
||||
dma_restrictions restrictions;
|
||||
memset(&restrictions, 0, sizeof(restrictions));
|
||||
if ((info->features & VIRTIO_BLK_F_SIZE_MAX) != 0)
|
||||
restrictions.max_segment_size = info->config.size_max;
|
||||
if ((info->features & VIRTIO_BLK_F_SEG_MAX) != 0)
|
||||
restrictions.max_segment_count = info->config.seg_max;
|
||||
|
||||
// TODO: we need to replace the DMAResource in our IOScheduler
|
||||
status_t status = info->dma_resource->Init(restrictions, blockSize,
|
||||
1024, 32);
|
||||
if (status != B_OK)
|
||||
panic("initializing DMAResource failed: %s", strerror(status));
|
||||
|
||||
info->io_scheduler = new(std::nothrow) IOSchedulerSimple(
|
||||
info->dma_resource);
|
||||
if (info->io_scheduler == NULL)
|
||||
panic("allocating IOScheduler failed.");
|
||||
|
||||
// TODO: use whole device name here
|
||||
status = info->io_scheduler->Init("virtio");
|
||||
if (status != B_OK)
|
||||
panic("initializing IOScheduler failed: %s", strerror(status));
|
||||
|
||||
info->io_scheduler->SetCallback(do_io, info);
|
||||
}
|
||||
|
||||
info->block_size = blockSize;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - driver module API
|
||||
|
||||
|
||||
static float
|
||||
virtio_block_supports_device(device_node *parent)
|
||||
{
|
||||
CALLED();
|
||||
const char *bus;
|
||||
uint16 deviceType;
|
||||
|
||||
// make sure parent is really the Virtio bus manager
|
||||
if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
|
||||
return -1;
|
||||
|
||||
if (strcmp(bus, "virtio"))
|
||||
return 0.0;
|
||||
|
||||
// check whether it's really a Direct Access Device
|
||||
if (sDeviceManager->get_attr_uint16(parent, VIRTIO_DEVICE_TYPE_ITEM,
|
||||
&deviceType, true) != B_OK || deviceType != VIRTIO_DEVICE_ID_BLOCK)
|
||||
return 0.0;
|
||||
|
||||
TRACE("Virtio block device found!\n");
|
||||
|
||||
return 0.6;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
virtio_block_register_device(device_node *node)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
// ready to register
|
||||
device_attr attrs[] = {
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
return sDeviceManager->register_node(node, VIRTIO_BLOCK_DRIVER_MODULE_NAME,
|
||||
attrs, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
virtio_block_init_driver(device_node *node, void **cookie)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
virtio_block_driver_info* info = (virtio_block_driver_info*)malloc(
|
||||
sizeof(virtio_block_driver_info));
|
||||
if (info == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
memset(info, 0, sizeof(*info));
|
||||
|
||||
info->dma_resource = new(std::nothrow) DMAResource;
|
||||
if (info->dma_resource == NULL) {
|
||||
free(info);
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
info->sem_cb = create_sem(0, "virtio_block_cb");
|
||||
if (info->sem_cb < 0) {
|
||||
delete info->dma_resource;
|
||||
status_t status = info->sem_cb;
|
||||
free(info);
|
||||
return status;
|
||||
}
|
||||
info->node = node;
|
||||
|
||||
*cookie = info;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
virtio_block_uninit_driver(void *_cookie)
|
||||
{
|
||||
CALLED();
|
||||
virtio_block_driver_info* info = (virtio_block_driver_info*)_cookie;
|
||||
delete_sem(info->sem_cb);
|
||||
free(info);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
virtio_block_register_child_devices(void* _cookie)
|
||||
{
|
||||
CALLED();
|
||||
virtio_block_driver_info* info = (virtio_block_driver_info*)_cookie;
|
||||
status_t status;
|
||||
|
||||
int32 id = sDeviceManager->create_id(VIRTIO_BLOCK_DEVICE_ID_GENERATOR);
|
||||
if (id < 0)
|
||||
return id;
|
||||
|
||||
char name[64];
|
||||
snprintf(name, sizeof(name), "disk/virtual/virtio_block/%" B_PRId32 "/raw",
|
||||
id);
|
||||
|
||||
status = sDeviceManager->publish_device(info->node, name,
|
||||
VIRTIO_BLOCK_DEVICE_MODULE_NAME);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
module_dependency module_dependencies[] = {
|
||||
{B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager},
|
||||
{}
|
||||
};
|
||||
|
||||
struct device_module_info sVirtioBlockDevice = {
|
||||
{
|
||||
VIRTIO_BLOCK_DEVICE_MODULE_NAME,
|
||||
0,
|
||||
NULL
|
||||
},
|
||||
|
||||
virtio_block_init_device,
|
||||
virtio_block_uninit_device,
|
||||
NULL, // remove,
|
||||
|
||||
virtio_block_open,
|
||||
virtio_block_close,
|
||||
virtio_block_free,
|
||||
virtio_block_read,
|
||||
virtio_block_write,
|
||||
virtio_block_io,
|
||||
virtio_block_ioctl,
|
||||
|
||||
NULL, // select
|
||||
NULL, // deselect
|
||||
};
|
||||
|
||||
struct driver_module_info sVirtioBlockDriver = {
|
||||
{
|
||||
VIRTIO_BLOCK_DRIVER_MODULE_NAME,
|
||||
0,
|
||||
NULL
|
||||
},
|
||||
|
||||
virtio_block_supports_device,
|
||||
virtio_block_register_device,
|
||||
virtio_block_init_driver,
|
||||
virtio_block_uninit_driver,
|
||||
virtio_block_register_child_devices,
|
||||
NULL, // rescan
|
||||
NULL, // removed
|
||||
};
|
||||
|
||||
module_info* modules[] = {
|
||||
(module_info*)&sVirtioBlockDriver,
|
||||
(module_info*)&sVirtioBlockDevice,
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user