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