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 *
virtio_get_feature_name(uint64 feature)36 virtio_get_feature_name(uint64 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
virtio_dump_features(const char * title,uint64 features,const char * (* get_feature_name)(uint64))55 virtio_dump_features(const char* title, uint64 features,
56 const char* (*get_feature_name)(uint64))
57 {
58 char features_string[512] = "";
59 for (uint64 i = 0; i < 32; i++) {
60 uint64 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
virtio_device_supports_device(device_node * parent)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
virtio_crs_find_address(acpi_resource * res,void * context)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
virtio_crs_find_interrupt(acpi_resource * res,void * context)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
virtio_device_register_device(device_node * parent)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, ®s, ®sLen)) {
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, {.ui16 = (uint16)mappedRegs->deviceId} },
237 { "virtio/vendor_id", B_UINT32_TYPE, {.ui32 = mappedRegs->vendorId} },
238 { NULL }
239 };
240
241 return gDeviceManager->register_node(parent, VIRTIO_MMIO_DEVICE_MODULE_NAME,
242 attrs, NULL, NULL);
243 }
244
245
246 static status_t
virtio_device_init_device(device_node * node,void ** cookie)247 virtio_device_init_device(device_node* node, void** cookie)
248 {
249 TRACE("init_device(%p)\n", node);
250
251 DeviceNodePutter<&gDeviceManager>
252 parent(gDeviceManager->get_parent_node(node));
253
254 const char* bus;
255
256 status_t status = gDeviceManager->get_attr_string(parent.Get(), B_DEVICE_BUS, &bus, false);
257
258 if (status < B_OK)
259 return -1.0f;
260
261 uint64 regs = 0;
262 uint64 regsLen = 0;
263 uint64 interrupt = 0;
264
265 // initialize virtio device from FDT
266 if (strcmp(bus, "fdt") == 0) {
267 fdt_device_module_info *parentModule;
268 fdt_device* parentDev;
269 if (gDeviceManager->get_driver(parent.Get(),
270 (driver_module_info**)&parentModule, (void**)&parentDev))
271 panic("can't get parent node driver");
272
273 TRACE(" bus: %p\n", parentModule->get_bus(parentDev));
274 TRACE(" compatible: %s\n", (const char*)parentModule->get_prop(parentDev,
275 "compatible", NULL));
276
277 for (uint32 i = 0; parentModule->get_reg(parentDev, i, ®s, ®sLen);
278 i++) {
279 TRACE(" reg[%" B_PRIu32 "]: (0x%" B_PRIx64 ", 0x%" B_PRIx64 ")\n",
280 i, regs, regsLen);
281 }
282
283 device_node* interruptController;
284 for (uint32 i = 0; parentModule->get_interrupt(parentDev,
285 i, &interruptController, &interrupt); i++) {
286
287 const char* name;
288 if (interruptController == NULL
289 || gDeviceManager->get_attr_string(interruptController, "fdt/name",
290 &name, false) < B_OK) {
291 name = NULL;
292 }
293
294 TRACE(" interrupt[%" B_PRIu32 "]: ('%s', 0x%" B_PRIx64 ")\n", i,
295 name, interrupt);
296 }
297
298 if (!parentModule->get_reg(parentDev, 0, ®s, ®sLen)) {
299 TRACE(" no regs\n");
300 return B_ERROR;
301 }
302
303 if (!parentModule->get_interrupt(parentDev, 0, &interruptController,
304 &interrupt)) {
305 TRACE(" no interrupts\n");
306 return B_ERROR;
307 }
308 }
309
310 // initialize virtio device from ACPI
311 if (strcmp(bus, "acpi") == 0) {
312 acpi_device_module_info *parentModule;
313 acpi_device parentDev;
314 if (gDeviceManager->get_driver(parent.Get(), (driver_module_info**)&parentModule,
315 (void**)&parentDev)) {
316 ERROR("can't get parent node driver");
317 return B_ERROR;
318 }
319
320 virtio_memory_range range = { 0, 0 };
321 parentModule->walk_resources(parentDev, (char *)"_CRS",
322 virtio_crs_find_address, &range);
323 regs = range.base;
324 regsLen = range.length;
325
326 parentModule->walk_resources(parentDev, (char *)"_CRS",
327 virtio_crs_find_interrupt, &interrupt);
328
329 TRACE(" regs: (0x%" B_PRIx64 ", 0x%" B_PRIx64 ")\n",
330 regs, regsLen);
331 TRACE(" interrupt: 0x%" B_PRIx64 "\n",
332 interrupt);
333 }
334
335 ObjectDeleter<VirtioDevice> dev(new(std::nothrow) VirtioDevice());
336 if (!dev.IsSet())
337 return B_NO_MEMORY;
338
339 status_t res = dev->Init(regs, regsLen, interrupt, 1);
340 if (res < B_OK)
341 return res;
342
343 *cookie = dev.Detach();
344 return B_OK;
345 }
346
347
348 static void
virtio_device_uninit_device(void * cookie)349 virtio_device_uninit_device(void* cookie)
350 {
351 TRACE("uninit_device(%p)\n", cookie);
352 ObjectDeleter<VirtioDevice> dev((VirtioDevice*)cookie);
353 }
354
355
356 static status_t
virtio_device_register_child_devices(void * cookie)357 virtio_device_register_child_devices(void* cookie)
358 {
359 TRACE("register_child_devices(%p)\n", cookie);
360 return B_OK;
361 }
362
363
364 //#pragma mark driver API
365
366
367 static status_t
virtio_device_negotiate_features(virtio_device cookie,uint64 supported,uint64 * negotiated,const char * (* get_feature_name)(uint64))368 virtio_device_negotiate_features(virtio_device cookie, uint64 supported,
369 uint64* negotiated, const char* (*get_feature_name)(uint64))
370 {
371 TRACE("virtio_device_negotiate_features(%p)\n", cookie);
372 VirtioDevice* dev = (VirtioDevice*)cookie;
373
374 dev->fRegs->status |= kVirtioConfigSAcknowledge;
375 dev->fRegs->status |= kVirtioConfigSDriver;
376
377 uint64 features = dev->fRegs->deviceFeatures;
378 virtio_dump_features("read features", features, get_feature_name);
379 features &= supported;
380
381 // filter our own features
382 features &= (VIRTIO_FEATURE_TRANSPORT_MASK
383 | VIRTIO_FEATURE_RING_INDIRECT_DESC | VIRTIO_FEATURE_RING_EVENT_IDX);
384 *negotiated = features;
385
386 virtio_dump_features("negotiated features", features, get_feature_name);
387
388 dev->fRegs->driverFeatures = features;
389 dev->fRegs->status |= kVirtioConfigSFeaturesOk;
390 dev->fRegs->status |= kVirtioConfigSDriverOk;
391 dev->fRegs->guestPageSize = B_PAGE_SIZE;
392
393 return B_OK;
394 }
395
396
397 static status_t
virtio_device_clear_feature(virtio_device cookie,uint64 feature)398 virtio_device_clear_feature(virtio_device cookie, uint64 feature)
399 {
400 panic("not implemented");
401 return B_ERROR;
402 }
403
404
405 static status_t
virtio_device_read_device_config(virtio_device cookie,uint8 offset,void * buffer,size_t bufferSize)406 virtio_device_read_device_config(virtio_device cookie, uint8 offset,
407 void* buffer, size_t bufferSize)
408 {
409 TRACE("virtio_device_read_device_config(%p, %d, %" B_PRIuSIZE ")\n", cookie,
410 offset, bufferSize);
411 VirtioDevice* dev = (VirtioDevice*)cookie;
412
413 // TODO: check ARM support, ARM seems support only 32 bit aligned MMIO access.
414 vuint8* src = &dev->fRegs->config[offset];
415 uint8* dst = (uint8*)buffer;
416 while (bufferSize > 0) {
417 uint8 size = 4;
418 if (bufferSize == 1) {
419 size = 1;
420 *dst = *src;
421 } else if (bufferSize <= 3) {
422 size = 2;
423 *(uint16*)dst = *(vuint16*)src;
424 } else
425 *(uint32*)dst = *(vuint32*)src;
426
427 dst += size;
428 bufferSize -= size;
429 src += size;
430 }
431
432 return B_OK;
433 }
434
435
436 static status_t
virtio_device_write_device_config(virtio_device cookie,uint8 offset,const void * buffer,size_t bufferSize)437 virtio_device_write_device_config(virtio_device cookie, uint8 offset,
438 const void* buffer, size_t bufferSize)
439 {
440 TRACE("virtio_device_write_device_config(%p, %d, %" B_PRIuSIZE ")\n",
441 cookie, offset, bufferSize);
442 VirtioDevice* dev = (VirtioDevice*)cookie;
443
444 // See virtio_device_read_device_config
445 uint8* src = (uint8*)buffer;
446 vuint8* dst = &dev->fRegs->config[offset];
447 while (bufferSize > 0) {
448 uint8 size = 4;
449 if (bufferSize == 1) {
450 size = 1;
451 *dst = *src;
452 } else if (bufferSize <= 3) {
453 size = 2;
454 *(vuint16*)dst = *(uint16*)src;
455 } else
456 *(vuint32*)dst = *(uint32*)src;
457
458 dst += size;
459 bufferSize -= size;
460 src += size;
461 }
462
463 return B_OK;
464 }
465
466
467 static status_t
virtio_device_alloc_queues(virtio_device cookie,size_t count,virtio_queue * queues,uint16 * requestedSizes)468 virtio_device_alloc_queues(virtio_device cookie, size_t count,
469 virtio_queue* queues, uint16* requestedSizes)
470 {
471 TRACE("virtio_device_alloc_queues(%p, %" B_PRIuSIZE ")\n", cookie, count);
472 VirtioDevice* dev = (VirtioDevice*)cookie;
473
474 ArrayDeleter<ObjectDeleter<VirtioQueue> > newQueues(new(std::nothrow)
475 ObjectDeleter<VirtioQueue>[count]);
476
477 if (!newQueues.IsSet())
478 return B_NO_MEMORY;
479
480 for (size_t i = 0; i < count; i++) {
481 newQueues[i].SetTo(new(std::nothrow) VirtioQueue(dev, i));
482
483 if (!newQueues[i].IsSet())
484 return B_NO_MEMORY;
485
486 status_t res = newQueues[i]->Init(
487 requestedSizes != NULL ? requestedSizes[i] : 0);
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
virtio_device_free_queues(virtio_device cookie)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
virtio_device_setup_interrupt(virtio_device cookie,virtio_intr_func config_handler,void * driverCookie)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
virtio_device_free_interrupts(virtio_device cookie)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
virtio_device_queue_setup_interrupt(virtio_queue aQueue,virtio_callback_func handler,void * cookie)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
virtio_device_queue_request_v(virtio_queue aQueue,const physical_entry * vector,size_t readVectorCount,size_t writtenVectorCount,void * cookie)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
virtio_device_queue_request(virtio_queue aQueue,const physical_entry * readEntry,const physical_entry * writtenEntry,void * cookie)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
virtio_device_queue_is_full(virtio_queue queue)604 virtio_device_queue_is_full(virtio_queue queue)
605 {
606 panic("not implemented");
607 return false;
608 }
609
610
611 static bool
virtio_device_queue_is_empty(virtio_queue aQueue)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
virtio_device_queue_size(virtio_queue aQueue)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
virtio_device_queue_dequeue(virtio_queue aQueue,void ** _cookie,uint32 * _usedLength)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