From d0545d272cf70103d8724976502e2ef7b80315c8 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Mon, 17 Aug 2026 05:17:44 +0200 Subject: [PATCH] testsuites/validation: Take the interrupted stack ScoreIsrValIsr checks that the context which an interrupt during the multitasking start interrupted ran on the stack of the thread which was running. A wrapper of the interrupt dispatch of the architecture takes the address of that stack, and there is one for six architectures only. Everywhere else the address stays zero, the check cannot pass, and it reports nothing but a line number, which says neither that the architecture is unsupported nor what was expected. Add the wrapper for mips, i386 and x86_64, and say in the check what a zero address means, so that the next port is told why it fails rather than left to find out. The frame which the mips dispatch receives is built on the stack of the interrupted context, so the address of the frame is an address within that stack. Its stack pointer member is not used. Only the path which the debugger stub takes fills it, and the interrupt path leaves it zero. The two x86 ports keep the frame base of the interrupted context in the frame pointer register and may then switch to the interrupt stack, so the wrapper has to take the value before the compiler establishes a frame of its own. A register variable does not do that, because the compiler emits its prologue first and the wrapper then reads its own frame. They get the same treatment as sparc, which has the problem for the same reason. Close #5712. Assisted-by: Claude:claude-opus-5 claude-code Signed-off-by: Sebastian Huber --- .../testsuites/validation/validation-intr.yml | 3 + testsuites/validation/tc-score-isr.c | 88 ++++++++++++++++++- 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/spec/build/testsuites/validation/validation-intr.yml b/spec/build/testsuites/validation/validation-intr.yml index 242db83319..996a1a7107 100644 --- a/spec/build/testsuites/validation/validation-intr.yml +++ b/spec/build/testsuites/validation/validation-intr.yml @@ -12,6 +12,9 @@ ldflags: - -Wl,--wrap=bsp_interrupt_dispatch - -Wl,--wrap=bsp_interrupt_handler_default - -Wl,--wrap=bsp_interrupt_spurious +- -Wl,--wrap=amd64_dispatch_isr +- -Wl,--wrap=BSP_dispatch_isr +- -Wl,--wrap=mips_vector_isr_handlers - -Wl,--wrap=_RISCV_Interrupt_dispatch - -Wl,--wrap=_SPARC_Interrupt_dispatch links: [] diff --git a/testsuites/validation/tc-score-isr.c b/testsuites/validation/tc-score-isr.c index b178541e72..425723af1b 100644 --- a/testsuites/validation/tc-score-isr.c +++ b/testsuites/validation/tc-score-isr.c @@ -214,6 +214,87 @@ __asm__ ( ); #endif +/* + * The interrupt prologue of these two ports leaves the frame base of the + * interrupted context in the frame pointer register and may then switch to + * the interrupt stack, so the value has to be taken before the compiler + * establishes a frame of its own. A register variable cannot do that. + */ +#if defined(__i386__) +void __real_BSP_dispatch_isr( int vector ); + +static RTEMS_USED void InterruptDispatch( int vector, uintptr_t sp ) +{ + if ( interrupted_stack_at_multitasking_start == 0 ) { + interrupted_stack_at_multitasking_start = sp; + } + + __real_BSP_dispatch_isr( vector ); +} + +__asm__ ( + "\t.section\t\".text\"\n" + "\t.align\t4\n" + "\t.globl\t__wrap_BSP_dispatch_isr\n" + "\t.type\t__wrap_BSP_dispatch_isr, @function\n" + "__wrap_BSP_dispatch_isr:\n" + "\tpushl\t%ebp\n" + "\tpushl\t8(%esp)\n" + "\tcall\tInterruptDispatch\n" + "\taddl\t$8, %esp\n" + "\tret\n" + "\t.previous\n" +); +#endif + +#if defined(__x86_64__) +void __real_amd64_dispatch_isr( rtems_vector_number vector ); + +static RTEMS_USED void InterruptDispatch( + rtems_vector_number vector, + uintptr_t sp +) +{ + if ( interrupted_stack_at_multitasking_start == 0 ) { + interrupted_stack_at_multitasking_start = sp; + } + + __real_amd64_dispatch_isr( vector ); +} + +__asm__ ( + "\t.section\t\".text\"\n" + "\t.align\t8\n" + "\t.globl\t__wrap_amd64_dispatch_isr\n" + "\t.type\t__wrap_amd64_dispatch_isr, @function\n" + "__wrap_amd64_dispatch_isr:\n" + "\tmovq\t%rbp, %rsi\n" + "\tjmp\tInterruptDispatch\n" + "\t.previous\n" +); +#endif + +#if defined(__mips__) +void __real_mips_vector_isr_handlers( CPU_Interrupt_frame *frame ); + +void __wrap_mips_vector_isr_handlers( CPU_Interrupt_frame *frame ); + +void __wrap_mips_vector_isr_handlers( CPU_Interrupt_frame *frame ) +{ + if ( interrupted_stack_at_multitasking_start == 0 ) { + /* + * The frame is built on the stack of the interrupted context, so its own + * address is an address within that stack. The stack pointer member of + * the frame is not used: the interrupt path never fills it, only the path + * which the debugger stub takes does. + */ + interrupted_stack_at_multitasking_start = (uintptr_t) frame; + } + + __real_mips_vector_isr_handlers( frame ); +} +#endif + static void ISRHandler( void *arg ) { uintptr_t begin; @@ -271,7 +352,12 @@ static void ScoreIsrValIsr_Action_0( void ) * Check that stack of the interrupted context was valid when an interrupt * was serviced during the multitasking start. */ - T_true( interrupted_stack_at_multitasking_start_is_valid ); + T_true( + interrupted_stack_at_multitasking_start_is_valid, + "the stack of the interrupted context was not the stack of the running" + " thread; a zero means that the interrupt dispatch of this architecture" + " has no wrapper in this test and the address was never taken" + ); } /**