]>
Commit | Line | Data |
---|---|---|
7e1c521e XY |
1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ |
2 | /* | |
3 | * QEMU LoongArch TLB helpers | |
4 | * | |
5 | * Copyright (c) 2021 Loongson Technology Corporation Limited | |
6 | * | |
7 | */ | |
8 | ||
9 | #include "qemu/osdep.h" | |
fcbbeb8e | 10 | #include "qemu/guest-random.h" |
7e1c521e XY |
11 | |
12 | #include "cpu.h" | |
13 | #include "internals.h" | |
fcbbeb8e | 14 | #include "exec/helper-proto.h" |
7e1c521e XY |
15 | #include "exec/exec-all.h" |
16 | #include "exec/cpu_ldst.h" | |
17 | #include "exec/log.h" | |
18 | #include "cpu-csr.h" | |
19 | ||
7e1c521e XY |
20 | static void raise_mmu_exception(CPULoongArchState *env, target_ulong address, |
21 | MMUAccessType access_type, int tlb_error) | |
22 | { | |
23 | CPUState *cs = env_cpu(env); | |
24 | ||
25 | switch (tlb_error) { | |
26 | default: | |
27 | case TLBRET_BADADDR: | |
8752b130 SG |
28 | cs->exception_index = access_type == MMU_INST_FETCH |
29 | ? EXCCODE_ADEF : EXCCODE_ADEM; | |
7e1c521e XY |
30 | break; |
31 | case TLBRET_NOMATCH: | |
32 | /* No TLB match for a mapped address */ | |
33 | if (access_type == MMU_DATA_LOAD) { | |
34 | cs->exception_index = EXCCODE_PIL; | |
35 | } else if (access_type == MMU_DATA_STORE) { | |
36 | cs->exception_index = EXCCODE_PIS; | |
37 | } else if (access_type == MMU_INST_FETCH) { | |
38 | cs->exception_index = EXCCODE_PIF; | |
39 | } | |
40 | env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 1); | |
41 | break; | |
42 | case TLBRET_INVALID: | |
43 | /* TLB match with no valid bit */ | |
44 | if (access_type == MMU_DATA_LOAD) { | |
45 | cs->exception_index = EXCCODE_PIL; | |
46 | } else if (access_type == MMU_DATA_STORE) { | |
47 | cs->exception_index = EXCCODE_PIS; | |
48 | } else if (access_type == MMU_INST_FETCH) { | |
49 | cs->exception_index = EXCCODE_PIF; | |
50 | } | |
51 | break; | |
52 | case TLBRET_DIRTY: | |
53 | /* TLB match but 'D' bit is cleared */ | |
54 | cs->exception_index = EXCCODE_PME; | |
55 | break; | |
56 | case TLBRET_XI: | |
57 | /* Execute-Inhibit Exception */ | |
58 | cs->exception_index = EXCCODE_PNX; | |
59 | break; | |
60 | case TLBRET_RI: | |
61 | /* Read-Inhibit Exception */ | |
62 | cs->exception_index = EXCCODE_PNR; | |
63 | break; | |
64 | case TLBRET_PE: | |
65 | /* Privileged Exception */ | |
66 | cs->exception_index = EXCCODE_PPI; | |
67 | break; | |
68 | } | |
69 | ||
70 | if (tlb_error == TLBRET_NOMATCH) { | |
71 | env->CSR_TLBRBADV = address; | |
50fffcc4 JC |
72 | if (is_la64(env)) { |
73 | env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI_64, | |
74 | VPPN, extract64(address, 13, 35)); | |
75 | } else { | |
76 | env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI_32, | |
77 | VPPN, extract64(address, 13, 19)); | |
78 | } | |
7e1c521e XY |
79 | } else { |
80 | if (!FIELD_EX64(env->CSR_DBG, CSR_DBG, DST)) { | |
81 | env->CSR_BADV = address; | |
82 | } | |
83 | env->CSR_TLBEHI = address & (TARGET_PAGE_MASK << 1); | |
84 | } | |
85 | } | |
86 | ||
fcbbeb8e XY |
87 | static void invalidate_tlb_entry(CPULoongArchState *env, int index) |
88 | { | |
89 | target_ulong addr, mask, pagesize; | |
90 | uint8_t tlb_ps; | |
91 | LoongArchTLB *tlb = &env->tlb[index]; | |
92 | ||
93 | int mmu_idx = cpu_mmu_index(env, false); | |
94 | uint8_t tlb_v0 = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, V); | |
95 | uint8_t tlb_v1 = FIELD_EX64(tlb->tlb_entry1, TLBENTRY, V); | |
96 | uint64_t tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); | |
97 | ||
98 | if (index >= LOONGARCH_STLB) { | |
99 | tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); | |
100 | } else { | |
101 | tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); | |
102 | } | |
2b3ef8e5 | 103 | pagesize = MAKE_64BIT_MASK(tlb_ps, 1); |
fcbbeb8e XY |
104 | mask = MAKE_64BIT_MASK(0, tlb_ps + 1); |
105 | ||
106 | if (tlb_v0) { | |
107 | addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & ~mask; /* even */ | |
108 | tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize, | |
109 | mmu_idx, TARGET_LONG_BITS); | |
110 | } | |
111 | ||
112 | if (tlb_v1) { | |
113 | addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & pagesize; /* odd */ | |
114 | tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize, | |
115 | mmu_idx, TARGET_LONG_BITS); | |
116 | } | |
117 | } | |
118 | ||
119 | static void invalidate_tlb(CPULoongArchState *env, int index) | |
120 | { | |
121 | LoongArchTLB *tlb; | |
122 | uint16_t csr_asid, tlb_asid, tlb_g; | |
123 | ||
124 | csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); | |
125 | tlb = &env->tlb[index]; | |
126 | tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); | |
127 | tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); | |
128 | if (tlb_g == 0 && tlb_asid != csr_asid) { | |
129 | return; | |
130 | } | |
131 | invalidate_tlb_entry(env, index); | |
132 | } | |
133 | ||
134 | static void fill_tlb_entry(CPULoongArchState *env, int index) | |
135 | { | |
136 | LoongArchTLB *tlb = &env->tlb[index]; | |
137 | uint64_t lo0, lo1, csr_vppn; | |
138 | uint16_t csr_asid; | |
139 | uint8_t csr_ps; | |
140 | ||
141 | if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) { | |
142 | csr_ps = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, PS); | |
50fffcc4 JC |
143 | if (is_la64(env)) { |
144 | csr_vppn = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI_64, VPPN); | |
145 | } else { | |
146 | csr_vppn = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI_32, VPPN); | |
147 | } | |
fcbbeb8e XY |
148 | lo0 = env->CSR_TLBRELO0; |
149 | lo1 = env->CSR_TLBRELO1; | |
150 | } else { | |
151 | csr_ps = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, PS); | |
50fffcc4 JC |
152 | if (is_la64(env)) { |
153 | csr_vppn = FIELD_EX64(env->CSR_TLBEHI, CSR_TLBEHI_64, VPPN); | |
154 | } else { | |
155 | csr_vppn = FIELD_EX64(env->CSR_TLBEHI, CSR_TLBEHI_32, VPPN); | |
156 | } | |
fcbbeb8e XY |
157 | lo0 = env->CSR_TLBELO0; |
158 | lo1 = env->CSR_TLBELO1; | |
159 | } | |
160 | ||
161 | if (csr_ps == 0) { | |
162 | qemu_log_mask(CPU_LOG_MMU, "page size is 0\n"); | |
163 | } | |
164 | ||
165 | /* Only MTLB has the ps fields */ | |
166 | if (index >= LOONGARCH_STLB) { | |
167 | tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, PS, csr_ps); | |
168 | } | |
169 | ||
170 | tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, VPPN, csr_vppn); | |
171 | tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 1); | |
172 | csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); | |
173 | tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, ASID, csr_asid); | |
174 | ||
175 | tlb->tlb_entry0 = lo0; | |
176 | tlb->tlb_entry1 = lo1; | |
177 | } | |
178 | ||
179 | /* Return an random value between low and high */ | |
180 | static uint32_t get_random_tlb(uint32_t low, uint32_t high) | |
181 | { | |
182 | uint32_t val; | |
183 | ||
184 | qemu_guest_getrandom_nofail(&val, sizeof(val)); | |
185 | return val % (high - low + 1) + low; | |
186 | } | |
187 | ||
188 | void helper_tlbsrch(CPULoongArchState *env) | |
189 | { | |
190 | int index, match; | |
191 | ||
192 | if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) { | |
193 | match = loongarch_tlb_search(env, env->CSR_TLBREHI, &index); | |
194 | } else { | |
195 | match = loongarch_tlb_search(env, env->CSR_TLBEHI, &index); | |
196 | } | |
197 | ||
198 | if (match) { | |
199 | env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX, index); | |
200 | env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 0); | |
201 | return; | |
202 | } | |
203 | ||
204 | env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 1); | |
205 | } | |
206 | ||
207 | void helper_tlbrd(CPULoongArchState *env) | |
208 | { | |
209 | LoongArchTLB *tlb; | |
210 | int index; | |
211 | uint8_t tlb_ps, tlb_e; | |
212 | ||
213 | index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); | |
214 | tlb = &env->tlb[index]; | |
215 | ||
216 | if (index >= LOONGARCH_STLB) { | |
217 | tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); | |
218 | } else { | |
219 | tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); | |
220 | } | |
221 | tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); | |
222 | ||
223 | if (!tlb_e) { | |
224 | /* Invalid TLB entry */ | |
225 | env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 1); | |
226 | env->CSR_ASID = FIELD_DP64(env->CSR_ASID, CSR_ASID, ASID, 0); | |
227 | env->CSR_TLBEHI = 0; | |
228 | env->CSR_TLBELO0 = 0; | |
229 | env->CSR_TLBELO1 = 0; | |
230 | env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, PS, 0); | |
231 | } else { | |
232 | /* Valid TLB entry */ | |
233 | env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 0); | |
234 | env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, | |
235 | PS, (tlb_ps & 0x3f)); | |
236 | env->CSR_TLBEHI = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN) << | |
237 | R_TLB_MISC_VPPN_SHIFT; | |
238 | env->CSR_TLBELO0 = tlb->tlb_entry0; | |
239 | env->CSR_TLBELO1 = tlb->tlb_entry1; | |
240 | } | |
241 | } | |
242 | ||
243 | void helper_tlbwr(CPULoongArchState *env) | |
244 | { | |
245 | int index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); | |
246 | ||
247 | invalidate_tlb(env, index); | |
248 | ||
249 | if (FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, NE)) { | |
250 | env->tlb[index].tlb_misc = FIELD_DP64(env->tlb[index].tlb_misc, | |
251 | TLB_MISC, E, 0); | |
252 | return; | |
253 | } | |
254 | ||
255 | fill_tlb_entry(env, index); | |
256 | } | |
257 | ||
258 | void helper_tlbfill(CPULoongArchState *env) | |
259 | { | |
260 | uint64_t address, entryhi; | |
261 | int index, set, stlb_idx; | |
262 | uint16_t pagesize, stlb_ps; | |
263 | ||
264 | if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) { | |
265 | entryhi = env->CSR_TLBREHI; | |
266 | pagesize = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, PS); | |
267 | } else { | |
268 | entryhi = env->CSR_TLBEHI; | |
269 | pagesize = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, PS); | |
270 | } | |
271 | ||
272 | stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); | |
273 | ||
274 | if (pagesize == stlb_ps) { | |
275 | /* Only write into STLB bits [47:13] */ | |
50fffcc4 | 276 | address = entryhi & ~MAKE_64BIT_MASK(0, R_CSR_TLBEHI_64_VPPN_SHIFT); |
fcbbeb8e XY |
277 | |
278 | /* Choose one set ramdomly */ | |
279 | set = get_random_tlb(0, 7); | |
280 | ||
281 | /* Index in one set */ | |
282 | stlb_idx = (address >> (stlb_ps + 1)) & 0xff; /* [0,255] */ | |
283 | ||
284 | index = set * 256 + stlb_idx; | |
285 | } else { | |
286 | /* Only write into MTLB */ | |
287 | index = get_random_tlb(LOONGARCH_STLB, LOONGARCH_TLB_MAX - 1); | |
288 | } | |
289 | ||
290 | invalidate_tlb(env, index); | |
291 | fill_tlb_entry(env, index); | |
292 | } | |
293 | ||
294 | void helper_tlbclr(CPULoongArchState *env) | |
295 | { | |
296 | LoongArchTLB *tlb; | |
297 | int i, index; | |
298 | uint16_t csr_asid, tlb_asid, tlb_g; | |
299 | ||
300 | csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); | |
301 | index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); | |
302 | ||
303 | if (index < LOONGARCH_STLB) { | |
304 | /* STLB. One line per operation */ | |
305 | for (i = 0; i < 8; i++) { | |
306 | tlb = &env->tlb[i * 256 + (index % 256)]; | |
307 | tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); | |
308 | tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); | |
309 | if (!tlb_g && tlb_asid == csr_asid) { | |
310 | tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); | |
311 | } | |
312 | } | |
313 | } else if (index < LOONGARCH_TLB_MAX) { | |
314 | /* All MTLB entries */ | |
315 | for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; i++) { | |
316 | tlb = &env->tlb[i]; | |
317 | tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); | |
318 | tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); | |
319 | if (!tlb_g && tlb_asid == csr_asid) { | |
320 | tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); | |
321 | } | |
322 | } | |
323 | } | |
324 | ||
325 | tlb_flush(env_cpu(env)); | |
326 | } | |
327 | ||
328 | void helper_tlbflush(CPULoongArchState *env) | |
329 | { | |
330 | int i, index; | |
331 | ||
332 | index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); | |
333 | ||
334 | if (index < LOONGARCH_STLB) { | |
335 | /* STLB. One line per operation */ | |
336 | for (i = 0; i < 8; i++) { | |
337 | int s_idx = i * 256 + (index % 256); | |
338 | env->tlb[s_idx].tlb_misc = FIELD_DP64(env->tlb[s_idx].tlb_misc, | |
339 | TLB_MISC, E, 0); | |
340 | } | |
341 | } else if (index < LOONGARCH_TLB_MAX) { | |
342 | /* All MTLB entries */ | |
343 | for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; i++) { | |
344 | env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc, | |
345 | TLB_MISC, E, 0); | |
346 | } | |
347 | } | |
348 | ||
349 | tlb_flush(env_cpu(env)); | |
350 | } | |
351 | ||
352 | void helper_invtlb_all(CPULoongArchState *env) | |
353 | { | |
354 | for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { | |
355 | env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc, | |
356 | TLB_MISC, E, 0); | |
357 | } | |
358 | tlb_flush(env_cpu(env)); | |
359 | } | |
360 | ||
361 | void helper_invtlb_all_g(CPULoongArchState *env, uint32_t g) | |
362 | { | |
363 | for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { | |
364 | LoongArchTLB *tlb = &env->tlb[i]; | |
365 | uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); | |
366 | ||
367 | if (tlb_g == g) { | |
368 | tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); | |
369 | } | |
370 | } | |
371 | tlb_flush(env_cpu(env)); | |
372 | } | |
373 | ||
374 | void helper_invtlb_all_asid(CPULoongArchState *env, target_ulong info) | |
375 | { | |
376 | uint16_t asid = info & R_CSR_ASID_ASID_MASK; | |
377 | ||
378 | for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { | |
379 | LoongArchTLB *tlb = &env->tlb[i]; | |
380 | uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); | |
381 | uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); | |
382 | ||
383 | if (!tlb_g && (tlb_asid == asid)) { | |
384 | tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); | |
385 | } | |
386 | } | |
387 | tlb_flush(env_cpu(env)); | |
388 | } | |
389 | ||
390 | void helper_invtlb_page_asid(CPULoongArchState *env, target_ulong info, | |
391 | target_ulong addr) | |
392 | { | |
393 | uint16_t asid = info & 0x3ff; | |
394 | ||
395 | for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { | |
396 | LoongArchTLB *tlb = &env->tlb[i]; | |
397 | uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); | |
398 | uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); | |
399 | uint64_t vpn, tlb_vppn; | |
400 | uint8_t tlb_ps, compare_shift; | |
401 | ||
402 | if (i >= LOONGARCH_STLB) { | |
403 | tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); | |
404 | } else { | |
405 | tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); | |
406 | } | |
407 | tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); | |
408 | vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1); | |
409 | compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; | |
410 | ||
411 | if (!tlb_g && (tlb_asid == asid) && | |
412 | (vpn == (tlb_vppn >> compare_shift))) { | |
413 | tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); | |
414 | } | |
415 | } | |
416 | tlb_flush(env_cpu(env)); | |
417 | } | |
418 | ||
419 | void helper_invtlb_page_asid_or_g(CPULoongArchState *env, | |
420 | target_ulong info, target_ulong addr) | |
421 | { | |
422 | uint16_t asid = info & 0x3ff; | |
423 | ||
424 | for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { | |
425 | LoongArchTLB *tlb = &env->tlb[i]; | |
426 | uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); | |
427 | uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); | |
428 | uint64_t vpn, tlb_vppn; | |
429 | uint8_t tlb_ps, compare_shift; | |
430 | ||
431 | if (i >= LOONGARCH_STLB) { | |
432 | tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); | |
433 | } else { | |
434 | tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); | |
435 | } | |
436 | tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); | |
437 | vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1); | |
438 | compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; | |
439 | ||
440 | if ((tlb_g || (tlb_asid == asid)) && | |
441 | (vpn == (tlb_vppn >> compare_shift))) { | |
442 | tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); | |
443 | } | |
444 | } | |
445 | tlb_flush(env_cpu(env)); | |
446 | } | |
447 | ||
7e1c521e XY |
448 | bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, |
449 | MMUAccessType access_type, int mmu_idx, | |
450 | bool probe, uintptr_t retaddr) | |
451 | { | |
452 | LoongArchCPU *cpu = LOONGARCH_CPU(cs); | |
453 | CPULoongArchState *env = &cpu->env; | |
454 | hwaddr physical; | |
455 | int prot; | |
8752b130 | 456 | int ret; |
7e1c521e XY |
457 | |
458 | /* Data access */ | |
459 | ret = get_physical_address(env, &physical, &prot, address, | |
460 | access_type, mmu_idx); | |
461 | ||
462 | if (ret == TLBRET_MATCH) { | |
463 | tlb_set_page(cs, address & TARGET_PAGE_MASK, | |
464 | physical & TARGET_PAGE_MASK, prot, | |
465 | mmu_idx, TARGET_PAGE_SIZE); | |
466 | qemu_log_mask(CPU_LOG_MMU, | |
883f2c59 | 467 | "%s address=%" VADDR_PRIx " physical " HWADDR_FMT_plx |
7e1c521e XY |
468 | " prot %d\n", __func__, address, physical, prot); |
469 | return true; | |
470 | } else { | |
471 | qemu_log_mask(CPU_LOG_MMU, | |
472 | "%s address=%" VADDR_PRIx " ret %d\n", __func__, address, | |
473 | ret); | |
474 | } | |
475 | if (probe) { | |
476 | return false; | |
477 | } | |
478 | raise_mmu_exception(env, address, access_type, ret); | |
479 | cpu_loop_exit_restore(cs, retaddr); | |
480 | } | |
d2cba6f7 XY |
481 | |
482 | target_ulong helper_lddir(CPULoongArchState *env, target_ulong base, | |
483 | target_ulong level, uint32_t mem_idx) | |
484 | { | |
485 | CPUState *cs = env_cpu(env); | |
486 | target_ulong badvaddr, index, phys, ret; | |
487 | int shift; | |
488 | uint64_t dir_base, dir_width; | |
489 | bool huge = (base >> LOONGARCH_PAGE_HUGE_SHIFT) & 0x1; | |
490 | ||
491 | badvaddr = env->CSR_TLBRBADV; | |
492 | base = base & TARGET_PHYS_MASK; | |
493 | ||
494 | /* 0:64bit, 1:128bit, 2:192bit, 3:256bit */ | |
495 | shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH); | |
496 | shift = (shift + 1) * 3; | |
497 | ||
498 | if (huge) { | |
499 | return base; | |
500 | } | |
501 | switch (level) { | |
502 | case 1: | |
503 | dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_BASE); | |
504 | dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_WIDTH); | |
505 | break; | |
506 | case 2: | |
507 | dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_BASE); | |
508 | dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_WIDTH); | |
509 | break; | |
510 | case 3: | |
511 | dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_BASE); | |
512 | dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_WIDTH); | |
513 | break; | |
514 | case 4: | |
515 | dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_BASE); | |
516 | dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_WIDTH); | |
517 | break; | |
518 | default: | |
519 | do_raise_exception(env, EXCCODE_INE, GETPC()); | |
520 | return 0; | |
521 | } | |
522 | index = (badvaddr >> dir_base) & ((1 << dir_width) - 1); | |
523 | phys = base | index << shift; | |
524 | ret = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK; | |
525 | return ret; | |
526 | } | |
527 | ||
528 | void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, | |
529 | uint32_t mem_idx) | |
530 | { | |
531 | CPUState *cs = env_cpu(env); | |
532 | target_ulong phys, tmp0, ptindex, ptoffset0, ptoffset1, ps, badv; | |
533 | int shift; | |
534 | bool huge = (base >> LOONGARCH_PAGE_HUGE_SHIFT) & 0x1; | |
535 | uint64_t ptbase = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE); | |
536 | uint64_t ptwidth = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH); | |
537 | ||
538 | base = base & TARGET_PHYS_MASK; | |
539 | ||
540 | if (huge) { | |
541 | /* Huge Page. base is paddr */ | |
542 | tmp0 = base ^ (1 << LOONGARCH_PAGE_HUGE_SHIFT); | |
543 | /* Move Global bit */ | |
544 | tmp0 = ((tmp0 & (1 << LOONGARCH_HGLOBAL_SHIFT)) >> | |
545 | LOONGARCH_HGLOBAL_SHIFT) << R_TLBENTRY_G_SHIFT | | |
1d832c19 | 546 | (tmp0 & (~(1 << LOONGARCH_HGLOBAL_SHIFT))); |
d2cba6f7 XY |
547 | ps = ptbase + ptwidth - 1; |
548 | if (odd) { | |
2b3ef8e5 | 549 | tmp0 += MAKE_64BIT_MASK(ps, 1); |
d2cba6f7 XY |
550 | } |
551 | } else { | |
552 | /* 0:64bit, 1:128bit, 2:192bit, 3:256bit */ | |
553 | shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH); | |
554 | shift = (shift + 1) * 3; | |
555 | badv = env->CSR_TLBRBADV; | |
556 | ||
557 | ptindex = (badv >> ptbase) & ((1 << ptwidth) - 1); | |
558 | ptindex = ptindex & ~0x1; /* clear bit 0 */ | |
559 | ptoffset0 = ptindex << shift; | |
560 | ptoffset1 = (ptindex + 1) << shift; | |
561 | ||
562 | phys = base | (odd ? ptoffset1 : ptoffset0); | |
563 | tmp0 = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK; | |
564 | ps = ptbase; | |
565 | } | |
566 | ||
567 | if (odd) { | |
568 | env->CSR_TLBRELO1 = tmp0; | |
569 | } else { | |
570 | env->CSR_TLBRELO0 = tmp0; | |
571 | } | |
572 | env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI, PS, ps); | |
573 | } |