]>
Commit | Line | Data |
---|---|---|
9952f691 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
4ba04bec SZ |
2 | /* |
3 | * Copyright (c) 2015, Linaro Limited, Shannon Zhao | |
4ba04bec SZ |
4 | */ |
5 | ||
6 | #include <linux/platform_device.h> | |
7 | #include <linux/acpi.h> | |
8 | #include <xen/xen.h> | |
9 | #include <xen/page.h> | |
10 | #include <xen/interface/memory.h> | |
11 | #include <asm/xen/hypervisor.h> | |
12 | #include <asm/xen/hypercall.h> | |
13 | ||
14 | static int xen_unmap_device_mmio(const struct resource *resources, | |
15 | unsigned int count) | |
16 | { | |
17 | unsigned int i, j, nr; | |
18 | int rc = 0; | |
19 | const struct resource *r; | |
20 | struct xen_remove_from_physmap xrp; | |
21 | ||
22 | for (i = 0; i < count; i++) { | |
23 | r = &resources[i]; | |
24 | nr = DIV_ROUND_UP(resource_size(r), XEN_PAGE_SIZE); | |
25 | if ((resource_type(r) != IORESOURCE_MEM) || (nr == 0)) | |
26 | continue; | |
27 | ||
28 | for (j = 0; j < nr; j++) { | |
29 | xrp.domid = DOMID_SELF; | |
30 | xrp.gpfn = XEN_PFN_DOWN(r->start) + j; | |
31 | rc = HYPERVISOR_memory_op(XENMEM_remove_from_physmap, | |
32 | &xrp); | |
33 | if (rc) | |
34 | return rc; | |
35 | } | |
36 | } | |
37 | ||
38 | return rc; | |
39 | } | |
40 | ||
41 | static int xen_map_device_mmio(const struct resource *resources, | |
42 | unsigned int count) | |
43 | { | |
44 | unsigned int i, j, nr; | |
45 | int rc = 0; | |
46 | const struct resource *r; | |
47 | xen_pfn_t *gpfns; | |
48 | xen_ulong_t *idxs; | |
49 | int *errs; | |
4ba04bec SZ |
50 | |
51 | for (i = 0; i < count; i++) { | |
0b47a6bd JA |
52 | struct xen_add_to_physmap_range xatp = { |
53 | .domid = DOMID_SELF, | |
54 | .space = XENMAPSPACE_dev_mmio | |
55 | }; | |
56 | ||
4ba04bec SZ |
57 | r = &resources[i]; |
58 | nr = DIV_ROUND_UP(resource_size(r), XEN_PAGE_SIZE); | |
59 | if ((resource_type(r) != IORESOURCE_MEM) || (nr == 0)) | |
60 | continue; | |
61 | ||
6396bb22 KC |
62 | gpfns = kcalloc(nr, sizeof(xen_pfn_t), GFP_KERNEL); |
63 | idxs = kcalloc(nr, sizeof(xen_ulong_t), GFP_KERNEL); | |
64 | errs = kcalloc(nr, sizeof(int), GFP_KERNEL); | |
4ba04bec SZ |
65 | if (!gpfns || !idxs || !errs) { |
66 | kfree(gpfns); | |
67 | kfree(idxs); | |
68 | kfree(errs); | |
69 | rc = -ENOMEM; | |
70 | goto unmap; | |
71 | } | |
72 | ||
73 | for (j = 0; j < nr; j++) { | |
74 | /* | |
75 | * The regions are always mapped 1:1 to DOM0 and this is | |
76 | * fine because the memory map for DOM0 is the same as | |
77 | * the host (except for the RAM). | |
78 | */ | |
79 | gpfns[j] = XEN_PFN_DOWN(r->start) + j; | |
80 | idxs[j] = XEN_PFN_DOWN(r->start) + j; | |
81 | } | |
82 | ||
4ba04bec | 83 | xatp.size = nr; |
4ba04bec SZ |
84 | |
85 | set_xen_guest_handle(xatp.gpfns, gpfns); | |
86 | set_xen_guest_handle(xatp.idxs, idxs); | |
87 | set_xen_guest_handle(xatp.errs, errs); | |
88 | ||
89 | rc = HYPERVISOR_memory_op(XENMEM_add_to_physmap_range, &xatp); | |
90 | kfree(gpfns); | |
91 | kfree(idxs); | |
92 | kfree(errs); | |
93 | if (rc) | |
94 | goto unmap; | |
95 | } | |
96 | ||
97 | return rc; | |
98 | ||
99 | unmap: | |
100 | xen_unmap_device_mmio(resources, i); | |
101 | return rc; | |
102 | } | |
103 | ||
104 | static int xen_platform_notifier(struct notifier_block *nb, | |
105 | unsigned long action, void *data) | |
106 | { | |
107 | struct platform_device *pdev = to_platform_device(data); | |
108 | int r = 0; | |
109 | ||
110 | if (pdev->num_resources == 0 || pdev->resource == NULL) | |
111 | return NOTIFY_OK; | |
112 | ||
113 | switch (action) { | |
114 | case BUS_NOTIFY_ADD_DEVICE: | |
115 | r = xen_map_device_mmio(pdev->resource, pdev->num_resources); | |
116 | break; | |
117 | case BUS_NOTIFY_DEL_DEVICE: | |
118 | r = xen_unmap_device_mmio(pdev->resource, pdev->num_resources); | |
119 | break; | |
120 | default: | |
121 | return NOTIFY_DONE; | |
122 | } | |
123 | if (r) | |
124 | dev_err(&pdev->dev, "Platform: Failed to %s device %s MMIO!\n", | |
125 | action == BUS_NOTIFY_ADD_DEVICE ? "map" : | |
126 | (action == BUS_NOTIFY_DEL_DEVICE ? "unmap" : "?"), | |
127 | pdev->name); | |
128 | ||
129 | return NOTIFY_OK; | |
130 | } | |
131 | ||
132 | static struct notifier_block platform_device_nb = { | |
133 | .notifier_call = xen_platform_notifier, | |
134 | }; | |
135 | ||
136 | static int __init register_xen_platform_notifier(void) | |
137 | { | |
138 | if (!xen_initial_domain() || acpi_disabled) | |
139 | return 0; | |
140 | ||
141 | return bus_register_notifier(&platform_bus_type, &platform_device_nb); | |
142 | } | |
143 | ||
144 | arch_initcall(register_xen_platform_notifier); | |
5789afeb SZ |
145 | |
146 | #ifdef CONFIG_ARM_AMBA | |
147 | #include <linux/amba/bus.h> | |
148 | ||
149 | static int xen_amba_notifier(struct notifier_block *nb, | |
150 | unsigned long action, void *data) | |
151 | { | |
152 | struct amba_device *adev = to_amba_device(data); | |
153 | int r = 0; | |
154 | ||
155 | switch (action) { | |
156 | case BUS_NOTIFY_ADD_DEVICE: | |
157 | r = xen_map_device_mmio(&adev->res, 1); | |
158 | break; | |
159 | case BUS_NOTIFY_DEL_DEVICE: | |
160 | r = xen_unmap_device_mmio(&adev->res, 1); | |
161 | break; | |
162 | default: | |
163 | return NOTIFY_DONE; | |
164 | } | |
165 | if (r) | |
166 | dev_err(&adev->dev, "AMBA: Failed to %s device %s MMIO!\n", | |
167 | action == BUS_NOTIFY_ADD_DEVICE ? "map" : | |
168 | (action == BUS_NOTIFY_DEL_DEVICE ? "unmap" : "?"), | |
169 | adev->dev.init_name); | |
170 | ||
171 | return NOTIFY_OK; | |
172 | } | |
173 | ||
174 | static struct notifier_block amba_device_nb = { | |
175 | .notifier_call = xen_amba_notifier, | |
176 | }; | |
177 | ||
178 | static int __init register_xen_amba_notifier(void) | |
179 | { | |
180 | if (!xen_initial_domain() || acpi_disabled) | |
181 | return 0; | |
182 | ||
183 | return bus_register_notifier(&amba_bustype, &amba_device_nb); | |
184 | } | |
185 | ||
186 | arch_initcall(register_xen_amba_notifier); | |
187 | #endif |