1 /* 2 * OpenRISC MMU. 3 * 4 * Copyright (c) 2011-2012 Jia Liu <proljc@gmail.com> 5 * Zhizhou Zhang <etouzh@gmail.com> 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 18 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 19 */ 20 21 #include "qemu/osdep.h" 22 #include "cpu.h" 23 #include "exec/exec-all.h" 24 #include "qemu-common.h" 25 #include "exec/gdbstub.h" 26 #include "qemu/host-utils.h" 27 #ifndef CONFIG_USER_ONLY 28 #include "hw/loader.h" 29 #endif 30 31 #ifndef CONFIG_USER_ONLY 32 int cpu_openrisc_get_phys_nommu(OpenRISCCPU *cpu, 33 hwaddr *physical, 34 int *prot, target_ulong address, int rw) 35 { 36 *physical = address; 37 *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; 38 return TLBRET_MATCH; 39 } 40 41 int cpu_openrisc_get_phys_code(OpenRISCCPU *cpu, 42 hwaddr *physical, 43 int *prot, target_ulong address, int rw) 44 { 45 int vpn = address >> TARGET_PAGE_BITS; 46 int idx = vpn & ITLB_MASK; 47 int right = 0; 48 49 if ((cpu->env.tlb->itlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) { 50 return TLBRET_NOMATCH; 51 } 52 if (!(cpu->env.tlb->itlb[0][idx].mr & 1)) { 53 return TLBRET_INVALID; 54 } 55 56 if (cpu->env.sr & SR_SM) { /* supervisor mode */ 57 if (cpu->env.tlb->itlb[0][idx].tr & SXE) { 58 right |= PAGE_EXEC; 59 } 60 } else { 61 if (cpu->env.tlb->itlb[0][idx].tr & UXE) { 62 right |= PAGE_EXEC; 63 } 64 } 65 66 if ((rw & 2) && ((right & PAGE_EXEC) == 0)) { 67 return TLBRET_BADADDR; 68 } 69 70 *physical = (cpu->env.tlb->itlb[0][idx].tr & TARGET_PAGE_MASK) | 71 (address & (TARGET_PAGE_SIZE-1)); 72 *prot = right; 73 return TLBRET_MATCH; 74 } 75 76 int cpu_openrisc_get_phys_data(OpenRISCCPU *cpu, 77 hwaddr *physical, 78 int *prot, target_ulong address, int rw) 79 { 80 int vpn = address >> TARGET_PAGE_BITS; 81 int idx = vpn & DTLB_MASK; 82 int right = 0; 83 84 if ((cpu->env.tlb->dtlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) { 85 return TLBRET_NOMATCH; 86 } 87 if (!(cpu->env.tlb->dtlb[0][idx].mr & 1)) { 88 return TLBRET_INVALID; 89 } 90 91 if (cpu->env.sr & SR_SM) { /* supervisor mode */ 92 if (cpu->env.tlb->dtlb[0][idx].tr & SRE) { 93 right |= PAGE_READ; 94 } 95 if (cpu->env.tlb->dtlb[0][idx].tr & SWE) { 96 right |= PAGE_WRITE; 97 } 98 } else { 99 if (cpu->env.tlb->dtlb[0][idx].tr & URE) { 100 right |= PAGE_READ; 101 } 102 if (cpu->env.tlb->dtlb[0][idx].tr & UWE) { 103 right |= PAGE_WRITE; 104 } 105 } 106 107 if (!(rw & 1) && ((right & PAGE_READ) == 0)) { 108 return TLBRET_BADADDR; 109 } 110 if ((rw & 1) && ((right & PAGE_WRITE) == 0)) { 111 return TLBRET_BADADDR; 112 } 113 114 *physical = (cpu->env.tlb->dtlb[0][idx].tr & TARGET_PAGE_MASK) | 115 (address & (TARGET_PAGE_SIZE-1)); 116 *prot = right; 117 return TLBRET_MATCH; 118 } 119 120 static int cpu_openrisc_get_phys_addr(OpenRISCCPU *cpu, 121 hwaddr *physical, 122 int *prot, target_ulong address, 123 int rw) 124 { 125 int ret = TLBRET_MATCH; 126 127 if (rw == MMU_INST_FETCH) { /* ITLB */ 128 *physical = 0; 129 ret = cpu->env.tlb->cpu_openrisc_map_address_code(cpu, physical, 130 prot, address, rw); 131 } else { /* DTLB */ 132 ret = cpu->env.tlb->cpu_openrisc_map_address_data(cpu, physical, 133 prot, address, rw); 134 } 135 136 return ret; 137 } 138 #endif 139 140 static void cpu_openrisc_raise_mmu_exception(OpenRISCCPU *cpu, 141 target_ulong address, 142 int rw, int tlb_error) 143 { 144 CPUState *cs = CPU(cpu); 145 int exception = 0; 146 147 switch (tlb_error) { 148 default: 149 if (rw == 2) { 150 exception = EXCP_IPF; 151 } else { 152 exception = EXCP_DPF; 153 } 154 break; 155 #ifndef CONFIG_USER_ONLY 156 case TLBRET_BADADDR: 157 if (rw == 2) { 158 exception = EXCP_IPF; 159 } else { 160 exception = EXCP_DPF; 161 } 162 break; 163 case TLBRET_INVALID: 164 case TLBRET_NOMATCH: 165 /* No TLB match for a mapped address */ 166 if (rw == 2) { 167 exception = EXCP_ITLBMISS; 168 } else { 169 exception = EXCP_DTLBMISS; 170 } 171 break; 172 #endif 173 } 174 175 cs->exception_index = exception; 176 cpu->env.eear = address; 177 cpu->env.lock_addr = -1; 178 } 179 180 #ifndef CONFIG_USER_ONLY 181 int openrisc_cpu_handle_mmu_fault(CPUState *cs, 182 vaddr address, int rw, int mmu_idx) 183 { 184 OpenRISCCPU *cpu = OPENRISC_CPU(cs); 185 int ret = 0; 186 hwaddr physical = 0; 187 int prot = 0; 188 189 ret = cpu_openrisc_get_phys_addr(cpu, &physical, &prot, 190 address, rw); 191 192 if (ret == TLBRET_MATCH) { 193 tlb_set_page(cs, address & TARGET_PAGE_MASK, 194 physical & TARGET_PAGE_MASK, prot, 195 mmu_idx, TARGET_PAGE_SIZE); 196 ret = 0; 197 } else if (ret < 0) { 198 cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret); 199 ret = 1; 200 } 201 202 return ret; 203 } 204 #else 205 int openrisc_cpu_handle_mmu_fault(CPUState *cs, 206 vaddr address, int rw, int mmu_idx) 207 { 208 OpenRISCCPU *cpu = OPENRISC_CPU(cs); 209 int ret = 0; 210 211 cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret); 212 ret = 1; 213 214 return ret; 215 } 216 #endif 217 218 #ifndef CONFIG_USER_ONLY 219 hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) 220 { 221 OpenRISCCPU *cpu = OPENRISC_CPU(cs); 222 hwaddr phys_addr; 223 int prot; 224 int miss; 225 226 /* Check memory for any kind of address, since during debug the 227 gdb can ask for anything, check data tlb for address */ 228 miss = cpu_openrisc_get_phys_addr(cpu, &phys_addr, &prot, addr, 0); 229 230 /* Check instruction tlb */ 231 if (miss) { 232 miss = cpu_openrisc_get_phys_addr(cpu, &phys_addr, &prot, addr, 233 MMU_INST_FETCH); 234 } 235 236 /* Last, fall back to a plain address */ 237 if (miss) { 238 miss = cpu_openrisc_get_phys_nommu(cpu, &phys_addr, &prot, addr, 0); 239 } 240 241 if (miss) { 242 return -1; 243 } else { 244 return phys_addr; 245 } 246 } 247 248 void cpu_openrisc_mmu_init(OpenRISCCPU *cpu) 249 { 250 cpu->env.tlb = g_malloc0(sizeof(CPUOpenRISCTLBContext)); 251 252 cpu->env.tlb->cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_nommu; 253 cpu->env.tlb->cpu_openrisc_map_address_data = &cpu_openrisc_get_phys_nommu; 254 } 255 #endif 256