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