vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / kernel / drivers / disk / virtual / virtio_block / virtio_block.cpp
bloba1a6d1d316756172cdeeb73660e80439771e01ca
1 /*
2 * Copyright 2013, Jérôme Duval, korli@users.berlios.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include <virtio.h>
9 #include "virtio_blk.h"
12 struct DMAResource;
13 struct IOScheduler;
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"
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;
56 struct virtio_blk_config config;
58 uint32 features;
59 uint64 capacity;
60 uint32 block_size;
61 status_t media_status;
63 sem_id sem_cb;
64 } virtio_block_driver_info;
67 typedef struct {
68 virtio_block_driver_info* info;
69 } virtio_block_handle;
72 #include <stdio.h>
73 #include <string.h>
74 #include <stdlib.h>
76 #include <fs/devfs.h>
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)
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__)
93 static device_manager_info* sDeviceManager;
96 void virtio_block_set_capacity(virtio_block_driver_info* info, uint64 capacity,
97 uint32 blockSize);
100 const char *
101 get_feature_name(uint32 feature)
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";
123 return NULL;
127 static status_t
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);
145 return B_OK;
149 static int
150 log2(uint32 x)
152 int y;
154 for (y = 31; y >= 0; --y) {
155 if (x == ((uint32)1 << y))
156 break;
159 return y;
163 static void
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));
170 if (status != B_OK)
171 return;
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;
186 static void
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);
195 static status_t
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;
209 header->ioprio = 1;
211 uint8* ack = (uint8*)buffer + sizeof(struct virtio_blk_outhdr);
212 *ack = 0xff;
214 get_memory_map(buffer, sizeof(struct virtio_blk_outhdr) + sizeof(uint8),
215 &entries[0], 1);
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);
231 switch (*ack) {
232 case VIRTIO_BLK_S_OK:
233 status = B_OK;
234 bytesTransferred = operation->Length();
235 break;
236 case VIRTIO_BLK_S_UNSUPP:
237 status = ENOTSUP;
238 break;
239 default:
240 status = EIO;
241 break;
243 free(buffer);
245 info->io_scheduler->OperationCompleted(operation, status,
246 bytesTransferred);
247 return status;
251 // #pragma mark - device module API
254 static status_t
255 virtio_block_init_device(void* _info, void** _cookie)
257 CALLED();
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));
275 if (status != B_OK)
276 return status;
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));
293 return status;
295 status = info->virtio->setup_interrupt(info->virtio_device,
296 virtio_block_config_callback, info);
298 *_cookie = info;
299 return status;
303 static void
304 virtio_block_uninit_device(void* _cookie)
306 CALLED();
307 virtio_block_driver_info* info = (virtio_block_driver_info*)_cookie;
309 delete info->io_scheduler;
310 delete info->dma_resource;
314 static status_t
315 virtio_block_open(void* _info, const char* path, int openMode, void** _cookie)
317 CALLED();
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));
322 if (handle == NULL)
323 return B_NO_MEMORY;
325 handle->info = info;
327 *_cookie = handle;
328 return B_OK;
332 static status_t
333 virtio_block_close(void* cookie)
335 //virtio_block_handle* handle = (virtio_block_handle*)cookie;
336 CALLED();
338 return B_OK;
342 static status_t
343 virtio_block_free(void* cookie)
345 CALLED();
346 virtio_block_handle* handle = (virtio_block_handle*)cookie;
348 free(handle);
349 return B_OK;
353 static status_t
354 virtio_block_read(void* cookie, off_t pos, void* buffer, size_t* _length)
356 CALLED();
357 virtio_block_handle* handle = (virtio_block_handle*)cookie;
358 size_t length = *_length;
360 IORequest request;
361 status_t status = request.Init(pos, (addr_t)buffer, length, false, 0);
362 if (status != B_OK)
363 return status;
365 status = handle->info->io_scheduler->ScheduleRequest(&request);
366 if (status != B_OK)
367 return status;
369 status = request.Wait(0, 0);
370 if (status == B_OK)
371 *_length = length;
372 else
373 dprintf("read(): request.Wait() returned: %s\n", strerror(status));
375 return status;
379 static status_t
380 virtio_block_write(void* cookie, off_t pos, const void* buffer,
381 size_t* _length)
383 CALLED();
384 virtio_block_handle* handle = (virtio_block_handle*)cookie;
385 size_t length = *_length;
387 IORequest request;
388 status_t status = request.Init(pos, (addr_t)buffer, length, true, 0);
389 if (status != B_OK)
390 return status;
392 status = handle->info->io_scheduler->ScheduleRequest(&request);
393 if (status != B_OK)
394 return status;
396 status = request.Wait(0, 0);
397 if (status == B_OK)
398 *_length = length;
399 else
400 dprintf("write(): request.Wait() returned: %s\n", strerror(status));
402 return status;
406 static status_t
407 virtio_block_io(void *cookie, io_request *request)
409 CALLED();
410 virtio_block_handle* handle = (virtio_block_handle*)cookie;
412 return handle->info->io_scheduler->ScheduleRequest(request);
416 static status_t
417 virtio_block_ioctl(void* cookie, uint32 op, void* buffer, size_t length)
419 CALLED();
420 virtio_block_handle* handle = (virtio_block_handle*)cookie;
421 virtio_block_driver_info* info = handle->info;
423 TRACE("ioctl(op = %ld)\n", op);
425 switch (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);
431 return B_OK;
432 break;
435 case B_GET_DEVICE_SIZE:
437 size_t size = info->capacity * info->block_size;
438 return user_memcpy(buffer, &size, sizeof(size_t));
441 case B_GET_GEOMETRY:
443 if (buffer == NULL /*|| length != sizeof(device_geometry)*/)
444 return B_BAD_VALUE;
446 device_geometry geometry;
447 status_t status = get_geometry(handle, &geometry);
448 if (status != B_OK)
449 return status;
451 return user_memcpy(buffer, &geometry, sizeof(device_geometry));
454 case B_GET_ICON_NAME:
455 return user_strlcpy((char*)buffer, "devices/drive-harddisk",
456 B_FILE_NAME_LENGTH);
458 case B_GET_VECTOR_ICON:
460 // TODO: take device type into account!
461 device_icon iconData;
462 if (length != sizeof(device_icon))
463 return B_BAD_VALUE;
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;
485 void
486 virtio_block_set_capacity(virtio_block_driver_info* info, uint64 capacity,
487 uint32 blockSize)
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)
496 blockShift = 0;
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,
503 blockSize);
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,
516 1024, 32);
517 if (status != B_OK)
518 panic("initializing DMAResource failed: %s", strerror(status));
520 info->io_scheduler = new(std::nothrow) IOSchedulerSimple(
521 info->dma_resource);
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");
527 if (status != B_OK)
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
540 static float
541 virtio_block_supports_device(device_node *parent)
543 CALLED();
544 const char *bus;
545 uint16 deviceType;
547 // make sure parent is really the Virtio bus manager
548 if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
549 return -1;
551 if (strcmp(bus, "virtio"))
552 return 0.0;
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)
557 return 0.0;
559 TRACE("Virtio block device found!\n");
561 return 0.6;
565 static status_t
566 virtio_block_register_device(device_node *node)
568 CALLED();
570 // ready to register
571 device_attr attrs[] = {
572 { NULL }
575 return sDeviceManager->register_node(node, VIRTIO_BLOCK_DRIVER_MODULE_NAME,
576 attrs, NULL, NULL);
580 static status_t
581 virtio_block_init_driver(device_node *node, void **cookie)
583 CALLED();
585 virtio_block_driver_info* info = (virtio_block_driver_info*)malloc(
586 sizeof(virtio_block_driver_info));
587 if (info == NULL)
588 return B_NO_MEMORY;
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) {
595 free(info);
596 return B_NO_MEMORY;
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;
603 free(info);
604 return status;
606 info->node = node;
608 *cookie = info;
609 return B_OK;
613 static void
614 virtio_block_uninit_driver(void *_cookie)
616 CALLED();
617 virtio_block_driver_info* info = (virtio_block_driver_info*)_cookie;
618 delete_sem(info->sem_cb);
619 free(info);
623 static status_t
624 virtio_block_register_child_devices(void* _cookie)
626 CALLED();
627 virtio_block_driver_info* info = (virtio_block_driver_info*)_cookie;
628 status_t status;
630 int32 id = sDeviceManager->create_id(VIRTIO_BLOCK_DEVICE_ID_GENERATOR);
631 if (id < 0)
632 return id;
634 char name[64];
635 snprintf(name, sizeof(name), "disk/virtual/virtio_block/%" B_PRId32 "/raw",
636 id);
638 status = sDeviceManager->publish_device(info->node, name,
639 VIRTIO_BLOCK_DEVICE_MODULE_NAME);
641 return status;
645 // #pragma mark -
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,
657 NULL
660 virtio_block_init_device,
661 virtio_block_uninit_device,
662 NULL, // remove,
664 virtio_block_open,
665 virtio_block_close,
666 virtio_block_free,
667 virtio_block_read,
668 virtio_block_write,
669 virtio_block_io,
670 virtio_block_ioctl,
672 NULL, // select
673 NULL, // deselect
676 struct driver_module_info sVirtioBlockDriver = {
678 VIRTIO_BLOCK_DRIVER_MODULE_NAME,
680 NULL
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,
688 NULL, // rescan
689 NULL, // removed
692 module_info* modules[] = {
693 (module_info*)&sVirtioBlockDriver,
694 (module_info*)&sVirtioBlockDevice,
695 NULL