1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 "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"
30
31static bool ppc_radix64_get_fully_qualified_addr(CPUPPCState *env, vaddr eaddr,
32 uint64_t *lpid, uint64_t *pid)
33{
34
35 assert(!msr_hv);
36
37 if (!msr_hv) {
38 switch (eaddr & R_EADDR_QUADRANT) {
39 case R_EADDR_QUADRANT0:
40 *lpid = env->spr[SPR_LPIDR];
41 *pid = env->spr[SPR_BOOKS_PID];
42 break;
43 case R_EADDR_QUADRANT1:
44 case R_EADDR_QUADRANT2:
45 return false;
46 case R_EADDR_QUADRANT3:
47 *lpid = env->spr[SPR_LPIDR];
48 *pid = 0;
49 break;
50 }
51 }
52
53 return true;
54}
55
56static void ppc_radix64_raise_segi(PowerPCCPU *cpu, int rwx, vaddr eaddr)
57{
58 CPUState *cs = CPU(cpu);
59 CPUPPCState *env = &cpu->env;
60
61 if (rwx == 2) {
62 cs->exception_index = POWERPC_EXCP_ISEG;
63 } else {
64 cs->exception_index = POWERPC_EXCP_DSEG;
65 env->spr[SPR_DAR] = eaddr;
66 }
67 env->error_code = 0;
68}
69
70static void ppc_radix64_raise_si(PowerPCCPU *cpu, int rwx, vaddr eaddr,
71 uint32_t cause)
72{
73 CPUState *cs = CPU(cpu);
74 CPUPPCState *env = &cpu->env;
75
76 if (rwx == 2) {
77 cs->exception_index = POWERPC_EXCP_ISI;
78 env->error_code = cause;
79 } else {
80 cs->exception_index = POWERPC_EXCP_DSI;
81 if (rwx == 1) {
82 cause |= DSISR_ISSTORE;
83 }
84 env->spr[SPR_DSISR] = cause;
85 env->spr[SPR_DAR] = eaddr;
86 env->error_code = 0;
87 }
88}
89
90
91static bool ppc_radix64_check_prot(PowerPCCPU *cpu, int rwx, uint64_t pte,
92 int *fault_cause, int *prot)
93{
94 CPUPPCState *env = &cpu->env;
95 const int need_prot[] = { PAGE_READ, PAGE_WRITE, PAGE_EXEC };
96
97
98 if (((pte & R_PTE_ATT) == R_PTE_ATT_NI_IO) && (rwx == 2)) {
99
100
101
102
103 *fault_cause |= SRR1_NOEXEC_GUARD;
104 return true;
105 }
106
107
108 if ((pte & R_PTE_EAA_PRIV) && msr_pr) {
109 *prot = 0;
110 } else if (msr_pr || (pte & R_PTE_EAA_PRIV)) {
111 *prot = ppc_radix64_get_prot_eaa(pte);
112 } else {
113 *prot = ppc_radix64_get_prot_eaa(pte);
114 *prot &= ppc_radix64_get_prot_amr(cpu);
115 }
116
117
118 if (need_prot[rwx] & ~(*prot)) {
119 *fault_cause |= DSISR_PROTFAULT;
120 return true;
121 }
122
123 return false;
124}
125
126static void ppc_radix64_set_rc(PowerPCCPU *cpu, int rwx, uint64_t pte,
127 hwaddr pte_addr, int *prot)
128{
129 CPUState *cs = CPU(cpu);
130 uint64_t npte;
131
132 npte = pte | R_PTE_R;
133
134 if (rwx == 1) {
135 npte |= R_PTE_C;
136 } else {
137
138
139
140
141 *prot &= ~PAGE_WRITE;
142 }
143
144 if (pte ^ npte) {
145 stq_phys(cs->as, pte_addr, npte);
146 }
147}
148
149static 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)
153{
154 CPUState *cs = CPU(cpu);
155 uint64_t index, pde;
156
157 if (nls < 5) {
158 *fault_cause |= DSISR_R_BADCONFIG;
159 return 0;
160 }
161
162
163 index = eaddr >> (*psize - nls);
164 index &= ((1UL << nls) - 1);
165 pde = ldq_phys(cs->as, base_addr + (index * sizeof(pde)));
166 if (!(pde & R_PTE_VALID)) {
167 *fault_cause |= DSISR_NOPTE;
168 return 0;
169 }
170
171 *psize -= nls;
172
173
174 if (pde & R_PTE_LEAF) {
175 uint64_t rpn = pde & R_PTE_RPN;
176 uint64_t mask = (1UL << *psize) - 1;
177
178
179 *raddr = (rpn & ~mask) | (eaddr & mask);
180 *pte_addr = base_addr + (index * sizeof(pde));
181 return pde;
182 }
183
184
185 return ppc_radix64_walk_tree(cpu, eaddr, pde & R_PDE_NLB, pde & R_PDE_NLS,
186 raddr, psize, fault_cause, pte_addr);
187}
188
189int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx,
190 int mmu_idx)
191{
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;
199
200 assert((rwx == 0) || (rwx == 1) || (rwx == 2));
201 assert(!msr_hv);
202 assert(cpu->vhyp);
203 assert(ppc64_use_proc_tbl(cpu));
204
205
206 if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) {
207
208 raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL;
209
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;
214 }
215
216
217 if (!ppc_radix64_get_fully_qualified_addr(env, eaddr, &lpid, &pid)) {
218 ppc_radix64_raise_segi(cpu, rwx, eaddr);
219 return 1;
220 }
221
222
223 patbe = vhc->get_patbe(cpu->vhyp);
224
225
226 offset = pid * sizeof(struct prtb_entry);
227 size = 1ULL << ((patbe & PATBE1_R_PRTS) + 12);
228 if (offset >= size) {
229
230 ppc_radix64_raise_si(cpu, rwx, eaddr, DSISR_NOPTE);
231 return 1;
232 }
233 prtbe0 = ldq_phys(cs->as, (patbe & PATBE1_R_PRTB) + offset);
234
235
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
242 ppc_radix64_raise_si(cpu, rwx, eaddr, fault_cause);
243 return 1;
244 }
245
246
247 ppc_radix64_set_rc(cpu, rwx, pte, pte_addr, &prot);
248
249 tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
250 prot, mmu_idx, 1UL << page_size);
251 return 0;
252}
253
254hwaddr ppc_radix64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong eaddr)
255{
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;
263
264
265 if (msr_dr == 0) {
266
267 return eaddr & 0x0FFFFFFFFFFFFFFFULL;
268 }
269
270
271 if (!ppc_radix64_get_fully_qualified_addr(env, eaddr, &lpid, &pid)) {
272 return -1;
273 }
274
275
276 patbe = vhc->get_patbe(cpu->vhyp);
277
278
279 offset = pid * sizeof(struct prtb_entry);
280 size = 1ULL << ((patbe & PATBE1_R_PRTS) + 12);
281 if (offset >= size) {
282
283 return -1;
284 }
285 prtbe0 = ldq_phys(cs->as, (patbe & PATBE1_R_PRTB) + offset);
286
287
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;
294 }
295
296 return raddr & TARGET_PAGE_MASK;
297}
298