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