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 static inline void get_phys_nommu(hwaddr *phys_addr, int *prot, 33 target_ulong address) 34 { 35 *phys_addr = address; 36 *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; 37 } 38 39 static int get_phys_mmu(OpenRISCCPU *cpu, hwaddr *phys_addr, int *prot, 40 target_ulong addr, int need, bool super) 41 { 42 int idx = (addr >> TARGET_PAGE_BITS) & TLB_MASK; 43 uint32_t imr = cpu->env.tlb.itlb[idx].mr; 44 uint32_t itr = cpu->env.tlb.itlb[idx].tr; 45 uint32_t dmr = cpu->env.tlb.dtlb[idx].mr; 46 uint32_t dtr = cpu->env.tlb.dtlb[idx].tr; 47 int right, match, valid; 48 49 /* If the ITLB and DTLB indexes map to the same page, we want to 50 load all permissions all at once. If the destination pages do 51 not match, zap the one we don't need. */ 52 if (unlikely((itr ^ dtr) & TARGET_PAGE_MASK)) { 53 if (need & PAGE_EXEC) { 54 dmr = dtr = 0; 55 } else { 56 imr = itr = 0; 57 } 58 } 59 60 /* Check if either of the entries matches the source address. */ 61 match = (imr ^ addr) & TARGET_PAGE_MASK ? 0 : PAGE_EXEC; 62 match |= (dmr ^ addr) & TARGET_PAGE_MASK ? 0 : PAGE_READ | PAGE_WRITE; 63 64 /* Check if either of the entries is valid. */ 65 valid = imr & 1 ? PAGE_EXEC : 0; 66 valid |= dmr & 1 ? PAGE_READ | PAGE_WRITE : 0; 67 valid &= match; 68 69 /* Collect the permissions from the entries. */ 70 right = itr & (super ? SXE : UXE) ? PAGE_EXEC : 0; 71 right |= dtr & (super ? SRE : URE) ? PAGE_READ : 0; 72 right |= dtr & (super ? SWE : UWE) ? PAGE_WRITE : 0; 73 right &= valid; 74 75 /* Note that above we validated that itr and dtr match on page. 76 So oring them together changes nothing without having to 77 check which one we needed. We also want to store to these 78 variables even on failure, as it avoids compiler warnings. */ 79 *phys_addr = ((itr | dtr) & TARGET_PAGE_MASK) | (addr & ~TARGET_PAGE_MASK); 80 *prot = right; 81 82 qemu_log_mask(CPU_LOG_MMU, 83 "MMU lookup: need %d match %d valid %d right %d -> %s\n", 84 need, match, valid, right, (need & right) ? "OK" : "FAIL"); 85 86 /* Check the collective permissions are present. */ 87 if (likely(need & right)) { 88 return 0; /* success! */ 89 } 90 91 /* Determine what kind of failure we have. */ 92 if (need & valid) { 93 return need & PAGE_EXEC ? EXCP_IPF : EXCP_DPF; 94 } else { 95 return need & PAGE_EXEC ? EXCP_ITLBMISS : EXCP_DTLBMISS; 96 } 97 } 98 #endif 99 100 static void raise_mmu_exception(OpenRISCCPU *cpu, target_ulong address, 101 int exception) 102 { 103 CPUState *cs = CPU(cpu); 104 105 cs->exception_index = exception; 106 cpu->env.eear = address; 107 cpu->env.lock_addr = -1; 108 } 109 110 int openrisc_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, 111 int rw, int mmu_idx) 112 { 113 #ifdef CONFIG_USER_ONLY 114 OpenRISCCPU *cpu = OPENRISC_CPU(cs); 115 raise_mmu_exception(cpu, address, EXCP_DPF); 116 return 1; 117 #else 118 g_assert_not_reached(); 119 #endif 120 } 121 122 #ifndef CONFIG_USER_ONLY 123 hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) 124 { 125 OpenRISCCPU *cpu = OPENRISC_CPU(cs); 126 int prot, excp, sr = cpu->env.sr; 127 hwaddr phys_addr; 128 129 switch (sr & (SR_DME | SR_IME)) { 130 case SR_DME | SR_IME: 131 /* The mmu is definitely enabled. */ 132 excp = get_phys_mmu(cpu, &phys_addr, &prot, addr, 133 PAGE_EXEC | PAGE_READ | PAGE_WRITE, 134 (sr & SR_SM) != 0); 135 return excp ? -1 : phys_addr; 136 137 default: 138 /* The mmu is partially enabled, and we don't really have 139 a "real" access type. Begin by trying the mmu, but if 140 that fails try again without. */ 141 excp = get_phys_mmu(cpu, &phys_addr, &prot, addr, 142 PAGE_EXEC | PAGE_READ | PAGE_WRITE, 143 (sr & SR_SM) != 0); 144 if (!excp) { 145 return phys_addr; 146 } 147 /* fallthru */ 148 149 case 0: 150 /* The mmu is definitely disabled; lookups never fail. */ 151 get_phys_nommu(&phys_addr, &prot, addr); 152 return phys_addr; 153 } 154 } 155 156 void tlb_fill(CPUState *cs, target_ulong addr, int size, 157 MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) 158 { 159 OpenRISCCPU *cpu = OPENRISC_CPU(cs); 160 int prot, excp; 161 hwaddr phys_addr; 162 163 if (mmu_idx == MMU_NOMMU_IDX) { 164 /* The mmu is disabled; lookups never fail. */ 165 get_phys_nommu(&phys_addr, &prot, addr); 166 excp = 0; 167 } else { 168 bool super = mmu_idx == MMU_SUPERVISOR_IDX; 169 int need = (access_type == MMU_INST_FETCH ? PAGE_EXEC 170 : access_type == MMU_DATA_STORE ? PAGE_WRITE 171 : PAGE_READ); 172 excp = get_phys_mmu(cpu, &phys_addr, &prot, addr, need, super); 173 } 174 175 if (unlikely(excp)) { 176 raise_mmu_exception(cpu, addr, excp); 177 cpu_loop_exit_restore(cs, retaddr); 178 } 179 180 tlb_set_page(cs, addr & TARGET_PAGE_MASK, 181 phys_addr & TARGET_PAGE_MASK, prot, 182 mmu_idx, TARGET_PAGE_SIZE); 183 } 184 #endif 185