110388SAndreas.Sandberg@ARM.com/*
211932Ssascha.bischoff@arm.com * Copyright (c) 2014, 2017 ARM Limited
310388SAndreas.Sandberg@ARM.com * All rights reserved
410388SAndreas.Sandberg@ARM.com *
510388SAndreas.Sandberg@ARM.com * The license below extends only to copyright in the software and shall
610388SAndreas.Sandberg@ARM.com * not be construed as granting a license to any other intellectual
710388SAndreas.Sandberg@ARM.com * property including but not limited to intellectual property relating
810388SAndreas.Sandberg@ARM.com * to a hardware implementation of the functionality of the software
910388SAndreas.Sandberg@ARM.com * licensed hereunder.  You may use the software subject to the license
1010388SAndreas.Sandberg@ARM.com * terms below provided that you ensure that this notice is replicated
1110388SAndreas.Sandberg@ARM.com * unmodified and in its entirety in all distributions of the software,
1210388SAndreas.Sandberg@ARM.com * modified or unmodified, in source code or in binary form.
1310388SAndreas.Sandberg@ARM.com *
1410388SAndreas.Sandberg@ARM.com * Redistribution and use in source and binary forms, with or without
1510388SAndreas.Sandberg@ARM.com * modification, are permitted provided that the following conditions are
1610388SAndreas.Sandberg@ARM.com * met: redistributions of source code must retain the above copyright
1710388SAndreas.Sandberg@ARM.com * notice, this list of conditions and the following disclaimer;
1810388SAndreas.Sandberg@ARM.com * redistributions in binary form must reproduce the above copyright
1910388SAndreas.Sandberg@ARM.com * notice, this list of conditions and the following disclaimer in the
2010388SAndreas.Sandberg@ARM.com * documentation and/or other materials provided with the distribution;
2110388SAndreas.Sandberg@ARM.com * neither the name of the copyright holders nor the names of its
2210388SAndreas.Sandberg@ARM.com * contributors may be used to endorse or promote products derived from
2310388SAndreas.Sandberg@ARM.com * this software without specific prior written permission.
2410388SAndreas.Sandberg@ARM.com *
2510388SAndreas.Sandberg@ARM.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2610388SAndreas.Sandberg@ARM.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2710388SAndreas.Sandberg@ARM.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2810388SAndreas.Sandberg@ARM.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2910388SAndreas.Sandberg@ARM.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3010388SAndreas.Sandberg@ARM.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3110388SAndreas.Sandberg@ARM.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3210388SAndreas.Sandberg@ARM.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3310388SAndreas.Sandberg@ARM.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3410388SAndreas.Sandberg@ARM.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3510388SAndreas.Sandberg@ARM.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3610388SAndreas.Sandberg@ARM.com *
3710388SAndreas.Sandberg@ARM.com * Authors: Andreas Sandberg
3810388SAndreas.Sandberg@ARM.com */
3910388SAndreas.Sandberg@ARM.com
4011793Sbrandon.potter@amd.com#include "dev/virtio/pci.hh"
4111793Sbrandon.potter@amd.com
4211932Ssascha.bischoff@arm.com#include "base/bitfield.hh"
4311930Sandreas.sandberg@arm.com#include "debug/VIOIface.hh"
4410388SAndreas.Sandberg@ARM.com#include "mem/packet_access.hh"
4510388SAndreas.Sandberg@ARM.com#include "params/PciVirtIO.hh"
4610388SAndreas.Sandberg@ARM.com
4710388SAndreas.Sandberg@ARM.comPciVirtIO::PciVirtIO(const Params *params)
4810559Sandreas.hansson@arm.com    : PciDevice(params), queueNotify(0), interruptDeliveryPending(false),
4910559Sandreas.hansson@arm.com      vio(*params->vio), callbackKick(this)
5010388SAndreas.Sandberg@ARM.com{
5110388SAndreas.Sandberg@ARM.com    // Override the subsystem ID with the device ID from VirtIO
5210388SAndreas.Sandberg@ARM.com    config.subsystemID = htole(vio.deviceId);
5311932Ssascha.bischoff@arm.com
5411932Ssascha.bischoff@arm.com    // The kernel driver expects the BAR size to be an exact power of
5511932Ssascha.bischoff@arm.com    // two. Nothing else is supported. Therefore, we need to force
5611932Ssascha.bischoff@arm.com    // that alignment here. We do not touch vio.configSize as this is
5711932Ssascha.bischoff@arm.com    // used to check accesses later on.
5811932Ssascha.bischoff@arm.com    BARSize[0] = alignToPowerOfTwo(BAR0_SIZE_BASE + vio.configSize);
5910388SAndreas.Sandberg@ARM.com
6010388SAndreas.Sandberg@ARM.com    vio.registerKickCallback(&callbackKick);
6110388SAndreas.Sandberg@ARM.com}
6210388SAndreas.Sandberg@ARM.com
6310388SAndreas.Sandberg@ARM.comPciVirtIO::~PciVirtIO()
6410388SAndreas.Sandberg@ARM.com{
6510388SAndreas.Sandberg@ARM.com}
6610388SAndreas.Sandberg@ARM.com
6710388SAndreas.Sandberg@ARM.comTick
6810388SAndreas.Sandberg@ARM.comPciVirtIO::read(PacketPtr pkt)
6910388SAndreas.Sandberg@ARM.com{
7010388SAndreas.Sandberg@ARM.com    const unsigned M5_VAR_USED size(pkt->getSize());
7110388SAndreas.Sandberg@ARM.com    int bar;
7210388SAndreas.Sandberg@ARM.com    Addr offset;
7310388SAndreas.Sandberg@ARM.com    if (!getBAR(pkt->getAddr(), bar, offset))
7410388SAndreas.Sandberg@ARM.com        panic("Invalid PCI memory access to unmapped memory.\n");
7510388SAndreas.Sandberg@ARM.com    assert(bar == 0);
7610388SAndreas.Sandberg@ARM.com
7711930Sandreas.sandberg@arm.com    DPRINTF(VIOIface, "Reading offset 0x%x [len: %i]\n", offset, size);
7810388SAndreas.Sandberg@ARM.com
7910388SAndreas.Sandberg@ARM.com    // Forward device configuration writes to the device VirtIO model
8010388SAndreas.Sandberg@ARM.com    if (offset >= OFF_VIO_DEVICE) {
8110388SAndreas.Sandberg@ARM.com        vio.readConfig(pkt, offset - OFF_VIO_DEVICE);
8210388SAndreas.Sandberg@ARM.com        return 0;
8310388SAndreas.Sandberg@ARM.com    }
8410388SAndreas.Sandberg@ARM.com
8510602SAndreas.Sandberg@ARM.com    pkt->makeResponse();
8610602SAndreas.Sandberg@ARM.com
8710388SAndreas.Sandberg@ARM.com    switch(offset) {
8810388SAndreas.Sandberg@ARM.com      case OFF_DEVICE_FEATURES:
8911930Sandreas.sandberg@arm.com        DPRINTF(VIOIface, "   DEVICE_FEATURES request\n");
9010388SAndreas.Sandberg@ARM.com        assert(size == sizeof(uint32_t));
9113342Sgabeblack@google.com        pkt->setLE<uint32_t>(vio.deviceFeatures);
9210388SAndreas.Sandberg@ARM.com        break;
9310388SAndreas.Sandberg@ARM.com
9410388SAndreas.Sandberg@ARM.com      case OFF_GUEST_FEATURES:
9511930Sandreas.sandberg@arm.com        DPRINTF(VIOIface, "   GUEST_FEATURES request\n");
9610388SAndreas.Sandberg@ARM.com        assert(size == sizeof(uint32_t));
9713342Sgabeblack@google.com        pkt->setLE<uint32_t>(vio.getGuestFeatures());
9810388SAndreas.Sandberg@ARM.com        break;
9910388SAndreas.Sandberg@ARM.com
10010388SAndreas.Sandberg@ARM.com      case OFF_QUEUE_ADDRESS:
10111930Sandreas.sandberg@arm.com        DPRINTF(VIOIface, "   QUEUE_ADDRESS request\n");
10210388SAndreas.Sandberg@ARM.com        assert(size == sizeof(uint32_t));
10313342Sgabeblack@google.com        pkt->setLE<uint32_t>(vio.getQueueAddress());
10410388SAndreas.Sandberg@ARM.com        break;
10510388SAndreas.Sandberg@ARM.com
10610388SAndreas.Sandberg@ARM.com      case OFF_QUEUE_SIZE:
10711930Sandreas.sandberg@arm.com        DPRINTF(VIOIface, "   QUEUE_SIZE request\n");
10810388SAndreas.Sandberg@ARM.com        assert(size == sizeof(uint16_t));
10913342Sgabeblack@google.com        pkt->setLE<uint16_t>(vio.getQueueSize());
11010388SAndreas.Sandberg@ARM.com        break;
11110388SAndreas.Sandberg@ARM.com
11210388SAndreas.Sandberg@ARM.com      case OFF_QUEUE_SELECT:
11311930Sandreas.sandberg@arm.com        DPRINTF(VIOIface, "   QUEUE_SELECT\n");
11410388SAndreas.Sandberg@ARM.com        assert(size == sizeof(uint16_t));
11513342Sgabeblack@google.com        pkt->setLE<uint16_t>(vio.getQueueSelect());
11610388SAndreas.Sandberg@ARM.com        break;
11710388SAndreas.Sandberg@ARM.com
11810388SAndreas.Sandberg@ARM.com      case OFF_QUEUE_NOTIFY:
11911930Sandreas.sandberg@arm.com        DPRINTF(VIOIface, "   QUEUE_NOTIFY request\n");
12010388SAndreas.Sandberg@ARM.com        assert(size == sizeof(uint16_t));
12113342Sgabeblack@google.com        pkt->setLE<uint16_t>(queueNotify);
12210388SAndreas.Sandberg@ARM.com        break;
12310388SAndreas.Sandberg@ARM.com
12410388SAndreas.Sandberg@ARM.com      case OFF_DEVICE_STATUS:
12511930Sandreas.sandberg@arm.com        DPRINTF(VIOIface, "   DEVICE_STATUS request\n");
12610388SAndreas.Sandberg@ARM.com        assert(size == sizeof(uint8_t));
12713342Sgabeblack@google.com        pkt->setLE<uint8_t>(vio.getDeviceStatus());
12810388SAndreas.Sandberg@ARM.com        break;
12910388SAndreas.Sandberg@ARM.com
13010388SAndreas.Sandberg@ARM.com      case OFF_ISR_STATUS: {
13111930Sandreas.sandberg@arm.com          DPRINTF(VIOIface, "   ISR_STATUS\n");
13210388SAndreas.Sandberg@ARM.com          assert(size == sizeof(uint8_t));
13310672SAndreas.Sandberg@ARM.com          const uint8_t isr_status(interruptDeliveryPending ? 1 : 0);
13410672SAndreas.Sandberg@ARM.com          if (interruptDeliveryPending) {
13510672SAndreas.Sandberg@ARM.com              interruptDeliveryPending = false;
13610672SAndreas.Sandberg@ARM.com              intrClear();
13710672SAndreas.Sandberg@ARM.com          }
13813342Sgabeblack@google.com          pkt->setLE<uint8_t>(isr_status);
13910388SAndreas.Sandberg@ARM.com      } break;
14010388SAndreas.Sandberg@ARM.com
14110388SAndreas.Sandberg@ARM.com      default:
14210388SAndreas.Sandberg@ARM.com        panic("Unhandled read offset (0x%x)\n", offset);
14310388SAndreas.Sandberg@ARM.com    }
14410388SAndreas.Sandberg@ARM.com
14510388SAndreas.Sandberg@ARM.com    return 0;
14610388SAndreas.Sandberg@ARM.com}
14710388SAndreas.Sandberg@ARM.com
14810388SAndreas.Sandberg@ARM.comTick
14910388SAndreas.Sandberg@ARM.comPciVirtIO::write(PacketPtr pkt)
15010388SAndreas.Sandberg@ARM.com{
15110388SAndreas.Sandberg@ARM.com    const unsigned M5_VAR_USED size(pkt->getSize());
15210388SAndreas.Sandberg@ARM.com    int bar;
15310388SAndreas.Sandberg@ARM.com    Addr offset;
15410388SAndreas.Sandberg@ARM.com    if (!getBAR(pkt->getAddr(), bar, offset))
15510388SAndreas.Sandberg@ARM.com        panic("Invalid PCI memory access to unmapped memory.\n");
15610388SAndreas.Sandberg@ARM.com    assert(bar == 0);
15710388SAndreas.Sandberg@ARM.com
15811930Sandreas.sandberg@arm.com    DPRINTF(VIOIface, "Writing offset 0x%x [len: %i]\n", offset, size);
15910388SAndreas.Sandberg@ARM.com
16010388SAndreas.Sandberg@ARM.com    // Forward device configuration writes to the device VirtIO model
16110388SAndreas.Sandberg@ARM.com    if (offset >= OFF_VIO_DEVICE) {
16210388SAndreas.Sandberg@ARM.com        vio.writeConfig(pkt, offset - OFF_VIO_DEVICE);
16310388SAndreas.Sandberg@ARM.com        return 0;
16410388SAndreas.Sandberg@ARM.com    }
16510388SAndreas.Sandberg@ARM.com
16610602SAndreas.Sandberg@ARM.com    pkt->makeResponse();
16710602SAndreas.Sandberg@ARM.com
16810388SAndreas.Sandberg@ARM.com    switch(offset) {
16910388SAndreas.Sandberg@ARM.com      case OFF_DEVICE_FEATURES:
17010388SAndreas.Sandberg@ARM.com        warn("Guest tried to write device features.");
17110388SAndreas.Sandberg@ARM.com        break;
17210388SAndreas.Sandberg@ARM.com
17310388SAndreas.Sandberg@ARM.com      case OFF_GUEST_FEATURES:
17411930Sandreas.sandberg@arm.com        DPRINTF(VIOIface, "   WRITE GUEST_FEATURES request\n");
17510388SAndreas.Sandberg@ARM.com        assert(size == sizeof(uint32_t));
17613342Sgabeblack@google.com        vio.setGuestFeatures(pkt->getLE<uint32_t>());
17710388SAndreas.Sandberg@ARM.com        break;
17810388SAndreas.Sandberg@ARM.com
17910388SAndreas.Sandberg@ARM.com      case OFF_QUEUE_ADDRESS:
18011930Sandreas.sandberg@arm.com        DPRINTF(VIOIface, "   WRITE QUEUE_ADDRESS\n");
18110388SAndreas.Sandberg@ARM.com        assert(size == sizeof(uint32_t));
18213342Sgabeblack@google.com        vio.setQueueAddress(pkt->getLE<uint32_t>());
18310388SAndreas.Sandberg@ARM.com        break;
18410388SAndreas.Sandberg@ARM.com
18510388SAndreas.Sandberg@ARM.com      case OFF_QUEUE_SIZE:
18610388SAndreas.Sandberg@ARM.com        panic("Guest tried to write queue size.");
18710388SAndreas.Sandberg@ARM.com        break;
18810388SAndreas.Sandberg@ARM.com
18910388SAndreas.Sandberg@ARM.com      case OFF_QUEUE_SELECT:
19011930Sandreas.sandberg@arm.com        DPRINTF(VIOIface, "   WRITE QUEUE_SELECT\n");
19110388SAndreas.Sandberg@ARM.com        assert(size == sizeof(uint16_t));
19213342Sgabeblack@google.com        vio.setQueueSelect(pkt->getLE<uint16_t>());
19310388SAndreas.Sandberg@ARM.com        break;
19410388SAndreas.Sandberg@ARM.com
19510388SAndreas.Sandberg@ARM.com      case OFF_QUEUE_NOTIFY:
19611930Sandreas.sandberg@arm.com        DPRINTF(VIOIface, "   WRITE QUEUE_NOTIFY\n");
19710388SAndreas.Sandberg@ARM.com        assert(size == sizeof(uint16_t));
19813342Sgabeblack@google.com        queueNotify = pkt->getLE<uint16_t>();
19910388SAndreas.Sandberg@ARM.com        vio.onNotify(queueNotify);
20010388SAndreas.Sandberg@ARM.com        break;
20110388SAndreas.Sandberg@ARM.com
20210388SAndreas.Sandberg@ARM.com      case OFF_DEVICE_STATUS: {
20310388SAndreas.Sandberg@ARM.com          assert(size == sizeof(uint8_t));
20413342Sgabeblack@google.com          uint8_t status(pkt->getLE<uint8_t>());
20511930Sandreas.sandberg@arm.com          DPRINTF(VIOIface, "VirtIO set status: 0x%x\n", status);
20610388SAndreas.Sandberg@ARM.com          vio.setDeviceStatus(status);
20710388SAndreas.Sandberg@ARM.com      } break;
20810388SAndreas.Sandberg@ARM.com
20910388SAndreas.Sandberg@ARM.com      case OFF_ISR_STATUS:
21010388SAndreas.Sandberg@ARM.com        warn("Guest tried to write ISR status.");
21110388SAndreas.Sandberg@ARM.com        break;
21210388SAndreas.Sandberg@ARM.com
21310388SAndreas.Sandberg@ARM.com      default:
21410388SAndreas.Sandberg@ARM.com        panic("Unhandled read offset (0x%x)\n", offset);
21510388SAndreas.Sandberg@ARM.com    }
21610388SAndreas.Sandberg@ARM.com
21710388SAndreas.Sandberg@ARM.com    return 0;
21810388SAndreas.Sandberg@ARM.com}
21910388SAndreas.Sandberg@ARM.com
22010388SAndreas.Sandberg@ARM.comvoid
22110388SAndreas.Sandberg@ARM.comPciVirtIO::kick()
22210388SAndreas.Sandberg@ARM.com{
22311930Sandreas.sandberg@arm.com    DPRINTF(VIOIface, "kick(): Sending interrupt...\n");
22410388SAndreas.Sandberg@ARM.com    interruptDeliveryPending = true;
22510388SAndreas.Sandberg@ARM.com    intrPost();
22610388SAndreas.Sandberg@ARM.com}
22710388SAndreas.Sandberg@ARM.com
22810388SAndreas.Sandberg@ARM.comPciVirtIO *
22910388SAndreas.Sandberg@ARM.comPciVirtIOParams::create()
23010388SAndreas.Sandberg@ARM.com{
23110388SAndreas.Sandberg@ARM.com    return new PciVirtIO(this);
23210388SAndreas.Sandberg@ARM.com}
233