xref: /haiku/src/add-ons/kernel/busses/virtio/virtio_mmio/virtio_mmio.cpp (revision da7a1c5cc8436d644d9790d083b1222009e517dd)
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 
413 	VirtioDevice* dev = (VirtioDevice*)cookie;
414 	memcpy(buffer, (void*)(dev->fRegs->config + offset), bufferSize);
415 
416 	return B_OK;
417 }
418 
419 
420 static status_t
421 virtio_device_write_device_config(virtio_device cookie, uint8 offset,
422 	const void* buffer, size_t bufferSize)
423 {
424 	TRACE("virtio_device_write_device_config(%p, %d, %" B_PRIuSIZE ")\n",
425 		cookie, offset, bufferSize);
426 	VirtioDevice* dev = (VirtioDevice*)cookie;
427 	memcpy((void*)(dev->fRegs->config + offset), buffer, bufferSize);
428 	return B_OK;
429 }
430 
431 
432 static status_t
433 virtio_device_alloc_queues(virtio_device cookie, size_t count,
434 	virtio_queue* queues)
435 {
436 	TRACE("virtio_device_alloc_queues(%p, %" B_PRIuSIZE ")\n", cookie, count);
437 	VirtioDevice* dev = (VirtioDevice*)cookie;
438 
439 	ArrayDeleter<ObjectDeleter<VirtioQueue> > newQueues(new(std::nothrow)
440 		ObjectDeleter<VirtioQueue>[count]);
441 
442 	if (!newQueues.IsSet())
443 		return B_NO_MEMORY;
444 
445 	for (size_t i = 0; i < count; i++) {
446 		newQueues[i].SetTo(new(std::nothrow) VirtioQueue(dev, i));
447 
448 		if (!newQueues[i].IsSet())
449 			return B_NO_MEMORY;
450 
451 		status_t res = newQueues[i]->Init();
452 		if (res < B_OK)
453 			return res;
454 	}
455 
456 	dev->fQueueCnt = count;
457 	dev->fQueues.SetTo(newQueues.Detach());
458 
459 	for (size_t i = 0; i < count; i++)
460 		queues[i] = dev->fQueues[i].Get();
461 
462 	return B_OK;
463 }
464 
465 
466 static void
467 virtio_device_free_queues(virtio_device cookie)
468 {
469 	TRACE("virtio_device_free_queues(%p)\n", cookie);
470 	VirtioDevice* dev = (VirtioDevice*)cookie;
471 
472 	dev->fQueues.Unset();
473 	dev->fQueueCnt = 0;
474 }
475 
476 
477 static status_t
478 virtio_device_setup_interrupt(virtio_device cookie,
479 	virtio_intr_func config_handler, void* driverCookie)
480 {
481 	VirtioDevice* dev = (VirtioDevice*)cookie;
482 	TRACE("virtio_device_setup_interrupt(%p, %#" B_PRIxADDR ")\n", dev,
483 		(addr_t)config_handler);
484 
485 	dev->fConfigHandler = config_handler;
486 	dev->fConfigHandlerCookie = driverCookie;
487 	dev->fConfigHandlerRef.SetTo((config_handler == NULL)
488 		? NULL : &dev->fIrqHandler);
489 
490 	return B_OK;
491 }
492 
493 
494 static status_t
495 virtio_device_free_interrupts(virtio_device cookie)
496 {
497 	VirtioDevice* dev = (VirtioDevice*)cookie;
498 	TRACE("virtio_device_free_interrupts(%p)\n", dev);
499 
500 	for (int32 i = 0; i < dev->fQueueCnt; i++) {
501 		VirtioQueue* queue = dev->fQueues[i].Get();
502 		queue->fQueueHandler = NULL;
503 		queue->fQueueHandlerCookie = NULL;
504 		queue->fQueueHandlerRef.Unset();
505 	}
506 
507 	dev->fConfigHandler = NULL;
508 	dev->fConfigHandlerCookie = NULL;
509 	dev->fConfigHandlerRef.Unset();
510 
511 	return B_OK;
512 }
513 
514 
515 static status_t
516 virtio_device_queue_setup_interrupt(virtio_queue aQueue,
517 	virtio_callback_func handler, void* cookie)
518 {
519 	TRACE("virtio_device_queue_setup_interrupt(%p, %p)\n", aQueue, handler);
520 
521 	VirtioQueue* queue = (VirtioQueue*)aQueue;
522 	VirtioDevice* dev = queue->fDev;
523 
524 	queue->fQueueHandler = handler;
525 	queue->fQueueHandlerCookie = cookie;
526 	queue->fQueueHandlerRef.SetTo((handler == NULL) ? NULL : &dev->fIrqHandler);
527 
528 	return B_OK;
529 }
530 
531 
532 static status_t
533 virtio_device_queue_request_v(virtio_queue aQueue,
534 	const physical_entry* vector,
535 	size_t readVectorCount, size_t writtenVectorCount,
536 	void* cookie)
537 {
538 	// TRACE("virtio_device_queue_request_v(%p, %" B_PRIuSIZE ", %" B_PRIuSIZE
539 	//	", %p)\n", aQueue, readVectorCount, writtenVectorCount, cookie);
540 	VirtioQueue* queue = (VirtioQueue*)aQueue;
541 
542 	return queue->Enqueue(vector, readVectorCount, writtenVectorCount, cookie);
543 }
544 
545 
546 static status_t
547 virtio_device_queue_request(virtio_queue aQueue,
548 	const physical_entry* readEntry,
549 	const physical_entry* writtenEntry, void* cookie)
550 {
551 	VirtioQueue* queue = (VirtioQueue*)aQueue;
552 
553 	physical_entry vector[2];
554 	physical_entry* vectorEnd = vector;
555 
556 	if (readEntry != NULL)
557 		*vectorEnd++ = *readEntry;
558 
559 	if (writtenEntry != NULL)
560 		*vectorEnd++ = *writtenEntry;
561 
562 	return queue->Enqueue(vector, (readEntry != NULL) ? 1 : 0,
563 		(writtenEntry != NULL) ? 1 : 0, cookie);
564 }
565 
566 
567 static bool
568 virtio_device_queue_is_full(virtio_queue queue)
569 {
570 	panic("not implemented");
571 	return false;
572 }
573 
574 
575 static bool
576 virtio_device_queue_is_empty(virtio_queue aQueue)
577 {
578 	VirtioQueue *queue = (VirtioQueue *)aQueue;
579 	return queue->fUsed->idx == queue->fLastUsed;
580 }
581 
582 
583 static uint16
584 virtio_device_queue_size(virtio_queue aQueue)
585 {
586 	VirtioQueue *queue = (VirtioQueue *)aQueue;
587 	return (uint16)queue->fQueueLen;
588 }
589 
590 
591 static bool
592 virtio_device_queue_dequeue(virtio_queue aQueue, void** _cookie,
593 	uint32* _usedLength)
594 {
595 	// TRACE("virtio_device_queue_dequeue(%p)\n", aQueue);
596 	VirtioQueue* queue = (VirtioQueue*)aQueue;
597 	return queue->Dequeue(_cookie, _usedLength);
598 }
599 
600 
601 //#pragma mark -
602 
603 
604 module_dependency module_dependencies[] = {
605 	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager },
606 	{ NULL }
607 };
608 
609 
610 static virtio_device_interface sVirtioDevice = {
611 	{
612 		{
613 			VIRTIO_MMIO_DEVICE_MODULE_NAME,
614 			0,
615 			NULL
616 		},
617 
618 		virtio_device_supports_device,
619 		virtio_device_register_device,
620 		virtio_device_init_device,
621 		virtio_device_uninit_device,
622 		virtio_device_register_child_devices,
623 		NULL,	// rescan
624 		NULL,	// device removed
625 	},
626 	virtio_device_negotiate_features,
627 	virtio_device_clear_feature,
628 	virtio_device_read_device_config,
629 	virtio_device_write_device_config,
630 	virtio_device_alloc_queues,
631 	virtio_device_free_queues,
632 	virtio_device_setup_interrupt,
633 	virtio_device_free_interrupts,
634 	virtio_device_queue_setup_interrupt,
635 	virtio_device_queue_request,
636 	virtio_device_queue_request_v,
637 	virtio_device_queue_is_full,
638 	virtio_device_queue_is_empty,
639 	virtio_device_queue_size,
640 	virtio_device_queue_dequeue,
641 };
642 
643 
644 module_info* modules[] = {
645 	(module_info* )&sVirtioDevice,
646 	NULL
647 };
648