master
c 470 lines 13.6 KB
Raw
1 /*
2 * Hexagon TLB QOM Device
3 *
4 * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8 #include "qemu/osdep.h"
9 #include "qemu/log.h"
10 #include "hw/hexagon/hexagon_tlb.h"
11 #include "hw/core/qdev-properties.h"
12 #include "hw/core/resettable.h"
13 #include "migration/vmstate.h"
14 #include "monitor/monitor.h"
15 #include "monitor/hmp.h"
16 #include "qapi/error.h"
17 #include "exec/page-protection.h"
18 #include "exec/target_page.h"
19 #include "target/hexagon/cpu.h"
20 #include "target/hexagon/cpu_bits.h"
21
22 /* PTE (TLB entry) field extraction */
23 #define GET_PTE_PPD(entry) extract64((entry), 0, 24)
24 #define GET_PTE_C(entry) extract64((entry), 24, 4)
25 #define GET_PTE_U(entry) extract64((entry), 28, 1)
26 #define GET_PTE_R(entry) extract64((entry), 29, 1)
27 #define GET_PTE_W(entry) extract64((entry), 30, 1)
28 #define GET_PTE_X(entry) extract64((entry), 31, 1)
29 #define GET_PTE_VPN(entry) extract64((entry), 32, 20)
30 #define GET_PTE_ASID(entry) extract64((entry), 52, 7)
31 #define GET_PTE_ATR0(entry) extract64((entry), 59, 1)
32 #define GET_PTE_ATR1(entry) extract64((entry), 60, 1)
33 #define GET_PTE_PA35(entry) extract64((entry), 61, 1)
34 #define GET_PTE_G(entry) extract64((entry), 62, 1)
35 #define GET_PTE_V(entry) extract64((entry), 63, 1)
36
37 /* PPD (physical page descriptor) */
38 static inline uint64_t GET_PPD(uint64_t entry)
39 {
40 return GET_PTE_PPD(entry) | (GET_PTE_PA35(entry) << 24);
41 }
42
43 #define NO_ASID (1 << 8)
44
45 typedef enum {
46 PGSIZE_4K,
47 PGSIZE_16K,
48 PGSIZE_64K,
49 PGSIZE_256K,
50 PGSIZE_1M,
51 PGSIZE_4M,
52 PGSIZE_16M,
53 PGSIZE_64M,
54 PGSIZE_256M,
55 PGSIZE_1G,
56 } tlb_pgsize_t;
57
58 #define NUM_PGSIZE_TYPES (PGSIZE_1G + 1)
59
60 #define INVALID_MASK 0xffffffffLL
61
62 static const uint64_t encmask_2_mask[] = {
63 0x0fffLL, /* 4k, 0000 */
64 0x3fffLL, /* 16k, 0001 */
65 0xffffLL, /* 64k, 0010 */
66 0x3ffffLL, /* 256k, 0011 */
67 0xfffffLL, /* 1m, 0100 */
68 0x3fffffLL, /* 4m, 0101 */
69 0xffffffLL, /* 16m, 0110 */
70 0x3ffffffLL, /* 64m, 0111 */
71 0xfffffffLL, /* 256m, 1000 */
72 0x3fffffffLL, /* 1g, 1001 */
73 INVALID_MASK, /* RSVD, 1010 */
74 };
75
76 static inline tlb_pgsize_t hex_tlb_pgsize_type(uint64_t entry)
77 {
78 if (entry == 0) {
79 qemu_log_mask(CPU_LOG_MMU, "%s: Supplied TLB entry was 0!\n",
80 __func__);
81 return 0;
82 }
83 tlb_pgsize_t size = ctz64(entry);
84 g_assert(size < NUM_PGSIZE_TYPES);
85 return size;
86 }
87
88 static inline uint64_t hex_tlb_page_size_bytes(uint64_t entry)
89 {
90 return 1ull << (qemu_target_page_bits() + 2 * hex_tlb_pgsize_type(entry));
91 }
92
93 static inline uint64_t hex_tlb_phys_page_num(uint64_t entry)
94 {
95 uint32_t ppd = GET_PPD(entry);
96 return ppd >> 1;
97 }
98
99 static inline uint64_t hex_tlb_phys_addr(uint64_t entry)
100 {
101 uint64_t pagemask = encmask_2_mask[hex_tlb_pgsize_type(entry)];
102 uint64_t pagenum = hex_tlb_phys_page_num(entry);
103 uint64_t PA = (pagenum << qemu_target_page_bits()) & (~pagemask);
104 return PA;
105 }
106
107 static inline uint64_t hex_tlb_virt_addr(uint64_t entry)
108 {
109 return (uint64_t)GET_PTE_VPN(entry) << qemu_target_page_bits();
110 }
111
112 #ifdef CONFIG_HMP
113 static const char *pgsize_str[NUM_PGSIZE_TYPES] = {
114 "4K",
115 "16K",
116 "64K",
117 "256K",
118 "1M",
119 "4M",
120 "16M",
121 "64M",
122 "256M",
123 "1G",
124 };
125
126 bool hexagon_tlb_dump_entry(MonitorHMP *hmp, uint64_t entry)
127 {
128 if (GET_PTE_V(entry)) {
129 uint64_t PA = hex_tlb_phys_addr(entry);
130 uint64_t VA = hex_tlb_virt_addr(entry);
131 monitor_hmp_printf(hmp, "0x%016" PRIx64 ": ", entry);
132 monitor_hmp_printf(hmp, "V:%" PRId64 " G:%" PRId64
133 " A1:%" PRId64 " A0:%" PRId64,
134 GET_PTE_V(entry),
135 GET_PTE_G(entry),
136 GET_PTE_ATR1(entry),
137 GET_PTE_ATR0(entry));
138 monitor_hmp_printf(hmp, " ASID:0x%02" PRIx64 " VA:0x%08" PRIx64,
139 GET_PTE_ASID(entry), VA);
140 monitor_hmp_printf(hmp,
141 " X:%" PRId64 " W:%" PRId64 " R:%" PRId64
142 " U:%" PRId64 " C:%" PRId64,
143 GET_PTE_X(entry),
144 GET_PTE_W(entry),
145 GET_PTE_R(entry),
146 GET_PTE_U(entry),
147 GET_PTE_C(entry));
148 monitor_hmp_printf(hmp, " PA:0x%09" PRIx64 " SZ:%s (0x%" PRIx64 ")",
149 PA, pgsize_str[hex_tlb_pgsize_type(entry)],
150 hex_tlb_page_size_bytes(entry));
151 monitor_hmp_printf(hmp, "\n");
152 return true;
153 }
154
155 /* Not valid */
156 return false;
157 }
158
159 void hexagon_tlb_dump(MonitorHMP *hmp, HexagonTLBState *tlb)
160 {
161 for (uint32_t i = 0; i < tlb->num_entries; i++) {
162 hexagon_tlb_dump_entry(hmp, tlb->entries[i]);
163 }
164 }
165 #endif
166
167 static inline bool hex_tlb_entry_match_noperm(uint64_t entry, uint32_t asid,
168 uint64_t VA)
169 {
170 if (GET_PTE_V(entry)) {
171 if (GET_PTE_G(entry)) {
172 /* Global entry - ignore ASID */
173 } else if (asid != NO_ASID) {
174 uint32_t tlb_asid = GET_PTE_ASID(entry);
175 if (tlb_asid != asid) {
176 return false;
177 }
178 }
179
180 uint64_t page_size = hex_tlb_page_size_bytes(entry);
181 uint64_t page_start =
182 ROUND_DOWN(hex_tlb_virt_addr(entry), page_size);
183 if (page_start <= VA && VA < page_start + page_size) {
184 return true;
185 }
186 }
187 return false;
188 }
189
190 static inline void hex_tlb_entry_get_perm(uint64_t entry,
191 MMUAccessType access_type,
192 int mmu_idx, int *prot,
193 int32_t *excp, int *cause_code)
194 {
195 bool perm_x = GET_PTE_X(entry);
196 bool perm_w = GET_PTE_W(entry);
197 bool perm_r = GET_PTE_R(entry);
198 bool perm_u = GET_PTE_U(entry);
199 bool user_idx = mmu_idx == MMU_USER_IDX;
200
201 if (mmu_idx == MMU_KERNEL_IDX) {
202 *prot = PAGE_VALID | PAGE_READ | PAGE_WRITE | PAGE_EXEC;
203 return;
204 }
205
206 *prot = PAGE_VALID;
207 switch (access_type) {
208 case MMU_INST_FETCH:
209 if (user_idx && !perm_u) {
210 *excp = HEX_EVENT_PRECISE;
211 *cause_code = HEX_CAUSE_FETCH_NO_UPAGE;
212 } else if (!perm_x) {
213 *excp = HEX_EVENT_PRECISE;
214 *cause_code = HEX_CAUSE_FETCH_NO_XPAGE;
215 }
216 break;
217 case MMU_DATA_LOAD:
218 if (user_idx && !perm_u) {
219 *excp = HEX_EVENT_PRECISE;
220 *cause_code = HEX_CAUSE_PRIV_NO_UREAD;
221 } else if (!perm_r) {
222 *excp = HEX_EVENT_PRECISE;
223 *cause_code = HEX_CAUSE_PRIV_NO_READ;
224 }
225 break;
226 case MMU_DATA_STORE:
227 if (user_idx && !perm_u) {
228 *excp = HEX_EVENT_PRECISE;
229 *cause_code = HEX_CAUSE_PRIV_NO_UWRITE;
230 } else if (!perm_w) {
231 *excp = HEX_EVENT_PRECISE;
232 *cause_code = HEX_CAUSE_PRIV_NO_WRITE;
233 }
234 break;
235 }
236
237 if (!user_idx || perm_u) {
238 if (perm_x) {
239 *prot |= PAGE_EXEC;
240 }
241 if (perm_r) {
242 *prot |= PAGE_READ;
243 }
244 if (perm_w) {
245 *prot |= PAGE_WRITE;
246 }
247 }
248 }
249
250 static inline bool hex_tlb_entry_match(uint64_t entry, uint8_t asid,
251 uint32_t VA,
252 MMUAccessType access_type, hwaddr *PA,
253 int *prot, uint64_t *size,
254 int32_t *excp, int *cause_code,
255 int mmu_idx)
256 {
257 if (hex_tlb_entry_match_noperm(entry, asid, VA)) {
258 hex_tlb_entry_get_perm(entry, access_type, mmu_idx, prot, excp,
259 cause_code);
260 *PA = hex_tlb_phys_addr(entry);
261 *size = hex_tlb_page_size_bytes(entry);
262 return true;
263 }
264 return false;
265 }
266
267 static bool hex_tlb_is_match(uint64_t entry1, uint64_t entry2,
268 bool consider_gbit)
269 {
270 bool valid1 = GET_PTE_V(entry1);
271 bool valid2 = GET_PTE_V(entry2);
272 uint64_t size1 = hex_tlb_page_size_bytes(entry1);
273 uint64_t vaddr1 = ROUND_DOWN(hex_tlb_virt_addr(entry1), size1);
274 uint64_t size2 = hex_tlb_page_size_bytes(entry2);
275 uint64_t vaddr2 = ROUND_DOWN(hex_tlb_virt_addr(entry2), size2);
276 int asid1 = GET_PTE_ASID(entry1);
277 int asid2 = GET_PTE_ASID(entry2);
278 bool gbit1 = GET_PTE_G(entry1);
279 bool gbit2 = GET_PTE_G(entry2);
280
281 if (!valid1 || !valid2) {
282 return false;
283 }
284
285 if (((vaddr1 <= vaddr2) && (vaddr2 < (vaddr1 + size1))) ||
286 ((vaddr2 <= vaddr1) && (vaddr1 < (vaddr2 + size2)))) {
287 if (asid1 == asid2) {
288 return true;
289 }
290 if ((consider_gbit && gbit1) || gbit2) {
291 return true;
292 }
293 }
294 return false;
295 }
296
297 /* Public API */
298
299 uint64_t hexagon_tlb_read(HexagonTLBState *tlb, uint32_t index)
300 {
301 g_assert(index < tlb->num_entries);
302 return tlb->entries[index];
303 }
304
305 void hexagon_tlb_write(HexagonTLBState *tlb, uint32_t index, uint64_t value)
306 {
307 g_assert(index < tlb->num_entries);
308 tlb->entries[index] = value;
309 }
310
311 bool hexagon_tlb_find_match(HexagonTLBState *tlb, uint32_t asid,
312 uint32_t VA, MMUAccessType access_type,
313 hwaddr *PA, int *prot, uint64_t *size,
314 int32_t *excp, int *cause_code, int mmu_idx)
315 {
316 *PA = 0;
317 *prot = 0;
318 *size = 0;
319 *excp = 0;
320 *cause_code = 0;
321
322 for (uint32_t i = 0; i < tlb->num_entries; i++) {
323 if (hex_tlb_entry_match(tlb->entries[i], asid, VA, access_type,
324 PA, prot, size, excp, cause_code, mmu_idx)) {
325 return true;
326 }
327 }
328 return false;
329 }
330
331 uint32_t hexagon_tlb_lookup(HexagonTLBState *tlb, uint32_t asid,
332 uint32_t VA, int *cause_code)
333 {
334 uint32_t not_found = 0x80000000;
335 uint32_t idx = not_found;
336
337 for (uint32_t i = 0; i < tlb->num_entries; i++) {
338 uint64_t entry = tlb->entries[i];
339 if (hex_tlb_entry_match_noperm(entry, asid, VA)) {
340 if (idx != not_found) {
341 *cause_code = HEX_CAUSE_IMPRECISE_MULTI_TLB_MATCH;
342 break;
343 }
344 idx = i;
345 }
346 }
347
348 if (idx == not_found) {
349 qemu_log_mask(CPU_LOG_MMU,
350 "%s: 0x%" PRIx32 ", 0x%08" PRIx32 " => NOT FOUND\n",
351 __func__, asid, VA);
352 } else {
353 qemu_log_mask(CPU_LOG_MMU,
354 "%s: 0x%" PRIx32 ", 0x%08" PRIx32 " => %d\n",
355 __func__, asid, VA, idx);
356 }
357
358 return idx;
359 }
360
361 /*
362 * Return codes:
363 * 0 or positive index of match
364 * -1 multiple matches
365 * -2 no match
366 */
367 int hexagon_tlb_check_overlap(HexagonTLBState *tlb, uint64_t entry,
368 uint64_t index)
369 {
370 int matches = 0;
371 int last_match = 0;
372
373 for (uint32_t i = 0; i < tlb->num_entries; i++) {
374 if (hex_tlb_is_match(entry, tlb->entries[i], false)) {
375 matches++;
376 last_match = i;
377 }
378 }
379
380 if (matches == 1) {
381 return last_match;
382 }
383 if (matches == 0) {
384 return -2;
385 }
386 return -1;
387 }
388
389 uint32_t hexagon_tlb_get_num_entries(HexagonTLBState *tlb)
390 {
391 return tlb->num_entries;
392 }
393
394 /* QOM lifecycle */
395
396 static void hexagon_tlb_init(Object *obj)
397 {
398 }
399
400 static void hexagon_tlb_realize(DeviceState *dev, Error **errp)
401 {
402 HexagonTLBState *s = HEXAGON_TLB(dev);
403
404 if (s->num_entries == 0 || s->num_entries > MAX_TLB_ENTRIES) {
405 error_setg(errp, "Invalid TLB num-entries: %" PRIu32,
406 s->num_entries);
407 return;
408 }
409 s->entries = g_new0(uint64_t, s->num_entries);
410 }
411
412 static void hexagon_tlb_unrealize(DeviceState *dev)
413 {
414 HexagonTLBState *s = HEXAGON_TLB(dev);
415 g_free(s->entries);
416 s->entries = NULL;
417 }
418
419 static void hexagon_tlb_reset_hold(Object *obj, ResetType type)
420 {
421 HexagonTLBState *s = HEXAGON_TLB(obj);
422 if (s->entries) {
423 memset(s->entries, 0, sizeof(uint64_t) * s->num_entries);
424 }
425 }
426
427 static const VMStateDescription vmstate_hexagon_tlb = {
428 .name = "hexagon-tlb",
429 .version_id = 0,
430 .minimum_version_id = 0,
431 .fields = (const VMStateField[]) {
432 VMSTATE_UINT32(num_entries, HexagonTLBState),
433 VMSTATE_VARRAY_UINT32_ALLOC(entries, HexagonTLBState, num_entries,
434 0, vmstate_info_uint64, uint64_t),
435 VMSTATE_END_OF_LIST()
436 },
437 };
438
439 static const Property hexagon_tlb_properties[] = {
440 DEFINE_PROP_UINT32("num-entries", HexagonTLBState, num_entries,
441 MAX_TLB_ENTRIES),
442 };
443
444 static void hexagon_tlb_class_init(ObjectClass *klass, const void *data)
445 {
446 DeviceClass *dc = DEVICE_CLASS(klass);
447 ResettableClass *rc = RESETTABLE_CLASS(klass);
448
449 dc->realize = hexagon_tlb_realize;
450 dc->unrealize = hexagon_tlb_unrealize;
451 rc->phases.hold = hexagon_tlb_reset_hold;
452 dc->vmsd = &vmstate_hexagon_tlb;
453 dc->user_creatable = false;
454 device_class_set_props(dc, hexagon_tlb_properties);
455 }
456
457 static const TypeInfo hexagon_tlb_info = {
458 .name = TYPE_HEXAGON_TLB,
459 .parent = TYPE_SYS_BUS_DEVICE,
460 .instance_size = sizeof(HexagonTLBState),
461 .instance_init = hexagon_tlb_init,
462 .class_init = hexagon_tlb_class_init,
463 };
464
465 static void hexagon_tlb_register_types(void)
466 {
467 type_register_static(&hexagon_tlb_info);
468 }
469
470 type_init(hexagon_tlb_register_types)