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 status_t media_status; 62 63 sem_id sem_cb; 64 } virtio_block_driver_info; 65 66 67 typedef struct { 68 virtio_block_driver_info* info; 69 } virtio_block_handle; 70 71 72 #include <stdio.h> 73 #include <string.h> 74 #include <stdlib.h> 75 76 #include <fs/devfs.h> 77 78 #include "dma_resources.h" 79 #include "IORequest.h" 80 #include "IOSchedulerSimple.h" 81 82 83 //#define TRACE_VIRTIO_BLOCK 84 #ifdef TRACE_VIRTIO_BLOCK 85 # define TRACE(x...) dprintf("virtio_block: " x) 86 #else 87 # define TRACE(x...) ; 88 #endif 89 #define ERROR(x...) dprintf("\33[33mvirtio_block:\33[0m " x) 90 #define CALLED() TRACE("CALLED %s\n", __PRETTY_FUNCTION__) 91 92 93 static device_manager_info* sDeviceManager; 94 95 96 void virtio_block_set_capacity(virtio_block_driver_info* info, uint64 capacity, 97 uint32 blockSize); 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 } 123 return NULL; 124 } 125 126 127 static status_t 128 get_geometry(virtio_block_handle* handle, device_geometry* geometry) 129 { 130 virtio_block_driver_info* info = handle->info; 131 132 devfs_compute_geometry_size(geometry, info->capacity, info->block_size); 133 134 geometry->device_type = B_DISK; 135 geometry->removable = false; 136 137 geometry->read_only = ((info->features & VIRTIO_BLK_F_RO) != 0); 138 geometry->write_once = false; 139 140 TRACE("virtio_block: get_geometry(): %ld, %ld, %ld, %ld, %d, %d, %d, %d\n", 141 geometry->bytes_per_sector, geometry->sectors_per_track, 142 geometry->cylinder_count, geometry->head_count, geometry->device_type, 143 geometry->removable, geometry->read_only, geometry->write_once); 144 145 return B_OK; 146 } 147 148 149 static int 150 log2(uint32 x) 151 { 152 int y; 153 154 for (y = 31; y >= 0; --y) { 155 if (x == ((uint32)1 << y)) 156 break; 157 } 158 159 return y; 160 } 161 162 163 static void 164 virtio_block_config_callback(void* driverCookie) 165 { 166 virtio_block_driver_info* info = (virtio_block_driver_info*)driverCookie; 167 168 status_t status = info->virtio->read_device_config(info->virtio_device, 0, 169 &info->config, sizeof(struct virtio_blk_config)); 170 if (status != B_OK) 171 return; 172 173 uint32 block_size = 512; 174 if ((info->features & VIRTIO_BLK_F_BLK_SIZE) != 0) 175 block_size = info->config.blk_size; 176 uint64 capacity = info->config.capacity * 512 / block_size; 177 178 if (block_size != info->block_size || capacity != info->capacity) { 179 virtio_block_set_capacity(info, capacity, block_size); 180 info->media_status = B_DEV_MEDIA_CHANGED; 181 } 182 183 } 184 185 186 static void 187 virtio_block_callback(void* driverCookie, void* cookie) 188 { 189 virtio_block_driver_info* info = (virtio_block_driver_info*)cookie; 190 191 // consume all queued elements 192 while (info->virtio->queue_dequeue(info->virtio_queue, NULL, NULL)) 193 ; 194 195 release_sem_etc(info->sem_cb, 1, B_DO_NOT_RESCHEDULE); 196 } 197 198 199 static status_t 200 do_io(void* cookie, IOOperation* operation) 201 { 202 virtio_block_driver_info* info = (virtio_block_driver_info*)cookie; 203 204 size_t bytesTransferred = 0; 205 status_t status = B_OK; 206 207 physical_entry entries[operation->VecCount() + 2]; 208 209 void *buffer = malloc(sizeof(struct virtio_blk_outhdr) + sizeof(uint8)); 210 struct virtio_blk_outhdr *header = (struct virtio_blk_outhdr*)buffer; 211 header->type = operation->IsWrite() ? VIRTIO_BLK_T_OUT : VIRTIO_BLK_T_IN; 212 header->sector = operation->Offset() / 512; 213 header->ioprio = 1; 214 215 uint8* ack = (uint8*)buffer + sizeof(struct virtio_blk_outhdr); 216 *ack = 0xff; 217 218 get_memory_map(buffer, sizeof(struct virtio_blk_outhdr) + sizeof(uint8), 219 &entries[0], 1); 220 entries[operation->VecCount() + 1].address = entries[0].address 221 + sizeof(struct virtio_blk_outhdr); 222 entries[operation->VecCount() + 1].size = sizeof(uint8); 223 entries[0].size = sizeof(struct virtio_blk_outhdr); 224 225 memcpy(entries + 1, operation->Vecs(), operation->VecCount() 226 * sizeof(physical_entry)); 227 228 info->virtio->queue_request_v(info->virtio_queue, entries, 229 1 + (operation->IsWrite() ? operation->VecCount() : 0 ), 230 1 + (operation->IsWrite() ? 0 : operation->VecCount()), 231 info); 232 233 acquire_sem(info->sem_cb); 234 235 switch (*ack) { 236 case VIRTIO_BLK_S_OK: 237 status = B_OK; 238 bytesTransferred = operation->Length(); 239 break; 240 case VIRTIO_BLK_S_UNSUPP: 241 status = ENOTSUP; 242 break; 243 default: 244 status = EIO; 245 break; 246 } 247 free(buffer); 248 249 info->io_scheduler->OperationCompleted(operation, status, 250 bytesTransferred); 251 return status; 252 } 253 254 255 // #pragma mark - device module API 256 257 258 static status_t 259 virtio_block_init_device(void* _info, void** _cookie) 260 { 261 CALLED(); 262 virtio_block_driver_info* info = (virtio_block_driver_info*)_info; 263 264 device_node* parent = sDeviceManager->get_parent_node(info->node); 265 sDeviceManager->get_driver(parent, (driver_module_info **)&info->virtio, 266 (void **)&info->virtio_device); 267 sDeviceManager->put_node(parent); 268 269 info->virtio->negotiate_features(info->virtio_device, 270 VIRTIO_BLK_F_BARRIER | VIRTIO_BLK_F_SIZE_MAX 271 | VIRTIO_BLK_F_SEG_MAX | VIRTIO_BLK_F_GEOMETRY 272 | VIRTIO_BLK_F_RO | VIRTIO_BLK_F_BLK_SIZE 273 | VIRTIO_BLK_F_FLUSH | VIRTIO_FEATURE_RING_INDIRECT_DESC, 274 &info->features, &get_feature_name); 275 276 status_t status = info->virtio->read_device_config( 277 info->virtio_device, 0, &info->config, 278 sizeof(struct virtio_blk_config)); 279 if (status != B_OK) 280 return status; 281 282 // and get (initial) capacity 283 uint32 block_size = 512; 284 if ((info->features & VIRTIO_BLK_F_BLK_SIZE) != 0) 285 block_size = info->config.blk_size; 286 uint64 capacity = info->config.capacity * 512 / block_size; 287 288 virtio_block_set_capacity(info, capacity, block_size); 289 290 TRACE("virtio_block: capacity: %" B_PRIu64 ", block_size %" B_PRIu32 "\n", 291 info->capacity, info->block_size); 292 293 status = info->virtio->alloc_queues(info->virtio_device, 1, 294 &info->virtio_queue); 295 if (status != B_OK) { 296 ERROR("queue allocation failed (%s)\n", strerror(status)); 297 return status; 298 } 299 status = info->virtio->setup_interrupt(info->virtio_device, 300 virtio_block_config_callback, info); 301 302 if (status == B_OK) { 303 status = info->virtio->queue_setup_interrupt(info->virtio_queue, 304 virtio_block_callback, info); 305 } 306 307 *_cookie = info; 308 return status; 309 } 310 311 312 static void 313 virtio_block_uninit_device(void* _cookie) 314 { 315 CALLED(); 316 virtio_block_driver_info* info = (virtio_block_driver_info*)_cookie; 317 318 delete info->io_scheduler; 319 delete info->dma_resource; 320 } 321 322 323 static status_t 324 virtio_block_open(void* _info, const char* path, int openMode, void** _cookie) 325 { 326 CALLED(); 327 virtio_block_driver_info* info = (virtio_block_driver_info*)_info; 328 329 virtio_block_handle* handle = (virtio_block_handle*)malloc( 330 sizeof(virtio_block_handle)); 331 if (handle == NULL) 332 return B_NO_MEMORY; 333 334 handle->info = info; 335 336 *_cookie = handle; 337 return B_OK; 338 } 339 340 341 static status_t 342 virtio_block_close(void* cookie) 343 { 344 //virtio_block_handle* handle = (virtio_block_handle*)cookie; 345 CALLED(); 346 347 return B_OK; 348 } 349 350 351 static status_t 352 virtio_block_free(void* cookie) 353 { 354 CALLED(); 355 virtio_block_handle* handle = (virtio_block_handle*)cookie; 356 357 free(handle); 358 return B_OK; 359 } 360 361 362 static status_t 363 virtio_block_read(void* cookie, off_t pos, void* buffer, size_t* _length) 364 { 365 CALLED(); 366 virtio_block_handle* handle = (virtio_block_handle*)cookie; 367 size_t length = *_length; 368 369 IORequest request; 370 status_t status = request.Init(pos, (addr_t)buffer, length, false, 0); 371 if (status != B_OK) 372 return status; 373 374 status = handle->info->io_scheduler->ScheduleRequest(&request); 375 if (status != B_OK) 376 return status; 377 378 status = request.Wait(0, 0); 379 if (status == B_OK) 380 *_length = length; 381 else 382 dprintf("read(): request.Wait() returned: %s\n", strerror(status)); 383 384 return status; 385 } 386 387 388 static status_t 389 virtio_block_write(void* cookie, off_t pos, const void* buffer, 390 size_t* _length) 391 { 392 CALLED(); 393 virtio_block_handle* handle = (virtio_block_handle*)cookie; 394 size_t length = *_length; 395 396 IORequest request; 397 status_t status = request.Init(pos, (addr_t)buffer, length, true, 0); 398 if (status != B_OK) 399 return status; 400 401 status = handle->info->io_scheduler->ScheduleRequest(&request); 402 if (status != B_OK) 403 return status; 404 405 status = request.Wait(0, 0); 406 if (status == B_OK) 407 *_length = length; 408 else 409 dprintf("write(): request.Wait() returned: %s\n", strerror(status)); 410 411 return status; 412 } 413 414 415 static status_t 416 virtio_block_io(void *cookie, io_request *request) 417 { 418 CALLED(); 419 virtio_block_handle* handle = (virtio_block_handle*)cookie; 420 421 return handle->info->io_scheduler->ScheduleRequest(request); 422 } 423 424 425 static status_t 426 virtio_block_ioctl(void* cookie, uint32 op, void* buffer, size_t length) 427 { 428 CALLED(); 429 virtio_block_handle* handle = (virtio_block_handle*)cookie; 430 virtio_block_driver_info* info = handle->info; 431 432 TRACE("ioctl(op = %ld)\n", op); 433 434 switch (op) { 435 case B_GET_MEDIA_STATUS: 436 { 437 *(status_t *)buffer = info->media_status; 438 info->media_status = B_OK; 439 TRACE("B_GET_MEDIA_STATUS: 0x%08lx\n", *(status_t *)buffer); 440 return B_OK; 441 break; 442 } 443 444 case B_GET_DEVICE_SIZE: 445 { 446 size_t size = info->capacity * info->block_size; 447 return user_memcpy(buffer, &size, sizeof(size_t)); 448 } 449 450 case B_GET_GEOMETRY: 451 { 452 if (buffer == NULL /*|| length != sizeof(device_geometry)*/) 453 return B_BAD_VALUE; 454 455 device_geometry geometry; 456 status_t status = get_geometry(handle, &geometry); 457 if (status != B_OK) 458 return status; 459 460 return user_memcpy(buffer, &geometry, sizeof(device_geometry)); 461 } 462 463 case B_GET_ICON_NAME: 464 return user_strlcpy((char*)buffer, "devices/drive-harddisk", 465 B_FILE_NAME_LENGTH); 466 467 case B_GET_VECTOR_ICON: 468 { 469 // TODO: take device type into account! 470 device_icon iconData; 471 if (length != sizeof(device_icon)) 472 return B_BAD_VALUE; 473 if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK) 474 return B_BAD_ADDRESS; 475 476 if (iconData.icon_size >= (int32)sizeof(kDriveIcon)) { 477 if (user_memcpy(iconData.icon_data, kDriveIcon, 478 sizeof(kDriveIcon)) != B_OK) 479 return B_BAD_ADDRESS; 480 } 481 482 iconData.icon_size = sizeof(kDriveIcon); 483 return user_memcpy(buffer, &iconData, sizeof(device_icon)); 484 } 485 486 /*case B_FLUSH_DRIVE_CACHE: 487 return synchronize_cache(info);*/ 488 } 489 490 return B_DEV_INVALID_IOCTL; 491 } 492 493 494 void 495 virtio_block_set_capacity(virtio_block_driver_info* info, uint64 capacity, 496 uint32 blockSize) 497 { 498 TRACE("set_capacity(device = %p, capacity = %Ld, blockSize = %ld)\n", 499 info, capacity, blockSize); 500 501 // get log2, if possible 502 uint32 blockShift = log2(blockSize); 503 504 if ((1UL << blockShift) != blockSize) 505 blockShift = 0; 506 507 info->capacity = capacity; 508 509 if (info->block_size != blockSize) { 510 if (info->block_size != 0) { 511 ERROR("old %" B_PRId32 ", new %" B_PRId32 "\n", info->block_size, 512 blockSize); 513 panic("updating DMAResource not yet implemented..."); 514 } 515 516 dma_restrictions restrictions; 517 memset(&restrictions, 0, sizeof(restrictions)); 518 if ((info->features & VIRTIO_BLK_F_SIZE_MAX) != 0) 519 restrictions.max_segment_size = info->config.size_max; 520 if ((info->features & VIRTIO_BLK_F_SEG_MAX) != 0) 521 restrictions.max_segment_count = info->config.seg_max; 522 523 // TODO: we need to replace the DMAResource in our IOScheduler 524 status_t status = info->dma_resource->Init(restrictions, blockSize, 525 1024, 32); 526 if (status != B_OK) 527 panic("initializing DMAResource failed: %s", strerror(status)); 528 529 info->io_scheduler = new(std::nothrow) IOSchedulerSimple( 530 info->dma_resource); 531 if (info->io_scheduler == NULL) 532 panic("allocating IOScheduler failed."); 533 534 // TODO: use whole device name here 535 status = info->io_scheduler->Init("virtio"); 536 if (status != B_OK) 537 panic("initializing IOScheduler failed: %s", strerror(status)); 538 539 info->io_scheduler->SetCallback(do_io, info); 540 } 541 542 info->block_size = blockSize; 543 } 544 545 546 // #pragma mark - driver module API 547 548 549 static float 550 virtio_block_supports_device(device_node *parent) 551 { 552 CALLED(); 553 const char *bus; 554 uint16 deviceType; 555 556 // make sure parent is really the Virtio bus manager 557 if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)) 558 return -1; 559 560 if (strcmp(bus, "virtio")) 561 return 0.0; 562 563 // check whether it's really a Direct Access Device 564 if (sDeviceManager->get_attr_uint16(parent, VIRTIO_DEVICE_TYPE_ITEM, 565 &deviceType, true) != B_OK || deviceType != VIRTIO_DEVICE_ID_BLOCK) 566 return 0.0; 567 568 TRACE("Virtio block device found!\n"); 569 570 return 0.6; 571 } 572 573 574 static status_t 575 virtio_block_register_device(device_node *node) 576 { 577 CALLED(); 578 579 device_attr attrs[] = { 580 { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "Virtio Block"} }, 581 { NULL } 582 }; 583 584 return sDeviceManager->register_node(node, VIRTIO_BLOCK_DRIVER_MODULE_NAME, 585 attrs, NULL, NULL); 586 } 587 588 589 static status_t 590 virtio_block_init_driver(device_node *node, void **cookie) 591 { 592 CALLED(); 593 594 virtio_block_driver_info* info = (virtio_block_driver_info*)malloc( 595 sizeof(virtio_block_driver_info)); 596 if (info == NULL) 597 return B_NO_MEMORY; 598 599 memset(info, 0, sizeof(*info)); 600 601 info->media_status = B_OK; 602 info->dma_resource = new(std::nothrow) DMAResource; 603 if (info->dma_resource == NULL) { 604 free(info); 605 return B_NO_MEMORY; 606 } 607 608 info->sem_cb = create_sem(0, "virtio_block_cb"); 609 if (info->sem_cb < 0) { 610 delete info->dma_resource; 611 status_t status = info->sem_cb; 612 free(info); 613 return status; 614 } 615 info->node = node; 616 617 *cookie = info; 618 return B_OK; 619 } 620 621 622 static void 623 virtio_block_uninit_driver(void *_cookie) 624 { 625 CALLED(); 626 virtio_block_driver_info* info = (virtio_block_driver_info*)_cookie; 627 delete_sem(info->sem_cb); 628 free(info); 629 } 630 631 632 static status_t 633 virtio_block_register_child_devices(void* _cookie) 634 { 635 CALLED(); 636 virtio_block_driver_info* info = (virtio_block_driver_info*)_cookie; 637 status_t status; 638 639 int32 id = sDeviceManager->create_id(VIRTIO_BLOCK_DEVICE_ID_GENERATOR); 640 if (id < 0) 641 return id; 642 643 char name[64]; 644 snprintf(name, sizeof(name), "disk/virtual/virtio_block/%" B_PRId32 "/raw", 645 id); 646 647 status = sDeviceManager->publish_device(info->node, name, 648 VIRTIO_BLOCK_DEVICE_MODULE_NAME); 649 650 return status; 651 } 652 653 654 // #pragma mark - 655 656 657 module_dependency module_dependencies[] = { 658 { B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager }, 659 { NULL } 660 }; 661 662 struct device_module_info sVirtioBlockDevice = { 663 { 664 VIRTIO_BLOCK_DEVICE_MODULE_NAME, 665 0, 666 NULL 667 }, 668 669 virtio_block_init_device, 670 virtio_block_uninit_device, 671 NULL, // remove, 672 673 virtio_block_open, 674 virtio_block_close, 675 virtio_block_free, 676 virtio_block_read, 677 virtio_block_write, 678 virtio_block_io, 679 virtio_block_ioctl, 680 681 NULL, // select 682 NULL, // deselect 683 }; 684 685 struct driver_module_info sVirtioBlockDriver = { 686 { 687 VIRTIO_BLOCK_DRIVER_MODULE_NAME, 688 0, 689 NULL 690 }, 691 692 virtio_block_supports_device, 693 virtio_block_register_device, 694 virtio_block_init_driver, 695 virtio_block_uninit_driver, 696 virtio_block_register_child_devices, 697 NULL, // rescan 698 NULL, // removed 699 }; 700 701 module_info* modules[] = { 702 (module_info*)&sVirtioBlockDriver, 703 (module_info*)&sVirtioBlockDevice, 704 NULL 705 }; 706