base.cc revision 10905
19243SN/A/*
210206Sandreas.hansson@arm.com * Copyright (c) 2014 ARM Limited
39243SN/A * All rights reserved
49243SN/A *
59243SN/A * The license below extends only to copyright in the software and shall
69243SN/A * not be construed as granting a license to any other intellectual
79243SN/A * property including but not limited to intellectual property relating
89243SN/A * to a hardware implementation of the functionality of the software
99243SN/A * licensed hereunder.  You may use the software subject to the license
109243SN/A * terms below provided that you ensure that this notice is replicated
119243SN/A * unmodified and in its entirety in all distributions of the software,
129243SN/A * modified or unmodified, in source code or in binary form.
139243SN/A *
149831SN/A * Redistribution and use in source and binary forms, with or without
159831SN/A * modification, are permitted provided that the following conditions are
169831SN/A * met: redistributions of source code must retain the above copyright
179243SN/A * notice, this list of conditions and the following disclaimer;
189243SN/A * redistributions in binary form must reproduce the above copyright
199243SN/A * notice, this list of conditions and the following disclaimer in the
209243SN/A * documentation and/or other materials provided with the distribution;
219243SN/A * neither the name of the copyright holders nor the names of its
229243SN/A * contributors may be used to endorse or promote products derived from
239243SN/A * this software without specific prior written permission.
249243SN/A *
259243SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
269243SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
279243SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
289243SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
299243SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
309243SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
319243SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
329243SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
339243SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
349243SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
359243SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
369243SN/A *
379243SN/A * Authors: Andreas Sandberg
389243SN/A */
399243SN/A
409243SN/A#include "debug/VIO.hh"
419243SN/A#include "dev/virtio/base.hh"
429967SN/A#include "params/VirtIODeviceBase.hh"
439243SN/A
449243SN/AVirtDescriptor::VirtDescriptor(PortProxy &_memProxy, VirtQueue &_queue,
4510146Sandreas.hansson@arm.com                               Index descIndex)
469356SN/A    : memProxy(&_memProxy), queue(&_queue), _index(descIndex),
4710146Sandreas.hansson@arm.com      desc{0, 0, 0, 0}
4810208Sandreas.hansson@arm.com{
499352SN/A}
5010146Sandreas.hansson@arm.com
519814SN/AVirtDescriptor::VirtDescriptor(VirtDescriptor &&other) noexcept
529243SN/A{
539243SN/A    *this = std::forward<VirtDescriptor>(other);
549243SN/A}
5510146Sandreas.hansson@arm.com
569243SN/AVirtDescriptor::~VirtDescriptor() noexcept
579243SN/A{
589243SN/A}
5910211Sandreas.hansson@arm.com
6010208Sandreas.hansson@arm.comVirtDescriptor &
6110208Sandreas.hansson@arm.comVirtDescriptor::operator=(VirtDescriptor &&rhs) noexcept
6210208Sandreas.hansson@arm.com{
639831SN/A    memProxy = std::move(rhs.memProxy);
649831SN/A    queue = std::move(rhs.queue);
659831SN/A    _index = std::move(rhs._index);
669831SN/A    desc = std::move(rhs.desc);
679831SN/A
6810140SN/A    return *this;
699243SN/A}
709566SN/A
719243SN/Avoid
729243SN/AVirtDescriptor::update()
7310140SN/A{
7410140SN/A    const Addr vq_addr(queue->getAddress());
7510147Sandreas.hansson@arm.com    // Check if the queue has been initialized yet
7610147Sandreas.hansson@arm.com    if (vq_addr == 0)
7710206Sandreas.hansson@arm.com        return;
7810210Sandreas.hansson@arm.com
7910212Sandreas.hansson@arm.com    assert(_index < queue->getSize());
809488SN/A    const Addr desc_addr(vq_addr + sizeof(desc) * _index);
819243SN/A    vring_desc guest_desc;
829243SN/A    memProxy->readBlob(desc_addr, (uint8_t *)&guest_desc, sizeof(guest_desc));
8310141SN/A    desc = vtoh_legacy(guest_desc);
849726SN/A    DPRINTF(VIO,
859726SN/A            "VirtDescriptor(%i): Addr: 0x%x, Len: %i, Flags: 0x%x, "
8610208Sandreas.hansson@arm.com            "Next: 0x%x\n",
8710208Sandreas.hansson@arm.com            _index, desc.addr, desc.len, desc.flags, desc.next);
8810208Sandreas.hansson@arm.com}
899243SN/A
909243SN/Avoid
919243SN/AVirtDescriptor::updateChain()
929243SN/A{
939969SN/A    VirtDescriptor *desc(this);
949243SN/A    do {
959243SN/A        desc->update();
969969SN/A    } while((desc = desc->next()) != NULL && desc != this);
979243SN/A
989243SN/A    if (desc == this)
9910140SN/A        panic("Loop in descriptor chain!\n");
10010140SN/A}
10110140SN/A
10210140SN/Avoid
10310140SN/AVirtDescriptor::dump() const
1049243SN/A{
1059243SN/A    if (!DTRACE(VIO))
1069567SN/A        return;
1079243SN/A
1089243SN/A    DPRINTF(VIO, "Descriptor[%i]: "
1099243SN/A            "Addr: 0x%x, Len: %i, Flags: 0x%x, Next: 0x%x\n",
1109831SN/A            _index, desc.addr, desc.len, desc.flags, desc.next);
1119831SN/A
1129831SN/A    if (isIncoming()) {
1139831SN/A        uint8_t data[desc.len];
1149831SN/A        read(0, data, desc.len);
1159243SN/A        DDUMP(VIO, data, desc.len);
1169566SN/A    }
1179566SN/A}
11810143SN/A
1199566SN/Avoid
1209566SN/AVirtDescriptor::dumpChain() const
12110136SN/A{
1229831SN/A    if (!DTRACE(VIO))
12310143SN/A        return;
12410136SN/A
1259566SN/A    const VirtDescriptor *desc(this);
12610136SN/A    do {
12710136SN/A        desc->dump();
12810143SN/A    } while((desc = desc->next()) != NULL);
12910136SN/A}
1309669SN/A
13110136SN/AVirtDescriptor *
13210136SN/AVirtDescriptor::next() const
13310143SN/A{
13410136SN/A    if (hasNext()) {
1359566SN/A        return queue->getDescriptor(desc.next);
1369566SN/A    } else {
13710207Sandreas.hansson@arm.com        return NULL;
13810207Sandreas.hansson@arm.com    }
13910207Sandreas.hansson@arm.com}
14010207Sandreas.hansson@arm.com
14110207Sandreas.hansson@arm.comvoid
14210207Sandreas.hansson@arm.comVirtDescriptor::read(size_t offset, uint8_t *dst, size_t size) const
1439243SN/A{
1449243SN/A    DPRINTF(VIO, "VirtDescriptor(%p, 0x%x, %i)::read: offset: %i, dst: 0x%x, size: %i\n",
1459243SN/A            this, desc.addr, desc.len, offset, (long)dst, size);
14610146Sandreas.hansson@arm.com    assert(size <= desc.len - offset);
14710140SN/A    if (!isIncoming())
14810140SN/A        panic("Trying to read from outgoing buffer\n");
14910146Sandreas.hansson@arm.com
15010140SN/A    memProxy->readBlob(desc.addr + offset, dst, size);
15110140SN/A}
15210140SN/A
15310140SN/Avoid
15410140SN/AVirtDescriptor::write(size_t offset, const uint8_t *src, size_t size)
15510140SN/A{
15610146Sandreas.hansson@arm.com    DPRINTF(VIO, "VirtDescriptor(%p, 0x%x, %i)::write: offset: %i, src: 0x%x, size: %i\n",
1579243SN/A            this, desc.addr, desc.len, offset, (long)src, size);
15810143SN/A    assert(size <= desc.len - offset);
15910143SN/A    if (!isOutgoing())
16010208Sandreas.hansson@arm.com        panic("Trying to write to incoming buffer\n");
16110143SN/A
16210206Sandreas.hansson@arm.com    memProxy->writeBlob(desc.addr + offset, const_cast<uint8_t *>(src), size);
16310206Sandreas.hansson@arm.com}
16410206Sandreas.hansson@arm.com
16510206Sandreas.hansson@arm.comvoid
16610206Sandreas.hansson@arm.comVirtDescriptor::chainRead(size_t offset, uint8_t *dst, size_t size) const
16710206Sandreas.hansson@arm.com{
16810207Sandreas.hansson@arm.com    const VirtDescriptor *desc(this);
16910207Sandreas.hansson@arm.com    const size_t full_size(size);
17010207Sandreas.hansson@arm.com    do {
1719243SN/A        if (offset < desc->size()) {
1729243SN/A            const size_t chunk_size(std::min(desc->size() - offset, size));
1739243SN/A            desc->read(offset, dst, chunk_size);
17410146Sandreas.hansson@arm.com            dst += chunk_size;
1759243SN/A            size -= chunk_size;
1769243SN/A            offset = 0;
1779243SN/A        } else {
1789243SN/A            offset -= desc->size();
1799243SN/A        }
1809243SN/A    } while((desc = desc->next()) != NULL && desc->isIncoming() && size > 0);
1819243SN/A
1829243SN/A    if (size != 0) {
1839243SN/A        panic("Failed to read %i bytes from chain of %i bytes @ offset %i\n",
1849243SN/A              full_size, chainSize(), offset);
1859243SN/A    }
1869243SN/A}
1879243SN/A
1889243SN/Avoid
1899243SN/AVirtDescriptor::chainWrite(size_t offset, const uint8_t *src, size_t size)
1909243SN/A{
19110146Sandreas.hansson@arm.com    VirtDescriptor *desc(this);
1929243SN/A    const size_t full_size(size);
1939831SN/A    do {
1949831SN/A        if (offset < desc->size()) {
1959831SN/A            const size_t chunk_size(std::min(desc->size() - offset, size));
1969243SN/A            desc->write(offset, src, chunk_size);
1979831SN/A            src += chunk_size;
1989831SN/A            size -= chunk_size;
1999243SN/A            offset = 0;
2009243SN/A        } else {
2019243SN/A            offset -= desc->size();
20210146Sandreas.hansson@arm.com        }
2039243SN/A    } while((desc = desc->next()) != NULL && size > 0);
2049831SN/A
2059831SN/A    if (size != 0) {
2069831SN/A        panic("Failed to write %i bytes into chain of %i bytes @ offset %i\n",
2079243SN/A              full_size, chainSize(), offset);
2089243SN/A    }
20910146Sandreas.hansson@arm.com}
21010146Sandreas.hansson@arm.com
21110143SN/Asize_t
2129243SN/AVirtDescriptor::chainSize() const
2139669SN/A{
21410136SN/A    size_t size(0);
21510136SN/A    const VirtDescriptor *desc(this);
2169243SN/A    do {
2179967SN/A        size += desc->size();
2189243SN/A    } while((desc = desc->next()) != NULL);
2199243SN/A
2209243SN/A    return size;
2219831SN/A}
2229243SN/A
2239491SN/A
2249831SN/A
22510136SN/AVirtQueue::VirtQueue(PortProxy &proxy, uint16_t size)
2269491SN/A    : _size(size), _address(0), memProxy(proxy),
2279491SN/A      avail(proxy, size), used(proxy, size),
2289831SN/A      _last_avail(0)
2299243SN/A{
2309669SN/A    descriptors.reserve(_size);
2319566SN/A    for (int i = 0; i < _size; ++i)
2329566SN/A        descriptors.emplace_back(proxy, *this, i);
2339669SN/A}
2349669SN/A
2359669SN/Avoid
2369669SN/AVirtQueue::serialize(CheckpointOut &cp) const
2379669SN/A{
2389669SN/A    SERIALIZE_SCALAR(_address);
2399669SN/A    SERIALIZE_SCALAR(_last_avail);
2409669SN/A}
2419669SN/A
2429669SN/Avoid
2439669SN/AVirtQueue::unserialize(CheckpointIn &cp)
2449669SN/A{
2459669SN/A    Addr addr_in;
24610136SN/A
2479669SN/A    paramIn(cp, "_address", addr_in);
2489669SN/A    UNSERIALIZE_SCALAR(_last_avail);
2499669SN/A
2509669SN/A    // Use the address setter to ensure that the ring buffer addresses
2519831SN/A    // are updated as well.
2529669SN/A    setAddress(addr_in);
2539669SN/A}
2549491SN/A
2559243SN/Avoid
2569243SN/AVirtQueue::setAddress(Addr address)
2579243SN/A{
2589491SN/A    const Addr addr_avail(address + _size * sizeof(struct vring_desc));
2599491SN/A    const Addr addr_avail_end(addr_avail + sizeof(struct vring_avail) +
2609243SN/A                              _size * sizeof(uint16_t));
2619243SN/A    const Addr addr_used((addr_avail_end + sizeof(uint16_t) +
2629243SN/A                          (ALIGN_SIZE - 1)) & ~(ALIGN_SIZE - 1));
2639491SN/A    _address = address;
2649243SN/A    avail.setAddress(addr_avail);
2659243SN/A    used.setAddress(addr_used);
26610136SN/A}
2679491SN/A
2689491SN/AVirtDescriptor *
2699491SN/AVirtQueue::consumeDescriptor()
2709566SN/A{
2719566SN/A    avail.read();
2729566SN/A    DPRINTF(VIO, "consumeDescriptor: _last_avail: %i, avail.idx: %i (->%i)\n",
2739566SN/A            _last_avail, avail.header.index,
2749566SN/A            avail.ring[_last_avail % used.ring.size()]);
2759491SN/A    if (_last_avail == avail.header.index)
2769491SN/A        return NULL;
2779243SN/A
2789243SN/A    VirtDescriptor::Index index(avail.ring[_last_avail % used.ring.size()]);
2799243SN/A    ++_last_avail;
2809491SN/A
2819243SN/A    VirtDescriptor *d(&descriptors[index]);
2829243SN/A    d->updateChain();
2839243SN/A
2849491SN/A    return d;
2859491SN/A}
2869831SN/A
2879243SN/Avoid
2889491SN/AVirtQueue::produceDescriptor(VirtDescriptor *desc, uint32_t len)
2899243SN/A{
2909243SN/A    used.readHeader();
2919243SN/A    DPRINTF(VIO, "produceDescriptor: dscIdx: %i, len: %i, used.idx: %i\n",
2929243SN/A            desc->index(), len, used.header.index);
2939243SN/A
2949243SN/A    struct vring_used_elem &e(used.ring[used.header.index % used.ring.size()]);
2959243SN/A    e.id = desc->index();
2969243SN/A    e.len = len;
2979243SN/A    used.header.index += 1;
2989243SN/A    used.write();
2999831SN/A}
3009243SN/A
3019243SN/Avoid
3029567SN/AVirtQueue::dump() const
3039567SN/A{
3049967SN/A    if (!DTRACE(VIO))
3059967SN/A        return;
3069967SN/A
3079243SN/A    for (const VirtDescriptor &d : descriptors)
3089243SN/A        d.dump();
3099243SN/A}
31010146Sandreas.hansson@arm.com
3119243SN/Avoid
3129243SN/AVirtQueue::onNotify()
3139243SN/A{
3149243SN/A    DPRINTF(VIO, "onNotify\n");
3159243SN/A
3169831SN/A    // Consume all pending descriptors from the input queue.
3179831SN/A    VirtDescriptor *d;
3189831SN/A    while((d = consumeDescriptor()) != NULL)
3199831SN/A        onNotifyDescriptor(d);
3209831SN/A}
3219831SN/A
3229831SN/A
3239831SN/AVirtIODeviceBase::VirtIODeviceBase(Params *params, DeviceId id,
3249243SN/A                                   size_t config_size, FeatureBits features)
3259831SN/A    : SimObject(params),
3269831SN/A      guestFeatures(0),
3279831SN/A      deviceId(id), configSize(config_size), deviceFeatures(features),
3289831SN/A      _deviceStatus(0), _queueSelect(0),
3299831SN/A      transKick(NULL)
3309831SN/A{
3319831SN/A}
3329243SN/A
3339831SN/A
3349831SN/AVirtIODeviceBase::~VirtIODeviceBase()
3359831SN/A{
3369833SN/A}
3379832SN/A
3389832SN/Avoid
3399832SN/AVirtIODeviceBase::serialize(CheckpointOut &cp) const
3409832SN/A{
3419831SN/A    SERIALIZE_SCALAR(guestFeatures);
3429831SN/A    SERIALIZE_SCALAR(_deviceStatus);
3439831SN/A    SERIALIZE_SCALAR(_queueSelect);
3449831SN/A    for (QueueID i = 0; i < _queues.size(); ++i)
3459831SN/A        _queues[i]->serializeSection(cp, csprintf("_queues.%i", i));
3469975SN/A}
3479831SN/A
3489831SN/Avoid
3499243SN/AVirtIODeviceBase::unserialize(CheckpointIn &cp)
3509831SN/A{
3519831SN/A    UNSERIALIZE_SCALAR(guestFeatures);
3529831SN/A    UNSERIALIZE_SCALAR(_deviceStatus);
3539831SN/A    UNSERIALIZE_SCALAR(_queueSelect);
3549831SN/A    for (QueueID i = 0; i < _queues.size(); ++i)
3559831SN/A        _queues[i]->unserializeSection(cp, csprintf("_queues.%i", i));
3569831SN/A}
3579831SN/A
3589831SN/Avoid
3599831SN/AVirtIODeviceBase::reset()
3609831SN/A{
3619831SN/A    _queueSelect = 0;
3629966SN/A    guestFeatures = 0;
3639831SN/A    _deviceStatus = 0;
3649831SN/A
3659831SN/A    for (QueueID i = 0; i < _queues.size(); ++i)
3669831SN/A        _queues[i]->setAddress(0);
3679831SN/A}
3689831SN/A
3699831SN/Avoid
3709831SN/AVirtIODeviceBase::onNotify(QueueID idx)
3719831SN/A{
3729831SN/A    DPRINTF(VIO, "onNotify: idx: %i\n", idx);
3739831SN/A    if (idx >= _queues.size()) {
3749831SN/A        panic("Guest tried to notify queue (%i), but only %i "
3759831SN/A              "queues registered.\n",
3769831SN/A              idx, _queues.size());
3779831SN/A    }
3789243SN/A    _queues[idx]->onNotify();
3799243SN/A}
3809831SN/A
3819831SN/Avoid
3829831SN/AVirtIODeviceBase::setGuestFeatures(FeatureBits features)
3839831SN/A{
3849831SN/A    DPRINTF(VIO, "Setting guest features: 0x%x\n", features);
3859243SN/A    if (~deviceFeatures & features) {
3869831SN/A        panic("Guest tried to enable unsupported features:\n"
3879831SN/A              "Device features: 0x%x\n"
3889831SN/A              "Requested features: 0x%x\n",
3899243SN/A              deviceFeatures, features);
39010206Sandreas.hansson@arm.com    }
39110206Sandreas.hansson@arm.com    guestFeatures = features;
39210206Sandreas.hansson@arm.com}
3939567SN/A
3949567SN/A
3959243SN/Avoid
3969243SN/AVirtIODeviceBase::setDeviceStatus(DeviceStatus status)
3979243SN/A{
3989243SN/A    _deviceStatus = status;
39910146Sandreas.hansson@arm.com    DPRINTF(VIO, "ACK: %i, DRIVER: %i, DRIVER_OK: %i, FAILED: %i\n",
4009243SN/A            status.acknowledge, status.driver, status.driver_ok, status.failed);
4019243SN/A    if (status == 0)
4029243SN/A        reset();
4039243SN/A}
4049243SN/A
4059831SN/Avoid
4069831SN/AVirtIODeviceBase::readConfig(PacketPtr pkt, Addr cfgOffset)
4079831SN/A{
4089831SN/A    panic("Unhandled device config read (offset: 0x%x).\n", cfgOffset);
4099831SN/A}
4109831SN/A
4119831SN/Avoid
4129831SN/AVirtIODeviceBase::writeConfig(PacketPtr pkt, Addr cfgOffset)
4139243SN/A{
4149832SN/A    panic("Unhandled device config write (offset: 0x%x).\n", cfgOffset);
4159838SN/A}
4169838SN/A
4179838SN/Avoid
4189832SN/AVirtIODeviceBase::readConfigBlob(PacketPtr pkt, Addr cfgOffset, const uint8_t *cfg)
4199832SN/A{
4209243SN/A    const unsigned size(pkt->getSize());
4219832SN/A
4229832SN/A    if (cfgOffset + size > configSize)
4239832SN/A        panic("Config read out of bounds.\n");
4249832SN/A
4259838SN/A    pkt->makeResponse();
4269838SN/A    pkt->setData(const_cast<uint8_t *>(cfg) + cfgOffset);
4279838SN/A}
4289832SN/A
4299832SN/Avoid
4309832SN/AVirtIODeviceBase::writeConfigBlob(PacketPtr pkt, Addr cfgOffset, uint8_t *cfg)
4319832SN/A{
4329832SN/A    const unsigned size(pkt->getSize());
4339832SN/A
4349832SN/A    if (cfgOffset + size > configSize)
4359832SN/A        panic("Config write out of bounds.\n");
4369832SN/A
4379832SN/A    pkt->makeResponse();
4389832SN/A    pkt->writeData((uint8_t *)cfg + cfgOffset);
4399832SN/A}
4409832SN/A
4419832SN/A
4429832SN/Aconst VirtQueue &
4439832SN/AVirtIODeviceBase::getCurrentQueue() const
4449832SN/A{
44510047SN/A    if (_queueSelect >= _queues.size())
4469832SN/A        panic("Guest tried to access non-existing VirtQueue (%i).\n", _queueSelect);
4479832SN/A
4489832SN/A    return *_queues[_queueSelect];
4499838SN/A}
4509838SN/A
4519838SN/AVirtQueue &
4529832SN/AVirtIODeviceBase::getCurrentQueue()
4539832SN/A{
4549832SN/A    if (_queueSelect >= _queues.size())
4559832SN/A        panic("Guest tried to access non-existing VirtQueue (%i).\n", _queueSelect);
4569832SN/A
4579832SN/A    return *_queues[_queueSelect];
4589832SN/A}
4599832SN/A
4609832SN/Avoid
4619832SN/AVirtIODeviceBase::setQueueAddress(uint32_t address)
4629832SN/A{
4639832SN/A    getCurrentQueue().setAddress(address * VirtQueue::ALIGN_SIZE);
4649832SN/A}
4659832SN/A
4669832SN/Auint32_t
4679832SN/AVirtIODeviceBase::getQueueAddress() const
4689832SN/A{
4699832SN/A    Addr address(getCurrentQueue().getAddress());
4709832SN/A    assert(!(address & ((1 >> VirtQueue::ALIGN_BITS) - 1)));
4719832SN/A    return address >> VirtQueue::ALIGN_BITS;
4729243SN/A}
4739832SN/A
4749832SN/Avoid
4759832SN/AVirtIODeviceBase::registerQueue(VirtQueue &queue)
4769966SN/A{
4779243SN/A    _queues.push_back(&queue);
4789832SN/A}
4799832SN/A