Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


0720e933f181bf0de23d55ef84a280a7f4db13e6
[palacios.git] / palacios / src / devices / lnx_virtio_blk.c
1 /* 
2  * This file is part of the Palacios Virtual Machine Monitor developed
3  * by the V3VEE Project with funding from the United States National 
4  * Science Foundation and the Department of Energy.  
5  *
6  * The V3VEE Project is a joint project between Northwestern University
7  * and the University of New Mexico.  You can find out more at 
8  * http://www.v3vee.org
9  *
10  * Copyright (c) 2008, Jack Lange <jarusl@cs.northwestern.edu>
11  * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org> 
12  * All rights reserved.
13  *
14  * Author: Jack Lange <jarusl@cs.northwestern.edu>
15  *
16  * This is free software.  You are permitted to use,
17  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
18  */
19
20 #include <palacios/vmm.h>
21 #include <palacios/vmm_dev_mgr.h>
22 #include <devices/lnx_virtio_pci.h>
23 #include <devices/lnx_virtio_blk.h>
24 #include <devices/block_dev.h>
25 #include <palacios/vm_guest_mem.h>
26
27 #include <devices/pci.h>
28
29
30 /*
31 #ifndef DEBUG_VIRTIO_BLK
32 #undef PrintDebug
33 #define PrintDebug(fmt, args...)
34 #endif
35 */
36
37 #define BLK_CAPACITY_PORT     20
38 #define BLK_MAX_SIZE_PORT     28
39 #define BLK_MAX_SEG_PORT      32
40 #define BLK_CYLINDERS_PORT    36
41 #define BLK_HEADS_PORT        38
42 #define BLK_SECTS_PORT        39
43
44 #define BLK_IN_REQ            0
45 #define BLK_OUT_REQ           1
46 #define BLK_SCSI_CMD          2
47
48 #define BLK_BARRIER_FLAG     0x80000000
49
50 #define BLK_STATUS_OK             0
51 #define BLK_STATUS_ERR            1
52 #define BLK_STATUS_NOT_SUPPORTED  2
53
54
55 struct blk_config {
56     uint64_t capacity;
57     uint32_t max_size;
58     uint32_t max_seg;
59     uint16_t cylinders;
60     uint8_t heads;
61     uint8_t sectors;
62 } __attribute__((packed));
63
64
65
66 struct blk_op_hdr {
67     uint32_t type;
68     uint32_t prior;
69     uint64_t sector;
70 } __attribute__((packed));
71
72 #define QUEUE_SIZE 128
73
74 /* Host Feature flags */
75 #define VIRTIO_BARRIER       0x01       /* Does host support barriers? */
76 #define VIRTIO_SIZE_MAX      0x02       /* Indicates maximum segment size */
77 #define VIRTIO_SEG_MAX       0x04       /* Indicates maximum # of segments */
78 #define VIRTIO_LEGACY_GEOM   0x10       /* Indicates support of legacy geometry */
79
80
81
82
83 struct virtio_blk_state {
84     struct blk_config block_cfg;
85     struct virtio_config virtio_cfg;
86
87     struct vm_device * pci_bus;
88     struct pci_device * pci_dev;
89     
90     struct virtio_queue queue;
91
92     union {
93         struct v3_cd_ops * cd_ops;
94         struct v3_hd_ops * hd_ops;
95     };
96
97     v3_block_type_t block_type;
98     void * backend_data;
99
100     int io_range_size;
101 };
102
103
104 static int virtio_free(struct vm_device * dev) {
105     return -1;
106 }
107
108 static int virtio_reset(struct vm_device * dev) {
109     struct virtio_blk_state * virtio = (struct virtio_blk_state *)dev->private_data;
110
111     virtio->queue.ring_desc_addr = 0;
112     virtio->queue.ring_avail_addr = 0;
113     virtio->queue.ring_used_addr = 0;
114     virtio->queue.pfn = 0;
115     virtio->queue.cur_avail_idx = 0;
116
117     virtio->virtio_cfg.status = 0;
118     virtio->virtio_cfg.pci_isr = 0;
119
120     return 0;
121 }
122
123 static int handle_read_op(struct vm_device * dev, uint8_t * buf, uint64_t * sector, uint32_t len) {
124     struct virtio_blk_state * virtio = (struct virtio_blk_state *)dev->private_data; 
125     int ret = -1;
126
127     if (virtio->block_type == BLOCK_DISK) {
128         if (len % HD_SECTOR_SIZE) {
129             PrintError("Write of something that is not a sector len %d, mod=%d\n", len, len % HD_SECTOR_SIZE);
130             return -1;
131         }
132
133
134         PrintDebug("Reading Disk\n");
135             
136         ret = virtio->hd_ops->read(buf, len / HD_SECTOR_SIZE, *sector, virtio->backend_data);
137
138         *sector += len / HD_SECTOR_SIZE;
139
140     } else if (virtio->block_type == BLOCK_CDROM) {
141         if (len % ATAPI_BLOCK_SIZE) {
142             PrintError("Write of something that is not an ATAPI block len %d, mod=%d\n", len, len % ATAPI_BLOCK_SIZE);
143             return -1;
144         }
145
146         ret = virtio->cd_ops->read(buf, len / ATAPI_BLOCK_SIZE, *sector , virtio->backend_data);
147
148         *sector += len / ATAPI_BLOCK_SIZE;
149     }
150
151     return ret;
152 }
153
154
155 static int handle_write_op(struct vm_device * dev, uint8_t * buf, uint64_t * sector, uint32_t len) {
156     struct virtio_blk_state * virtio = (struct virtio_blk_state *)dev->private_data; 
157     int ret = -1;
158
159     if (virtio->block_type == BLOCK_DISK) {
160         if (len % HD_SECTOR_SIZE) {
161             PrintError("Write of something that is not a sector len %d, mod=%d\n", len, len % HD_SECTOR_SIZE);
162             return -1;
163         }
164
165         PrintDebug("Writing Disk\n");
166
167         ret = virtio->hd_ops->write(buf, len / HD_SECTOR_SIZE, *sector, virtio->backend_data);
168
169         *sector += len / HD_SECTOR_SIZE;        
170     }
171
172     return ret;
173 }
174
175
176
177 // multiple block operations need to increment the sector 
178
179 static int handle_block_op(struct vm_device * dev, struct blk_op_hdr * hdr, 
180                            struct vring_desc * buf_desc, uint8_t * status) {
181     struct virtio_blk_state * virtio = (struct virtio_blk_state *)dev->private_data;    
182     uint8_t * buf = NULL;
183
184     PrintDebug("Handling Block op\n");
185
186
187
188     if (guest_pa_to_host_va(dev->vm, buf_desc->addr_gpa, (addr_t *)&(buf)) == -1) {
189         PrintError("Could not translate buffer address\n");
190         return -1;
191     }
192
193
194     PrintDebug("Sector=%p Length=%d\n", (void *)(addr_t)(hdr->sector), buf_desc->length);
195
196     if (hdr->type == BLK_IN_REQ) {
197         if (virtio->block_type != BLOCK_NONE) {
198             if (handle_read_op(dev, buf, &(hdr->sector), buf_desc->length) == -1) {
199                 *status = BLK_STATUS_ERR;
200                 return -1;
201             } else {
202                 *status = BLK_STATUS_OK;
203             }
204         } else {
205             *status = BLK_STATUS_NOT_SUPPORTED;
206         }
207
208     } else if (hdr->type == BLK_OUT_REQ) {
209         if (virtio->block_type == BLOCK_DISK) {
210             if (handle_write_op(dev, buf, &(hdr->sector), buf_desc->length) == -1) {
211                 *status = BLK_STATUS_ERR;
212                 return -1;
213             } else {
214                 *status = BLK_STATUS_OK;
215             }
216
217         } else {
218             *status = BLK_STATUS_NOT_SUPPORTED;
219         }
220
221     } else if (hdr->type == BLK_SCSI_CMD) {
222         PrintError("VIRTIO: SCSI Command Not supported!!!\n");
223         *status = BLK_STATUS_NOT_SUPPORTED;
224         return -1;
225     }
226
227
228
229     PrintDebug("Returning Status: %d\n", *status);
230
231     return 0;
232 }
233
234 static int get_desc_count(struct virtio_queue * q, int index) {
235     struct vring_desc * tmp_desc = &(q->desc[index]);
236     int cnt = 1;
237     
238     while (tmp_desc->flags & VIRTIO_NEXT_FLAG) {
239         tmp_desc = &(q->desc[tmp_desc->next]);
240         cnt++;
241     }
242
243     return cnt;
244 }
245
246
247
248 static int handle_kick(struct vm_device * dev) {
249     struct virtio_blk_state * virtio = (struct virtio_blk_state *)dev->private_data;    
250     struct virtio_queue * q = &(virtio->queue);
251
252     PrintDebug("VIRTIO KICK: cur_index=%d (mod=%d), avail_index=%d\n", 
253                q->cur_avail_idx, q->cur_avail_idx % QUEUE_SIZE, q->avail->index);
254
255     while (q->cur_avail_idx < q->avail->index) {
256         struct vring_desc * hdr_desc = NULL;
257         struct vring_desc * buf_desc = NULL;
258         struct vring_desc * status_desc = NULL;
259         struct blk_op_hdr hdr;
260         addr_t hdr_addr = 0;
261         uint16_t desc_idx = q->avail->ring[q->cur_avail_idx % QUEUE_SIZE];
262         int desc_cnt = get_desc_count(q, desc_idx);
263         int i = 0;
264         uint8_t * status_ptr = NULL;
265         uint8_t status = BLK_STATUS_OK;
266         uint32_t req_len = 0;
267
268         PrintDebug("Descriptor Count=%d, index=%d\n", desc_cnt, q->cur_avail_idx % QUEUE_SIZE);
269
270         if (desc_cnt < 3) {
271             PrintError("Block operations must include at least 3 descriptors\n");
272             return -1;
273         }
274
275         hdr_desc = &(q->desc[desc_idx]);
276
277
278         PrintDebug("Header Descriptor (ptr=%p) gpa=%p, len=%d, flags=%x, next=%d\n", hdr_desc, 
279                    (void *)(hdr_desc->addr_gpa), hdr_desc->length, hdr_desc->flags, hdr_desc->next);    
280
281         if (guest_pa_to_host_va(dev->vm, hdr_desc->addr_gpa, &(hdr_addr)) == -1) {
282             PrintError("Could not translate block header address\n");
283             return -1;
284         }
285
286         // We copy the block op header out because we are going to modify its contents
287         memcpy(&hdr, (void *)hdr_addr, sizeof(struct blk_op_hdr));
288         
289         PrintDebug("Blk Op Hdr (ptr=%p) type=%d, sector=%p\n", (void *)hdr_addr, hdr.type, (void *)hdr.sector);
290
291         desc_idx = hdr_desc->next;
292
293         for (i = 0; i < desc_cnt - 2; i++) {
294             uint8_t tmp_status = BLK_STATUS_OK;
295
296             buf_desc = &(q->desc[desc_idx]);
297
298             PrintDebug("Buffer Descriptor (ptr=%p) gpa=%p, len=%d, flags=%x, next=%d\n", buf_desc, 
299                        (void *)(buf_desc->addr_gpa), buf_desc->length, buf_desc->flags, buf_desc->next);
300
301             if (handle_block_op(dev, &hdr, buf_desc, &tmp_status) == -1) {
302                 PrintError("Error handling block operation\n");
303                 return -1;
304             }
305
306             if (tmp_status != BLK_STATUS_OK) {
307                 status = tmp_status;
308             }
309
310             req_len += buf_desc->length;
311             desc_idx = buf_desc->next;
312         }
313
314         status_desc = &(q->desc[desc_idx]);
315
316         PrintDebug("Status Descriptor (ptr=%p) gpa=%p, len=%d, flags=%x, next=%d\n", status_desc, 
317                    (void *)(status_desc->addr_gpa), status_desc->length, status_desc->flags, status_desc->next);
318
319         if (guest_pa_to_host_va(dev->vm, status_desc->addr_gpa, (addr_t *)&(status_ptr)) == -1) {
320             PrintError("Could not translate status address\n");
321             return -1;
322         }
323
324         req_len += status_desc->length;
325         *status_ptr = status;
326
327         q->used->ring[q->used->index % QUEUE_SIZE].id = q->avail->ring[q->cur_avail_idx % QUEUE_SIZE];
328         q->used->ring[q->used->index % QUEUE_SIZE].length = req_len; // What do we set this to????
329
330         q->used->index++;
331         q->cur_avail_idx++;
332     }
333
334     if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
335         PrintDebug("Raising IRQ %d\n",  virtio->pci_dev->config_header.intr_line);
336         v3_pci_raise_irq(virtio->pci_bus, 0, virtio->pci_dev);
337         virtio->virtio_cfg.pci_isr = 1;
338     }
339
340     return 0;
341 }
342
343 static int virtio_io_write(uint16_t port, void * src, uint_t length, struct vm_device * dev) {
344     struct virtio_blk_state * virtio = (struct virtio_blk_state *)dev->private_data;
345     int port_idx = port % virtio->io_range_size;
346
347
348     PrintDebug("VIRTIO BLOCK Write for port %d (index=%d) len=%d, value=%x\n", 
349                port, port_idx,  length, *(uint32_t *)src);
350
351
352
353     switch (port_idx) {
354         case GUEST_FEATURES_PORT:
355             if (length != 4) {
356                 PrintError("Illegal write length for guest features\n");
357                 return -1;
358             }
359             
360             virtio->virtio_cfg.guest_features = *(uint32_t *)src;
361             PrintDebug("Setting Guest Features to %x\n", virtio->virtio_cfg.guest_features);
362
363             break;
364         case VRING_PG_NUM_PORT:
365             if (length == 4) {
366                 addr_t pfn = *(uint32_t *)src;
367                 addr_t page_addr = (pfn << VIRTIO_PAGE_SHIFT);
368
369
370                 virtio->queue.pfn = pfn;
371                 
372                 virtio->queue.ring_desc_addr = page_addr ;
373                 virtio->queue.ring_avail_addr = page_addr + (QUEUE_SIZE * sizeof(struct vring_desc));
374                 virtio->queue.ring_used_addr = ( virtio->queue.ring_avail_addr + \
375                                                  sizeof(struct vring_avail)    + \
376                                                  (QUEUE_SIZE * sizeof(uint16_t)));
377                 
378                 // round up to next page boundary.
379                 virtio->queue.ring_used_addr = (virtio->queue.ring_used_addr + 0xfff) & ~0xfff;
380
381                 if (guest_pa_to_host_va(dev->vm, virtio->queue.ring_desc_addr, (addr_t *)&(virtio->queue.desc)) == -1) {
382                     PrintError("Could not translate ring descriptor address\n");
383                     return -1;
384                 }
385
386
387                 if (guest_pa_to_host_va(dev->vm, virtio->queue.ring_avail_addr, (addr_t *)&(virtio->queue.avail)) == -1) {
388                     PrintError("Could not translate ring available address\n");
389                     return -1;
390                 }
391
392
393                 if (guest_pa_to_host_va(dev->vm, virtio->queue.ring_used_addr, (addr_t *)&(virtio->queue.used)) == -1) {
394                     PrintError("Could not translate ring used address\n");
395                     return -1;
396                 }
397
398                 PrintDebug("RingDesc_addr=%p, Avail_addr=%p, Used_addr=%p\n",
399                            (void *)(virtio->queue.ring_desc_addr),
400                            (void *)(virtio->queue.ring_avail_addr),
401                            (void *)(virtio->queue.ring_used_addr));
402
403                 PrintDebug("RingDesc=%p, Avail=%p, Used=%p\n", 
404                            virtio->queue.desc, virtio->queue.avail, virtio->queue.used);
405
406             } else {
407                 PrintError("Illegal write length for page frame number\n");
408                 return -1;
409             }
410             break;
411         case VRING_Q_SEL_PORT:
412             virtio->virtio_cfg.vring_queue_selector = *(uint16_t *)src;
413
414             if (virtio->virtio_cfg.vring_queue_selector != 0) {
415                 PrintError("Virtio Block device only uses 1 queue, selected %d\n", 
416                            virtio->virtio_cfg.vring_queue_selector);
417                 return -1;
418             }
419
420             break;
421         case VRING_Q_NOTIFY_PORT:
422             PrintDebug("Handling Kick\n");
423             if (handle_kick(dev) == -1) {
424                 PrintError("Could not handle Block Notification\n");
425                 return -1;
426             }
427             break;
428         case VIRTIO_STATUS_PORT:
429             virtio->virtio_cfg.status = *(uint8_t *)src;
430
431             if (virtio->virtio_cfg.status == 0) {
432                 PrintDebug("Resetting device\n");
433                 virtio_reset(dev);
434             }
435
436             break;
437
438         case VIRTIO_ISR_PORT:
439             virtio->virtio_cfg.pci_isr = *(uint8_t *)src;
440             break;
441         default:
442             return -1;
443             break;
444     }
445
446     return length;
447 }
448
449
450 static int virtio_io_read(uint16_t port, void * dst, uint_t length, struct vm_device * dev) {
451     struct virtio_blk_state * virtio = (struct virtio_blk_state *)dev->private_data;
452     int port_idx = port % virtio->io_range_size;
453
454
455     PrintDebug("VIRTIO BLOCK Read  for port %d (index =%d), length=%d\n", 
456                port, port_idx, length);
457
458     switch (port_idx) {
459         case HOST_FEATURES_PORT:
460             if (length != 4) {
461                 PrintError("Illegal read length for host features\n");
462                 return -1;
463             }
464
465             *(uint32_t *)dst = virtio->virtio_cfg.host_features;
466         
467             break;
468         case VRING_PG_NUM_PORT:
469             if (length != 4) {
470                 PrintError("Illegal read length for page frame number\n");
471                 return -1;
472             }
473
474             *(uint32_t *)dst = virtio->queue.pfn;
475
476             break;
477         case VRING_SIZE_PORT:
478             if (length != 2) {
479                 PrintError("Illegal read length for vring size\n");
480                 return -1;
481             }
482                 
483             *(uint16_t *)dst = virtio->queue.queue_size;
484
485             break;
486
487         case VIRTIO_STATUS_PORT:
488             if (length != 1) {
489                 PrintError("Illegal read length for status\n");
490                 return -1;
491             }
492
493             *(uint8_t *)dst = virtio->virtio_cfg.status;
494             break;
495
496         case VIRTIO_ISR_PORT:
497             *(uint8_t *)dst = virtio->virtio_cfg.pci_isr;
498             virtio->virtio_cfg.pci_isr = 0;
499             v3_pci_lower_irq(virtio->pci_bus, 0, virtio->pci_dev);
500             break;
501
502         default:
503             if ( (port_idx >= sizeof(struct virtio_config)) && 
504                  (port_idx < (sizeof(struct virtio_config) + sizeof(struct blk_config))) ) {
505                 int cfg_offset = port_idx - sizeof(struct virtio_config);
506                 uint8_t * cfg_ptr = (uint8_t *)&(virtio->block_cfg);
507
508                 memcpy(dst, cfg_ptr + cfg_offset, length);
509                 
510             } else {
511                 PrintError("Read of Unhandled Virtio Read\n");
512                 return -1;
513             }
514           
515             break;
516     }
517
518     return length;
519 }
520
521
522
523
524 static struct v3_device_ops dev_ops = {
525     .free = virtio_free,
526     .reset = NULL,
527     .start = NULL,
528     .stop = NULL,
529 };
530
531
532
533
534 int v3_virtio_register_cdrom(struct vm_device * dev, struct v3_cd_ops * ops, void * private_data) {
535     struct virtio_blk_state * virtio = (struct virtio_blk_state *)dev->private_data;
536     
537     virtio->block_type = BLOCK_CDROM;
538     virtio->cd_ops = ops;
539     virtio->backend_data = private_data;
540
541     virtio->block_cfg.capacity = ops->get_capacity(private_data);
542
543     return 0;
544 }
545
546
547 int v3_virtio_register_harddisk(struct vm_device * dev, struct v3_hd_ops * ops, void * private_data) {
548     struct virtio_blk_state * virtio = (struct virtio_blk_state *)dev->private_data;
549
550     virtio->block_type = BLOCK_DISK;
551     virtio->hd_ops = ops;
552     virtio->backend_data = private_data;
553
554     virtio->block_cfg.capacity = ops->get_capacity(private_data);
555
556     PrintDebug("Virtio Capacity = %d -- 0x%p\n", (int)(virtio->block_cfg.capacity), 
557         (void *)(addr_t)(virtio->block_cfg.capacity));
558
559     return 0;
560 }
561
562
563
564 static int virtio_init(struct guest_info * vm, void * cfg_data) {
565     struct vm_device * pci_bus = v3_find_dev(vm, (char *)cfg_data);
566     struct virtio_blk_state * virtio_state = NULL;
567     struct pci_device * pci_dev = NULL;
568
569     PrintDebug("Initializing VIRTIO Block device\n");
570
571     if (pci_bus == NULL) {
572         PrintError("VirtIO devices require a PCI Bus");
573         return -1;
574     }
575
576     
577     virtio_state  = (struct virtio_blk_state *)V3_Malloc(sizeof(struct virtio_blk_state));
578     memset(virtio_state, 0, sizeof(struct virtio_blk_state));
579
580
581     struct vm_device * dev = v3_allocate_device("LNX_VIRTIO_BLK", &dev_ops, virtio_state);
582     if (v3_attach_device(vm, dev) == -1) {
583         PrintError("Could not attach device %s\n", "LNX_VIRTIO_BLK");
584         return -1;
585     }
586
587
588     // PCI initialization
589     {
590         struct v3_pci_bar bars[6];
591         int num_ports = sizeof(struct virtio_config) + sizeof(struct blk_config);
592         int tmp_ports = num_ports;
593         int i;
594
595
596
597         // This gets the number of ports, rounded up to a power of 2
598         virtio_state->io_range_size = 1; // must be a power of 2
599
600         while (tmp_ports > 0) {
601             tmp_ports >>= 1;
602             virtio_state->io_range_size <<= 1;
603         }
604         
605         // this is to account for any low order bits being set in num_ports
606         // if there are none, then num_ports was already a power of 2 so we shift right to reset it
607         if ((num_ports & ((virtio_state->io_range_size >> 1) - 1)) == 0) {
608             virtio_state->io_range_size >>= 1;
609         }
610
611
612         for (i = 0; i < 6; i++) {
613             bars[i].type = PCI_BAR_NONE;
614         }
615
616         PrintDebug("Virtio-BLK io_range_size = %d\n", virtio_state->io_range_size);
617
618         bars[0].type = PCI_BAR_IO;
619         bars[0].default_base_port = -1;
620         bars[0].num_ports = virtio_state->io_range_size;
621
622         bars[0].io_read = virtio_io_read;
623         bars[0].io_write = virtio_io_write;
624
625         pci_dev = v3_pci_register_device(pci_bus, PCI_STD_DEVICE, 
626                                          0, PCI_AUTO_DEV_NUM, 0,
627                                          "LNX_VIRTIO_BLK", bars,
628                                          NULL, NULL, NULL, dev);
629
630         if (!pci_dev) {
631             PrintError("Could not register PCI Device\n");
632             return -1;
633         }
634         
635         pci_dev->config_header.vendor_id = VIRTIO_VENDOR_ID;
636         pci_dev->config_header.subsystem_vendor_id = VIRTIO_SUBVENDOR_ID;
637         
638
639         pci_dev->config_header.device_id = VIRTIO_BLOCK_DEV_ID;
640         pci_dev->config_header.class = PCI_CLASS_STORAGE;
641         pci_dev->config_header.subclass = PCI_STORAGE_SUBCLASS_OTHER;
642     
643         pci_dev->config_header.subsystem_id = VIRTIO_BLOCK_SUBDEVICE_ID;
644
645
646         pci_dev->config_header.intr_pin = 1;
647
648         pci_dev->config_header.max_latency = 1; // ?? (qemu does it...)
649
650
651         virtio_state->pci_dev = pci_dev;
652         virtio_state->pci_bus = pci_bus;
653     }
654
655     /* Block configuration */
656     virtio_state->virtio_cfg.host_features = VIRTIO_SEG_MAX;
657     virtio_state->block_cfg.max_seg = QUEUE_SIZE - 2;
658
659     // Virtio Block only uses one queue
660     virtio_state->queue.queue_size = QUEUE_SIZE;
661
662     virtio_reset(dev);
663
664
665
666     virtio_state->backend_data = NULL;
667     virtio_state->block_type = BLOCK_NONE;
668     virtio_state->hd_ops = NULL;
669
670     return 0;
671 }
672
673
674 device_register("LNX_VIRTIO_BLK", virtio_init)