dev-arm: Add a MMIO transport interface for VirtIO
authorAndreas Sandberg <andreas.sandberg@arm.com>
Mon, 7 Nov 2016 18:21:43 +0000 (18:21 +0000)
committerAndreas Sandberg <andreas.sandberg@arm.com>
Thu, 7 Jun 2018 17:33:30 +0000 (17:33 +0000)
The MMIO interface currently only supports a subset of version 0.9.5
of the VirtIO specification. It has the following known limitations:

  * The queue size hint (the QUEUE_NUM register) is ignored.

  * Queue alignment is assumed to be hard-coded to
    VirtQueue::ALIGN_SIZE (4096 bytes).

  * Only 4096 byte pages are currently supported.

Change-Id: Ifd318f5e5bddab0b6a42d8c8af9ff2fbb477f98b
Signed-off-by: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-by: Nikos Nikoleris <nikos.nikoleris@arm.com>
Reviewed-by: Rekai Gonzalez Alberquilla <rekai.gonzalezalberquilla@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/2326
Reviewed-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
src/dev/arm/SConscript
src/dev/arm/VirtIOMMIO.py [new file with mode: 0644]
src/dev/arm/vio_mmio.cc [new file with mode: 0644]
src/dev/arm/vio_mmio.hh [new file with mode: 0644]

index ae3ed719488c0f53d84ad9b515b4bee3d14629a5..d4585892525ca05cb5c6682732c5fda0842f866b 100644 (file)
@@ -47,6 +47,7 @@ if env['TARGET_ISA'] == 'arm':
     SimObject('UFSHostDevice.py')
     SimObject('EnergyCtrl.py')
     SimObject('NoMali.py')
+    SimObject('VirtIOMMIO.py')
 
     Source('a9scu.cc')
     Source('amba_device.cc')
@@ -69,6 +70,7 @@ if env['TARGET_ISA'] == 'arm':
     Source('timer_cpulocal.cc')
     Source('timer_a9global.cc')
     Source('vgic.cc')
+    Source('vio_mmio.cc')
     Source('ufs_device.cc')
     Source('energy_ctrl.cc')
 
diff --git a/src/dev/arm/VirtIOMMIO.py b/src/dev/arm/VirtIOMMIO.py
new file mode 100644 (file)
index 0000000..2c95ef3
--- /dev/null
@@ -0,0 +1,63 @@
+# -*- mode:python -*-
+
+# Copyright (c) 2014, 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
+
+from m5.SimObject import SimObject
+from m5.params import *
+from m5.proxy import *
+
+from Device import BasicPioDevice
+from Gic import ArmInterruptPin
+from VirtIO import VirtIODeviceBase, VirtIODummyDevice
+
+class MmioVirtIO(BasicPioDevice):
+    type = 'MmioVirtIO'
+    cxx_header = 'dev/arm/vio_mmio.hh'
+
+    pio_size = Param.Addr(4096, "IO range")
+    interrupt = Param.ArmInterruptPin("Interrupt to use for this device")
+
+    vio = Param.VirtIODeviceBase(VirtIODummyDevice(), "VirtIO device")
+
+    def generateDeviceTree(self, state):
+        node = self.generateBasicPioDeviceNode(state, 'virtio', self.pio_addr,
+                                               int(self.pio_size), [
+                                                   int(self.interrupt.num),
+                                               ])
+        node.appendCompatible(["virtio,mmio"])
+        yield node
diff --git a/src/dev/arm/vio_mmio.cc b/src/dev/arm/vio_mmio.cc
new file mode 100644 (file)
index 0000000..1dcaf8c
--- /dev/null
@@ -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);
+}
diff --git a/src/dev/arm/vio_mmio.hh b/src/dev/arm/vio_mmio.hh
new file mode 100644 (file)
index 0000000..5a5144b
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * 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
+ */
+
+#ifndef __DEV_ARM_VIO_MMIO_HH__
+#define __DEV_ARM_VIO_MMIO_HH__
+
+#include "dev/io_device.hh"
+#include "dev/virtio/base.hh"
+
+class ArmInterruptPin;
+struct MmioVirtIOParams;
+
+class MmioVirtIO : public BasicPioDevice
+{
+  public:
+    MmioVirtIO(const MmioVirtIOParams *params);
+    virtual ~MmioVirtIO();
+
+  protected: // BasicPioDevice
+    Tick read(PacketPtr pkt) override;
+    Tick write(PacketPtr pkt) override;
+
+  protected:
+    /** @{ */
+    /** Offsets into VirtIO MMIO space. */
+
+    enum : Addr {
+        OFF_MAGIC = 0x00,
+        OFF_VERSION = 0x04,
+        OFF_DEVICE_ID = 0x08,
+        OFF_VENDOR_ID = 0x0C,
+        OFF_HOST_FEATURES = 0x10,
+        OFF_HOST_FEATURES_SELECT = 0x14,
+        OFF_GUEST_FEATURES = 0x20,
+        OFF_GUEST_FEATURES_SELECT = 0x24,
+        OFF_GUEST_PAGE_SIZE = 0x28,
+        OFF_QUEUE_SELECT = 0x30,
+        OFF_QUEUE_NUM_MAX = 0x34,
+        OFF_QUEUE_NUM = 0x38,
+        OFF_QUEUE_ALIGN = 0x3C,
+        OFF_QUEUE_PFN = 0x40,
+        OFF_QUEUE_NOTIFY = 0x50,
+        OFF_INTERRUPT_STATUS = 0x60,
+        OFF_INTERRUPT_ACK = 0x64,
+        OFF_STATUS = 0x70,
+        OFF_CONFIG = 0x100,
+    };
+
+    /** @} */
+
+    enum {
+        INT_USED_RING = 1 << 0,
+        INT_CONFIG = 1 << 1,
+    };
+
+    static const uint32_t MAGIC = 0x74726976;
+    static const uint32_t VERSION = 1;
+    static const uint32_t VENDOR_ID = 0x1AF4;
+
+
+    uint32_t read(Addr offset);
+    void write(Addr offset, uint32_t value);
+
+    void kick();
+    void setInterrupts(uint32_t value);
+
+    uint32_t hostFeaturesSelect;
+    uint32_t guestFeaturesSelect;
+    uint32_t pageSize;
+    uint32_t interruptStatus;
+
+    MakeCallback<MmioVirtIO, &MmioVirtIO::kick> callbackKick;
+
+  protected: // Params
+    VirtIODeviceBase &vio;
+    ArmInterruptPin *const interrupt;
+};
+
+#endif // __DEV_ARM_VIO_MMIO_HH__