]>
Commit | Line | Data |
---|---|---|
420557e8 FB |
1 | /* |
2 | * QEMU SPARC iommu emulation | |
3 | * | |
66321a11 | 4 | * Copyright (c) 2003-2005 Fabrice Bellard |
420557e8 FB |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
23 | */ | |
24 | #include "vl.h" | |
25 | ||
26 | /* debug iommu */ | |
27 | //#define DEBUG_IOMMU | |
28 | ||
66321a11 FB |
29 | #ifdef DEBUG_IOMMU |
30 | #define DPRINTF(fmt, args...) \ | |
31 | do { printf("IOMMU: " fmt , ##args); } while (0) | |
32 | #else | |
33 | #define DPRINTF(fmt, args...) | |
34 | #endif | |
420557e8 | 35 | |
4e3b1ea1 FB |
36 | #define IOMMU_NREGS (3*4096/4) |
37 | #define IOMMU_CTRL (0x0000 >> 2) | |
420557e8 FB |
38 | #define IOMMU_CTRL_IMPL 0xf0000000 /* Implementation */ |
39 | #define IOMMU_CTRL_VERS 0x0f000000 /* Version */ | |
4e3b1ea1 | 40 | #define IOMMU_VERSION 0x04000000 |
420557e8 FB |
41 | #define IOMMU_CTRL_RNGE 0x0000001c /* Mapping RANGE */ |
42 | #define IOMMU_RNGE_16MB 0x00000000 /* 0xff000000 -> 0xffffffff */ | |
43 | #define IOMMU_RNGE_32MB 0x00000004 /* 0xfe000000 -> 0xffffffff */ | |
44 | #define IOMMU_RNGE_64MB 0x00000008 /* 0xfc000000 -> 0xffffffff */ | |
45 | #define IOMMU_RNGE_128MB 0x0000000c /* 0xf8000000 -> 0xffffffff */ | |
46 | #define IOMMU_RNGE_256MB 0x00000010 /* 0xf0000000 -> 0xffffffff */ | |
47 | #define IOMMU_RNGE_512MB 0x00000014 /* 0xe0000000 -> 0xffffffff */ | |
48 | #define IOMMU_RNGE_1GB 0x00000018 /* 0xc0000000 -> 0xffffffff */ | |
49 | #define IOMMU_RNGE_2GB 0x0000001c /* 0x80000000 -> 0xffffffff */ | |
50 | #define IOMMU_CTRL_ENAB 0x00000001 /* IOMMU Enable */ | |
4e3b1ea1 FB |
51 | #define IOMMU_CTRL_MASK 0x0000001d |
52 | ||
53 | #define IOMMU_BASE (0x0004 >> 2) | |
54 | #define IOMMU_BASE_MASK 0x07fffc00 | |
55 | ||
56 | #define IOMMU_TLBFLUSH (0x0014 >> 2) | |
57 | #define IOMMU_TLBFLUSH_MASK 0xffffffff | |
58 | ||
59 | #define IOMMU_PGFLUSH (0x0018 >> 2) | |
60 | #define IOMMU_PGFLUSH_MASK 0xffffffff | |
61 | ||
62 | #define IOMMU_SBCFG0 (0x1010 >> 2) /* SBUS configration per-slot */ | |
63 | #define IOMMU_SBCFG1 (0x1014 >> 2) /* SBUS configration per-slot */ | |
64 | #define IOMMU_SBCFG2 (0x1018 >> 2) /* SBUS configration per-slot */ | |
65 | #define IOMMU_SBCFG3 (0x101c >> 2) /* SBUS configration per-slot */ | |
66 | #define IOMMU_SBCFG_SAB30 0x00010000 /* Phys-address bit 30 when bypass enabled */ | |
67 | #define IOMMU_SBCFG_BA16 0x00000004 /* Slave supports 16 byte bursts */ | |
68 | #define IOMMU_SBCFG_BA8 0x00000002 /* Slave supports 8 byte bursts */ | |
69 | #define IOMMU_SBCFG_BYPASS 0x00000001 /* Bypass IOMMU, treat all addresses | |
70 | produced by this device as pure | |
71 | physical. */ | |
72 | #define IOMMU_SBCFG_MASK 0x00010003 | |
73 | ||
74 | #define IOMMU_ARBEN (0x2000 >> 2) /* SBUS arbitration enable */ | |
75 | #define IOMMU_ARBEN_MASK 0x001f0000 | |
76 | #define IOMMU_MID 0x00000008 | |
420557e8 | 77 | |
420557e8 FB |
78 | /* The format of an iopte in the page tables */ |
79 | #define IOPTE_PAGE 0x07ffff00 /* Physical page number (PA[30:12]) */ | |
80 | #define IOPTE_CACHE 0x00000080 /* Cached (in vme IOCACHE or Viking/MXCC) */ | |
81 | #define IOPTE_WRITE 0x00000004 /* Writeable */ | |
82 | #define IOPTE_VALID 0x00000002 /* IOPTE is valid */ | |
83 | #define IOPTE_WAZ 0x00000001 /* Write as zeros */ | |
84 | ||
420557e8 FB |
85 | #define PAGE_SHIFT 12 |
86 | #define PAGE_SIZE (1 << PAGE_SHIFT) | |
87 | #define PAGE_MASK (PAGE_SIZE - 1) | |
88 | ||
89 | typedef struct IOMMUState { | |
8d5f07fa | 90 | uint32_t addr; |
66321a11 | 91 | uint32_t regs[IOMMU_NREGS]; |
8d5f07fa | 92 | uint32_t iostart; |
420557e8 FB |
93 | } IOMMUState; |
94 | ||
420557e8 FB |
95 | static uint32_t iommu_mem_readw(void *opaque, target_phys_addr_t addr) |
96 | { | |
97 | IOMMUState *s = opaque; | |
98 | uint32_t saddr; | |
99 | ||
8d5f07fa | 100 | saddr = (addr - s->addr) >> 2; |
420557e8 FB |
101 | switch (saddr) { |
102 | default: | |
66321a11 | 103 | DPRINTF("read reg[%d] = %x\n", saddr, s->regs[saddr]); |
420557e8 FB |
104 | return s->regs[saddr]; |
105 | break; | |
106 | } | |
107 | return 0; | |
108 | } | |
109 | ||
110 | static void iommu_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val) | |
111 | { | |
112 | IOMMUState *s = opaque; | |
113 | uint32_t saddr; | |
114 | ||
8d5f07fa | 115 | saddr = (addr - s->addr) >> 2; |
66321a11 | 116 | DPRINTF("write reg[%d] = %x\n", saddr, val); |
420557e8 | 117 | switch (saddr) { |
4e3b1ea1 | 118 | case IOMMU_CTRL: |
8d5f07fa FB |
119 | switch (val & IOMMU_CTRL_RNGE) { |
120 | case IOMMU_RNGE_16MB: | |
121 | s->iostart = 0xff000000; | |
122 | break; | |
123 | case IOMMU_RNGE_32MB: | |
124 | s->iostart = 0xfe000000; | |
125 | break; | |
126 | case IOMMU_RNGE_64MB: | |
127 | s->iostart = 0xfc000000; | |
128 | break; | |
129 | case IOMMU_RNGE_128MB: | |
130 | s->iostart = 0xf8000000; | |
131 | break; | |
132 | case IOMMU_RNGE_256MB: | |
133 | s->iostart = 0xf0000000; | |
134 | break; | |
135 | case IOMMU_RNGE_512MB: | |
136 | s->iostart = 0xe0000000; | |
137 | break; | |
138 | case IOMMU_RNGE_1GB: | |
139 | s->iostart = 0xc0000000; | |
140 | break; | |
141 | default: | |
142 | case IOMMU_RNGE_2GB: | |
143 | s->iostart = 0x80000000; | |
144 | break; | |
145 | } | |
66321a11 | 146 | DPRINTF("iostart = %x\n", s->iostart); |
4e3b1ea1 FB |
147 | s->regs[saddr] = ((val & IOMMU_CTRL_MASK) | IOMMU_VERSION); |
148 | break; | |
149 | case IOMMU_BASE: | |
150 | s->regs[saddr] = val & IOMMU_BASE_MASK; | |
151 | break; | |
152 | case IOMMU_TLBFLUSH: | |
153 | DPRINTF("tlb flush %x\n", val); | |
154 | s->regs[saddr] = val & IOMMU_TLBFLUSH_MASK; | |
155 | break; | |
156 | case IOMMU_PGFLUSH: | |
157 | DPRINTF("page flush %x\n", val); | |
158 | s->regs[saddr] = val & IOMMU_PGFLUSH_MASK; | |
159 | break; | |
160 | case IOMMU_SBCFG0: | |
161 | case IOMMU_SBCFG1: | |
162 | case IOMMU_SBCFG2: | |
163 | case IOMMU_SBCFG3: | |
164 | s->regs[saddr] = val & IOMMU_SBCFG_MASK; | |
165 | break; | |
166 | case IOMMU_ARBEN: | |
167 | // XXX implement SBus probing: fault when reading unmapped | |
168 | // addresses, fault cause and address stored to MMU/IOMMU | |
169 | s->regs[saddr] = (val & IOMMU_ARBEN_MASK) | IOMMU_MID; | |
170 | break; | |
420557e8 FB |
171 | default: |
172 | s->regs[saddr] = val; | |
173 | break; | |
174 | } | |
175 | } | |
176 | ||
177 | static CPUReadMemoryFunc *iommu_mem_read[3] = { | |
178 | iommu_mem_readw, | |
179 | iommu_mem_readw, | |
180 | iommu_mem_readw, | |
181 | }; | |
182 | ||
183 | static CPUWriteMemoryFunc *iommu_mem_write[3] = { | |
184 | iommu_mem_writew, | |
185 | iommu_mem_writew, | |
186 | iommu_mem_writew, | |
187 | }; | |
188 | ||
a917d384 | 189 | static uint32_t iommu_page_get_flags(IOMMUState *s, uint32_t addr) |
420557e8 | 190 | { |
a917d384 | 191 | uint32_t iopte; |
420557e8 | 192 | |
66321a11 FB |
193 | iopte = s->regs[1] << 4; |
194 | addr &= ~s->iostart; | |
195 | iopte += (addr >> (PAGE_SHIFT - 2)) & ~3; | |
a917d384 PB |
196 | return ldl_phys(iopte); |
197 | } | |
198 | ||
199 | static uint32_t iommu_translate_pa(IOMMUState *s, uint32_t addr, uint32_t pa) | |
200 | { | |
201 | uint32_t tmppte; | |
202 | ||
66321a11 FB |
203 | tmppte = pa; |
204 | pa = ((pa & IOPTE_PAGE) << 4) + (addr & PAGE_MASK); | |
a917d384 | 205 | DPRINTF("xlate dva %x => pa %x (iopte = %x)\n", addr, pa, tmppte); |
66321a11 | 206 | return pa; |
420557e8 FB |
207 | } |
208 | ||
67e999be FB |
209 | void sparc_iommu_memory_rw(void *opaque, target_phys_addr_t addr, |
210 | uint8_t *buf, int len, int is_write) | |
a917d384 PB |
211 | { |
212 | int l, flags; | |
213 | target_ulong page, phys_addr; | |
a917d384 PB |
214 | |
215 | while (len > 0) { | |
216 | page = addr & TARGET_PAGE_MASK; | |
217 | l = (page + TARGET_PAGE_SIZE) - addr; | |
218 | if (l > len) | |
219 | l = len; | |
220 | flags = iommu_page_get_flags(opaque, page); | |
221 | if (!(flags & IOPTE_VALID)) | |
222 | return; | |
223 | phys_addr = iommu_translate_pa(opaque, addr, flags); | |
224 | if (is_write) { | |
225 | if (!(flags & IOPTE_WRITE)) | |
226 | return; | |
227 | cpu_physical_memory_write(phys_addr, buf, len); | |
228 | } else { | |
229 | cpu_physical_memory_read(phys_addr, buf, len); | |
230 | } | |
231 | len -= l; | |
232 | buf += l; | |
233 | addr += l; | |
234 | } | |
235 | } | |
236 | ||
e80cfcfc FB |
237 | static void iommu_save(QEMUFile *f, void *opaque) |
238 | { | |
239 | IOMMUState *s = opaque; | |
240 | int i; | |
241 | ||
242 | qemu_put_be32s(f, &s->addr); | |
66321a11 | 243 | for (i = 0; i < IOMMU_NREGS; i++) |
e80cfcfc FB |
244 | qemu_put_be32s(f, &s->regs[i]); |
245 | qemu_put_be32s(f, &s->iostart); | |
246 | } | |
247 | ||
248 | static int iommu_load(QEMUFile *f, void *opaque, int version_id) | |
249 | { | |
250 | IOMMUState *s = opaque; | |
251 | int i; | |
252 | ||
253 | if (version_id != 1) | |
254 | return -EINVAL; | |
255 | ||
256 | qemu_get_be32s(f, &s->addr); | |
66321a11 | 257 | for (i = 0; i < IOMMU_NREGS; i++) |
e80cfcfc FB |
258 | qemu_put_be32s(f, &s->regs[i]); |
259 | qemu_get_be32s(f, &s->iostart); | |
260 | ||
261 | return 0; | |
262 | } | |
263 | ||
264 | static void iommu_reset(void *opaque) | |
265 | { | |
266 | IOMMUState *s = opaque; | |
267 | ||
66321a11 | 268 | memset(s->regs, 0, IOMMU_NREGS * 4); |
e80cfcfc | 269 | s->iostart = 0; |
4e3b1ea1 | 270 | s->regs[0] = IOMMU_VERSION; |
e80cfcfc FB |
271 | } |
272 | ||
273 | void *iommu_init(uint32_t addr) | |
420557e8 FB |
274 | { |
275 | IOMMUState *s; | |
8d5f07fa | 276 | int iommu_io_memory; |
420557e8 FB |
277 | |
278 | s = qemu_mallocz(sizeof(IOMMUState)); | |
279 | if (!s) | |
e80cfcfc | 280 | return NULL; |
420557e8 | 281 | |
8d5f07fa FB |
282 | s->addr = addr; |
283 | ||
420557e8 | 284 | iommu_io_memory = cpu_register_io_memory(0, iommu_mem_read, iommu_mem_write, s); |
66321a11 | 285 | cpu_register_physical_memory(addr, IOMMU_NREGS * 4, iommu_io_memory); |
420557e8 | 286 | |
e80cfcfc FB |
287 | register_savevm("iommu", addr, 1, iommu_save, iommu_load, s); |
288 | qemu_register_reset(iommu_reset, s); | |
289 | return s; | |
420557e8 FB |
290 | } |
291 |