vio_mmio.cc revision 12740
15217Ssaidi@eecs.umich.edu/*
29428SAndreas.Sandberg@ARM.com * Copyright (c) 2016-2018 ARM Limited
39920Syasuko.eckert@amd.com * All rights reserved
49428SAndreas.Sandberg@ARM.com *
59428SAndreas.Sandberg@ARM.com * The license below extends only to copyright in the software and shall
69428SAndreas.Sandberg@ARM.com * not be construed as granting a license to any other intellectual
79428SAndreas.Sandberg@ARM.com * property including but not limited to intellectual property relating
89428SAndreas.Sandberg@ARM.com * to a hardware implementation of the functionality of the software
99428SAndreas.Sandberg@ARM.com * licensed hereunder.  You may use the software subject to the license
109428SAndreas.Sandberg@ARM.com * terms below provided that you ensure that this notice is replicated
119428SAndreas.Sandberg@ARM.com * unmodified and in its entirety in all distributions of the software,
129428SAndreas.Sandberg@ARM.com * modified or unmodified, in source code or in binary form.
139428SAndreas.Sandberg@ARM.com *
149428SAndreas.Sandberg@ARM.com * Redistribution and use in source and binary forms, with or without
155217Ssaidi@eecs.umich.edu * modification, are permitted provided that the following conditions are
165217Ssaidi@eecs.umich.edu * met: redistributions of source code must retain the above copyright
175217Ssaidi@eecs.umich.edu * notice, this list of conditions and the following disclaimer;
185217Ssaidi@eecs.umich.edu * redistributions in binary form must reproduce the above copyright
195217Ssaidi@eecs.umich.edu * notice, this list of conditions and the following disclaimer in the
205217Ssaidi@eecs.umich.edu * documentation and/or other materials provided with the distribution;
215217Ssaidi@eecs.umich.edu * neither the name of the copyright holders nor the names of its
225217Ssaidi@eecs.umich.edu * contributors may be used to endorse or promote products derived from
235217Ssaidi@eecs.umich.edu * this software without specific prior written permission.
245217Ssaidi@eecs.umich.edu *
255217Ssaidi@eecs.umich.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
265217Ssaidi@eecs.umich.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
275217Ssaidi@eecs.umich.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
285217Ssaidi@eecs.umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
295217Ssaidi@eecs.umich.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
305217Ssaidi@eecs.umich.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
315217Ssaidi@eecs.umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
325217Ssaidi@eecs.umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
335217Ssaidi@eecs.umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
345217Ssaidi@eecs.umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
355217Ssaidi@eecs.umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
365217Ssaidi@eecs.umich.edu *
375217Ssaidi@eecs.umich.edu * Authors: Andreas Sandberg
385217Ssaidi@eecs.umich.edu */
395217Ssaidi@eecs.umich.edu
405217Ssaidi@eecs.umich.edu#include "dev/arm/vio_mmio.hh"
415217Ssaidi@eecs.umich.edu
425217Ssaidi@eecs.umich.edu#include "debug/VIOIface.hh"
435217Ssaidi@eecs.umich.edu#include "dev/arm/base_gic.hh"
445217Ssaidi@eecs.umich.edu#include "mem/packet_access.hh"
455217Ssaidi@eecs.umich.edu#include "params/MmioVirtIO.hh"
466658Snate@binkert.org
479441SAndreas.Sandberg@ARM.comMmioVirtIO::MmioVirtIO(const MmioVirtIOParams *params)
489441SAndreas.Sandberg@ARM.com    : BasicPioDevice(params, params->pio_size),
495217Ssaidi@eecs.umich.edu      hostFeaturesSelect(0), guestFeaturesSelect(0), pageSize(0),
508232Snate@binkert.org      interruptStatus(0),
519441SAndreas.Sandberg@ARM.com      callbackKick(this), vio(*params->vio), interrupt(params->interrupt)
525217Ssaidi@eecs.umich.edu{
535217Ssaidi@eecs.umich.edu    fatal_if(!interrupt, "No MMIO VirtIO interrupt specified\n");
545217Ssaidi@eecs.umich.edu
555217Ssaidi@eecs.umich.edu    vio.registerKickCallback(&callbackKick);
565217Ssaidi@eecs.umich.edu}
575217Ssaidi@eecs.umich.edu
585217Ssaidi@eecs.umich.eduMmioVirtIO::~MmioVirtIO()
595217Ssaidi@eecs.umich.edu{
605217Ssaidi@eecs.umich.edu}
615217Ssaidi@eecs.umich.edu
625217Ssaidi@eecs.umich.eduTick
635217Ssaidi@eecs.umich.eduMmioVirtIO::read(PacketPtr pkt)
645217Ssaidi@eecs.umich.edu{
655217Ssaidi@eecs.umich.edu    const Addr offset = pkt->getAddr() - pioAddr;
665217Ssaidi@eecs.umich.edu    const unsigned size(pkt->getSize());
675217Ssaidi@eecs.umich.edu
685217Ssaidi@eecs.umich.edu    DPRINTF(VIOIface, "Reading %u bytes @ 0x%x:\n", size, offset);
695217Ssaidi@eecs.umich.edu
705217Ssaidi@eecs.umich.edu    // Forward device configuration writes to the device VirtIO model
715217Ssaidi@eecs.umich.edu    if (offset >= OFF_CONFIG) {
725217Ssaidi@eecs.umich.edu        vio.readConfig(pkt, offset - OFF_CONFIG);
735217Ssaidi@eecs.umich.edu        return 0;
745217Ssaidi@eecs.umich.edu    }
755217Ssaidi@eecs.umich.edu
765217Ssaidi@eecs.umich.edu    panic_if(size != 4, "Unexpected read size: %u\n", size);
775217Ssaidi@eecs.umich.edu
785217Ssaidi@eecs.umich.edu    const uint32_t value = read(offset);
795217Ssaidi@eecs.umich.edu    DPRINTF(VIOIface, "    value: 0x%x\n", value);
805217Ssaidi@eecs.umich.edu    pkt->makeResponse();
815217Ssaidi@eecs.umich.edu    pkt->set<uint32_t>(value);
825217Ssaidi@eecs.umich.edu
839920Syasuko.eckert@amd.com    return 0;
849920Syasuko.eckert@amd.com}
859920Syasuko.eckert@amd.com
869920Syasuko.eckert@amd.comuint32_t
879920Syasuko.eckert@amd.comMmioVirtIO::read(Addr offset)
889920Syasuko.eckert@amd.com{
899920Syasuko.eckert@amd.com    switch(offset) {
909920Syasuko.eckert@amd.com      case OFF_MAGIC:
917720Sgblack@eecs.umich.edu        return MAGIC;
927720Sgblack@eecs.umich.edu
935712Shsul@eecs.umich.edu      case OFF_VERSION:
945712Shsul@eecs.umich.edu        return VERSION;
955217Ssaidi@eecs.umich.edu
965217Ssaidi@eecs.umich.edu      case OFF_DEVICE_ID:
975714Shsul@eecs.umich.edu        return vio.deviceId;
985714Shsul@eecs.umich.edu
995714Shsul@eecs.umich.edu      case OFF_VENDOR_ID:
1005714Shsul@eecs.umich.edu        return VENDOR_ID;
1015714Shsul@eecs.umich.edu
1025714Shsul@eecs.umich.edu      case OFF_HOST_FEATURES:
1035714Shsul@eecs.umich.edu        // We only implement 32 bits of this register
1045217Ssaidi@eecs.umich.edu        if (hostFeaturesSelect == 0)
1059428SAndreas.Sandberg@ARM.com            return vio.deviceFeatures;
1069428SAndreas.Sandberg@ARM.com        else
10710905Sandreas.sandberg@arm.com            return 0;
1089428SAndreas.Sandberg@ARM.com
1099428SAndreas.Sandberg@ARM.com      case OFF_HOST_FEATURES_SELECT:
1109428SAndreas.Sandberg@ARM.com        return hostFeaturesSelect;
1119428SAndreas.Sandberg@ARM.com
1129428SAndreas.Sandberg@ARM.com      case OFF_GUEST_FEATURES:
1139428SAndreas.Sandberg@ARM.com        // We only implement 32 bits of this register
1149428SAndreas.Sandberg@ARM.com        if (guestFeaturesSelect == 0)
1159428SAndreas.Sandberg@ARM.com            return vio.getGuestFeatures();
11610905Sandreas.sandberg@arm.com        else
1179428SAndreas.Sandberg@ARM.com            return 0;
1189428SAndreas.Sandberg@ARM.com
1199428SAndreas.Sandberg@ARM.com      case OFF_GUEST_FEATURES_SELECT:
1209428SAndreas.Sandberg@ARM.com        return hostFeaturesSelect;
1219428SAndreas.Sandberg@ARM.com
1229428SAndreas.Sandberg@ARM.com      case OFF_GUEST_PAGE_SIZE:
1239920Syasuko.eckert@amd.com        return pageSize;
1249920Syasuko.eckert@amd.com
1259920Syasuko.eckert@amd.com      case OFF_QUEUE_SELECT:
1269920Syasuko.eckert@amd.com        return vio.getQueueSelect();
1279920Syasuko.eckert@amd.com
1289920Syasuko.eckert@amd.com      case OFF_QUEUE_NUM_MAX:
1299920Syasuko.eckert@amd.com        return vio.getQueueSize();
13010905Sandreas.sandberg@arm.com
1319428SAndreas.Sandberg@ARM.com      case OFF_QUEUE_NUM:
1329428SAndreas.Sandberg@ARM.com        // TODO: We don't support queue resizing, so ignore this for now.
1339428SAndreas.Sandberg@ARM.com        return vio.getQueueSize();
1349428SAndreas.Sandberg@ARM.com
1359428SAndreas.Sandberg@ARM.com      case OFF_QUEUE_ALIGN:
13610905Sandreas.sandberg@arm.com        // TODO: Implement this once we support other alignment sizes
1379428SAndreas.Sandberg@ARM.com        return VirtQueue::ALIGN_SIZE;
1389428SAndreas.Sandberg@ARM.com
1399428SAndreas.Sandberg@ARM.com      case OFF_QUEUE_PFN:
1409428SAndreas.Sandberg@ARM.com        return vio.getQueueAddress();
1419428SAndreas.Sandberg@ARM.com
1429428SAndreas.Sandberg@ARM.com      case OFF_INTERRUPT_STATUS:
14310905Sandreas.sandberg@arm.com        return interruptStatus;
1449428SAndreas.Sandberg@ARM.com
1459428SAndreas.Sandberg@ARM.com      case OFF_STATUS:
1469428SAndreas.Sandberg@ARM.com        return vio.getDeviceStatus();
1479428SAndreas.Sandberg@ARM.com
1489428SAndreas.Sandberg@ARM.com        // Write-only registers
1499428SAndreas.Sandberg@ARM.com      case OFF_QUEUE_NOTIFY:
1509428SAndreas.Sandberg@ARM.com      case OFF_INTERRUPT_ACK:
1519428SAndreas.Sandberg@ARM.com        warn("Guest is trying to read to write-only register 0x%\n",
1529920Syasuko.eckert@amd.com             offset);
1539920Syasuko.eckert@amd.com        return 0;
1549920Syasuko.eckert@amd.com
1559920Syasuko.eckert@amd.com      default:
1569920Syasuko.eckert@amd.com        panic("Unhandled read offset (0x%x)\n", offset);
1579920Syasuko.eckert@amd.com    }
1589920Syasuko.eckert@amd.com}
1599428SAndreas.Sandberg@ARM.com
16010905Sandreas.sandberg@arm.comTick
1619428SAndreas.Sandberg@ARM.comMmioVirtIO::write(PacketPtr pkt)
1629428SAndreas.Sandberg@ARM.com{
1639428SAndreas.Sandberg@ARM.com    const Addr offset = pkt->getAddr() - pioAddr;
1649428SAndreas.Sandberg@ARM.com    const unsigned size(pkt->getSize());
1659441SAndreas.Sandberg@ARM.com
1669441SAndreas.Sandberg@ARM.com    DPRINTF(VIOIface, "Writing %u bytes @ 0x%x:\n", size, offset);
1679441SAndreas.Sandberg@ARM.com
1689441SAndreas.Sandberg@ARM.com    // Forward device configuration writes to the device VirtIO model
1699441SAndreas.Sandberg@ARM.com    if (offset >= OFF_CONFIG) {
1709441SAndreas.Sandberg@ARM.com        vio.writeConfig(pkt, offset - OFF_CONFIG);
1719441SAndreas.Sandberg@ARM.com        return 0;
1729441SAndreas.Sandberg@ARM.com    }
1739441SAndreas.Sandberg@ARM.com
1749441SAndreas.Sandberg@ARM.com    panic_if(size != 4, "Unexpected write size @ 0x%x: %u\n", offset, size);
1759441SAndreas.Sandberg@ARM.com    DPRINTF(VIOIface, "    value: 0x%x\n", pkt->get<uint32_t>());
1769441SAndreas.Sandberg@ARM.com    pkt->makeResponse();
1779441SAndreas.Sandberg@ARM.com    write(offset, pkt->get<uint32_t>());
1789441SAndreas.Sandberg@ARM.com    return 0;
1799441SAndreas.Sandberg@ARM.com}
1809441SAndreas.Sandberg@ARM.com
1819441SAndreas.Sandberg@ARM.comvoid
1829441SAndreas.Sandberg@ARM.comMmioVirtIO::write(Addr offset, uint32_t value)
1839441SAndreas.Sandberg@ARM.com{
1849441SAndreas.Sandberg@ARM.com    switch(offset) {
1859441SAndreas.Sandberg@ARM.com      case OFF_HOST_FEATURES_SELECT:
1869441SAndreas.Sandberg@ARM.com        hostFeaturesSelect = value;
1879441SAndreas.Sandberg@ARM.com        return;
1889441SAndreas.Sandberg@ARM.com
1899441SAndreas.Sandberg@ARM.com      case OFF_GUEST_FEATURES:
1909441SAndreas.Sandberg@ARM.com        if (guestFeaturesSelect == 0) {
1919441SAndreas.Sandberg@ARM.com            vio.setGuestFeatures(value);
1929441SAndreas.Sandberg@ARM.com        } else if (value != 0) {
1939441SAndreas.Sandberg@ARM.com            warn("Setting unimplemented guest features register %u: %u\n",
1949441SAndreas.Sandberg@ARM.com                 guestFeaturesSelect, value);
1959441SAndreas.Sandberg@ARM.com        }
1969441SAndreas.Sandberg@ARM.com        return;
1979441SAndreas.Sandberg@ARM.com
1989441SAndreas.Sandberg@ARM.com      case OFF_GUEST_FEATURES_SELECT:
199        guestFeaturesSelect = value;
200        return;
201
202      case OFF_GUEST_PAGE_SIZE:
203        // TODO: We only support 4096 byte pages at the moment
204        panic_if(value != VirtQueue::ALIGN_SIZE,
205                 "Unhandled VirtIO page size: %u", value);
206        pageSize = value;
207        return;
208
209      case OFF_QUEUE_SELECT:
210        vio.setQueueSelect(value);
211        return;
212
213      case OFF_QUEUE_NUM:
214        // TODO: We don't support queue resizing, so ignore this for now.
215        warn_once("Ignoring queue resize hint. Requested size: %u\n", value);
216        return;
217
218      case OFF_QUEUE_ALIGN:
219        // TODO: We currently only support the hard-coded 4k alignment used
220        // in legacy VirtIO.
221        panic_if(value != VirtQueue::ALIGN_SIZE,
222                 "Unhandled VirtIO alignment size: %u", value);
223        return;
224
225      case OFF_QUEUE_PFN:
226        vio.setQueueAddress(value);
227        return;
228
229      case OFF_QUEUE_NOTIFY:
230        vio.onNotify(value);
231        return;
232
233      case OFF_INTERRUPT_ACK:
234        setInterrupts(interruptStatus & (~value));
235        return;
236
237      case OFF_STATUS:
238        panic_if(value > 0xff, "Unexpected status: 0x%x\n", value);
239        vio.setDeviceStatus(value);
240        return;
241
242        /* Read-only registers */
243      case OFF_MAGIC:
244      case OFF_VERSION:
245      case OFF_DEVICE_ID:
246      case OFF_VENDOR_ID:
247      case OFF_HOST_FEATURES:
248      case OFF_QUEUE_NUM_MAX:
249      case OFF_INTERRUPT_STATUS:
250        warn("Guest is trying to write to read-only register 0x%\n",
251             offset);
252        return;
253
254      default:
255        panic("Unhandled read offset (0x%x)\n", offset);
256    }
257}
258
259void
260MmioVirtIO::kick()
261{
262    DPRINTF(VIOIface, "kick(): Sending interrupt...\n");
263    setInterrupts(interruptStatus | INT_USED_RING);
264}
265
266void
267MmioVirtIO::setInterrupts(uint32_t value)
268{
269    const uint32_t old_ints = interruptStatus;
270    interruptStatus = value;
271
272    if (!old_ints && interruptStatus) {
273        interrupt->raise();
274    } else if (old_ints && !interruptStatus) {
275        interrupt->clear();
276    }
277}
278
279
280MmioVirtIO *
281MmioVirtIOParams::create()
282{
283    return new MmioVirtIO(this);
284}
285