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