/*
* Copyright 2013, 2018, Jérôme Duval, jerome.duval@gmail.com.
* 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),
fAlignment(0),
fConfigHandler(NULL),
fDriverCookie(NULL)
{
CALLED();
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()
{
if (fQueues != NULL) {
_DestroyQueues(fQueueCount);
}
fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_RESET);
}
status_t
VirtioDevice::InitCheck()
{
return fStatus;
}
status_t
VirtioDevice::NegotiateFeatures(uint32 supported, uint32* negotiated,
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);
*negotiated = fFeatures;
_DumpFeatures("negotiated 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;
fQueues = new(std::nothrow) VirtioQueue*[count];
if (fQueues == NULL)
return B_NO_MEMORY;
status_t status = B_OK;
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) {
_DestroyQueues(index + 1);
return status;
}
}
return B_OK;
}
void
VirtioDevice::FreeQueues()
{
if (fQueues != NULL)
_DestroyQueues(fQueueCount);
fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_RESET);
fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER);
}
status_t
VirtioDevice::SetupInterrupt(virtio_intr_func configHandler, void *driverCookie)
{
fConfigHandler = configHandler;
fDriverCookie = driverCookie;
status_t status = fController->setup_interrupt(fCookie, fQueueCount);
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::FreeInterrupts()
{
for (size_t index = 0; index < fQueueCount; index++)
fQueues[index]->DisableInterrupt();
fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER);
return fController->free_interrupt(fCookie);
}
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(fDriverCookie);
return B_OK;
}
void
VirtioDevice::_DestroyQueues(size_t count)
{
for (size_t i = 0; i < count; i++) {
delete fQueues[i];
}
delete[] fQueues;
fQueues = NULL;
}
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) {
strncat(features_string, "[", sizeof(features_string));
strncat(features_string, name, sizeof(features_string));
strncat(features_string, "] ", sizeof(features_string));
}
}
TRACE("%s: %s\n", title, features_string);
}
↑ V645 The 'strncat' function call could lead to the 'features_string' buffer overflow. The bounds should not contain the size of the buffer, but a number of characters it can hold.
↑ V645 The 'strncat' function call could lead to the 'features_string' buffer overflow. The bounds should not contain the size of the buffer, but a number of characters it can hold.
↑ V645 The 'strncat' function call could lead to the 'features_string' buffer overflow. The bounds should not contain the size of the buffer, but a number of characters it can hold.