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