2 * Copyright 2013, Jérôme Duval, korli@users.berlios.de.
3 * Distributed under the terms of the MIT License.
9 #include "virtio_blk.h"
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
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"
50 ::virtio_device virtio_device
;
51 virtio_device_interface
* virtio
;
52 ::virtio_queue virtio_queue
;
53 IOScheduler
* io_scheduler
;
54 DMAResource
* dma_resource
;
56 struct virtio_blk_config config
;
61 status_t media_status
;
64 } virtio_block_driver_info
;
68 virtio_block_driver_info
* info
;
69 } virtio_block_handle
;
78 #include "dma_resources.h"
79 #include "IORequest.h"
80 #include "IOSchedulerSimple.h"
83 //#define TRACE_VIRTIO_BLOCK
84 #ifdef TRACE_VIRTIO_BLOCK
85 # define TRACE(x...) dprintf("virtio_block: " x)
87 # define TRACE(x...) ;
89 #define ERROR(x...) dprintf("\33[33mvirtio_block:\33[0m " x)
90 #define CALLED() TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
93 static device_manager_info
* sDeviceManager
;
96 void virtio_block_set_capacity(virtio_block_driver_info
* info
, uint64 capacity
,
101 get_feature_name(uint32 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
:
114 case VIRTIO_BLK_F_BLK_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
:
128 get_geometry(virtio_block_handle
* handle
, device_geometry
* geometry
)
130 virtio_block_driver_info
* info
= handle
->info
;
132 devfs_compute_geometry_size(geometry
, info
->capacity
, info
->block_size
);
134 geometry
->device_type
= B_DISK
;
135 geometry
->removable
= false;
137 geometry
->read_only
= ((info
->features
& VIRTIO_BLK_F_RO
) != 0);
138 geometry
->write_once
= false;
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
);
154 for (y
= 31; y
>= 0; --y
) {
155 if (x
== ((uint32
)1 << y
))
164 virtio_block_config_callback(void* driverCookie
)
166 virtio_block_driver_info
* info
= (virtio_block_driver_info
*)driverCookie
;
168 status_t status
= info
->virtio
->read_device_config(info
->virtio_device
, 0,
169 &info
->config
, sizeof(struct virtio_blk_config
));
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
;
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
;
187 virtio_block_callback(void* driverCookie
, void* cookie
)
189 virtio_block_driver_info
* info
= (virtio_block_driver_info
*)cookie
;
191 release_sem_etc(info
->sem_cb
, 1, B_DO_NOT_RESCHEDULE
);
196 do_io(void* cookie
, IOOperation
* operation
)
198 virtio_block_driver_info
* info
= (virtio_block_driver_info
*)cookie
;
200 size_t bytesTransferred
= 0;
201 status_t status
= B_OK
;
203 physical_entry entries
[operation
->VecCount() + 2];
205 void *buffer
= malloc(sizeof(struct virtio_blk_outhdr
) + sizeof(uint8
));
206 struct virtio_blk_outhdr
*header
= (struct virtio_blk_outhdr
*)buffer
;
207 header
->type
= operation
->IsWrite() ? VIRTIO_BLK_T_OUT
: VIRTIO_BLK_T_IN
;
208 header
->sector
= operation
->Offset() / 512;
211 uint8
* ack
= (uint8
*)buffer
+ sizeof(struct virtio_blk_outhdr
);
214 get_memory_map(buffer
, sizeof(struct virtio_blk_outhdr
) + sizeof(uint8
),
216 entries
[operation
->VecCount() + 1].address
= entries
[0].address
217 + sizeof(struct virtio_blk_outhdr
);
218 entries
[operation
->VecCount() + 1].size
= sizeof(uint8
);
219 entries
[0].size
= sizeof(struct virtio_blk_outhdr
);
221 memcpy(entries
+ 1, operation
->Vecs(), operation
->VecCount()
222 * sizeof(physical_entry
));
224 info
->virtio
->queue_request_v(info
->virtio_queue
, entries
,
225 1 + (operation
->IsWrite() ? operation
->VecCount() : 0 ),
226 1 + (operation
->IsWrite() ? 0 : operation
->VecCount()),
227 virtio_block_callback
, info
);
229 acquire_sem(info
->sem_cb
);
232 case VIRTIO_BLK_S_OK
:
234 bytesTransferred
= operation
->Length();
236 case VIRTIO_BLK_S_UNSUPP
:
245 info
->io_scheduler
->OperationCompleted(operation
, status
,
251 // #pragma mark - device module API
255 virtio_block_init_device(void* _info
, void** _cookie
)
258 virtio_block_driver_info
* info
= (virtio_block_driver_info
*)_info
;
260 device_node
* parent
= sDeviceManager
->get_parent_node(info
->node
);
261 sDeviceManager
->get_driver(parent
, (driver_module_info
**)&info
->virtio
,
262 (void **)&info
->virtio_device
);
263 sDeviceManager
->put_node(parent
);
265 info
->virtio
->negociate_features(info
->virtio_device
,
266 VIRTIO_BLK_F_BARRIER
| VIRTIO_BLK_F_SIZE_MAX
267 | VIRTIO_BLK_F_SEG_MAX
| VIRTIO_BLK_F_GEOMETRY
268 | VIRTIO_BLK_F_RO
| VIRTIO_BLK_F_BLK_SIZE
269 | VIRTIO_BLK_F_FLUSH
| VIRTIO_FEATURE_RING_INDIRECT_DESC
,
270 &info
->features
, &get_feature_name
);
272 status_t status
= info
->virtio
->read_device_config(
273 info
->virtio_device
, 0, &info
->config
,
274 sizeof(struct virtio_blk_config
));
278 // and get (initial) capacity
279 uint32 block_size
= 512;
280 if ((info
->features
& VIRTIO_BLK_F_BLK_SIZE
) != 0)
281 block_size
= info
->config
.blk_size
;
282 uint64 capacity
= info
->config
.capacity
* 512 / block_size
;
284 virtio_block_set_capacity(info
, capacity
, block_size
);
286 TRACE("virtio_block: capacity: %" B_PRIu64
", block_size %" B_PRIu32
"\n",
287 info
->capacity
, info
->block_size
);
289 status
= info
->virtio
->alloc_queues(info
->virtio_device
, 1,
290 &info
->virtio_queue
);
291 if (status
!= B_OK
) {
292 ERROR("queue allocation failed (%s)\n", strerror(status
));
295 status
= info
->virtio
->setup_interrupt(info
->virtio_device
,
296 virtio_block_config_callback
, info
);
304 virtio_block_uninit_device(void* _cookie
)
307 virtio_block_driver_info
* info
= (virtio_block_driver_info
*)_cookie
;
309 delete info
->io_scheduler
;
310 delete info
->dma_resource
;
315 virtio_block_open(void* _info
, const char* path
, int openMode
, void** _cookie
)
318 virtio_block_driver_info
* info
= (virtio_block_driver_info
*)_info
;
320 virtio_block_handle
* handle
= (virtio_block_handle
*)malloc(
321 sizeof(virtio_block_handle
));
333 virtio_block_close(void* cookie
)
335 //virtio_block_handle* handle = (virtio_block_handle*)cookie;
343 virtio_block_free(void* cookie
)
346 virtio_block_handle
* handle
= (virtio_block_handle
*)cookie
;
354 virtio_block_read(void* cookie
, off_t pos
, void* buffer
, size_t* _length
)
357 virtio_block_handle
* handle
= (virtio_block_handle
*)cookie
;
358 size_t length
= *_length
;
361 status_t status
= request
.Init(pos
, (addr_t
)buffer
, length
, false, 0);
365 status
= handle
->info
->io_scheduler
->ScheduleRequest(&request
);
369 status
= request
.Wait(0, 0);
373 dprintf("read(): request.Wait() returned: %s\n", strerror(status
));
380 virtio_block_write(void* cookie
, off_t pos
, const void* buffer
,
384 virtio_block_handle
* handle
= (virtio_block_handle
*)cookie
;
385 size_t length
= *_length
;
388 status_t status
= request
.Init(pos
, (addr_t
)buffer
, length
, true, 0);
392 status
= handle
->info
->io_scheduler
->ScheduleRequest(&request
);
396 status
= request
.Wait(0, 0);
400 dprintf("write(): request.Wait() returned: %s\n", strerror(status
));
407 virtio_block_io(void *cookie
, io_request
*request
)
410 virtio_block_handle
* handle
= (virtio_block_handle
*)cookie
;
412 return handle
->info
->io_scheduler
->ScheduleRequest(request
);
417 virtio_block_ioctl(void* cookie
, uint32 op
, void* buffer
, size_t length
)
420 virtio_block_handle
* handle
= (virtio_block_handle
*)cookie
;
421 virtio_block_driver_info
* info
= handle
->info
;
423 TRACE("ioctl(op = %ld)\n", op
);
426 case B_GET_MEDIA_STATUS
:
428 *(status_t
*)buffer
= info
->media_status
;
429 info
->media_status
= B_OK
;
430 TRACE("B_GET_MEDIA_STATUS: 0x%08lx\n", *(status_t
*)buffer
);
435 case B_GET_DEVICE_SIZE
:
437 size_t size
= info
->capacity
* info
->block_size
;
438 return user_memcpy(buffer
, &size
, sizeof(size_t));
443 if (buffer
== NULL
/*|| length != sizeof(device_geometry)*/)
446 device_geometry geometry
;
447 status_t status
= get_geometry(handle
, &geometry
);
451 return user_memcpy(buffer
, &geometry
, sizeof(device_geometry
));
454 case B_GET_ICON_NAME
:
455 return user_strlcpy((char*)buffer
, "devices/drive-harddisk",
458 case B_GET_VECTOR_ICON
:
460 // TODO: take device type into account!
461 device_icon iconData
;
462 if (length
!= sizeof(device_icon
))
464 if (user_memcpy(&iconData
, buffer
, sizeof(device_icon
)) != B_OK
)
465 return B_BAD_ADDRESS
;
467 if (iconData
.icon_size
>= (int32
)sizeof(kDriveIcon
)) {
468 if (user_memcpy(iconData
.icon_data
, kDriveIcon
,
469 sizeof(kDriveIcon
)) != B_OK
)
470 return B_BAD_ADDRESS
;
473 iconData
.icon_size
= sizeof(kDriveIcon
);
474 return user_memcpy(buffer
, &iconData
, sizeof(device_icon
));
477 /*case B_FLUSH_DRIVE_CACHE:
478 return synchronize_cache(info);*/
481 return B_DEV_INVALID_IOCTL
;
486 virtio_block_set_capacity(virtio_block_driver_info
* info
, uint64 capacity
,
489 TRACE("set_capacity(device = %p, capacity = %Ld, blockSize = %ld)\n",
490 info
, capacity
, blockSize
);
492 // get log2, if possible
493 uint32 blockShift
= log2(blockSize
);
495 if ((1UL << blockShift
) != blockSize
)
498 info
->capacity
= capacity
;
500 if (info
->block_size
!= blockSize
) {
501 if (info
->block_size
!= 0) {
502 ERROR("old %" B_PRId32
", new %" B_PRId32
"\n", info
->block_size
,
504 panic("updating DMAResource not yet implemented...");
507 dma_restrictions restrictions
;
508 memset(&restrictions
, 0, sizeof(restrictions
));
509 if ((info
->features
& VIRTIO_BLK_F_SIZE_MAX
) != 0)
510 restrictions
.max_segment_size
= info
->config
.size_max
;
511 if ((info
->features
& VIRTIO_BLK_F_SEG_MAX
) != 0)
512 restrictions
.max_segment_count
= info
->config
.seg_max
;
514 // TODO: we need to replace the DMAResource in our IOScheduler
515 status_t status
= info
->dma_resource
->Init(restrictions
, blockSize
,
518 panic("initializing DMAResource failed: %s", strerror(status
));
520 info
->io_scheduler
= new(std::nothrow
) IOSchedulerSimple(
522 if (info
->io_scheduler
== NULL
)
523 panic("allocating IOScheduler failed.");
525 // TODO: use whole device name here
526 status
= info
->io_scheduler
->Init("virtio");
528 panic("initializing IOScheduler failed: %s", strerror(status
));
530 info
->io_scheduler
->SetCallback(do_io
, info
);
533 info
->block_size
= blockSize
;
537 // #pragma mark - driver module API
541 virtio_block_supports_device(device_node
*parent
)
547 // make sure parent is really the Virtio bus manager
548 if (sDeviceManager
->get_attr_string(parent
, B_DEVICE_BUS
, &bus
, false))
551 if (strcmp(bus
, "virtio"))
554 // check whether it's really a Direct Access Device
555 if (sDeviceManager
->get_attr_uint16(parent
, VIRTIO_DEVICE_TYPE_ITEM
,
556 &deviceType
, true) != B_OK
|| deviceType
!= VIRTIO_DEVICE_ID_BLOCK
)
559 TRACE("Virtio block device found!\n");
566 virtio_block_register_device(device_node
*node
)
571 device_attr attrs
[] = {
575 return sDeviceManager
->register_node(node
, VIRTIO_BLOCK_DRIVER_MODULE_NAME
,
581 virtio_block_init_driver(device_node
*node
, void **cookie
)
585 virtio_block_driver_info
* info
= (virtio_block_driver_info
*)malloc(
586 sizeof(virtio_block_driver_info
));
590 memset(info
, 0, sizeof(*info
));
592 info
->media_status
= B_OK
;
593 info
->dma_resource
= new(std::nothrow
) DMAResource
;
594 if (info
->dma_resource
== NULL
) {
599 info
->sem_cb
= create_sem(0, "virtio_block_cb");
600 if (info
->sem_cb
< 0) {
601 delete info
->dma_resource
;
602 status_t status
= info
->sem_cb
;
614 virtio_block_uninit_driver(void *_cookie
)
617 virtio_block_driver_info
* info
= (virtio_block_driver_info
*)_cookie
;
618 delete_sem(info
->sem_cb
);
624 virtio_block_register_child_devices(void* _cookie
)
627 virtio_block_driver_info
* info
= (virtio_block_driver_info
*)_cookie
;
630 int32 id
= sDeviceManager
->create_id(VIRTIO_BLOCK_DEVICE_ID_GENERATOR
);
635 snprintf(name
, sizeof(name
), "disk/virtual/virtio_block/%" B_PRId32
"/raw",
638 status
= sDeviceManager
->publish_device(info
->node
, name
,
639 VIRTIO_BLOCK_DEVICE_MODULE_NAME
);
648 module_dependency module_dependencies
[] = {
649 {B_DEVICE_MANAGER_MODULE_NAME
, (module_info
**)&sDeviceManager
},
653 struct device_module_info sVirtioBlockDevice
= {
655 VIRTIO_BLOCK_DEVICE_MODULE_NAME
,
660 virtio_block_init_device
,
661 virtio_block_uninit_device
,
676 struct driver_module_info sVirtioBlockDriver
= {
678 VIRTIO_BLOCK_DRIVER_MODULE_NAME
,
683 virtio_block_supports_device
,
684 virtio_block_register_device
,
685 virtio_block_init_driver
,
686 virtio_block_uninit_driver
,
687 virtio_block_register_child_devices
,
692 module_info
* modules
[] = {
693 (module_info
*)&sVirtioBlockDriver
,
694 (module_info
*)&sVirtioBlockDevice
,