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 <condition_variable.h>
8#include <lock.h>
9#include <StackOrHeapArray.h>
10#include <virtio.h>
11
12#include "virtio_blk.h"
13
14
15class DMAResource;
16class IOScheduler;
17
18
19static const uint8 kDriveIcon[] = {
20	0x6e, 0x63, 0x69, 0x66, 0x08, 0x03, 0x01, 0x00, 0x00, 0x02, 0x00, 0x16,
21	0x02, 0x3c, 0xc7, 0xee, 0x38, 0x9b, 0xc0, 0xba, 0x16, 0x57, 0x3e, 0x39,
22	0xb0, 0x49, 0x77, 0xc8, 0x42, 0xad, 0xc7, 0x00, 0xff, 0xff, 0xd3, 0x02,
23	0x00, 0x06, 0x02, 0x3c, 0x96, 0x32, 0x3a, 0x4d, 0x3f, 0xba, 0xfc, 0x01,
24	0x3d, 0x5a, 0x97, 0x4b, 0x57, 0xa5, 0x49, 0x84, 0x4d, 0x00, 0x47, 0x47,
25	0x47, 0xff, 0xa5, 0xa0, 0xa0, 0x02, 0x00, 0x16, 0x02, 0xbc, 0x59, 0x2f,
26	0xbb, 0x29, 0xa7, 0x3c, 0x0c, 0xe4, 0xbd, 0x0b, 0x7c, 0x48, 0x92, 0xc0,
27	0x4b, 0x79, 0x66, 0x00, 0x7d, 0xff, 0xd4, 0x02, 0x00, 0x06, 0x02, 0x38,
28	0xdb, 0xb4, 0x39, 0x97, 0x33, 0xbc, 0x4a, 0x33, 0x3b, 0xa5, 0x42, 0x48,
29	0x6e, 0x66, 0x49, 0xee, 0x7b, 0x00, 0x59, 0x67, 0x56, 0xff, 0xeb, 0xb2,
30	0xb2, 0x03, 0xa7, 0xff, 0x00, 0x03, 0xff, 0x00, 0x00, 0x04, 0x01, 0x80,
31	0x07, 0x0a, 0x06, 0x22, 0x3c, 0x22, 0x49, 0x44, 0x5b, 0x5a, 0x3e, 0x5a,
32	0x31, 0x39, 0x25, 0x0a, 0x04, 0x22, 0x3c, 0x44, 0x4b, 0x5a, 0x31, 0x39,
33	0x25, 0x0a, 0x04, 0x44, 0x4b, 0x44, 0x5b, 0x5a, 0x3e, 0x5a, 0x31, 0x0a,
34	0x04, 0x22, 0x3c, 0x22, 0x49, 0x44, 0x5b, 0x44, 0x4b, 0x08, 0x02, 0x27,
35	0x43, 0xb8, 0x14, 0xc1, 0xf1, 0x08, 0x02, 0x26, 0x43, 0x29, 0x44, 0x0a,
36	0x05, 0x44, 0x5d, 0x49, 0x5d, 0x60, 0x3e, 0x5a, 0x3b, 0x5b, 0x3f, 0x08,
37	0x0a, 0x07, 0x01, 0x06, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x10, 0x01, 0x17,
38	0x84, 0x00, 0x04, 0x0a, 0x01, 0x01, 0x01, 0x00, 0x0a, 0x02, 0x01, 0x02,
39	0x00, 0x0a, 0x03, 0x01, 0x03, 0x00, 0x0a, 0x04, 0x01, 0x04, 0x10, 0x01,
40	0x17, 0x85, 0x20, 0x04, 0x0a, 0x06, 0x01, 0x05, 0x30, 0x24, 0xb3, 0x99,
41	0x01, 0x17, 0x82, 0x00, 0x04, 0x0a, 0x05, 0x01, 0x05, 0x30, 0x20, 0xb2,
42	0xe6, 0x01, 0x17, 0x82, 0x00, 0x04
43};
44
45
46#define VIRTIO_BLOCK_DRIVER_MODULE_NAME "drivers/disk/virtual/virtio_block/driver_v1"
47#define VIRTIO_BLOCK_DEVICE_MODULE_NAME "drivers/disk/virtual/virtio_block/device_v1"
48#define VIRTIO_BLOCK_DEVICE_ID_GENERATOR	"virtio_block/device_id"
49
50
51typedef struct {
52	device_node*			node;
53	::virtio_device			virtio_device;
54	virtio_device_interface*	virtio;
55	::virtio_queue			virtio_queue;
56	IOScheduler*			io_scheduler;
57	DMAResource*			dma_resource;
58
59	struct virtio_blk_config	config;
60
61	area_id					bufferArea;
62	addr_t					bufferAddr;
63	phys_addr_t				bufferPhysAddr;
64
65	uint64 					features;
66	uint64					capacity;
67	uint32					block_size;
68	uint32					physical_block_size;
69	status_t				media_status;
70
71	mutex					lock;
72	int32					currentRequest;
73	ConditionVariable		interruptCondition;
74	ConditionVariableEntry 	interruptConditionEntry;
75} virtio_block_driver_info;
76
77
78typedef struct {
79	virtio_block_driver_info*		info;
80} virtio_block_handle;
81
82
83#include <stdio.h>
84#include <string.h>
85#include <stdlib.h>
86
87#include <fs/devfs.h>
88
89#include "dma_resources.h"
90#include "IORequest.h"
91#include "IOSchedulerSimple.h"
92
93
94//#define TRACE_VIRTIO_BLOCK
95#ifdef TRACE_VIRTIO_BLOCK
96#	define TRACE(x...) dprintf("virtio_block: " x)
97#else
98#	define TRACE(x...) ;
99#endif
100#define ERROR(x...)			dprintf("\33[33mvirtio_block:\33[0m " x)
101#define CALLED() 			TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
102
103
104static device_manager_info* sDeviceManager;
105
106
107bool virtio_block_set_capacity(virtio_block_driver_info* info);
108
109
110const char *
111get_feature_name(uint64 feature)
112{
113	switch (feature) {
114		case VIRTIO_BLK_F_BARRIER:
115			return "host barrier";
116		case VIRTIO_BLK_F_SIZE_MAX:
117			return "maximum segment size";
118		case VIRTIO_BLK_F_SEG_MAX:
119			return "maximum segment count";
120		case VIRTIO_BLK_F_GEOMETRY:
121			return "disk geometry";
122		case VIRTIO_BLK_F_RO:
123			return "read only";
124		case VIRTIO_BLK_F_BLK_SIZE:
125			return "block size";
126		case VIRTIO_BLK_F_SCSI:
127			return "scsi commands";
128		case VIRTIO_BLK_F_FLUSH:
129			return "flush command";
130		case VIRTIO_BLK_F_TOPOLOGY:
131			return "topology";
132		case VIRTIO_BLK_F_CONFIG_WCE:
133			return "config wce";
134	}
135	return NULL;
136}
137
138
139static status_t
140get_geometry(virtio_block_handle* handle, device_geometry* geometry)
141{
142	virtio_block_driver_info* info = handle->info;
143
144	devfs_compute_geometry_size(geometry, info->capacity, info->block_size);
145	geometry->bytes_per_physical_sector = info->physical_block_size;
146
147	geometry->device_type = B_DISK;
148	geometry->removable = false;
149
150	geometry->read_only = ((info->features & VIRTIO_BLK_F_RO) != 0);
151	geometry->write_once = false;
152
153	TRACE("virtio_block: get_geometry(): %" B_PRIu32 ", %" B_PRIu32 ", %" B_PRIu32 ", %" B_PRIu32
154		", %d, %d, %d, %d\n", geometry->bytes_per_sector, geometry->sectors_per_track,
155		geometry->cylinder_count, geometry->head_count, geometry->device_type,
156		geometry->removable, geometry->read_only, geometry->write_once);
157
158	return B_OK;
159}
160
161
162static void
163virtio_block_config_callback(void* driverCookie)
164{
165	virtio_block_driver_info* info = (virtio_block_driver_info*)driverCookie;
166
167	status_t status = info->virtio->read_device_config(info->virtio_device, 0,
168		&info->config, sizeof(struct virtio_blk_config));
169	if (status != B_OK)
170		return;
171
172	if (virtio_block_set_capacity(info))
173		info->media_status = B_DEV_MEDIA_CHANGED;
174}
175
176
177static void
178virtio_block_callback(void* driverCookie, void* _cookie)
179{
180	virtio_block_driver_info* info = (virtio_block_driver_info*)_cookie;
181
182	void* cookie = NULL;
183	while (info->virtio->queue_dequeue(info->virtio_queue, &cookie, NULL)) {
184		if ((int32)(addr_t)cookie == atomic_get(&info->currentRequest))
185			info->interruptCondition.NotifyAll();
186	}
187}
188
189
190static status_t
191do_io(void* cookie, IOOperation* operation)
192{
193	virtio_block_driver_info* info = (virtio_block_driver_info*)cookie;
194
195	if (mutex_trylock(&info->lock) != B_OK)
196		return B_BUSY;
197
198	BStackOrHeapArray<physical_entry, 16> entries(operation->VecCount() + 2);
199
200	struct virtio_blk_outhdr *header = (struct virtio_blk_outhdr*)info->bufferAddr;
201	header->type = operation->IsWrite() ? VIRTIO_BLK_T_OUT : VIRTIO_BLK_T_IN;
202	header->sector = operation->Offset() / 512;
203	header->ioprio = 1;
204
205	uint8* ack = (uint8*)info->bufferAddr + sizeof(struct virtio_blk_outhdr);
206	*ack = 0xff;
207
208	entries[0].address = info->bufferPhysAddr;
209	entries[0].size = sizeof(struct virtio_blk_outhdr);
210	entries[operation->VecCount() + 1].address = entries[0].address
211		+ sizeof(struct virtio_blk_outhdr);
212	entries[operation->VecCount() + 1].size = sizeof(uint8);
213
214	memcpy(entries + 1, operation->Vecs(), operation->VecCount()
215		* sizeof(physical_entry));
216
217	atomic_add(&info->currentRequest, 1);
218	info->interruptCondition.Add(&info->interruptConditionEntry);
219
220	info->virtio->queue_request_v(info->virtio_queue, entries,
221		1 + (operation->IsWrite() ? operation->VecCount() : 0 ),
222		1 + (operation->IsWrite() ? 0 : operation->VecCount()),
223		(void *)(addr_t)info->currentRequest);
224
225	status_t result = info->interruptConditionEntry.Wait(B_RELATIVE_TIMEOUT,
226		10 * 1000 * 1000);
227
228	size_t bytesTransferred = 0;
229	status_t status = B_OK;
230	if (result != B_OK) {
231		status = EIO;
232	} else {
233		switch (*ack) {
234			case VIRTIO_BLK_S_OK:
235				status = B_OK;
236				bytesTransferred = operation->Length();
237				break;
238			case VIRTIO_BLK_S_UNSUPP:
239				status = ENOTSUP;
240				break;
241			default:
242				status = EIO;
243				break;
244		}
245	}
246
247	info->io_scheduler->OperationCompleted(operation, status,
248		bytesTransferred);
249	mutex_unlock(&info->lock);
250	return status;
251}
252
253
254//	#pragma mark - device module API
255
256
257static status_t
258virtio_block_init_device(void* _info, void** _cookie)
259{
260	CALLED();
261	virtio_block_driver_info* info = (virtio_block_driver_info*)_info;
262
263	device_node* parent = sDeviceManager->get_parent_node(info->node);
264	sDeviceManager->get_driver(parent, (driver_module_info **)&info->virtio,
265		(void **)&info->virtio_device);
266	sDeviceManager->put_node(parent);
267
268	info->virtio->negotiate_features(info->virtio_device,
269		VIRTIO_BLK_F_BARRIER | VIRTIO_BLK_F_SIZE_MAX
270			| VIRTIO_BLK_F_SEG_MAX | VIRTIO_BLK_F_GEOMETRY
271			| VIRTIO_BLK_F_RO | VIRTIO_BLK_F_BLK_SIZE
272			| VIRTIO_BLK_F_FLUSH | VIRTIO_BLK_F_TOPOLOGY
273			| 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	virtio_block_set_capacity(info);
283
284	TRACE("virtio_block: capacity: %" B_PRIu64 ", block_size %" B_PRIu32 "\n",
285		info->capacity, info->block_size);
286
287	status = info->virtio->alloc_queues(info->virtio_device, 1,
288		&info->virtio_queue);
289	if (status != B_OK) {
290		ERROR("queue allocation failed (%s)\n", strerror(status));
291		return status;
292	}
293	status = info->virtio->setup_interrupt(info->virtio_device,
294		virtio_block_config_callback, info);
295
296	if (status == B_OK) {
297		status = info->virtio->queue_setup_interrupt(info->virtio_queue,
298			virtio_block_callback, info);
299	}
300
301	*_cookie = info;
302	return status;
303}
304
305
306static void
307virtio_block_uninit_device(void* _cookie)
308{
309	CALLED();
310	virtio_block_driver_info* info = (virtio_block_driver_info*)_cookie;
311
312	delete info->io_scheduler;
313	delete info->dma_resource;
314}
315
316
317static status_t
318virtio_block_open(void* _info, const char* path, int openMode, void** _cookie)
319{
320	CALLED();
321	virtio_block_driver_info* info = (virtio_block_driver_info*)_info;
322
323	virtio_block_handle* handle = (virtio_block_handle*)malloc(
324		sizeof(virtio_block_handle));
325	if (handle == NULL)
326		return B_NO_MEMORY;
327
328	handle->info = info;
329
330	*_cookie = handle;
331	return B_OK;
332}
333
334
335static status_t
336virtio_block_close(void* cookie)
337{
338	//virtio_block_handle* handle = (virtio_block_handle*)cookie;
339	CALLED();
340
341	return B_OK;
342}
343
344
345static status_t
346virtio_block_free(void* cookie)
347{
348	CALLED();
349	virtio_block_handle* handle = (virtio_block_handle*)cookie;
350
351	free(handle);
352	return B_OK;
353}
354
355
356static status_t
357virtio_block_io(void *cookie, io_request *request)
358{
359	CALLED();
360	virtio_block_handle* handle = (virtio_block_handle*)cookie;
361
362	return handle->info->io_scheduler->ScheduleRequest(request);
363}
364
365
366static status_t
367virtio_block_ioctl(void* cookie, uint32 op, void* buffer, size_t length)
368{
369	CALLED();
370	virtio_block_handle* handle = (virtio_block_handle*)cookie;
371	virtio_block_driver_info* info = handle->info;
372
373	TRACE("ioctl(op = %" B_PRIu32 ")\n", op);
374
375	switch (op) {
376		case B_GET_MEDIA_STATUS:
377		{
378			user_memcpy(buffer, &info->media_status, sizeof(info->media_status));
379			TRACE("B_GET_MEDIA_STATUS: 0x%08" B_PRIx32 "\n", info->media_status);
380			info->media_status = B_OK;
381			return B_OK;
382		}
383
384		case B_GET_DEVICE_SIZE:
385		{
386			size_t size = info->capacity * info->block_size;
387			return user_memcpy(buffer, &size, sizeof(size_t));
388		}
389
390		case B_GET_GEOMETRY:
391		{
392			if (buffer == NULL || length > sizeof(device_geometry))
393				return B_BAD_VALUE;
394
395		 	device_geometry geometry;
396			status_t status = get_geometry(handle, &geometry);
397			if (status != B_OK)
398				return status;
399
400			return user_memcpy(buffer, &geometry, length);
401		}
402
403		case B_GET_ICON_NAME:
404			return user_strlcpy((char*)buffer, "devices/drive-harddisk",
405				B_FILE_NAME_LENGTH);
406
407		case B_GET_VECTOR_ICON:
408		{
409			// TODO: take device type into account!
410			device_icon iconData;
411			if (length != sizeof(device_icon))
412				return B_BAD_VALUE;
413			if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK)
414				return B_BAD_ADDRESS;
415
416			if (iconData.icon_size >= (int32)sizeof(kDriveIcon)) {
417				if (user_memcpy(iconData.icon_data, kDriveIcon,
418						sizeof(kDriveIcon)) != B_OK)
419					return B_BAD_ADDRESS;
420			}
421
422			iconData.icon_size = sizeof(kDriveIcon);
423			return user_memcpy(buffer, &iconData, sizeof(device_icon));
424		}
425
426		/*case B_FLUSH_DRIVE_CACHE:
427			return synchronize_cache(info);*/
428	}
429
430	return B_DEV_INVALID_IOCTL;
431}
432
433
434bool
435virtio_block_set_capacity(virtio_block_driver_info* info)
436{
437	// get capacity
438	uint32 blockSize = 512;
439	if ((info->features & VIRTIO_BLK_F_BLK_SIZE) != 0)
440		blockSize = info->config.blk_size;
441	uint64 capacity = info->config.capacity * 512 / blockSize;
442	uint32 physicalBlockSize = blockSize;
443
444	if ((info->features & VIRTIO_BLK_F_TOPOLOGY) != 0
445		&& info->config.topology.physical_block_exp > 0) {
446		physicalBlockSize = blockSize * (1 << info->config.topology.physical_block_exp);
447	}
448
449	TRACE("set_capacity(device = %p, capacity = %" B_PRIu64 ", blockSize = %" B_PRIu32 ")\n",
450		info, capacity, blockSize);
451
452	if (info->block_size == blockSize && info->capacity == capacity)
453		return false;
454
455	info->capacity = capacity;
456
457	if (info->block_size != 0) {
458		ERROR("old %" B_PRId32 ", new %" B_PRId32 "\n", info->block_size,
459			blockSize);
460		panic("updating DMAResource not yet implemented...");
461	}
462
463	dma_restrictions restrictions;
464	memset(&restrictions, 0, sizeof(restrictions));
465	if ((info->features & VIRTIO_BLK_F_SIZE_MAX) != 0)
466		restrictions.max_segment_size = info->config.size_max;
467	if ((info->features & VIRTIO_BLK_F_SEG_MAX) != 0)
468		restrictions.max_segment_count = info->config.seg_max;
469
470	// TODO: we need to replace the DMAResource in our IOScheduler
471	status_t status = info->dma_resource->Init(restrictions, blockSize,
472		1024, 32);
473	if (status != B_OK)
474		panic("initializing DMAResource failed: %s", strerror(status));
475
476	info->io_scheduler = new(std::nothrow) IOSchedulerSimple(
477		info->dma_resource);
478	if (info->io_scheduler == NULL)
479		panic("allocating IOScheduler failed.");
480
481	// TODO: use whole device name here
482	status = info->io_scheduler->Init("virtio");
483	if (status != B_OK)
484		panic("initializing IOScheduler failed: %s", strerror(status));
485
486	info->io_scheduler->SetCallback(do_io, info);
487
488	info->block_size = blockSize;
489	info->physical_block_size = physicalBlockSize;
490	return true;
491}
492
493
494//	#pragma mark - driver module API
495
496
497static float
498virtio_block_supports_device(device_node *parent)
499{
500	CALLED();
501	const char *bus;
502	uint16 deviceType;
503
504	// make sure parent is really the Virtio bus manager
505	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
506		return -1;
507
508	if (strcmp(bus, "virtio"))
509		return 0.0;
510
511	// check whether it's really a Direct Access Device
512	if (sDeviceManager->get_attr_uint16(parent, VIRTIO_DEVICE_TYPE_ITEM,
513			&deviceType, true) != B_OK || deviceType != VIRTIO_DEVICE_ID_BLOCK)
514		return 0.0;
515
516	TRACE("Virtio block device found!\n");
517
518	return 0.6;
519}
520
521
522static status_t
523virtio_block_register_device(device_node *node)
524{
525	CALLED();
526
527	device_attr attrs[] = {
528		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {.string = "Virtio Block"} },
529		{ NULL }
530	};
531
532	return sDeviceManager->register_node(node, VIRTIO_BLOCK_DRIVER_MODULE_NAME,
533		attrs, NULL, NULL);
534}
535
536
537static status_t
538virtio_block_init_driver(device_node *node, void **cookie)
539{
540	CALLED();
541
542	virtio_block_driver_info* info = (virtio_block_driver_info*)malloc(
543		sizeof(virtio_block_driver_info));
544	if (info == NULL)
545		return B_NO_MEMORY;
546
547	memset(info, 0, sizeof(*info));
548
549	info->media_status = B_OK;
550	info->dma_resource = new(std::nothrow) DMAResource;
551	if (info->dma_resource == NULL) {
552		free(info);
553		return B_NO_MEMORY;
554	}
555
556	// create command buffer area
557	info->bufferArea = create_area("virtio_block command buffer", (void**)&info->bufferAddr,
558		B_ANY_KERNEL_BLOCK_ADDRESS, B_PAGE_SIZE,
559		B_FULL_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
560	if (info->bufferArea < B_OK) {
561		status_t status = info->bufferArea;
562		delete info->dma_resource;
563		free(info);
564		return status;
565	}
566
567	physical_entry entry;
568	status_t status = get_memory_map((void*)info->bufferAddr, B_PAGE_SIZE, &entry, 1);
569	if (status != B_OK) {
570		delete_area(info->bufferArea);
571		delete info->dma_resource;
572		free(info);
573		return status;
574	}
575
576	info->bufferPhysAddr = entry.address;
577	info->interruptCondition.Init(info, "virtio block transfer");
578	info->currentRequest = 0;
579	mutex_init(&info->lock, "virtio block request");
580
581	info->node = node;
582
583	*cookie = info;
584	return B_OK;
585}
586
587
588static void
589virtio_block_uninit_driver(void *_cookie)
590{
591	CALLED();
592	virtio_block_driver_info* info = (virtio_block_driver_info*)_cookie;
593	mutex_destroy(&info->lock);
594	delete_area(info->bufferArea);
595	free(info);
596}
597
598
599static status_t
600virtio_block_register_child_devices(void* _cookie)
601{
602	CALLED();
603	virtio_block_driver_info* info = (virtio_block_driver_info*)_cookie;
604	status_t status;
605
606	int32 id = sDeviceManager->create_id(VIRTIO_BLOCK_DEVICE_ID_GENERATOR);
607	if (id < 0)
608		return id;
609
610	char name[64];
611	snprintf(name, sizeof(name), "disk/virtual/virtio_block/%" B_PRId32 "/raw",
612		id);
613
614	status = sDeviceManager->publish_device(info->node, name,
615		VIRTIO_BLOCK_DEVICE_MODULE_NAME);
616
617	return status;
618}
619
620
621//	#pragma mark -
622
623
624module_dependency module_dependencies[] = {
625	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager },
626	{ NULL }
627};
628
629struct device_module_info sVirtioBlockDevice = {
630	{
631		VIRTIO_BLOCK_DEVICE_MODULE_NAME,
632		0,
633		NULL
634	},
635
636	virtio_block_init_device,
637	virtio_block_uninit_device,
638	NULL, // remove,
639
640	virtio_block_open,
641	virtio_block_close,
642	virtio_block_free,
643	NULL,	// read
644	NULL,	// write
645	virtio_block_io,
646	virtio_block_ioctl,
647
648	NULL,	// select
649	NULL,	// deselect
650};
651
652struct driver_module_info sVirtioBlockDriver = {
653	{
654		VIRTIO_BLOCK_DRIVER_MODULE_NAME,
655		0,
656		NULL
657	},
658
659	virtio_block_supports_device,
660	virtio_block_register_device,
661	virtio_block_init_driver,
662	virtio_block_uninit_driver,
663	virtio_block_register_child_devices,
664	NULL,	// rescan
665	NULL,	// removed
666};
667
668module_info* modules[] = {
669	(module_info*)&sVirtioBlockDriver,
670	(module_info*)&sVirtioBlockDevice,
671	NULL
672};
673