base.cc revision 14010:0e1e887507c0
110448Snilay@cs.wisc.edu/* 210448Snilay@cs.wisc.edu * Copyright (c) 2014, 2016 ARM Limited 310448Snilay@cs.wisc.edu * All rights reserved 410448Snilay@cs.wisc.edu * 510448Snilay@cs.wisc.edu * The license below extends only to copyright in the software and shall 610448Snilay@cs.wisc.edu * not be construed as granting a license to any other intellectual 710448Snilay@cs.wisc.edu * property including but not limited to intellectual property relating 810448Snilay@cs.wisc.edu * to a hardware implementation of the functionality of the software 910448Snilay@cs.wisc.edu * licensed hereunder. You may use the software subject to the license 1010448Snilay@cs.wisc.edu * terms below provided that you ensure that this notice is replicated 1110448Snilay@cs.wisc.edu * unmodified and in its entirety in all distributions of the software, 1210448Snilay@cs.wisc.edu * modified or unmodified, in source code or in binary form. 1310448Snilay@cs.wisc.edu * 1410448Snilay@cs.wisc.edu * Redistribution and use in source and binary forms, with or without 1510448Snilay@cs.wisc.edu * modification, are permitted provided that the following conditions are 1610448Snilay@cs.wisc.edu * met: redistributions of source code must retain the above copyright 1710448Snilay@cs.wisc.edu * notice, this list of conditions and the following disclaimer; 1810448Snilay@cs.wisc.edu * redistributions in binary form must reproduce the above copyright 1910448Snilay@cs.wisc.edu * notice, this list of conditions and the following disclaimer in the 2010448Snilay@cs.wisc.edu * documentation and/or other materials provided with the distribution; 2110448Snilay@cs.wisc.edu * neither the name of the copyright holders nor the names of its 2210448Snilay@cs.wisc.edu * contributors may be used to endorse or promote products derived from 2310448Snilay@cs.wisc.edu * this software without specific prior written permission. 2410448Snilay@cs.wisc.edu * 2510448Snilay@cs.wisc.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2610448Snilay@cs.wisc.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2710448Snilay@cs.wisc.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 2810448Snilay@cs.wisc.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2910448Snilay@cs.wisc.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 3010448Snilay@cs.wisc.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 3110448Snilay@cs.wisc.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3210448Snilay@cs.wisc.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3310448Snilay@cs.wisc.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3410448Snilay@cs.wisc.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 3510448Snilay@cs.wisc.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3610448Snilay@cs.wisc.edu * 3710448Snilay@cs.wisc.edu * Authors: Andreas Sandberg 3810448Snilay@cs.wisc.edu */ 3910448Snilay@cs.wisc.edu 4010448Snilay@cs.wisc.edu#include "dev/virtio/base.hh" 4110448Snilay@cs.wisc.edu 4210448Snilay@cs.wisc.edu#include "debug/VIO.hh" 4310448Snilay@cs.wisc.edu#include "params/VirtIODeviceBase.hh" 4410448Snilay@cs.wisc.edu#include "params/VirtIODummyDevice.hh" 4510448Snilay@cs.wisc.edu 4610448Snilay@cs.wisc.eduVirtDescriptor::VirtDescriptor(PortProxy &_memProxy, VirtQueue &_queue, 4710448Snilay@cs.wisc.edu Index descIndex) 4810448Snilay@cs.wisc.edu : memProxy(&_memProxy), queue(&_queue), _index(descIndex), 4910448Snilay@cs.wisc.edu desc{0, 0, 0, 0} 5010448Snilay@cs.wisc.edu{ 5110448Snilay@cs.wisc.edu} 5210448Snilay@cs.wisc.edu 5310448Snilay@cs.wisc.eduVirtDescriptor::VirtDescriptor(VirtDescriptor &&other) noexcept 5410448Snilay@cs.wisc.edu{ 5510448Snilay@cs.wisc.edu *this = std::forward<VirtDescriptor>(other); 5610448Snilay@cs.wisc.edu} 5710448Snilay@cs.wisc.edu 5810448Snilay@cs.wisc.eduVirtDescriptor::~VirtDescriptor() noexcept 5910448Snilay@cs.wisc.edu{ 6010448Snilay@cs.wisc.edu} 6110448Snilay@cs.wisc.edu 6210448Snilay@cs.wisc.eduVirtDescriptor & 6310448Snilay@cs.wisc.eduVirtDescriptor::operator=(VirtDescriptor &&rhs) noexcept 6410448Snilay@cs.wisc.edu{ 6510448Snilay@cs.wisc.edu memProxy = std::move(rhs.memProxy); 6610448Snilay@cs.wisc.edu queue = std::move(rhs.queue); 6710448Snilay@cs.wisc.edu _index = std::move(rhs._index); 6810448Snilay@cs.wisc.edu desc = std::move(rhs.desc); 6910448Snilay@cs.wisc.edu 7010448Snilay@cs.wisc.edu return *this; 7110448Snilay@cs.wisc.edu} 7210448Snilay@cs.wisc.edu 7310448Snilay@cs.wisc.eduvoid 7410448Snilay@cs.wisc.eduVirtDescriptor::update() 7510448Snilay@cs.wisc.edu{ 7610448Snilay@cs.wisc.edu const Addr vq_addr(queue->getAddress()); 7710448Snilay@cs.wisc.edu // Check if the queue has been initialized yet 7810448Snilay@cs.wisc.edu if (vq_addr == 0) 7910448Snilay@cs.wisc.edu return; 8010448Snilay@cs.wisc.edu 8110448Snilay@cs.wisc.edu assert(_index < queue->getSize()); 8210448Snilay@cs.wisc.edu const Addr desc_addr(vq_addr + sizeof(desc) * _index); 8310448Snilay@cs.wisc.edu vring_desc guest_desc; 8410448Snilay@cs.wisc.edu memProxy->readBlob(desc_addr, &guest_desc, sizeof(guest_desc)); 8510448Snilay@cs.wisc.edu desc = vtoh_legacy(guest_desc); 8610448Snilay@cs.wisc.edu DPRINTF(VIO, 8710448Snilay@cs.wisc.edu "VirtDescriptor(%i): Addr: 0x%x, Len: %i, Flags: 0x%x, " 8810448Snilay@cs.wisc.edu "Next: 0x%x\n", 8910448Snilay@cs.wisc.edu _index, desc.addr, desc.len, desc.flags, desc.next); 9010448Snilay@cs.wisc.edu} 9110448Snilay@cs.wisc.edu 9210448Snilay@cs.wisc.eduvoid 9310448Snilay@cs.wisc.eduVirtDescriptor::updateChain() 9410448Snilay@cs.wisc.edu{ 9510448Snilay@cs.wisc.edu VirtDescriptor *desc(this); 9610448Snilay@cs.wisc.edu do { 9710448Snilay@cs.wisc.edu desc->update(); 9810448Snilay@cs.wisc.edu } while ((desc = desc->next()) != NULL && desc != this); 9910448Snilay@cs.wisc.edu 10010448Snilay@cs.wisc.edu if (desc == this) 10110448Snilay@cs.wisc.edu panic("Loop in descriptor chain!\n"); 10210448Snilay@cs.wisc.edu} 10310448Snilay@cs.wisc.edu 10410448Snilay@cs.wisc.eduvoid 10510448Snilay@cs.wisc.eduVirtDescriptor::dump() const 10610448Snilay@cs.wisc.edu{ 10710448Snilay@cs.wisc.edu if (!DTRACE(VIO)) 10810448Snilay@cs.wisc.edu return; 10910448Snilay@cs.wisc.edu 11010448Snilay@cs.wisc.edu DPRINTF(VIO, "Descriptor[%i]: " 11110448Snilay@cs.wisc.edu "Addr: 0x%x, Len: %i, Flags: 0x%x, Next: 0x%x\n", 11210448Snilay@cs.wisc.edu _index, desc.addr, desc.len, desc.flags, desc.next); 11310448Snilay@cs.wisc.edu 11410448Snilay@cs.wisc.edu if (isIncoming()) { 11510448Snilay@cs.wisc.edu uint8_t data[desc.len]; 11610448Snilay@cs.wisc.edu read(0, data, desc.len); 11710448Snilay@cs.wisc.edu DDUMP(VIO, data, desc.len); 11810448Snilay@cs.wisc.edu } 11910448Snilay@cs.wisc.edu} 12010448Snilay@cs.wisc.edu 12110448Snilay@cs.wisc.eduvoid 12210448Snilay@cs.wisc.eduVirtDescriptor::dumpChain() const 12310448Snilay@cs.wisc.edu{ 12410448Snilay@cs.wisc.edu if (!DTRACE(VIO)) 12510448Snilay@cs.wisc.edu return; 12610448Snilay@cs.wisc.edu 12710448Snilay@cs.wisc.edu const VirtDescriptor *desc(this); 12810448Snilay@cs.wisc.edu do { 12910448Snilay@cs.wisc.edu desc->dump(); 13010448Snilay@cs.wisc.edu } while ((desc = desc->next()) != NULL); 13110448Snilay@cs.wisc.edu} 13210448Snilay@cs.wisc.edu 13310448Snilay@cs.wisc.eduVirtDescriptor * 13410448Snilay@cs.wisc.eduVirtDescriptor::next() const 13510448Snilay@cs.wisc.edu{ 13610448Snilay@cs.wisc.edu if (hasNext()) { 13710448Snilay@cs.wisc.edu return queue->getDescriptor(desc.next); 13810448Snilay@cs.wisc.edu } else { 13910448Snilay@cs.wisc.edu return NULL; 14010448Snilay@cs.wisc.edu } 14110448Snilay@cs.wisc.edu} 14210448Snilay@cs.wisc.edu 14310448Snilay@cs.wisc.eduvoid 14410448Snilay@cs.wisc.eduVirtDescriptor::read(size_t offset, uint8_t *dst, size_t size) const 14510448Snilay@cs.wisc.edu{ 14610448Snilay@cs.wisc.edu DPRINTF(VIO, "VirtDescriptor(%p, 0x%x, %i)::read: offset: %i, dst: 0x%x, size: %i\n", 14710448Snilay@cs.wisc.edu this, desc.addr, desc.len, offset, (long)dst, size); 14810448Snilay@cs.wisc.edu assert(size <= desc.len - offset); 14910448Snilay@cs.wisc.edu if (!isIncoming()) 15010448Snilay@cs.wisc.edu panic("Trying to read from outgoing buffer\n"); 15110448Snilay@cs.wisc.edu 15210448Snilay@cs.wisc.edu memProxy->readBlob(desc.addr + offset, dst, size); 15310448Snilay@cs.wisc.edu} 15410448Snilay@cs.wisc.edu 15510448Snilay@cs.wisc.eduvoid 15610448Snilay@cs.wisc.eduVirtDescriptor::write(size_t offset, const uint8_t *src, size_t size) 15710448Snilay@cs.wisc.edu{ 15810448Snilay@cs.wisc.edu DPRINTF(VIO, "VirtDescriptor(%p, 0x%x, %i)::write: offset: %i, src: 0x%x, size: %i\n", 15910448Snilay@cs.wisc.edu this, desc.addr, desc.len, offset, (long)src, size); 16010448Snilay@cs.wisc.edu assert(size <= desc.len - offset); 16110448Snilay@cs.wisc.edu if (!isOutgoing()) 16210448Snilay@cs.wisc.edu panic("Trying to write to incoming buffer\n"); 16310448Snilay@cs.wisc.edu 16410448Snilay@cs.wisc.edu memProxy->writeBlob(desc.addr + offset, src, size); 16510448Snilay@cs.wisc.edu} 16610448Snilay@cs.wisc.edu 16710448Snilay@cs.wisc.eduvoid 16810448Snilay@cs.wisc.eduVirtDescriptor::chainRead(size_t offset, uint8_t *dst, size_t size) const 16910448Snilay@cs.wisc.edu{ 17010448Snilay@cs.wisc.edu const VirtDescriptor *desc(this); 17110448Snilay@cs.wisc.edu const size_t full_size(size); 17210448Snilay@cs.wisc.edu do { 17310448Snilay@cs.wisc.edu if (offset < desc->size()) { 17410448Snilay@cs.wisc.edu const size_t chunk_size(std::min(desc->size() - offset, size)); 17510448Snilay@cs.wisc.edu desc->read(offset, dst, chunk_size); 17610448Snilay@cs.wisc.edu dst += chunk_size; 17710448Snilay@cs.wisc.edu size -= chunk_size; 17810448Snilay@cs.wisc.edu offset = 0; 17910448Snilay@cs.wisc.edu } else { 18010448Snilay@cs.wisc.edu offset -= desc->size(); 18110448Snilay@cs.wisc.edu } 18210448Snilay@cs.wisc.edu } while ((desc = desc->next()) != NULL && desc->isIncoming() && size > 0); 18310448Snilay@cs.wisc.edu 18410448Snilay@cs.wisc.edu if (size != 0) { 18510448Snilay@cs.wisc.edu panic("Failed to read %i bytes from chain of %i bytes @ offset %i\n", 18610448Snilay@cs.wisc.edu full_size, chainSize(), offset); 18710448Snilay@cs.wisc.edu } 18810448Snilay@cs.wisc.edu} 18910448Snilay@cs.wisc.edu 19010448Snilay@cs.wisc.eduvoid 19110448Snilay@cs.wisc.eduVirtDescriptor::chainWrite(size_t offset, const uint8_t *src, size_t size) 19210448Snilay@cs.wisc.edu{ 19310448Snilay@cs.wisc.edu VirtDescriptor *desc(this); 19410448Snilay@cs.wisc.edu const size_t full_size(size); 19510448Snilay@cs.wisc.edu do { 19610448Snilay@cs.wisc.edu if (offset < desc->size()) { 19710448Snilay@cs.wisc.edu const size_t chunk_size(std::min(desc->size() - offset, size)); 19810448Snilay@cs.wisc.edu desc->write(offset, src, chunk_size); 19910448Snilay@cs.wisc.edu src += chunk_size; 20010448Snilay@cs.wisc.edu size -= chunk_size; 20110448Snilay@cs.wisc.edu offset = 0; 20210448Snilay@cs.wisc.edu } else { 20310448Snilay@cs.wisc.edu offset -= desc->size(); 20410448Snilay@cs.wisc.edu } 20510448Snilay@cs.wisc.edu } while ((desc = desc->next()) != NULL && size > 0); 20610448Snilay@cs.wisc.edu 20710448Snilay@cs.wisc.edu if (size != 0) { 20810448Snilay@cs.wisc.edu panic("Failed to write %i bytes into chain of %i bytes @ offset %i\n", 20910448Snilay@cs.wisc.edu full_size, chainSize(), offset); 21010448Snilay@cs.wisc.edu } 21110448Snilay@cs.wisc.edu} 21210448Snilay@cs.wisc.edu 21310448Snilay@cs.wisc.edusize_t 214VirtDescriptor::chainSize() const 215{ 216 size_t size(0); 217 const VirtDescriptor *desc(this); 218 do { 219 size += desc->size(); 220 } while ((desc = desc->next()) != NULL); 221 222 return size; 223} 224 225 226 227VirtQueue::VirtQueue(PortProxy &proxy, uint16_t size) 228 : _size(size), _address(0), memProxy(proxy), 229 avail(proxy, size), used(proxy, size), 230 _last_avail(0) 231{ 232 descriptors.reserve(_size); 233 for (int i = 0; i < _size; ++i) 234 descriptors.emplace_back(proxy, *this, i); 235} 236 237void 238VirtQueue::serialize(CheckpointOut &cp) const 239{ 240 SERIALIZE_SCALAR(_address); 241 SERIALIZE_SCALAR(_last_avail); 242} 243 244void 245VirtQueue::unserialize(CheckpointIn &cp) 246{ 247 Addr addr_in; 248 249 paramIn(cp, "_address", addr_in); 250 UNSERIALIZE_SCALAR(_last_avail); 251 252 // Use the address setter to ensure that the ring buffer addresses 253 // are updated as well. 254 setAddress(addr_in); 255} 256 257void 258VirtQueue::setAddress(Addr address) 259{ 260 const Addr addr_avail(address + _size * sizeof(struct vring_desc)); 261 const Addr addr_avail_end(addr_avail + sizeof(struct vring_avail) + 262 _size * sizeof(uint16_t)); 263 const Addr addr_used((addr_avail_end + sizeof(uint16_t) + 264 (ALIGN_SIZE - 1)) & ~(ALIGN_SIZE - 1)); 265 _address = address; 266 avail.setAddress(addr_avail); 267 used.setAddress(addr_used); 268} 269 270VirtDescriptor * 271VirtQueue::consumeDescriptor() 272{ 273 avail.read(); 274 DPRINTF(VIO, "consumeDescriptor: _last_avail: %i, avail.idx: %i (->%i)\n", 275 _last_avail, avail.header.index, 276 avail.ring[_last_avail % used.ring.size()]); 277 if (_last_avail == avail.header.index) 278 return NULL; 279 280 VirtDescriptor::Index index(avail.ring[_last_avail % used.ring.size()]); 281 ++_last_avail; 282 283 VirtDescriptor *d(&descriptors[index]); 284 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