arch/sparc change common file to support SMP
This commit is contained in:
parent
488a56280a
commit
b3ff4ce301
19 changed files with 410 additions and 72 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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/<chip> */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue