From bf2c2183c6292e8052cdf47c743e0b63f248f6ab Mon Sep 17 00:00:00 2001 From: Andreas Sandberg Date: Sat, 20 Sep 2014 17:17:51 -0400 Subject: dev, pci: Implement basic VirtIO support This patch adds support for VirtIO over the PCI bus. It does so by providing the following new SimObjects: * VirtIODeviceBase - Abstract base class for VirtIO devices. * PciVirtIO - VirtIO PCI transport interface. A VirtIO device is hooked up to the guest system by adding a PciVirtIO device to the PCI bus and connecting it to a VirtIO device using the vio parameter. New VirtIO devices should inherit from VirtIODevice base and implementing one or more VirtQueues. The VirtQueues are usually device-specific and all derive from the VirtQueue class. Queues must be registered with the base class from the constructor since the device assumes that the number of queues stay constant. --- src/dev/virtio/pci.cc | 222 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 src/dev/virtio/pci.cc (limited to 'src/dev/virtio/pci.cc') diff --git a/src/dev/virtio/pci.cc b/src/dev/virtio/pci.cc new file mode 100644 index 000000000..399590c1b --- /dev/null +++ b/src/dev/virtio/pci.cc @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2014 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * 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; + * neither the name of the copyright holders 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 THE COPYRIGHT + * OWNER 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. + * + * Authors: Andreas Sandberg + */ + +#include "debug/VIOPci.hh" +#include "dev/virtio/pci.hh" +#include "mem/packet_access.hh" +#include "params/PciVirtIO.hh" + +PciVirtIO::PciVirtIO(const Params *params) + : PciDevice(params), vio(*params->vio), + callbackKick(this) +{ + // Override the subsystem ID with the device ID from VirtIO + config.subsystemID = htole(vio.deviceId); + BARSize[0] = BAR0_SIZE_BASE + vio.configSize; + + vio.registerKickCallback(&callbackKick); +} + +PciVirtIO::~PciVirtIO() +{ +} + +Tick +PciVirtIO::read(PacketPtr pkt) +{ + const unsigned M5_VAR_USED size(pkt->getSize()); + int bar; + Addr offset; + if (!getBAR(pkt->getAddr(), bar, offset)) + panic("Invalid PCI memory access to unmapped memory.\n"); + assert(bar == 0); + + DPRINTF(VIOPci, "Reading offset 0x%x [len: %i]\n", offset, size); + + // Forward device configuration writes to the device VirtIO model + if (offset >= OFF_VIO_DEVICE) { + vio.readConfig(pkt, offset - OFF_VIO_DEVICE); + return 0; + } + + pkt->allocate(); + + switch(offset) { + case OFF_DEVICE_FEATURES: + DPRINTF(VIOPci, " DEVICE_FEATURES request\n"); + assert(size == sizeof(uint32_t)); + pkt->set(vio.deviceFeatures); + break; + + case OFF_GUEST_FEATURES: + DPRINTF(VIOPci, " GUEST_FEATURES request\n"); + assert(size == sizeof(uint32_t)); + pkt->set(vio.getGuestFeatures()); + break; + + case OFF_QUEUE_ADDRESS: + DPRINTF(VIOPci, " QUEUE_ADDRESS request\n"); + assert(size == sizeof(uint32_t)); + pkt->set(vio.getQueueAddress()); + break; + + case OFF_QUEUE_SIZE: + DPRINTF(VIOPci, " QUEUE_SIZE request\n"); + assert(size == sizeof(uint16_t)); + pkt->set(vio.getQueueSize()); + break; + + case OFF_QUEUE_SELECT: + DPRINTF(VIOPci, " QUEUE_SELECT\n"); + assert(size == sizeof(uint16_t)); + pkt->set(vio.getQueueSelect()); + break; + + case OFF_QUEUE_NOTIFY: + DPRINTF(VIOPci, " QUEUE_NOTIFY request\n"); + assert(size == sizeof(uint16_t)); + pkt->set(queueNotify); + break; + + case OFF_DEVICE_STATUS: + DPRINTF(VIOPci, " DEVICE_STATUS request\n"); + assert(size == sizeof(uint8_t)); + pkt->set(vio.getDeviceStatus()); + break; + + case OFF_ISR_STATUS: { + DPRINTF(VIOPci, " ISR_STATUS\n"); + assert(size == sizeof(uint8_t)); + uint8_t isr_status(interruptDeliveryPending ? 1 : 0); + interruptDeliveryPending = false; + pkt->set(isr_status); + } break; + + default: + panic("Unhandled read offset (0x%x)\n", offset); + } + + return 0; +} + +Tick +PciVirtIO::write(PacketPtr pkt) +{ + const unsigned M5_VAR_USED size(pkt->getSize()); + int bar; + Addr offset; + if (!getBAR(pkt->getAddr(), bar, offset)) + panic("Invalid PCI memory access to unmapped memory.\n"); + assert(bar == 0); + + DPRINTF(VIOPci, "Writing offset 0x%x [len: %i]\n", offset, size); + + // Forward device configuration writes to the device VirtIO model + if (offset >= OFF_VIO_DEVICE) { + vio.writeConfig(pkt, offset - OFF_VIO_DEVICE); + return 0; + } + + pkt->allocate(); + + switch(offset) { + case OFF_DEVICE_FEATURES: + warn("Guest tried to write device features."); + break; + + case OFF_GUEST_FEATURES: + DPRINTF(VIOPci, " WRITE GUEST_FEATURES request\n"); + assert(size == sizeof(uint32_t)); + vio.setGuestFeatures(pkt->get()); + break; + + case OFF_QUEUE_ADDRESS: + DPRINTF(VIOPci, " WRITE QUEUE_ADDRESS\n"); + assert(size == sizeof(uint32_t)); + vio.setQueueAddress(pkt->get()); + break; + + case OFF_QUEUE_SIZE: + panic("Guest tried to write queue size."); + break; + + case OFF_QUEUE_SELECT: + DPRINTF(VIOPci, " WRITE QUEUE_SELECT\n"); + assert(size == sizeof(uint16_t)); + vio.setQueueSelect(pkt->get()); + break; + + case OFF_QUEUE_NOTIFY: + DPRINTF(VIOPci, " WRITE QUEUE_NOTIFY\n"); + assert(size == sizeof(uint16_t)); + queueNotify = pkt->get(); + vio.onNotify(queueNotify); + break; + + case OFF_DEVICE_STATUS: { + assert(size == sizeof(uint8_t)); + uint8_t status(pkt->get()); + DPRINTF(VIOPci, "VirtIO set status: 0x%x\n", status); + vio.setDeviceStatus(status); + } break; + + case OFF_ISR_STATUS: + warn("Guest tried to write ISR status."); + break; + + default: + panic("Unhandled read offset (0x%x)\n", offset); + } + + return 0; +} + +void +PciVirtIO::kick() +{ + DPRINTF(VIOPci, "kick(): Sending interrupt...\n"); + interruptDeliveryPending = true; + intrPost(); +} + +PciVirtIO * +PciVirtIOParams::create() +{ + return new PciVirtIO(this); +} -- cgit v1.2.3