Commit 81a054ce0b469b66c88e9da6403082759c64bf73
Committed by
Rusty Russell
1 parent
f65ca1dc6a
Exists in
master
and in
20 other branches
virtio-mmio: Devices parameter parsing
This patch adds an option to instantiate guest virtio-mmio devices basing on a kernel command line (or module) parameter, for example: virtio_mmio.devices=0x100@0x100b0000:48 Signed-off-by: Pawel Moll <pawel.moll@arm.com> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Showing 3 changed files with 191 additions and 0 deletions Side-by-side Diff
Documentation/kernel-parameters.txt
... | ... | @@ -110,6 +110,7 @@ |
110 | 110 | USB USB support is enabled. |
111 | 111 | USBHID USB Human Interface Device support is enabled. |
112 | 112 | V4L Video For Linux support is enabled. |
113 | + VMMIO Driver for memory mapped virtio devices is enabled. | |
113 | 114 | VGA The VGA console has been enabled. |
114 | 115 | VT Virtual terminal support is enabled. |
115 | 116 | WDT Watchdog support is enabled. |
... | ... | @@ -2846,6 +2847,22 @@ |
2846 | 2847 | |
2847 | 2848 | video= [FB] Frame buffer configuration |
2848 | 2849 | See Documentation/fb/modedb.txt. |
2850 | + | |
2851 | + virtio_mmio.device= | |
2852 | + [VMMIO] Memory mapped virtio (platform) device. | |
2853 | + | |
2854 | + <size>@<baseaddr>:<irq>[:<id>] | |
2855 | + where: | |
2856 | + <size> := size (can use standard suffixes | |
2857 | + like K, M and G) | |
2858 | + <baseaddr> := physical base address | |
2859 | + <irq> := interrupt number (as passed to | |
2860 | + request_irq()) | |
2861 | + <id> := (optional) platform device id | |
2862 | + example: | |
2863 | + virtio_mmio.device=1K@0x100b0000:48:7 | |
2864 | + | |
2865 | + Can be used multiple times for multiple devices. | |
2849 | 2866 | |
2850 | 2867 | vga= [BOOT,X86-32] Select a particular video mode |
2851 | 2868 | See Documentation/x86/boot.txt and |
drivers/virtio/Kconfig
... | ... | @@ -46,5 +46,16 @@ |
46 | 46 | |
47 | 47 | If unsure, say N. |
48 | 48 | |
49 | +config VIRTIO_MMIO_CMDLINE_DEVICES | |
50 | + bool "Memory mapped virtio devices parameter parsing" | |
51 | + depends on VIRTIO_MMIO | |
52 | + ---help--- | |
53 | + Allow virtio-mmio devices instantiation via the kernel command line | |
54 | + or module parameters. Be aware that using incorrect parameters (base | |
55 | + address in particular) can crash your system - you have been warned. | |
56 | + See Documentation/kernel-parameters.txt for details. | |
57 | + | |
58 | + If unsure, say 'N'. | |
59 | + | |
49 | 60 | endmenu |
drivers/virtio/virtio_mmio.c
... | ... | @@ -6,6 +6,50 @@ |
6 | 6 | * This module allows virtio devices to be used over a virtual, memory mapped |
7 | 7 | * platform device. |
8 | 8 | * |
9 | + * The guest device(s) may be instantiated in one of three equivalent ways: | |
10 | + * | |
11 | + * 1. Static platform device in board's code, eg.: | |
12 | + * | |
13 | + * static struct platform_device v2m_virtio_device = { | |
14 | + * .name = "virtio-mmio", | |
15 | + * .id = -1, | |
16 | + * .num_resources = 2, | |
17 | + * .resource = (struct resource []) { | |
18 | + * { | |
19 | + * .start = 0x1001e000, | |
20 | + * .end = 0x1001e0ff, | |
21 | + * .flags = IORESOURCE_MEM, | |
22 | + * }, { | |
23 | + * .start = 42 + 32, | |
24 | + * .end = 42 + 32, | |
25 | + * .flags = IORESOURCE_IRQ, | |
26 | + * }, | |
27 | + * } | |
28 | + * }; | |
29 | + * | |
30 | + * 2. Device Tree node, eg.: | |
31 | + * | |
32 | + * virtio_block@1e000 { | |
33 | + * compatible = "virtio,mmio"; | |
34 | + * reg = <0x1e000 0x100>; | |
35 | + * interrupts = <42>; | |
36 | + * } | |
37 | + * | |
38 | + * 3. Kernel module (or command line) parameter. Can be used more than once - | |
39 | + * one device will be created for each one. Syntax: | |
40 | + * | |
41 | + * [virtio_mmio.]device=<size>@<baseaddr>:<irq>[:<id>] | |
42 | + * where: | |
43 | + * <size> := size (can use standard suffixes like K, M or G) | |
44 | + * <baseaddr> := physical base address | |
45 | + * <irq> := interrupt number (as passed to request_irq()) | |
46 | + * <id> := (optional) platform device id | |
47 | + * eg.: | |
48 | + * virtio_mmio.device=0x100@0x100b0000:48 \ | |
49 | + * virtio_mmio.device=1K@0x1001e000:74 | |
50 | + * | |
51 | + * | |
52 | + * | |
9 | 53 | * Registers layout (all 32-bit wide): |
10 | 54 | * |
11 | 55 | * offset d. name description |
... | ... | @@ -42,6 +86,8 @@ |
42 | 86 | * See the COPYING file in the top-level directory. |
43 | 87 | */ |
44 | 88 | |
89 | +#define pr_fmt(fmt) "virtio-mmio: " fmt | |
90 | + | |
45 | 91 | #include <linux/highmem.h> |
46 | 92 | #include <linux/interrupt.h> |
47 | 93 | #include <linux/io.h> |
... | ... | @@ -449,6 +495,122 @@ |
449 | 495 | |
450 | 496 | |
451 | 497 | |
498 | +/* Devices list parameter */ | |
499 | + | |
500 | +#if defined(CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES) | |
501 | + | |
502 | +static struct device vm_cmdline_parent = { | |
503 | + .init_name = "virtio-mmio-cmdline", | |
504 | +}; | |
505 | + | |
506 | +static int vm_cmdline_parent_registered; | |
507 | +static int vm_cmdline_id; | |
508 | + | |
509 | +static int vm_cmdline_set(const char *device, | |
510 | + const struct kernel_param *kp) | |
511 | +{ | |
512 | + int err; | |
513 | + struct resource resources[2] = {}; | |
514 | + char *str; | |
515 | + long long int base; | |
516 | + int processed, consumed = 0; | |
517 | + struct platform_device *pdev; | |
518 | + | |
519 | + resources[0].flags = IORESOURCE_MEM; | |
520 | + resources[1].flags = IORESOURCE_IRQ; | |
521 | + | |
522 | + resources[0].end = memparse(device, &str) - 1; | |
523 | + | |
524 | + processed = sscanf(str, "@%lli:%u%n:%d%n", | |
525 | + &base, &resources[1].start, &consumed, | |
526 | + &vm_cmdline_id, &consumed); | |
527 | + | |
528 | + if (processed < 2 || processed > 3 || str[consumed]) | |
529 | + return -EINVAL; | |
530 | + | |
531 | + resources[0].start = base; | |
532 | + resources[0].end += base; | |
533 | + resources[1].end = resources[1].start; | |
534 | + | |
535 | + if (!vm_cmdline_parent_registered) { | |
536 | + err = device_register(&vm_cmdline_parent); | |
537 | + if (err) { | |
538 | + pr_err("Failed to register parent device!\n"); | |
539 | + return err; | |
540 | + } | |
541 | + vm_cmdline_parent_registered = 1; | |
542 | + } | |
543 | + | |
544 | + pr_info("Registering device virtio-mmio.%d at 0x%llx-0x%llx, IRQ %d.\n", | |
545 | + vm_cmdline_id, | |
546 | + (unsigned long long)resources[0].start, | |
547 | + (unsigned long long)resources[0].end, | |
548 | + (int)resources[1].start); | |
549 | + | |
550 | + pdev = platform_device_register_resndata(&vm_cmdline_parent, | |
551 | + "virtio-mmio", vm_cmdline_id++, | |
552 | + resources, ARRAY_SIZE(resources), NULL, 0); | |
553 | + if (IS_ERR(pdev)) | |
554 | + return PTR_ERR(pdev); | |
555 | + | |
556 | + return 0; | |
557 | +} | |
558 | + | |
559 | +static int vm_cmdline_get_device(struct device *dev, void *data) | |
560 | +{ | |
561 | + char *buffer = data; | |
562 | + unsigned int len = strlen(buffer); | |
563 | + struct platform_device *pdev = to_platform_device(dev); | |
564 | + | |
565 | + snprintf(buffer + len, PAGE_SIZE - len, "0x%llx@0x%llx:%llu:%d\n", | |
566 | + pdev->resource[0].end - pdev->resource[0].start + 1ULL, | |
567 | + (unsigned long long)pdev->resource[0].start, | |
568 | + (unsigned long long)pdev->resource[1].start, | |
569 | + pdev->id); | |
570 | + return 0; | |
571 | +} | |
572 | + | |
573 | +static int vm_cmdline_get(char *buffer, const struct kernel_param *kp) | |
574 | +{ | |
575 | + buffer[0] = '\0'; | |
576 | + device_for_each_child(&vm_cmdline_parent, buffer, | |
577 | + vm_cmdline_get_device); | |
578 | + return strlen(buffer) + 1; | |
579 | +} | |
580 | + | |
581 | +static struct kernel_param_ops vm_cmdline_param_ops = { | |
582 | + .set = vm_cmdline_set, | |
583 | + .get = vm_cmdline_get, | |
584 | +}; | |
585 | + | |
586 | +device_param_cb(device, &vm_cmdline_param_ops, NULL, S_IRUSR); | |
587 | + | |
588 | +static int vm_unregister_cmdline_device(struct device *dev, | |
589 | + void *data) | |
590 | +{ | |
591 | + platform_device_unregister(to_platform_device(dev)); | |
592 | + | |
593 | + return 0; | |
594 | +} | |
595 | + | |
596 | +static void vm_unregister_cmdline_devices(void) | |
597 | +{ | |
598 | + if (vm_cmdline_parent_registered) { | |
599 | + device_for_each_child(&vm_cmdline_parent, NULL, | |
600 | + vm_unregister_cmdline_device); | |
601 | + device_unregister(&vm_cmdline_parent); | |
602 | + vm_cmdline_parent_registered = 0; | |
603 | + } | |
604 | +} | |
605 | + | |
606 | +#else | |
607 | + | |
608 | +static void vm_unregister_cmdline_devices(void) | |
609 | +{ | |
610 | +} | |
611 | + | |
612 | +#endif | |
613 | + | |
452 | 614 | /* Platform driver */ |
453 | 615 | |
454 | 616 | static struct of_device_id virtio_mmio_match[] = { |
... | ... | @@ -475,6 +637,7 @@ |
475 | 637 | static void __exit virtio_mmio_exit(void) |
476 | 638 | { |
477 | 639 | platform_driver_unregister(&virtio_mmio_driver); |
640 | + vm_unregister_cmdline_devices(); | |
478 | 641 | } |
479 | 642 | |
480 | 643 | module_init(virtio_mmio_init); |