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 == 2) { /* 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 } 178 179 #ifndef CONFIG_USER_ONLY 180 int openrisc_cpu_handle_mmu_fault(CPUState *cs, 181 vaddr address, int rw, int mmu_idx) 182 { 183 OpenRISCCPU *cpu = OPENRISC_CPU(cs); 184 int ret = 0; 185 hwaddr physical = 0; 186 int prot = 0; 187 188 ret = cpu_openrisc_get_phys_addr(cpu, &physical, &prot, 189 address, rw); 190 191 if (ret == TLBRET_MATCH) { 192 tlb_set_page(cs, address & TARGET_PAGE_MASK, 193 physical & TARGET_PAGE_MASK, prot, 194 mmu_idx, TARGET_PAGE_SIZE); 195 ret = 0; 196 } else if (ret < 0) { 197 cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret); 198 ret = 1; 199 } 200 201 return ret; 202 } 203 #else 204 int openrisc_cpu_handle_mmu_fault(CPUState *cs, 205 vaddr address, int rw, int mmu_idx) 206 { 207 OpenRISCCPU *cpu = OPENRISC_CPU(cs); 208 int ret = 0; 209 210 cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret); 211 ret = 1; 212 213 return ret; 214 } 215 #endif 216 217 #ifndef CONFIG_USER_ONLY 218 hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) 219 { 220 OpenRISCCPU *cpu = OPENRISC_CPU(cs); 221 hwaddr phys_addr; 222 int prot; 223 224 if (cpu_openrisc_get_phys_addr(cpu, &phys_addr, &prot, addr, 0)) { 225 return -1; 226 } 227 228 return phys_addr; 229 } 230 231 void cpu_openrisc_mmu_init(OpenRISCCPU *cpu) 232 { 233 cpu->env.tlb = g_malloc0(sizeof(CPUOpenRISCTLBContext)); 234 235 cpu->env.tlb->cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_nommu; 236 cpu->env.tlb->cpu_openrisc_map_address_data = &cpu_openrisc_get_phys_nommu; 237 } 238 #endif 239