OSDN Git Service

plugins: update lockstep to use g_memdup2
[qmiga/qemu.git] / target / nios2 / helper.c
1 /*
2  * Altera Nios II helper routines.
3  *
4  * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see
18  * <http://www.gnu.org/licenses/lgpl-2.1.html>
19  */
20
21 #include "qemu/osdep.h"
22
23 #include "cpu.h"
24 #include "qemu/host-utils.h"
25 #include "exec/exec-all.h"
26 #include "exec/cpu_ldst.h"
27 #include "exec/log.h"
28 #include "exec/helper-proto.h"
29 #include "semihosting/semihost.h"
30
31
32 static void do_exception(Nios2CPU *cpu, uint32_t exception_addr,
33                          uint32_t tlbmisc_set, bool is_break)
34 {
35     CPUNios2State *env = &cpu->env;
36     CPUState *cs = CPU(cpu);
37     uint32_t old_status = env->ctrl[CR_STATUS];
38     uint32_t new_status = old_status;
39
40     /* With shadow regs, exceptions are always taken into CRS 0. */
41     new_status &= ~R_CR_STATUS_CRS_MASK;
42     env->regs = env->shadow_regs[0];
43
44     if ((old_status & CR_STATUS_EH) == 0) {
45         int r_ea = R_EA, cr_es = CR_ESTATUS;
46
47         if (is_break) {
48             r_ea = R_BA;
49             cr_es = CR_BSTATUS;
50         }
51         env->ctrl[cr_es] = old_status;
52         env->regs[r_ea] = env->pc;
53
54         if (cpu->mmu_present) {
55             new_status |= CR_STATUS_EH;
56
57             /*
58              * There are 4 bits that are always written.
59              * Explicitly clear them, to be set via the argument.
60              */
61             env->ctrl[CR_TLBMISC] &= ~(CR_TLBMISC_D |
62                                        CR_TLBMISC_PERM |
63                                        CR_TLBMISC_BAD |
64                                        CR_TLBMISC_DBL);
65             env->ctrl[CR_TLBMISC] |= tlbmisc_set;
66         }
67
68         /*
69          * With shadow regs, and EH == 0, PRS is set from CRS.
70          * At least, so says Table 3-9, and some other text,
71          * though Table 3-38 says otherwise.
72          */
73         new_status = FIELD_DP32(new_status, CR_STATUS, PRS,
74                                 FIELD_EX32(old_status, CR_STATUS, CRS));
75     }
76
77     new_status &= ~(CR_STATUS_PIE | CR_STATUS_U);
78
79     env->ctrl[CR_STATUS] = new_status;
80     if (!is_break) {
81         env->ctrl[CR_EXCEPTION] = FIELD_DP32(0, CR_EXCEPTION, CAUSE,
82                                              cs->exception_index);
83     }
84     env->pc = exception_addr;
85 }
86
87 static void do_iic_irq(Nios2CPU *cpu)
88 {
89     do_exception(cpu, cpu->exception_addr, 0, false);
90 }
91
92 static void do_eic_irq(Nios2CPU *cpu)
93 {
94     CPUNios2State *env = &cpu->env;
95     uint32_t old_status = env->ctrl[CR_STATUS];
96     uint32_t new_status = old_status;
97     uint32_t old_rs = FIELD_EX32(old_status, CR_STATUS, CRS);
98     uint32_t new_rs = cpu->rrs;
99
100     new_status = FIELD_DP32(new_status, CR_STATUS, CRS, new_rs);
101     new_status = FIELD_DP32(new_status, CR_STATUS, IL, cpu->ril);
102     new_status = FIELD_DP32(new_status, CR_STATUS, NMI, cpu->rnmi);
103     new_status &= ~(CR_STATUS_RSIE | CR_STATUS_U);
104     new_status |= CR_STATUS_IH;
105
106     if (!(new_status & CR_STATUS_EH)) {
107         new_status = FIELD_DP32(new_status, CR_STATUS, PRS, old_rs);
108         if (new_rs == 0) {
109             env->ctrl[CR_ESTATUS] = old_status;
110         } else {
111             if (new_rs != old_rs) {
112                 old_status |= CR_STATUS_SRS;
113             }
114             env->shadow_regs[new_rs][R_SSTATUS] = old_status;
115         }
116         env->shadow_regs[new_rs][R_EA] = env->pc;
117     }
118
119     env->ctrl[CR_STATUS] = new_status;
120     nios2_update_crs(env);
121
122     env->pc = cpu->rha;
123 }
124
125 void nios2_cpu_do_interrupt(CPUState *cs)
126 {
127     Nios2CPU *cpu = NIOS2_CPU(cs);
128     CPUNios2State *env = &cpu->env;
129     uint32_t tlbmisc_set = 0;
130
131     if (qemu_loglevel_mask(CPU_LOG_INT)) {
132         const char *name = NULL;
133
134         switch (cs->exception_index) {
135         case EXCP_IRQ:
136             name = "interrupt";
137             break;
138         case EXCP_TLB_X:
139         case EXCP_TLB_D:
140             if (env->ctrl[CR_STATUS] & CR_STATUS_EH) {
141                 name = "TLB MISS (double)";
142             } else {
143                 name = "TLB MISS (fast)";
144             }
145             break;
146         case EXCP_PERM_R:
147         case EXCP_PERM_W:
148         case EXCP_PERM_X:
149             name = "TLB PERM";
150             break;
151         case EXCP_SUPERA_X:
152         case EXCP_SUPERA_D:
153             name = "SUPERVISOR (address)";
154             break;
155         case EXCP_SUPERI:
156             name = "SUPERVISOR (insn)";
157             break;
158         case EXCP_ILLEGAL:
159             name = "ILLEGAL insn";
160             break;
161         case EXCP_UNALIGN:
162             name = "Misaligned (data)";
163             break;
164         case EXCP_UNALIGND:
165             name = "Misaligned (destination)";
166             break;
167         case EXCP_DIV:
168             name = "DIV error";
169             break;
170         case EXCP_TRAP:
171             name = "TRAP insn";
172             break;
173         case EXCP_BREAK:
174             name = "BREAK insn";
175             break;
176         case EXCP_SEMIHOST:
177             name = "SEMIHOST insn";
178             break;
179         }
180         if (name) {
181             qemu_log("%s at pc=0x%08x\n", name, env->pc);
182         } else {
183             qemu_log("Unknown exception %d at pc=0x%08x\n",
184                      cs->exception_index, env->pc);
185         }
186     }
187
188     switch (cs->exception_index) {
189     case EXCP_IRQ:
190         /* Note that PC is advanced for interrupts as well. */
191         env->pc += 4;
192         if (cpu->eic_present) {
193             do_eic_irq(cpu);
194         } else {
195             do_iic_irq(cpu);
196         }
197         break;
198
199     case EXCP_TLB_D:
200         tlbmisc_set = CR_TLBMISC_D;
201         /* fall through */
202     case EXCP_TLB_X:
203         if (env->ctrl[CR_STATUS] & CR_STATUS_EH) {
204             tlbmisc_set |= CR_TLBMISC_DBL;
205             /*
206              * Normally, we don't write to tlbmisc unless !EH,
207              * so do it manually for the double-tlb miss exception.
208              */
209             env->ctrl[CR_TLBMISC] &= ~(CR_TLBMISC_D |
210                                        CR_TLBMISC_PERM |
211                                        CR_TLBMISC_BAD);
212             env->ctrl[CR_TLBMISC] |= tlbmisc_set;
213             do_exception(cpu, cpu->exception_addr, 0, false);
214         } else {
215             tlbmisc_set |= CR_TLBMISC_WE;
216             do_exception(cpu, cpu->fast_tlb_miss_addr, tlbmisc_set, false);
217         }
218         break;
219
220     case EXCP_PERM_R:
221     case EXCP_PERM_W:
222         tlbmisc_set = CR_TLBMISC_D;
223         /* fall through */
224     case EXCP_PERM_X:
225         tlbmisc_set |= CR_TLBMISC_PERM;
226         if (!(env->ctrl[CR_STATUS] & CR_STATUS_EH)) {
227             tlbmisc_set |= CR_TLBMISC_WE;
228         }
229         do_exception(cpu, cpu->exception_addr, tlbmisc_set, false);
230         break;
231
232     case EXCP_SUPERA_D:
233     case EXCP_UNALIGN:
234         tlbmisc_set = CR_TLBMISC_D;
235         /* fall through */
236     case EXCP_SUPERA_X:
237     case EXCP_UNALIGND:
238         tlbmisc_set |= CR_TLBMISC_BAD;
239         do_exception(cpu, cpu->exception_addr, tlbmisc_set, false);
240         break;
241
242     case EXCP_SUPERI:
243     case EXCP_ILLEGAL:
244     case EXCP_DIV:
245     case EXCP_TRAP:
246         do_exception(cpu, cpu->exception_addr, 0, false);
247         break;
248
249     case EXCP_BREAK:
250         do_exception(cpu, cpu->exception_addr, 0, true);
251         break;
252
253     case EXCP_SEMIHOST:
254         do_nios2_semihosting(env);
255         break;
256
257     default:
258         cpu_abort(cs, "unhandled exception type=%d\n", cs->exception_index);
259     }
260 }
261
262 hwaddr nios2_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
263 {
264     Nios2CPU *cpu = NIOS2_CPU(cs);
265     CPUNios2State *env = &cpu->env;
266     target_ulong vaddr, paddr = 0;
267     Nios2MMULookup lu;
268     unsigned int hit;
269
270     if (cpu->mmu_present && (addr < 0xC0000000)) {
271         hit = mmu_translate(env, &lu, addr, 0, 0);
272         if (hit) {
273             vaddr = addr & TARGET_PAGE_MASK;
274             paddr = lu.paddr + vaddr - lu.vaddr;
275         } else {
276             paddr = -1;
277             qemu_log("cpu_get_phys_page debug MISS: %#" PRIx64 "\n", addr);
278         }
279     } else {
280         paddr = addr & TARGET_PAGE_MASK;
281     }
282
283     return paddr;
284 }
285
286 void nios2_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
287                                    MMUAccessType access_type,
288                                    int mmu_idx, uintptr_t retaddr)
289 {
290     Nios2CPU *cpu = NIOS2_CPU(cs);
291     CPUNios2State *env = &cpu->env;
292
293     env->ctrl[CR_BADADDR] = addr;
294     cs->exception_index = EXCP_UNALIGN;
295     nios2_cpu_loop_exit_advance(env, retaddr);
296 }
297
298 bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
299                         MMUAccessType access_type, int mmu_idx,
300                         bool probe, uintptr_t retaddr)
301 {
302     Nios2CPU *cpu = NIOS2_CPU(cs);
303     CPUNios2State *env = &cpu->env;
304     unsigned int excp;
305     target_ulong vaddr, paddr;
306     Nios2MMULookup lu;
307     unsigned int hit;
308
309     if (!cpu->mmu_present) {
310         /* No MMU */
311         address &= TARGET_PAGE_MASK;
312         tlb_set_page(cs, address, address, PAGE_BITS,
313                      mmu_idx, TARGET_PAGE_SIZE);
314         return true;
315     }
316
317     if (MMU_SUPERVISOR_IDX == mmu_idx) {
318         if (address >= 0xC0000000) {
319             /* Kernel physical page - TLB bypassed */
320             address &= TARGET_PAGE_MASK;
321             tlb_set_page(cs, address, address, PAGE_BITS,
322                          mmu_idx, TARGET_PAGE_SIZE);
323             return true;
324         }
325     } else {
326         if (address >= 0x80000000) {
327             /* Illegal access from user mode */
328             if (probe) {
329                 return false;
330             }
331             cs->exception_index = (access_type == MMU_INST_FETCH
332                                    ? EXCP_SUPERA_X : EXCP_SUPERA_D);
333             env->ctrl[CR_BADADDR] = address;
334             nios2_cpu_loop_exit_advance(env, retaddr);
335         }
336     }
337
338     /* Virtual page.  */
339     hit = mmu_translate(env, &lu, address, access_type, mmu_idx);
340     if (hit) {
341         vaddr = address & TARGET_PAGE_MASK;
342         paddr = lu.paddr + vaddr - lu.vaddr;
343
344         if (((access_type == MMU_DATA_LOAD) && (lu.prot & PAGE_READ)) ||
345             ((access_type == MMU_DATA_STORE) && (lu.prot & PAGE_WRITE)) ||
346             ((access_type == MMU_INST_FETCH) && (lu.prot & PAGE_EXEC))) {
347             tlb_set_page(cs, vaddr, paddr, lu.prot,
348                          mmu_idx, TARGET_PAGE_SIZE);
349             return true;
350         }
351
352         /* Permission violation */
353         excp = (access_type == MMU_DATA_LOAD ? EXCP_PERM_R :
354                 access_type == MMU_DATA_STORE ? EXCP_PERM_W : EXCP_PERM_X);
355     } else {
356         excp = (access_type == MMU_INST_FETCH ? EXCP_TLB_X: EXCP_TLB_D);
357     }
358
359     if (probe) {
360         return false;
361     }
362
363     env->ctrl[CR_TLBMISC] = FIELD_DP32(env->ctrl[CR_TLBMISC], CR_TLBMISC, D,
364                                        access_type != MMU_INST_FETCH);
365     env->ctrl[CR_PTEADDR] = FIELD_DP32(env->ctrl[CR_PTEADDR], CR_PTEADDR, VPN,
366                                        address >> TARGET_PAGE_BITS);
367     env->mmu.pteaddr_wr = env->ctrl[CR_PTEADDR];
368
369     cs->exception_index = excp;
370     env->ctrl[CR_BADADDR] = address;
371     nios2_cpu_loop_exit_advance(env, retaddr);
372 }