@samitouri / QOSamiQemu / commits / 440db644c7

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