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