1 /* 2 * Copyright 2013, Jérôme Duval, korli@users.berlios.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <virtio.h> 8 9 #include "virtio_blk.h" 10 11 12 class DMAResource; 13 class IOScheduler; 14 15 16 static const uint8 kDriveIcon[] = { 17 0x6e, 0x63, 0x69, 0x66, 0x08, 0x03, 0x01, 0x00, 0x00, 0x02, 0x00, 0x16, 18 0x02, 0x3c, 0xc7, 0xee, 0x38, 0x9b, 0xc0, 0xba, 0x16, 0x57, 0x3e, 0x39, 19 0xb0, 0x49, 0x77, 0xc8, 0x42, 0xad, 0xc7, 0x00, 0xff, 0xff, 0xd3, 0x02, 20 0x00, 0x06, 0x02, 0x3c, 0x96, 0x32, 0x3a, 0x4d, 0x3f, 0xba, 0xfc, 0x01, 21 0x3d, 0x5a, 0x97, 0x4b, 0x57, 0xa5, 0x49, 0x84, 0x4d, 0x00, 0x47, 0x47, 22 0x47, 0xff, 0xa5, 0xa0, 0xa0, 0x02, 0x00, 0x16, 0x02, 0xbc, 0x59, 0x2f, 23 0xbb, 0x29, 0xa7, 0x3c, 0x0c, 0xe4, 0xbd, 0x0b, 0x7c, 0x48, 0x92, 0xc0, 24 0x4b, 0x79, 0x66, 0x00, 0x7d, 0xff, 0xd4, 0x02, 0x00, 0x06, 0x02, 0x38, 25 0xdb, 0xb4, 0x39, 0x97, 0x33, 0xbc, 0x4a, 0x33, 0x3b, 0xa5, 0x42, 0x48, 26 0x6e, 0x66, 0x49, 0xee, 0x7b, 0x00, 0x59, 0x67, 0x56, 0xff, 0xeb, 0xb2, 27 0xb2, 0x03, 0xa7, 0xff, 0x00, 0x03, 0xff, 0x00, 0x00, 0x04, 0x01, 0x80, 28 0x07, 0x0a, 0x06, 0x22, 0x3c, 0x22, 0x49, 0x44, 0x5b, 0x5a, 0x3e, 0x5a, 29 0x31, 0x39, 0x25, 0x0a, 0x04, 0x22, 0x3c, 0x44, 0x4b, 0x5a, 0x31, 0x39, 30 0x25, 0x0a, 0x04, 0x44, 0x4b, 0x44, 0x5b, 0x5a, 0x3e, 0x5a, 0x31, 0x0a, 31 0x04, 0x22, 0x3c, 0x22, 0x49, 0x44, 0x5b, 0x44, 0x4b, 0x08, 0x02, 0x27, 32 0x43, 0xb8, 0x14, 0xc1, 0xf1, 0x08, 0x02, 0x26, 0x43, 0x29, 0x44, 0x0a, 33 0x05, 0x44, 0x5d, 0x49, 0x5d, 0x60, 0x3e, 0x5a, 0x3b, 0x5b, 0x3f, 0x08, 34 0x0a, 0x07, 0x01, 0x06, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x10, 0x01, 0x17, 35 0x84, 0x00, 0x04, 0x0a, 0x01, 0x01, 0x01, 0x00, 0x0a, 0x02, 0x01, 0x02, 36 0x00, 0x0a, 0x03, 0x01, 0x03, 0x00, 0x0a, 0x04, 0x01, 0x04, 0x10, 0x01, 37 0x17, 0x85, 0x20, 0x04, 0x0a, 0x06, 0x01, 0x05, 0x30, 0x24, 0xb3, 0x99, 38 0x01, 0x17, 0x82, 0x00, 0x04, 0x0a, 0x05, 0x01, 0x05, 0x30, 0x20, 0xb2, 39 0xe6, 0x01, 0x17, 0x82, 0x00, 0x04 40 }; 41 42 43 #define VIRTIO_BLOCK_DRIVER_MODULE_NAME "drivers/disk/virtual/virtio_block/driver_v1" 44 #define VIRTIO_BLOCK_DEVICE_MODULE_NAME "drivers/disk/virtual/virtio_block/device_v1" 45 #define VIRTIO_BLOCK_DEVICE_ID_GENERATOR "virtio_block/device_id" 46 47 48 typedef struct { 49 device_node* node; 50 ::virtio_device virtio_device; 51 virtio_device_interface* virtio; 52 ::virtio_queue virtio_queue; 53 IOScheduler* io_scheduler; 54 DMAResource* dma_resource; 55 56 struct virtio_blk_config config; 57 58 uint32 features; 59 uint64 capacity; 60 uint32 block_size; 61 uint32 physical_block_size; 62 status_t media_status; 63 64 sem_id sem_cb; 65 } virtio_block_driver_info; 66 67 68 typedef struct { 69 virtio_block_driver_info* info; 70 } virtio_block_handle; 71 72 73 #include <stdio.h> 74 #include <string.h> 75 #include <stdlib.h> 76 77 #include <fs/devfs.h> 78 79 #include "dma_resources.h" 80 #include "IORequest.h" 81 #include "IOSchedulerSimple.h" 82 83 84 //#define TRACE_VIRTIO_BLOCK 85 #ifdef TRACE_VIRTIO_BLOCK 86 # define TRACE(x...) dprintf("virtio_block: " x) 87 #else 88 # define TRACE(x...) ; 89 #endif 90 #define ERROR(x...) dprintf("\33[33mvirtio_block:\33[0m " x) 91 #define CALLED() TRACE("CALLED %s\n", __PRETTY_FUNCTION__) 92 93 94 static device_manager_info* sDeviceManager; 95 96 97 bool virtio_block_set_capacity(virtio_block_driver_info* info); 98 99 100 const char * 101 get_feature_name(uint32 feature) 102 { 103 switch (feature) { 104 case VIRTIO_BLK_F_BARRIER: 105 return "host barrier"; 106 case VIRTIO_BLK_F_SIZE_MAX: 107 return "maximum segment size"; 108 case VIRTIO_BLK_F_SEG_MAX: 109 return "maximum segment count"; 110 case VIRTIO_BLK_F_GEOMETRY: 111 return "disk geometry"; 112 case VIRTIO_BLK_F_RO: 113 return "read only"; 114 case VIRTIO_BLK_F_BLK_SIZE: 115 return "block size"; 116 case VIRTIO_BLK_F_SCSI: 117 return "scsi commands"; 118 case VIRTIO_BLK_F_FLUSH: 119 return "flush command"; 120 case VIRTIO_BLK_F_TOPOLOGY: 121 return "topology"; 122 case VIRTIO_BLK_F_CONFIG_WCE: 123 return "config wce"; 124 } 125 return NULL; 126 } 127 128 129 static status_t 130 get_geometry(virtio_block_handle* handle, device_geometry* geometry) 131 { 132 virtio_block_driver_info* info = handle->info; 133 134 devfs_compute_geometry_size(geometry, info->capacity, info->block_size); 135 geometry->bytes_per_physical_sector = info->physical_block_size; 136 137 geometry->device_type = B_DISK; 138 geometry->removable = false; 139 140 geometry->read_only = ((info->features & VIRTIO_BLK_F_RO) != 0); 141 geometry->write_once = false; 142 143 TRACE("virtio_block: get_geometry(): %" B_PRIu32 ", %" B_PRIu32 ", %" B_PRIu32 ", %" B_PRIu32 144 ", %d, %d, %d, %d\n", geometry->bytes_per_sector, geometry->sectors_per_track, 145 geometry->cylinder_count, geometry->head_count, geometry->device_type, 146 geometry->removable, geometry->read_only, geometry->write_once); 147 148 return B_OK; 149 } 150 151 152 static void 153 virtio_block_config_callback(void* driverCookie) 154 { 155 virtio_block_driver_info* info = (virtio_block_driver_info*)driverCookie; 156 157 status_t status = info->virtio->read_device_config(info->virtio_device, 0, 158 &info->config, sizeof(struct virtio_blk_config)); 159 if (status != B_OK) 160 return; 161 162 if (virtio_block_set_capacity(info)) 163 info->media_status = B_DEV_MEDIA_CHANGED; 164 } 165 166 167 static void 168 virtio_block_callback(void* driverCookie, void* cookie) 169 { 170 virtio_block_driver_info* info = (virtio_block_driver_info*)cookie; 171 172 // consume all queued elements 173 while (info->virtio->queue_dequeue(info->virtio_queue, NULL, NULL)) 174 ; 175 176 release_sem_etc(info->sem_cb, 1, B_DO_NOT_RESCHEDULE); 177 } 178 179 180 static status_t 181 do_io(void* cookie, IOOperation* operation) 182 { 183 virtio_block_driver_info* info = (virtio_block_driver_info*)cookie; 184 185 size_t bytesTransferred = 0; 186 status_t status = B_OK; 187 188 physical_entry entries[operation->VecCount() + 2]; 189 190 void *buffer = malloc(sizeof(struct virtio_blk_outhdr) + sizeof(uint8)); 191 struct virtio_blk_outhdr *header = (struct virtio_blk_outhdr*)buffer; 192 header->type = operation->IsWrite() ? VIRTIO_BLK_T_OUT : VIRTIO_BLK_T_IN; 193 header->sector = operation->Offset() / 512; 194 header->ioprio = 1; 195 196 uint8* ack = (uint8*)buffer + sizeof(struct virtio_blk_outhdr); 197 *ack = 0xff; 198 199 get_memory_map(buffer, sizeof(struct virtio_blk_outhdr) + sizeof(uint8), 200 &entries[0], 1); 201 entries[operation->VecCount() + 1].address = entries[0].address 202 + sizeof(struct virtio_blk_outhdr); 203 entries[operation->VecCount() + 1].size = sizeof(uint8); 204 entries[0].size = sizeof(struct virtio_blk_outhdr); 205 206 memcpy(entries + 1, operation->Vecs(), operation->VecCount() 207 * sizeof(physical_entry)); 208 209 info->virtio->queue_request_v(info->virtio_queue, entries, 210 1 + (operation->IsWrite() ? operation->VecCount() : 0 ), 211 1 + (operation->IsWrite() ? 0 : operation->VecCount()), 212 info); 213 214 acquire_sem(info->sem_cb); 215 216 switch (*ack) { 217 case VIRTIO_BLK_S_OK: 218 status = B_OK; 219 bytesTransferred = operation->Length(); 220 break; 221 case VIRTIO_BLK_S_UNSUPP: 222 status = ENOTSUP; 223 break; 224 default: 225 status = EIO; 226 break; 227 } 228 free(buffer); 229 230 info->io_scheduler->OperationCompleted(operation, status, 231 bytesTransferred); 232 return status; 233 } 234 235 236 // #pragma mark - device module API 237 238 239 static status_t 240 virtio_block_init_device(void* _info, void** _cookie) 241 { 242 CALLED(); 243 virtio_block_driver_info* info = (virtio_block_driver_info*)_info; 244 245 device_node* parent = sDeviceManager->get_parent_node(info->node); 246 sDeviceManager->get_driver(parent, (driver_module_info **)&info->virtio, 247 (void **)&info->virtio_device); 248 sDeviceManager->put_node(parent); 249 250 info->virtio->negotiate_features(info->virtio_device, 251 VIRTIO_BLK_F_BARRIER | VIRTIO_BLK_F_SIZE_MAX 252 | VIRTIO_BLK_F_SEG_MAX | VIRTIO_BLK_F_GEOMETRY 253 | VIRTIO_BLK_F_RO | VIRTIO_BLK_F_BLK_SIZE 254 | VIRTIO_BLK_F_FLUSH | VIRTIO_BLK_F_TOPOLOGY 255 | VIRTIO_FEATURE_RING_INDIRECT_DESC, 256 &info->features, &get_feature_name); 257 258 status_t status = info->virtio->read_device_config( 259 info->virtio_device, 0, &info->config, 260 sizeof(struct virtio_blk_config)); 261 if (status != B_OK) 262 return status; 263 264 virtio_block_set_capacity(info); 265 266 TRACE("virtio_block: capacity: %" B_PRIu64 ", block_size %" B_PRIu32 "\n", 267 info->capacity, info->block_size); 268 269 status = info->virtio->alloc_queues(info->virtio_device, 1, 270 &info->virtio_queue); 271 if (status != B_OK) { 272 ERROR("queue allocation failed (%s)\n", strerror(status)); 273 return status; 274 } 275 status = info->virtio->setup_interrupt(info->virtio_device, 276 virtio_block_config_callback, info); 277 278 if (status == B_OK) { 279 status = info->virtio->queue_setup_interrupt(info->virtio_queue, 280 virtio_block_callback, info); 281 } 282 283 *_cookie = info; 284 return status; 285 } 286 287 288 static void 289 virtio_block_uninit_device(void* _cookie) 290 { 291 CALLED(); 292 virtio_block_driver_info* info = (virtio_block_driver_info*)_cookie; 293 294 delete info->io_scheduler; 295 delete info->dma_resource; 296 } 297 298 299 static status_t 300 virtio_block_open(void* _info, const char* path, int openMode, void** _cookie) 301 { 302 CALLED(); 303 virtio_block_driver_info* info = (virtio_block_driver_info*)_info; 304 305 virtio_block_handle* handle = (virtio_block_handle*)malloc( 306 sizeof(virtio_block_handle)); 307 if (handle == NULL) 308 return B_NO_MEMORY; 309 310 handle->info = info; 311 312 *_cookie = handle; 313 return B_OK; 314 } 315 316 317 static status_t 318 virtio_block_close(void* cookie) 319 { 320 //virtio_block_handle* handle = (virtio_block_handle*)cookie; 321 CALLED(); 322 323 return B_OK; 324 } 325 326 327 static status_t 328 virtio_block_free(void* cookie) 329 { 330 CALLED(); 331 virtio_block_handle* handle = (virtio_block_handle*)cookie; 332 333 free(handle); 334 return B_OK; 335 } 336 337 338 static status_t 339 virtio_block_read(void* cookie, off_t pos, void* buffer, size_t* _length) 340 { 341 CALLED(); 342 virtio_block_handle* handle = (virtio_block_handle*)cookie; 343 size_t length = *_length; 344 345 IORequest request; 346 status_t status = request.Init(pos, (addr_t)buffer, length, false, 0); 347 if (status != B_OK) 348 return status; 349 350 status = handle->info->io_scheduler->ScheduleRequest(&request); 351 if (status != B_OK) 352 return status; 353 354 status = request.Wait(0, 0); 355 if (status == B_OK) 356 *_length = length; 357 else 358 dprintf("read(): request.Wait() returned: %s\n", strerror(status)); 359 360 return status; 361 } 362 363 364 static status_t 365 virtio_block_write(void* cookie, off_t pos, const void* buffer, 366 size_t* _length) 367 { 368 CALLED(); 369 virtio_block_handle* handle = (virtio_block_handle*)cookie; 370 size_t length = *_length; 371 372 IORequest request; 373 status_t status = request.Init(pos, (addr_t)buffer, length, true, 0); 374 if (status != B_OK) 375 return status; 376 377 status = handle->info->io_scheduler->ScheduleRequest(&request); 378 if (status != B_OK) 379 return status; 380 381 status = request.Wait(0, 0); 382 if (status == B_OK) 383 *_length = length; 384 else 385 dprintf("write(): request.Wait() returned: %s\n", strerror(status)); 386 387 return status; 388 } 389 390 391 static status_t 392 virtio_block_io(void *cookie, io_request *request) 393 { 394 CALLED(); 395 virtio_block_handle* handle = (virtio_block_handle*)cookie; 396 397 return handle->info->io_scheduler->ScheduleRequest(request); 398 } 399 400 401 static status_t 402 virtio_block_ioctl(void* cookie, uint32 op, void* buffer, size_t length) 403 { 404 CALLED(); 405 virtio_block_handle* handle = (virtio_block_handle*)cookie; 406 virtio_block_driver_info* info = handle->info; 407 408 TRACE("ioctl(op = %" B_PRIu32 ")\n", op); 409 410 switch (op) { 411 case B_GET_MEDIA_STATUS: 412 { 413 *(status_t *)buffer = info->media_status; 414 info->media_status = B_OK; 415 TRACE("B_GET_MEDIA_STATUS: 0x%08" B_PRIx32 "\n", *(status_t *)buffer); 416 return B_OK; 417 break; 418 } 419 420 case B_GET_DEVICE_SIZE: 421 { 422 size_t size = info->capacity * info->block_size; 423 return user_memcpy(buffer, &size, sizeof(size_t)); 424 } 425 426 case B_GET_GEOMETRY: 427 { 428 if (buffer == NULL || length > sizeof(device_geometry)) 429 return B_BAD_VALUE; 430 431 device_geometry geometry; 432 status_t status = get_geometry(handle, &geometry); 433 if (status != B_OK) 434 return status; 435 436 return user_memcpy(buffer, &geometry, length); 437 } 438 439 case B_GET_ICON_NAME: 440 return user_strlcpy((char*)buffer, "devices/drive-harddisk", 441 B_FILE_NAME_LENGTH); 442 443 case B_GET_VECTOR_ICON: 444 { 445 // TODO: take device type into account! 446 device_icon iconData; 447 if (length != sizeof(device_icon)) 448 return B_BAD_VALUE; 449 if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK) 450 return B_BAD_ADDRESS; 451 452 if (iconData.icon_size >= (int32)sizeof(kDriveIcon)) { 453 if (user_memcpy(iconData.icon_data, kDriveIcon, 454 sizeof(kDriveIcon)) != B_OK) 455 return B_BAD_ADDRESS; 456 } 457 458 iconData.icon_size = sizeof(kDriveIcon); 459 return user_memcpy(buffer, &iconData, sizeof(device_icon)); 460 } 461 462 /*case B_FLUSH_DRIVE_CACHE: 463 return synchronize_cache(info);*/ 464 } 465 466 return B_DEV_INVALID_IOCTL; 467 } 468 469 470 bool 471 virtio_block_set_capacity(virtio_block_driver_info* info) 472 { 473 // get capacity 474 uint32 blockSize = 512; 475 if ((info->features & VIRTIO_BLK_F_BLK_SIZE) != 0) 476 blockSize = info->config.blk_size; 477 uint64 capacity = info->config.capacity * 512 / blockSize; 478 uint32 physicalBlockSize = blockSize; 479 480 if ((info->features & VIRTIO_BLK_F_TOPOLOGY) != 0 481 && info->config.topology.physical_block_exp > 0) { 482 physicalBlockSize = blockSize * (1 << info->config.topology.physical_block_exp); 483 } 484 485 TRACE("set_capacity(device = %p, capacity = %" B_PRIu64 ", blockSize = %" B_PRIu32 ")\n", 486 info, capacity, blockSize); 487 488 if (info->block_size == blockSize && info->capacity == capacity) 489 return false; 490 491 info->capacity = capacity; 492 493 if (info->block_size != 0) { 494 ERROR("old %" B_PRId32 ", new %" B_PRId32 "\n", info->block_size, 495 blockSize); 496 panic("updating DMAResource not yet implemented..."); 497 } 498 499 dma_restrictions restrictions; 500 memset(&restrictions, 0, sizeof(restrictions)); 501 if ((info->features & VIRTIO_BLK_F_SIZE_MAX) != 0) 502 restrictions.max_segment_size = info->config.size_max; 503 if ((info->features & VIRTIO_BLK_F_SEG_MAX) != 0) 504 restrictions.max_segment_count = info->config.seg_max; 505 506 // TODO: we need to replace the DMAResource in our IOScheduler 507 status_t status = info->dma_resource->Init(restrictions, blockSize, 508 1024, 32); 509 if (status != B_OK) 510 panic("initializing DMAResource failed: %s", strerror(status)); 511 512 info->io_scheduler = new(std::nothrow) IOSchedulerSimple( 513 info->dma_resource); 514 if (info->io_scheduler == NULL) 515 panic("allocating IOScheduler failed."); 516 517 // TODO: use whole device name here 518 status = info->io_scheduler->Init("virtio"); 519 if (status != B_OK) 520 panic("initializing IOScheduler failed: %s", strerror(status)); 521 522 info->io_scheduler->SetCallback(do_io, info); 523 524 info->block_size = blockSize; 525 info->physical_block_size = physicalBlockSize; 526 return true; 527 } 528 529 530 // #pragma mark - driver module API 531 532 533 static float 534 virtio_block_supports_device(device_node *parent) 535 { 536 CALLED(); 537 const char *bus; 538 uint16 deviceType; 539 540 // make sure parent is really the Virtio bus manager 541 if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)) 542 return -1; 543 544 if (strcmp(bus, "virtio")) 545 return 0.0; 546 547 // check whether it's really a Direct Access Device 548 if (sDeviceManager->get_attr_uint16(parent, VIRTIO_DEVICE_TYPE_ITEM, 549 &deviceType, true) != B_OK || deviceType != VIRTIO_DEVICE_ID_BLOCK) 550 return 0.0; 551 552 TRACE("Virtio block device found!\n"); 553 554 return 0.6; 555 } 556 557 558 static status_t 559 virtio_block_register_device(device_node *node) 560 { 561 CALLED(); 562 563 device_attr attrs[] = { 564 { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {.string = "Virtio Block"} }, 565 { NULL } 566 }; 567 568 return sDeviceManager->register_node(node, VIRTIO_BLOCK_DRIVER_MODULE_NAME, 569 attrs, NULL, NULL); 570 } 571 572 573 static status_t 574 virtio_block_init_driver(device_node *node, void **cookie) 575 { 576 CALLED(); 577 578 virtio_block_driver_info* info = (virtio_block_driver_info*)malloc( 579 sizeof(virtio_block_driver_info)); 580 if (info == NULL) 581 return B_NO_MEMORY; 582 583 memset(info, 0, sizeof(*info)); 584 585 info->media_status = B_OK; 586 info->dma_resource = new(std::nothrow) DMAResource; 587 if (info->dma_resource == NULL) { 588 free(info); 589 return B_NO_MEMORY; 590 } 591 592 info->sem_cb = create_sem(0, "virtio_block_cb"); 593 if (info->sem_cb < 0) { 594 delete info->dma_resource; 595 status_t status = info->sem_cb; 596 free(info); 597 return status; 598 } 599 info->node = node; 600 601 *cookie = info; 602 return B_OK; 603 } 604 605 606 static void 607 virtio_block_uninit_driver(void *_cookie) 608 { 609 CALLED(); 610 virtio_block_driver_info* info = (virtio_block_driver_info*)_cookie; 611 delete_sem(info->sem_cb); 612 free(info); 613 } 614 615 616 static status_t 617 virtio_block_register_child_devices(void* _cookie) 618 { 619 CALLED(); 620 virtio_block_driver_info* info = (virtio_block_driver_info*)_cookie; 621 status_t status; 622 623 int32 id = sDeviceManager->create_id(VIRTIO_BLOCK_DEVICE_ID_GENERATOR); 624 if (id < 0) 625 return id; 626 627 char name[64]; 628 snprintf(name, sizeof(name), "disk/virtual/virtio_block/%" B_PRId32 "/raw", 629 id); 630 631 status = sDeviceManager->publish_device(info->node, name, 632 VIRTIO_BLOCK_DEVICE_MODULE_NAME); 633 634 return status; 635 } 636 637 638 // #pragma mark - 639 640 641 module_dependency module_dependencies[] = { 642 { B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager }, 643 { NULL } 644 }; 645 646 struct device_module_info sVirtioBlockDevice = { 647 { 648 VIRTIO_BLOCK_DEVICE_MODULE_NAME, 649 0, 650 NULL 651 }, 652 653 virtio_block_init_device, 654 virtio_block_uninit_device, 655 NULL, // remove, 656 657 virtio_block_open, 658 virtio_block_close, 659 virtio_block_free, 660 virtio_block_read, 661 virtio_block_write, 662 virtio_block_io, 663 virtio_block_ioctl, 664 665 NULL, // select 666 NULL, // deselect 667 }; 668 669 struct driver_module_info sVirtioBlockDriver = { 670 { 671 VIRTIO_BLOCK_DRIVER_MODULE_NAME, 672 0, 673 NULL 674 }, 675 676 virtio_block_supports_device, 677 virtio_block_register_device, 678 virtio_block_init_driver, 679 virtio_block_uninit_driver, 680 virtio_block_register_child_devices, 681 NULL, // rescan 682 NULL, // removed 683 }; 684 685 module_info* modules[] = { 686 (module_info*)&sVirtioBlockDriver, 687 (module_info*)&sVirtioBlockDevice, 688 NULL 689 }; 690