blob: 2dfbc61a3b64a2fcfa738133affaabacb6d1de5d [file] [log] [blame]
/*
* 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);
}