112740Sandreas.sandberg@arm.com/*
212740Sandreas.sandberg@arm.com * Copyright (c) 2016-2018 ARM Limited
312740Sandreas.sandberg@arm.com * All rights reserved
412740Sandreas.sandberg@arm.com *
512740Sandreas.sandberg@arm.com * The license below extends only to copyright in the software and shall
612740Sandreas.sandberg@arm.com * not be construed as granting a license to any other intellectual
712740Sandreas.sandberg@arm.com * property including but not limited to intellectual property relating
812740Sandreas.sandberg@arm.com * to a hardware implementation of the functionality of the software
912740Sandreas.sandberg@arm.com * licensed hereunder.  You may use the software subject to the license
1012740Sandreas.sandberg@arm.com * terms below provided that you ensure that this notice is replicated
1112740Sandreas.sandberg@arm.com * unmodified and in its entirety in all distributions of the software,
1212740Sandreas.sandberg@arm.com * modified or unmodified, in source code or in binary form.
1312740Sandreas.sandberg@arm.com *
1412740Sandreas.sandberg@arm.com * Redistribution and use in source and binary forms, with or without
1512740Sandreas.sandberg@arm.com * modification, are permitted provided that the following conditions are
1612740Sandreas.sandberg@arm.com * met: redistributions of source code must retain the above copyright
1712740Sandreas.sandberg@arm.com * notice, this list of conditions and the following disclaimer;
1812740Sandreas.sandberg@arm.com * redistributions in binary form must reproduce the above copyright
1912740Sandreas.sandberg@arm.com * notice, this list of conditions and the following disclaimer in the
2012740Sandreas.sandberg@arm.com * documentation and/or other materials provided with the distribution;
2112740Sandreas.sandberg@arm.com * neither the name of the copyright holders nor the names of its
2212740Sandreas.sandberg@arm.com * contributors may be used to endorse or promote products derived from
2312740Sandreas.sandberg@arm.com * this software without specific prior written permission.
2412740Sandreas.sandberg@arm.com *
2512740Sandreas.sandberg@arm.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2612740Sandreas.sandberg@arm.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2712740Sandreas.sandberg@arm.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2812740Sandreas.sandberg@arm.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2912740Sandreas.sandberg@arm.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3012740Sandreas.sandberg@arm.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3112740Sandreas.sandberg@arm.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3212740Sandreas.sandberg@arm.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3312740Sandreas.sandberg@arm.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3412740Sandreas.sandberg@arm.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3512740Sandreas.sandberg@arm.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3612740Sandreas.sandberg@arm.com *
3712740Sandreas.sandberg@arm.com * Authors: Andreas Sandberg
3812740Sandreas.sandberg@arm.com */
3912740Sandreas.sandberg@arm.com
4012740Sandreas.sandberg@arm.com#include "dev/arm/vio_mmio.hh"
4112740Sandreas.sandberg@arm.com
4212740Sandreas.sandberg@arm.com#include "debug/VIOIface.hh"
4312740Sandreas.sandberg@arm.com#include "dev/arm/base_gic.hh"
4412740Sandreas.sandberg@arm.com#include "mem/packet_access.hh"
4512740Sandreas.sandberg@arm.com#include "params/MmioVirtIO.hh"
4612740Sandreas.sandberg@arm.com
4712740Sandreas.sandberg@arm.comMmioVirtIO::MmioVirtIO(const MmioVirtIOParams *params)
4812740Sandreas.sandberg@arm.com    : BasicPioDevice(params, params->pio_size),
4912740Sandreas.sandberg@arm.com      hostFeaturesSelect(0), guestFeaturesSelect(0), pageSize(0),
5012740Sandreas.sandberg@arm.com      interruptStatus(0),
5112974Sgiacomo.travaglini@arm.com      callbackKick(this), vio(*params->vio),
5212974Sgiacomo.travaglini@arm.com      interrupt(params->interrupt->get())
5312740Sandreas.sandberg@arm.com{
5412740Sandreas.sandberg@arm.com    fatal_if(!interrupt, "No MMIO VirtIO interrupt specified\n");
5512740Sandreas.sandberg@arm.com
5612740Sandreas.sandberg@arm.com    vio.registerKickCallback(&callbackKick);
5712740Sandreas.sandberg@arm.com}
5812740Sandreas.sandberg@arm.com
5912740Sandreas.sandberg@arm.comMmioVirtIO::~MmioVirtIO()
6012740Sandreas.sandberg@arm.com{
6112740Sandreas.sandberg@arm.com}
6212740Sandreas.sandberg@arm.com
6312740Sandreas.sandberg@arm.comTick
6412740Sandreas.sandberg@arm.comMmioVirtIO::read(PacketPtr pkt)
6512740Sandreas.sandberg@arm.com{
6612740Sandreas.sandberg@arm.com    const Addr offset = pkt->getAddr() - pioAddr;
6712740Sandreas.sandberg@arm.com    const unsigned size(pkt->getSize());
6812740Sandreas.sandberg@arm.com
6912740Sandreas.sandberg@arm.com    DPRINTF(VIOIface, "Reading %u bytes @ 0x%x:\n", size, offset);
7012740Sandreas.sandberg@arm.com
7112740Sandreas.sandberg@arm.com    // Forward device configuration writes to the device VirtIO model
7212740Sandreas.sandberg@arm.com    if (offset >= OFF_CONFIG) {
7312740Sandreas.sandberg@arm.com        vio.readConfig(pkt, offset - OFF_CONFIG);
7412740Sandreas.sandberg@arm.com        return 0;
7512740Sandreas.sandberg@arm.com    }
7612740Sandreas.sandberg@arm.com
7712740Sandreas.sandberg@arm.com    panic_if(size != 4, "Unexpected read size: %u\n", size);
7812740Sandreas.sandberg@arm.com
7912740Sandreas.sandberg@arm.com    const uint32_t value = read(offset);
8012740Sandreas.sandberg@arm.com    DPRINTF(VIOIface, "    value: 0x%x\n", value);
8112740Sandreas.sandberg@arm.com    pkt->makeResponse();
8213230Sgabeblack@google.com    pkt->setLE<uint32_t>(value);
8312740Sandreas.sandberg@arm.com
8412740Sandreas.sandberg@arm.com    return 0;
8512740Sandreas.sandberg@arm.com}
8612740Sandreas.sandberg@arm.com
8712740Sandreas.sandberg@arm.comuint32_t
8812740Sandreas.sandberg@arm.comMmioVirtIO::read(Addr offset)
8912740Sandreas.sandberg@arm.com{
9012740Sandreas.sandberg@arm.com    switch(offset) {
9112740Sandreas.sandberg@arm.com      case OFF_MAGIC:
9212740Sandreas.sandberg@arm.com        return MAGIC;
9312740Sandreas.sandberg@arm.com
9412740Sandreas.sandberg@arm.com      case OFF_VERSION:
9512740Sandreas.sandberg@arm.com        return VERSION;
9612740Sandreas.sandberg@arm.com
9712740Sandreas.sandberg@arm.com      case OFF_DEVICE_ID:
9812740Sandreas.sandberg@arm.com        return vio.deviceId;
9912740Sandreas.sandberg@arm.com
10012740Sandreas.sandberg@arm.com      case OFF_VENDOR_ID:
10112740Sandreas.sandberg@arm.com        return VENDOR_ID;
10212740Sandreas.sandberg@arm.com
10312740Sandreas.sandberg@arm.com      case OFF_HOST_FEATURES:
10412740Sandreas.sandberg@arm.com        // We only implement 32 bits of this register
10512740Sandreas.sandberg@arm.com        if (hostFeaturesSelect == 0)
10612740Sandreas.sandberg@arm.com            return vio.deviceFeatures;
10712740Sandreas.sandberg@arm.com        else
10812740Sandreas.sandberg@arm.com            return 0;
10912740Sandreas.sandberg@arm.com
11012740Sandreas.sandberg@arm.com      case OFF_HOST_FEATURES_SELECT:
11112740Sandreas.sandberg@arm.com        return hostFeaturesSelect;
11212740Sandreas.sandberg@arm.com
11312740Sandreas.sandberg@arm.com      case OFF_GUEST_FEATURES:
11412740Sandreas.sandberg@arm.com        // We only implement 32 bits of this register
11512740Sandreas.sandberg@arm.com        if (guestFeaturesSelect == 0)
11612740Sandreas.sandberg@arm.com            return vio.getGuestFeatures();
11712740Sandreas.sandberg@arm.com        else
11812740Sandreas.sandberg@arm.com            return 0;
11912740Sandreas.sandberg@arm.com
12012740Sandreas.sandberg@arm.com      case OFF_GUEST_FEATURES_SELECT:
12112740Sandreas.sandberg@arm.com        return hostFeaturesSelect;
12212740Sandreas.sandberg@arm.com
12312740Sandreas.sandberg@arm.com      case OFF_GUEST_PAGE_SIZE:
12412740Sandreas.sandberg@arm.com        return pageSize;
12512740Sandreas.sandberg@arm.com
12612740Sandreas.sandberg@arm.com      case OFF_QUEUE_SELECT:
12712740Sandreas.sandberg@arm.com        return vio.getQueueSelect();
12812740Sandreas.sandberg@arm.com
12912740Sandreas.sandberg@arm.com      case OFF_QUEUE_NUM_MAX:
13012740Sandreas.sandberg@arm.com        return vio.getQueueSize();
13112740Sandreas.sandberg@arm.com
13212740Sandreas.sandberg@arm.com      case OFF_QUEUE_NUM:
13312740Sandreas.sandberg@arm.com        // TODO: We don't support queue resizing, so ignore this for now.
13412740Sandreas.sandberg@arm.com        return vio.getQueueSize();
13512740Sandreas.sandberg@arm.com
13612740Sandreas.sandberg@arm.com      case OFF_QUEUE_ALIGN:
13712740Sandreas.sandberg@arm.com        // TODO: Implement this once we support other alignment sizes
13812740Sandreas.sandberg@arm.com        return VirtQueue::ALIGN_SIZE;
13912740Sandreas.sandberg@arm.com
14012740Sandreas.sandberg@arm.com      case OFF_QUEUE_PFN:
14112740Sandreas.sandberg@arm.com        return vio.getQueueAddress();
14212740Sandreas.sandberg@arm.com
14312740Sandreas.sandberg@arm.com      case OFF_INTERRUPT_STATUS:
14412740Sandreas.sandberg@arm.com        return interruptStatus;
14512740Sandreas.sandberg@arm.com
14612740Sandreas.sandberg@arm.com      case OFF_STATUS:
14712740Sandreas.sandberg@arm.com        return vio.getDeviceStatus();
14812740Sandreas.sandberg@arm.com
14912740Sandreas.sandberg@arm.com        // Write-only registers
15012740Sandreas.sandberg@arm.com      case OFF_QUEUE_NOTIFY:
15112740Sandreas.sandberg@arm.com      case OFF_INTERRUPT_ACK:
15212740Sandreas.sandberg@arm.com        warn("Guest is trying to read to write-only register 0x%\n",
15312740Sandreas.sandberg@arm.com             offset);
15412740Sandreas.sandberg@arm.com        return 0;
15512740Sandreas.sandberg@arm.com
15612740Sandreas.sandberg@arm.com      default:
15712740Sandreas.sandberg@arm.com        panic("Unhandled read offset (0x%x)\n", offset);
15812740Sandreas.sandberg@arm.com    }
15912740Sandreas.sandberg@arm.com}
16012740Sandreas.sandberg@arm.com
16112740Sandreas.sandberg@arm.comTick
16212740Sandreas.sandberg@arm.comMmioVirtIO::write(PacketPtr pkt)
16312740Sandreas.sandberg@arm.com{
16412740Sandreas.sandberg@arm.com    const Addr offset = pkt->getAddr() - pioAddr;
16512740Sandreas.sandberg@arm.com    const unsigned size(pkt->getSize());
16612740Sandreas.sandberg@arm.com
16712740Sandreas.sandberg@arm.com    DPRINTF(VIOIface, "Writing %u bytes @ 0x%x:\n", size, offset);
16812740Sandreas.sandberg@arm.com
16912740Sandreas.sandberg@arm.com    // Forward device configuration writes to the device VirtIO model
17012740Sandreas.sandberg@arm.com    if (offset >= OFF_CONFIG) {
17112740Sandreas.sandberg@arm.com        vio.writeConfig(pkt, offset - OFF_CONFIG);
17212740Sandreas.sandberg@arm.com        return 0;
17312740Sandreas.sandberg@arm.com    }
17412740Sandreas.sandberg@arm.com
17512740Sandreas.sandberg@arm.com    panic_if(size != 4, "Unexpected write size @ 0x%x: %u\n", offset, size);
17613230Sgabeblack@google.com    DPRINTF(VIOIface, "    value: 0x%x\n", pkt->getLE<uint32_t>());
17712740Sandreas.sandberg@arm.com    pkt->makeResponse();
17813230Sgabeblack@google.com    write(offset, pkt->getLE<uint32_t>());
17912740Sandreas.sandberg@arm.com    return 0;
18012740Sandreas.sandberg@arm.com}
18112740Sandreas.sandberg@arm.com
18212740Sandreas.sandberg@arm.comvoid
18312740Sandreas.sandberg@arm.comMmioVirtIO::write(Addr offset, uint32_t value)
18412740Sandreas.sandberg@arm.com{
18512740Sandreas.sandberg@arm.com    switch(offset) {
18612740Sandreas.sandberg@arm.com      case OFF_HOST_FEATURES_SELECT:
18712740Sandreas.sandberg@arm.com        hostFeaturesSelect = value;
18812740Sandreas.sandberg@arm.com        return;
18912740Sandreas.sandberg@arm.com
19012740Sandreas.sandberg@arm.com      case OFF_GUEST_FEATURES:
19112740Sandreas.sandberg@arm.com        if (guestFeaturesSelect == 0) {
19212740Sandreas.sandberg@arm.com            vio.setGuestFeatures(value);
19312740Sandreas.sandberg@arm.com        } else if (value != 0) {
19412740Sandreas.sandberg@arm.com            warn("Setting unimplemented guest features register %u: %u\n",
19512740Sandreas.sandberg@arm.com                 guestFeaturesSelect, value);
19612740Sandreas.sandberg@arm.com        }
19712740Sandreas.sandberg@arm.com        return;
19812740Sandreas.sandberg@arm.com
19912740Sandreas.sandberg@arm.com      case OFF_GUEST_FEATURES_SELECT:
20012740Sandreas.sandberg@arm.com        guestFeaturesSelect = value;
20112740Sandreas.sandberg@arm.com        return;
20212740Sandreas.sandberg@arm.com
20312740Sandreas.sandberg@arm.com      case OFF_GUEST_PAGE_SIZE:
20412740Sandreas.sandberg@arm.com        // TODO: We only support 4096 byte pages at the moment
20512740Sandreas.sandberg@arm.com        panic_if(value != VirtQueue::ALIGN_SIZE,
20612740Sandreas.sandberg@arm.com                 "Unhandled VirtIO page size: %u", value);
20712740Sandreas.sandberg@arm.com        pageSize = value;
20812740Sandreas.sandberg@arm.com        return;
20912740Sandreas.sandberg@arm.com
21012740Sandreas.sandberg@arm.com      case OFF_QUEUE_SELECT:
21112740Sandreas.sandberg@arm.com        vio.setQueueSelect(value);
21212740Sandreas.sandberg@arm.com        return;
21312740Sandreas.sandberg@arm.com
21412740Sandreas.sandberg@arm.com      case OFF_QUEUE_NUM:
21512740Sandreas.sandberg@arm.com        // TODO: We don't support queue resizing, so ignore this for now.
21612740Sandreas.sandberg@arm.com        warn_once("Ignoring queue resize hint. Requested size: %u\n", value);
21712740Sandreas.sandberg@arm.com        return;
21812740Sandreas.sandberg@arm.com
21912740Sandreas.sandberg@arm.com      case OFF_QUEUE_ALIGN:
22012740Sandreas.sandberg@arm.com        // TODO: We currently only support the hard-coded 4k alignment used
22112740Sandreas.sandberg@arm.com        // in legacy VirtIO.
22212740Sandreas.sandberg@arm.com        panic_if(value != VirtQueue::ALIGN_SIZE,
22312740Sandreas.sandberg@arm.com                 "Unhandled VirtIO alignment size: %u", value);
22412740Sandreas.sandberg@arm.com        return;
22512740Sandreas.sandberg@arm.com
22612740Sandreas.sandberg@arm.com      case OFF_QUEUE_PFN:
22712740Sandreas.sandberg@arm.com        vio.setQueueAddress(value);
22812740Sandreas.sandberg@arm.com        return;
22912740Sandreas.sandberg@arm.com
23012740Sandreas.sandberg@arm.com      case OFF_QUEUE_NOTIFY:
23112740Sandreas.sandberg@arm.com        vio.onNotify(value);
23212740Sandreas.sandberg@arm.com        return;
23312740Sandreas.sandberg@arm.com
23412740Sandreas.sandberg@arm.com      case OFF_INTERRUPT_ACK:
23512740Sandreas.sandberg@arm.com        setInterrupts(interruptStatus & (~value));
23612740Sandreas.sandberg@arm.com        return;
23712740Sandreas.sandberg@arm.com
23812740Sandreas.sandberg@arm.com      case OFF_STATUS:
23912740Sandreas.sandberg@arm.com        panic_if(value > 0xff, "Unexpected status: 0x%x\n", value);
24012740Sandreas.sandberg@arm.com        vio.setDeviceStatus(value);
24112740Sandreas.sandberg@arm.com        return;
24212740Sandreas.sandberg@arm.com
24312740Sandreas.sandberg@arm.com        /* Read-only registers */
24412740Sandreas.sandberg@arm.com      case OFF_MAGIC:
24512740Sandreas.sandberg@arm.com      case OFF_VERSION:
24612740Sandreas.sandberg@arm.com      case OFF_DEVICE_ID:
24712740Sandreas.sandberg@arm.com      case OFF_VENDOR_ID:
24812740Sandreas.sandberg@arm.com      case OFF_HOST_FEATURES:
24912740Sandreas.sandberg@arm.com      case OFF_QUEUE_NUM_MAX:
25012740Sandreas.sandberg@arm.com      case OFF_INTERRUPT_STATUS:
25112740Sandreas.sandberg@arm.com        warn("Guest is trying to write to read-only register 0x%\n",
25212740Sandreas.sandberg@arm.com             offset);
25312740Sandreas.sandberg@arm.com        return;
25412740Sandreas.sandberg@arm.com
25512740Sandreas.sandberg@arm.com      default:
25612740Sandreas.sandberg@arm.com        panic("Unhandled read offset (0x%x)\n", offset);
25712740Sandreas.sandberg@arm.com    }
25812740Sandreas.sandberg@arm.com}
25912740Sandreas.sandberg@arm.com
26012740Sandreas.sandberg@arm.comvoid
26112740Sandreas.sandberg@arm.comMmioVirtIO::kick()
26212740Sandreas.sandberg@arm.com{
26312740Sandreas.sandberg@arm.com    DPRINTF(VIOIface, "kick(): Sending interrupt...\n");
26412740Sandreas.sandberg@arm.com    setInterrupts(interruptStatus | INT_USED_RING);
26512740Sandreas.sandberg@arm.com}
26612740Sandreas.sandberg@arm.com
26712740Sandreas.sandberg@arm.comvoid
26812740Sandreas.sandberg@arm.comMmioVirtIO::setInterrupts(uint32_t value)
26912740Sandreas.sandberg@arm.com{
27012740Sandreas.sandberg@arm.com    const uint32_t old_ints = interruptStatus;
27112740Sandreas.sandberg@arm.com    interruptStatus = value;
27212740Sandreas.sandberg@arm.com
27312740Sandreas.sandberg@arm.com    if (!old_ints && interruptStatus) {
27412740Sandreas.sandberg@arm.com        interrupt->raise();
27512740Sandreas.sandberg@arm.com    } else if (old_ints && !interruptStatus) {
27612740Sandreas.sandberg@arm.com        interrupt->clear();
27712740Sandreas.sandberg@arm.com    }
27812740Sandreas.sandberg@arm.com}
27912740Sandreas.sandberg@arm.com
28012740Sandreas.sandberg@arm.com
28112740Sandreas.sandberg@arm.comMmioVirtIO *
28212740Sandreas.sandberg@arm.comMmioVirtIOParams::create()
28312740Sandreas.sandberg@arm.com{
28412740Sandreas.sandberg@arm.com    return new MmioVirtIO(this);
28512740Sandreas.sandberg@arm.com}
286