target/hexagon: Implement hexagon_tlb_fill()
Reviewed-by: Taylor Simpson <ltaylorsimpson@gmail.com> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
Brian Cain committed
Jun 22, 2026 at 15:28 UTC
440db644c7e835906e6c3a00c9422f3b62aab3a2
1 file changed
+136
target/hexagon/cpu.c
+136
@@ -38,6 +38,9 @@
38
#include "qemu/main-loop.h"
39
#include "hex_interrupts.h"
40
#include "exec/cpu-interrupt.h"
41
+#include "exec/page-protection.h"
42
+#include "exec/target_page.h"
43
+#include "hw/hexagon/hexagon_globalreg.h"
44
#endif
45
46
static ObjectClass *hexagon_cpu_class_by_name(const char *cpu_model)
@@ -475,6 +478,138 @@ static void hexagon_cpu_init(Object *obj)
478
}
479
480
#ifndef CONFIG_USER_ONLY
481
+static bool get_physical_address(CPUHexagonState *env, hwaddr *phys, int *prot,
482
+ uint64_t *size, int32_t *excp,
483
+ uint32_t address,
484
+ MMUAccessType access_type, int mmu_idx)
485
+
486
+{
487
+ if (hexagon_cpu_mmu_enabled(env)) {
488
+ return hex_tlb_find_match(env, address, access_type, phys, prot, size,
489
+ excp, mmu_idx);
490
+ } else {
491
+ *phys = address & 0xFFFFFFFF;
492
+ *prot = PAGE_VALID | PAGE_READ | PAGE_WRITE | PAGE_EXEC;
493
+ *size = TARGET_PAGE_SIZE;
494
+ return true;
495
+ }
496
+}
497
+
498
+/* qemu seems to only want to know about TARGET_PAGE_SIZE pages */
499
+static void find_qemu_subpage(vaddr *addr, hwaddr *phys, uint64_t page_size)
500
+{
501
+ vaddr page_start = *addr & ~((vaddr)(page_size - 1));
502
+ vaddr offset = ((*addr - page_start) / TARGET_PAGE_SIZE) * TARGET_PAGE_SIZE;
503
+ *addr = page_start + offset;
504
+ *phys += offset;
505
+}
506
+
507
+
508
+#define INVALID_BADVA 0xbadabada
509
+
510
+static void set_badva_regs(CPUHexagonState *env, uint32_t VA, int slot,
511
+ MMUAccessType access_type)
512
+{
513
+ env->t_sreg[HEX_SREG_BADVA] = VA;
514
+
515
+ if (access_type == MMU_INST_FETCH || slot == 0) {
516
+ env->t_sreg[HEX_SREG_BADVA0] = VA;
517
+ env->t_sreg[HEX_SREG_BADVA1] = INVALID_BADVA;
518
+ SET_SSR_FIELD(env, SSR_V0, 1);
519
+ SET_SSR_FIELD(env, SSR_V1, 0);
520
+ SET_SSR_FIELD(env, SSR_BVS, 0);
521
+ } else if (slot == 1) {
522
+ env->t_sreg[HEX_SREG_BADVA0] = INVALID_BADVA;
523
+ env->t_sreg[HEX_SREG_BADVA1] = VA;
524
+ SET_SSR_FIELD(env, SSR_V0, 0);
525
+ SET_SSR_FIELD(env, SSR_V1, 1);
526
+ SET_SSR_FIELD(env, SSR_BVS, 1);
527
+ } else {
528
+ g_assert_not_reached();
529
+ }
530
+}
531
+
532
+static void raise_tlbmiss_exception(CPUState *cs, uint32_t VA, int slot,
533
+ MMUAccessType access_type)
534
+{
535
+ CPUHexagonState *env = cpu_env(cs);
536
+
537
+ set_badva_regs(env, VA, slot, access_type);
538
+
539
+ switch (access_type) {
540
+ case MMU_INST_FETCH:
541
+ cs->exception_index = HEX_EVENT_TLB_MISS_X;
542
+ if ((VA & ~TARGET_PAGE_MASK) == 0) {
543
+ env->cause_code = HEX_CAUSE_TLBMISSX_CAUSE_NEXTPAGE;
544
+ } else {
545
+ env->cause_code = HEX_CAUSE_TLBMISSX_CAUSE_NORMAL;
546
+ }
547
+ break;
548
+ case MMU_DATA_LOAD:
549
+ cs->exception_index = HEX_EVENT_TLB_MISS_RW;
550
+ env->cause_code = HEX_CAUSE_TLBMISSRW_CAUSE_READ;
551
+ break;
552
+ case MMU_DATA_STORE:
553
+ cs->exception_index = HEX_EVENT_TLB_MISS_RW;
554
+ env->cause_code = HEX_CAUSE_TLBMISSRW_CAUSE_WRITE;
555
+ break;
556
+ }
557
+}
558
+
559
+static void raise_perm_exception(CPUState *cs, uint32_t VA, int slot,
560
+ MMUAccessType access_type, int32_t excp)
561
+{
562
+ CPUHexagonState *env = cpu_env(cs);
563
+
564
+ set_badva_regs(env, VA, slot, access_type);
565
+ cs->exception_index = excp;
566
+}
567
+
568
+static const char *access_type_names[] = { "MMU_DATA_LOAD ", "MMU_DATA_STORE",
569
+ "MMU_INST_FETCH" };
570
+
571
+static const char *mmu_idx_names[] = { "MMU_USER_IDX", "MMU_GUEST_IDX",
572
+ "MMU_KERNEL_IDX" };
573
+
574
+static bool hexagon_tlb_fill(CPUState *cs, vaddr address, int size,
575
+ MMUAccessType access_type, int mmu_idx, bool probe,
576
+ uintptr_t retaddr)
577
+{
578
+ CPUHexagonState *env = cpu_env(cs);
579
+ int slot = 0;
580
+ hwaddr phys;
581
+ int prot = 0;
582
+ uint64_t page_size = 0;
583
+ int32_t excp = 0;
584
+ bool ret = 0;
585
+
586
+ qemu_log_mask(
587
+ CPU_LOG_MMU,
588
+ "%s: tid = 0x%" PRIx32 ", pc = 0x%08" PRIx32
589
+ ", vaddr = 0x%08" VADDR_PRIx ", size = %d, %s,\tprobe = %d, %s\n",
590
+ __func__, env->threadId, env->gpr[HEX_REG_PC], address, size,
591
+ access_type_names[access_type], probe, mmu_idx_names[mmu_idx]);
592
+ ret = get_physical_address(env, &phys, &prot, &page_size, &excp, address,
593
+ access_type, mmu_idx);
594
+ if (ret) {
595
+ if (!excp) {
596
+ find_qemu_subpage(&address, &phys, page_size);
597
+ tlb_set_page(cs, address, phys, prot, mmu_idx, TARGET_PAGE_SIZE);
598
+ return ret;
599
+ }
600
+ if (probe) {
601
+ return false;
602
+ }
603
+ raise_perm_exception(cs, address, slot, access_type, excp);
604
+ do_raise_exception(env, cs->exception_index, env->gpr[HEX_REG_PC],
605
+ retaddr);
606
+ }
607
+ if (probe) {
608
+ return false;
609
+ }
610
+ raise_tlbmiss_exception(cs, address, slot, access_type);
611
+ do_raise_exception(env, cs->exception_index, env->gpr[HEX_REG_PC], retaddr);
612
+}
613
614
static bool hexagon_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
615
{
@@ -517,6 +652,7 @@ static const TCGCPUOps hexagon_tcg_ops = {
652
.cpu_exec_interrupt = hexagon_cpu_exec_interrupt,
653
.pointer_wrap = hexagon_pointer_wrap,
654
.cpu_exec_reset = cpu_reset,
655
+ .tlb_fill = hexagon_tlb_fill,
656
#endif /* !CONFIG_USER_ONLY */
657
};
658