diff options
| author | Shannon Zhao <shannon.zhao@linaro.org> | 2016-04-07 08:03:23 -0400 |
|---|---|---|
| committer | David Vrabel <david.vrabel@citrix.com> | 2016-07-06 05:34:43 -0400 |
| commit | 4ba04bec3755b765bb10b21943afbee60c33288d (patch) | |
| tree | 52f4c223a386479aadad3c749dddd33f8db08d19 /drivers/xen | |
| parent | 712a5b77cb5a1d3028a8c202f4f839802a03f19f (diff) | |
Xen: ARM: Add support for mapping platform device mmio
Add a bus_notifier for platform bus device in order to map the device
mmio regions when DOM0 booting with ACPI.
Signed-off-by: Shannon Zhao <shannon.zhao@linaro.org>
Acked-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
Tested-by: Julien Grall <julien.grall@arm.com>
Diffstat (limited to 'drivers/xen')
| -rw-r--r-- | drivers/xen/Makefile | 1 | ||||
| -rw-r--r-- | drivers/xen/arm-device.c | 153 |
2 files changed, 154 insertions, 0 deletions
diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile index 030e91b38e32..8feab810aed9 100644 --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile | |||
| @@ -10,6 +10,7 @@ CFLAGS_features.o := $(nostackp) | |||
| 10 | CFLAGS_efi.o += -fshort-wchar | 10 | CFLAGS_efi.o += -fshort-wchar |
| 11 | LDFLAGS += $(call ld-option, --no-wchar-size-warning) | 11 | LDFLAGS += $(call ld-option, --no-wchar-size-warning) |
| 12 | 12 | ||
| 13 | dom0-$(CONFIG_ARM64) += arm-device.o | ||
| 13 | dom0-$(CONFIG_PCI) += pci.o | 14 | dom0-$(CONFIG_PCI) += pci.o |
| 14 | dom0-$(CONFIG_USB_SUPPORT) += dbgp.o | 15 | dom0-$(CONFIG_USB_SUPPORT) += dbgp.o |
| 15 | dom0-$(CONFIG_XEN_ACPI) += acpi.o $(xen-pad-y) | 16 | dom0-$(CONFIG_XEN_ACPI) += acpi.o $(xen-pad-y) |
diff --git a/drivers/xen/arm-device.c b/drivers/xen/arm-device.c new file mode 100644 index 000000000000..b918e8ed5084 --- /dev/null +++ b/drivers/xen/arm-device.c | |||
| @@ -0,0 +1,153 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2015, Linaro Limited, Shannon Zhao | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms and conditions of the GNU General Public License, | ||
| 6 | * version 2, as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License | ||
| 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/platform_device.h> | ||
| 18 | #include <linux/acpi.h> | ||
| 19 | #include <xen/xen.h> | ||
| 20 | #include <xen/page.h> | ||
| 21 | #include <xen/interface/memory.h> | ||
| 22 | #include <asm/xen/hypervisor.h> | ||
| 23 | #include <asm/xen/hypercall.h> | ||
| 24 | |||
| 25 | static int xen_unmap_device_mmio(const struct resource *resources, | ||
| 26 | unsigned int count) | ||
| 27 | { | ||
| 28 | unsigned int i, j, nr; | ||
| 29 | int rc = 0; | ||
| 30 | const struct resource *r; | ||
| 31 | struct xen_remove_from_physmap xrp; | ||
| 32 | |||
| 33 | for (i = 0; i < count; i++) { | ||
| 34 | r = &resources[i]; | ||
| 35 | nr = DIV_ROUND_UP(resource_size(r), XEN_PAGE_SIZE); | ||
| 36 | if ((resource_type(r) != IORESOURCE_MEM) || (nr == 0)) | ||
| 37 | continue; | ||
| 38 | |||
| 39 | for (j = 0; j < nr; j++) { | ||
| 40 | xrp.domid = DOMID_SELF; | ||
| 41 | xrp.gpfn = XEN_PFN_DOWN(r->start) + j; | ||
| 42 | rc = HYPERVISOR_memory_op(XENMEM_remove_from_physmap, | ||
| 43 | &xrp); | ||
| 44 | if (rc) | ||
| 45 | return rc; | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | return rc; | ||
| 50 | } | ||
| 51 | |||
| 52 | static int xen_map_device_mmio(const struct resource *resources, | ||
| 53 | unsigned int count) | ||
| 54 | { | ||
| 55 | unsigned int i, j, nr; | ||
| 56 | int rc = 0; | ||
| 57 | const struct resource *r; | ||
| 58 | xen_pfn_t *gpfns; | ||
| 59 | xen_ulong_t *idxs; | ||
| 60 | int *errs; | ||
| 61 | struct xen_add_to_physmap_range xatp; | ||
| 62 | |||
| 63 | for (i = 0; i < count; i++) { | ||
| 64 | r = &resources[i]; | ||
| 65 | nr = DIV_ROUND_UP(resource_size(r), XEN_PAGE_SIZE); | ||
| 66 | if ((resource_type(r) != IORESOURCE_MEM) || (nr == 0)) | ||
| 67 | continue; | ||
| 68 | |||
| 69 | gpfns = kzalloc(sizeof(xen_pfn_t) * nr, GFP_KERNEL); | ||
| 70 | idxs = kzalloc(sizeof(xen_ulong_t) * nr, GFP_KERNEL); | ||
| 71 | errs = kzalloc(sizeof(int) * nr, GFP_KERNEL); | ||
| 72 | if (!gpfns || !idxs || !errs) { | ||
| 73 | kfree(gpfns); | ||
| 74 | kfree(idxs); | ||
| 75 | kfree(errs); | ||
| 76 | rc = -ENOMEM; | ||
| 77 | goto unmap; | ||
| 78 | } | ||
| 79 | |||
| 80 | for (j = 0; j < nr; j++) { | ||
| 81 | /* | ||
| 82 | * The regions are always mapped 1:1 to DOM0 and this is | ||
| 83 | * fine because the memory map for DOM0 is the same as | ||
| 84 | * the host (except for the RAM). | ||
| 85 | */ | ||
| 86 | gpfns[j] = XEN_PFN_DOWN(r->start) + j; | ||
| 87 | idxs[j] = XEN_PFN_DOWN(r->start) + j; | ||
| 88 | } | ||
| 89 | |||
| 90 | xatp.domid = DOMID_SELF; | ||
| 91 | xatp.size = nr; | ||
| 92 | xatp.space = XENMAPSPACE_dev_mmio; | ||
| 93 | |||
| 94 | set_xen_guest_handle(xatp.gpfns, gpfns); | ||
| 95 | set_xen_guest_handle(xatp.idxs, idxs); | ||
| 96 | set_xen_guest_handle(xatp.errs, errs); | ||
| 97 | |||
| 98 | rc = HYPERVISOR_memory_op(XENMEM_add_to_physmap_range, &xatp); | ||
| 99 | kfree(gpfns); | ||
| 100 | kfree(idxs); | ||
| 101 | kfree(errs); | ||
| 102 | if (rc) | ||
| 103 | goto unmap; | ||
| 104 | } | ||
| 105 | |||
| 106 | return rc; | ||
| 107 | |||
| 108 | unmap: | ||
| 109 | xen_unmap_device_mmio(resources, i); | ||
| 110 | return rc; | ||
| 111 | } | ||
| 112 | |||
| 113 | static int xen_platform_notifier(struct notifier_block *nb, | ||
| 114 | unsigned long action, void *data) | ||
| 115 | { | ||
| 116 | struct platform_device *pdev = to_platform_device(data); | ||
| 117 | int r = 0; | ||
| 118 | |||
| 119 | if (pdev->num_resources == 0 || pdev->resource == NULL) | ||
| 120 | return NOTIFY_OK; | ||
| 121 | |||
| 122 | switch (action) { | ||
| 123 | case BUS_NOTIFY_ADD_DEVICE: | ||
| 124 | r = xen_map_device_mmio(pdev->resource, pdev->num_resources); | ||
| 125 | break; | ||
| 126 | case BUS_NOTIFY_DEL_DEVICE: | ||
| 127 | r = xen_unmap_device_mmio(pdev->resource, pdev->num_resources); | ||
| 128 | break; | ||
| 129 | default: | ||
| 130 | return NOTIFY_DONE; | ||
| 131 | } | ||
| 132 | if (r) | ||
| 133 | dev_err(&pdev->dev, "Platform: Failed to %s device %s MMIO!\n", | ||
| 134 | action == BUS_NOTIFY_ADD_DEVICE ? "map" : | ||
| 135 | (action == BUS_NOTIFY_DEL_DEVICE ? "unmap" : "?"), | ||
| 136 | pdev->name); | ||
| 137 | |||
| 138 | return NOTIFY_OK; | ||
| 139 | } | ||
| 140 | |||
| 141 | static struct notifier_block platform_device_nb = { | ||
| 142 | .notifier_call = xen_platform_notifier, | ||
| 143 | }; | ||
| 144 | |||
| 145 | static int __init register_xen_platform_notifier(void) | ||
| 146 | { | ||
| 147 | if (!xen_initial_domain() || acpi_disabled) | ||
| 148 | return 0; | ||
| 149 | |||
| 150 | return bus_register_notifier(&platform_bus_type, &platform_device_nb); | ||
| 151 | } | ||
| 152 | |||
| 153 | arch_initcall(register_xen_platform_notifier); | ||
