diff options
Diffstat (limited to 'src/dev/arm/vio_mmio.cc')
-rw-r--r-- | src/dev/arm/vio_mmio.cc | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/src/dev/arm/vio_mmio.cc b/src/dev/arm/vio_mmio.cc new file mode 100644 index 000000000..1dcaf8cd9 --- /dev/null +++ b/src/dev/arm/vio_mmio.cc @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2016-2018 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 "dev/arm/vio_mmio.hh" + +#include "debug/VIOIface.hh" +#include "dev/arm/base_gic.hh" +#include "mem/packet_access.hh" +#include "params/MmioVirtIO.hh" + +MmioVirtIO::MmioVirtIO(const MmioVirtIOParams *params) + : BasicPioDevice(params, params->pio_size), + hostFeaturesSelect(0), guestFeaturesSelect(0), pageSize(0), + interruptStatus(0), + callbackKick(this), vio(*params->vio), interrupt(params->interrupt) +{ + fatal_if(!interrupt, "No MMIO VirtIO interrupt specified\n"); + + vio.registerKickCallback(&callbackKick); +} + +MmioVirtIO::~MmioVirtIO() +{ +} + +Tick +MmioVirtIO::read(PacketPtr pkt) +{ + const Addr offset = pkt->getAddr() - pioAddr; + const unsigned size(pkt->getSize()); + + DPRINTF(VIOIface, "Reading %u bytes @ 0x%x:\n", size, offset); + + // Forward device configuration writes to the device VirtIO model + if (offset >= OFF_CONFIG) { + vio.readConfig(pkt, offset - OFF_CONFIG); + return 0; + } + + panic_if(size != 4, "Unexpected read size: %u\n", size); + + const uint32_t value = read(offset); + DPRINTF(VIOIface, " value: 0x%x\n", value); + pkt->makeResponse(); + pkt->set<uint32_t>(value); + + return 0; +} + +uint32_t +MmioVirtIO::read(Addr offset) +{ + switch(offset) { + case OFF_MAGIC: + return MAGIC; + + case OFF_VERSION: + return VERSION; + + case OFF_DEVICE_ID: + return vio.deviceId; + + case OFF_VENDOR_ID: + return VENDOR_ID; + + case OFF_HOST_FEATURES: + // We only implement 32 bits of this register + if (hostFeaturesSelect == 0) + return vio.deviceFeatures; + else + return 0; + + case OFF_HOST_FEATURES_SELECT: + return hostFeaturesSelect; + + case OFF_GUEST_FEATURES: + // We only implement 32 bits of this register + if (guestFeaturesSelect == 0) + return vio.getGuestFeatures(); + else + return 0; + + case OFF_GUEST_FEATURES_SELECT: + return hostFeaturesSelect; + + case OFF_GUEST_PAGE_SIZE: + return pageSize; + + case OFF_QUEUE_SELECT: + return vio.getQueueSelect(); + + case OFF_QUEUE_NUM_MAX: + return vio.getQueueSize(); + + case OFF_QUEUE_NUM: + // TODO: We don't support queue resizing, so ignore this for now. + return vio.getQueueSize(); + + case OFF_QUEUE_ALIGN: + // TODO: Implement this once we support other alignment sizes + return VirtQueue::ALIGN_SIZE; + + case OFF_QUEUE_PFN: + return vio.getQueueAddress(); + + case OFF_INTERRUPT_STATUS: + return interruptStatus; + + case OFF_STATUS: + return vio.getDeviceStatus(); + + // Write-only registers + case OFF_QUEUE_NOTIFY: + case OFF_INTERRUPT_ACK: + warn("Guest is trying to read to write-only register 0x%\n", + offset); + return 0; + + default: + panic("Unhandled read offset (0x%x)\n", offset); + } +} + +Tick +MmioVirtIO::write(PacketPtr pkt) +{ + const Addr offset = pkt->getAddr() - pioAddr; + const unsigned size(pkt->getSize()); + + DPRINTF(VIOIface, "Writing %u bytes @ 0x%x:\n", size, offset); + + // Forward device configuration writes to the device VirtIO model + if (offset >= OFF_CONFIG) { + vio.writeConfig(pkt, offset - OFF_CONFIG); + return 0; + } + + panic_if(size != 4, "Unexpected write size @ 0x%x: %u\n", offset, size); + DPRINTF(VIOIface, " value: 0x%x\n", pkt->get<uint32_t>()); + pkt->makeResponse(); + write(offset, pkt->get<uint32_t>()); + return 0; +} + +void +MmioVirtIO::write(Addr offset, uint32_t value) +{ + switch(offset) { + case OFF_HOST_FEATURES_SELECT: + hostFeaturesSelect = value; + return; + + case OFF_GUEST_FEATURES: + if (guestFeaturesSelect == 0) { + vio.setGuestFeatures(value); + } else if (value != 0) { + warn("Setting unimplemented guest features register %u: %u\n", + guestFeaturesSelect, value); + } + return; + + case OFF_GUEST_FEATURES_SELECT: + guestFeaturesSelect = value; + return; + + case OFF_GUEST_PAGE_SIZE: + // TODO: We only support 4096 byte pages at the moment + panic_if(value != VirtQueue::ALIGN_SIZE, + "Unhandled VirtIO page size: %u", value); + pageSize = value; + return; + + case OFF_QUEUE_SELECT: + vio.setQueueSelect(value); + return; + + case OFF_QUEUE_NUM: + // TODO: We don't support queue resizing, so ignore this for now. + warn_once("Ignoring queue resize hint. Requested size: %u\n", value); + return; + + case OFF_QUEUE_ALIGN: + // TODO: We currently only support the hard-coded 4k alignment used + // in legacy VirtIO. + panic_if(value != VirtQueue::ALIGN_SIZE, + "Unhandled VirtIO alignment size: %u", value); + return; + + case OFF_QUEUE_PFN: + vio.setQueueAddress(value); + return; + + case OFF_QUEUE_NOTIFY: + vio.onNotify(value); + return; + + case OFF_INTERRUPT_ACK: + setInterrupts(interruptStatus & (~value)); + return; + + case OFF_STATUS: + panic_if(value > 0xff, "Unexpected status: 0x%x\n", value); + vio.setDeviceStatus(value); + return; + + /* Read-only registers */ + case OFF_MAGIC: + case OFF_VERSION: + case OFF_DEVICE_ID: + case OFF_VENDOR_ID: + case OFF_HOST_FEATURES: + case OFF_QUEUE_NUM_MAX: + case OFF_INTERRUPT_STATUS: + warn("Guest is trying to write to read-only register 0x%\n", + offset); + return; + + default: + panic("Unhandled read offset (0x%x)\n", offset); + } +} + +void +MmioVirtIO::kick() +{ + DPRINTF(VIOIface, "kick(): Sending interrupt...\n"); + setInterrupts(interruptStatus | INT_USED_RING); +} + +void +MmioVirtIO::setInterrupts(uint32_t value) +{ + const uint32_t old_ints = interruptStatus; + interruptStatus = value; + + if (!old_ints && interruptStatus) { + interrupt->raise(); + } else if (old_ints && !interruptStatus) { + interrupt->clear(); + } +} + + +MmioVirtIO * +MmioVirtIOParams::create() +{ + return new MmioVirtIO(this); +} |