root / hw / ppc / spapr_iommu.c @ 284d1c6b
History | View | Annotate | Download (7.4 kB)
1 | ad0ebb91 | David Gibson | /*
|
---|---|---|---|
2 | ad0ebb91 | David Gibson | * QEMU sPAPR IOMMU (TCE) code
|
3 | ad0ebb91 | David Gibson | *
|
4 | ad0ebb91 | David Gibson | * Copyright (c) 2010 David Gibson, IBM Corporation <dwg@au1.ibm.com>
|
5 | ad0ebb91 | David Gibson | *
|
6 | ad0ebb91 | David Gibson | * This library is free software; you can redistribute it and/or
|
7 | ad0ebb91 | David Gibson | * modify it under the terms of the GNU Lesser General Public
|
8 | ad0ebb91 | David Gibson | * License as published by the Free Software Foundation; either
|
9 | ad0ebb91 | David Gibson | * version 2 of the License, or (at your option) any later version.
|
10 | ad0ebb91 | David Gibson | *
|
11 | ad0ebb91 | David Gibson | * This library is distributed in the hope that it will be useful,
|
12 | ad0ebb91 | David Gibson | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 | ad0ebb91 | David Gibson | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14 | ad0ebb91 | David Gibson | * Lesser General Public License for more details.
|
15 | ad0ebb91 | David Gibson | *
|
16 | ad0ebb91 | David Gibson | * You should have received a copy of the GNU Lesser General Public
|
17 | ad0ebb91 | David Gibson | * License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
18 | ad0ebb91 | David Gibson | */
|
19 | 83c9f4ca | Paolo Bonzini | #include "hw/hw.h" |
20 | 9c17d615 | Paolo Bonzini | #include "sysemu/kvm.h" |
21 | 83c9f4ca | Paolo Bonzini | #include "hw/qdev.h" |
22 | ad0ebb91 | David Gibson | #include "kvm_ppc.h" |
23 | 9c17d615 | Paolo Bonzini | #include "sysemu/dma.h" |
24 | 022c62cb | Paolo Bonzini | #include "exec/address-spaces.h" |
25 | ad0ebb91 | David Gibson | |
26 | 0d09e41a | Paolo Bonzini | #include "hw/ppc/spapr.h" |
27 | ad0ebb91 | David Gibson | |
28 | ad0ebb91 | David Gibson | #include <libfdt.h> |
29 | ad0ebb91 | David Gibson | |
30 | ad0ebb91 | David Gibson | /* #define DEBUG_TCE */
|
31 | ad0ebb91 | David Gibson | |
32 | ad0ebb91 | David Gibson | enum sPAPRTCEAccess {
|
33 | ad0ebb91 | David Gibson | SPAPR_TCE_FAULT = 0,
|
34 | ad0ebb91 | David Gibson | SPAPR_TCE_RO = 1,
|
35 | ad0ebb91 | David Gibson | SPAPR_TCE_WO = 2,
|
36 | ad0ebb91 | David Gibson | SPAPR_TCE_RW = 3,
|
37 | ad0ebb91 | David Gibson | }; |
38 | ad0ebb91 | David Gibson | |
39 | ad0ebb91 | David Gibson | typedef struct sPAPRTCETable sPAPRTCETable; |
40 | ad0ebb91 | David Gibson | |
41 | ad0ebb91 | David Gibson | struct sPAPRTCETable {
|
42 | ad0ebb91 | David Gibson | DMAContext dma; |
43 | ad0ebb91 | David Gibson | uint32_t liobn; |
44 | ad0ebb91 | David Gibson | uint32_t window_size; |
45 | ad0ebb91 | David Gibson | sPAPRTCE *table; |
46 | 53724ee5 | David Gibson | bool bypass;
|
47 | ad0ebb91 | David Gibson | int fd;
|
48 | ad0ebb91 | David Gibson | QLIST_ENTRY(sPAPRTCETable) list; |
49 | ad0ebb91 | David Gibson | }; |
50 | ad0ebb91 | David Gibson | |
51 | ad0ebb91 | David Gibson | |
52 | ad0ebb91 | David Gibson | QLIST_HEAD(spapr_tce_tables, sPAPRTCETable) spapr_tce_tables; |
53 | ad0ebb91 | David Gibson | |
54 | ad0ebb91 | David Gibson | static sPAPRTCETable *spapr_tce_find_by_liobn(uint32_t liobn)
|
55 | ad0ebb91 | David Gibson | { |
56 | ad0ebb91 | David Gibson | sPAPRTCETable *tcet; |
57 | ad0ebb91 | David Gibson | |
58 | ad0ebb91 | David Gibson | QLIST_FOREACH(tcet, &spapr_tce_tables, list) { |
59 | ad0ebb91 | David Gibson | if (tcet->liobn == liobn) {
|
60 | ad0ebb91 | David Gibson | return tcet;
|
61 | ad0ebb91 | David Gibson | } |
62 | ad0ebb91 | David Gibson | } |
63 | ad0ebb91 | David Gibson | |
64 | ad0ebb91 | David Gibson | return NULL; |
65 | ad0ebb91 | David Gibson | } |
66 | ad0ebb91 | David Gibson | |
67 | ad0ebb91 | David Gibson | static int spapr_tce_translate(DMAContext *dma, |
68 | ad0ebb91 | David Gibson | dma_addr_t addr, |
69 | a8170e5e | Avi Kivity | hwaddr *paddr, |
70 | a8170e5e | Avi Kivity | hwaddr *len, |
71 | ad0ebb91 | David Gibson | DMADirection dir) |
72 | ad0ebb91 | David Gibson | { |
73 | ad0ebb91 | David Gibson | sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, dma); |
74 | ad0ebb91 | David Gibson | enum sPAPRTCEAccess access = (dir == DMA_DIRECTION_FROM_DEVICE)
|
75 | ad0ebb91 | David Gibson | ? SPAPR_TCE_WO : SPAPR_TCE_RO; |
76 | ad0ebb91 | David Gibson | uint64_t tce; |
77 | ad0ebb91 | David Gibson | |
78 | ad0ebb91 | David Gibson | #ifdef DEBUG_TCE
|
79 | ad0ebb91 | David Gibson | fprintf(stderr, "spapr_tce_translate liobn=0x%" PRIx32 " addr=0x" |
80 | ad0ebb91 | David Gibson | DMA_ADDR_FMT "\n", tcet->liobn, addr);
|
81 | ad0ebb91 | David Gibson | #endif
|
82 | ad0ebb91 | David Gibson | |
83 | 53724ee5 | David Gibson | if (tcet->bypass) {
|
84 | 53724ee5 | David Gibson | *paddr = addr; |
85 | a8170e5e | Avi Kivity | *len = (hwaddr)-1;
|
86 | 53724ee5 | David Gibson | return 0; |
87 | 53724ee5 | David Gibson | } |
88 | 53724ee5 | David Gibson | |
89 | ad0ebb91 | David Gibson | /* Check if we are in bound */
|
90 | ad0ebb91 | David Gibson | if (addr >= tcet->window_size) {
|
91 | ad0ebb91 | David Gibson | #ifdef DEBUG_TCE
|
92 | ad0ebb91 | David Gibson | fprintf(stderr, "spapr_tce_translate out of bounds\n");
|
93 | ad0ebb91 | David Gibson | #endif
|
94 | ad0ebb91 | David Gibson | return -EFAULT;
|
95 | ad0ebb91 | David Gibson | } |
96 | ad0ebb91 | David Gibson | |
97 | ad0ebb91 | David Gibson | tce = tcet->table[addr >> SPAPR_TCE_PAGE_SHIFT].tce; |
98 | ad0ebb91 | David Gibson | |
99 | ad0ebb91 | David Gibson | /* Check TCE */
|
100 | ad0ebb91 | David Gibson | if (!(tce & access)) {
|
101 | ad0ebb91 | David Gibson | return -EPERM;
|
102 | ad0ebb91 | David Gibson | } |
103 | ad0ebb91 | David Gibson | |
104 | ad0ebb91 | David Gibson | /* How much til end of page ? */
|
105 | ad0ebb91 | David Gibson | *len = ((~addr) & SPAPR_TCE_PAGE_MASK) + 1;
|
106 | ad0ebb91 | David Gibson | |
107 | ad0ebb91 | David Gibson | /* Translate */
|
108 | ad0ebb91 | David Gibson | *paddr = (tce & ~SPAPR_TCE_PAGE_MASK) | |
109 | ad0ebb91 | David Gibson | (addr & SPAPR_TCE_PAGE_MASK); |
110 | ad0ebb91 | David Gibson | |
111 | ad0ebb91 | David Gibson | #ifdef DEBUG_TCE
|
112 | ad0ebb91 | David Gibson | fprintf(stderr, " -> *paddr=0x" TARGET_FMT_plx ", *len=0x" |
113 | ad0ebb91 | David Gibson | TARGET_FMT_plx "\n", *paddr, *len);
|
114 | ad0ebb91 | David Gibson | #endif
|
115 | ad0ebb91 | David Gibson | |
116 | ad0ebb91 | David Gibson | return 0; |
117 | ad0ebb91 | David Gibson | } |
118 | ad0ebb91 | David Gibson | |
119 | ad0ebb91 | David Gibson | DMAContext *spapr_tce_new_dma_context(uint32_t liobn, size_t window_size) |
120 | ad0ebb91 | David Gibson | { |
121 | ad0ebb91 | David Gibson | sPAPRTCETable *tcet; |
122 | ad0ebb91 | David Gibson | |
123 | 8b1853e7 | David Gibson | if (spapr_tce_find_by_liobn(liobn)) {
|
124 | 8b1853e7 | David Gibson | fprintf(stderr, "Attempted to create TCE table with duplicate"
|
125 | 8b1853e7 | David Gibson | " LIOBN 0x%x\n", liobn);
|
126 | 8b1853e7 | David Gibson | return NULL; |
127 | 8b1853e7 | David Gibson | } |
128 | 8b1853e7 | David Gibson | |
129 | ad0ebb91 | David Gibson | if (!window_size) {
|
130 | ad0ebb91 | David Gibson | return NULL; |
131 | ad0ebb91 | David Gibson | } |
132 | ad0ebb91 | David Gibson | |
133 | ad0ebb91 | David Gibson | tcet = g_malloc0(sizeof(*tcet));
|
134 | b90600ee | Avi Kivity | dma_context_init(&tcet->dma, &address_space_memory, spapr_tce_translate, NULL, NULL); |
135 | ad0ebb91 | David Gibson | |
136 | ad0ebb91 | David Gibson | tcet->liobn = liobn; |
137 | ad0ebb91 | David Gibson | tcet->window_size = window_size; |
138 | ad0ebb91 | David Gibson | |
139 | ad0ebb91 | David Gibson | if (kvm_enabled()) {
|
140 | ad0ebb91 | David Gibson | tcet->table = kvmppc_create_spapr_tce(liobn, |
141 | ad0ebb91 | David Gibson | window_size, |
142 | ad0ebb91 | David Gibson | &tcet->fd); |
143 | ad0ebb91 | David Gibson | } |
144 | ad0ebb91 | David Gibson | |
145 | ad0ebb91 | David Gibson | if (!tcet->table) {
|
146 | ad0ebb91 | David Gibson | size_t table_size = (window_size >> SPAPR_TCE_PAGE_SHIFT) |
147 | ad0ebb91 | David Gibson | * sizeof(sPAPRTCE);
|
148 | ad0ebb91 | David Gibson | tcet->table = g_malloc0(table_size); |
149 | ad0ebb91 | David Gibson | } |
150 | ad0ebb91 | David Gibson | |
151 | ad0ebb91 | David Gibson | #ifdef DEBUG_TCE
|
152 | ad0ebb91 | David Gibson | fprintf(stderr, "spapr_iommu: New TCE table, liobn=0x%x, context @ %p, "
|
153 | ad0ebb91 | David Gibson | "table @ %p, fd=%d\n", liobn, &tcet->dma, tcet->table, tcet->fd);
|
154 | ad0ebb91 | David Gibson | #endif
|
155 | ad0ebb91 | David Gibson | |
156 | ad0ebb91 | David Gibson | QLIST_INSERT_HEAD(&spapr_tce_tables, tcet, list); |
157 | ad0ebb91 | David Gibson | |
158 | ad0ebb91 | David Gibson | return &tcet->dma;
|
159 | ad0ebb91 | David Gibson | } |
160 | ad0ebb91 | David Gibson | |
161 | ad0ebb91 | David Gibson | void spapr_tce_free(DMAContext *dma)
|
162 | ad0ebb91 | David Gibson | { |
163 | ad0ebb91 | David Gibson | |
164 | ad0ebb91 | David Gibson | if (dma) {
|
165 | ad0ebb91 | David Gibson | sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, dma); |
166 | ad0ebb91 | David Gibson | |
167 | ad0ebb91 | David Gibson | QLIST_REMOVE(tcet, list); |
168 | ad0ebb91 | David Gibson | |
169 | ad0ebb91 | David Gibson | if (!kvm_enabled() ||
|
170 | ad0ebb91 | David Gibson | (kvmppc_remove_spapr_tce(tcet->table, tcet->fd, |
171 | ad0ebb91 | David Gibson | tcet->window_size) != 0)) {
|
172 | ad0ebb91 | David Gibson | g_free(tcet->table); |
173 | ad0ebb91 | David Gibson | } |
174 | ad0ebb91 | David Gibson | |
175 | ad0ebb91 | David Gibson | g_free(tcet); |
176 | ad0ebb91 | David Gibson | } |
177 | ad0ebb91 | David Gibson | } |
178 | ad0ebb91 | David Gibson | |
179 | 53724ee5 | David Gibson | void spapr_tce_set_bypass(DMAContext *dma, bool bypass) |
180 | 53724ee5 | David Gibson | { |
181 | 53724ee5 | David Gibson | sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, dma); |
182 | 53724ee5 | David Gibson | |
183 | 53724ee5 | David Gibson | tcet->bypass = bypass; |
184 | 53724ee5 | David Gibson | } |
185 | 53724ee5 | David Gibson | |
186 | eddeed26 | David Gibson | void spapr_tce_reset(DMAContext *dma)
|
187 | eddeed26 | David Gibson | { |
188 | 53724ee5 | David Gibson | sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, dma); |
189 | 53724ee5 | David Gibson | size_t table_size = (tcet->window_size >> SPAPR_TCE_PAGE_SHIFT) |
190 | 53724ee5 | David Gibson | * sizeof(sPAPRTCE);
|
191 | eddeed26 | David Gibson | |
192 | 53724ee5 | David Gibson | tcet->bypass = false;
|
193 | 53724ee5 | David Gibson | memset(tcet->table, 0, table_size);
|
194 | eddeed26 | David Gibson | } |
195 | eddeed26 | David Gibson | |
196 | edded454 | David Gibson | static target_ulong put_tce_emu(sPAPRTCETable *tcet, target_ulong ioba,
|
197 | edded454 | David Gibson | target_ulong tce) |
198 | edded454 | David Gibson | { |
199 | edded454 | David Gibson | sPAPRTCE *tcep; |
200 | edded454 | David Gibson | |
201 | edded454 | David Gibson | if (ioba >= tcet->window_size) {
|
202 | edded454 | David Gibson | hcall_dprintf("spapr_vio_put_tce on out-of-boards IOBA 0x"
|
203 | edded454 | David Gibson | TARGET_FMT_lx "\n", ioba);
|
204 | edded454 | David Gibson | return H_PARAMETER;
|
205 | edded454 | David Gibson | } |
206 | edded454 | David Gibson | |
207 | edded454 | David Gibson | tcep = tcet->table + (ioba >> SPAPR_TCE_PAGE_SHIFT); |
208 | edded454 | David Gibson | tcep->tce = tce; |
209 | edded454 | David Gibson | |
210 | edded454 | David Gibson | return H_SUCCESS;
|
211 | edded454 | David Gibson | } |
212 | ad0ebb91 | David Gibson | |
213 | b13ce26d | Andreas Färber | static target_ulong h_put_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
214 | ad0ebb91 | David Gibson | target_ulong opcode, target_ulong *args) |
215 | ad0ebb91 | David Gibson | { |
216 | ad0ebb91 | David Gibson | target_ulong liobn = args[0];
|
217 | ad0ebb91 | David Gibson | target_ulong ioba = args[1];
|
218 | ad0ebb91 | David Gibson | target_ulong tce = args[2];
|
219 | ad0ebb91 | David Gibson | sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn); |
220 | ad0ebb91 | David Gibson | |
221 | ad0ebb91 | David Gibson | if (liobn & 0xFFFFFFFF00000000ULL) { |
222 | ad0ebb91 | David Gibson | hcall_dprintf("spapr_vio_put_tce on out-of-boundsw LIOBN "
|
223 | ad0ebb91 | David Gibson | TARGET_FMT_lx "\n", liobn);
|
224 | ad0ebb91 | David Gibson | return H_PARAMETER;
|
225 | ad0ebb91 | David Gibson | } |
226 | ad0ebb91 | David Gibson | |
227 | ad0ebb91 | David Gibson | ioba &= ~(SPAPR_TCE_PAGE_SIZE - 1);
|
228 | ad0ebb91 | David Gibson | |
229 | edded454 | David Gibson | if (tcet) {
|
230 | edded454 | David Gibson | return put_tce_emu(tcet, ioba, tce);
|
231 | edded454 | David Gibson | } |
232 | ad0ebb91 | David Gibson | #ifdef DEBUG_TCE
|
233 | edded454 | David Gibson | fprintf(stderr, "%s on liobn=" TARGET_FMT_lx /*%s*/ |
234 | ad0ebb91 | David Gibson | " ioba 0x" TARGET_FMT_lx " TCE 0x" TARGET_FMT_lx "\n", |
235 | edded454 | David Gibson | __func__, liobn, /*dev->qdev.id, */ioba, tce);
|
236 | ad0ebb91 | David Gibson | #endif
|
237 | ad0ebb91 | David Gibson | |
238 | edded454 | David Gibson | return H_PARAMETER;
|
239 | ad0ebb91 | David Gibson | } |
240 | ad0ebb91 | David Gibson | |
241 | ad0ebb91 | David Gibson | void spapr_iommu_init(void) |
242 | ad0ebb91 | David Gibson | { |
243 | ad0ebb91 | David Gibson | QLIST_INIT(&spapr_tce_tables); |
244 | ad0ebb91 | David Gibson | |
245 | ad0ebb91 | David Gibson | /* hcall-tce */
|
246 | ad0ebb91 | David Gibson | spapr_register_hypercall(H_PUT_TCE, h_put_tce); |
247 | ad0ebb91 | David Gibson | } |
248 | ad0ebb91 | David Gibson | |
249 | ad0ebb91 | David Gibson | int spapr_dma_dt(void *fdt, int node_off, const char *propname, |
250 | 5c4cbcf2 | Alexey Kardashevskiy | uint32_t liobn, uint64_t window, uint32_t size) |
251 | ad0ebb91 | David Gibson | { |
252 | 5c4cbcf2 | Alexey Kardashevskiy | uint32_t dma_prop[5];
|
253 | 5c4cbcf2 | Alexey Kardashevskiy | int ret;
|
254 | 5c4cbcf2 | Alexey Kardashevskiy | |
255 | 5c4cbcf2 | Alexey Kardashevskiy | dma_prop[0] = cpu_to_be32(liobn);
|
256 | 5c4cbcf2 | Alexey Kardashevskiy | dma_prop[1] = cpu_to_be32(window >> 32); |
257 | 5c4cbcf2 | Alexey Kardashevskiy | dma_prop[2] = cpu_to_be32(window & 0xFFFFFFFF); |
258 | 5c4cbcf2 | Alexey Kardashevskiy | dma_prop[3] = 0; /* window size is 32 bits */ |
259 | 5c4cbcf2 | Alexey Kardashevskiy | dma_prop[4] = cpu_to_be32(size);
|
260 | 5c4cbcf2 | Alexey Kardashevskiy | |
261 | 5c4cbcf2 | Alexey Kardashevskiy | ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-address-cells", 2); |
262 | 5c4cbcf2 | Alexey Kardashevskiy | if (ret < 0) { |
263 | 5c4cbcf2 | Alexey Kardashevskiy | return ret;
|
264 | 5c4cbcf2 | Alexey Kardashevskiy | } |
265 | ad0ebb91 | David Gibson | |
266 | 5c4cbcf2 | Alexey Kardashevskiy | ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-size-cells", 2); |
267 | 5c4cbcf2 | Alexey Kardashevskiy | if (ret < 0) { |
268 | 5c4cbcf2 | Alexey Kardashevskiy | return ret;
|
269 | 5c4cbcf2 | Alexey Kardashevskiy | } |
270 | ad0ebb91 | David Gibson | |
271 | 5c4cbcf2 | Alexey Kardashevskiy | ret = fdt_setprop(fdt, node_off, propname, dma_prop, sizeof(dma_prop));
|
272 | 5c4cbcf2 | Alexey Kardashevskiy | if (ret < 0) { |
273 | 5c4cbcf2 | Alexey Kardashevskiy | return ret;
|
274 | ad0ebb91 | David Gibson | } |
275 | ad0ebb91 | David Gibson | |
276 | ad0ebb91 | David Gibson | return 0; |
277 | ad0ebb91 | David Gibson | } |
278 | 5c4cbcf2 | Alexey Kardashevskiy | |
279 | 5c4cbcf2 | Alexey Kardashevskiy | int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname, |
280 | 5c4cbcf2 | Alexey Kardashevskiy | DMAContext *iommu) |
281 | 5c4cbcf2 | Alexey Kardashevskiy | { |
282 | 5c4cbcf2 | Alexey Kardashevskiy | if (!iommu) {
|
283 | 5c4cbcf2 | Alexey Kardashevskiy | return 0; |
284 | 5c4cbcf2 | Alexey Kardashevskiy | } |
285 | 5c4cbcf2 | Alexey Kardashevskiy | |
286 | 5c4cbcf2 | Alexey Kardashevskiy | if (iommu->translate == spapr_tce_translate) {
|
287 | 5c4cbcf2 | Alexey Kardashevskiy | sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, iommu); |
288 | 5c4cbcf2 | Alexey Kardashevskiy | return spapr_dma_dt(fdt, node_off, propname,
|
289 | 5c4cbcf2 | Alexey Kardashevskiy | tcet->liobn, 0, tcet->window_size);
|
290 | 5c4cbcf2 | Alexey Kardashevskiy | } |
291 | 5c4cbcf2 | Alexey Kardashevskiy | |
292 | 5c4cbcf2 | Alexey Kardashevskiy | return -1; |
293 | 5c4cbcf2 | Alexey Kardashevskiy | } |