1 | /* |
2 | * MicroBlaze helper routines. |
3 | * |
4 | * Copyright (c) 2009 Edgar E. Iglesias <edgar.iglesias@gmail.com> |
5 | * Copyright (c) 2009-2012 PetaLogix Qld Pty Ltd. |
6 | * |
7 | * This library is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either |
10 | * version 2 of the License, or (at your option) any later version. |
11 | * |
12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
19 | */ |
20 | |
21 | #include "qemu/osdep.h" |
22 | #include "cpu.h" |
23 | #include "exec/exec-all.h" |
24 | #include "qemu/host-utils.h" |
25 | #include "exec/log.h" |
26 | |
27 | #define D(x) |
28 | |
29 | #if defined(CONFIG_USER_ONLY) |
30 | |
31 | void mb_cpu_do_interrupt(CPUState *cs) |
32 | { |
33 | MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); |
34 | CPUMBState *env = &cpu->env; |
35 | |
36 | cs->exception_index = -1; |
37 | env->res_addr = RES_ADDR_NONE; |
38 | env->regs[14] = env->sregs[SR_PC]; |
39 | } |
40 | |
41 | bool mb_cpu_tlb_fill(CPUState *cs, vaddr address, int size, |
42 | MMUAccessType access_type, int mmu_idx, |
43 | bool probe, uintptr_t retaddr) |
44 | { |
45 | cs->exception_index = 0xaa; |
46 | cpu_loop_exit_restore(cs, retaddr); |
47 | } |
48 | |
49 | #else /* !CONFIG_USER_ONLY */ |
50 | |
51 | bool mb_cpu_tlb_fill(CPUState *cs, vaddr address, int size, |
52 | MMUAccessType access_type, int mmu_idx, |
53 | bool probe, uintptr_t retaddr) |
54 | { |
55 | MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); |
56 | CPUMBState *env = &cpu->env; |
57 | struct microblaze_mmu_lookup lu; |
58 | unsigned int hit; |
59 | int prot; |
60 | |
61 | if (mmu_idx == MMU_NOMMU_IDX) { |
62 | /* MMU disabled or not available. */ |
63 | address &= TARGET_PAGE_MASK; |
64 | prot = PAGE_BITS; |
65 | tlb_set_page(cs, address, address, prot, mmu_idx, TARGET_PAGE_SIZE); |
66 | return true; |
67 | } |
68 | |
69 | hit = mmu_translate(&env->mmu, &lu, address, access_type, mmu_idx); |
70 | if (likely(hit)) { |
71 | uint32_t vaddr = address & TARGET_PAGE_MASK; |
72 | uint32_t paddr = lu.paddr + vaddr - lu.vaddr; |
73 | |
74 | qemu_log_mask(CPU_LOG_MMU, "MMU map mmu=%d v=%x p=%x prot=%x\n" , |
75 | mmu_idx, vaddr, paddr, lu.prot); |
76 | tlb_set_page(cs, vaddr, paddr, lu.prot, mmu_idx, TARGET_PAGE_SIZE); |
77 | return true; |
78 | } |
79 | |
80 | /* TLB miss. */ |
81 | if (probe) { |
82 | return false; |
83 | } |
84 | |
85 | qemu_log_mask(CPU_LOG_MMU, "mmu=%d miss v=%" VADDR_PRIx "\n" , |
86 | mmu_idx, address); |
87 | |
88 | env->sregs[SR_EAR] = address; |
89 | switch (lu.err) { |
90 | case ERR_PROT: |
91 | env->sregs[SR_ESR] = access_type == MMU_INST_FETCH ? 17 : 16; |
92 | env->sregs[SR_ESR] |= (access_type == MMU_DATA_STORE) << 10; |
93 | break; |
94 | case ERR_MISS: |
95 | env->sregs[SR_ESR] = access_type == MMU_INST_FETCH ? 19 : 18; |
96 | env->sregs[SR_ESR] |= (access_type == MMU_DATA_STORE) << 10; |
97 | break; |
98 | default: |
99 | abort(); |
100 | } |
101 | |
102 | if (cs->exception_index == EXCP_MMU) { |
103 | cpu_abort(cs, "recursive faults\n" ); |
104 | } |
105 | |
106 | /* TLB miss. */ |
107 | cs->exception_index = EXCP_MMU; |
108 | cpu_loop_exit_restore(cs, retaddr); |
109 | } |
110 | |
111 | void mb_cpu_do_interrupt(CPUState *cs) |
112 | { |
113 | MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); |
114 | CPUMBState *env = &cpu->env; |
115 | uint32_t t; |
116 | |
117 | /* IMM flag cannot propagate across a branch and into the dslot. */ |
118 | assert(!((env->iflags & D_FLAG) && (env->iflags & IMM_FLAG))); |
119 | assert(!(env->iflags & (DRTI_FLAG | DRTE_FLAG | DRTB_FLAG))); |
120 | /* assert(env->sregs[SR_MSR] & (MSR_EE)); Only for HW exceptions. */ |
121 | env->res_addr = RES_ADDR_NONE; |
122 | switch (cs->exception_index) { |
123 | case EXCP_HW_EXCP: |
124 | if (!(env->pvr.regs[0] & PVR0_USE_EXC_MASK)) { |
125 | qemu_log_mask(LOG_GUEST_ERROR, "Exception raised on system without exceptions!\n" ); |
126 | return; |
127 | } |
128 | |
129 | env->regs[17] = env->sregs[SR_PC] + 4; |
130 | env->sregs[SR_ESR] &= ~(1 << 12); |
131 | |
132 | /* Exception breaks branch + dslot sequence? */ |
133 | if (env->iflags & D_FLAG) { |
134 | env->sregs[SR_ESR] |= 1 << 12 ; |
135 | env->sregs[SR_BTR] = env->btarget; |
136 | } |
137 | |
138 | /* Disable the MMU. */ |
139 | t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; |
140 | env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); |
141 | env->sregs[SR_MSR] |= t; |
142 | /* Exception in progress. */ |
143 | env->sregs[SR_MSR] |= MSR_EIP; |
144 | |
145 | qemu_log_mask(CPU_LOG_INT, |
146 | "hw exception at pc=%" PRIx64 " ear=%" PRIx64 " " |
147 | "esr=%" PRIx64 " iflags=%x\n" , |
148 | env->sregs[SR_PC], env->sregs[SR_EAR], |
149 | env->sregs[SR_ESR], env->iflags); |
150 | log_cpu_state_mask(CPU_LOG_INT, cs, 0); |
151 | env->iflags &= ~(IMM_FLAG | D_FLAG); |
152 | env->sregs[SR_PC] = cpu->cfg.base_vectors + 0x20; |
153 | break; |
154 | |
155 | case EXCP_MMU: |
156 | env->regs[17] = env->sregs[SR_PC]; |
157 | |
158 | env->sregs[SR_ESR] &= ~(1 << 12); |
159 | /* Exception breaks branch + dslot sequence? */ |
160 | if (env->iflags & D_FLAG) { |
161 | D(qemu_log("D_FLAG set at exception bimm=%d\n" , env->bimm)); |
162 | env->sregs[SR_ESR] |= 1 << 12 ; |
163 | env->sregs[SR_BTR] = env->btarget; |
164 | |
165 | /* Reexecute the branch. */ |
166 | env->regs[17] -= 4; |
167 | /* was the branch immprefixed?. */ |
168 | if (env->bimm) { |
169 | qemu_log_mask(CPU_LOG_INT, |
170 | "bimm exception at pc=%" PRIx64 " " |
171 | "iflags=%x\n" , |
172 | env->sregs[SR_PC], env->iflags); |
173 | env->regs[17] -= 4; |
174 | log_cpu_state_mask(CPU_LOG_INT, cs, 0); |
175 | } |
176 | } else if (env->iflags & IMM_FLAG) { |
177 | D(qemu_log("IMM_FLAG set at exception\n" )); |
178 | env->regs[17] -= 4; |
179 | } |
180 | |
181 | /* Disable the MMU. */ |
182 | t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; |
183 | env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); |
184 | env->sregs[SR_MSR] |= t; |
185 | /* Exception in progress. */ |
186 | env->sregs[SR_MSR] |= MSR_EIP; |
187 | |
188 | qemu_log_mask(CPU_LOG_INT, |
189 | "exception at pc=%" PRIx64 " ear=%" PRIx64 " " |
190 | "iflags=%x\n" , |
191 | env->sregs[SR_PC], env->sregs[SR_EAR], env->iflags); |
192 | log_cpu_state_mask(CPU_LOG_INT, cs, 0); |
193 | env->iflags &= ~(IMM_FLAG | D_FLAG); |
194 | env->sregs[SR_PC] = cpu->cfg.base_vectors + 0x20; |
195 | break; |
196 | |
197 | case EXCP_IRQ: |
198 | assert(!(env->sregs[SR_MSR] & (MSR_EIP | MSR_BIP))); |
199 | assert(env->sregs[SR_MSR] & MSR_IE); |
200 | assert(!(env->iflags & D_FLAG)); |
201 | |
202 | t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; |
203 | |
204 | #if 0 |
205 | #include "disas/disas.h" |
206 | |
207 | /* Useful instrumentation when debugging interrupt issues in either |
208 | the models or in sw. */ |
209 | { |
210 | const char *sym; |
211 | |
212 | sym = lookup_symbol(env->sregs[SR_PC]); |
213 | if (sym |
214 | && (!strcmp("netif_rx" , sym) |
215 | || !strcmp("process_backlog" , sym))) { |
216 | |
217 | qemu_log( |
218 | "interrupt at pc=%x msr=%x %x iflags=%x sym=%s\n" , |
219 | env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags, |
220 | sym); |
221 | |
222 | log_cpu_state(cs, 0); |
223 | } |
224 | } |
225 | #endif |
226 | qemu_log_mask(CPU_LOG_INT, |
227 | "interrupt at pc=%" PRIx64 " msr=%" PRIx64 " %x " |
228 | "iflags=%x\n" , |
229 | env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags); |
230 | |
231 | env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM \ |
232 | | MSR_UM | MSR_IE); |
233 | env->sregs[SR_MSR] |= t; |
234 | |
235 | env->regs[14] = env->sregs[SR_PC]; |
236 | env->sregs[SR_PC] = cpu->cfg.base_vectors + 0x10; |
237 | //log_cpu_state_mask(CPU_LOG_INT, cs, 0); |
238 | break; |
239 | |
240 | case EXCP_BREAK: |
241 | case EXCP_HW_BREAK: |
242 | assert(!(env->iflags & IMM_FLAG)); |
243 | assert(!(env->iflags & D_FLAG)); |
244 | t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; |
245 | qemu_log_mask(CPU_LOG_INT, |
246 | "break at pc=%" PRIx64 " msr=%" PRIx64 " %x " |
247 | "iflags=%x\n" , |
248 | env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags); |
249 | log_cpu_state_mask(CPU_LOG_INT, cs, 0); |
250 | env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); |
251 | env->sregs[SR_MSR] |= t; |
252 | env->sregs[SR_MSR] |= MSR_BIP; |
253 | if (cs->exception_index == EXCP_HW_BREAK) { |
254 | env->regs[16] = env->sregs[SR_PC]; |
255 | env->sregs[SR_MSR] |= MSR_BIP; |
256 | env->sregs[SR_PC] = cpu->cfg.base_vectors + 0x18; |
257 | } else |
258 | env->sregs[SR_PC] = env->btarget; |
259 | break; |
260 | default: |
261 | cpu_abort(cs, "unhandled exception type=%d\n" , |
262 | cs->exception_index); |
263 | break; |
264 | } |
265 | } |
266 | |
267 | hwaddr mb_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) |
268 | { |
269 | MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); |
270 | CPUMBState *env = &cpu->env; |
271 | target_ulong vaddr, paddr = 0; |
272 | struct microblaze_mmu_lookup lu; |
273 | int mmu_idx = cpu_mmu_index(env, false); |
274 | unsigned int hit; |
275 | |
276 | if (mmu_idx != MMU_NOMMU_IDX) { |
277 | hit = mmu_translate(&env->mmu, &lu, addr, 0, 0); |
278 | if (hit) { |
279 | vaddr = addr & TARGET_PAGE_MASK; |
280 | paddr = lu.paddr + vaddr - lu.vaddr; |
281 | } else |
282 | paddr = 0; /* ???. */ |
283 | } else |
284 | paddr = addr & TARGET_PAGE_MASK; |
285 | |
286 | return paddr; |
287 | } |
288 | #endif |
289 | |
290 | bool mb_cpu_exec_interrupt(CPUState *cs, int interrupt_request) |
291 | { |
292 | MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); |
293 | CPUMBState *env = &cpu->env; |
294 | |
295 | if ((interrupt_request & CPU_INTERRUPT_HARD) |
296 | && (env->sregs[SR_MSR] & MSR_IE) |
297 | && !(env->sregs[SR_MSR] & (MSR_EIP | MSR_BIP)) |
298 | && !(env->iflags & (D_FLAG | IMM_FLAG))) { |
299 | cs->exception_index = EXCP_IRQ; |
300 | mb_cpu_do_interrupt(cs); |
301 | return true; |
302 | } |
303 | return false; |
304 | } |
305 | |