root / hw / spapr_iommu.c @ 37952117
History | View | Annotate | Download (6.3 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 | ad0ebb91 | David Gibson | #include "hw.h" |
20 | ad0ebb91 | David Gibson | #include "kvm.h" |
21 | ad0ebb91 | David Gibson | #include "qdev.h" |
22 | ad0ebb91 | David Gibson | #include "kvm_ppc.h" |
23 | ad0ebb91 | David Gibson | #include "dma.h" |
24 | ad0ebb91 | David Gibson | |
25 | ad0ebb91 | David Gibson | #include "hw/spapr.h" |
26 | ad0ebb91 | David Gibson | |
27 | ad0ebb91 | David Gibson | #include <libfdt.h> |
28 | ad0ebb91 | David Gibson | |
29 | ad0ebb91 | David Gibson | /* #define DEBUG_TCE */
|
30 | ad0ebb91 | David Gibson | |
31 | ad0ebb91 | David Gibson | enum sPAPRTCEAccess {
|
32 | ad0ebb91 | David Gibson | SPAPR_TCE_FAULT = 0,
|
33 | ad0ebb91 | David Gibson | SPAPR_TCE_RO = 1,
|
34 | ad0ebb91 | David Gibson | SPAPR_TCE_WO = 2,
|
35 | ad0ebb91 | David Gibson | SPAPR_TCE_RW = 3,
|
36 | ad0ebb91 | David Gibson | }; |
37 | ad0ebb91 | David Gibson | |
38 | ad0ebb91 | David Gibson | typedef struct sPAPRTCETable sPAPRTCETable; |
39 | ad0ebb91 | David Gibson | |
40 | ad0ebb91 | David Gibson | struct sPAPRTCETable {
|
41 | ad0ebb91 | David Gibson | DMAContext dma; |
42 | ad0ebb91 | David Gibson | uint32_t liobn; |
43 | ad0ebb91 | David Gibson | uint32_t window_size; |
44 | ad0ebb91 | David Gibson | sPAPRTCE *table; |
45 | ad0ebb91 | David Gibson | int fd;
|
46 | ad0ebb91 | David Gibson | QLIST_ENTRY(sPAPRTCETable) list; |
47 | ad0ebb91 | David Gibson | }; |
48 | ad0ebb91 | David Gibson | |
49 | ad0ebb91 | David Gibson | |
50 | ad0ebb91 | David Gibson | QLIST_HEAD(spapr_tce_tables, sPAPRTCETable) spapr_tce_tables; |
51 | ad0ebb91 | David Gibson | |
52 | ad0ebb91 | David Gibson | static sPAPRTCETable *spapr_tce_find_by_liobn(uint32_t liobn)
|
53 | ad0ebb91 | David Gibson | { |
54 | ad0ebb91 | David Gibson | sPAPRTCETable *tcet; |
55 | ad0ebb91 | David Gibson | |
56 | ad0ebb91 | David Gibson | QLIST_FOREACH(tcet, &spapr_tce_tables, list) { |
57 | ad0ebb91 | David Gibson | if (tcet->liobn == liobn) {
|
58 | ad0ebb91 | David Gibson | return tcet;
|
59 | ad0ebb91 | David Gibson | } |
60 | ad0ebb91 | David Gibson | } |
61 | ad0ebb91 | David Gibson | |
62 | ad0ebb91 | David Gibson | return NULL; |
63 | ad0ebb91 | David Gibson | } |
64 | ad0ebb91 | David Gibson | |
65 | ad0ebb91 | David Gibson | static int spapr_tce_translate(DMAContext *dma, |
66 | ad0ebb91 | David Gibson | dma_addr_t addr, |
67 | ad0ebb91 | David Gibson | target_phys_addr_t *paddr, |
68 | ad0ebb91 | David Gibson | target_phys_addr_t *len, |
69 | ad0ebb91 | David Gibson | DMADirection dir) |
70 | ad0ebb91 | David Gibson | { |
71 | ad0ebb91 | David Gibson | sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, dma); |
72 | ad0ebb91 | David Gibson | enum sPAPRTCEAccess access = (dir == DMA_DIRECTION_FROM_DEVICE)
|
73 | ad0ebb91 | David Gibson | ? SPAPR_TCE_WO : SPAPR_TCE_RO; |
74 | ad0ebb91 | David Gibson | uint64_t tce; |
75 | ad0ebb91 | David Gibson | |
76 | ad0ebb91 | David Gibson | #ifdef DEBUG_TCE
|
77 | ad0ebb91 | David Gibson | fprintf(stderr, "spapr_tce_translate liobn=0x%" PRIx32 " addr=0x" |
78 | ad0ebb91 | David Gibson | DMA_ADDR_FMT "\n", tcet->liobn, addr);
|
79 | ad0ebb91 | David Gibson | #endif
|
80 | ad0ebb91 | David Gibson | |
81 | ad0ebb91 | David Gibson | /* Check if we are in bound */
|
82 | ad0ebb91 | David Gibson | if (addr >= tcet->window_size) {
|
83 | ad0ebb91 | David Gibson | #ifdef DEBUG_TCE
|
84 | ad0ebb91 | David Gibson | fprintf(stderr, "spapr_tce_translate out of bounds\n");
|
85 | ad0ebb91 | David Gibson | #endif
|
86 | ad0ebb91 | David Gibson | return -EFAULT;
|
87 | ad0ebb91 | David Gibson | } |
88 | ad0ebb91 | David Gibson | |
89 | ad0ebb91 | David Gibson | tce = tcet->table[addr >> SPAPR_TCE_PAGE_SHIFT].tce; |
90 | ad0ebb91 | David Gibson | |
91 | ad0ebb91 | David Gibson | /* Check TCE */
|
92 | ad0ebb91 | David Gibson | if (!(tce & access)) {
|
93 | ad0ebb91 | David Gibson | return -EPERM;
|
94 | ad0ebb91 | David Gibson | } |
95 | ad0ebb91 | David Gibson | |
96 | ad0ebb91 | David Gibson | /* How much til end of page ? */
|
97 | ad0ebb91 | David Gibson | *len = ((~addr) & SPAPR_TCE_PAGE_MASK) + 1;
|
98 | ad0ebb91 | David Gibson | |
99 | ad0ebb91 | David Gibson | /* Translate */
|
100 | ad0ebb91 | David Gibson | *paddr = (tce & ~SPAPR_TCE_PAGE_MASK) | |
101 | ad0ebb91 | David Gibson | (addr & SPAPR_TCE_PAGE_MASK); |
102 | ad0ebb91 | David Gibson | |
103 | ad0ebb91 | David Gibson | #ifdef DEBUG_TCE
|
104 | ad0ebb91 | David Gibson | fprintf(stderr, " -> *paddr=0x" TARGET_FMT_plx ", *len=0x" |
105 | ad0ebb91 | David Gibson | TARGET_FMT_plx "\n", *paddr, *len);
|
106 | ad0ebb91 | David Gibson | #endif
|
107 | ad0ebb91 | David Gibson | |
108 | ad0ebb91 | David Gibson | return 0; |
109 | ad0ebb91 | David Gibson | } |
110 | ad0ebb91 | David Gibson | |
111 | ad0ebb91 | David Gibson | DMAContext *spapr_tce_new_dma_context(uint32_t liobn, size_t window_size) |
112 | ad0ebb91 | David Gibson | { |
113 | ad0ebb91 | David Gibson | sPAPRTCETable *tcet; |
114 | ad0ebb91 | David Gibson | |
115 | ad0ebb91 | David Gibson | if (!window_size) {
|
116 | ad0ebb91 | David Gibson | return NULL; |
117 | ad0ebb91 | David Gibson | } |
118 | ad0ebb91 | David Gibson | |
119 | ad0ebb91 | David Gibson | tcet = g_malloc0(sizeof(*tcet));
|
120 | ad0ebb91 | David Gibson | dma_context_init(&tcet->dma, spapr_tce_translate, NULL, NULL); |
121 | ad0ebb91 | David Gibson | |
122 | ad0ebb91 | David Gibson | tcet->liobn = liobn; |
123 | ad0ebb91 | David Gibson | tcet->window_size = window_size; |
124 | ad0ebb91 | David Gibson | |
125 | ad0ebb91 | David Gibson | if (kvm_enabled()) {
|
126 | ad0ebb91 | David Gibson | tcet->table = kvmppc_create_spapr_tce(liobn, |
127 | ad0ebb91 | David Gibson | window_size, |
128 | ad0ebb91 | David Gibson | &tcet->fd); |
129 | ad0ebb91 | David Gibson | } |
130 | ad0ebb91 | David Gibson | |
131 | ad0ebb91 | David Gibson | if (!tcet->table) {
|
132 | ad0ebb91 | David Gibson | size_t table_size = (window_size >> SPAPR_TCE_PAGE_SHIFT) |
133 | ad0ebb91 | David Gibson | * sizeof(sPAPRTCE);
|
134 | ad0ebb91 | David Gibson | tcet->table = g_malloc0(table_size); |
135 | ad0ebb91 | David Gibson | } |
136 | ad0ebb91 | David Gibson | |
137 | ad0ebb91 | David Gibson | #ifdef DEBUG_TCE
|
138 | ad0ebb91 | David Gibson | fprintf(stderr, "spapr_iommu: New TCE table, liobn=0x%x, context @ %p, "
|
139 | ad0ebb91 | David Gibson | "table @ %p, fd=%d\n", liobn, &tcet->dma, tcet->table, tcet->fd);
|
140 | ad0ebb91 | David Gibson | #endif
|
141 | ad0ebb91 | David Gibson | |
142 | ad0ebb91 | David Gibson | QLIST_INSERT_HEAD(&spapr_tce_tables, tcet, list); |
143 | ad0ebb91 | David Gibson | |
144 | ad0ebb91 | David Gibson | return &tcet->dma;
|
145 | ad0ebb91 | David Gibson | } |
146 | ad0ebb91 | David Gibson | |
147 | ad0ebb91 | David Gibson | void spapr_tce_free(DMAContext *dma)
|
148 | ad0ebb91 | David Gibson | { |
149 | ad0ebb91 | David Gibson | |
150 | ad0ebb91 | David Gibson | if (dma) {
|
151 | ad0ebb91 | David Gibson | sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, dma); |
152 | ad0ebb91 | David Gibson | |
153 | ad0ebb91 | David Gibson | QLIST_REMOVE(tcet, list); |
154 | ad0ebb91 | David Gibson | |
155 | ad0ebb91 | David Gibson | if (!kvm_enabled() ||
|
156 | ad0ebb91 | David Gibson | (kvmppc_remove_spapr_tce(tcet->table, tcet->fd, |
157 | ad0ebb91 | David Gibson | tcet->window_size) != 0)) {
|
158 | ad0ebb91 | David Gibson | g_free(tcet->table); |
159 | ad0ebb91 | David Gibson | } |
160 | ad0ebb91 | David Gibson | |
161 | ad0ebb91 | David Gibson | g_free(tcet); |
162 | ad0ebb91 | David Gibson | } |
163 | ad0ebb91 | David Gibson | } |
164 | ad0ebb91 | David Gibson | |
165 | edded454 | David Gibson | static target_ulong put_tce_emu(sPAPRTCETable *tcet, target_ulong ioba,
|
166 | edded454 | David Gibson | target_ulong tce) |
167 | edded454 | David Gibson | { |
168 | edded454 | David Gibson | sPAPRTCE *tcep; |
169 | edded454 | David Gibson | |
170 | edded454 | David Gibson | if (ioba >= tcet->window_size) {
|
171 | edded454 | David Gibson | hcall_dprintf("spapr_vio_put_tce on out-of-boards IOBA 0x"
|
172 | edded454 | David Gibson | TARGET_FMT_lx "\n", ioba);
|
173 | edded454 | David Gibson | return H_PARAMETER;
|
174 | edded454 | David Gibson | } |
175 | edded454 | David Gibson | |
176 | edded454 | David Gibson | tcep = tcet->table + (ioba >> SPAPR_TCE_PAGE_SHIFT); |
177 | edded454 | David Gibson | tcep->tce = tce; |
178 | edded454 | David Gibson | |
179 | edded454 | David Gibson | return H_SUCCESS;
|
180 | edded454 | David Gibson | } |
181 | ad0ebb91 | David Gibson | |
182 | ad0ebb91 | David Gibson | static target_ulong h_put_tce(CPUPPCState *env, sPAPREnvironment *spapr,
|
183 | ad0ebb91 | David Gibson | target_ulong opcode, target_ulong *args) |
184 | ad0ebb91 | David Gibson | { |
185 | ad0ebb91 | David Gibson | target_ulong liobn = args[0];
|
186 | ad0ebb91 | David Gibson | target_ulong ioba = args[1];
|
187 | ad0ebb91 | David Gibson | target_ulong tce = args[2];
|
188 | ad0ebb91 | David Gibson | sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn); |
189 | ad0ebb91 | David Gibson | |
190 | ad0ebb91 | David Gibson | if (liobn & 0xFFFFFFFF00000000ULL) { |
191 | ad0ebb91 | David Gibson | hcall_dprintf("spapr_vio_put_tce on out-of-boundsw LIOBN "
|
192 | ad0ebb91 | David Gibson | TARGET_FMT_lx "\n", liobn);
|
193 | ad0ebb91 | David Gibson | return H_PARAMETER;
|
194 | ad0ebb91 | David Gibson | } |
195 | ad0ebb91 | David Gibson | |
196 | ad0ebb91 | David Gibson | ioba &= ~(SPAPR_TCE_PAGE_SIZE - 1);
|
197 | ad0ebb91 | David Gibson | |
198 | edded454 | David Gibson | if (tcet) {
|
199 | edded454 | David Gibson | return put_tce_emu(tcet, ioba, tce);
|
200 | edded454 | David Gibson | } |
201 | ad0ebb91 | David Gibson | #ifdef DEBUG_TCE
|
202 | edded454 | David Gibson | fprintf(stderr, "%s on liobn=" TARGET_FMT_lx /*%s*/ |
203 | ad0ebb91 | David Gibson | " ioba 0x" TARGET_FMT_lx " TCE 0x" TARGET_FMT_lx "\n", |
204 | edded454 | David Gibson | __func__, liobn, /*dev->qdev.id, */ioba, tce);
|
205 | ad0ebb91 | David Gibson | #endif
|
206 | ad0ebb91 | David Gibson | |
207 | edded454 | David Gibson | return H_PARAMETER;
|
208 | ad0ebb91 | David Gibson | } |
209 | ad0ebb91 | David Gibson | |
210 | ad0ebb91 | David Gibson | void spapr_iommu_init(void) |
211 | ad0ebb91 | David Gibson | { |
212 | ad0ebb91 | David Gibson | QLIST_INIT(&spapr_tce_tables); |
213 | ad0ebb91 | David Gibson | |
214 | ad0ebb91 | David Gibson | /* hcall-tce */
|
215 | ad0ebb91 | David Gibson | spapr_register_hypercall(H_PUT_TCE, h_put_tce); |
216 | ad0ebb91 | David Gibson | } |
217 | ad0ebb91 | David Gibson | |
218 | ad0ebb91 | David Gibson | int spapr_dma_dt(void *fdt, int node_off, const char *propname, |
219 | ad0ebb91 | David Gibson | DMAContext *dma) |
220 | ad0ebb91 | David Gibson | { |
221 | ad0ebb91 | David Gibson | if (dma) {
|
222 | ad0ebb91 | David Gibson | sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, dma); |
223 | ad0ebb91 | David Gibson | uint32_t dma_prop[] = {cpu_to_be32(tcet->liobn), |
224 | ad0ebb91 | David Gibson | 0, 0, |
225 | ad0ebb91 | David Gibson | 0, cpu_to_be32(tcet->window_size)};
|
226 | ad0ebb91 | David Gibson | int ret;
|
227 | ad0ebb91 | David Gibson | |
228 | ad0ebb91 | David Gibson | ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-address-cells", 2); |
229 | ad0ebb91 | David Gibson | if (ret < 0) { |
230 | ad0ebb91 | David Gibson | return ret;
|
231 | ad0ebb91 | David Gibson | } |
232 | ad0ebb91 | David Gibson | |
233 | ad0ebb91 | David Gibson | ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-size-cells", 2); |
234 | ad0ebb91 | David Gibson | if (ret < 0) { |
235 | ad0ebb91 | David Gibson | return ret;
|
236 | ad0ebb91 | David Gibson | } |
237 | ad0ebb91 | David Gibson | |
238 | ad0ebb91 | David Gibson | ret = fdt_setprop(fdt, node_off, propname, dma_prop, |
239 | ad0ebb91 | David Gibson | sizeof(dma_prop));
|
240 | ad0ebb91 | David Gibson | if (ret < 0) { |
241 | ad0ebb91 | David Gibson | return ret;
|
242 | ad0ebb91 | David Gibson | } |
243 | ad0ebb91 | David Gibson | } |
244 | ad0ebb91 | David Gibson | |
245 | ad0ebb91 | David Gibson | return 0; |
246 | ad0ebb91 | David Gibson | } |