From 5ee9e9cd8c3b60f346bbbf423e8731f723c61590 Mon Sep 17 00:00:00 2001 From: chiangkd Date: Mon, 3 Jun 2024 01:04:32 +0800 Subject: [PATCH] Implement Real time counter to prevent mtime overflow Make 64-bit unsigned integer `mtime` with Real time counter (RTC) to prevent mtime overflow. Take 16-bit unsigned integer for example: mtime mtimecmp delta 0xFFF8 0x0004 0x000c 0xFFFC 0x0004 0x0008 0x0000 0x0004 0x0004 0x0004 0x0004 0x0000 <- trigger interrupt 0x0008 0x0004 0xfffc Trigger timer interrupt when `delta` is zero or negative (highest bit set). --- aclint.c | 24 ++++++++++-------------- device.h | 3 +-- main.c | 9 ++++----- riscv.c | 8 ++++---- riscv.h | 3 +-- 5 files changed, 20 insertions(+), 27 deletions(-) diff --git a/aclint.c b/aclint.c index aafe8ce..d84176e 100644 --- a/aclint.c +++ b/aclint.c @@ -4,13 +4,11 @@ void aclint_timer_interrupts(vm_t *vm, aclint_state_t *aclint) { - if ((vm->mtimeh > aclint->mtimecmp_hi) || - ((vm->mtimeh == aclint->mtimecmp_hi) && - (vm->mtime >= aclint->mtimecmp_lo))) + uint64_t time_delta = aclint->mtimecmp - vm->mtime; + if ((time_delta & 0x8000000000000000) || time_delta == 0) vm->sip |= RV_INT_STI_BIT; else vm->sip &= ~RV_INT_STI_BIT; - return; } void aclint_update_interrupts(vm_t *vm, aclint_state_t *aclint) @@ -19,8 +17,6 @@ void aclint_update_interrupts(vm_t *vm, aclint_state_t *aclint) vm->sip |= RV_INT_SSI_BIT; else vm->sip &= ~RV_INT_SSI_BIT; - - return; } static bool aclint_reg_read(aclint_state_t *aclint, @@ -35,19 +31,19 @@ static bool aclint_reg_read(aclint_state_t *aclint, return true; case _(MTIMECMP_LO): /* mtimecmp */ - *value = aclint->mtimecmp_lo; + *value = aclint->mtimecmp & 0xFFFFFFFF; return true; case _(MTIMECMP_HI): /* mtimecmph */ - *value = aclint->mtimecmp_hi; + *value = (aclint->mtimecmp >> 32) & 0xFFFFFFFF; return true; case _(MTIME_LO): /* mtime */ - *value = aclint->vm->mtime; + *value = aclint->vm->mtime & 0xFFFFFFFF; return true; case _(MTIME_HI): /* mtimeh */ - *value = aclint->vm->mtimeh; + *value = (aclint->vm->mtime >> 32) & 0xFFFFFFFF; return true; default: return false; @@ -67,19 +63,19 @@ static bool aclint_reg_write(aclint_state_t *aclint, return true; case _(MTIMECMP_LO): /* mtimecmp */ - aclint->mtimecmp_lo = value; + aclint->mtimecmp |= value; return true; case _(MTIMECMP_HI): /* mtimecmph */ - aclint->mtimecmp_hi = value; + aclint->mtimecmp |= ((uint64_t) value) << 32; return true; case _(MTIME_LO): /* mtime */ - aclint->vm->mtime = value; + aclint->vm->mtime |= value; return true; case _(MTIME_HI): /* mtimeh */ - aclint->vm->mtimeh = value; + aclint->vm->mtime |= ((uint64_t) value) << 32; return true; default: return false; diff --git a/device.h b/device.h index 470bf83..8cc4915 100644 --- a/device.h +++ b/device.h @@ -37,8 +37,7 @@ enum { typedef struct { vm_t *vm; - uint32_t mtimecmp_lo; - uint32_t mtimecmp_hi; + uint64_t mtimecmp; uint32_t setssip; } aclint_state_t; diff --git a/main.c b/main.c index 326d0cb..be51604 100644 --- a/main.c +++ b/main.c @@ -170,8 +170,8 @@ static inline sbi_ret_t handle_sbi_ecall_TIMER(vm_t *vm, int32_t fid) emu_state_t *data = PRIV(vm); switch (fid) { case SBI_TIMER__SET_TIMER: - data->aclint.mtimecmp_lo = vm->x_regs[RV_R_A0]; - data->aclint.mtimecmp_hi = vm->x_regs[RV_R_A1]; + data->aclint.mtimecmp = (((uint64_t) vm->x_regs[RV_R_A1])) << 32 | + (uint64_t) vm->x_regs[RV_R_A0]; return (sbi_ret_t){SBI_SUCCESS, 0}; default: return (sbi_ret_t){SBI_ERR_NOT_SUPPORTED, 0}; @@ -453,12 +453,11 @@ static int semu_start(int argc, char **argv) #endif } - if (!++vm.mtime) - vm.mtimeh++; - aclint_timer_interrupts(&vm, &emu.aclint); aclint_update_interrupts(&vm, &emu.aclint); + vm.mtime++; + vm_step(&vm); if (likely(!vm.error)) continue; diff --git a/riscv.c b/riscv.c index 4dae853..e28fd10 100644 --- a/riscv.c +++ b/riscv.c @@ -488,10 +488,10 @@ static void csr_read(vm_t *vm, uint16_t addr, uint32_t *value) *value = vm->stval; break; case RV_CSR_TIME: - *value = vm->mtime; + *value = vm->mtime & 0xFFFFFFFF; break; case RV_CSR_TIMEH: - *value = vm->mtimeh; + *value = (vm->mtime >> 32) & 0xFFFFFFFF; break; default: vm_set_exception(vm, RV_EXC_ILLEGAL_INSN, 0); @@ -546,10 +546,10 @@ static void csr_write(vm_t *vm, uint16_t addr, uint32_t value) vm->stval = value; break; case RV_CSR_TIME: - vm->mtime = value; + vm->mtime |= value; break; case RV_CSR_TIMEH: - vm->mtimeh = value; + vm->mtime |= ((uint64_t) value) << 32; break; default: vm_set_exception(vm, RV_EXC_ILLEGAL_INSN, 0); diff --git a/riscv.h b/riscv.h index 3e9d2b6..4fbfdd7 100644 --- a/riscv.h +++ b/riscv.h @@ -104,8 +104,7 @@ struct __vm_internal { uint32_t *page_table; /* Timer */ - uint32_t mtime; - uint32_t mtimeh; + uint64_t mtime; void *priv; /**< environment supplied */