target/nios2: Use hw/registerfields.h for CR_EXCEPTION fields
[qemu.git] / target / nios2 / helper.c
blobb30740824c5a38a82ae91bbd2381d56c684ab291
1 /*
2 * Altera Nios II helper routines.
4 * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
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.
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.
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>
21 #include "qemu/osdep.h"
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"
32 void nios2_cpu_do_interrupt(CPUState *cs)
34 Nios2CPU *cpu = NIOS2_CPU(cs);
35 CPUNios2State *env = &cpu->env;
37 switch (cs->exception_index) {
38 case EXCP_IRQ:
39 assert(env->ctrl[CR_STATUS] & CR_STATUS_PIE);
41 qemu_log_mask(CPU_LOG_INT, "interrupt at pc=%x\n", env->pc);
43 env->ctrl[CR_ESTATUS] = env->ctrl[CR_STATUS];
44 env->ctrl[CR_STATUS] |= CR_STATUS_IH;
45 env->ctrl[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
47 env->ctrl[CR_EXCEPTION] = FIELD_DP32(env->ctrl[CR_EXCEPTION],
48 CR_EXCEPTION, CAUSE,
49 cs->exception_index);
51 env->regs[R_EA] = env->pc + 4;
52 env->pc = cpu->exception_addr;
53 break;
55 case EXCP_TLBD:
56 if ((env->ctrl[CR_STATUS] & CR_STATUS_EH) == 0) {
57 qemu_log_mask(CPU_LOG_INT, "TLB MISS (fast) at pc=%x\n", env->pc);
59 /* Fast TLB miss */
60 /* Variation from the spec. Table 3-35 of the cpu reference shows
61 * estatus not being changed for TLB miss but this appears to
62 * be incorrect. */
63 env->ctrl[CR_ESTATUS] = env->ctrl[CR_STATUS];
64 env->ctrl[CR_STATUS] |= CR_STATUS_EH;
65 env->ctrl[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
67 env->ctrl[CR_EXCEPTION] = FIELD_DP32(env->ctrl[CR_EXCEPTION],
68 CR_EXCEPTION, CAUSE,
69 cs->exception_index);
71 env->ctrl[CR_TLBMISC] &= ~CR_TLBMISC_DBL;
72 env->ctrl[CR_TLBMISC] |= CR_TLBMISC_WR;
74 env->regs[R_EA] = env->pc + 4;
75 env->pc = cpu->fast_tlb_miss_addr;
76 } else {
77 qemu_log_mask(CPU_LOG_INT, "TLB MISS (double) at pc=%x\n", env->pc);
79 /* Double TLB miss */
80 env->ctrl[CR_STATUS] |= CR_STATUS_EH;
81 env->ctrl[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
83 env->ctrl[CR_EXCEPTION] = FIELD_DP32(env->ctrl[CR_EXCEPTION],
84 CR_EXCEPTION, CAUSE,
85 cs->exception_index);
87 env->ctrl[CR_TLBMISC] |= CR_TLBMISC_DBL;
89 env->pc = cpu->exception_addr;
91 break;
93 case EXCP_TLBR:
94 case EXCP_TLBW:
95 case EXCP_TLBX:
96 qemu_log_mask(CPU_LOG_INT, "TLB PERM at pc=%x\n", env->pc);
98 env->ctrl[CR_ESTATUS] = env->ctrl[CR_STATUS];
99 env->ctrl[CR_STATUS] |= CR_STATUS_EH;
100 env->ctrl[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
102 env->ctrl[CR_EXCEPTION] = FIELD_DP32(env->ctrl[CR_EXCEPTION],
103 CR_EXCEPTION, CAUSE,
104 cs->exception_index);
106 if ((env->ctrl[CR_STATUS] & CR_STATUS_EH) == 0) {
107 env->ctrl[CR_TLBMISC] |= CR_TLBMISC_WR;
110 env->regs[R_EA] = env->pc + 4;
111 env->pc = cpu->exception_addr;
112 break;
114 case EXCP_SUPERA:
115 case EXCP_SUPERI:
116 case EXCP_SUPERD:
117 qemu_log_mask(CPU_LOG_INT, "SUPERVISOR exception at pc=%x\n", env->pc);
119 if ((env->ctrl[CR_STATUS] & CR_STATUS_EH) == 0) {
120 env->ctrl[CR_ESTATUS] = env->ctrl[CR_STATUS];
121 env->regs[R_EA] = env->pc + 4;
124 env->ctrl[CR_STATUS] |= CR_STATUS_EH;
125 env->ctrl[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
127 env->ctrl[CR_EXCEPTION] = FIELD_DP32(env->ctrl[CR_EXCEPTION],
128 CR_EXCEPTION, CAUSE,
129 cs->exception_index);
131 env->pc = cpu->exception_addr;
132 break;
134 case EXCP_ILLEGAL:
135 case EXCP_TRAP:
136 qemu_log_mask(CPU_LOG_INT, "TRAP exception at pc=%x\n", env->pc);
138 if ((env->ctrl[CR_STATUS] & CR_STATUS_EH) == 0) {
139 env->ctrl[CR_ESTATUS] = env->ctrl[CR_STATUS];
140 env->regs[R_EA] = env->pc + 4;
143 env->ctrl[CR_STATUS] |= CR_STATUS_EH;
144 env->ctrl[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
146 env->ctrl[CR_EXCEPTION] = FIELD_DP32(env->ctrl[CR_EXCEPTION],
147 CR_EXCEPTION, CAUSE,
148 cs->exception_index);
150 env->pc = cpu->exception_addr;
151 break;
153 case EXCP_BREAK:
154 qemu_log_mask(CPU_LOG_INT, "BREAK exception at pc=%x\n", env->pc);
155 /* The semihosting instruction is "break 1". */
156 if (semihosting_enabled() &&
157 cpu_ldl_code(env, env->pc) == 0x003da07a) {
158 qemu_log_mask(CPU_LOG_INT, "Entering semihosting\n");
159 env->pc += 4;
160 do_nios2_semihosting(env);
161 break;
164 if ((env->ctrl[CR_STATUS] & CR_STATUS_EH) == 0) {
165 env->ctrl[CR_BSTATUS] = env->ctrl[CR_STATUS];
166 env->regs[R_BA] = env->pc + 4;
169 env->ctrl[CR_STATUS] |= CR_STATUS_EH;
170 env->ctrl[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
172 env->ctrl[CR_EXCEPTION] = FIELD_DP32(env->ctrl[CR_EXCEPTION],
173 CR_EXCEPTION, CAUSE,
174 cs->exception_index);
176 env->pc = cpu->exception_addr;
177 break;
179 default:
180 cpu_abort(cs, "unhandled exception type=%d\n",
181 cs->exception_index);
182 break;
186 hwaddr nios2_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
188 Nios2CPU *cpu = NIOS2_CPU(cs);
189 CPUNios2State *env = &cpu->env;
190 target_ulong vaddr, paddr = 0;
191 Nios2MMULookup lu;
192 unsigned int hit;
194 if (cpu->mmu_present && (addr < 0xC0000000)) {
195 hit = mmu_translate(env, &lu, addr, 0, 0);
196 if (hit) {
197 vaddr = addr & TARGET_PAGE_MASK;
198 paddr = lu.paddr + vaddr - lu.vaddr;
199 } else {
200 paddr = -1;
201 qemu_log("cpu_get_phys_page debug MISS: %#" PRIx64 "\n", addr);
203 } else {
204 paddr = addr & TARGET_PAGE_MASK;
207 return paddr;
210 void nios2_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
211 MMUAccessType access_type,
212 int mmu_idx, uintptr_t retaddr)
214 Nios2CPU *cpu = NIOS2_CPU(cs);
215 CPUNios2State *env = &cpu->env;
217 env->ctrl[CR_BADADDR] = addr;
218 env->ctrl[CR_EXCEPTION] = FIELD_DP32(0, CR_EXCEPTION, CAUSE, EXCP_UNALIGN);
219 helper_raise_exception(env, EXCP_UNALIGN);
222 bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
223 MMUAccessType access_type, int mmu_idx,
224 bool probe, uintptr_t retaddr)
226 Nios2CPU *cpu = NIOS2_CPU(cs);
227 CPUNios2State *env = &cpu->env;
228 unsigned int excp = EXCP_TLBD;
229 target_ulong vaddr, paddr;
230 Nios2MMULookup lu;
231 unsigned int hit;
233 if (!cpu->mmu_present) {
234 /* No MMU */
235 address &= TARGET_PAGE_MASK;
236 tlb_set_page(cs, address, address, PAGE_BITS,
237 mmu_idx, TARGET_PAGE_SIZE);
238 return true;
241 if (MMU_SUPERVISOR_IDX == mmu_idx) {
242 if (address >= 0xC0000000) {
243 /* Kernel physical page - TLB bypassed */
244 address &= TARGET_PAGE_MASK;
245 tlb_set_page(cs, address, address, PAGE_BITS,
246 mmu_idx, TARGET_PAGE_SIZE);
247 return true;
249 } else {
250 if (address >= 0x80000000) {
251 /* Illegal access from user mode */
252 if (probe) {
253 return false;
255 cs->exception_index = EXCP_SUPERA;
256 env->ctrl[CR_BADADDR] = address;
257 cpu_loop_exit_restore(cs, retaddr);
261 /* Virtual page. */
262 hit = mmu_translate(env, &lu, address, access_type, mmu_idx);
263 if (hit) {
264 vaddr = address & TARGET_PAGE_MASK;
265 paddr = lu.paddr + vaddr - lu.vaddr;
267 if (((access_type == MMU_DATA_LOAD) && (lu.prot & PAGE_READ)) ||
268 ((access_type == MMU_DATA_STORE) && (lu.prot & PAGE_WRITE)) ||
269 ((access_type == MMU_INST_FETCH) && (lu.prot & PAGE_EXEC))) {
270 tlb_set_page(cs, vaddr, paddr, lu.prot,
271 mmu_idx, TARGET_PAGE_SIZE);
272 return true;
275 /* Permission violation */
276 excp = (access_type == MMU_DATA_LOAD ? EXCP_TLBR :
277 access_type == MMU_DATA_STORE ? EXCP_TLBW : EXCP_TLBX);
280 if (probe) {
281 return false;
284 if (access_type == MMU_INST_FETCH) {
285 env->ctrl[CR_TLBMISC] &= ~CR_TLBMISC_D;
286 } else {
287 env->ctrl[CR_TLBMISC] |= CR_TLBMISC_D;
289 env->ctrl[CR_PTEADDR] &= CR_PTEADDR_PTBASE_MASK;
290 env->ctrl[CR_PTEADDR] |= (address >> 10) & CR_PTEADDR_VPN_MASK;
291 env->mmu.pteaddr_wr = env->ctrl[CR_PTEADDR];
293 cs->exception_index = excp;
294 env->ctrl[CR_BADADDR] = address;
295 cpu_loop_exit_restore(cs, retaddr);