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