Skip to content

Commit 132a5ac

Browse files
committed
riscv: advance PC by real instruction size on exception
cpu_handle_exception unconditionally does env->pc += 4 for RISC-V. That corrupts the PC for 16-bit compressed instructions and for instruction access/page faults, where env->pc already holds the faulting address and must not be advanced. Move the logic into a RISC-V helper, riscv_cpu_prepare_exception_pc(), which mirrors the trap state riscv_cpu_do_interrupt would set up: - load mbadaddr from badaddr for every address-bearing exception so the UC_HOOK_INTR callback can inspect the faulting address - advance the PC by the real instruction size (2 or 4 bytes, from the low two opcode bits) for execution-time exceptions - leave the PC untouched for INST_ADDR_MIS / INST_ACCESS_FAULT / INST_PAGE_FAULT Extract the address-bearing exception list into a shared riscv_cpu_exception_has_badaddr() predicate used by both do_interrupt and the new helper, instead of duplicating the switch.
1 parent 7c5db94 commit 132a5ac

5 files changed

Lines changed: 70 additions & 20 deletions

File tree

qemu/accel/tcg/cpu-exec.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -391,8 +391,8 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
391391
env->active_tc.PC = uc->next_pc;
392392
#endif
393393
#if defined(TARGET_RISCV)
394-
CPURISCVState *env = &(RISCV_CPU(uc->cpu)->env);
395-
env->pc += 4;
394+
riscv_cpu_prepare_exception_pc(&(RISCV_CPU(uc->cpu)->env),
395+
cpu->exception_index);
396396
#endif
397397
#if defined(TARGET_SPARC)
398398
CPUSPARCState *env = &(SPARC_CPU(uc->cpu)->env);

qemu/riscv32.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,6 +1311,7 @@
13111311
#define riscv_cpu_do_unaligned_access riscv_cpu_do_unaligned_access_riscv32
13121312
#define riscv_cpu_tlb_fill riscv_cpu_tlb_fill_riscv32
13131313
#define riscv_cpu_do_interrupt riscv_cpu_do_interrupt_riscv32
1314+
#define riscv_cpu_prepare_exception_pc riscv_cpu_prepare_exception_pc_riscv32
13141315
#define riscv_get_csr_ops riscv_get_csr_ops_riscv32
13151316
#define riscv_set_csr_ops riscv_set_csr_ops_riscv32
13161317
#define riscv_csrrw riscv_csrrw_riscv32

qemu/riscv64.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,6 +1311,7 @@
13111311
#define riscv_cpu_do_unaligned_access riscv_cpu_do_unaligned_access_riscv64
13121312
#define riscv_cpu_tlb_fill riscv_cpu_tlb_fill_riscv64
13131313
#define riscv_cpu_do_interrupt riscv_cpu_do_interrupt_riscv64
1314+
#define riscv_cpu_prepare_exception_pc riscv_cpu_prepare_exception_pc_riscv64
13141315
#define riscv_get_csr_ops riscv_get_csr_ops_riscv64
13151316
#define riscv_set_csr_ops riscv_set_csr_ops_riscv64
13161317
#define riscv_csrrw riscv_csrrw_riscv64

qemu/target/riscv/cpu.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,13 @@ void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable);
290290
bool riscv_cpu_force_hs_excep_enabled(CPURISCVState *env);
291291
void riscv_cpu_set_force_hs_excep(CPURISCVState *env, bool enable);
292292
int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch);
293+
/*
294+
* Unicorn helper: prepare the PC for a UC_HOOK_INTR callback. Loads the
295+
* trap value (mbadaddr) for address-bearing exceptions and advances the PC
296+
* past the faulting instruction, mirroring the state riscv_cpu_do_interrupt
297+
* would set up.
298+
*/
299+
void riscv_cpu_prepare_exception_pc(CPURISCVState *env, uint32_t cause);
293300
hwaddr riscv_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
294301
void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
295302
MMUAccessType access_type, int mmu_idx,

qemu/target/riscv/cpu_helper.c

Lines changed: 59 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -819,6 +819,59 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
819819
return true;
820820
}
821821

822+
/*
823+
* Return true if a synchronous exception carries a faulting address in
824+
* env->badaddr (used as the machine trap value, mbadaddr/mtval).
825+
*/
826+
static bool riscv_cpu_exception_has_badaddr(target_ulong cause)
827+
{
828+
switch (cause) {
829+
case RISCV_EXCP_INST_GUEST_PAGE_FAULT:
830+
case RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT:
831+
case RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT:
832+
case RISCV_EXCP_INST_ADDR_MIS:
833+
case RISCV_EXCP_INST_ACCESS_FAULT:
834+
case RISCV_EXCP_LOAD_ADDR_MIS:
835+
case RISCV_EXCP_STORE_AMO_ADDR_MIS:
836+
case RISCV_EXCP_LOAD_ACCESS_FAULT:
837+
case RISCV_EXCP_STORE_AMO_ACCESS_FAULT:
838+
case RISCV_EXCP_INST_PAGE_FAULT:
839+
case RISCV_EXCP_LOAD_PAGE_FAULT:
840+
case RISCV_EXCP_STORE_PAGE_FAULT:
841+
return true;
842+
default:
843+
return false;
844+
}
845+
}
846+
847+
/*
848+
* Unicorn helper: prepare the PC for a UC_HOOK_INTR callback. This mirrors
849+
* the state riscv_cpu_do_interrupt would have set up (mbadaddr from badaddr
850+
* for address-bearing exceptions) and advances the PC past the faulting
851+
* instruction. Instruction fetch faults already hold the faulting address
852+
* in env->pc and are left untouched.
853+
*/
854+
void riscv_cpu_prepare_exception_pc(CPURISCVState *env, uint32_t cause)
855+
{
856+
if (riscv_cpu_exception_has_badaddr(cause)) {
857+
env->mbadaddr = env->badaddr;
858+
}
859+
860+
if (cause == RISCV_EXCP_INST_ADDR_MIS ||
861+
cause == RISCV_EXCP_INST_ACCESS_FAULT ||
862+
cause == RISCV_EXCP_INST_PAGE_FAULT) {
863+
return;
864+
}
865+
866+
/*
867+
* Execution-time exception: advance past the faulting instruction.
868+
* RISC-V instructions are 16 or 32 bits, selected by the two
869+
* least-significant bits of the opcode.
870+
*/
871+
uint16_t op = cpu_lduw_code(env, env->pc);
872+
env->pc += ((op & 0x3) == 0x3) ? 4 : 2;
873+
}
874+
822875
/*
823876
* Handle Traps
824877
*
@@ -844,25 +897,13 @@ void riscv_cpu_do_interrupt(CPUState *cs)
844897

845898
if (!async) {
846899
/* set tval to badaddr for traps with address information */
847-
switch (cause) {
848-
case RISCV_EXCP_INST_GUEST_PAGE_FAULT:
849-
case RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT:
850-
case RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT:
851-
force_hs_execp = true;
852-
/* fallthrough */
853-
case RISCV_EXCP_INST_ADDR_MIS:
854-
case RISCV_EXCP_INST_ACCESS_FAULT:
855-
case RISCV_EXCP_LOAD_ADDR_MIS:
856-
case RISCV_EXCP_STORE_AMO_ADDR_MIS:
857-
case RISCV_EXCP_LOAD_ACCESS_FAULT:
858-
case RISCV_EXCP_STORE_AMO_ACCESS_FAULT:
859-
case RISCV_EXCP_INST_PAGE_FAULT:
860-
case RISCV_EXCP_LOAD_PAGE_FAULT:
861-
case RISCV_EXCP_STORE_PAGE_FAULT:
900+
if (riscv_cpu_exception_has_badaddr(cause)) {
862901
tval = env->badaddr;
863-
break;
864-
default:
865-
break;
902+
}
903+
if (cause == RISCV_EXCP_INST_GUEST_PAGE_FAULT ||
904+
cause == RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT ||
905+
cause == RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT) {
906+
force_hs_execp = true;
866907
}
867908
/* ecall is dispatched as one cause so translate based on mode */
868909
if (cause == RISCV_EXCP_U_ECALL) {

0 commit comments

Comments
 (0)