target/arm: Mark PMINTENSET accesses as possibly doing IO
[qemu.git] / target / ppc / mmu-radix64.c
blobab76cbc83530cc9bcc31c046a039dad77a829ccb
1 /*
2 * PowerPC Radix MMU mulation helpers for QEMU.
4 * Copyright (c) 2016 Suraj Jitindar Singh, IBM Corporation
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.
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.
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/>.
20 #include "qemu/osdep.h"
21 #include "cpu.h"
22 #include "exec/exec-all.h"
23 #include "exec/helper-proto.h"
24 #include "qemu/error-report.h"
25 #include "sysemu/kvm.h"
26 #include "kvm_ppc.h"
27 #include "exec/log.h"
28 #include "mmu-radix64.h"
29 #include "mmu-book3s-v3.h"
31 static bool ppc_radix64_get_fully_qualified_addr(CPUPPCState *env, vaddr eaddr,
32 uint64_t *lpid, uint64_t *pid)
34 /* We don't have HV support yet and shouldn't get here with it set anyway */
35 assert(!msr_hv);
37 if (!msr_hv) { /* !MSR[HV] -> Guest */
38 switch (eaddr & R_EADDR_QUADRANT) {
39 case R_EADDR_QUADRANT0: /* Guest application */
40 *lpid = env->spr[SPR_LPIDR];
41 *pid = env->spr[SPR_BOOKS_PID];
42 break;
43 case R_EADDR_QUADRANT1: /* Illegal */
44 case R_EADDR_QUADRANT2:
45 return false;
46 case R_EADDR_QUADRANT3: /* Guest OS */
47 *lpid = env->spr[SPR_LPIDR];
48 *pid = 0; /* pid set to 0 -> addresses guest operating system */
49 break;
53 return true;
56 static void ppc_radix64_raise_segi(PowerPCCPU *cpu, int rwx, vaddr eaddr)
58 CPUState *cs = CPU(cpu);
59 CPUPPCState *env = &cpu->env;
61 if (rwx == 2) { /* Instruction Segment Interrupt */
62 cs->exception_index = POWERPC_EXCP_ISEG;
63 } else { /* Data Segment Interrupt */
64 cs->exception_index = POWERPC_EXCP_DSEG;
65 env->spr[SPR_DAR] = eaddr;
67 env->error_code = 0;
70 static void ppc_radix64_raise_si(PowerPCCPU *cpu, int rwx, vaddr eaddr,
71 uint32_t cause)
73 CPUState *cs = CPU(cpu);
74 CPUPPCState *env = &cpu->env;
76 if (rwx == 2) { /* Instruction Storage Interrupt */
77 cs->exception_index = POWERPC_EXCP_ISI;
78 env->error_code = cause;
79 } else { /* Data Storage Interrupt */
80 cs->exception_index = POWERPC_EXCP_DSI;
81 if (rwx == 1) { /* Write -> Store */
82 cause |= DSISR_ISSTORE;
84 env->spr[SPR_DSISR] = cause;
85 env->spr[SPR_DAR] = eaddr;
86 env->error_code = 0;
91 static bool ppc_radix64_check_prot(PowerPCCPU *cpu, int rwx, uint64_t pte,
92 int *fault_cause, int *prot)
94 CPUPPCState *env = &cpu->env;
95 const int need_prot[] = { PAGE_READ, PAGE_WRITE, PAGE_EXEC };
97 /* Check Page Attributes (pte58:59) */
98 if (((pte & R_PTE_ATT) == R_PTE_ATT_NI_IO) && (rwx == 2)) {
100 * Radix PTE entries with the non-idempotent I/O attribute are treated
101 * as guarded storage
103 *fault_cause |= SRR1_NOEXEC_GUARD;
104 return true;
107 /* Determine permissions allowed by Encoded Access Authority */
108 if ((pte & R_PTE_EAA_PRIV) && msr_pr) { /* Insufficient Privilege */
109 *prot = 0;
110 } else if (msr_pr || (pte & R_PTE_EAA_PRIV)) {
111 *prot = ppc_radix64_get_prot_eaa(pte);
112 } else { /* !msr_pr && !(pte & R_PTE_EAA_PRIV) */
113 *prot = ppc_radix64_get_prot_eaa(pte);
114 *prot &= ppc_radix64_get_prot_amr(cpu); /* Least combined permissions */
117 /* Check if requested access type is allowed */
118 if (need_prot[rwx] & ~(*prot)) { /* Page Protected for that Access */
119 *fault_cause |= DSISR_PROTFAULT;
120 return true;
123 return false;
126 static void ppc_radix64_set_rc(PowerPCCPU *cpu, int rwx, uint64_t pte,
127 hwaddr pte_addr, int *prot)
129 CPUState *cs = CPU(cpu);
130 uint64_t npte;
132 npte = pte | R_PTE_R; /* Always set reference bit */
134 if (rwx == 1) { /* Store/Write */
135 npte |= R_PTE_C; /* Set change bit */
136 } else {
138 * Treat the page as read-only for now, so that a later write
139 * will pass through this function again to set the C bit.
141 *prot &= ~PAGE_WRITE;
144 if (pte ^ npte) { /* If pte has changed then write it back */
145 stq_phys(cs->as, pte_addr, npte);
149 static uint64_t ppc_radix64_walk_tree(PowerPCCPU *cpu, vaddr eaddr,
150 uint64_t base_addr, uint64_t nls,
151 hwaddr *raddr, int *psize,
152 int *fault_cause, hwaddr *pte_addr)
154 CPUState *cs = CPU(cpu);
155 uint64_t index, pde;
157 if (nls < 5) { /* Directory maps less than 2**5 entries */
158 *fault_cause |= DSISR_R_BADCONFIG;
159 return 0;
162 /* Read page <directory/table> entry from guest address space */
163 index = eaddr >> (*psize - nls); /* Shift */
164 index &= ((1UL << nls) - 1); /* Mask */
165 pde = ldq_phys(cs->as, base_addr + (index * sizeof(pde)));
166 if (!(pde & R_PTE_VALID)) { /* Invalid Entry */
167 *fault_cause |= DSISR_NOPTE;
168 return 0;
171 *psize -= nls;
173 /* Check if Leaf Entry -> Page Table Entry -> Stop the Search */
174 if (pde & R_PTE_LEAF) {
175 uint64_t rpn = pde & R_PTE_RPN;
176 uint64_t mask = (1UL << *psize) - 1;
178 /* Or high bits of rpn and low bits to ea to form whole real addr */
179 *raddr = (rpn & ~mask) | (eaddr & mask);
180 *pte_addr = base_addr + (index * sizeof(pde));
181 return pde;
184 /* Next Level of Radix Tree */
185 return ppc_radix64_walk_tree(cpu, eaddr, pde & R_PDE_NLB, pde & R_PDE_NLS,
186 raddr, psize, fault_cause, pte_addr);
189 int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx,
190 int mmu_idx)
192 CPUState *cs = CPU(cpu);
193 CPUPPCState *env = &cpu->env;
194 PPCVirtualHypervisorClass *vhc =
195 PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
196 hwaddr raddr, pte_addr;
197 uint64_t lpid = 0, pid = 0, offset, size, patbe, prtbe0, pte;
198 int page_size, prot, fault_cause = 0;
200 assert((rwx == 0) || (rwx == 1) || (rwx == 2));
201 assert(!msr_hv); /* For now there is no Radix PowerNV Support */
202 assert(cpu->vhyp);
203 assert(ppc64_use_proc_tbl(cpu));
205 /* Real Mode Access */
206 if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) {
207 /* In real mode top 4 effective addr bits (mostly) ignored */
208 raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL;
210 tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
211 PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx,
212 TARGET_PAGE_SIZE);
213 return 0;
216 /* Virtual Mode Access - get the fully qualified address */
217 if (!ppc_radix64_get_fully_qualified_addr(env, eaddr, &lpid, &pid)) {
218 ppc_radix64_raise_segi(cpu, rwx, eaddr);
219 return 1;
222 /* Get Process Table */
223 patbe = vhc->get_patbe(cpu->vhyp);
225 /* Index Process Table by PID to Find Corresponding Process Table Entry */
226 offset = pid * sizeof(struct prtb_entry);
227 size = 1ULL << ((patbe & PATBE1_R_PRTS) + 12);
228 if (offset >= size) {
229 /* offset exceeds size of the process table */
230 ppc_radix64_raise_si(cpu, rwx, eaddr, DSISR_NOPTE);
231 return 1;
233 prtbe0 = ldq_phys(cs->as, (patbe & PATBE1_R_PRTB) + offset);
235 /* Walk Radix Tree from Process Table Entry to Convert EA to RA */
236 page_size = PRTBE_R_GET_RTS(prtbe0);
237 pte = ppc_radix64_walk_tree(cpu, eaddr & R_EADDR_MASK,
238 prtbe0 & PRTBE_R_RPDB, prtbe0 & PRTBE_R_RPDS,
239 &raddr, &page_size, &fault_cause, &pte_addr);
240 if (!pte || ppc_radix64_check_prot(cpu, rwx, pte, &fault_cause, &prot)) {
241 /* Couldn't get pte or access denied due to protection */
242 ppc_radix64_raise_si(cpu, rwx, eaddr, fault_cause);
243 return 1;
246 /* Update Reference and Change Bits */
247 ppc_radix64_set_rc(cpu, rwx, pte, pte_addr, &prot);
249 tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
250 prot, mmu_idx, 1UL << page_size);
251 return 0;
254 hwaddr ppc_radix64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong eaddr)
256 CPUState *cs = CPU(cpu);
257 CPUPPCState *env = &cpu->env;
258 PPCVirtualHypervisorClass *vhc =
259 PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
260 hwaddr raddr, pte_addr;
261 uint64_t lpid = 0, pid = 0, offset, size, patbe, prtbe0, pte;
262 int page_size, fault_cause = 0;
264 /* Handle Real Mode */
265 if (msr_dr == 0) {
266 /* In real mode top 4 effective addr bits (mostly) ignored */
267 return eaddr & 0x0FFFFFFFFFFFFFFFULL;
270 /* Virtual Mode Access - get the fully qualified address */
271 if (!ppc_radix64_get_fully_qualified_addr(env, eaddr, &lpid, &pid)) {
272 return -1;
275 /* Get Process Table */
276 patbe = vhc->get_patbe(cpu->vhyp);
278 /* Index Process Table by PID to Find Corresponding Process Table Entry */
279 offset = pid * sizeof(struct prtb_entry);
280 size = 1ULL << ((patbe & PATBE1_R_PRTS) + 12);
281 if (offset >= size) {
282 /* offset exceeds size of the process table */
283 return -1;
285 prtbe0 = ldq_phys(cs->as, (patbe & PATBE1_R_PRTB) + offset);
287 /* Walk Radix Tree from Process Table Entry to Convert EA to RA */
288 page_size = PRTBE_R_GET_RTS(prtbe0);
289 pte = ppc_radix64_walk_tree(cpu, eaddr & R_EADDR_MASK,
290 prtbe0 & PRTBE_R_RPDB, prtbe0 & PRTBE_R_RPDS,
291 &raddr, &page_size, &fault_cause, &pte_addr);
292 if (!pte) {
293 return -1;
296 return raddr & TARGET_PAGE_MASK;