| /* |
| * 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. |
| */ |
| |
| #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), vio(*params->vio), |
| interrupt(params->interrupt->get()) |
| { |
| fatal_if(!interrupt, "No MMIO VirtIO interrupt specified\n"); |
| |
| vio.registerKickCallback([this]() { kick(); }); |
| } |
| |
| 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->setLE<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->getLE<uint32_t>()); |
| pkt->makeResponse(); |
| write(offset, pkt->getLE<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); |
| } |