Loading target/riscv/cpu.h +1 −0 Original line number Diff line number Diff line Loading @@ -104,6 +104,7 @@ struct CPURISCVState { target_ulong frm; target_ulong badaddr; target_ulong guest_phys_fault_addr; target_ulong priv_ver; target_ulong misa; Loading target/riscv/cpu_helper.c +174 −19 Original line number Diff line number Diff line Loading @@ -285,11 +285,12 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv) * @mmu_idx: Indicates current privilege level * @first_stage: Are we in first stage translation? * Second stage is used for hypervisor guest translation * @two_stage: Are we going to perform two stage translation */ static int get_physical_address(CPURISCVState *env, hwaddr *physical, int *prot, target_ulong addr, int access_type, int mmu_idx, bool first_stage) bool first_stage, bool two_stage) { /* NOTE: the env->pc value visible here will not be * correct, but the value visible to the exception handler Loading @@ -297,11 +298,38 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, MemTxResult res; MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; int mode = mmu_idx; bool use_background = false; /* * Check if we should use the background registers for the two * stage translation. We don't need to check if we actually need * two stage translation as that happened before this function * was called. Background registers will be used if the guest has * forced a two stage translation to be on (in HS or M mode). */ if (mode == PRV_M && access_type != MMU_INST_FETCH) { if (get_field(env->mstatus, MSTATUS_MPRV)) { mode = get_field(env->mstatus, MSTATUS_MPP); if (riscv_has_ext(env, RVH) && get_field(env->mstatus, MSTATUS_MPV)) { use_background = true; } } } if (mode == PRV_S && access_type != MMU_INST_FETCH && riscv_has_ext(env, RVH) && !riscv_cpu_virt_enabled(env)) { if (get_field(env->hstatus, HSTATUS_SPRV)) { mode = get_field(env->mstatus, SSTATUS_SPP); use_background = true; } } if (first_stage == false) { /* We are in stage 2 translation, this is similar to stage 1. */ /* Stage 2 is always taken as U-mode */ mode = PRV_U; } if (mode == PRV_M || !riscv_feature(env, RISCV_FEATURE_MMU)) { Loading @@ -313,13 +341,30 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, *prot = 0; hwaddr base; int levels, ptidxbits, ptesize, vm, sum; int mxr = get_field(env->mstatus, MSTATUS_MXR); int levels, ptidxbits, ptesize, vm, sum, mxr, widened; if (first_stage == true) { mxr = get_field(env->mstatus, MSTATUS_MXR); } else { mxr = get_field(env->vsstatus, MSTATUS_MXR); } if (env->priv_ver >= PRIV_VERSION_1_10_0) { if (first_stage == true) { if (use_background) { base = (hwaddr)get_field(env->vsatp, SATP_PPN) << PGSHIFT; vm = get_field(env->vsatp, SATP_MODE); } else { base = (hwaddr)get_field(env->satp, SATP_PPN) << PGSHIFT; sum = get_field(env->mstatus, MSTATUS_SUM); vm = get_field(env->satp, SATP_MODE); } widened = 0; } else { base = (hwaddr)get_field(env->hgatp, HGATP_PPN) << PGSHIFT; vm = get_field(env->hgatp, HGATP_MODE); widened = 2; } sum = get_field(env->mstatus, MSTATUS_SUM); switch (vm) { case VM_1_10_SV32: levels = 2; ptidxbits = 10; ptesize = 4; break; Loading @@ -337,6 +382,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, g_assert_not_reached(); } } else { widened = 0; base = (hwaddr)(env->sptbr) << PGSHIFT; sum = !get_field(env->mstatus, MSTATUS_PUM); vm = get_field(env->mstatus, MSTATUS_VM); Loading @@ -357,9 +403,16 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, } CPUState *cs = env_cpu(env); int va_bits = PGSHIFT + levels * ptidxbits; target_ulong mask = (1L << (TARGET_LONG_BITS - (va_bits - 1))) - 1; target_ulong masked_msbs = (addr >> (va_bits - 1)) & mask; int va_bits = PGSHIFT + levels * ptidxbits + widened; target_ulong mask, masked_msbs; if (TARGET_LONG_BITS > (va_bits - 1)) { mask = (1L << (TARGET_LONG_BITS - (va_bits - 1))) - 1; } else { mask = 0; } masked_msbs = (addr >> (va_bits - 1)) & mask; if (masked_msbs != 0 && masked_msbs != mask) { return TRANSLATE_FAIL; } Loading @@ -371,11 +424,29 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, restart: #endif for (i = 0; i < levels; i++, ptshift -= ptidxbits) { target_ulong idx = (addr >> (PGSHIFT + ptshift)) & target_ulong idx; if (i == 0) { idx = (addr >> (PGSHIFT + ptshift)) & ((1 << (ptidxbits + widened)) - 1); } else { idx = (addr >> (PGSHIFT + ptshift)) & ((1 << ptidxbits) - 1); } /* check that physical address of PTE is legal */ hwaddr pte_addr = base + idx * ptesize; hwaddr pte_addr; if (two_stage && first_stage) { hwaddr vbase; /* Do the second stage translation on the base PTE address. */ get_physical_address(env, &vbase, prot, base, access_type, mmu_idx, false, true); pte_addr = vbase + idx * ptesize; } else { pte_addr = base + idx * ptesize; } if (riscv_feature(env, RISCV_FEATURE_PMP) && !pmp_hart_has_privs(env, pte_addr, sizeof(target_ulong), Loading Loading @@ -472,7 +543,12 @@ restart: /* for superpage mappings, make a fake leaf PTE for the TLB's benefit. */ target_ulong vpn = addr >> PGSHIFT; if (i == 0) { *physical = (ppn | (vpn & ((1L << (ptshift + widened)) - 1))) << PGSHIFT; } else { *physical = (ppn | (vpn & ((1L << ptshift) - 1))) << PGSHIFT; } /* set permissions on the TLB entry */ if ((pte & PTE_R) || ((pte & PTE_X) && mxr)) { Loading Loading @@ -531,14 +607,23 @@ static void raise_mmu_exception(CPURISCVState *env, target_ulong address, hwaddr riscv_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; hwaddr phys_addr; int prot; int mmu_idx = cpu_mmu_index(&cpu->env, false); if (get_physical_address(&cpu->env, &phys_addr, &prot, addr, 0, mmu_idx, true)) { if (get_physical_address(env, &phys_addr, &prot, addr, 0, mmu_idx, true, riscv_cpu_virt_enabled(env))) { return -1; } if (riscv_cpu_virt_enabled(env)) { if (get_physical_address(env, &phys_addr, &prot, phys_addr, 0, mmu_idx, false, true)) { return -1; } } return phys_addr; } Loading Loading @@ -592,17 +677,37 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; #ifndef CONFIG_USER_ONLY vaddr im_address; hwaddr pa = 0; int prot; bool pmp_violation = false; bool m_mode_two_stage = false; bool hs_mode_two_stage = false; bool first_stage_error = true; int ret = TRANSLATE_FAIL; int mode = mmu_idx; env->guest_phys_fault_addr = 0; qemu_log_mask(CPU_LOG_MMU, "%s ad %" VADDR_PRIx " rw %d mmu_idx %d\n", __func__, address, access_type, mmu_idx); ret = get_physical_address(env, &pa, &prot, address, access_type, mmu_idx, true); /* * Determine if we are in M mode and MPRV is set or in HS mode and SPRV is * set and we want to access a virtulisation address. */ if (riscv_has_ext(env, RVH)) { m_mode_two_stage = env->priv == PRV_M && access_type != MMU_INST_FETCH && get_field(env->mstatus, MSTATUS_MPRV) && get_field(env->mstatus, MSTATUS_MPV); hs_mode_two_stage = env->priv == PRV_S && !riscv_cpu_virt_enabled(env) && access_type != MMU_INST_FETCH && get_field(env->hstatus, HSTATUS_SPRV) && get_field(env->hstatus, HSTATUS_SPV); } if (mode == PRV_M && access_type != MMU_INST_FETCH) { if (get_field(env->mstatus, MSTATUS_MPRV)) { Loading @@ -610,9 +715,55 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, } } if (riscv_cpu_virt_enabled(env) || m_mode_two_stage || hs_mode_two_stage) { /* Two stage lookup */ ret = get_physical_address(env, &pa, &prot, address, access_type, mmu_idx, true, true); qemu_log_mask(CPU_LOG_MMU, "%s 1st-stage address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx " prot %d\n", __func__, address, ret, pa, prot); if (ret != TRANSLATE_FAIL) { /* Second stage lookup */ im_address = pa; ret = get_physical_address(env, &pa, &prot, im_address, access_type, mmu_idx, false, true); qemu_log_mask(CPU_LOG_MMU, "%s 2nd-stage address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx " prot %d\n", __func__, im_address, ret, pa, prot); if (riscv_feature(env, RISCV_FEATURE_PMP) && (ret == TRANSLATE_SUCCESS) && !pmp_hart_has_privs(env, pa, size, 1 << access_type, mode)) { ret = TRANSLATE_PMP_FAIL; } if (ret != TRANSLATE_SUCCESS) { /* * Guest physical address translation failed, this is a HS * level exception */ first_stage_error = false; env->guest_phys_fault_addr = (im_address | (address & (TARGET_PAGE_SIZE - 1))) >> 2; } } } else { /* Single stage lookup */ ret = get_physical_address(env, &pa, &prot, address, access_type, mmu_idx, true, false); qemu_log_mask(CPU_LOG_MMU, "%s address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx " prot %d\n", __func__, address, ret, pa, prot); "%s address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx " prot %d\n", __func__, address, ret, pa, prot); } if (riscv_feature(env, RISCV_FEATURE_PMP) && (ret == TRANSLATE_SUCCESS) && Loading @@ -622,6 +773,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, if (ret == TRANSLATE_PMP_FAIL) { pmp_violation = true; } if (ret == TRANSLATE_SUCCESS) { tlb_set_page(cs, address & TARGET_PAGE_MASK, pa & TARGET_PAGE_MASK, prot, mmu_idx, TARGET_PAGE_SIZE); Loading @@ -629,9 +781,12 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, } else if (probe) { return false; } else { raise_mmu_exception(env, address, access_type, pmp_violation, true); raise_mmu_exception(env, address, access_type, pmp_violation, first_stage_error); riscv_raise_exception(env, cs->exception_index, retaddr); } return true; #else switch (access_type) { case MMU_INST_FETCH: Loading Loading
target/riscv/cpu.h +1 −0 Original line number Diff line number Diff line Loading @@ -104,6 +104,7 @@ struct CPURISCVState { target_ulong frm; target_ulong badaddr; target_ulong guest_phys_fault_addr; target_ulong priv_ver; target_ulong misa; Loading
target/riscv/cpu_helper.c +174 −19 Original line number Diff line number Diff line Loading @@ -285,11 +285,12 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv) * @mmu_idx: Indicates current privilege level * @first_stage: Are we in first stage translation? * Second stage is used for hypervisor guest translation * @two_stage: Are we going to perform two stage translation */ static int get_physical_address(CPURISCVState *env, hwaddr *physical, int *prot, target_ulong addr, int access_type, int mmu_idx, bool first_stage) bool first_stage, bool two_stage) { /* NOTE: the env->pc value visible here will not be * correct, but the value visible to the exception handler Loading @@ -297,11 +298,38 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, MemTxResult res; MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; int mode = mmu_idx; bool use_background = false; /* * Check if we should use the background registers for the two * stage translation. We don't need to check if we actually need * two stage translation as that happened before this function * was called. Background registers will be used if the guest has * forced a two stage translation to be on (in HS or M mode). */ if (mode == PRV_M && access_type != MMU_INST_FETCH) { if (get_field(env->mstatus, MSTATUS_MPRV)) { mode = get_field(env->mstatus, MSTATUS_MPP); if (riscv_has_ext(env, RVH) && get_field(env->mstatus, MSTATUS_MPV)) { use_background = true; } } } if (mode == PRV_S && access_type != MMU_INST_FETCH && riscv_has_ext(env, RVH) && !riscv_cpu_virt_enabled(env)) { if (get_field(env->hstatus, HSTATUS_SPRV)) { mode = get_field(env->mstatus, SSTATUS_SPP); use_background = true; } } if (first_stage == false) { /* We are in stage 2 translation, this is similar to stage 1. */ /* Stage 2 is always taken as U-mode */ mode = PRV_U; } if (mode == PRV_M || !riscv_feature(env, RISCV_FEATURE_MMU)) { Loading @@ -313,13 +341,30 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, *prot = 0; hwaddr base; int levels, ptidxbits, ptesize, vm, sum; int mxr = get_field(env->mstatus, MSTATUS_MXR); int levels, ptidxbits, ptesize, vm, sum, mxr, widened; if (first_stage == true) { mxr = get_field(env->mstatus, MSTATUS_MXR); } else { mxr = get_field(env->vsstatus, MSTATUS_MXR); } if (env->priv_ver >= PRIV_VERSION_1_10_0) { if (first_stage == true) { if (use_background) { base = (hwaddr)get_field(env->vsatp, SATP_PPN) << PGSHIFT; vm = get_field(env->vsatp, SATP_MODE); } else { base = (hwaddr)get_field(env->satp, SATP_PPN) << PGSHIFT; sum = get_field(env->mstatus, MSTATUS_SUM); vm = get_field(env->satp, SATP_MODE); } widened = 0; } else { base = (hwaddr)get_field(env->hgatp, HGATP_PPN) << PGSHIFT; vm = get_field(env->hgatp, HGATP_MODE); widened = 2; } sum = get_field(env->mstatus, MSTATUS_SUM); switch (vm) { case VM_1_10_SV32: levels = 2; ptidxbits = 10; ptesize = 4; break; Loading @@ -337,6 +382,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, g_assert_not_reached(); } } else { widened = 0; base = (hwaddr)(env->sptbr) << PGSHIFT; sum = !get_field(env->mstatus, MSTATUS_PUM); vm = get_field(env->mstatus, MSTATUS_VM); Loading @@ -357,9 +403,16 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, } CPUState *cs = env_cpu(env); int va_bits = PGSHIFT + levels * ptidxbits; target_ulong mask = (1L << (TARGET_LONG_BITS - (va_bits - 1))) - 1; target_ulong masked_msbs = (addr >> (va_bits - 1)) & mask; int va_bits = PGSHIFT + levels * ptidxbits + widened; target_ulong mask, masked_msbs; if (TARGET_LONG_BITS > (va_bits - 1)) { mask = (1L << (TARGET_LONG_BITS - (va_bits - 1))) - 1; } else { mask = 0; } masked_msbs = (addr >> (va_bits - 1)) & mask; if (masked_msbs != 0 && masked_msbs != mask) { return TRANSLATE_FAIL; } Loading @@ -371,11 +424,29 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, restart: #endif for (i = 0; i < levels; i++, ptshift -= ptidxbits) { target_ulong idx = (addr >> (PGSHIFT + ptshift)) & target_ulong idx; if (i == 0) { idx = (addr >> (PGSHIFT + ptshift)) & ((1 << (ptidxbits + widened)) - 1); } else { idx = (addr >> (PGSHIFT + ptshift)) & ((1 << ptidxbits) - 1); } /* check that physical address of PTE is legal */ hwaddr pte_addr = base + idx * ptesize; hwaddr pte_addr; if (two_stage && first_stage) { hwaddr vbase; /* Do the second stage translation on the base PTE address. */ get_physical_address(env, &vbase, prot, base, access_type, mmu_idx, false, true); pte_addr = vbase + idx * ptesize; } else { pte_addr = base + idx * ptesize; } if (riscv_feature(env, RISCV_FEATURE_PMP) && !pmp_hart_has_privs(env, pte_addr, sizeof(target_ulong), Loading Loading @@ -472,7 +543,12 @@ restart: /* for superpage mappings, make a fake leaf PTE for the TLB's benefit. */ target_ulong vpn = addr >> PGSHIFT; if (i == 0) { *physical = (ppn | (vpn & ((1L << (ptshift + widened)) - 1))) << PGSHIFT; } else { *physical = (ppn | (vpn & ((1L << ptshift) - 1))) << PGSHIFT; } /* set permissions on the TLB entry */ if ((pte & PTE_R) || ((pte & PTE_X) && mxr)) { Loading Loading @@ -531,14 +607,23 @@ static void raise_mmu_exception(CPURISCVState *env, target_ulong address, hwaddr riscv_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; hwaddr phys_addr; int prot; int mmu_idx = cpu_mmu_index(&cpu->env, false); if (get_physical_address(&cpu->env, &phys_addr, &prot, addr, 0, mmu_idx, true)) { if (get_physical_address(env, &phys_addr, &prot, addr, 0, mmu_idx, true, riscv_cpu_virt_enabled(env))) { return -1; } if (riscv_cpu_virt_enabled(env)) { if (get_physical_address(env, &phys_addr, &prot, phys_addr, 0, mmu_idx, false, true)) { return -1; } } return phys_addr; } Loading Loading @@ -592,17 +677,37 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; #ifndef CONFIG_USER_ONLY vaddr im_address; hwaddr pa = 0; int prot; bool pmp_violation = false; bool m_mode_two_stage = false; bool hs_mode_two_stage = false; bool first_stage_error = true; int ret = TRANSLATE_FAIL; int mode = mmu_idx; env->guest_phys_fault_addr = 0; qemu_log_mask(CPU_LOG_MMU, "%s ad %" VADDR_PRIx " rw %d mmu_idx %d\n", __func__, address, access_type, mmu_idx); ret = get_physical_address(env, &pa, &prot, address, access_type, mmu_idx, true); /* * Determine if we are in M mode and MPRV is set or in HS mode and SPRV is * set and we want to access a virtulisation address. */ if (riscv_has_ext(env, RVH)) { m_mode_two_stage = env->priv == PRV_M && access_type != MMU_INST_FETCH && get_field(env->mstatus, MSTATUS_MPRV) && get_field(env->mstatus, MSTATUS_MPV); hs_mode_two_stage = env->priv == PRV_S && !riscv_cpu_virt_enabled(env) && access_type != MMU_INST_FETCH && get_field(env->hstatus, HSTATUS_SPRV) && get_field(env->hstatus, HSTATUS_SPV); } if (mode == PRV_M && access_type != MMU_INST_FETCH) { if (get_field(env->mstatus, MSTATUS_MPRV)) { Loading @@ -610,9 +715,55 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, } } if (riscv_cpu_virt_enabled(env) || m_mode_two_stage || hs_mode_two_stage) { /* Two stage lookup */ ret = get_physical_address(env, &pa, &prot, address, access_type, mmu_idx, true, true); qemu_log_mask(CPU_LOG_MMU, "%s 1st-stage address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx " prot %d\n", __func__, address, ret, pa, prot); if (ret != TRANSLATE_FAIL) { /* Second stage lookup */ im_address = pa; ret = get_physical_address(env, &pa, &prot, im_address, access_type, mmu_idx, false, true); qemu_log_mask(CPU_LOG_MMU, "%s 2nd-stage address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx " prot %d\n", __func__, im_address, ret, pa, prot); if (riscv_feature(env, RISCV_FEATURE_PMP) && (ret == TRANSLATE_SUCCESS) && !pmp_hart_has_privs(env, pa, size, 1 << access_type, mode)) { ret = TRANSLATE_PMP_FAIL; } if (ret != TRANSLATE_SUCCESS) { /* * Guest physical address translation failed, this is a HS * level exception */ first_stage_error = false; env->guest_phys_fault_addr = (im_address | (address & (TARGET_PAGE_SIZE - 1))) >> 2; } } } else { /* Single stage lookup */ ret = get_physical_address(env, &pa, &prot, address, access_type, mmu_idx, true, false); qemu_log_mask(CPU_LOG_MMU, "%s address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx " prot %d\n", __func__, address, ret, pa, prot); "%s address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx " prot %d\n", __func__, address, ret, pa, prot); } if (riscv_feature(env, RISCV_FEATURE_PMP) && (ret == TRANSLATE_SUCCESS) && Loading @@ -622,6 +773,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, if (ret == TRANSLATE_PMP_FAIL) { pmp_violation = true; } if (ret == TRANSLATE_SUCCESS) { tlb_set_page(cs, address & TARGET_PAGE_MASK, pa & TARGET_PAGE_MASK, prot, mmu_idx, TARGET_PAGE_SIZE); Loading @@ -629,9 +781,12 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, } else if (probe) { return false; } else { raise_mmu_exception(env, address, access_type, pmp_violation, true); raise_mmu_exception(env, address, access_type, pmp_violation, first_stage_error); riscv_raise_exception(env, cs->exception_index, retaddr); } return true; #else switch (access_type) { case MMU_INST_FETCH: Loading