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