base.cc revision 10905
12SN/A/*
21762SN/A * Copyright (c) 2014 ARM Limited
32SN/A * All rights reserved
42SN/A *
52SN/A * The license below extends only to copyright in the software and shall
62SN/A * not be construed as granting a license to any other intellectual
72SN/A * property including but not limited to intellectual property relating
82SN/A * to a hardware implementation of the functionality of the software
92SN/A * licensed hereunder.  You may use the software subject to the license
102SN/A * terms below provided that you ensure that this notice is replicated
112SN/A * unmodified and in its entirety in all distributions of the software,
122SN/A * modified or unmodified, in source code or in binary form.
132SN/A *
142SN/A * Redistribution and use in source and binary forms, with or without
152SN/A * modification, are permitted provided that the following conditions are
162SN/A * met: redistributions of source code must retain the above copyright
172SN/A * notice, this list of conditions and the following disclaimer;
182SN/A * redistributions in binary form must reproduce the above copyright
192SN/A * notice, this list of conditions and the following disclaimer in the
202SN/A * documentation and/or other materials provided with the distribution;
212SN/A * neither the name of the copyright holders nor the names of its
222SN/A * contributors may be used to endorse or promote products derived from
232SN/A * this software without specific prior written permission.
242SN/A *
252SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
262SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
272665Ssaidi@eecs.umich.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
282665Ssaidi@eecs.umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
292665Ssaidi@eecs.umich.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
302SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
312SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
322SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
332SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
342SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
352520SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
362207SN/A *
372207SN/A * Authors: Andreas Sandberg
385543Ssaidi@eecs.umich.edu */
392SN/A
402519SN/A#include "debug/VIO.hh"
412SN/A#include "dev/virtio/base.hh"
422SN/A#include "params/VirtIODeviceBase.hh"
432SN/A
442SN/AVirtDescriptor::VirtDescriptor(PortProxy &_memProxy, VirtQueue &_queue,
45360SN/A                               Index descIndex)
46360SN/A    : memProxy(&_memProxy), queue(&_queue), _index(descIndex),
47360SN/A      desc{0, 0, 0, 0}
48360SN/A{
492207SN/A}
504111Sgblack@eecs.umich.edu
514111Sgblack@eecs.umich.eduVirtDescriptor::VirtDescriptor(VirtDescriptor &&other) noexcept
524155Sgblack@eecs.umich.edu{
535874Sgblack@eecs.umich.edu    *this = std::forward<VirtDescriptor>(other);
545874Sgblack@eecs.umich.edu}
555335Shines@cs.fsu.edu
56360SN/AVirtDescriptor::~VirtDescriptor() noexcept
57360SN/A{
58360SN/A}
59360SN/A
60360SN/AVirtDescriptor &
612207SN/AVirtDescriptor::operator=(VirtDescriptor &&rhs) noexcept
622207SN/A{
63360SN/A    memProxy = std::move(rhs.memProxy);
64360SN/A    queue = std::move(rhs.queue);
652SN/A    _index = std::move(rhs._index);
6612SN/A    desc = std::move(rhs.desc);
672SN/A
6812SN/A    return *this;
692SN/A}
702SN/A
71360SN/Avoid
72360SN/AVirtDescriptor::update()
73360SN/A{
7412SN/A    const Addr vq_addr(queue->getAddress());
75360SN/A    // Check if the queue has been initialized yet
76360SN/A    if (vq_addr == 0)
7712SN/A        return;
782SN/A
792SN/A    assert(_index < queue->getSize());
802SN/A    const Addr desc_addr(vq_addr + sizeof(desc) * _index);
812SN/A    vring_desc guest_desc;
822SN/A    memProxy->readBlob(desc_addr, (uint8_t *)&guest_desc, sizeof(guest_desc));
832520SN/A    desc = vtoh_legacy(guest_desc);
842520SN/A    DPRINTF(VIO,
853812Ssaidi@eecs.umich.edu            "VirtDescriptor(%i): Addr: 0x%x, Len: %i, Flags: 0x%x, "
863812Ssaidi@eecs.umich.edu            "Next: 0x%x\n",
873812Ssaidi@eecs.umich.edu            _index, desc.addr, desc.len, desc.flags, desc.next);
883812Ssaidi@eecs.umich.edu}
892SN/A
905070Ssaidi@eecs.umich.eduvoid
915070Ssaidi@eecs.umich.eduVirtDescriptor::updateChain()
923917Ssaidi@eecs.umich.edu{
93360SN/A    VirtDescriptor *desc(this);
94360SN/A    do {
95360SN/A        desc->update();
962SN/A    } while((desc = desc->next()) != NULL && desc != this);
972SN/A
9812SN/A    if (desc == this)
992420SN/A        panic("Loop in descriptor chain!\n");
1002420SN/A}
1012420SN/A
10212SN/Avoid
10312SN/AVirtDescriptor::dump() const
10412SN/A{
10512SN/A    if (!DTRACE(VIO))
10612SN/A        return;
10712SN/A
10812SN/A    DPRINTF(VIO, "Descriptor[%i]: "
10912SN/A            "Addr: 0x%x, Len: %i, Flags: 0x%x, Next: 0x%x\n",
1102SN/A            _index, desc.addr, desc.len, desc.flags, desc.next);
1112520SN/A
1122472SN/A    if (isIncoming()) {
1132420SN/A        uint8_t data[desc.len];
1142SN/A        read(0, data, desc.len);
11512SN/A        DDUMP(VIO, data, desc.len);
1162472SN/A    }
11712SN/A}
1182SN/A
11912SN/Avoid
12012SN/AVirtDescriptor::dumpChain() const
12112SN/A{
12212SN/A    if (!DTRACE(VIO))
12312SN/A        return;
12412SN/A
12512SN/A    const VirtDescriptor *desc(this);
1263584Ssaidi@eecs.umich.edu    do {
1273584Ssaidi@eecs.umich.edu        desc->dump();
1282SN/A    } while((desc = desc->next()) != NULL);
1292SN/A}
1303584Ssaidi@eecs.umich.edu
1312SN/AVirtDescriptor *
1322SN/AVirtDescriptor::next() const
1332SN/A{
134    if (hasNext()) {
135        return queue->getDescriptor(desc.next);
136    } else {
137        return NULL;
138    }
139}
140
141void
142VirtDescriptor::read(size_t offset, uint8_t *dst, size_t size) const
143{
144    DPRINTF(VIO, "VirtDescriptor(%p, 0x%x, %i)::read: offset: %i, dst: 0x%x, size: %i\n",
145            this, desc.addr, desc.len, offset, (long)dst, size);
146    assert(size <= desc.len - offset);
147    if (!isIncoming())
148        panic("Trying to read from outgoing buffer\n");
149
150    memProxy->readBlob(desc.addr + offset, dst, size);
151}
152
153void
154VirtDescriptor::write(size_t offset, const uint8_t *src, size_t size)
155{
156    DPRINTF(VIO, "VirtDescriptor(%p, 0x%x, %i)::write: offset: %i, src: 0x%x, size: %i\n",
157            this, desc.addr, desc.len, offset, (long)src, size);
158    assert(size <= desc.len - offset);
159    if (!isOutgoing())
160        panic("Trying to write to incoming buffer\n");
161
162    memProxy->writeBlob(desc.addr + offset, const_cast<uint8_t *>(src), size);
163}
164
165void
166VirtDescriptor::chainRead(size_t offset, uint8_t *dst, size_t size) const
167{
168    const VirtDescriptor *desc(this);
169    const size_t full_size(size);
170    do {
171        if (offset < desc->size()) {
172            const size_t chunk_size(std::min(desc->size() - offset, size));
173            desc->read(offset, dst, chunk_size);
174            dst += chunk_size;
175            size -= chunk_size;
176            offset = 0;
177        } else {
178            offset -= desc->size();
179        }
180    } while((desc = desc->next()) != NULL && desc->isIncoming() && size > 0);
181
182    if (size != 0) {
183        panic("Failed to read %i bytes from chain of %i bytes @ offset %i\n",
184              full_size, chainSize(), offset);
185    }
186}
187
188void
189VirtDescriptor::chainWrite(size_t offset, const uint8_t *src, size_t size)
190{
191    VirtDescriptor *desc(this);
192    const size_t full_size(size);
193    do {
194        if (offset < desc->size()) {
195            const size_t chunk_size(std::min(desc->size() - offset, size));
196            desc->write(offset, src, chunk_size);
197            src += chunk_size;
198            size -= chunk_size;
199            offset = 0;
200        } else {
201            offset -= desc->size();
202        }
203    } while((desc = desc->next()) != NULL && size > 0);
204
205    if (size != 0) {
206        panic("Failed to write %i bytes into chain of %i bytes @ offset %i\n",
207              full_size, chainSize(), offset);
208    }
209}
210
211size_t
212VirtDescriptor::chainSize() const
213{
214    size_t size(0);
215    const VirtDescriptor *desc(this);
216    do {
217        size += desc->size();
218    } while((desc = desc->next()) != NULL);
219
220    return size;
221}
222
223
224
225VirtQueue::VirtQueue(PortProxy &proxy, uint16_t size)
226    : _size(size), _address(0), memProxy(proxy),
227      avail(proxy, size), used(proxy, size),
228      _last_avail(0)
229{
230    descriptors.reserve(_size);
231    for (int i = 0; i < _size; ++i)
232        descriptors.emplace_back(proxy, *this, i);
233}
234
235void
236VirtQueue::serialize(CheckpointOut &cp) const
237{
238    SERIALIZE_SCALAR(_address);
239    SERIALIZE_SCALAR(_last_avail);
240}
241
242void
243VirtQueue::unserialize(CheckpointIn &cp)
244{
245    Addr addr_in;
246
247    paramIn(cp, "_address", addr_in);
248    UNSERIALIZE_SCALAR(_last_avail);
249
250    // Use the address setter to ensure that the ring buffer addresses
251    // are updated as well.
252    setAddress(addr_in);
253}
254
255void
256VirtQueue::setAddress(Addr address)
257{
258    const Addr addr_avail(address + _size * sizeof(struct vring_desc));
259    const Addr addr_avail_end(addr_avail + sizeof(struct vring_avail) +
260                              _size * sizeof(uint16_t));
261    const Addr addr_used((addr_avail_end + sizeof(uint16_t) +
262                          (ALIGN_SIZE - 1)) & ~(ALIGN_SIZE - 1));
263    _address = address;
264    avail.setAddress(addr_avail);
265    used.setAddress(addr_used);
266}
267
268VirtDescriptor *
269VirtQueue::consumeDescriptor()
270{
271    avail.read();
272    DPRINTF(VIO, "consumeDescriptor: _last_avail: %i, avail.idx: %i (->%i)\n",
273            _last_avail, avail.header.index,
274            avail.ring[_last_avail % used.ring.size()]);
275    if (_last_avail == avail.header.index)
276        return NULL;
277
278    VirtDescriptor::Index index(avail.ring[_last_avail % used.ring.size()]);
279    ++_last_avail;
280
281    VirtDescriptor *d(&descriptors[index]);
282    d->updateChain();
283
284    return d;
285}
286
287void
288VirtQueue::produceDescriptor(VirtDescriptor *desc, uint32_t len)
289{
290    used.readHeader();
291    DPRINTF(VIO, "produceDescriptor: dscIdx: %i, len: %i, used.idx: %i\n",
292            desc->index(), len, used.header.index);
293
294    struct vring_used_elem &e(used.ring[used.header.index % used.ring.size()]);
295    e.id = desc->index();
296    e.len = len;
297    used.header.index += 1;
298    used.write();
299}
300
301void
302VirtQueue::dump() const
303{
304    if (!DTRACE(VIO))
305        return;
306
307    for (const VirtDescriptor &d : descriptors)
308        d.dump();
309}
310
311void
312VirtQueue::onNotify()
313{
314    DPRINTF(VIO, "onNotify\n");
315
316    // Consume all pending descriptors from the input queue.
317    VirtDescriptor *d;
318    while((d = consumeDescriptor()) != NULL)
319        onNotifyDescriptor(d);
320}
321
322
323VirtIODeviceBase::VirtIODeviceBase(Params *params, DeviceId id,
324                                   size_t config_size, FeatureBits features)
325    : SimObject(params),
326      guestFeatures(0),
327      deviceId(id), configSize(config_size), deviceFeatures(features),
328      _deviceStatus(0), _queueSelect(0),
329      transKick(NULL)
330{
331}
332
333
334VirtIODeviceBase::~VirtIODeviceBase()
335{
336}
337
338void
339VirtIODeviceBase::serialize(CheckpointOut &cp) const
340{
341    SERIALIZE_SCALAR(guestFeatures);
342    SERIALIZE_SCALAR(_deviceStatus);
343    SERIALIZE_SCALAR(_queueSelect);
344    for (QueueID i = 0; i < _queues.size(); ++i)
345        _queues[i]->serializeSection(cp, csprintf("_queues.%i", i));
346}
347
348void
349VirtIODeviceBase::unserialize(CheckpointIn &cp)
350{
351    UNSERIALIZE_SCALAR(guestFeatures);
352    UNSERIALIZE_SCALAR(_deviceStatus);
353    UNSERIALIZE_SCALAR(_queueSelect);
354    for (QueueID i = 0; i < _queues.size(); ++i)
355        _queues[i]->unserializeSection(cp, csprintf("_queues.%i", i));
356}
357
358void
359VirtIODeviceBase::reset()
360{
361    _queueSelect = 0;
362    guestFeatures = 0;
363    _deviceStatus = 0;
364
365    for (QueueID i = 0; i < _queues.size(); ++i)
366        _queues[i]->setAddress(0);
367}
368
369void
370VirtIODeviceBase::onNotify(QueueID idx)
371{
372    DPRINTF(VIO, "onNotify: idx: %i\n", idx);
373    if (idx >= _queues.size()) {
374        panic("Guest tried to notify queue (%i), but only %i "
375              "queues registered.\n",
376              idx, _queues.size());
377    }
378    _queues[idx]->onNotify();
379}
380
381void
382VirtIODeviceBase::setGuestFeatures(FeatureBits features)
383{
384    DPRINTF(VIO, "Setting guest features: 0x%x\n", features);
385    if (~deviceFeatures & features) {
386        panic("Guest tried to enable unsupported features:\n"
387              "Device features: 0x%x\n"
388              "Requested features: 0x%x\n",
389              deviceFeatures, features);
390    }
391    guestFeatures = features;
392}
393
394
395void
396VirtIODeviceBase::setDeviceStatus(DeviceStatus status)
397{
398    _deviceStatus = status;
399    DPRINTF(VIO, "ACK: %i, DRIVER: %i, DRIVER_OK: %i, FAILED: %i\n",
400            status.acknowledge, status.driver, status.driver_ok, status.failed);
401    if (status == 0)
402        reset();
403}
404
405void
406VirtIODeviceBase::readConfig(PacketPtr pkt, Addr cfgOffset)
407{
408    panic("Unhandled device config read (offset: 0x%x).\n", cfgOffset);
409}
410
411void
412VirtIODeviceBase::writeConfig(PacketPtr pkt, Addr cfgOffset)
413{
414    panic("Unhandled device config write (offset: 0x%x).\n", cfgOffset);
415}
416
417void
418VirtIODeviceBase::readConfigBlob(PacketPtr pkt, Addr cfgOffset, const uint8_t *cfg)
419{
420    const unsigned size(pkt->getSize());
421
422    if (cfgOffset + size > configSize)
423        panic("Config read out of bounds.\n");
424
425    pkt->makeResponse();
426    pkt->setData(const_cast<uint8_t *>(cfg) + cfgOffset);
427}
428
429void
430VirtIODeviceBase::writeConfigBlob(PacketPtr pkt, Addr cfgOffset, uint8_t *cfg)
431{
432    const unsigned size(pkt->getSize());
433
434    if (cfgOffset + size > configSize)
435        panic("Config write out of bounds.\n");
436
437    pkt->makeResponse();
438    pkt->writeData((uint8_t *)cfg + cfgOffset);
439}
440
441
442const VirtQueue &
443VirtIODeviceBase::getCurrentQueue() const
444{
445    if (_queueSelect >= _queues.size())
446        panic("Guest tried to access non-existing VirtQueue (%i).\n", _queueSelect);
447
448    return *_queues[_queueSelect];
449}
450
451VirtQueue &
452VirtIODeviceBase::getCurrentQueue()
453{
454    if (_queueSelect >= _queues.size())
455        panic("Guest tried to access non-existing VirtQueue (%i).\n", _queueSelect);
456
457    return *_queues[_queueSelect];
458}
459
460void
461VirtIODeviceBase::setQueueAddress(uint32_t address)
462{
463    getCurrentQueue().setAddress(address * VirtQueue::ALIGN_SIZE);
464}
465
466uint32_t
467VirtIODeviceBase::getQueueAddress() const
468{
469    Addr address(getCurrentQueue().getAddress());
470    assert(!(address & ((1 >> VirtQueue::ALIGN_BITS) - 1)));
471    return address >> VirtQueue::ALIGN_BITS;
472}
473
474void
475VirtIODeviceBase::registerQueue(VirtQueue &queue)
476{
477    _queues.push_back(&queue);
478}
479