xref: /haiku/src/add-ons/kernel/busses/virtio/virtio_mmio/virtio_mmio.cpp (revision 52f7c9389475e19fc21487b38064b4390eeb6fea)
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/bus/FDT.h>
16 
17 #include <debug.h>
18 #include <AutoDeleterDrivers.h>
19 
20 #include <virtio.h>
21 #include <virtio_defs.h>
22 #include "VirtioDevice.h"
23 
24 
25 #define VIRTIO_MMIO_DEVICE_MODULE_NAME "busses/virtio/virtio_mmio/driver_v1"
26 #define VIRTIO_MMIO_CONTROLLER_TYPE_NAME "virtio MMIO controller"
27 
28 
29 device_manager_info* gDeviceManager;
30 
31 
32 static const char *
33 virtio_get_feature_name(uint32 feature)
34 {
35 	switch (feature) {
36 		case VIRTIO_FEATURE_NOTIFY_ON_EMPTY:
37 			return "notify on empty";
38 		case VIRTIO_FEATURE_ANY_LAYOUT:
39 			return "any layout";
40 		case VIRTIO_FEATURE_RING_INDIRECT_DESC:
41 			return "ring indirect";
42 		case VIRTIO_FEATURE_RING_EVENT_IDX:
43 			return "ring event index";
44 		case VIRTIO_FEATURE_BAD_FEATURE:
45 			return "bad feature";
46 	}
47 	return NULL;
48 }
49 
50 
51 static void
52 virtio_dump_features(const char* title, uint32 features,
53 	const char* (*get_feature_name)(uint32))
54 {
55 	char features_string[512] = "";
56 	for (uint32 i = 0; i < 32; i++) {
57 		uint32 feature = features & (1 << i);
58 		if (feature == 0)
59 			continue;
60 		const char* name = virtio_get_feature_name(feature);
61 		if (name == NULL)
62 			name = get_feature_name(feature);
63 		if (name != NULL) {
64 			strlcat(features_string, "[", sizeof(features_string));
65 			strlcat(features_string, name, sizeof(features_string));
66 			strlcat(features_string, "] ", sizeof(features_string));
67 		}
68 	}
69 	TRACE("%s: %s\n", title, features_string);
70 }
71 
72 
73 //#pragma mark Device
74 
75 
76 static float
77 virtio_device_supports_device(device_node* parent)
78 {
79 	TRACE("supports_device(%p)\n", parent);
80 
81 	const char* name;
82 	const char* bus;
83 	const char* compatible;
84 
85 	status_t status = gDeviceManager->get_attr_string(parent,
86 		B_DEVICE_PRETTY_NAME, &name, false);
87 
88 	if (status >= B_OK)
89 		dprintf("  name: %s\n", name);
90 
91 	status = gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false);
92 
93 	if (status < B_OK)
94 		return -1.0f;
95 
96 	status = gDeviceManager->get_attr_string(parent, "fdt/compatible",
97 		&compatible, false);
98 
99 	if (status < B_OK)
100 		return -1.0f;
101 
102 	if (strcmp(bus, "fdt") != 0)
103 		return 0.0f;
104 
105 	if (strcmp(compatible, "virtio,mmio") != 0)
106 		return 0.0f;
107 
108 	return 1.0f;
109 }
110 
111 
112 static status_t
113 virtio_device_register_device(device_node* parent)
114 {
115 	TRACE("register_device(%p)\n", parent);
116 
117 	fdt_device_module_info *parentModule;
118 	fdt_device* parentDev;
119 	if (gDeviceManager->get_driver(parent, (driver_module_info**)&parentModule,
120 			(void**)&parentDev)) {
121 		ERROR("can't get parent node driver");
122 		return B_ERROR;
123 	}
124 
125 	uint64 regs, regsLen;
126 	if (!parentModule->get_reg(parentDev, 0, &regs, &regsLen)) {
127 		ERROR("no regs");
128 		return B_ERROR;
129 	}
130 
131 	VirtioRegs *volatile mappedRegs;
132 	AreaDeleter fRegsArea(map_physical_memory("Virtio MMIO", regs, regsLen,
133 		B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
134 		(void **)&mappedRegs));
135 
136 	if (!fRegsArea.IsSet()) {
137 		ERROR("cant't map regs");
138 		return B_ERROR;
139 	}
140 
141 	if (mappedRegs->signature != kVirtioSignature) {
142 		ERROR("bad signature: 0x%08" B_PRIx32 ", should be 0x%08" B_PRIx32 "\n",
143 			mappedRegs->signature, (uint32)kVirtioSignature);
144 		return B_ERROR;
145 	}
146 
147 	dprintf("  version: 0x%08" B_PRIx32 "\n",   mappedRegs->version);
148 	dprintf("  deviceId: 0x%08" B_PRIx32 "\n",  mappedRegs->deviceId);
149 	dprintf("  vendorId: 0x%08" B_PRIx32 "\n",  mappedRegs->vendorId);
150 
151 	device_attr attrs[] = {
152 		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "Virtio MMIO"} },
153 		{ B_DEVICE_BUS,         B_STRING_TYPE, {string: "virtio"} },
154 		{ "virtio/version",     B_UINT32_TYPE, {ui32: mappedRegs->version} },
155 		{ "virtio/device_id",   B_UINT32_TYPE, {ui32: mappedRegs->deviceId} },
156 		{ "virtio/type",        B_UINT16_TYPE,
157 			{ui16: (uint16)mappedRegs->deviceId} },
158 		{ "virtio/vendor_id",   B_UINT32_TYPE, {ui32: mappedRegs->vendorId} },
159 		{ NULL }
160 	};
161 
162 	return gDeviceManager->register_node(parent, VIRTIO_MMIO_DEVICE_MODULE_NAME,
163 		attrs, NULL, NULL);
164 }
165 
166 
167 static status_t
168 virtio_device_init_device(device_node* node, void** cookie)
169 {
170 	TRACE("init_device(%p)\n", node);
171 
172 	DeviceNodePutter<&gDeviceManager>
173 		parent(gDeviceManager->get_parent_node(node));
174 
175 	fdt_device_module_info *parentModule;
176 	fdt_device* parentDev;
177 	if (gDeviceManager->get_driver(parent.Get(),
178 			(driver_module_info**)&parentModule, (void**)&parentDev))
179 		panic("can't get parent node driver");
180 
181 	dprintf("  bus: %p\n", parentModule->get_bus(parentDev));
182 	dprintf("  compatible: %s\n", (const char*)parentModule->get_prop(parentDev,
183 		"compatible", NULL));
184 
185 	uint64 regs;
186 	uint64 regsLen;
187 	for (uint32 i = 0; parentModule->get_reg(parentDev, i, &regs, &regsLen);
188 			i++) {
189 		dprintf("  reg[%" B_PRIu32 "]: (0x%" B_PRIx64 ", 0x%" B_PRIx64 ")\n",
190 			i, regs, regsLen);
191 	}
192 
193 	device_node* interruptController;
194 	uint64 interrupt;
195 	for (uint32 i = 0; parentModule->get_interrupt(parentDev,
196 			i, &interruptController, &interrupt); i++) {
197 
198 		const char* name;
199 		if (interruptController == NULL
200 			|| gDeviceManager->get_attr_string(interruptController, "fdt/name",
201 				&name, false) < B_OK) {
202 			name = NULL;
203 		}
204 
205 		dprintf("  interrupt[%" B_PRIu32 "]: ('%s', 0x%" B_PRIx64 ")\n", i,
206 			name, interrupt);
207 	}
208 
209 	if (!parentModule->get_reg(parentDev, 0, &regs, &regsLen)) {
210 		dprintf("  no regs\n");
211 		return B_ERROR;
212 	}
213 
214 	if (!parentModule->get_interrupt(parentDev, 0, &interruptController,
215 			&interrupt)) {
216 		dprintf("  no interrupts\n");
217 		return B_ERROR;
218 	}
219 
220 	ObjectDeleter<VirtioDevice> dev(new(std::nothrow) VirtioDevice());
221 	if (!dev.IsSet())
222 		return B_NO_MEMORY;
223 
224 	status_t res = dev->Init(regs, regsLen, interrupt, 1);
225 	if (res < B_OK)
226 		return res;
227 
228 	*cookie = dev.Detach();
229 	return B_OK;
230 }
231 
232 
233 static void
234 virtio_device_uninit_device(void* cookie)
235 {
236 	TRACE("uninit_device(%p)\n", cookie);
237 	ObjectDeleter<VirtioDevice> dev((VirtioDevice*)cookie);
238 }
239 
240 
241 static status_t
242 virtio_device_register_child_devices(void* cookie)
243 {
244 	TRACE("register_child_devices(%p)\n", cookie);
245 	return B_OK;
246 }
247 
248 
249 //#pragma mark driver API
250 
251 
252 static status_t
253 virtio_device_negotiate_features(virtio_device cookie, uint32 supported,
254 	uint32* negotiated, const char* (*get_feature_name)(uint32))
255 {
256 	TRACE("virtio_device_negotiate_features(%p)\n", cookie);
257 	VirtioDevice* dev = (VirtioDevice*)cookie;
258 
259 	dev->fRegs->status |= kVirtioConfigSAcknowledge;
260 	dev->fRegs->status |= kVirtioConfigSDriver;
261 
262 	uint32 features = dev->fRegs->deviceFeatures;
263 	virtio_dump_features("read features", features, get_feature_name);
264 	features &= supported;
265 
266 	// filter our own features
267 	features &= (VIRTIO_FEATURE_TRANSPORT_MASK
268 		| VIRTIO_FEATURE_RING_INDIRECT_DESC | VIRTIO_FEATURE_RING_EVENT_IDX);
269 	*negotiated = features;
270 
271 	virtio_dump_features("negotiated features", features, get_feature_name);
272 
273 	dev->fRegs->driverFeatures = features;
274 	dev->fRegs->status |= kVirtioConfigSFeaturesOk;
275 	dev->fRegs->status |= kVirtioConfigSDriverOk;
276 	dev->fRegs->guestPageSize = B_PAGE_SIZE;
277 
278 	return B_OK;
279 }
280 
281 
282 static status_t
283 virtio_device_clear_feature(virtio_device cookie, uint32 feature)
284 {
285 	panic("not implemented");
286 	return B_ERROR;
287 }
288 
289 
290 static status_t
291 virtio_device_read_device_config(virtio_device cookie, uint8 offset,
292 	void* buffer, size_t bufferSize)
293 {
294 	TRACE("virtio_device_read_device_config(%p, %d, %" B_PRIuSIZE ")\n", cookie,
295 		offset, bufferSize);
296 
297 	VirtioDevice* dev = (VirtioDevice*)cookie;
298 	memcpy(buffer, (void*)(dev->fRegs->config + offset), bufferSize);
299 
300 	return B_OK;
301 }
302 
303 
304 static status_t
305 virtio_device_write_device_config(virtio_device cookie, uint8 offset,
306 	const void* buffer, size_t bufferSize)
307 {
308 	TRACE("virtio_device_write_device_config(%p, %d, %" B_PRIuSIZE ")\n",
309 		cookie, offset, bufferSize);
310 	VirtioDevice* dev = (VirtioDevice*)cookie;
311 	memcpy((void*)(dev->fRegs->config + offset), buffer, bufferSize);
312 	return B_OK;
313 }
314 
315 
316 static status_t
317 virtio_device_alloc_queues(virtio_device cookie, size_t count,
318 	virtio_queue* queues)
319 {
320 	TRACE("virtio_device_alloc_queues(%p, %" B_PRIuSIZE ")\n", cookie, count);
321 	VirtioDevice* dev = (VirtioDevice*)cookie;
322 
323 	ArrayDeleter<ObjectDeleter<VirtioQueue> > newQueues(new(std::nothrow)
324 		ObjectDeleter<VirtioQueue>[count]);
325 
326 	if (!newQueues.IsSet())
327 		return B_NO_MEMORY;
328 
329 	for (size_t i = 0; i < count; i++) {
330 		newQueues[i].SetTo(new(std::nothrow) VirtioQueue(dev, i));
331 
332 		if (!newQueues[i].IsSet())
333 			return B_NO_MEMORY;
334 
335 		status_t res = newQueues[i]->Init();
336 		if (res < B_OK)
337 			return res;
338 	}
339 
340 	dev->fQueueCnt = count;
341 	dev->fQueues.SetTo(newQueues.Detach());
342 
343 	for (size_t i = 0; i < count; i++)
344 		queues[i] = dev->fQueues[i].Get();
345 
346 	return B_OK;
347 }
348 
349 
350 static void
351 virtio_device_free_queues(virtio_device cookie)
352 {
353 	TRACE("virtio_device_free_queues(%p)\n", cookie);
354 	VirtioDevice* dev = (VirtioDevice*)cookie;
355 
356 	dev->fQueues.Unset();
357 	dev->fQueueCnt = 0;
358 }
359 
360 
361 static status_t
362 virtio_device_setup_interrupt(virtio_device cookie,
363 	virtio_intr_func config_handler, void* driverCookie)
364 {
365 	VirtioDevice* dev = (VirtioDevice*)cookie;
366 	TRACE("virtio_device_setup_interrupt(%p, %#" B_PRIxADDR ")\n", dev,
367 		(addr_t)config_handler);
368 
369 	dev->fConfigHandler = config_handler;
370 	dev->fConfigHandlerCookie = driverCookie;
371 	dev->fConfigHandlerRef.SetTo((config_handler == NULL)
372 		? NULL : &dev->fIrqHandler);
373 
374 	return B_OK;
375 }
376 
377 
378 static status_t
379 virtio_device_free_interrupts(virtio_device cookie)
380 {
381 	VirtioDevice* dev = (VirtioDevice*)cookie;
382 	TRACE("virtio_device_free_interrupts(%p)\n", dev);
383 
384 	for (int32 i = 0; i < dev->fQueueCnt; i++) {
385 		VirtioQueue* queue = dev->fQueues[i].Get();
386 		queue->fQueueHandler = NULL;
387 		queue->fQueueHandlerCookie = NULL;
388 		queue->fQueueHandlerRef.Unset();
389 	}
390 
391 	dev->fConfigHandler = NULL;
392 	dev->fConfigHandlerCookie = NULL;
393 	dev->fConfigHandlerRef.Unset();
394 
395 	return B_OK;
396 }
397 
398 
399 static status_t
400 virtio_device_queue_setup_interrupt(virtio_queue aQueue,
401 	virtio_callback_func handler, void* cookie)
402 {
403 	TRACE("virtio_device_queue_setup_interrupt(%p, %p)\n", aQueue, handler);
404 
405 	VirtioQueue* queue = (VirtioQueue*)aQueue;
406 	VirtioDevice* dev = queue->fDev;
407 
408 	queue->fQueueHandler = handler;
409 	queue->fQueueHandlerCookie = cookie;
410 	queue->fQueueHandlerRef.SetTo((handler == NULL) ? NULL : &dev->fIrqHandler);
411 
412 	return B_OK;
413 }
414 
415 
416 static status_t
417 virtio_device_queue_request_v(virtio_queue aQueue,
418 	const physical_entry* vector,
419 	size_t readVectorCount, size_t writtenVectorCount,
420 	void* cookie)
421 {
422 	// TRACE("virtio_device_queue_request_v(%p, %" B_PRIuSIZE ", %" B_PRIuSIZE
423 	//	", %p)\n", aQueue, readVectorCount, writtenVectorCount, cookie);
424 	VirtioQueue* queue = (VirtioQueue*)aQueue;
425 
426 	return queue->Enqueue(vector, readVectorCount, writtenVectorCount, cookie);
427 }
428 
429 
430 static status_t
431 virtio_device_queue_request(virtio_queue aQueue,
432 	const physical_entry* readEntry,
433 	const physical_entry* writtenEntry, void* cookie)
434 {
435 	VirtioQueue* queue = (VirtioQueue*)aQueue;
436 
437 	physical_entry vector[2];
438 	physical_entry* vectorEnd = vector;
439 
440 	if (readEntry != NULL)
441 		*vectorEnd++ = *readEntry;
442 
443 	if (writtenEntry != NULL)
444 		*vectorEnd++ = *writtenEntry;
445 
446 	return queue->Enqueue(vector, (readEntry != NULL) ? 1 : 0,
447 		(writtenEntry != NULL) ? 1 : 0, cookie);
448 }
449 
450 
451 static bool
452 virtio_device_queue_is_full(virtio_queue queue)
453 {
454 	panic("not implemented");
455 	return false;
456 }
457 
458 
459 static bool
460 virtio_device_queue_is_empty(virtio_queue aQueue)
461 {
462 	VirtioQueue *queue = (VirtioQueue *)aQueue;
463 	return queue->fUsed->idx == queue->fLastUsed;
464 }
465 
466 
467 static uint16
468 virtio_device_queue_size(virtio_queue aQueue)
469 {
470 	VirtioQueue *queue = (VirtioQueue *)aQueue;
471 	return (uint16)queue->fQueueLen;
472 }
473 
474 
475 static bool
476 virtio_device_queue_dequeue(virtio_queue aQueue, void** _cookie,
477 	uint32* _usedLength)
478 {
479 	// TRACE("virtio_device_queue_dequeue(%p)\n", aQueue);
480 	VirtioQueue* queue = (VirtioQueue*)aQueue;
481 	return queue->Dequeue(_cookie, _usedLength);
482 }
483 
484 
485 //#pragma mark -
486 
487 
488 module_dependency module_dependencies[] = {
489 	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager },
490 	{ NULL }
491 };
492 
493 
494 static virtio_device_interface sVirtioDevice = {
495 	{
496 		{
497 			VIRTIO_MMIO_DEVICE_MODULE_NAME,
498 			0,
499 			NULL
500 		},
501 
502 		virtio_device_supports_device,
503 		virtio_device_register_device,
504 		virtio_device_init_device,
505 		virtio_device_uninit_device,
506 		virtio_device_register_child_devices,
507 		NULL,	// rescan
508 		NULL,	// device removed
509 	},
510 	virtio_device_negotiate_features,
511 	virtio_device_clear_feature,
512 	virtio_device_read_device_config,
513 	virtio_device_write_device_config,
514 	virtio_device_alloc_queues,
515 	virtio_device_free_queues,
516 	virtio_device_setup_interrupt,
517 	virtio_device_free_interrupts,
518 	virtio_device_queue_setup_interrupt,
519 	virtio_device_queue_request,
520 	virtio_device_queue_request_v,
521 	virtio_device_queue_is_full,
522 	virtio_device_queue_is_empty,
523 	virtio_device_queue_size,
524 	virtio_device_queue_dequeue,
525 };
526 
527 
528 module_info* modules[] = {
529 	(module_info* )&sVirtioDevice,
530 	NULL
531 };
532