]> git.proxmox.com Git - mirror_qemu.git/blob - target/hppa/mem_helper.c
target/hppa: Always return EXCP_DMAR for protection id trap
[mirror_qemu.git] / target / hppa / mem_helper.c
1 /*
2 * HPPA memory access helper routines
3 *
4 * Copyright (c) 2017 Helge Deller
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "qemu/osdep.h"
21 #include "cpu.h"
22 #include "exec/exec-all.h"
23 #include "exec/helper-proto.h"
24 #include "qom/cpu.h"
25 #include "trace.h"
26
27 #ifdef CONFIG_USER_ONLY
28 int hppa_cpu_handle_mmu_fault(CPUState *cs, vaddr address,
29 int size, int rw, int mmu_idx)
30 {
31 HPPACPU *cpu = HPPA_CPU(cs);
32
33 /* ??? Test between data page fault and data memory protection trap,
34 which would affect si_code. */
35 cs->exception_index = EXCP_DMP;
36 cpu->env.cr[CR_IOR] = address;
37 return 1;
38 }
39 #else
40 static hppa_tlb_entry *hppa_find_tlb(CPUHPPAState *env, vaddr addr)
41 {
42 int i;
43
44 for (i = 0; i < ARRAY_SIZE(env->tlb); ++i) {
45 hppa_tlb_entry *ent = &env->tlb[i];
46 if (ent->va_b <= addr && addr <= ent->va_e) {
47 trace_hppa_tlb_find_entry(env, ent + i, ent->entry_valid,
48 ent->va_b, ent->va_e, ent->pa);
49 return ent;
50 }
51 }
52 trace_hppa_tlb_find_entry_not_found(env, addr);
53 return NULL;
54 }
55
56 static void hppa_flush_tlb_ent(CPUHPPAState *env, hppa_tlb_entry *ent)
57 {
58 CPUState *cs = CPU(hppa_env_get_cpu(env));
59 unsigned i, n = 1 << (2 * ent->page_size);
60 uint64_t addr = ent->va_b;
61
62 trace_hppa_tlb_flush_ent(env, ent, ent->va_b, ent->va_e, ent->pa);
63
64 for (i = 0; i < n; ++i, addr += TARGET_PAGE_SIZE) {
65 /* Do not flush MMU_PHYS_IDX. */
66 tlb_flush_page_by_mmuidx(cs, addr, 0xf);
67 }
68
69 memset(ent, 0, sizeof(*ent));
70 ent->va_b = -1;
71 }
72
73 static hppa_tlb_entry *hppa_alloc_tlb_ent(CPUHPPAState *env)
74 {
75 hppa_tlb_entry *ent;
76 uint32_t i = env->tlb_last;
77
78 env->tlb_last = (i == ARRAY_SIZE(env->tlb) - 1 ? 0 : i + 1);
79 ent = &env->tlb[i];
80
81 hppa_flush_tlb_ent(env, ent);
82 return ent;
83 }
84
85 int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx,
86 int type, hwaddr *pphys, int *pprot)
87 {
88 hwaddr phys;
89 int prot, r_prot, w_prot, x_prot;
90 hppa_tlb_entry *ent;
91 int ret = -1;
92
93 /* Virtual translation disabled. Direct map virtual to physical. */
94 if (mmu_idx == MMU_PHYS_IDX) {
95 phys = addr;
96 prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
97 goto egress;
98 }
99
100 /* Find a valid tlb entry that matches the virtual address. */
101 ent = hppa_find_tlb(env, addr);
102 if (ent == NULL || !ent->entry_valid) {
103 phys = 0;
104 prot = 0;
105 ret = (type == PAGE_EXEC) ? EXCP_ITLB_MISS : EXCP_DTLB_MISS;
106 goto egress;
107 }
108
109 /* We now know the physical address. */
110 phys = ent->pa + (addr & ~TARGET_PAGE_MASK);
111
112 /* Map TLB access_rights field to QEMU protection. */
113 r_prot = (mmu_idx <= ent->ar_pl1) * PAGE_READ;
114 w_prot = (mmu_idx <= ent->ar_pl2) * PAGE_WRITE;
115 x_prot = (ent->ar_pl2 <= mmu_idx && mmu_idx <= ent->ar_pl1) * PAGE_EXEC;
116 switch (ent->ar_type) {
117 case 0: /* read-only: data page */
118 prot = r_prot;
119 break;
120 case 1: /* read/write: dynamic data page */
121 prot = r_prot | w_prot;
122 break;
123 case 2: /* read/execute: normal code page */
124 prot = r_prot | x_prot;
125 break;
126 case 3: /* read/write/execute: dynamic code page */
127 prot = r_prot | w_prot | x_prot;
128 break;
129 default: /* execute: promote to privilege level type & 3 */
130 prot = x_prot;
131 break;
132 }
133
134 /* access_id == 0 means public page and no check is performed */
135 if ((env->psw & PSW_P) && ent->access_id) {
136 /* If bits [31:1] match, and bit 0 is set, suppress write. */
137 int match = ent->access_id * 2 + 1;
138
139 if (match == env->cr[CR_PID1] || match == env->cr[CR_PID2] ||
140 match == env->cr[CR_PID3] || match == env->cr[CR_PID4]) {
141 prot &= PAGE_READ | PAGE_EXEC;
142 if (type == PAGE_WRITE) {
143 ret = EXCP_DMPI;
144 goto egress;
145 }
146 }
147 }
148
149 /* No guest access type indicates a non-architectural access from
150 within QEMU. Bypass checks for access, D, B and T bits. */
151 if (type == 0) {
152 goto egress;
153 }
154
155 if (unlikely(!(prot & type))) {
156 /* The access isn't allowed -- Inst/Data Memory Protection Fault. */
157 ret = (type & PAGE_EXEC) ? EXCP_IMP : EXCP_DMAR;
158 goto egress;
159 }
160
161 /* In reverse priority order, check for conditions which raise faults.
162 As we go, remove PROT bits that cover the condition we want to check.
163 In this way, the resulting PROT will force a re-check of the
164 architectural TLB entry for the next access. */
165 if (unlikely(!ent->d)) {
166 if (type & PAGE_WRITE) {
167 /* The D bit is not set -- TLB Dirty Bit Fault. */
168 ret = EXCP_TLB_DIRTY;
169 }
170 prot &= PAGE_READ | PAGE_EXEC;
171 }
172 if (unlikely(ent->b)) {
173 if (type & PAGE_WRITE) {
174 /* The B bit is set -- Data Memory Break Fault. */
175 ret = EXCP_DMB;
176 }
177 prot &= PAGE_READ | PAGE_EXEC;
178 }
179 if (unlikely(ent->t)) {
180 if (!(type & PAGE_EXEC)) {
181 /* The T bit is set -- Page Reference Fault. */
182 ret = EXCP_PAGE_REF;
183 }
184 prot &= PAGE_EXEC;
185 }
186
187 egress:
188 *pphys = phys;
189 *pprot = prot;
190 trace_hppa_tlb_get_physical_address(env, ret, prot, addr, phys);
191 return ret;
192 }
193
194 hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
195 {
196 HPPACPU *cpu = HPPA_CPU(cs);
197 hwaddr phys;
198 int prot, excp;
199
200 /* If the (data) mmu is disabled, bypass translation. */
201 /* ??? We really ought to know if the code mmu is disabled too,
202 in order to get the correct debugging dumps. */
203 if (!(cpu->env.psw & PSW_D)) {
204 return addr;
205 }
206
207 excp = hppa_get_physical_address(&cpu->env, addr, MMU_KERNEL_IDX, 0,
208 &phys, &prot);
209
210 /* Since we're translating for debugging, the only error that is a
211 hard error is no translation at all. Otherwise, while a real cpu
212 access might not have permission, the debugger does. */
213 return excp == EXCP_DTLB_MISS ? -1 : phys;
214 }
215
216 void tlb_fill(CPUState *cs, target_ulong addr, int size,
217 MMUAccessType type, int mmu_idx, uintptr_t retaddr)
218 {
219 HPPACPU *cpu = HPPA_CPU(cs);
220 CPUHPPAState *env = &cpu->env;
221 int prot, excp, a_prot;
222 hwaddr phys;
223
224 switch (type) {
225 case MMU_INST_FETCH:
226 a_prot = PAGE_EXEC;
227 break;
228 case MMU_DATA_STORE:
229 a_prot = PAGE_WRITE;
230 break;
231 default:
232 a_prot = PAGE_READ;
233 break;
234 }
235
236 excp = hppa_get_physical_address(env, addr, mmu_idx,
237 a_prot, &phys, &prot);
238 if (unlikely(excp >= 0)) {
239 trace_hppa_tlb_fill_excp(env, addr, size, type, mmu_idx);
240 /* Failure. Raise the indicated exception. */
241 cs->exception_index = excp;
242 if (cpu->env.psw & PSW_Q) {
243 /* ??? Needs tweaking for hppa64. */
244 cpu->env.cr[CR_IOR] = addr;
245 cpu->env.cr[CR_ISR] = addr >> 32;
246 }
247 cpu_loop_exit_restore(cs, retaddr);
248 }
249
250 trace_hppa_tlb_fill_success(env, addr & TARGET_PAGE_MASK,
251 phys & TARGET_PAGE_MASK, size, type, mmu_idx);
252 /* Success! Store the translation into the QEMU TLB. */
253 tlb_set_page(cs, addr & TARGET_PAGE_MASK, phys & TARGET_PAGE_MASK,
254 prot, mmu_idx, TARGET_PAGE_SIZE);
255 }
256
257 /* Insert (Insn/Data) TLB Address. Note this is PA 1.1 only. */
258 void HELPER(itlba)(CPUHPPAState *env, target_ulong addr, target_ureg reg)
259 {
260 hppa_tlb_entry *empty = NULL;
261 int i;
262
263 /* Zap any old entries covering ADDR; notice empty entries on the way. */
264 for (i = 0; i < ARRAY_SIZE(env->tlb); ++i) {
265 hppa_tlb_entry *ent = &env->tlb[i];
266 if (ent->va_b <= addr && addr <= ent->va_e) {
267 if (ent->entry_valid) {
268 hppa_flush_tlb_ent(env, ent);
269 }
270 if (!empty) {
271 empty = ent;
272 }
273 }
274 }
275
276 /* If we didn't see an empty entry, evict one. */
277 if (empty == NULL) {
278 empty = hppa_alloc_tlb_ent(env);
279 }
280
281 /* Note that empty->entry_valid == 0 already. */
282 empty->va_b = addr & TARGET_PAGE_MASK;
283 empty->va_e = empty->va_b + TARGET_PAGE_SIZE - 1;
284 empty->pa = extract32(reg, 5, 20) << TARGET_PAGE_BITS;
285 trace_hppa_tlb_itlba(env, empty, empty->va_b, empty->va_e, empty->pa);
286 }
287
288 /* Insert (Insn/Data) TLB Protection. Note this is PA 1.1 only. */
289 void HELPER(itlbp)(CPUHPPAState *env, target_ulong addr, target_ureg reg)
290 {
291 hppa_tlb_entry *ent = hppa_find_tlb(env, addr);
292
293 if (unlikely(ent == NULL)) {
294 qemu_log_mask(LOG_GUEST_ERROR, "ITLBP not following ITLBA\n");
295 return;
296 }
297
298 ent->access_id = extract32(reg, 1, 18);
299 ent->u = extract32(reg, 19, 1);
300 ent->ar_pl2 = extract32(reg, 20, 2);
301 ent->ar_pl1 = extract32(reg, 22, 2);
302 ent->ar_type = extract32(reg, 24, 3);
303 ent->b = extract32(reg, 27, 1);
304 ent->d = extract32(reg, 28, 1);
305 ent->t = extract32(reg, 29, 1);
306 ent->entry_valid = 1;
307 trace_hppa_tlb_itlbp(env, ent, ent->access_id, ent->u, ent->ar_pl2,
308 ent->ar_pl1, ent->ar_type, ent->b, ent->d, ent->t);
309 }
310
311 /* Purge (Insn/Data) TLB. This is explicitly page-based, and is
312 synchronous across all processors. */
313 static void ptlb_work(CPUState *cpu, run_on_cpu_data data)
314 {
315 CPUHPPAState *env = cpu->env_ptr;
316 target_ulong addr = (target_ulong) data.target_ptr;
317 hppa_tlb_entry *ent = hppa_find_tlb(env, addr);
318
319 if (ent && ent->entry_valid) {
320 hppa_flush_tlb_ent(env, ent);
321 }
322 }
323
324 void HELPER(ptlb)(CPUHPPAState *env, target_ulong addr)
325 {
326 CPUState *src = CPU(hppa_env_get_cpu(env));
327 CPUState *cpu;
328 trace_hppa_tlb_ptlb(env);
329 run_on_cpu_data data = RUN_ON_CPU_TARGET_PTR(addr);
330
331 CPU_FOREACH(cpu) {
332 if (cpu != src) {
333 async_run_on_cpu(cpu, ptlb_work, data);
334 }
335 }
336 async_safe_run_on_cpu(src, ptlb_work, data);
337 }
338
339 /* Purge (Insn/Data) TLB entry. This affects an implementation-defined
340 number of pages/entries (we choose all), and is local to the cpu. */
341 void HELPER(ptlbe)(CPUHPPAState *env)
342 {
343 CPUState *src = CPU(hppa_env_get_cpu(env));
344 trace_hppa_tlb_ptlbe(env);
345 memset(env->tlb, 0, sizeof(env->tlb));
346 tlb_flush_by_mmuidx(src, 0xf);
347 }
348
349 void cpu_hppa_change_prot_id(CPUHPPAState *env)
350 {
351 if (env->psw & PSW_P) {
352 CPUState *src = CPU(hppa_env_get_cpu(env));
353 tlb_flush_by_mmuidx(src, 0xf);
354 }
355 }
356
357 void HELPER(change_prot_id)(CPUHPPAState *env)
358 {
359 cpu_hppa_change_prot_id(env);
360 }
361
362 target_ureg HELPER(lpa)(CPUHPPAState *env, target_ulong addr)
363 {
364 hwaddr phys;
365 int prot, excp;
366
367 excp = hppa_get_physical_address(env, addr, MMU_KERNEL_IDX, 0,
368 &phys, &prot);
369 if (excp >= 0) {
370 if (env->psw & PSW_Q) {
371 /* ??? Needs tweaking for hppa64. */
372 env->cr[CR_IOR] = addr;
373 env->cr[CR_ISR] = addr >> 32;
374 }
375 if (excp == EXCP_DTLB_MISS) {
376 excp = EXCP_NA_DTLB_MISS;
377 }
378 trace_hppa_tlb_lpa_failed(env, addr);
379 hppa_dynamic_excp(env, excp, GETPC());
380 }
381 trace_hppa_tlb_lpa_success(env, addr, phys);
382 return phys;
383 }
384
385 /* Return the ar_type of the TLB at VADDR, or -1. */
386 int hppa_artype_for_page(CPUHPPAState *env, target_ulong vaddr)
387 {
388 hppa_tlb_entry *ent = hppa_find_tlb(env, vaddr);
389 return ent ? ent->ar_type : -1;
390 }
391 #endif /* CONFIG_USER_ONLY */