mirror of
https://github.com/reactos/reactos.git
synced 2024-09-06 02:37:06 +00:00
284 lines
10 KiB
C
284 lines
10 KiB
C
![]() |
/*
|
||
|
* Virtio PCI driver - legacy (virtio 0.9) device support
|
||
|
*
|
||
|
* Copyright IBM Corp. 2007
|
||
|
*
|
||
|
* Authors:
|
||
|
* Anthony Liguori <aliguori@us.ibm.com>
|
||
|
* Windows porting - Yan Vugenfirer <yvugenfi@redhat.com>
|
||
|
*
|
||
|
* 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 names of the copyright holders nor the names of their 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 THE COPYRIGHT HOLDERS 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.
|
||
|
*/
|
||
|
#include "osdep.h"
|
||
|
#include "virtio_pci.h"
|
||
|
#include "VirtIO.h"
|
||
|
#include "kdebugprint.h"
|
||
|
#include "virtio_ring.h"
|
||
|
#include "virtio_pci_common.h"
|
||
|
#include "windows/virtio_ring_allocation.h"
|
||
|
|
||
|
#ifdef WPP_EVENT_TRACING
|
||
|
#include "VirtIOPCILegacy.tmh"
|
||
|
#endif
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// vio_legacy_dump_registers - Dump HW registers of the device
|
||
|
//
|
||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||
|
void vio_legacy_dump_registers(VirtIODevice *vdev)
|
||
|
{
|
||
|
DPrintf(5, "%s\n", __FUNCTION__);
|
||
|
|
||
|
DPrintf(0, "[VIRTIO_PCI_HOST_FEATURES] = %x\n", ioread32(vdev, vdev->addr + VIRTIO_PCI_HOST_FEATURES));
|
||
|
DPrintf(0, "[VIRTIO_PCI_GUEST_FEATURES] = %x\n", ioread32(vdev, vdev->addr + VIRTIO_PCI_GUEST_FEATURES));
|
||
|
DPrintf(0, "[VIRTIO_PCI_QUEUE_PFN] = %x\n", ioread32(vdev, vdev->addr + VIRTIO_PCI_QUEUE_PFN));
|
||
|
DPrintf(0, "[VIRTIO_PCI_QUEUE_NUM] = %x\n", ioread32(vdev, vdev->addr + VIRTIO_PCI_QUEUE_NUM));
|
||
|
DPrintf(0, "[VIRTIO_PCI_QUEUE_SEL] = %x\n", ioread32(vdev, vdev->addr + VIRTIO_PCI_QUEUE_SEL));
|
||
|
DPrintf(0, "[VIRTIO_PCI_QUEUE_NOTIFY] = %x\n", ioread32(vdev, vdev->addr + VIRTIO_PCI_QUEUE_NOTIFY));
|
||
|
DPrintf(0, "[VIRTIO_PCI_STATUS] = %x\n", ioread32(vdev, vdev->addr + VIRTIO_PCI_STATUS));
|
||
|
DPrintf(0, "[VIRTIO_PCI_ISR] = %x\n", ioread32(vdev, vdev->addr + VIRTIO_PCI_ISR));
|
||
|
}
|
||
|
|
||
|
static void vio_legacy_get_config(VirtIODevice * vdev,
|
||
|
unsigned offset,
|
||
|
void *buf,
|
||
|
unsigned len)
|
||
|
{
|
||
|
ULONG_PTR ioaddr = vdev->addr + VIRTIO_PCI_CONFIG(vdev->msix_used) + offset;
|
||
|
u8 *ptr = buf;
|
||
|
unsigned i;
|
||
|
|
||
|
DPrintf(5, "%s\n", __FUNCTION__);
|
||
|
|
||
|
for (i = 0; i < len; i++) {
|
||
|
ptr[i] = ioread8(vdev, ioaddr + i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void vio_legacy_set_config(VirtIODevice *vdev,
|
||
|
unsigned offset,
|
||
|
const void *buf,
|
||
|
unsigned len)
|
||
|
{
|
||
|
ULONG_PTR ioaddr = vdev->addr + VIRTIO_PCI_CONFIG(vdev->msix_used) + offset;
|
||
|
const u8 *ptr = buf;
|
||
|
unsigned i;
|
||
|
|
||
|
DPrintf(5, "%s\n", __FUNCTION__);
|
||
|
|
||
|
for (i = 0; i < len; i++) {
|
||
|
iowrite8(vdev, ptr[i], ioaddr + i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static u8 vio_legacy_get_status(VirtIODevice *vdev)
|
||
|
{
|
||
|
DPrintf(6, "%s\n", __FUNCTION__);
|
||
|
return ioread8(vdev, vdev->addr + VIRTIO_PCI_STATUS);
|
||
|
}
|
||
|
|
||
|
static void vio_legacy_set_status(VirtIODevice *vdev, u8 status)
|
||
|
{
|
||
|
DPrintf(6, "%s>>> %x\n", __FUNCTION__, status);
|
||
|
iowrite8(vdev, status, vdev->addr + VIRTIO_PCI_STATUS);
|
||
|
}
|
||
|
|
||
|
static void vio_legacy_reset(VirtIODevice *vdev)
|
||
|
{
|
||
|
/* 0 status means a reset. */
|
||
|
iowrite8(vdev, 0, vdev->addr + VIRTIO_PCI_STATUS);
|
||
|
}
|
||
|
|
||
|
static u64 vio_legacy_get_features(VirtIODevice *vdev)
|
||
|
{
|
||
|
return ioread32(vdev, vdev->addr + VIRTIO_PCI_HOST_FEATURES);
|
||
|
}
|
||
|
|
||
|
static NTSTATUS vio_legacy_set_features(VirtIODevice *vdev, u64 features)
|
||
|
{
|
||
|
/* Give virtio_ring a chance to accept features. */
|
||
|
vring_transport_features(vdev, &features);
|
||
|
|
||
|
/* Make sure we don't have any features > 32 bits! */
|
||
|
ASSERT((u32)features == features);
|
||
|
iowrite32(vdev, (u32)features, vdev->addr + VIRTIO_PCI_GUEST_FEATURES);
|
||
|
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
static u16 vio_legacy_set_config_vector(VirtIODevice *vdev, u16 vector)
|
||
|
{
|
||
|
/* Setup the vector used for configuration events */
|
||
|
iowrite16(vdev, vector, vdev->addr + VIRTIO_MSI_CONFIG_VECTOR);
|
||
|
/* Verify we had enough resources to assign the vector */
|
||
|
/* Will also flush the write out to device */
|
||
|
return ioread16(vdev, vdev->addr + VIRTIO_MSI_CONFIG_VECTOR);
|
||
|
}
|
||
|
|
||
|
static u16 vio_legacy_set_queue_vector(struct virtqueue *vq, u16 vector)
|
||
|
{
|
||
|
VirtIODevice *vdev = vq->vdev;
|
||
|
|
||
|
iowrite16(vdev, (u16)vq->index, vdev->addr + VIRTIO_PCI_QUEUE_SEL);
|
||
|
iowrite16(vdev, vector, vdev->addr + VIRTIO_MSI_QUEUE_VECTOR);
|
||
|
return ioread16(vdev, vdev->addr + VIRTIO_MSI_QUEUE_VECTOR);
|
||
|
}
|
||
|
|
||
|
static NTSTATUS vio_legacy_query_vq_alloc(VirtIODevice *vdev,
|
||
|
unsigned index,
|
||
|
unsigned short *pNumEntries,
|
||
|
unsigned long *pRingSize,
|
||
|
unsigned long *pHeapSize)
|
||
|
{
|
||
|
unsigned long ring_size, data_size;
|
||
|
u16 num;
|
||
|
|
||
|
/* Select the queue we're interested in */
|
||
|
iowrite16(vdev, (u16)index, vdev->addr + VIRTIO_PCI_QUEUE_SEL);
|
||
|
|
||
|
/* Check if queue is either not available or already active. */
|
||
|
num = ioread16(vdev, vdev->addr + VIRTIO_PCI_QUEUE_NUM);
|
||
|
if (!num || ioread32(vdev, vdev->addr + VIRTIO_PCI_QUEUE_PFN)) {
|
||
|
return STATUS_NOT_FOUND;
|
||
|
}
|
||
|
|
||
|
ring_size = ROUND_TO_PAGES(vring_size(num, VIRTIO_PCI_VRING_ALIGN, false));
|
||
|
data_size = ROUND_TO_PAGES(vring_control_block_size(num, false));
|
||
|
|
||
|
*pNumEntries = num;
|
||
|
*pRingSize = ring_size + data_size;
|
||
|
*pHeapSize = 0;
|
||
|
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
static NTSTATUS vio_legacy_setup_vq(struct virtqueue **queue,
|
||
|
VirtIODevice *vdev,
|
||
|
VirtIOQueueInfo *info,
|
||
|
unsigned index,
|
||
|
u16 msix_vec)
|
||
|
{
|
||
|
struct virtqueue *vq;
|
||
|
unsigned long ring_size, heap_size;
|
||
|
NTSTATUS status;
|
||
|
|
||
|
/* Select the queue and query allocation parameters */
|
||
|
status = vio_legacy_query_vq_alloc(vdev, index, &info->num, &ring_size, &heap_size);
|
||
|
if (!NT_SUCCESS(status)) {
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
info->queue = mem_alloc_contiguous_pages(vdev, ring_size);
|
||
|
if (info->queue == NULL) {
|
||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
||
|
}
|
||
|
|
||
|
/* activate the queue */
|
||
|
iowrite32(vdev, (u32)(mem_get_physical_address(vdev, info->queue) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT),
|
||
|
vdev->addr + VIRTIO_PCI_QUEUE_PFN);
|
||
|
|
||
|
/* create the vring */
|
||
|
vq = vring_new_virtqueue_split(index, info->num,
|
||
|
VIRTIO_PCI_VRING_ALIGN, vdev,
|
||
|
info->queue, vp_notify,
|
||
|
(u8 *)info->queue + ROUND_TO_PAGES(vring_size(info->num, VIRTIO_PCI_VRING_ALIGN, false)));
|
||
|
if (!vq) {
|
||
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
goto err_activate_queue;
|
||
|
}
|
||
|
|
||
|
vq->notification_addr = (void *)(vdev->addr + VIRTIO_PCI_QUEUE_NOTIFY);
|
||
|
|
||
|
if (msix_vec != VIRTIO_MSI_NO_VECTOR) {
|
||
|
msix_vec = vdev->device->set_queue_vector(vq, msix_vec);
|
||
|
if (msix_vec == VIRTIO_MSI_NO_VECTOR) {
|
||
|
status = STATUS_DEVICE_BUSY;
|
||
|
goto err_assign;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*queue = vq;
|
||
|
return STATUS_SUCCESS;
|
||
|
|
||
|
err_assign:
|
||
|
err_activate_queue:
|
||
|
iowrite32(vdev, 0, vdev->addr + VIRTIO_PCI_QUEUE_PFN);
|
||
|
mem_free_contiguous_pages(vdev, info->queue);
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
static void vio_legacy_del_vq(VirtIOQueueInfo *info)
|
||
|
{
|
||
|
struct virtqueue *vq = info->vq;
|
||
|
VirtIODevice *vdev = vq->vdev;
|
||
|
|
||
|
iowrite16(vdev, (u16)vq->index, vdev->addr + VIRTIO_PCI_QUEUE_SEL);
|
||
|
|
||
|
if (vdev->msix_used) {
|
||
|
iowrite16(vdev, VIRTIO_MSI_NO_VECTOR,
|
||
|
vdev->addr + VIRTIO_MSI_QUEUE_VECTOR);
|
||
|
/* Flush the write out to device */
|
||
|
ioread8(vdev, vdev->addr + VIRTIO_PCI_ISR);
|
||
|
}
|
||
|
|
||
|
/* Select and deactivate the queue */
|
||
|
iowrite32(vdev, 0, vdev->addr + VIRTIO_PCI_QUEUE_PFN);
|
||
|
|
||
|
mem_free_contiguous_pages(vdev, info->queue);
|
||
|
}
|
||
|
|
||
|
static const struct virtio_device_ops virtio_pci_device_ops = {
|
||
|
/* .get_config = */ vio_legacy_get_config,
|
||
|
/* .set_config = */ vio_legacy_set_config,
|
||
|
/* .get_config_generation = */ NULL,
|
||
|
/* .get_status = */ vio_legacy_get_status,
|
||
|
/* .set_status = */ vio_legacy_set_status,
|
||
|
/* .reset = */ vio_legacy_reset,
|
||
|
/* .get_features = */ vio_legacy_get_features,
|
||
|
/* .set_features = */ vio_legacy_set_features,
|
||
|
/* .set_config_vector = */ vio_legacy_set_config_vector,
|
||
|
/* .set_queue_vector = */ vio_legacy_set_queue_vector,
|
||
|
/* .query_queue_alloc = */ vio_legacy_query_vq_alloc,
|
||
|
/* .setup_queue = */ vio_legacy_setup_vq,
|
||
|
/* .delete_queue = */ vio_legacy_del_vq,
|
||
|
};
|
||
|
|
||
|
/* Legacy device initialization */
|
||
|
NTSTATUS vio_legacy_initialize(VirtIODevice *vdev)
|
||
|
{
|
||
|
size_t length = pci_get_resource_len(vdev, 0);
|
||
|
vdev->addr = (ULONG_PTR)pci_map_address_range(vdev, 0, 0, length);
|
||
|
|
||
|
if (!vdev->addr) {
|
||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
||
|
}
|
||
|
|
||
|
vdev->isr = (u8 *)vdev->addr + VIRTIO_PCI_ISR;
|
||
|
|
||
|
vdev->device = &virtio_pci_device_ops;
|
||
|
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|