]> git.proxmox.com Git - qemu.git/blob - target-microblaze/helper.c
Merge remote-tracking branch 'afaerber/qom-cpu-lm32.v3' into staging
[qemu.git] / target-microblaze / helper.c
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 "cpu.h"
22 #include "host-utils.h"
23
24 #define D(x)
25 #define DMMU(x)
26
27 #if defined(CONFIG_USER_ONLY)
28
29 void do_interrupt (CPUMBState *env)
30 {
31 env->exception_index = -1;
32 env->regs[14] = env->sregs[SR_PC];
33 }
34
35 int cpu_mb_handle_mmu_fault(CPUMBState * env, target_ulong address, int rw,
36 int mmu_idx)
37 {
38 env->exception_index = 0xaa;
39 cpu_dump_state(env, stderr, fprintf, 0);
40 return 1;
41 }
42
43 #else /* !CONFIG_USER_ONLY */
44
45 int cpu_mb_handle_mmu_fault (CPUMBState *env, target_ulong address, int rw,
46 int mmu_idx)
47 {
48 unsigned int hit;
49 unsigned int mmu_available;
50 int r = 1;
51 int prot;
52
53 mmu_available = 0;
54 if (env->pvr.regs[0] & PVR0_USE_MMU) {
55 mmu_available = 1;
56 if ((env->pvr.regs[0] & PVR0_PVR_FULL_MASK)
57 && (env->pvr.regs[11] & PVR11_USE_MMU) != PVR11_USE_MMU) {
58 mmu_available = 0;
59 }
60 }
61
62 /* Translate if the MMU is available and enabled. */
63 if (mmu_available && (env->sregs[SR_MSR] & MSR_VM)) {
64 target_ulong vaddr, paddr;
65 struct microblaze_mmu_lookup lu;
66
67 hit = mmu_translate(&env->mmu, &lu, address, rw, mmu_idx);
68 if (hit) {
69 vaddr = address & TARGET_PAGE_MASK;
70 paddr = lu.paddr + vaddr - lu.vaddr;
71
72 DMMU(qemu_log("MMU map mmu=%d v=%x p=%x prot=%x\n",
73 mmu_idx, vaddr, paddr, lu.prot));
74 tlb_set_page(env, vaddr, paddr, lu.prot, mmu_idx, TARGET_PAGE_SIZE);
75 r = 0;
76 } else {
77 env->sregs[SR_EAR] = address;
78 DMMU(qemu_log("mmu=%d miss v=%x\n", mmu_idx, address));
79
80 switch (lu.err) {
81 case ERR_PROT:
82 env->sregs[SR_ESR] = rw == 2 ? 17 : 16;
83 env->sregs[SR_ESR] |= (rw == 1) << 10;
84 break;
85 case ERR_MISS:
86 env->sregs[SR_ESR] = rw == 2 ? 19 : 18;
87 env->sregs[SR_ESR] |= (rw == 1) << 10;
88 break;
89 default:
90 abort();
91 break;
92 }
93
94 if (env->exception_index == EXCP_MMU) {
95 cpu_abort(env, "recursive faults\n");
96 }
97
98 /* TLB miss. */
99 env->exception_index = EXCP_MMU;
100 }
101 } else {
102 /* MMU disabled or not available. */
103 address &= TARGET_PAGE_MASK;
104 prot = PAGE_BITS;
105 tlb_set_page(env, address, address, prot, mmu_idx, TARGET_PAGE_SIZE);
106 r = 0;
107 }
108 return r;
109 }
110
111 void do_interrupt(CPUMBState *env)
112 {
113 uint32_t t;
114
115 /* IMM flag cannot propagate across a branch and into the dslot. */
116 assert(!((env->iflags & D_FLAG) && (env->iflags & IMM_FLAG)));
117 assert(!(env->iflags & (DRTI_FLAG | DRTE_FLAG | DRTB_FLAG)));
118 /* assert(env->sregs[SR_MSR] & (MSR_EE)); Only for HW exceptions. */
119 switch (env->exception_index) {
120 case EXCP_HW_EXCP:
121 if (!(env->pvr.regs[0] & PVR0_USE_EXC_MASK)) {
122 qemu_log("Exception raised on system without exceptions!\n");
123 return;
124 }
125
126 env->regs[17] = env->sregs[SR_PC] + 4;
127 env->sregs[SR_ESR] &= ~(1 << 12);
128
129 /* Exception breaks branch + dslot sequence? */
130 if (env->iflags & D_FLAG) {
131 env->sregs[SR_ESR] |= 1 << 12 ;
132 env->sregs[SR_BTR] = env->btarget;
133 }
134
135 /* Disable the MMU. */
136 t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1;
137 env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM);
138 env->sregs[SR_MSR] |= t;
139 /* Exception in progress. */
140 env->sregs[SR_MSR] |= MSR_EIP;
141
142 qemu_log_mask(CPU_LOG_INT,
143 "hw exception at pc=%x ear=%x esr=%x iflags=%x\n",
144 env->sregs[SR_PC], env->sregs[SR_EAR],
145 env->sregs[SR_ESR], env->iflags);
146 log_cpu_state_mask(CPU_LOG_INT, env, 0);
147 env->iflags &= ~(IMM_FLAG | D_FLAG);
148 env->sregs[SR_PC] = 0x20;
149 break;
150
151 case EXCP_MMU:
152 env->regs[17] = env->sregs[SR_PC];
153
154 env->sregs[SR_ESR] &= ~(1 << 12);
155 /* Exception breaks branch + dslot sequence? */
156 if (env->iflags & D_FLAG) {
157 D(qemu_log("D_FLAG set at exception bimm=%d\n", env->bimm));
158 env->sregs[SR_ESR] |= 1 << 12 ;
159 env->sregs[SR_BTR] = env->btarget;
160
161 /* Reexecute the branch. */
162 env->regs[17] -= 4;
163 /* was the branch immprefixed?. */
164 if (env->bimm) {
165 qemu_log_mask(CPU_LOG_INT,
166 "bimm exception at pc=%x iflags=%x\n",
167 env->sregs[SR_PC], env->iflags);
168 env->regs[17] -= 4;
169 log_cpu_state_mask(CPU_LOG_INT, env, 0);
170 }
171 } else if (env->iflags & IMM_FLAG) {
172 D(qemu_log("IMM_FLAG set at exception\n"));
173 env->regs[17] -= 4;
174 }
175
176 /* Disable the MMU. */
177 t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1;
178 env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM);
179 env->sregs[SR_MSR] |= t;
180 /* Exception in progress. */
181 env->sregs[SR_MSR] |= MSR_EIP;
182
183 qemu_log_mask(CPU_LOG_INT,
184 "exception at pc=%x ear=%x iflags=%x\n",
185 env->sregs[SR_PC], env->sregs[SR_EAR], env->iflags);
186 log_cpu_state_mask(CPU_LOG_INT, env, 0);
187 env->iflags &= ~(IMM_FLAG | D_FLAG);
188 env->sregs[SR_PC] = 0x20;
189 break;
190
191 case EXCP_IRQ:
192 assert(!(env->sregs[SR_MSR] & (MSR_EIP | MSR_BIP)));
193 assert(env->sregs[SR_MSR] & MSR_IE);
194 assert(!(env->iflags & D_FLAG));
195
196 t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1;
197
198 #if 0
199 #include "disas.h"
200
201 /* Useful instrumentation when debugging interrupt issues in either
202 the models or in sw. */
203 {
204 const char *sym;
205
206 sym = lookup_symbol(env->sregs[SR_PC]);
207 if (sym
208 && (!strcmp("netif_rx", sym)
209 || !strcmp("process_backlog", sym))) {
210
211 qemu_log(
212 "interrupt at pc=%x msr=%x %x iflags=%x sym=%s\n",
213 env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags,
214 sym);
215
216 log_cpu_state(env, 0);
217 }
218 }
219 #endif
220 qemu_log_mask(CPU_LOG_INT,
221 "interrupt at pc=%x msr=%x %x iflags=%x\n",
222 env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags);
223
224 env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM \
225 | MSR_UM | MSR_IE);
226 env->sregs[SR_MSR] |= t;
227
228 env->regs[14] = env->sregs[SR_PC];
229 env->sregs[SR_PC] = 0x10;
230 //log_cpu_state_mask(CPU_LOG_INT, env, 0);
231 break;
232
233 case EXCP_BREAK:
234 case EXCP_HW_BREAK:
235 assert(!(env->iflags & IMM_FLAG));
236 assert(!(env->iflags & D_FLAG));
237 t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1;
238 qemu_log_mask(CPU_LOG_INT,
239 "break at pc=%x msr=%x %x iflags=%x\n",
240 env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags);
241 log_cpu_state_mask(CPU_LOG_INT, env, 0);
242 env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM);
243 env->sregs[SR_MSR] |= t;
244 env->sregs[SR_MSR] |= MSR_BIP;
245 if (env->exception_index == EXCP_HW_BREAK) {
246 env->regs[16] = env->sregs[SR_PC];
247 env->sregs[SR_MSR] |= MSR_BIP;
248 env->sregs[SR_PC] = 0x18;
249 } else
250 env->sregs[SR_PC] = env->btarget;
251 break;
252 default:
253 cpu_abort(env, "unhandled exception type=%d\n",
254 env->exception_index);
255 break;
256 }
257 }
258
259 target_phys_addr_t cpu_get_phys_page_debug(CPUMBState * env, target_ulong addr)
260 {
261 target_ulong vaddr, paddr = 0;
262 struct microblaze_mmu_lookup lu;
263 unsigned int hit;
264
265 if (env->sregs[SR_MSR] & MSR_VM) {
266 hit = mmu_translate(&env->mmu, &lu, addr, 0, 0);
267 if (hit) {
268 vaddr = addr & TARGET_PAGE_MASK;
269 paddr = lu.paddr + vaddr - lu.vaddr;
270 } else
271 paddr = 0; /* ???. */
272 } else
273 paddr = addr & TARGET_PAGE_MASK;
274
275 return paddr;
276 }
277 #endif