diff --git a/src/emulate.c b/src/emulate.c index 00317e4c..f1d4b2c2 100644 --- a/src/emulate.c +++ b/src/emulate.c @@ -46,6 +46,7 @@ static bool need_clear_block_map = false; static uint32_t reloc_enable_mmu_jalr_addr; static bool reloc_enable_mmu = false; bool need_retranslate = false; +bool need_handle_signal = false; #endif static void rv_trap_default_handler(riscv_t *rv) @@ -379,8 +380,12 @@ static uint32_t peripheral_update_ctr = 64; { \ IIF(RV32_HAS(SYSTEM))(ctr++;, ) cycle++; \ code; \ - nextop: \ - PC += __rv_insn_##inst##_len; \ + IIF(RV32_HAS(SYSTEM)) \ + ( \ + if (need_handle_signal) { \ + need_handle_signal = false; \ + return true; \ + }, ) nextop : PC += __rv_insn_##inst##_len; \ IIF(RV32_HAS(SYSTEM)) \ (IIF(RV32_HAS(JIT))( \ , if (unlikely(need_clear_block_map)) { \ @@ -1219,6 +1224,9 @@ static void _trap_handler(riscv_t *rv) mode = rv->csr_stvec & 0x3; cause = rv->csr_scause; rv->csr_sepc = rv->PC; +#if RV32_HAS(SYSTEM) + rv->last_csr_sepc = rv->csr_sepc; +#endif } else { /* machine */ const uint32_t mstatus_mie = (rv->csr_mstatus & MSTATUS_MIE) >> MSTATUS_MIE_SHIFT; diff --git a/src/riscv_private.h b/src/riscv_private.h index 0ae6f279..4e052760 100644 --- a/src/riscv_private.h +++ b/src/riscv_private.h @@ -201,6 +201,12 @@ struct riscv_internal { #if RV32_HAS(SYSTEM) /* The flag is used to indicate the current emulation is in a trap */ bool is_trapped; + + /* + * The flag that stores the SEPC CSR at the trap point for corectly + * executing signal handler. + */ + uint32_t last_csr_sepc; #endif }; diff --git a/src/system.c b/src/system.c index 62ecbc11..f0bc0359 100644 --- a/src/system.c +++ b/src/system.c @@ -25,6 +25,17 @@ void emu_update_uart_interrupts(riscv_t *rv) plic_update_interrupts(attr->plic); } +/* + * Linux kernel might create signal frame when returning from trap + * handling, which modifies the SEPC CSR. Thus, the fault instruction + * cannot always redo. For example, invalid memory access causes SIGSEGV. + */ +extern bool need_handle_signal; +#define CHECK_PENDING_SIGNAL(rv, signal_flag) \ + do { \ + signal_flag = (rv->csr_sepc != rv->last_csr_sepc); \ + } while (0) + #define MMIO_R 1 #define MMIO_W 0 @@ -297,8 +308,14 @@ static uint32_t mmu_read_w(riscv_t *rv, const uint32_t addr) uint32_t level; pte_t *pte = mmu_walk(rv, addr, &level); bool ok = MMU_FAULT_CHECK(read, rv, pte, addr, PTE_R); - if (unlikely(!ok)) + if (unlikely(!ok)) { +#if RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER) + CHECK_PENDING_SIGNAL(rv, need_handle_signal); + if (need_handle_signal) + return 0; +#endif pte = mmu_walk(rv, addr, &level); + } { get_ppn_and_offset(); @@ -323,8 +340,14 @@ static uint16_t mmu_read_s(riscv_t *rv, const uint32_t addr) uint32_t level; pte_t *pte = mmu_walk(rv, addr, &level); bool ok = MMU_FAULT_CHECK(read, rv, pte, addr, PTE_R); - if (unlikely(!ok)) + if (unlikely(!ok)) { +#if RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER) + CHECK_PENDING_SIGNAL(rv, need_handle_signal); + if (need_handle_signal) + return 0; +#endif pte = mmu_walk(rv, addr, &level); + } get_ppn_and_offset(); return memory_read_s(ppn | offset); @@ -338,8 +361,14 @@ static uint8_t mmu_read_b(riscv_t *rv, const uint32_t addr) uint32_t level; pte_t *pte = mmu_walk(rv, addr, &level); bool ok = MMU_FAULT_CHECK(read, rv, pte, addr, PTE_R); - if (unlikely(!ok)) + if (unlikely(!ok)) { +#if RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER) + CHECK_PENDING_SIGNAL(rv, need_handle_signal); + if (need_handle_signal) + return 0; +#endif pte = mmu_walk(rv, addr, &level); + } { get_ppn_and_offset(); @@ -364,8 +393,14 @@ static void mmu_write_w(riscv_t *rv, const uint32_t addr, const uint32_t val) uint32_t level; pte_t *pte = mmu_walk(rv, addr, &level); bool ok = MMU_FAULT_CHECK(write, rv, pte, addr, PTE_W); - if (unlikely(!ok)) + if (unlikely(!ok)) { +#if RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER) + CHECK_PENDING_SIGNAL(rv, need_handle_signal); + if (need_handle_signal) + return; +#endif pte = mmu_walk(rv, addr, &level); + } { get_ppn_and_offset(); @@ -390,8 +425,14 @@ static void mmu_write_s(riscv_t *rv, const uint32_t addr, const uint16_t val) uint32_t level; pte_t *pte = mmu_walk(rv, addr, &level); bool ok = MMU_FAULT_CHECK(write, rv, pte, addr, PTE_W); - if (unlikely(!ok)) + if (unlikely(!ok)) { +#if RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER) + CHECK_PENDING_SIGNAL(rv, need_handle_signal); + if (need_handle_signal) + return; +#endif pte = mmu_walk(rv, addr, &level); + } get_ppn_and_offset(); memory_write_s(ppn | offset, (uint8_t *) &val); @@ -405,8 +446,14 @@ static void mmu_write_b(riscv_t *rv, const uint32_t addr, const uint8_t val) uint32_t level; pte_t *pte = mmu_walk(rv, addr, &level); bool ok = MMU_FAULT_CHECK(write, rv, pte, addr, PTE_W); - if (unlikely(!ok)) + if (unlikely(!ok)) { +#if RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER) + CHECK_PENDING_SIGNAL(rv, need_handle_signal); + if (need_handle_signal) + return; +#endif pte = mmu_walk(rv, addr, &level); + } { get_ppn_and_offset();