xref: /openbmc/qemu/target/nios2/helper.c (revision 99d46107)
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/log.h"
27 #include "exec/helper-proto.h"
28 
29 #if defined(CONFIG_USER_ONLY)
30 
31 void nios2_cpu_do_interrupt(CPUState *cs)
32 {
33     Nios2CPU *cpu = NIOS2_CPU(cs);
34     CPUNios2State *env = &cpu->env;
35     cs->exception_index = -1;
36     env->regs[R_EA] = env->regs[R_PC] + 4;
37 }
38 
39 int nios2_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size,
40                                int rw, int mmu_idx)
41 {
42     cs->exception_index = 0xaa;
43     /* Page 0x1000 is kuser helper */
44     if (address < 0x1000 || address >= 0x2000) {
45         cpu_dump_state(cs, stderr, fprintf, 0);
46     }
47     return 1;
48 }
49 
50 #else /* !CONFIG_USER_ONLY */
51 
52 void nios2_cpu_do_interrupt(CPUState *cs)
53 {
54     Nios2CPU *cpu = NIOS2_CPU(cs);
55     CPUNios2State *env = &cpu->env;
56 
57     switch (cs->exception_index) {
58     case EXCP_IRQ:
59         assert(env->regs[CR_STATUS] & CR_STATUS_PIE);
60 
61         qemu_log_mask(CPU_LOG_INT, "interrupt at pc=%x\n", env->regs[R_PC]);
62 
63         env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
64         env->regs[CR_STATUS] |= CR_STATUS_IH;
65         env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
66 
67         env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
68         env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
69 
70         env->regs[R_EA] = env->regs[R_PC] + 4;
71         env->regs[R_PC] = cpu->exception_addr;
72         break;
73 
74     case EXCP_TLBD:
75         if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
76             qemu_log_mask(CPU_LOG_INT, "TLB MISS (fast) at pc=%x\n",
77                           env->regs[R_PC]);
78 
79             /* Fast TLB miss */
80             /* Variation from the spec. Table 3-35 of the cpu reference shows
81              * estatus not being changed for TLB miss but this appears to
82              * be incorrect. */
83             env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
84             env->regs[CR_STATUS] |= CR_STATUS_EH;
85             env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
86 
87             env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
88             env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
89 
90             env->regs[CR_TLBMISC] &= ~CR_TLBMISC_DBL;
91             env->regs[CR_TLBMISC] |= CR_TLBMISC_WR;
92 
93             env->regs[R_EA] = env->regs[R_PC] + 4;
94             env->regs[R_PC] = cpu->fast_tlb_miss_addr;
95         } else {
96             qemu_log_mask(CPU_LOG_INT, "TLB MISS (double) at pc=%x\n",
97                           env->regs[R_PC]);
98 
99             /* Double TLB miss */
100             env->regs[CR_STATUS] |= CR_STATUS_EH;
101             env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
102 
103             env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
104             env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
105 
106             env->regs[CR_TLBMISC] |= CR_TLBMISC_DBL;
107 
108             env->regs[R_PC] = cpu->exception_addr;
109         }
110         break;
111 
112     case EXCP_TLBR:
113     case EXCP_TLBW:
114     case EXCP_TLBX:
115         qemu_log_mask(CPU_LOG_INT, "TLB PERM at pc=%x\n", env->regs[R_PC]);
116 
117         env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
118         env->regs[CR_STATUS] |= CR_STATUS_EH;
119         env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
120 
121         env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
122         env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
123 
124         if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
125             env->regs[CR_TLBMISC] |= CR_TLBMISC_WR;
126         }
127 
128         env->regs[R_EA] = env->regs[R_PC] + 4;
129         env->regs[R_PC] = cpu->exception_addr;
130         break;
131 
132     case EXCP_SUPERA:
133     case EXCP_SUPERI:
134     case EXCP_SUPERD:
135         qemu_log_mask(CPU_LOG_INT, "SUPERVISOR exception at pc=%x\n",
136                       env->regs[R_PC]);
137 
138         if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
139             env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
140             env->regs[R_EA] = env->regs[R_PC] + 4;
141         }
142 
143         env->regs[CR_STATUS] |= CR_STATUS_EH;
144         env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
145 
146         env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
147         env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
148 
149         env->regs[R_PC] = cpu->exception_addr;
150         break;
151 
152     case EXCP_ILLEGAL:
153     case EXCP_TRAP:
154         qemu_log_mask(CPU_LOG_INT, "TRAP exception at pc=%x\n",
155                       env->regs[R_PC]);
156 
157         if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
158             env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
159             env->regs[R_EA] = env->regs[R_PC] + 4;
160         }
161 
162         env->regs[CR_STATUS] |= CR_STATUS_EH;
163         env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
164 
165         env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
166         env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
167 
168         env->regs[R_PC] = cpu->exception_addr;
169         break;
170 
171     case EXCP_BREAK:
172         if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
173             env->regs[CR_BSTATUS] = env->regs[CR_STATUS];
174             env->regs[R_BA] = env->regs[R_PC] + 4;
175         }
176 
177         env->regs[CR_STATUS] |= CR_STATUS_EH;
178         env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
179 
180         env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
181         env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
182 
183         env->regs[R_PC] = cpu->exception_addr;
184         break;
185 
186     default:
187         cpu_abort(cs, "unhandled exception type=%d\n",
188                   cs->exception_index);
189         break;
190     }
191 }
192 
193 static int cpu_nios2_handle_virtual_page(
194     CPUState *cs, target_ulong address, int rw, int mmu_idx)
195 {
196     Nios2CPU *cpu = NIOS2_CPU(cs);
197     CPUNios2State *env = &cpu->env;
198     target_ulong vaddr, paddr;
199     Nios2MMULookup lu;
200     unsigned int hit;
201     hit = mmu_translate(env, &lu, address, rw, mmu_idx);
202     if (hit) {
203         vaddr = address & TARGET_PAGE_MASK;
204         paddr = lu.paddr + vaddr - lu.vaddr;
205 
206         if (((rw == 0) && (lu.prot & PAGE_READ)) ||
207             ((rw == 1) && (lu.prot & PAGE_WRITE)) ||
208             ((rw == 2) && (lu.prot & PAGE_EXEC))) {
209 
210             tlb_set_page(cs, vaddr, paddr, lu.prot,
211                          mmu_idx, TARGET_PAGE_SIZE);
212             return 0;
213         } else {
214             /* Permission violation */
215             cs->exception_index = (rw == 0) ? EXCP_TLBR :
216                                                ((rw == 1) ? EXCP_TLBW :
217                                                             EXCP_TLBX);
218         }
219     } else {
220         cs->exception_index = EXCP_TLBD;
221     }
222 
223     if (rw == 2) {
224         env->regs[CR_TLBMISC] &= ~CR_TLBMISC_D;
225     } else {
226         env->regs[CR_TLBMISC] |= CR_TLBMISC_D;
227     }
228     env->regs[CR_PTEADDR] &= CR_PTEADDR_PTBASE_MASK;
229     env->regs[CR_PTEADDR] |= (address >> 10) & CR_PTEADDR_VPN_MASK;
230     env->mmu.pteaddr_wr = env->regs[CR_PTEADDR];
231     env->regs[CR_BADADDR] = address;
232     return 1;
233 }
234 
235 int nios2_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size,
236                                int rw, int mmu_idx)
237 {
238     Nios2CPU *cpu = NIOS2_CPU(cs);
239     CPUNios2State *env = &cpu->env;
240 
241     if (cpu->mmu_present) {
242         if (MMU_SUPERVISOR_IDX == mmu_idx) {
243             if (address >= 0xC0000000) {
244                 /* Kernel physical page - TLB bypassed */
245                 address &= TARGET_PAGE_MASK;
246                 tlb_set_page(cs, address, address, PAGE_BITS,
247                              mmu_idx, TARGET_PAGE_SIZE);
248             } else if (address >= 0x80000000) {
249                 /* Kernel virtual page */
250                 return cpu_nios2_handle_virtual_page(cs, address, rw, mmu_idx);
251             } else {
252                 /* User virtual page */
253                 return cpu_nios2_handle_virtual_page(cs, address, rw, mmu_idx);
254             }
255         } else {
256             if (address >= 0x80000000) {
257                 /* Illegal access from user mode */
258                 cs->exception_index = EXCP_SUPERA;
259                 env->regs[CR_BADADDR] = address;
260                 return 1;
261             } else {
262                 /* User virtual page */
263                 return cpu_nios2_handle_virtual_page(cs, address, rw, mmu_idx);
264             }
265         }
266     } else {
267         /* No MMU */
268         address &= TARGET_PAGE_MASK;
269         tlb_set_page(cs, address, address, PAGE_BITS,
270                      mmu_idx, TARGET_PAGE_SIZE);
271     }
272 
273     return 0;
274 }
275 
276 hwaddr nios2_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
277 {
278     Nios2CPU *cpu = NIOS2_CPU(cs);
279     CPUNios2State *env = &cpu->env;
280     target_ulong vaddr, paddr = 0;
281     Nios2MMULookup lu;
282     unsigned int hit;
283 
284     if (cpu->mmu_present && (addr < 0xC0000000)) {
285         hit = mmu_translate(env, &lu, addr, 0, 0);
286         if (hit) {
287             vaddr = addr & TARGET_PAGE_MASK;
288             paddr = lu.paddr + vaddr - lu.vaddr;
289         } else {
290             paddr = -1;
291             qemu_log("cpu_get_phys_page debug MISS: %#" PRIx64 "\n", addr);
292         }
293     } else {
294         paddr = addr & TARGET_PAGE_MASK;
295     }
296 
297     return paddr;
298 }
299 
300 void nios2_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
301                                    MMUAccessType access_type,
302                                    int mmu_idx, uintptr_t retaddr)
303 {
304     Nios2CPU *cpu = NIOS2_CPU(cs);
305     CPUNios2State *env = &cpu->env;
306 
307     env->regs[CR_BADADDR] = addr;
308     env->regs[CR_EXCEPTION] = EXCP_UNALIGN << 2;
309     helper_raise_exception(env, EXCP_UNALIGN);
310 }
311 #endif /* !CONFIG_USER_ONLY */
312