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