Edgar E. Iglesias | 4acb54b | 2009-05-20 19:37:39 +0200 | [diff] [blame] | 1 | /* |
| 2 | * MicroBlaze helper routines. |
| 3 | * |
| 4 | * Copyright (c) 2009 Edgar E. Iglesias <edgar.iglesias@gmail.com> |
Peter A. G. Crosthwaite | dadc106 | 2012-04-12 14:30:30 +1000 | [diff] [blame] | 5 | * Copyright (c) 2009-2012 PetaLogix Qld Pty Ltd. |
Edgar E. Iglesias | 4acb54b | 2009-05-20 19:37:39 +0200 | [diff] [blame] | 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 |
Blue Swirl | 8167ee8 | 2009-07-16 20:47:01 +0000 | [diff] [blame] | 18 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
Edgar E. Iglesias | 4acb54b | 2009-05-20 19:37:39 +0200 | [diff] [blame] | 19 | */ |
| 20 | |
Edgar E. Iglesias | 4acb54b | 2009-05-20 19:37:39 +0200 | [diff] [blame] | 21 | #include "cpu.h" |
Edgar E. Iglesias | 4acb54b | 2009-05-20 19:37:39 +0200 | [diff] [blame] | 22 | #include "host-utils.h" |
| 23 | |
| 24 | #define D(x) |
| 25 | #define DMMU(x) |
| 26 | |
| 27 | #if defined(CONFIG_USER_ONLY) |
| 28 | |
Andreas Färber | 68cee38 | 2012-03-14 01:38:22 +0100 | [diff] [blame] | 29 | void do_interrupt (CPUMBState *env) |
Edgar E. Iglesias | 4acb54b | 2009-05-20 19:37:39 +0200 | [diff] [blame] | 30 | { |
| 31 | env->exception_index = -1; |
Peter A. G. Crosthwaite | 8cc9b43 | 2012-06-01 13:23:28 +1000 | [diff] [blame] | 32 | env->res_addr = RES_ADDR_NONE; |
Edgar E. Iglesias | 4acb54b | 2009-05-20 19:37:39 +0200 | [diff] [blame] | 33 | env->regs[14] = env->sregs[SR_PC]; |
| 34 | } |
| 35 | |
Andreas Färber | 68cee38 | 2012-03-14 01:38:22 +0100 | [diff] [blame] | 36 | int cpu_mb_handle_mmu_fault(CPUMBState * env, target_ulong address, int rw, |
Blue Swirl | 97b348e | 2011-08-01 16:12:17 +0000 | [diff] [blame] | 37 | int mmu_idx) |
Edgar E. Iglesias | 4acb54b | 2009-05-20 19:37:39 +0200 | [diff] [blame] | 38 | { |
| 39 | env->exception_index = 0xaa; |
| 40 | cpu_dump_state(env, stderr, fprintf, 0); |
| 41 | return 1; |
| 42 | } |
| 43 | |
Edgar E. Iglesias | 4acb54b | 2009-05-20 19:37:39 +0200 | [diff] [blame] | 44 | #else /* !CONFIG_USER_ONLY */ |
| 45 | |
Andreas Färber | 68cee38 | 2012-03-14 01:38:22 +0100 | [diff] [blame] | 46 | int cpu_mb_handle_mmu_fault (CPUMBState *env, target_ulong address, int rw, |
Blue Swirl | 97b348e | 2011-08-01 16:12:17 +0000 | [diff] [blame] | 47 | int mmu_idx) |
Edgar E. Iglesias | 4acb54b | 2009-05-20 19:37:39 +0200 | [diff] [blame] | 48 | { |
| 49 | unsigned int hit; |
| 50 | unsigned int mmu_available; |
| 51 | int r = 1; |
| 52 | int prot; |
| 53 | |
| 54 | mmu_available = 0; |
| 55 | if (env->pvr.regs[0] & PVR0_USE_MMU) { |
| 56 | mmu_available = 1; |
| 57 | if ((env->pvr.regs[0] & PVR0_PVR_FULL_MASK) |
| 58 | && (env->pvr.regs[11] & PVR11_USE_MMU) != PVR11_USE_MMU) { |
| 59 | mmu_available = 0; |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | /* Translate if the MMU is available and enabled. */ |
| 64 | if (mmu_available && (env->sregs[SR_MSR] & MSR_VM)) { |
| 65 | target_ulong vaddr, paddr; |
| 66 | struct microblaze_mmu_lookup lu; |
| 67 | |
| 68 | hit = mmu_translate(&env->mmu, &lu, address, rw, mmu_idx); |
| 69 | if (hit) { |
| 70 | vaddr = address & TARGET_PAGE_MASK; |
| 71 | paddr = lu.paddr + vaddr - lu.vaddr; |
| 72 | |
| 73 | DMMU(qemu_log("MMU map mmu=%d v=%x p=%x prot=%x\n", |
| 74 | mmu_idx, vaddr, paddr, lu.prot)); |
Paul Brook | d4c430a | 2010-03-17 02:14:28 +0000 | [diff] [blame] | 75 | tlb_set_page(env, vaddr, paddr, lu.prot, mmu_idx, TARGET_PAGE_SIZE); |
| 76 | r = 0; |
Edgar E. Iglesias | 4acb54b | 2009-05-20 19:37:39 +0200 | [diff] [blame] | 77 | } else { |
| 78 | env->sregs[SR_EAR] = address; |
Edgar E. Iglesias | 21d2063 | 2009-09-03 10:13:08 +0200 | [diff] [blame] | 79 | DMMU(qemu_log("mmu=%d miss v=%x\n", mmu_idx, address)); |
Edgar E. Iglesias | 4acb54b | 2009-05-20 19:37:39 +0200 | [diff] [blame] | 80 | |
| 81 | switch (lu.err) { |
| 82 | case ERR_PROT: |
| 83 | env->sregs[SR_ESR] = rw == 2 ? 17 : 16; |
| 84 | env->sregs[SR_ESR] |= (rw == 1) << 10; |
| 85 | break; |
| 86 | case ERR_MISS: |
| 87 | env->sregs[SR_ESR] = rw == 2 ? 19 : 18; |
| 88 | env->sregs[SR_ESR] |= (rw == 1) << 10; |
| 89 | break; |
| 90 | default: |
| 91 | abort(); |
| 92 | break; |
| 93 | } |
| 94 | |
| 95 | if (env->exception_index == EXCP_MMU) { |
| 96 | cpu_abort(env, "recursive faults\n"); |
| 97 | } |
| 98 | |
| 99 | /* TLB miss. */ |
| 100 | env->exception_index = EXCP_MMU; |
| 101 | } |
| 102 | } else { |
| 103 | /* MMU disabled or not available. */ |
| 104 | address &= TARGET_PAGE_MASK; |
| 105 | prot = PAGE_BITS; |
Paul Brook | d4c430a | 2010-03-17 02:14:28 +0000 | [diff] [blame] | 106 | tlb_set_page(env, address, address, prot, mmu_idx, TARGET_PAGE_SIZE); |
| 107 | r = 0; |
Edgar E. Iglesias | 4acb54b | 2009-05-20 19:37:39 +0200 | [diff] [blame] | 108 | } |
| 109 | return r; |
| 110 | } |
| 111 | |
Andreas Färber | 68cee38 | 2012-03-14 01:38:22 +0100 | [diff] [blame] | 112 | void do_interrupt(CPUMBState *env) |
Edgar E. Iglesias | 4acb54b | 2009-05-20 19:37:39 +0200 | [diff] [blame] | 113 | { |
| 114 | uint32_t t; |
| 115 | |
Stefan Weil | 5225d66 | 2011-04-28 17:20:26 +0200 | [diff] [blame] | 116 | /* IMM flag cannot propagate across a branch and into the dslot. */ |
Edgar E. Iglesias | 4acb54b | 2009-05-20 19:37:39 +0200 | [diff] [blame] | 117 | assert(!((env->iflags & D_FLAG) && (env->iflags & IMM_FLAG))); |
| 118 | assert(!(env->iflags & (DRTI_FLAG | DRTE_FLAG | DRTB_FLAG))); |
| 119 | /* assert(env->sregs[SR_MSR] & (MSR_EE)); Only for HW exceptions. */ |
Peter A. G. Crosthwaite | 8cc9b43 | 2012-06-01 13:23:28 +1000 | [diff] [blame] | 120 | env->res_addr = RES_ADDR_NONE; |
Edgar E. Iglesias | 4acb54b | 2009-05-20 19:37:39 +0200 | [diff] [blame] | 121 | switch (env->exception_index) { |
Edgar E. Iglesias | cedb936 | 2009-09-03 10:25:00 +0200 | [diff] [blame] | 122 | case EXCP_HW_EXCP: |
| 123 | if (!(env->pvr.regs[0] & PVR0_USE_EXC_MASK)) { |
| 124 | qemu_log("Exception raised on system without exceptions!\n"); |
| 125 | return; |
| 126 | } |
| 127 | |
| 128 | env->regs[17] = env->sregs[SR_PC] + 4; |
| 129 | env->sregs[SR_ESR] &= ~(1 << 12); |
| 130 | |
| 131 | /* Exception breaks branch + dslot sequence? */ |
| 132 | if (env->iflags & D_FLAG) { |
| 133 | env->sregs[SR_ESR] |= 1 << 12 ; |
| 134 | env->sregs[SR_BTR] = env->btarget; |
| 135 | } |
| 136 | |
| 137 | /* Disable the MMU. */ |
| 138 | t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; |
| 139 | env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); |
| 140 | env->sregs[SR_MSR] |= t; |
| 141 | /* Exception in progress. */ |
| 142 | env->sregs[SR_MSR] |= MSR_EIP; |
| 143 | |
| 144 | qemu_log_mask(CPU_LOG_INT, |
| 145 | "hw exception at pc=%x ear=%x esr=%x iflags=%x\n", |
| 146 | env->sregs[SR_PC], env->sregs[SR_EAR], |
| 147 | env->sregs[SR_ESR], env->iflags); |
| 148 | log_cpu_state_mask(CPU_LOG_INT, env, 0); |
| 149 | env->iflags &= ~(IMM_FLAG | D_FLAG); |
| 150 | env->sregs[SR_PC] = 0x20; |
| 151 | break; |
| 152 | |
Edgar E. Iglesias | 4acb54b | 2009-05-20 19:37:39 +0200 | [diff] [blame] | 153 | case EXCP_MMU: |
| 154 | env->regs[17] = env->sregs[SR_PC]; |
| 155 | |
Edgar E. Iglesias | a75cf0c | 2009-09-03 10:15:17 +0200 | [diff] [blame] | 156 | env->sregs[SR_ESR] &= ~(1 << 12); |
Edgar E. Iglesias | 4acb54b | 2009-05-20 19:37:39 +0200 | [diff] [blame] | 157 | /* Exception breaks branch + dslot sequence? */ |
| 158 | if (env->iflags & D_FLAG) { |
| 159 | D(qemu_log("D_FLAG set at exception bimm=%d\n", env->bimm)); |
| 160 | env->sregs[SR_ESR] |= 1 << 12 ; |
| 161 | env->sregs[SR_BTR] = env->btarget; |
| 162 | |
| 163 | /* Reexecute the branch. */ |
| 164 | env->regs[17] -= 4; |
| 165 | /* was the branch immprefixed?. */ |
| 166 | if (env->bimm) { |
| 167 | qemu_log_mask(CPU_LOG_INT, |
| 168 | "bimm exception at pc=%x iflags=%x\n", |
| 169 | env->sregs[SR_PC], env->iflags); |
| 170 | env->regs[17] -= 4; |
| 171 | log_cpu_state_mask(CPU_LOG_INT, env, 0); |
| 172 | } |
| 173 | } else if (env->iflags & IMM_FLAG) { |
| 174 | D(qemu_log("IMM_FLAG set at exception\n")); |
| 175 | env->regs[17] -= 4; |
| 176 | } |
| 177 | |
| 178 | /* Disable the MMU. */ |
| 179 | t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; |
| 180 | env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); |
| 181 | env->sregs[SR_MSR] |= t; |
| 182 | /* Exception in progress. */ |
| 183 | env->sregs[SR_MSR] |= MSR_EIP; |
| 184 | |
| 185 | qemu_log_mask(CPU_LOG_INT, |
| 186 | "exception at pc=%x ear=%x iflags=%x\n", |
| 187 | env->sregs[SR_PC], env->sregs[SR_EAR], env->iflags); |
| 188 | log_cpu_state_mask(CPU_LOG_INT, env, 0); |
| 189 | env->iflags &= ~(IMM_FLAG | D_FLAG); |
| 190 | env->sregs[SR_PC] = 0x20; |
| 191 | break; |
| 192 | |
| 193 | case EXCP_IRQ: |
| 194 | assert(!(env->sregs[SR_MSR] & (MSR_EIP | MSR_BIP))); |
| 195 | assert(env->sregs[SR_MSR] & MSR_IE); |
| 196 | assert(!(env->iflags & D_FLAG)); |
| 197 | |
| 198 | t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; |
| 199 | |
| 200 | #if 0 |
| 201 | #include "disas.h" |
| 202 | |
| 203 | /* Useful instrumentation when debugging interrupt issues in either |
| 204 | the models or in sw. */ |
| 205 | { |
| 206 | const char *sym; |
| 207 | |
| 208 | sym = lookup_symbol(env->sregs[SR_PC]); |
| 209 | if (sym |
| 210 | && (!strcmp("netif_rx", sym) |
| 211 | || !strcmp("process_backlog", sym))) { |
| 212 | |
| 213 | qemu_log( |
| 214 | "interrupt at pc=%x msr=%x %x iflags=%x sym=%s\n", |
| 215 | env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags, |
| 216 | sym); |
| 217 | |
| 218 | log_cpu_state(env, 0); |
| 219 | } |
| 220 | } |
| 221 | #endif |
| 222 | qemu_log_mask(CPU_LOG_INT, |
| 223 | "interrupt at pc=%x msr=%x %x iflags=%x\n", |
| 224 | env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags); |
| 225 | |
| 226 | env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM \ |
| 227 | | MSR_UM | MSR_IE); |
| 228 | env->sregs[SR_MSR] |= t; |
| 229 | |
| 230 | env->regs[14] = env->sregs[SR_PC]; |
| 231 | env->sregs[SR_PC] = 0x10; |
| 232 | //log_cpu_state_mask(CPU_LOG_INT, env, 0); |
| 233 | break; |
| 234 | |
| 235 | case EXCP_BREAK: |
| 236 | case EXCP_HW_BREAK: |
| 237 | assert(!(env->iflags & IMM_FLAG)); |
| 238 | assert(!(env->iflags & D_FLAG)); |
| 239 | t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; |
| 240 | qemu_log_mask(CPU_LOG_INT, |
| 241 | "break at pc=%x msr=%x %x iflags=%x\n", |
| 242 | env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags); |
| 243 | log_cpu_state_mask(CPU_LOG_INT, env, 0); |
| 244 | env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); |
| 245 | env->sregs[SR_MSR] |= t; |
| 246 | env->sregs[SR_MSR] |= MSR_BIP; |
| 247 | if (env->exception_index == EXCP_HW_BREAK) { |
| 248 | env->regs[16] = env->sregs[SR_PC]; |
| 249 | env->sregs[SR_MSR] |= MSR_BIP; |
| 250 | env->sregs[SR_PC] = 0x18; |
| 251 | } else |
| 252 | env->sregs[SR_PC] = env->btarget; |
| 253 | break; |
| 254 | default: |
| 255 | cpu_abort(env, "unhandled exception type=%d\n", |
| 256 | env->exception_index); |
| 257 | break; |
| 258 | } |
| 259 | } |
| 260 | |
Avi Kivity | a8170e5 | 2012-10-23 12:30:10 +0200 | [diff] [blame] | 261 | hwaddr cpu_get_phys_page_debug(CPUMBState * env, target_ulong addr) |
Edgar E. Iglesias | 4acb54b | 2009-05-20 19:37:39 +0200 | [diff] [blame] | 262 | { |
| 263 | target_ulong vaddr, paddr = 0; |
| 264 | struct microblaze_mmu_lookup lu; |
| 265 | unsigned int hit; |
| 266 | |
| 267 | if (env->sregs[SR_MSR] & MSR_VM) { |
| 268 | hit = mmu_translate(&env->mmu, &lu, addr, 0, 0); |
| 269 | if (hit) { |
| 270 | vaddr = addr & TARGET_PAGE_MASK; |
| 271 | paddr = lu.paddr + vaddr - lu.vaddr; |
| 272 | } else |
| 273 | paddr = 0; /* ???. */ |
| 274 | } else |
| 275 | paddr = addr & TARGET_PAGE_MASK; |
| 276 | |
| 277 | return paddr; |
| 278 | } |
| 279 | #endif |