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