From b3ff4ce3013953ad6c5a4efecbb36f06b5c1984d Mon Sep 17 00:00:00 2001 From: zouboan Date: Thu, 27 Oct 2022 15:42:36 +0800 Subject: [PATCH] arch/sparc change common file to support SMP --- arch/sparc/Kconfig | 15 ++ arch/sparc/include/irq.h | 34 ++- arch/sparc/src/common/up_assert.c | 2 +- arch/sparc/src/common/up_checkstack.c | 4 +- arch/sparc/src/common/up_initialize.c | 33 ++- arch/sparc/src/common/up_internal.h | 15 +- arch/sparc/src/sparc_v8/Toolchain.defs | 2 + arch/sparc/src/sparc_v8/sparc_v8.h | 4 +- arch/sparc/src/sparc_v8/up_blocktask.c | 4 +- arch/sparc/src/sparc_v8/up_copystate.c | 2 +- arch/sparc/src/sparc_v8/up_doirq.c | 29 ++- arch/sparc/src/sparc_v8/up_dumpstate.c | 36 ++-- arch/sparc/src/sparc_v8/up_initialstate.c | 2 +- arch/sparc/src/sparc_v8/up_releasepending.c | 4 +- arch/sparc/src/sparc_v8/up_reprioritizertr.c | 4 +- .../sparc/src/sparc_v8/up_schedulesigaction.c | 201 +++++++++++++++++- arch/sparc/src/sparc_v8/up_sigdeliver.c | 67 ++++++ arch/sparc/src/sparc_v8/up_swint1.c | 20 +- arch/sparc/src/sparc_v8/up_unblocktask.c | 4 +- 19 files changed, 410 insertions(+), 72 deletions(-) diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 66044e72fc..af0c6b2790 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -32,6 +32,19 @@ config ARCH_CHIP_BM3823 ---help--- Microchip BM3823 (ARCH_SPARC_V8) +config ARCH_CHIP_S698PM + bool "S698PM" + select ARCH_SPARC_V8 + select ARCH_HAVE_MATH_H + select ARCH_HAVE_IRQPRIO + select ARCH_VECNOTIRQ + select ARCH_HAVE_RAMFUNCS + select ARCH_HAVE_MULTICPU + select ARCH_HAVE_TESTSET + select ARCH_HAVE_SERIAL_TERMIOS + ---help--- + Microchip S698PM (ARCH_SPARC_V8) + endchoice config ARCH_SPARC_V8 @@ -47,9 +60,11 @@ config ARCH_CHIP string default "bm3803" if ARCH_CHIP_BM3803 default "bm3823" if ARCH_CHIP_BM3823 + default "s698pm" if ARCH_CHIP_S698PM source arch/sparc/src/sparc_v8/Kconfig source arch/sparc/src/bm3803/Kconfig source arch/sparc/src/bm3823/Kconfig +source arch/sparc/src/s698pm/Kconfig endif diff --git a/arch/sparc/include/irq.h b/arch/sparc/include/irq.h index 030da6f95b..96cbef6a82 100644 --- a/arch/sparc/include/irq.h +++ b/arch/sparc/include/irq.h @@ -92,11 +92,18 @@ static inline uint32_t up_getsp(void) * Public Data ****************************************************************************/ -/* This holds a references to the current interrupt level register storage - * structure. If is non-NULL only during interrupt processing. +/* g_current_regs[] holds a references to the current interrupt level + * register storage structure. If is non-NULL only during interrupt + * processing. Access to g_current_regs[] must be through the macro + * CURRENT_REGS for portability. */ -EXTERN volatile uint32_t *g_current_regs; +/* For the case of architectures with multiple CPUs, then there must be one + * such value for each processor that can receive an interrupt. + */ + +EXTERN volatile uint32_t *g_current_regs[CONFIG_SMP_NCPUS]; +#define CURRENT_REGS (g_current_regs[up_cpu_index()]) /**************************************************************************** * Public Function Prototypes @@ -118,7 +125,11 @@ EXTERN volatile uint32_t *g_current_regs; * ****************************************************************************/ -#define up_cpu_index() (0) +#ifdef CONFIG_SMP +int up_cpu_index(void); +#else +# define up_cpu_index() (0) +#endif /**************************************************************************** * Inline functions @@ -133,7 +144,20 @@ EXTERN volatile uint32_t *g_current_regs; * ****************************************************************************/ -#define up_interrupt_context() (g_current_regs != NULL) +static inline bool up_interrupt_context(void) +{ +#ifdef CONFIG_SMP + irqstate_t flags = up_irq_save(); +#endif + + bool ret = CURRENT_REGS != NULL; + +#ifdef CONFIG_SMP + up_irq_restore(flags); +#endif + + return ret; +} /**************************************************************************** * Public Function Prototypes diff --git a/arch/sparc/src/common/up_assert.c b/arch/sparc/src/common/up_assert.c index 8ecb8ac684..001e2667bd 100644 --- a/arch/sparc/src/common/up_assert.c +++ b/arch/sparc/src/common/up_assert.c @@ -71,7 +71,7 @@ static void _up_assert(int errorcode) /* Are we in an interrupt handler or the idle task? */ - if (g_current_regs || running_task()->flink == NULL) + if (CURRENT_REGS || running_task()->flink == NULL) { #if CONFIG_BOARD_RESET_ON_ASSERT >= 1 board_reset(CONFIG_BOARD_ASSERT_RESET_VALUE); diff --git a/arch/sparc/src/common/up_checkstack.c b/arch/sparc/src/common/up_checkstack.c index 3cd6b4199d..ff5b3d2b91 100644 --- a/arch/sparc/src/common/up_checkstack.c +++ b/arch/sparc/src/common/up_checkstack.c @@ -218,10 +218,10 @@ ssize_t up_check_stack_remain(void) return up_check_tcbstack_remain(running_task()); } -#if CONFIG_ARCH_INTERRUPTSTACK > 3 +#if CONFIG_ARCH_INTERRUPTSTACK > 7 size_t up_check_intstack(void) { - return sparc_stack_check((uintptr_t)g_intstackalloc, + return sparc_stack_check((void *)up_intstack_alloc(), STACK_ALIGN_DOWN(CONFIG_ARCH_INTERRUPTSTACK)); } diff --git a/arch/sparc/src/common/up_initialize.c b/arch/sparc/src/common/up_initialize.c index 61906f5583..769753e705 100644 --- a/arch/sparc/src/common/up_initialize.c +++ b/arch/sparc/src/common/up_initialize.c @@ -75,7 +75,17 @@ * Public Data ****************************************************************************/ -volatile uint32_t *g_current_regs; +/* g_current_regs[] holds a reference to the current interrupt level + * register storage structure. It is non-NULL only during interrupt + * processing. Access to g_current_regs[] must be through the macro + * CURRENT_REGS for portability. + */ + +/* For the case of architectures with multiple CPUs, then there must be one + * such value for each processor that can receive an interrupt. + */ + +volatile uint32_t *g_current_regs[CONFIG_SMP_NCPUS]; /**************************************************************************** * Private Functions @@ -90,15 +100,15 @@ volatile uint32_t *g_current_regs; * ****************************************************************************/ -#if defined(CONFIG_STACK_COLORATION) && CONFIG_ARCH_INTERRUPTSTACK > 3 +#if defined(CONFIG_STACK_COLORATION) && CONFIG_ARCH_INTERRUPTSTACK > 7 static inline void up_color_intstack(void) { - uint8_t *ptr = g_intstackalloc; + uint32_t *ptr = (uint32_t *)up_intstack_alloc(); ssize_t size; - for (size = (CONFIG_ARCH_INTERRUPTSTACK & ~3); + for (size = ((CONFIG_ARCH_INTERRUPTSTACK & ~7) * CONFIG_SMP_NCPUS); size > 0; - size -= sizeof(uint8_t)) + size -= sizeof(uint32_t)) { *ptr++ = INTSTACK_COLOR; } @@ -130,6 +140,19 @@ static inline void up_color_intstack(void) void up_initialize(void) { +#ifdef CONFIG_SMP + int i; + + /* Initialize global variables */ + + for (i = 0; i < CONFIG_SMP_NCPUS; i++) + { + g_current_regs[i] = NULL; + } +#else + CURRENT_REGS = NULL; +#endif + /* Colorize the interrupt stack */ up_color_intstack(); diff --git a/arch/sparc/src/common/up_internal.h b/arch/sparc/src/common/up_internal.h index 02d33a15ea..81d42b65ce 100644 --- a/arch/sparc/src/common/up_internal.h +++ b/arch/sparc/src/common/up_internal.h @@ -80,6 +80,8 @@ # define CONFIG_ARCH_INTERRUPTSTACK 0 #endif +#define INTSTACK_SIZE (CONFIG_ARCH_INTERRUPTSTACK & ~STACK_ALIGN_MASK) + /* sparc requires at least a 4-byte stack alignment. For floating point use, * however, the stack must be aligned to 8-byte addresses. */ @@ -129,8 +131,9 @@ extern uint32_t g_idle_topstack; /* Address of the saved user stack pointer */ -#if CONFIG_ARCH_INTERRUPTSTACK > 3 -extern void g_intstackbase; +#if CONFIG_ARCH_INTERRUPTSTACK > 7 +extern uint8_t g_intstackalloc[]; /* Allocated stack base */ +extern uint8_t g_intstacktop[]; /* Initial top of interrupt stack */ #endif /* These symbols are setup by the linker script. */ @@ -200,6 +203,14 @@ int up_swint1(int irq, void *context, void *arg); void up_sigdeliver(void); +/* Interrupt handling *******************************************************/ + +#if CONFIG_ARCH_INTERRUPTSTACK > 7 +uintptr_t up_intstack_alloc(void); +uintptr_t up_intstack_top(void); +#endif + + /* Chip-specific functions **************************************************/ /* Chip specific functions defined in arch/sparc/src/ */ diff --git a/arch/sparc/src/sparc_v8/Toolchain.defs b/arch/sparc/src/sparc_v8/Toolchain.defs index df20e56147..623692ee6a 100644 --- a/arch/sparc/src/sparc_v8/Toolchain.defs +++ b/arch/sparc/src/sparc_v8/Toolchain.defs @@ -52,6 +52,8 @@ ifeq ($(CONFIG_ARCH_CHIP_BM3803),y) ARCHCPUFLAGS += -mcpu=leon else ifeq ($(CONFIG_ARCH_CHIP_BM3823),y) ARCHCPUFLAGS += -mcpu=leon -mflat +else ifeq ($(CONFIG_ARCH_CHIP_S698PM),y) + ARCHCPUFLAGS += -mcpu=leon3 endif ifeq ($(CONFIG_DEBUG_CUSTOMOPT),y) diff --git a/arch/sparc/src/sparc_v8/sparc_v8.h b/arch/sparc/src/sparc_v8/sparc_v8.h index 4f73302e0e..95faba8b67 100644 --- a/arch/sparc/src/sparc_v8/sparc_v8.h +++ b/arch/sparc/src/sparc_v8/sparc_v8.h @@ -41,9 +41,9 @@ * state from the TCB. */ -#define up_restorestate(regs) (g_current_regs = regs) +#define up_restorestate(regs) (CURRENT_REGS = regs) -#define up_savestate(regs) trap_flush_task(regs, (uint32_t*)g_current_regs) +#define up_savestate(regs) trap_flush_task(regs, (uint32_t*)CURRENT_REGS) /**************************************************************************** * Public Types diff --git a/arch/sparc/src/sparc_v8/up_blocktask.c b/arch/sparc/src/sparc_v8/up_blocktask.c index dc3e7bb8b2..a80b4f48bb 100644 --- a/arch/sparc/src/sparc_v8/up_blocktask.c +++ b/arch/sparc/src/sparc_v8/up_blocktask.c @@ -103,10 +103,10 @@ void up_block_task(struct tcb_s *tcb, tstate_t task_state) /* Are we in an interrupt handler? */ - if (g_current_regs) + if (CURRENT_REGS) { /* Yes, then we have to do things differently. - * Just copy the g_current_regs into the OLD rtcb. + * Just copy the CURRENT_REGS into the OLD rtcb. */ up_savestate(rtcb->xcp.regs); diff --git a/arch/sparc/src/sparc_v8/up_copystate.c b/arch/sparc/src/sparc_v8/up_copystate.c index e49a09d02a..c797e44dff 100644 --- a/arch/sparc/src/sparc_v8/up_copystate.c +++ b/arch/sparc/src/sparc_v8/up_copystate.c @@ -71,7 +71,7 @@ void up_copystate(uint32_t *dest, uint32_t *src) void task_flush_trap(uint32_t *trap, uint32_t *task) { - g_current_regs = task; + CURRENT_REGS = task; } void trap_flush_task(uint32_t *task, uint32_t *trap) diff --git a/arch/sparc/src/sparc_v8/up_doirq.c b/arch/sparc/src/sparc_v8/up_doirq.c index abfad3fd4a..1ae4546bf5 100644 --- a/arch/sparc/src/sparc_v8/up_doirq.c +++ b/arch/sparc/src/sparc_v8/up_doirq.c @@ -63,13 +63,13 @@ uint32_t *up_doirq(int irq, uint32_t *regs) #else regs = (uint32_t *)((uint32_t)regs + CPU_MINIMUM_STACK_FRAME_SIZE); /* Current regs non-zero indicates that we are processing an interrupt; - * g_current_regs is also used to manage interrupt level context switches. + * CURRENT_REGS is also used to manage interrupt level context switches. * * Nested interrupts are not supported. */ - DEBUGASSERT(g_current_regs == NULL); - g_current_regs = regs; + DEBUGASSERT(CURRENT_REGS == NULL); + CURRENT_REGS = regs; /* Deliver the IRQ */ @@ -77,18 +77,18 @@ uint32_t *up_doirq(int irq, uint32_t *regs) #if defined(CONFIG_ARCH_FPU) || defined(CONFIG_ARCH_ADDRENV) /* Check for a context switch. If a context switch occurred, then - * g_current_regs will have a different value than it did on entry. + * CURRENT_REGS will have a different value than it did on entry. * If an interrupt level context switch has occurred, then restore * the floating point state and the establish the correct address * environment before returning from the interrupt. */ - if (regs != g_current_regs) + if (regs != CURRENT_REGS) { #ifdef CONFIG_ARCH_FPU /* Restore floating point registers */ - up_restorefpu((uint32_t *)g_current_regs); + up_restorefpu((uint32_t *)CURRENT_REGS); #endif #ifdef CONFIG_ARCH_ADDRENV @@ -104,19 +104,26 @@ uint32_t *up_doirq(int irq, uint32_t *regs) #endif /* If a context switch occurred while processing the interrupt then - * g_current_regs may have change value. If we return any value different + * CURRENT_REGS may have change value. If we return any value different * from the input regs, then the lower level will know that a context * switch occurred during interrupt processing. */ - regs = (uint32_t *)((uint32_t)g_current_regs - + regs = (uint32_t *)((uint32_t)CURRENT_REGS - CPU_MINIMUM_STACK_FRAME_SIZE); - /* Set g_current_regs to NULL to indicate that we are no longer in - * an interrupt handler. + /* Restore the cpu lock */ + + if (regs != CURRENT_REGS) + { + restore_critical_section(); + } + + /* Set CURRENT_REGS to NULL to indicate that we are no longer in an + * interrupt handler. */ - g_current_regs = NULL; + CURRENT_REGS = NULL; #endif board_autoled_off(LED_INIRQ); return regs; diff --git a/arch/sparc/src/sparc_v8/up_dumpstate.c b/arch/sparc/src/sparc_v8/up_dumpstate.c index 0641edb5ee..9bef8bedac 100644 --- a/arch/sparc/src/sparc_v8/up_dumpstate.c +++ b/arch/sparc/src/sparc_v8/up_dumpstate.c @@ -65,25 +65,27 @@ static void up_stackdump(uint32_t sp, uint32_t stack_base) static inline void up_registerdump(void) { + uint32_t *regs = (uint32_t *)CURRENT_REGS; /* Don't need volatile here */ + /* Are user registers available from interrupt processing? */ - if (g_current_regs) + if (regs) { _alert("R%d: %08x %08x %08x %08x %08x %08x %08x %08x\n", 0, - g_current_regs[REG_R16], g_current_regs[REG_R17], - g_current_regs[REG_R18], g_current_regs[REG_R19], - g_current_regs[REG_R20], g_current_regs[REG_R21], - g_current_regs[REG_R22], g_current_regs[REG_R23]); + regs[REG_R16], regs[REG_R17], + regs[REG_R18], regs[REG_R19], + regs[REG_R20], regs[REG_R21], + regs[REG_R22], regs[REG_R23]); _alert("R%d: %08x %08x %08x %08x %08x %08x %08x %08x\n", 8, - g_current_regs[REG_R24], g_current_regs[REG_R25], - g_current_regs[REG_R26], g_current_regs[REG_R27], - g_current_regs[REG_R28], g_current_regs[REG_R29], - g_current_regs[REG_R30], g_current_regs[REG_R31]); + regs[REG_R24], regs[REG_R25], + regs[REG_R26], regs[REG_R27], + regs[REG_R28], regs[REG_R29], + regs[REG_R30], regs[REG_R31]); - _alert("SR: %08x\n", g_current_regs[REG_R14]); + _alert("SR: %08x\n", regs[REG_R14]); } } @@ -102,6 +104,12 @@ void up_dumpstate(void) uint32_t istacksize; #endif +#ifdef CONFIG_SMP + /* Show the CPU number */ + + _alert("CPU%d:\n", up_cpu_index()); +#endif + /* Dump the registers (if available) */ up_registerdump(); @@ -122,7 +130,7 @@ void up_dumpstate(void) /* Get the limits on the interrupt stack memory */ #if CONFIG_ARCH_INTERRUPTSTACK > 3 - istackbase = (uint32_t)g_intstackbase; + istackbase = (uint32_t)up_intstack_alloc(); istacksize = (CONFIG_ARCH_INTERRUPTSTACK & ~3) - 4; /* Show interrupt stack info */ @@ -145,7 +153,7 @@ void up_dumpstate(void) up_stackdump(sp, istackbase); } - else if (g_current_regs) + else if (CURRENT_REGS) { _alert("ERROR: Stack pointer is not within the interrupt stack\n"); up_stackdump(istackbase - istacksize, istackbase); @@ -156,9 +164,9 @@ void up_dumpstate(void) * pointer (and the above range check should have failed). */ - if (g_current_regs) + if (CURRENT_REGS) { - sp = g_current_regs[REG_I6]; + sp = CURRENT_REGS[REG_I6]; _alert("sp: %08x\n", sp); } diff --git a/arch/sparc/src/sparc_v8/up_initialstate.c b/arch/sparc/src/sparc_v8/up_initialstate.c index 6ef0754516..f608e87a4b 100644 --- a/arch/sparc/src/sparc_v8/up_initialstate.c +++ b/arch/sparc/src/sparc_v8/up_initialstate.c @@ -71,7 +71,7 @@ void up_initial_state(struct tcb_s *tcb) if (tcb->pid == IDLE_PROCESS_ID) { tcb->stack_alloc_ptr = (void *)(g_idle_topstack - - CONFIG_IDLETHREAD_STACKSIZE); + (CONFIG_SMP_NCPUS * CONFIG_IDLETHREAD_STACKSIZE)); tcb->stack_base_ptr = tcb->stack_alloc_ptr; tcb->adj_stack_size = CONFIG_IDLETHREAD_STACKSIZE; diff --git a/arch/sparc/src/sparc_v8/up_releasepending.c b/arch/sparc/src/sparc_v8/up_releasepending.c index bac279138e..c7e2af59d0 100644 --- a/arch/sparc/src/sparc_v8/up_releasepending.c +++ b/arch/sparc/src/sparc_v8/up_releasepending.c @@ -68,10 +68,10 @@ void up_release_pending(void) nxsched_suspend_scheduler(rtcb); - if (g_current_regs) + if (CURRENT_REGS) { /* Yes, then we have to do things differently. - * Just copy the g_current_regs into the OLD rtcb. + * Just copy the CURRENT_REGS into the OLD rtcb. */ up_savestate(rtcb->xcp.regs); diff --git a/arch/sparc/src/sparc_v8/up_reprioritizertr.c b/arch/sparc/src/sparc_v8/up_reprioritizertr.c index b2c12f6e26..1a8c575328 100644 --- a/arch/sparc/src/sparc_v8/up_reprioritizertr.c +++ b/arch/sparc/src/sparc_v8/up_reprioritizertr.c @@ -124,10 +124,10 @@ void up_reprioritize_rtr(struct tcb_s *tcb, uint8_t priority) /* Are we in an interrupt handler? */ - if (g_current_regs) + if (CURRENT_REGS) { /* Yes, then we have to do things differently. - * Just copy the g_current_regs into the OLD rtcb. + * Just copy the CURRENT_REGS into the OLD rtcb. */ up_savestate(rtcb->xcp.regs); diff --git a/arch/sparc/src/sparc_v8/up_schedulesigaction.c b/arch/sparc/src/sparc_v8/up_schedulesigaction.c index e6fe5e99f6..374bfea381 100644 --- a/arch/sparc/src/sparc_v8/up_schedulesigaction.c +++ b/arch/sparc/src/sparc_v8/up_schedulesigaction.c @@ -71,6 +71,7 @@ * ****************************************************************************/ +#ifndef CONFIG_SMP void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver) { irqstate_t flags; @@ -89,8 +90,7 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver) * being delivered to the currently executing task. */ - sinfo("rtcb=%p g_current_regs=%p\n", - this_task(), g_current_regs); + sinfo("rtcb=%p CURRENT_REGS=%p\n", this_task(), CURRENT_REGS); if (tcb == this_task()) { @@ -98,7 +98,7 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver) * a task is signalling itself for some reason. */ - if (!g_current_regs) + if (!CURRENT_REGS) { /* In this case just deliver the signal now. */ @@ -114,7 +114,7 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver) * logic would fail in the strange case where we are in an * interrupt handler, the thread is signalling itself, but * a context switch to another task has occurred so that - * g_current_regs does not refer to the thread of this_task()! + * CURRENT_REGS does not refer to the thread of this_task()! */ else @@ -125,17 +125,17 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver) */ tcb->xcp.sigdeliver = sigdeliver; - tcb->xcp.saved_pc = g_current_regs[REG_PC]; - tcb->xcp.saved_npc = g_current_regs[REG_NPC]; - tcb->xcp.saved_status = g_current_regs[REG_PSR]; + tcb->xcp.saved_pc = CURRENT_REGS[REG_PC]; + tcb->xcp.saved_npc = CURRENT_REGS[REG_NPC]; + tcb->xcp.saved_status = CURRENT_REGS[REG_PSR]; /* Then set up to vector to the trampoline with interrupts * disabled */ - g_current_regs[REG_PC] = (uint32_t)up_sigdeliver; - g_current_regs[REG_NPC] = (uint32_t)up_sigdeliver + 4; - g_current_regs[REG_PSR] |= SPARC_PSR_ET_MASK; + CURRENT_REGS[REG_PC] = (uint32_t)up_sigdeliver; + CURRENT_REGS[REG_NPC] = (uint32_t)up_sigdeliver + 4; + CURRENT_REGS[REG_PSR] |= SPARC_PSR_ET_MASK; /* And make sure that the saved context in the TCB * is the same as the interrupt return context. @@ -175,3 +175,184 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver) leave_critical_section(flags); } +#endif /* !CONFIG_SMP */ + +#ifdef CONFIG_SMP +void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver) +{ + irqstate_t flags; + int cpu; + int me; + + sinfo("tcb=0x%p sigdeliver=0x%p\n", tcb, sigdeliver); + + /* Make sure that interrupts are disabled */ + + flags = enter_critical_section(); + + /* Refuse to handle nested signal actions */ + + if (!tcb->xcp.sigdeliver) + { + /* First, handle some special cases when the signal is being delivered + * to task that is currently executing on any CPU. + */ + + sinfo("rtcb=0x%p CURRENT_REGS=0x%p\n", this_task(), CURRENT_REGS); + + if (tcb->task_state == TSTATE_TASK_RUNNING) + { + me = this_cpu(); + cpu = tcb->cpu; + + /* CASE 1: We are not in an interrupt handler and a task is + * signaling itself for some reason. + */ + + if (cpu == me && !CURRENT_REGS) + { + /* In this case just deliver the signal now. + * REVISIT: Signal handler will run in a critical section! + */ + + sigdeliver(tcb); + } + + /* CASE 2: The task that needs to receive the signal is running. + * This could happen if the task is running on another CPU OR if + * we are in an interrupt handler and the task is running on this + * CPU. In the former case, we will have to PAUSE the other CPU + * first. But in either case, we will have to modify the return + * state as well as the state in the TCB. + */ + + else + { + /* If we signaling a task running on the other CPU, we have + * to PAUSE the other CPU. + */ + + if (cpu != me) + { + /* Pause the CPU */ + + up_cpu_pause(cpu); + + /* Wait while the pause request is pending */ + + while (up_cpu_pausereq(cpu)) + { + } + + /* Now tcb on the other CPU can be accessed safely */ + + /* Copy tcb->xcp.regs to tcp.xcp.saved. These will be + * restored by the signal trampoline after the signal has + * been delivered. + */ + + tcb->xcp.sigdeliver = (FAR void *)sigdeliver; + tcb->xcp.saved_pc = tcb->xcp.regs[REG_PC]; + tcb->xcp.saved_npc = tcb->xcp.regs[REG_NPC]; + tcb->xcp.saved_status = tcb->xcp.regs[REG_PSR]; + + /* Then set up vector to the trampoline with interrupts + * disabled. We must already be in privileged thread mode + * to be here. + */ + + tcb->xcp.regs[REG_PC] = (uint32_t)up_sigdeliver; + tcb->xcp.regs[REG_NPC] = (uint32_t)up_sigdeliver + 4; + tcb->xcp.regs[REG_PSR] |= SPARC_PSR_ET_MASK; + } + else + { + /* tcb is running on the same CPU */ + + /* Save registers that must be protected while the signal + * handler runs. These will be restored by the signal + * trampoline after the signal(s) have been delivered. + */ + + tcb->xcp.sigdeliver = (FAR void *)sigdeliver; + tcb->xcp.saved_pc = CURRENT_REGS[REG_PC]; + tcb->xcp.saved_npc = CURRENT_REGS[REG_NPC]; + tcb->xcp.saved_status = CURRENT_REGS[REG_PSR]; + + /* Then set up vector to the trampoline with interrupts + * disabled. The kernel-space trampoline must run in + * privileged thread mode. + */ + + CURRENT_REGS[REG_PC] = (uint32_t)up_sigdeliver; + CURRENT_REGS[REG_NPC] = (uint32_t)up_sigdeliver + 4; + CURRENT_REGS[REG_PSR] |= SPARC_PSR_ET_MASK; + + /* And make sure that the saved context in the TCB is the + * same as the interrupt return context. + */ + + up_savestate(tcb->xcp.regs); + } + + /* Increment the IRQ lock count so that when the task is + * restarted, it will hold the IRQ spinlock. + */ + + DEBUGASSERT(tcb->irqcount < INT16_MAX); + tcb->irqcount++; + + /* NOTE: If the task runs on another CPU(cpu), adjusting + * global IRQ controls will be done in the pause handler + * on the CPU(cpu) by taking a critical section. + * If the task is scheduled on this CPU(me), do nothing + * because this CPU already took a critical section + */ + + /* RESUME the other CPU if it was PAUSED */ + + if (cpu != me) + { + up_cpu_resume(cpu); + } + } + } + + /* Otherwise, we are (1) signaling a task is not running from an + * interrupt handler or (2) we are not in an interrupt handler and the + * running task is signaling some other non-running task. + */ + + else + { + /* Save registers that must be protected while the signal + * handler runs. These will be restored by the signal + * trampoline after the signal(s) have been delivered. + */ + + tcb->xcp.sigdeliver = (FAR void *)sigdeliver; + tcb->xcp.saved_pc = CURRENT_REGS[REG_PC]; + tcb->xcp.saved_npc = CURRENT_REGS[REG_NPC]; + tcb->xcp.saved_status = CURRENT_REGS[REG_PSR]; + + /* Increment the IRQ lock count so that when the task is restarted, + * it will hold the IRQ spinlock. + */ + + DEBUGASSERT(tcb->irqcount < INT16_MAX); + tcb->irqcount++; + + /* Then set up to vector to the trampoline with interrupts + * disabled. We must already be in privileged thread mode to be + * here. + */ + + tcb->xcp.regs[REG_PC] = (uint32_t)up_sigdeliver; + tcb->xcp.regs[REG_NPC] = (uint32_t)up_sigdeliver + 4; + tcb->xcp.regs[REG_PSR] |= SPARC_PSR_ET_MASK; + } + } + + leave_critical_section(flags); +} +#endif /* CONFIG_SMP */ diff --git a/arch/sparc/src/sparc_v8/up_sigdeliver.c b/arch/sparc/src/sparc_v8/up_sigdeliver.c index 5e83b9c73f..835364f9cf 100644 --- a/arch/sparc/src/sparc_v8/up_sigdeliver.c +++ b/arch/sparc/src/sparc_v8/up_sigdeliver.c @@ -63,6 +63,17 @@ void up_sigdeliver(void) * EINTR). */ + int saved_errno = get_errno(); + +#ifdef CONFIG_SMP + /* In the SMP case, we must terminate the critical section while the signal + * handler executes, but we also need to restore the irqcount when the + * we resume the main thread of the task. + */ + + int16_t saved_irqcount; +#endif + board_autoled_on(LED_SIGNAL); sinfo("rtcb=%p sigdeliver=%p sigpendactionq.head=%p\n", @@ -73,6 +84,26 @@ void up_sigdeliver(void) up_copystate(regs, rtcb->xcp.regs); +#ifdef CONFIG_SMP + /* In the SMP case, up_schedule_sigaction(0) will have incremented + * 'irqcount' in order to force us into a critical section. Save the + * pre-incremented irqcount. + */ + + saved_irqcount = rtcb->irqcount - 1; + DEBUGASSERT(saved_irqcount >= 0); + + /* Now we need call leave_critical_section() repeatedly to get the irqcount + * to zero, freeing all global spinlocks that enforce the critical section. + */ + + do + { + leave_critical_section((regs[REG_PSR])); + } + while (rtcb->irqcount > 0); +#endif /* CONFIG_SMP */ + #ifndef CONFIG_SUPPRESS_INTERRUPTS /* Then make sure that interrupts are enabled. Signal handlers must always * run with interrupts enabled. @@ -91,7 +122,27 @@ void up_sigdeliver(void) */ sinfo("Resuming\n"); + + /* Call enter_critical_section() to disable local interrupts before + * restoring local context. + * + * Here, we should not use up_irq_save() in SMP mode. + * For example, if we call up_irq_save() here and another CPU might + * have called up_cpu_pause() to this cpu, hence g_cpu_irqlock has + * been locked by the cpu, in this case, we would see a deadlock in + * later call of enter_critical_section() to restore irqcount. + * To avoid this situation, we need to call enter_critical_section(). + */ + +#ifdef CONFIG_SMP + enter_critical_section(); +#else up_irq_save(); +#endif + + /* Restore the saved errno value */ + + set_errno(saved_errno); /* Modify the saved return state with the actual saved values in the * TCB. This depends on the fact that nested signal handling is @@ -108,6 +159,22 @@ void up_sigdeliver(void) regs[REG_PSR] = rtcb->xcp.saved_status; rtcb->xcp.sigdeliver = NULL; /* Allows next handler to be scheduled */ +#ifdef CONFIG_SMP + /* Restore the saved 'irqcount' and recover the critical section + * spinlocks. + * + * REVISIT: irqcount should be one from the above call to + * enter_critical_section(). Could the saved_irqcount be zero? That + * would be a problem. + */ + + DEBUGASSERT(rtcb->irqcount == 1); + while (rtcb->irqcount < saved_irqcount) + { + enter_critical_section(); + } +#endif + /* Then restore the correct state for this thread of execution. This is an * unusual case that must be handled by up_fullcontextresore. This case is * unusal in two ways: diff --git a/arch/sparc/src/sparc_v8/up_swint1.c b/arch/sparc/src/sparc_v8/up_swint1.c index 432d232f1d..1c690e7430 100644 --- a/arch/sparc/src/sparc_v8/up_swint1.c +++ b/arch/sparc/src/sparc_v8/up_swint1.c @@ -85,7 +85,7 @@ int up_swint1(int irq, void *context, void *arg) { uint32_t *regs = (uint32_t *)context; - DEBUGASSERT(regs && regs == g_current_regs); + DEBUGASSERT(regs && regs == CURRENT_REGS); /* Software interrupt 0 is invoked with REG_A0 (REG_R4) = system call * command and REG_A1-3 and REG_T0-2 (REG_R5-10) = variable number of @@ -110,7 +110,7 @@ int up_swint1(int irq, void *context, void *arg) * A0 = SYS_restore_context * A1 = restoreregs * - * In this case, we simply need to set g_current_regs to restore + * In this case, we simply need to set CURRENT_REGS to restore * register area referenced in the saved R1. context == g_current * regs is the normal exception return. By setting g_current * regs = context[R1], we force the return to the saved context @@ -120,7 +120,7 @@ int up_swint1(int irq, void *context, void *arg) case SYS_restore_context: { DEBUGASSERT(regs[REG_I1] != 0); - g_current_regs = (uint32_t *)regs[REG_I1]; + CURRENT_REGS = (uint32_t *)regs[REG_I1]; } break; @@ -136,7 +136,7 @@ int up_swint1(int irq, void *context, void *arg) * * In this case, we save the context registers to the save register * area referenced by the saved contents of R5 and then set - * g_current_regs to the save register area referenced by the saved + * CURRENT_REGS to the save register area referenced by the saved * contents of R6. */ @@ -147,7 +147,7 @@ int up_swint1(int irq, void *context, void *arg) /* task_flush_trap(regs,(uint32_t *)regs[REG_I2]); */ - g_current_regs = (uint32_t *)regs[REG_I2]; + CURRENT_REGS = (uint32_t *)regs[REG_I2]; } break; @@ -177,7 +177,7 @@ int up_swint1(int irq, void *context, void *arg) * the original mode. */ - g_current_regs[REG_I7] = rtcb->xcp.syscall[index].sysreturn; + CURRENT_REGS[REG_I7] = rtcb->xcp.syscall[index].sysreturn; #error "Missing logic -- need to restore the original mode" rtcb->xcp.nsyscalls = index; } @@ -192,7 +192,7 @@ int up_swint1(int irq, void *context, void *arg) /* Verify that the SYS call number is within range */ - DEBUGASSERT(g_current_regs[REG_I1] < SYS_maxsyscall); + DEBUGASSERT(CURRENT_REGS[REG_I1] < SYS_maxsyscall); /* Make sure that we got here that there is a no saved syscall * return address. We cannot yet handle nested system calls. @@ -211,7 +211,7 @@ int up_swint1(int irq, void *context, void *arg) /* Offset R0 to account for the reserved values */ - /* g_current_regs[REG_R0] -= CONFIG_SYS_RESERVED; *//*zouboan*/ + /* CURRENT_REGS[REG_R0] -= CONFIG_SYS_RESERVED; *//*zouboan*/ #else svcerr("ERROR: Bad SYS call: %d\n", regs[REG_I1]); #endif @@ -224,10 +224,10 @@ int up_swint1(int irq, void *context, void *arg) */ #ifdef CONFIG_DEBUG_SYSCALL_INFO - if (regs != g_current_regs) + if (regs != CURRENT_REGS) { svcinfo("SWInt Return: Context switch!\n"); - up_registerdump((const uint32_t *)g_current_regs); + up_registerdump((const uint32_t *)CURRENT_REGS); } else { diff --git a/arch/sparc/src/sparc_v8/up_unblocktask.c b/arch/sparc/src/sparc_v8/up_unblocktask.c index d09511bd1e..986b4af358 100644 --- a/arch/sparc/src/sparc_v8/up_unblocktask.c +++ b/arch/sparc/src/sparc_v8/up_unblocktask.c @@ -86,10 +86,10 @@ void up_unblock_task(struct tcb_s *tcb) /* Are we in an interrupt handler? */ - if (g_current_regs) + if (CURRENT_REGS) { /* Yes, then we have to do things differently. - * Just copy the g_current_regs into the OLD rtcb. + * Just copy the CURRENT_REGS into the OLD rtcb. */ up_savestate(rtcb->xcp.regs);