arch: armv6-m: Apply armv7-m signal handling logic
Summary: - This commit applies armv7-m signal handling logic Impact: - armv6-m signal handling Testing: - Tested with ostest with the following configs - raspberrypi-pico:nsh, raspberrypi-pico:smp Signed-off-by: Masayuki Ishikawa <Masayuki.Ishikawa@jp.sony.com>
This commit is contained in:
parent
40fdf388bd
commit
ef1826e133
@ -52,6 +52,8 @@
|
||||
#include "arm_internal.h"
|
||||
#include "arm_arch.h"
|
||||
|
||||
#include "irq/irq.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
@ -92,13 +94,15 @@
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CONFIG_SMP
|
||||
void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
|
||||
{
|
||||
sinfo("tcb=0x%p sigdeliver=0x%p\n", tcb, sigdeliver);
|
||||
DEBUGASSERT(tcb != NULL && sigdeliver != NULL);
|
||||
|
||||
/* Refuse to handle nested signal actions */
|
||||
|
||||
if (!tcb->xcp.sigdeliver)
|
||||
if (tcb->xcp.sigdeliver == NULL)
|
||||
{
|
||||
/* First, handle some special cases when the signal is being delivered
|
||||
* to the currently executing task.
|
||||
@ -109,12 +113,14 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
|
||||
if (tcb == this_task())
|
||||
{
|
||||
/* CASE 1: We are not in an interrupt handler and a task is
|
||||
* signalling itself for some reason.
|
||||
* signaling itself for some reason.
|
||||
*/
|
||||
|
||||
if (!CURRENT_REGS)
|
||||
{
|
||||
/* In this case just deliver the signal now. */
|
||||
/* In this case just deliver the signal now.
|
||||
* REVISIT: Signal handle will run in a critical section!
|
||||
*/
|
||||
|
||||
sigdeliver(tcb);
|
||||
}
|
||||
@ -127,28 +133,30 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
|
||||
|
||||
else
|
||||
{
|
||||
/* Save the return PC, CPSR, and PRIMASK registers (and
|
||||
* perhaps the LR). These will be restored by the signal
|
||||
* trampoline after the signal has been delivered.
|
||||
/* Save the return PC, CPSR and PRIMASK
|
||||
* registers (and perhaps also the LR). These will be
|
||||
* restored by the signal trampoline after the signal has been
|
||||
* delivered.
|
||||
*/
|
||||
|
||||
tcb->xcp.sigdeliver = sigdeliver;
|
||||
tcb->xcp.saved_pc = CURRENT_REGS[REG_PC];
|
||||
tcb->xcp.saved_primask = CURRENT_REGS[REG_PRIMASK];
|
||||
tcb->xcp.saved_xpsr = CURRENT_REGS[REG_XPSR];
|
||||
tcb->xcp.sigdeliver = (FAR void *)sigdeliver;
|
||||
tcb->xcp.saved_pc = CURRENT_REGS[REG_PC];
|
||||
tcb->xcp.saved_primask = CURRENT_REGS[REG_PRIMASK];
|
||||
tcb->xcp.saved_xpsr = CURRENT_REGS[REG_XPSR];
|
||||
#ifdef CONFIG_BUILD_PROTECTED
|
||||
tcb->xcp.saved_lr = CURRENT_REGS[REG_LR];
|
||||
tcb->xcp.saved_lr = CURRENT_REGS[REG_LR];
|
||||
#endif
|
||||
/* Then set up to vector to the trampoline with interrupts
|
||||
* disabled. The kernel-space trampoline must run in
|
||||
* privileged thread mode.
|
||||
*/
|
||||
|
||||
CURRENT_REGS[REG_PC] = (uint32_t)arm_sigdeliver;
|
||||
CURRENT_REGS[REG_PRIMASK] = 1;
|
||||
CURRENT_REGS[REG_XPSR] = ARMV6M_XPSR_T;
|
||||
CURRENT_REGS[REG_PC] = (uint32_t)arm_sigdeliver;
|
||||
CURRENT_REGS[REG_PRIMASK] = 1;
|
||||
CURRENT_REGS[REG_XPSR] = ARMV6M_XPSR_T;
|
||||
#ifdef CONFIG_BUILD_PROTECTED
|
||||
CURRENT_REGS[REG_LR] = EXC_RETURN_PRIVTHR;
|
||||
CURRENT_REGS[REG_LR] = EXC_RETURN_PRIVTHR;
|
||||
CURRENT_REGS[REG_EXC_RETURN] = EXC_RETURN_PRIVTHR;
|
||||
#endif
|
||||
/* And make sure that the saved context in the TCB is the same
|
||||
* as the interrupt return context.
|
||||
@ -158,25 +166,215 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise, we are (1) signalling a task is not running from an
|
||||
/* 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 signalling some non-running task.
|
||||
* running task is signaling* some non-running task.
|
||||
*/
|
||||
|
||||
else
|
||||
{
|
||||
/* Save the return PS, CPSR and PRIMASK register (a perhaps also
|
||||
* the LR). These will be restored by the signal trampoline after
|
||||
* the signal has been delivered.
|
||||
/* Save the return PC, CPSR and PRIMASK
|
||||
* registers (and perhaps also the LR). These will be restored
|
||||
* by the signal trampoline after the signal has been delivered.
|
||||
*/
|
||||
|
||||
tcb->xcp.sigdeliver = sigdeliver;
|
||||
tcb->xcp.saved_pc = tcb->xcp.regs[REG_PC];
|
||||
tcb->xcp.saved_primask = tcb->xcp.regs[REG_PRIMASK];
|
||||
tcb->xcp.saved_xpsr = tcb->xcp.regs[REG_XPSR];
|
||||
tcb->xcp.sigdeliver = (FAR void *)sigdeliver;
|
||||
tcb->xcp.saved_pc = tcb->xcp.regs[REG_PC];
|
||||
tcb->xcp.saved_primask = tcb->xcp.regs[REG_PRIMASK];
|
||||
tcb->xcp.saved_xpsr = tcb->xcp.regs[REG_XPSR];
|
||||
#ifdef CONFIG_BUILD_PROTECTED
|
||||
tcb->xcp.saved_lr = tcb->xcp.regs[REG_LR];
|
||||
tcb->xcp.saved_lr = tcb->xcp.regs[REG_LR];
|
||||
#endif
|
||||
/* 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)arm_sigdeliver;
|
||||
tcb->xcp.regs[REG_PRIMASK] = 1;
|
||||
tcb->xcp.regs[REG_XPSR] = ARMV6M_XPSR_T;
|
||||
#ifdef CONFIG_BUILD_PROTECTED
|
||||
tcb->xcp.regs[REG_LR] = EXC_RETURN_PRIVTHR;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* !CONFIG_SMP */
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
|
||||
{
|
||||
int cpu;
|
||||
int me;
|
||||
|
||||
sinfo("tcb=0x%p sigdeliver=0x%p\n", tcb, sigdeliver);
|
||||
|
||||
/* 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_primask = tcb->xcp.regs[REG_PRIMASK];
|
||||
tcb->xcp.saved_xpsr = tcb->xcp.regs[REG_XPSR];
|
||||
#ifdef CONFIG_BUILD_PROTECTED
|
||||
tcb->xcp.saved_lr = tcb->xcp.regs[REG_LR];
|
||||
#endif
|
||||
|
||||
/* 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)arm_sigdeliver;
|
||||
tcb->xcp.regs[REG_PRIMASK] = 1;
|
||||
tcb->xcp.regs[REG_XPSR] = ARMV6M_XPSR_T;
|
||||
#ifdef CONFIG_BUILD_PROTECTED
|
||||
tcb->xcp.regs[REG_LR] = EXC_RETURN_PRIVTHR;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
/* tcb is running on the same CPU */
|
||||
|
||||
/* Save the return PC, CPSR and either the BASEPRI or
|
||||
* PRIMASK registers (and perhaps also the LR). These
|
||||
* will be restored by the signal trampoline after the
|
||||
* signal has been delivered.
|
||||
*/
|
||||
|
||||
tcb->xcp.sigdeliver = (FAR void *)sigdeliver;
|
||||
tcb->xcp.saved_pc = CURRENT_REGS[REG_PC];
|
||||
tcb->xcp.saved_primask = CURRENT_REGS[REG_PRIMASK];
|
||||
tcb->xcp.saved_xpsr = CURRENT_REGS[REG_XPSR];
|
||||
#ifdef CONFIG_BUILD_PROTECTED
|
||||
tcb->xcp.saved_lr = CURRENT_REGS[REG_LR];
|
||||
#endif
|
||||
|
||||
/* 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)arm_sigdeliver;
|
||||
CURRENT_REGS[REG_PRIMASK] = 1;
|
||||
CURRENT_REGS[REG_XPSR] = ARMV6M_XPSR_T;
|
||||
#ifdef CONFIG_BUILD_PROTECTED
|
||||
CURRENT_REGS[REG_LR] = EXC_RETURN_PRIVTHR;
|
||||
#endif
|
||||
|
||||
/* And make sure that the saved context in the TCB is the
|
||||
* same as the interrupt return context.
|
||||
*/
|
||||
|
||||
arm_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 the return PC, CPSR and either the BASEPRI or PRIMASK
|
||||
* registers (and perhaps also the LR). 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_primask = tcb->xcp.regs[REG_PRIMASK];
|
||||
tcb->xcp.saved_xpsr = tcb->xcp.regs[REG_XPSR];
|
||||
#ifdef CONFIG_BUILD_PROTECTED
|
||||
tcb->xcp.saved_lr = tcb->xcp.regs[REG_LR];
|
||||
#endif
|
||||
/* 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
|
||||
@ -192,3 +390,4 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_SMP */
|
||||
|
@ -83,6 +83,15 @@ void arm_sigdeliver(void)
|
||||
|
||||
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",
|
||||
@ -93,6 +102,26 @@ void arm_sigdeliver(void)
|
||||
|
||||
arm_copyfullstate(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((uint16_t)regs[REG_PRIMASK]);
|
||||
}
|
||||
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.
|
||||
@ -108,10 +137,32 @@ void arm_sigdeliver(void)
|
||||
/* Output any debug messages BEFORE restoring errno (because they may
|
||||
* alter errno), then disable interrupts again and restore the original
|
||||
* errno that is needed by the user logic (it is probably EINTR).
|
||||
*
|
||||
* I would prefer that all interrupts are disabled when
|
||||
* arm_fullcontextrestore() is called, but that may not be necessary.
|
||||
*/
|
||||
|
||||
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
|
||||
@ -132,6 +183,22 @@ void arm_sigdeliver(void)
|
||||
#endif
|
||||
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.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user