xref: /haiku/src/add-ons/kernel/busses/virtio/virtio_mmio/virtio_mmio.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
1 /*
2  * Copyright 2013, 2018, Jérôme Duval, jerome.duval@gmail.com.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <new>
8 #include <stdio.h>
9 #include <string.h>
10 
11 #include <ByteOrder.h>
12 #include <KernelExport.h>
13 #include <device_manager.h>
14 
15 #include <drivers/ACPI.h>
16 #include <drivers/bus/FDT.h>
17 
18 #include <debug.h>
19 #include <AutoDeleterDrivers.h>
20 
21 #include <acpi.h>
22 
23 #include <virtio.h>
24 #include <virtio_defs.h>
25 #include "VirtioDevice.h"
26 
27 
28 #define VIRTIO_MMIO_DEVICE_MODULE_NAME "busses/virtio/virtio_mmio/driver_v1"
29 #define VIRTIO_MMIO_CONTROLLER_TYPE_NAME "virtio MMIO controller"
30 
31 
32 device_manager_info* gDeviceManager;
33 
34 
35 static const char *
36 virtio_get_feature_name(uint32 feature)
37 {
38 	switch (feature) {
39 		case VIRTIO_FEATURE_NOTIFY_ON_EMPTY:
40 			return "notify on empty";
41 		case VIRTIO_FEATURE_ANY_LAYOUT:
42 			return "any layout";
43 		case VIRTIO_FEATURE_RING_INDIRECT_DESC:
44 			return "ring indirect";
45 		case VIRTIO_FEATURE_RING_EVENT_IDX:
46 			return "ring event index";
47 		case VIRTIO_FEATURE_BAD_FEATURE:
48 			return "bad feature";
49 	}
50 	return NULL;
51 }
52 
53 
54 static void
55 virtio_dump_features(const char* title, uint32 features,
56 	const char* (*get_feature_name)(uint32))
57 {
58 	char features_string[512] = "";
59 	for (uint32 i = 0; i < 32; i++) {
60 		uint32 feature = features & (1 << i);
61 		if (feature == 0)
62 			continue;
63 		const char* name = virtio_get_feature_name(feature);
64 		if (name == NULL)
65 			name = get_feature_name(feature);
66 		if (name != NULL) {
67 			strlcat(features_string, "[", sizeof(features_string));
68 			strlcat(features_string, name, sizeof(features_string));
69 			strlcat(features_string, "] ", sizeof(features_string));
70 		}
71 	}
72 	TRACE("%s: %s\n", title, features_string);
73 }
74 
75 
76 //#pragma mark Device
77 
78 
79 static float
80 virtio_device_supports_device(device_node* parent)
81 {
82 	TRACE("supports_device(%p)\n", parent);
83 
84 	const char* name;
85 	const char* bus;
86 
87 	status_t status = gDeviceManager->get_attr_string(parent,
88 		B_DEVICE_PRETTY_NAME, &name, false);
89 
90 	if (status >= B_OK)
91 		TRACE("  name: %s\n", name);
92 
93 	status = gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false);
94 
95 	if (status < B_OK)
96 		return -1.0f;
97 
98 	// detect virtio device from FDT
99 	if (strcmp(bus, "fdt") == 0) {
100 		const char* compatible;
101 		status = gDeviceManager->get_attr_string(parent, "fdt/compatible",
102 			&compatible, false);
103 
104 		if (status < B_OK)
105 			return -1.0f;
106 
107 		if (strcmp(compatible, "virtio,mmio") != 0)
108 			return 0.0f;
109 
110 		return 1.0f;
111 	}
112 
113 	// detect virtio device from ACPI
114 	if (strcmp(bus, "acpi") == 0) {
115 		const char* hid;
116 		status = gDeviceManager->get_attr_string(parent, "acpi/hid",
117 			&hid, false);
118 
119 		if (status < B_OK)
120 			return -1.0f;
121 
122 		if (strcmp(hid, "LNRO0005") != 0)
123 			return 0.0f;
124 
125 		return 1.0f;
126 	}
127 
128 	return 0.0f;
129 }
130 
131 
132 struct virtio_memory_range {
133 	uint64 base;
134 	uint64 length;
135 };
136 
137 static acpi_status
138 virtio_crs_find_address(acpi_resource *res, void *context)
139 {
140 	virtio_memory_range &range = *((virtio_memory_range *)context);
141 
142 	if (res->type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) {
143 		range.base = res->data.fixed_memory32.address;
144 		range.length = res->data.fixed_memory32.address_length;
145 	}
146 
147 	return B_OK;
148 }
149 
150 
151 static acpi_status
152 virtio_crs_find_interrupt(acpi_resource *res, void *context)
153 {
154 	uint64 &interrupt = *((uint64 *)context);
155 
156 	if (res->type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ)
157 		interrupt = res->data.extended_irq.interrupts[0];
158 
159 	return B_OK;
160 }
161 
162 
163 static status_t
164 virtio_device_register_device(device_node* parent)
165 {
166 	TRACE("register_device(%p)\n", parent);
167 
168 	const char* bus;
169 
170 	status_t status = gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false);
171 
172 	if (status < B_OK)
173 		return -1.0f;
174 
175 	uint64 regs = 0;
176 	uint64 regsLen = 0;
177 
178 	// initialize virtio device from FDT
179 	if (strcmp(bus, "fdt") == 0) {
180 		fdt_device_module_info *parentModule;
181 		fdt_device* parentDev;
182 		if (gDeviceManager->get_driver(parent, (driver_module_info**)&parentModule,
183 				(void**)&parentDev)) {
184 			ERROR("can't get parent node driver");
185 			return B_ERROR;
186 		}
187 
188 		if (!parentModule->get_reg(parentDev, 0, &regs, &regsLen)) {
189 			ERROR("no regs");
190 			return B_ERROR;
191 		}
192 	}
193 
194 	// initialize virtio device from ACPI
195 	if (strcmp(bus, "acpi") == 0) {
196 		acpi_device_module_info *parentModule;
197 		acpi_device parentDev;
198 		if (gDeviceManager->get_driver(parent, (driver_module_info**)&parentModule,
199 				(void**)&parentDev)) {
200 			ERROR("can't get parent node driver");
201 			return B_ERROR;
202 		}
203 
204 		virtio_memory_range range = { 0, 0 };
205 		parentModule->walk_resources(parentDev, (char *)"_CRS",
206 			virtio_crs_find_address, &range);
207 		regs = range.base;
208 		regsLen = range.length;
209 	}
210 
211 	VirtioRegs *volatile mappedRegs;
212 	AreaDeleter fRegsArea(map_physical_memory("Virtio MMIO", regs, regsLen,
213 		B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
214 		(void **)&mappedRegs));
215 
216 	if (!fRegsArea.IsSet()) {
217 		ERROR("cant't map regs");
218 		return B_ERROR;
219 	}
220 
221 	if (mappedRegs->signature != kVirtioSignature) {
222 		ERROR("bad signature: 0x%08" B_PRIx32 ", should be 0x%08" B_PRIx32 "\n",
223 			mappedRegs->signature, (uint32)kVirtioSignature);
224 		return B_ERROR;
225 	}
226 
227 	TRACE("  version: 0x%08" B_PRIx32 "\n",   mappedRegs->version);
228 	TRACE("  deviceId: 0x%08" B_PRIx32 "\n",  mappedRegs->deviceId);
229 	TRACE("  vendorId: 0x%08" B_PRIx32 "\n",  mappedRegs->vendorId);
230 
231 	device_attr attrs[] = {
232 		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "Virtio MMIO"} },
233 		{ B_DEVICE_BUS,         B_STRING_TYPE, {string: "virtio"} },
234 		{ "virtio/version",     B_UINT32_TYPE, {ui32: mappedRegs->version} },
235 		{ "virtio/device_id",   B_UINT32_TYPE, {ui32: mappedRegs->deviceId} },
236 		{ "virtio/type",        B_UINT16_TYPE,
237 			{ui16: (uint16)mappedRegs->deviceId} },
238 		{ "virtio/vendor_id",   B_UINT32_TYPE, {ui32: mappedRegs->vendorId} },
239 		{ NULL }
240 	};
241 
242 	return gDeviceManager->register_node(parent, VIRTIO_MMIO_DEVICE_MODULE_NAME,
243 		attrs, NULL, NULL);
244 }
245 
246 
247 static status_t
248 virtio_device_init_device(device_node* node, void** cookie)
249 {
250 	TRACE("init_device(%p)\n", node);
251 
252 	DeviceNodePutter<&gDeviceManager>
253 		parent(gDeviceManager->get_parent_node(node));
254 
255 	const char* bus;
256 
257 	status_t status = gDeviceManager->get_attr_string(parent.Get(), B_DEVICE_BUS, &bus, false);
258 
259 	if (status < B_OK)
260 		return -1.0f;
261 
262 	uint64 regs = 0;
263 	uint64 regsLen = 0;
264 	uint64 interrupt = 0;
265 
266 	// initialize virtio device from FDT
267 	if (strcmp(bus, "fdt") == 0) {
268 		fdt_device_module_info *parentModule;
269 		fdt_device* parentDev;
270 		if (gDeviceManager->get_driver(parent.Get(),
271 				(driver_module_info**)&parentModule, (void**)&parentDev))
272 			panic("can't get parent node driver");
273 
274 		TRACE("  bus: %p\n", parentModule->get_bus(parentDev));
275 		TRACE("  compatible: %s\n", (const char*)parentModule->get_prop(parentDev,
276 			"compatible", NULL));
277 
278 		for (uint32 i = 0; parentModule->get_reg(parentDev, i, &regs, &regsLen);
279 				i++) {
280 			TRACE("  reg[%" B_PRIu32 "]: (0x%" B_PRIx64 ", 0x%" B_PRIx64 ")\n",
281 				i, regs, regsLen);
282 		}
283 
284 		device_node* interruptController;
285 		for (uint32 i = 0; parentModule->get_interrupt(parentDev,
286 				i, &interruptController, &interrupt); i++) {
287 
288 			const char* name;
289 			if (interruptController == NULL
290 				|| gDeviceManager->get_attr_string(interruptController, "fdt/name",
291 					&name, false) < B_OK) {
292 				name = NULL;
293 			}
294 
295 			TRACE("  interrupt[%" B_PRIu32 "]: ('%s', 0x%" B_PRIx64 ")\n", i,
296 				name, interrupt);
297 		}
298 
299 		if (!parentModule->get_reg(parentDev, 0, &regs, &regsLen)) {
300 			TRACE("  no regs\n");
301 			return B_ERROR;
302 		}
303 
304 		if (!parentModule->get_interrupt(parentDev, 0, &interruptController,
305 				&interrupt)) {
306 			TRACE("  no interrupts\n");
307 			return B_ERROR;
308 		}
309 	}
310 
311 	// initialize virtio device from ACPI
312 	if (strcmp(bus, "acpi") == 0) {
313 		acpi_device_module_info *parentModule;
314 		acpi_device parentDev;
315 		if (gDeviceManager->get_driver(parent.Get(), (driver_module_info**)&parentModule,
316 				(void**)&parentDev)) {
317 			ERROR("can't get parent node driver");
318 			return B_ERROR;
319 		}
320 
321 		virtio_memory_range range = { 0, 0 };
322 		parentModule->walk_resources(parentDev, (char *)"_CRS",
323 			virtio_crs_find_address, &range);
324 		regs = range.base;
325 		regsLen = range.length;
326 
327 		parentModule->walk_resources(parentDev, (char *)"_CRS",
328 			virtio_crs_find_interrupt, &interrupt);
329 
330 		TRACE("  regs: (0x%" B_PRIx64 ", 0x%" B_PRIx64 ")\n",
331 			regs, regsLen);
332 		TRACE("  interrupt: 0x%" B_PRIx64 "\n",
333 			interrupt);
334 	}
335 
336 	ObjectDeleter<VirtioDevice> dev(new(std::nothrow) VirtioDevice());
337 	if (!dev.IsSet())
338 		return B_NO_MEMORY;
339 
340 	status_t res = dev->Init(regs, regsLen, interrupt, 1);
341 	if (res < B_OK)
342 		return res;
343 
344 	*cookie = dev.Detach();
345 	return B_OK;
346 }
347 
348 
349 static void
350 virtio_device_uninit_device(void* cookie)
351 {
352 	TRACE("uninit_device(%p)\n", cookie);
353 	ObjectDeleter<VirtioDevice> dev((VirtioDevice*)cookie);
354 }
355 
356 
357 static status_t
358 virtio_device_register_child_devices(void* cookie)
359 {
360 	TRACE("register_child_devices(%p)\n", cookie);
361 	return B_OK;
362 }
363 
364 
365 //#pragma mark driver API
366 
367 
368 static status_t
369 virtio_device_negotiate_features(virtio_device cookie, uint32 supported,
370 	uint32* negotiated, const char* (*get_feature_name)(uint32))
371 {
372 	TRACE("virtio_device_negotiate_features(%p)\n", cookie);
373 	VirtioDevice* dev = (VirtioDevice*)cookie;
374 
375 	dev->fRegs->status |= kVirtioConfigSAcknowledge;
376 	dev->fRegs->status |= kVirtioConfigSDriver;
377 
378 	uint32 features = dev->fRegs->deviceFeatures;
379 	virtio_dump_features("read features", features, get_feature_name);
380 	features &= supported;
381 
382 	// filter our own features
383 	features &= (VIRTIO_FEATURE_TRANSPORT_MASK
384 		| VIRTIO_FEATURE_RING_INDIRECT_DESC | VIRTIO_FEATURE_RING_EVENT_IDX);
385 	*negotiated = features;
386 
387 	virtio_dump_features("negotiated features", features, get_feature_name);
388 
389 	dev->fRegs->driverFeatures = features;
390 	dev->fRegs->status |= kVirtioConfigSFeaturesOk;
391 	dev->fRegs->status |= kVirtioConfigSDriverOk;
392 	dev->fRegs->guestPageSize = B_PAGE_SIZE;
393 
394 	return B_OK;
395 }
396 
397 
398 static status_t
399 virtio_device_clear_feature(virtio_device cookie, uint32 feature)
400 {
401 	panic("not implemented");
402 	return B_ERROR;
403 }
404 
405 
406 static status_t
407 virtio_device_read_device_config(virtio_device cookie, uint8 offset,
408 	void* buffer, size_t bufferSize)
409 {
410 	TRACE("virtio_device_read_device_config(%p, %d, %" B_PRIuSIZE ")\n", cookie,
411 		offset, bufferSize);
412 	VirtioDevice* dev = (VirtioDevice*)cookie;
413 
414 	// TODO: check ARM support, ARM seems support only 32 bit aligned MMIO access.
415 	vuint8* src = &dev->fRegs->config[offset];
416 	uint8* dst = (uint8*)buffer;
417 	while (bufferSize > 0) {
418 		uint8 size = 4;
419 		if (bufferSize == 1) {
420 			size = 1;
421 			*dst = *src;
422 		} else if (bufferSize <= 3) {
423 			size = 2;
424 			*(uint16*)dst = *(vuint16*)src;
425 		} else
426 			*(uint32*)dst = *(vuint32*)src;
427 
428 		dst += size;
429 		bufferSize -= size;
430 		src += size;
431 	}
432 
433 	return B_OK;
434 }
435 
436 
437 static status_t
438 virtio_device_write_device_config(virtio_device cookie, uint8 offset,
439 	const void* buffer, size_t bufferSize)
440 {
441 	TRACE("virtio_device_write_device_config(%p, %d, %" B_PRIuSIZE ")\n",
442 		cookie, offset, bufferSize);
443 	VirtioDevice* dev = (VirtioDevice*)cookie;
444 
445 	// See virtio_device_read_device_config
446 	uint8* src = (uint8*)buffer;
447 	vuint8* dst = &dev->fRegs->config[offset];
448 	while (bufferSize > 0) {
449 		uint8 size = 4;
450 		if (bufferSize == 1) {
451 			size = 1;
452 			*dst = *src;
453 		} else if (bufferSize <= 3) {
454 			size = 2;
455 			*(vuint16*)dst = *(uint16*)src;
456 		} else
457 			*(vuint32*)dst = *(uint32*)src;
458 
459 		dst += size;
460 		bufferSize -= size;
461 		src += size;
462 	}
463 
464 	return B_OK;
465 }
466 
467 
468 static status_t
469 virtio_device_alloc_queues(virtio_device cookie, size_t count,
470 	virtio_queue* queues)
471 {
472 	TRACE("virtio_device_alloc_queues(%p, %" B_PRIuSIZE ")\n", cookie, count);
473 	VirtioDevice* dev = (VirtioDevice*)cookie;
474 
475 	ArrayDeleter<ObjectDeleter<VirtioQueue> > newQueues(new(std::nothrow)
476 		ObjectDeleter<VirtioQueue>[count]);
477 
478 	if (!newQueues.IsSet())
479 		return B_NO_MEMORY;
480 
481 	for (size_t i = 0; i < count; i++) {
482 		newQueues[i].SetTo(new(std::nothrow) VirtioQueue(dev, i));
483 
484 		if (!newQueues[i].IsSet())
485 			return B_NO_MEMORY;
486 
487 		status_t res = newQueues[i]->Init();
488 		if (res < B_OK)
489 			return res;
490 	}
491 
492 	dev->fQueueCnt = count;
493 	dev->fQueues.SetTo(newQueues.Detach());
494 
495 	for (size_t i = 0; i < count; i++)
496 		queues[i] = dev->fQueues[i].Get();
497 
498 	return B_OK;
499 }
500 
501 
502 static void
503 virtio_device_free_queues(virtio_device cookie)
504 {
505 	TRACE("virtio_device_free_queues(%p)\n", cookie);
506 	VirtioDevice* dev = (VirtioDevice*)cookie;
507 
508 	dev->fQueues.Unset();
509 	dev->fQueueCnt = 0;
510 }
511 
512 
513 static status_t
514 virtio_device_setup_interrupt(virtio_device cookie,
515 	virtio_intr_func config_handler, void* driverCookie)
516 {
517 	VirtioDevice* dev = (VirtioDevice*)cookie;
518 	TRACE("virtio_device_setup_interrupt(%p, %#" B_PRIxADDR ")\n", dev,
519 		(addr_t)config_handler);
520 
521 	dev->fConfigHandler = config_handler;
522 	dev->fConfigHandlerCookie = driverCookie;
523 	dev->fConfigHandlerRef.SetTo((config_handler == NULL)
524 		? NULL : &dev->fIrqHandler);
525 
526 	return B_OK;
527 }
528 
529 
530 static status_t
531 virtio_device_free_interrupts(virtio_device cookie)
532 {
533 	VirtioDevice* dev = (VirtioDevice*)cookie;
534 	TRACE("virtio_device_free_interrupts(%p)\n", dev);
535 
536 	for (int32 i = 0; i < dev->fQueueCnt; i++) {
537 		VirtioQueue* queue = dev->fQueues[i].Get();
538 		queue->fQueueHandler = NULL;
539 		queue->fQueueHandlerCookie = NULL;
540 		queue->fQueueHandlerRef.Unset();
541 	}
542 
543 	dev->fConfigHandler = NULL;
544 	dev->fConfigHandlerCookie = NULL;
545 	dev->fConfigHandlerRef.Unset();
546 
547 	return B_OK;
548 }
549 
550 
551 static status_t
552 virtio_device_queue_setup_interrupt(virtio_queue aQueue,
553 	virtio_callback_func handler, void* cookie)
554 {
555 	TRACE("virtio_device_queue_setup_interrupt(%p, %p)\n", aQueue, handler);
556 
557 	VirtioQueue* queue = (VirtioQueue*)aQueue;
558 	VirtioDevice* dev = queue->fDev;
559 
560 	queue->fQueueHandler = handler;
561 	queue->fQueueHandlerCookie = cookie;
562 	queue->fQueueHandlerRef.SetTo((handler == NULL) ? NULL : &dev->fIrqHandler);
563 
564 	return B_OK;
565 }
566 
567 
568 static status_t
569 virtio_device_queue_request_v(virtio_queue aQueue,
570 	const physical_entry* vector,
571 	size_t readVectorCount, size_t writtenVectorCount,
572 	void* cookie)
573 {
574 	// TRACE("virtio_device_queue_request_v(%p, %" B_PRIuSIZE ", %" B_PRIuSIZE
575 	//	", %p)\n", aQueue, readVectorCount, writtenVectorCount, cookie);
576 	VirtioQueue* queue = (VirtioQueue*)aQueue;
577 
578 	return queue->Enqueue(vector, readVectorCount, writtenVectorCount, cookie);
579 }
580 
581 
582 static status_t
583 virtio_device_queue_request(virtio_queue aQueue,
584 	const physical_entry* readEntry,
585 	const physical_entry* writtenEntry, void* cookie)
586 {
587 	VirtioQueue* queue = (VirtioQueue*)aQueue;
588 
589 	physical_entry vector[2];
590 	physical_entry* vectorEnd = vector;
591 
592 	if (readEntry != NULL)
593 		*vectorEnd++ = *readEntry;
594 
595 	if (writtenEntry != NULL)
596 		*vectorEnd++ = *writtenEntry;
597 
598 	return queue->Enqueue(vector, (readEntry != NULL) ? 1 : 0,
599 		(writtenEntry != NULL) ? 1 : 0, cookie);
600 }
601 
602 
603 static bool
604 virtio_device_queue_is_full(virtio_queue queue)
605 {
606 	panic("not implemented");
607 	return false;
608 }
609 
610 
611 static bool
612 virtio_device_queue_is_empty(virtio_queue aQueue)
613 {
614 	VirtioQueue *queue = (VirtioQueue *)aQueue;
615 	return queue->fUsed->idx == queue->fLastUsed;
616 }
617 
618 
619 static uint16
620 virtio_device_queue_size(virtio_queue aQueue)
621 {
622 	VirtioQueue *queue = (VirtioQueue *)aQueue;
623 	return (uint16)queue->fQueueLen;
624 }
625 
626 
627 static bool
628 virtio_device_queue_dequeue(virtio_queue aQueue, void** _cookie,
629 	uint32* _usedLength)
630 {
631 	// TRACE("virtio_device_queue_dequeue(%p)\n", aQueue);
632 	VirtioQueue* queue = (VirtioQueue*)aQueue;
633 	return queue->Dequeue(_cookie, _usedLength);
634 }
635 
636 
637 //#pragma mark -
638 
639 
640 module_dependency module_dependencies[] = {
641 	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager },
642 	{ NULL }
643 };
644 
645 
646 static virtio_device_interface sVirtioDevice = {
647 	{
648 		{
649 			VIRTIO_MMIO_DEVICE_MODULE_NAME,
650 			0,
651 			NULL
652 		},
653 
654 		virtio_device_supports_device,
655 		virtio_device_register_device,
656 		virtio_device_init_device,
657 		virtio_device_uninit_device,
658 		virtio_device_register_child_devices,
659 		NULL,	// rescan
660 		NULL,	// device removed
661 	},
662 	virtio_device_negotiate_features,
663 	virtio_device_clear_feature,
664 	virtio_device_read_device_config,
665 	virtio_device_write_device_config,
666 	virtio_device_alloc_queues,
667 	virtio_device_free_queues,
668 	virtio_device_setup_interrupt,
669 	virtio_device_free_interrupts,
670 	virtio_device_queue_setup_interrupt,
671 	virtio_device_queue_request,
672 	virtio_device_queue_request_v,
673 	virtio_device_queue_is_full,
674 	virtio_device_queue_is_empty,
675 	virtio_device_queue_size,
676 	virtio_device_queue_dequeue,
677 };
678 
679 
680 module_info* modules[] = {
681 	(module_info* )&sVirtioDevice,
682 	NULL
683 };
684