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