base.cc revision 14010:0e1e887507c0
12567SN/A/*
211234Sandreas.sandberg@arm.com * Copyright (c) 2014, 2016 ARM Limited
37650SAli.Saidi@ARM.com * All rights reserved
47650SAli.Saidi@ARM.com *
57650SAli.Saidi@ARM.com * The license below extends only to copyright in the software and shall
67650SAli.Saidi@ARM.com * not be construed as granting a license to any other intellectual
77650SAli.Saidi@ARM.com * property including but not limited to intellectual property relating
87650SAli.Saidi@ARM.com * to a hardware implementation of the functionality of the software
97650SAli.Saidi@ARM.com * licensed hereunder.  You may use the software subject to the license
107650SAli.Saidi@ARM.com * terms below provided that you ensure that this notice is replicated
117650SAli.Saidi@ARM.com * unmodified and in its entirety in all distributions of the software,
127650SAli.Saidi@ARM.com * modified or unmodified, in source code or in binary form.
137650SAli.Saidi@ARM.com *
142567SN/A * Redistribution and use in source and binary forms, with or without
152567SN/A * modification, are permitted provided that the following conditions are
162567SN/A * met: redistributions of source code must retain the above copyright
172567SN/A * notice, this list of conditions and the following disclaimer;
182567SN/A * redistributions in binary form must reproduce the above copyright
192567SN/A * notice, this list of conditions and the following disclaimer in the
202567SN/A * documentation and/or other materials provided with the distribution;
212567SN/A * neither the name of the copyright holders nor the names of its
222567SN/A * contributors may be used to endorse or promote products derived from
232567SN/A * this software without specific prior written permission.
242567SN/A *
252567SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
262567SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
272567SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
282567SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
292567SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
302567SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
312567SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
322567SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
332567SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
342567SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
352567SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
362567SN/A *
372567SN/A * Authors: Andreas Sandberg
382567SN/A */
392665SN/A
402665SN/A#include "dev/virtio/base.hh"
412567SN/A
422567SN/A#include "debug/VIO.hh"
436757SAli.Saidi@ARM.com#include "params/VirtIODeviceBase.hh"
446757SAli.Saidi@ARM.com#include "params/VirtIODummyDevice.hh"
452567SN/A
4611234Sandreas.sandberg@arm.comVirtDescriptor::VirtDescriptor(PortProxy &_memProxy, VirtQueue &_queue,
472567SN/A                               Index descIndex)
482567SN/A    : memProxy(&_memProxy), queue(&_queue), _index(descIndex),
492567SN/A      desc{0, 0, 0, 0}
508229Snate@binkert.org{
516757SAli.Saidi@ARM.com}
5210810Sbr@bsdpad.com
532567SN/AVirtDescriptor::VirtDescriptor(VirtDescriptor &&other) noexcept
542567SN/A{
552567SN/A    *this = std::forward<VirtDescriptor>(other);
5610844Sandreas.sandberg@arm.com}
5710037SARM gem5 Developers
5810037SARM gem5 DevelopersVirtDescriptor::~VirtDescriptor() noexcept
596757SAli.Saidi@ARM.com{
602567SN/A}
618285SPrakash.Ramrakhyani@arm.com
627650SAli.Saidi@ARM.comVirtDescriptor &
637650SAli.Saidi@ARM.comVirtDescriptor::operator=(VirtDescriptor &&rhs) noexcept
647650SAli.Saidi@ARM.com{
657650SAli.Saidi@ARM.com    memProxy = std::move(rhs.memProxy);
667650SAli.Saidi@ARM.com    queue = std::move(rhs.queue);
677650SAli.Saidi@ARM.com    _index = std::move(rhs._index);
6811234Sandreas.sandberg@arm.com    desc = std::move(rhs.desc);
6911234Sandreas.sandberg@arm.com
7011234Sandreas.sandberg@arm.com    return *this;
718286SAli.Saidi@ARM.com}
728286SAli.Saidi@ARM.com
738286SAli.Saidi@ARM.comvoid
748286SAli.Saidi@ARM.comVirtDescriptor::update()
758286SAli.Saidi@ARM.com{
7610037SARM gem5 Developers    const Addr vq_addr(queue->getAddress());
7710037SARM gem5 Developers    // Check if the queue has been initialized yet
7810037SARM gem5 Developers    if (vq_addr == 0)
7910037SARM gem5 Developers        return;
8010037SARM gem5 Developers
8110037SARM gem5 Developers    assert(_index < queue->getSize());
8210037SARM gem5 Developers    const Addr desc_addr(vq_addr + sizeof(desc) * _index);
8310037SARM gem5 Developers    vring_desc guest_desc;
8410037SARM gem5 Developers    memProxy->readBlob(desc_addr, &guest_desc, sizeof(guest_desc));
8510037SARM gem5 Developers    desc = vtoh_legacy(guest_desc);
8610037SARM gem5 Developers    DPRINTF(VIO,
8710037SARM gem5 Developers            "VirtDescriptor(%i): Addr: 0x%x, Len: %i, Flags: 0x%x, "
8810037SARM gem5 Developers            "Next: 0x%x\n",
8910037SARM gem5 Developers            _index, desc.addr, desc.len, desc.flags, desc.next);
9010037SARM gem5 Developers}
9110037SARM gem5 Developers
9210037SARM gem5 Developersvoid
9310037SARM gem5 DevelopersVirtDescriptor::updateChain()
9410037SARM gem5 Developers{
9510037SARM gem5 Developers    VirtDescriptor *desc(this);
9610037SARM gem5 Developers    do {
9710037SARM gem5 Developers        desc->update();
9810037SARM gem5 Developers    } while ((desc = desc->next()) != NULL && desc != this);
9910037SARM gem5 Developers
10010037SARM gem5 Developers    if (desc == this)
10110037SARM gem5 Developers        panic("Loop in descriptor chain!\n");
10210037SARM gem5 Developers}
10310037SARM gem5 Developers
10410037SARM gem5 Developersvoid
10510037SARM gem5 DevelopersVirtDescriptor::dump() const
10610037SARM gem5 Developers{
10710037SARM gem5 Developers    if (!DTRACE(VIO))
10810037SARM gem5 Developers        return;
10910037SARM gem5 Developers
11010037SARM gem5 Developers    DPRINTF(VIO, "Descriptor[%i]: "
11110037SARM gem5 Developers            "Addr: 0x%x, Len: %i, Flags: 0x%x, Next: 0x%x\n",
11210037SARM gem5 Developers            _index, desc.addr, desc.len, desc.flags, desc.next);
11310037SARM gem5 Developers
11410037SARM gem5 Developers    if (isIncoming()) {
11510037SARM gem5 Developers        uint8_t data[desc.len];
11610037SARM gem5 Developers        read(0, data, desc.len);
11710037SARM gem5 Developers        DDUMP(VIO, data, desc.len);
11810037SARM gem5 Developers    }
11911234Sandreas.sandberg@arm.com}
12011234Sandreas.sandberg@arm.com
12111234Sandreas.sandberg@arm.comvoid
12211234Sandreas.sandberg@arm.comVirtDescriptor::dumpChain() const
12311234Sandreas.sandberg@arm.com{
12411234Sandreas.sandberg@arm.com    if (!DTRACE(VIO))
12511234Sandreas.sandberg@arm.com        return;
12611234Sandreas.sandberg@arm.com
12711234Sandreas.sandberg@arm.com    const VirtDescriptor *desc(this);
12811234Sandreas.sandberg@arm.com    do {
1292567SN/A        desc->dump();
1306757SAli.Saidi@ARM.com    } while ((desc = desc->next()) != NULL);
1318286SAli.Saidi@ARM.com}
1328286SAli.Saidi@ARM.com
1338286SAli.Saidi@ARM.comVirtDescriptor *
1348286SAli.Saidi@ARM.comVirtDescriptor::next() const
1358286SAli.Saidi@ARM.com{
1368286SAli.Saidi@ARM.com    if (hasNext()) {
1376757SAli.Saidi@ARM.com        return queue->getDescriptor(desc.next);
1386757SAli.Saidi@ARM.com    } else {
1398286SAli.Saidi@ARM.com        return NULL;
1408706Sandreas.hansson@arm.com    }
1418706Sandreas.hansson@arm.com}
1428706Sandreas.hansson@arm.com
1438706Sandreas.hansson@arm.comvoid
1448286SAli.Saidi@ARM.comVirtDescriptor::read(size_t offset, uint8_t *dst, size_t size) const
1453553SN/A{
1463553SN/A    DPRINTF(VIO, "VirtDescriptor(%p, 0x%x, %i)::read: offset: %i, dst: 0x%x, size: %i\n",
1477693SAli.Saidi@ARM.com            this, desc.addr, desc.len, offset, (long)dst, size);
1487693SAli.Saidi@ARM.com    assert(size <= desc.len - offset);
1497693SAli.Saidi@ARM.com    if (!isIncoming())
1507720Sgblack@eecs.umich.edu        panic("Trying to read from outgoing buffer\n");
1513553SN/A
1523553SN/A    memProxy->readBlob(desc.addr + offset, dst, size);
1539050Schander.sudanthi@arm.com}
1549050Schander.sudanthi@arm.com
1559050Schander.sudanthi@arm.comvoid
15610037SARM gem5 DevelopersVirtDescriptor::write(size_t offset, const uint8_t *src, size_t size)
15710037SARM gem5 Developers{
15810037SARM gem5 Developers    DPRINTF(VIO, "VirtDescriptor(%p, 0x%x, %i)::write: offset: %i, src: 0x%x, size: %i\n",
15910037SARM gem5 Developers            this, desc.addr, desc.len, offset, (long)src, size);
16010037SARM gem5 Developers    assert(size <= desc.len - offset);
16110037SARM gem5 Developers    if (!isOutgoing())
16210037SARM gem5 Developers        panic("Trying to write to incoming buffer\n");
16310037SARM gem5 Developers
16410037SARM gem5 Developers    memProxy->writeBlob(desc.addr + offset, src, size);
16510037SARM gem5 Developers}
16610037SARM gem5 Developers
16710037SARM gem5 Developersvoid
16810037SARM gem5 DevelopersVirtDescriptor::chainRead(size_t offset, uint8_t *dst, size_t size) const
16910037SARM gem5 Developers{
17010037SARM gem5 Developers    const VirtDescriptor *desc(this);
17110037SARM gem5 Developers    const size_t full_size(size);
17210037SARM gem5 Developers    do {
17310037SARM gem5 Developers        if (offset < desc->size()) {
17410037SARM gem5 Developers            const size_t chunk_size(std::min(desc->size() - offset, size));
17510844Sandreas.sandberg@arm.com            desc->read(offset, dst, chunk_size);
17610844Sandreas.sandberg@arm.com            dst += chunk_size;
17710037SARM gem5 Developers            size -= chunk_size;
17810037SARM gem5 Developers            offset = 0;
17910037SARM gem5 Developers        } else {
18010037SARM gem5 Developers            offset -= desc->size();
18110037SARM gem5 Developers        }
18210037SARM gem5 Developers    } while ((desc = desc->next()) != NULL && desc->isIncoming() && size > 0);
18310037SARM gem5 Developers
18410037SARM gem5 Developers    if (size != 0) {
18510037SARM gem5 Developers        panic("Failed to read %i bytes from chain of %i bytes @ offset %i\n",
18610037SARM gem5 Developers              full_size, chainSize(), offset);
18710037SARM gem5 Developers    }
18810037SARM gem5 Developers}
18910037SARM gem5 Developers
19010037SARM gem5 Developersvoid
19110037SARM gem5 DevelopersVirtDescriptor::chainWrite(size_t offset, const uint8_t *src, size_t size)
19210037SARM gem5 Developers{
19310037SARM gem5 Developers    VirtDescriptor *desc(this);
19410037SARM gem5 Developers    const size_t full_size(size);
19510037SARM gem5 Developers    do {
19610037SARM gem5 Developers        if (offset < desc->size()) {
19710037SARM gem5 Developers            const size_t chunk_size(std::min(desc->size() - offset, size));
19810037SARM gem5 Developers            desc->write(offset, src, chunk_size);
19910037SARM gem5 Developers            src += chunk_size;
20010037SARM gem5 Developers            size -= chunk_size;
20110037SARM gem5 Developers            offset = 0;
20210037SARM gem5 Developers        } else {
20310037SARM gem5 Developers            offset -= desc->size();
20410037SARM gem5 Developers        }
20510037SARM gem5 Developers    } while ((desc = desc->next()) != NULL && size > 0);
20610037SARM gem5 Developers
20710037SARM gem5 Developers    if (size != 0) {
20810037SARM gem5 Developers        panic("Failed to write %i bytes into chain of %i bytes @ offset %i\n",
20910037SARM gem5 Developers              full_size, chainSize(), offset);
21010037SARM gem5 Developers    }
21110037SARM gem5 Developers}
21210037SARM gem5 Developers
21310037SARM gem5 Developerssize_t
21410037SARM gem5 DevelopersVirtDescriptor::chainSize() const
21510037SARM gem5 Developers{
21610037SARM gem5 Developers    size_t size(0);
21710037SARM gem5 Developers    const VirtDescriptor *desc(this);
21810037SARM gem5 Developers    do {
21910037SARM gem5 Developers        size += desc->size();
22010037SARM gem5 Developers    } while ((desc = desc->next()) != NULL);
22110037SARM gem5 Developers
22210037SARM gem5 Developers    return size;
22310037SARM gem5 Developers}
22410037SARM gem5 Developers
22510037SARM gem5 Developers
22610037SARM gem5 Developers
22710037SARM gem5 DevelopersVirtQueue::VirtQueue(PortProxy &proxy, uint16_t size)
22810037SARM gem5 Developers    : _size(size), _address(0), memProxy(proxy),
22910037SARM gem5 Developers      avail(proxy, size), used(proxy, size),
23010037SARM gem5 Developers      _last_avail(0)
23110037SARM gem5 Developers{
23210037SARM gem5 Developers    descriptors.reserve(_size);
23310037SARM gem5 Developers    for (int i = 0; i < _size; ++i)
23410037SARM gem5 Developers        descriptors.emplace_back(proxy, *this, i);
23510037SARM gem5 Developers}
23610037SARM gem5 Developers
23710037SARM gem5 Developersvoid
23810037SARM gem5 DevelopersVirtQueue::serialize(CheckpointOut &cp) const
23910037SARM gem5 Developers{
24010037SARM gem5 Developers    SERIALIZE_SCALAR(_address);
24110037SARM gem5 Developers    SERIALIZE_SCALAR(_last_avail);
24210037SARM gem5 Developers}
24310037SARM gem5 Developers
24410037SARM gem5 Developersvoid
24510037SARM gem5 DevelopersVirtQueue::unserialize(CheckpointIn &cp)
24610037SARM gem5 Developers{
24710037SARM gem5 Developers    Addr addr_in;
24810037SARM gem5 Developers
24910037SARM gem5 Developers    paramIn(cp, "_address", addr_in);
25010037SARM gem5 Developers    UNSERIALIZE_SCALAR(_last_avail);
25110037SARM gem5 Developers
25210037SARM gem5 Developers    // Use the address setter to ensure that the ring buffer addresses
25310037SARM gem5 Developers    // are updated as well.
25410037SARM gem5 Developers    setAddress(addr_in);
25510037SARM gem5 Developers}
25610037SARM gem5 Developers
25710037SARM gem5 Developersvoid
25810037SARM gem5 DevelopersVirtQueue::setAddress(Addr address)
25910037SARM gem5 Developers{
26010037SARM gem5 Developers    const Addr addr_avail(address + _size * sizeof(struct vring_desc));
26110037SARM gem5 Developers    const Addr addr_avail_end(addr_avail + sizeof(struct vring_avail) +
26210037SARM gem5 Developers                              _size * sizeof(uint16_t));
26310810Sbr@bsdpad.com    const Addr addr_used((addr_avail_end + sizeof(uint16_t) +
26410037SARM gem5 Developers                          (ALIGN_SIZE - 1)) & ~(ALIGN_SIZE - 1));
26510810Sbr@bsdpad.com    _address = address;
26610810Sbr@bsdpad.com    avail.setAddress(addr_avail);
26710810Sbr@bsdpad.com    used.setAddress(addr_used);
26810810Sbr@bsdpad.com}
26910810Sbr@bsdpad.com
27010810Sbr@bsdpad.comVirtDescriptor *
27110810Sbr@bsdpad.comVirtQueue::consumeDescriptor()
27210810Sbr@bsdpad.com{
27310810Sbr@bsdpad.com    avail.read();
27410810Sbr@bsdpad.com    DPRINTF(VIO, "consumeDescriptor: _last_avail: %i, avail.idx: %i (->%i)\n",
27510810Sbr@bsdpad.com            _last_avail, avail.header.index,
27610810Sbr@bsdpad.com            avail.ring[_last_avail % used.ring.size()]);
27710810Sbr@bsdpad.com    if (_last_avail == avail.header.index)
27810810Sbr@bsdpad.com        return NULL;
27910810Sbr@bsdpad.com
28010810Sbr@bsdpad.com    VirtDescriptor::Index index(avail.ring[_last_avail % used.ring.size()]);
28110810Sbr@bsdpad.com    ++_last_avail;
2822567SN/A
2832567SN/A    VirtDescriptor *d(&descriptors[index]);
2842567SN/A    d->updateChain();
285
286    return d;
287}
288
289void
290VirtQueue::produceDescriptor(VirtDescriptor *desc, uint32_t len)
291{
292    used.readHeader();
293    DPRINTF(VIO, "produceDescriptor: dscIdx: %i, len: %i, used.idx: %i\n",
294            desc->index(), len, used.header.index);
295
296    struct vring_used_elem &e(used.ring[used.header.index % used.ring.size()]);
297    e.id = desc->index();
298    e.len = len;
299    used.header.index += 1;
300    used.write();
301}
302
303void
304VirtQueue::dump() const
305{
306    if (!DTRACE(VIO))
307        return;
308
309    for (const VirtDescriptor &d : descriptors)
310        d.dump();
311}
312
313void
314VirtQueue::onNotify()
315{
316    DPRINTF(VIO, "onNotify\n");
317
318    // Consume all pending descriptors from the input queue.
319    VirtDescriptor *d;
320    while ((d = consumeDescriptor()) != NULL)
321        onNotifyDescriptor(d);
322}
323
324
325VirtIODeviceBase::VirtIODeviceBase(Params *params, DeviceId id,
326                                   size_t config_size, FeatureBits features)
327    : SimObject(params),
328      guestFeatures(0),
329      deviceId(id), configSize(config_size), deviceFeatures(features),
330      _deviceStatus(0), _queueSelect(0),
331      transKick(NULL)
332{
333}
334
335
336VirtIODeviceBase::~VirtIODeviceBase()
337{
338}
339
340void
341VirtIODeviceBase::serialize(CheckpointOut &cp) const
342{
343    SERIALIZE_SCALAR(guestFeatures);
344    SERIALIZE_SCALAR(_deviceStatus);
345    SERIALIZE_SCALAR(_queueSelect);
346    for (QueueID i = 0; i < _queues.size(); ++i)
347        _queues[i]->serializeSection(cp, csprintf("_queues.%i", i));
348}
349
350void
351VirtIODeviceBase::unserialize(CheckpointIn &cp)
352{
353    UNSERIALIZE_SCALAR(guestFeatures);
354    UNSERIALIZE_SCALAR(_deviceStatus);
355    UNSERIALIZE_SCALAR(_queueSelect);
356    for (QueueID i = 0; i < _queues.size(); ++i)
357        _queues[i]->unserializeSection(cp, csprintf("_queues.%i", i));
358}
359
360void
361VirtIODeviceBase::reset()
362{
363    _queueSelect = 0;
364    guestFeatures = 0;
365    _deviceStatus = 0;
366
367    for (QueueID i = 0; i < _queues.size(); ++i)
368        _queues[i]->setAddress(0);
369}
370
371void
372VirtIODeviceBase::onNotify(QueueID idx)
373{
374    DPRINTF(VIO, "onNotify: idx: %i\n", idx);
375    if (idx >= _queues.size()) {
376        panic("Guest tried to notify queue (%i), but only %i "
377              "queues registered.\n",
378              idx, _queues.size());
379    }
380    _queues[idx]->onNotify();
381}
382
383void
384VirtIODeviceBase::setGuestFeatures(FeatureBits features)
385{
386    DPRINTF(VIO, "Setting guest features: 0x%x\n", features);
387    if (~deviceFeatures & features) {
388        panic("Guest tried to enable unsupported features:\n"
389              "Device features: 0x%x\n"
390              "Requested features: 0x%x\n",
391              deviceFeatures, features);
392    }
393    guestFeatures = features;
394}
395
396
397void
398VirtIODeviceBase::setDeviceStatus(DeviceStatus status)
399{
400    _deviceStatus = status;
401    DPRINTF(VIO, "ACK: %i, DRIVER: %i, DRIVER_OK: %i, FAILED: %i\n",
402            status.acknowledge, status.driver, status.driver_ok, status.failed);
403    if (status == 0)
404        reset();
405}
406
407void
408VirtIODeviceBase::readConfig(PacketPtr pkt, Addr cfgOffset)
409{
410    panic("Unhandled device config read (offset: 0x%x).\n", cfgOffset);
411}
412
413void
414VirtIODeviceBase::writeConfig(PacketPtr pkt, Addr cfgOffset)
415{
416    panic("Unhandled device config write (offset: 0x%x).\n", cfgOffset);
417}
418
419void
420VirtIODeviceBase::readConfigBlob(PacketPtr pkt, Addr cfgOffset, const uint8_t *cfg)
421{
422    const unsigned size(pkt->getSize());
423
424    if (cfgOffset + size > configSize)
425        panic("Config read out of bounds.\n");
426
427    pkt->makeResponse();
428    pkt->setData(const_cast<uint8_t *>(cfg) + cfgOffset);
429}
430
431void
432VirtIODeviceBase::writeConfigBlob(PacketPtr pkt, Addr cfgOffset, uint8_t *cfg)
433{
434    const unsigned size(pkt->getSize());
435
436    if (cfgOffset + size > configSize)
437        panic("Config write out of bounds.\n");
438
439    pkt->makeResponse();
440    pkt->writeData((uint8_t *)cfg + cfgOffset);
441}
442
443
444const VirtQueue &
445VirtIODeviceBase::getCurrentQueue() const
446{
447    if (_queueSelect >= _queues.size())
448        panic("Guest tried to access non-existing VirtQueue (%i).\n", _queueSelect);
449
450    return *_queues[_queueSelect];
451}
452
453VirtQueue &
454VirtIODeviceBase::getCurrentQueue()
455{
456    if (_queueSelect >= _queues.size())
457        panic("Guest tried to access non-existing VirtQueue (%i).\n", _queueSelect);
458
459    return *_queues[_queueSelect];
460}
461
462void
463VirtIODeviceBase::setQueueAddress(uint32_t address)
464{
465    getCurrentQueue().setAddress(address * VirtQueue::ALIGN_SIZE);
466}
467
468uint32_t
469VirtIODeviceBase::getQueueAddress() const
470{
471    Addr address(getCurrentQueue().getAddress());
472    assert(!(address & ((1 >> VirtQueue::ALIGN_BITS) - 1)));
473    return address >> VirtQueue::ALIGN_BITS;
474}
475
476void
477VirtIODeviceBase::registerQueue(VirtQueue &queue)
478{
479    _queues.push_back(&queue);
480}
481
482
483VirtIODummyDevice::VirtIODummyDevice(VirtIODummyDeviceParams *params)
484    : VirtIODeviceBase(params, ID_INVALID, 0, 0)
485{
486}
487
488VirtIODummyDevice *
489VirtIODummyDeviceParams::create()
490{
491    return new VirtIODummyDevice(this);
492}
493