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