master
c 327 lines 9.78 KB
Raw
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.1 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/cputlb.h"
24 #include "accel/tcg/cpu-loop.h"
25 #include "accel/tcg/cpu-mmu-index.h"
26 #include "exec/page-protection.h"
27 #include "exec/target_page.h"
28 #include "qemu/host-utils.h"
29 #include "exec/log.h"
30 #include "exec/helper-proto.h"
31 #include "qemu/plugin.h"
32
33
34 G_NORETURN
35 static void mb_unaligned_access_internal(CPUState *cs, uint64_t addr,
36 uintptr_t retaddr)
37 {
38 CPUMBState *env = cpu_env(cs);
39 uint32_t esr, iflags;
40 uint64_t last_pc = env->pc;
41
42 /* Recover the pc and iflags from the corresponding insn_start. */
43 cpu_restore_state(cs, retaddr);
44 iflags = env->iflags;
45
46 qemu_log_mask(CPU_LOG_INT,
47 "Unaligned access addr=0x%" PRIx64 " pc=%x iflags=%x\n",
48 addr, env->pc, iflags);
49
50 esr = ESR_EC_UNALIGNED_DATA;
51 if (likely(iflags & ESR_ESS_FLAG)) {
52 esr |= iflags & ESR_ESS_MASK;
53 } else {
54 qemu_log_mask(LOG_UNIMP, "Unaligned access without ESR_ESS_FLAG\n");
55 }
56
57 env->ear = addr;
58 env->esr = esr;
59 cs->exception_index = EXCP_HW_EXCP;
60 qemu_plugin_vcpu_exception_cb(cs, last_pc);
61 cpu_loop_exit(cs);
62 }
63
64 void mb_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
65 MMUAccessType access_type,
66 int mmu_idx, uintptr_t retaddr)
67 {
68 mb_unaligned_access_internal(cs, addr, retaddr);
69 }
70
71 #ifndef CONFIG_USER_ONLY
72
73 void HELPER(microblaze_unaligned_access)(CPUMBState *env, uint64_t addr)
74 {
75 mb_unaligned_access_internal(env_cpu(env), addr, GETPC());
76 }
77
78 static bool mb_cpu_access_is_secure(MicroBlazeCPU *cpu,
79 MMUAccessType access_type)
80 {
81 if (access_type == MMU_INST_FETCH) {
82 return !cpu->ns_axi_ip;
83 } else {
84 return !cpu->ns_axi_dp;
85 }
86 }
87
88 bool mb_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
89 MMUAccessType access_type, int mmu_idx,
90 bool probe, uintptr_t retaddr)
91 {
92 MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs);
93 CPUMBState *env = &cpu->env;
94 MicroBlazeMMULookup lu;
95 unsigned int hit;
96 int prot;
97 MemTxAttrs attrs = {};
98
99 attrs.secure = mb_cpu_access_is_secure(cpu, access_type);
100
101 if (mmu_idx == MMU_NOMMU_IDX) {
102 /* MMU disabled or not available. */
103 address &= TARGET_PAGE_MASK;
104 prot = PAGE_RWX;
105 tlb_set_page_with_attrs(cs, address, address, attrs, prot, mmu_idx,
106 TARGET_PAGE_SIZE);
107 return true;
108 }
109
110 hit = mmu_translate(cpu, &lu, address, access_type, mmu_idx);
111 if (likely(hit)) {
112 uint32_t vaddr = address & TARGET_PAGE_MASK;
113 uint32_t paddr = lu.paddr + vaddr - lu.vaddr;
114
115 qemu_log_mask(CPU_LOG_MMU, "MMU map mmu=%d v=%x p=%x prot=%x\n",
116 mmu_idx, vaddr, paddr, lu.prot);
117 tlb_set_page_with_attrs(cs, vaddr, paddr, attrs, lu.prot, mmu_idx,
118 TARGET_PAGE_SIZE);
119 return true;
120 }
121
122 /* TLB miss. */
123 if (probe) {
124 return false;
125 }
126
127 qemu_log_mask(CPU_LOG_MMU, "mmu=%d miss v=%" VADDR_PRIx "\n",
128 mmu_idx, address);
129
130 env->ear = address;
131 switch (lu.err) {
132 case ERR_PROT:
133 env->esr = access_type == MMU_INST_FETCH ? 17 : 16;
134 env->esr |= (access_type == MMU_DATA_STORE) << 10;
135 break;
136 case ERR_MISS:
137 env->esr = access_type == MMU_INST_FETCH ? 19 : 18;
138 env->esr |= (access_type == MMU_DATA_STORE) << 10;
139 break;
140 default:
141 abort();
142 }
143
144 if (cs->exception_index == EXCP_MMU) {
145 cpu_abort(cs, "recursive faults\n");
146 }
147
148 /* TLB miss. */
149 cs->exception_index = EXCP_MMU;
150 cpu_loop_exit_restore(cs, retaddr);
151 }
152
153 void mb_cpu_do_interrupt(CPUState *cs)
154 {
155 MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs);
156 CPUMBState *env = &cpu->env;
157 uint32_t t, msr = mb_cpu_read_msr(env);
158 bool set_esr;
159 uint64_t last_pc = env->pc;
160
161 /* IMM flag cannot propagate across a branch and into the dslot. */
162 assert((env->iflags & (D_FLAG | IMM_FLAG)) != (D_FLAG | IMM_FLAG));
163 /* BIMM flag cannot be set without D_FLAG. */
164 assert((env->iflags & (D_FLAG | BIMM_FLAG)) != BIMM_FLAG);
165 /* RTI flags are private to translate. */
166 assert(!(env->iflags & (DRTI_FLAG | DRTE_FLAG | DRTB_FLAG)));
167
168 switch (cs->exception_index) {
169 case EXCP_HW_EXCP:
170 if (!(cpu->cfg.pvr_regs[0] & PVR0_USE_EXC_MASK)) {
171 qemu_log_mask(LOG_GUEST_ERROR,
172 "Exception raised on system without exceptions!\n");
173 return;
174 }
175
176 qemu_log_mask(CPU_LOG_INT,
177 "INT: HWE at pc=%08x msr=%08x iflags=%x\n",
178 env->pc, msr, env->iflags);
179
180 /* Exception breaks branch + dslot sequence? */
181 set_esr = true;
182 env->esr &= ~D_FLAG;
183 if (env->iflags & D_FLAG) {
184 env->esr |= D_FLAG;
185 env->btr = env->btarget;
186 }
187
188 /* Exception in progress. */
189 msr |= MSR_EIP;
190 env->regs[17] = env->pc + 4;
191 env->pc = cpu->cfg.base_vectors + 0x20;
192 break;
193
194 case EXCP_MMU:
195 qemu_log_mask(CPU_LOG_INT,
196 "INT: MMU at pc=%08x msr=%08x "
197 "ear=%" PRIx64 " iflags=%x\n",
198 env->pc, msr, env->ear, env->iflags);
199
200 /* Exception breaks branch + dslot sequence? */
201 set_esr = true;
202 env->esr &= ~D_FLAG;
203 if (env->iflags & D_FLAG) {
204 env->esr |= D_FLAG;
205 env->btr = env->btarget;
206 /* Reexecute the branch. */
207 env->regs[17] = env->pc - (env->iflags & BIMM_FLAG ? 8 : 4);
208 } else if (env->iflags & IMM_FLAG) {
209 /* Reexecute the imm. */
210 env->regs[17] = env->pc - 4;
211 } else {
212 env->regs[17] = env->pc;
213 }
214
215 /* Exception in progress. */
216 msr |= MSR_EIP;
217 env->pc = cpu->cfg.base_vectors + 0x20;
218 break;
219
220 case EXCP_IRQ:
221 assert(!(msr & (MSR_EIP | MSR_BIP)));
222 assert(msr & MSR_IE);
223 assert(!(env->iflags & (D_FLAG | IMM_FLAG)));
224
225 qemu_log_mask(CPU_LOG_INT,
226 "INT: DEV at pc=%08x msr=%08x iflags=%x\n",
227 env->pc, msr, env->iflags);
228 set_esr = false;
229
230 /* Disable interrupts. */
231 msr &= ~MSR_IE;
232 env->regs[14] = env->pc;
233 env->pc = cpu->cfg.base_vectors + 0x10;
234 break;
235
236 case EXCP_HW_BREAK:
237 assert(!(env->iflags & (D_FLAG | IMM_FLAG)));
238
239 qemu_log_mask(CPU_LOG_INT,
240 "INT: BRK at pc=%08x msr=%08x iflags=%x\n",
241 env->pc, msr, env->iflags);
242 set_esr = false;
243
244 /* Break in progress. */
245 msr |= MSR_BIP;
246 env->regs[16] = env->pc;
247 env->pc = cpu->cfg.base_vectors + 0x18;
248 break;
249
250 default:
251 cpu_abort(cs, "unhandled exception type=%d\n", cs->exception_index);
252 /* not reached */
253 }
254
255 /* Save previous mode, disable mmu, disable user-mode. */
256 t = (msr & (MSR_VM | MSR_UM)) << 1;
257 msr &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM);
258 msr |= t;
259 mb_cpu_write_msr(env, msr);
260
261 env->res_addr = RES_ADDR_NONE;
262 env->iflags = 0;
263
264 if (cs->exception_index == EXCP_IRQ) {
265 qemu_plugin_vcpu_interrupt_cb(cs, last_pc);
266 } else {
267 qemu_plugin_vcpu_exception_cb(cs, last_pc);
268 }
269
270 if (!set_esr) {
271 qemu_log_mask(CPU_LOG_INT,
272 " to pc=%08x msr=%08x\n", env->pc, msr);
273 } else if (env->esr & D_FLAG) {
274 qemu_log_mask(CPU_LOG_INT,
275 " to pc=%08x msr=%08x esr=%04x btr=%08x\n",
276 env->pc, msr, env->esr, env->btr);
277 } else {
278 qemu_log_mask(CPU_LOG_INT,
279 " to pc=%08x msr=%08x esr=%04x\n",
280 env->pc, msr, env->esr);
281 }
282 }
283
284 bool mb_cpu_translate_for_debug(CPUState *cs, vaddr addr,
285 TranslateForDebugResult *result)
286 {
287 MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs);
288 hwaddr paddr = 0;
289 MicroBlazeMMULookup lu;
290 int mmu_idx = cpu_mmu_index(cs, false);
291 unsigned int hit;
292
293 if (mmu_idx != MMU_NOMMU_IDX) {
294 hit = mmu_translate(cpu, &lu, addr, 0, 0);
295 if (hit) {
296 paddr = lu.paddr + addr - lu.vaddr;
297 } else
298 paddr = 0; /* ???. */
299 } else {
300 paddr = addr;
301 }
302
303 *result = (TranslateForDebugResult) {
304 .physaddr = paddr,
305 .lg_page_size = TARGET_PAGE_BITS,
306 .attrs.secure = mb_cpu_access_is_secure(cpu, MMU_DATA_LOAD),
307 .attrs.debug = 1,
308 };
309 return true;
310 }
311
312 bool mb_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
313 {
314 CPUMBState *env = cpu_env(cs);
315
316 if ((interrupt_request & CPU_INTERRUPT_HARD)
317 && (env->msr & MSR_IE)
318 && !(env->msr & (MSR_EIP | MSR_BIP))
319 && !(env->iflags & (D_FLAG | IMM_FLAG))) {
320 cs->exception_index = EXCP_IRQ;
321 mb_cpu_do_interrupt(cs);
322 return true;
323 }
324 return false;
325 }
326
327 #endif /* !CONFIG_USER_ONLY */