From a4288af5e6216f9e1c27379ada3dd02ea68d46db Mon Sep 17 00:00:00 2001 From: meighti Date: Mon, 27 Mar 2023 12:18:31 +0200 Subject: [PATCH] added a timerhalt() function The virt QEMU board integrates a "test finisher" device. Writing FINISHER_PASS(=0x5555) to its address which is VIRT_TEST(=0x100000), will halt virt machine. Since this should be done in machine mode, a new flag is set by timerhalt() in supervisor mode. Later timervec() will check this flag, which is at the end of timer scratch area. If the halt flag is not zero, it will branch to halt. It can be used to halt when kernel panics in printf.c It can be triggered in user mode by killing init process, using the following command at shell: kill 1 --- kernel/defs.h | 3 +++ kernel/kernelvec.S | 16 ++++++++++++++++ kernel/printf.c | 1 + kernel/start.c | 13 ++++++++++++- 4 files changed, 32 insertions(+), 1 deletion(-) diff --git a/kernel/defs.h b/kernel/defs.h index a3c962b37d..6fd054fb18 100644 --- a/kernel/defs.h +++ b/kernel/defs.h @@ -9,6 +9,9 @@ struct sleeplock; struct stat; struct superblock; +// start.c +void timerhalt(void); + // bio.c void binit(void); struct buf* bread(uint, uint); diff --git a/kernel/kernelvec.S b/kernel/kernelvec.S index fb31b328c4..538e5c892d 100644 --- a/kernel/kernelvec.S +++ b/kernel/kernelvec.S @@ -97,12 +97,17 @@ timervec: # scratch[0,8,16] : register save area. # scratch[24] : address of CLINT's MTIMECMP register. # scratch[32] : desired interval between interrupts. + # scratch[40] : halt flag set by timerhalt. csrrw a0, mscratch, a0 sd a1, 0(a0) sd a2, 8(a0) sd a3, 16(a0) + # halt if timerhalt has set halt flag to 1 + ld a1, 40(a0) + bne a1, zero, halt + # schedule the next timer interrupt # by adding interval to mtimecmp. ld a1, 24(a0) # CLINT_MTIMECMP(hart) @@ -122,3 +127,14 @@ timervec: csrrw a0, mscratch, a0 mret + +halt: + # based on qemu's hw/riscv/virt.c: + # qemu halts if FINISHER_PASS(=0x5555) is + # written at address *VIRT_TEST(=0x100000L) + lui a1, 0x100 + lui a2, 0x5 + addi a2, a2, 0x555 + sw a2, 0(a1) +spin: + j spin diff --git a/kernel/printf.c b/kernel/printf.c index 1a50203d9a..7246eea43c 100644 --- a/kernel/printf.c +++ b/kernel/printf.c @@ -123,6 +123,7 @@ panic(char *s) printf(s); printf("\n"); panicked = 1; // freeze uart output from other CPUs + timerhalt(); for(;;) ; } diff --git a/kernel/start.c b/kernel/start.c index e16f18a37b..b15829d29d 100644 --- a/kernel/start.c +++ b/kernel/start.c @@ -11,7 +11,7 @@ void timerinit(); __attribute__ ((aligned (16))) char stack0[4096 * NCPU]; // a scratch area per CPU for machine-mode timer interrupts. -uint64 timer_scratch[NCPU][5]; +uint64 timer_scratch[NCPU][6]; // assembly code in kernelvec.S for machine-mode timer interrupt. extern void timervec(); @@ -73,9 +73,11 @@ timerinit() // scratch[0..2] : space for timervec to save registers. // scratch[3] : address of CLINT MTIMECMP register. // scratch[4] : desired interval (in cycles) between timer interrupts. + // scratch[5] : halt flag to signal halt to timervec. uint64 *scratch = &timer_scratch[id][0]; scratch[3] = CLINT_MTIMECMP(id); scratch[4] = interval; + scratch[5] = 0; w_mscratch((uint64)scratch); // set the machine-mode trap handler. @@ -87,3 +89,12 @@ timerinit() // enable machine-mode timer interrupts. w_mie(r_mie() | MIE_MTIE); } + +// signal halt to timervec. +void +timerhalt() +{ + uint64 *scratch = &timer_scratch[0][0]; + scratch[5] = 1; + w_mscratch((uint64)scratch); +}