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 | |
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>
-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); | ||