xref: /openbmc/qemu/target/microblaze/helper.c (revision 6b8f9c6e)
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