Commit 09f8a6db authored by Max Filippov's avatar Max Filippov
Browse files

xtensa: add support for call0 ABI in userspace



Provide a Kconfig choice to select whether only the default ABI, only
call0 ABI or both are supported. The default for XEA2 is windowed, but
it may change for XEA3. Call0 only runs userspace with PS.WOE disabled.
Supporting both windowed and call0 ABIs is tricky, as there's no
indication in the ELF binaries which ABI they use. So it is done by
probing: each process is started with PS.WOE disabled, but the handler
of an illegal instruction exception taken with PS.WOE retries faulting
instruction after enabling PS.WOE. It must happen before any signal is
delivered to the process, otherwise it may be delivered incorrectly.

Signed-off-by: default avatarMax Filippov <jcmvbkbc@gmail.com>
parent 9e1e41c4
Loading
Loading
Loading
Loading
+48 −0
Original line number Diff line number Diff line
@@ -385,6 +385,54 @@ config FAST_SYSCALL_SPILL_REGISTERS

	  If unsure, say N.

config USER_ABI_CALL0
	bool

choice
	prompt "Userspace ABI"
	default USER_ABI_DEFAULT
	help
	  Select supported userspace ABI.

	  If unsure, choose the default ABI.

config USER_ABI_DEFAULT
	bool "Default ABI only"
	help
	  Assume default userspace ABI. For XEA2 cores it is windowed ABI.
	  call0 ABI binaries may be run on such kernel, but signal delivery
	  will not work correctly for them.

config USER_ABI_CALL0_ONLY
	bool "Call0 ABI only"
	select USER_ABI_CALL0
	help
	  Select this option to support only call0 ABI in userspace.
	  Windowed ABI binaries will crash with a segfault caused by
	  an illegal instruction exception on the first 'entry' opcode.

	  Choose this option if you're planning to run only user code
	  built with call0 ABI.

config USER_ABI_CALL0_PROBE
	bool "Support both windowed and call0 ABI by probing"
	select USER_ABI_CALL0
	help
	  Select this option to support both windowed and call0 userspace
	  ABIs. When enabled all processes are started with PS.WOE disabled
	  and a fast user exception handler for an illegal instruction is
	  used to turn on PS.WOE bit on the first 'entry' opcode executed by
	  the userspace.

	  This option should be enabled for the kernel that must support
	  both call0 and windowed ABIs in userspace at the same time.

	  Note that Xtensa ISA does not guarantee that entry opcode will
	  raise an illegal instruction exception on cores with XEA2 when
	  PS.WOE is disabled, check whether the target core supports it.

endchoice

endmenu

config XTENSA_CALIBRATE_CCOUNT
+8 −1
Original line number Diff line number Diff line
@@ -176,14 +176,21 @@ struct thread_struct {

/*
 * Do necessary setup to start up a newly executed thread.
 * Note: We set-up ps as if we did a call4 to the new pc.
 * Note: When windowed ABI is used for userspace we set-up ps
 *       as if we did a call4 to the new pc.
 *       set_thread_state in signal.c depends on it.
 */
#if IS_ENABLED(CONFIG_USER_ABI_CALL0)
#define USER_PS_VALUE ((USER_RING << PS_RING_SHIFT) |			\
		       (1 << PS_UM_BIT) |				\
		       (1 << PS_EXCM_BIT))
#else
#define USER_PS_VALUE (PS_WOE_MASK |					\
		       (1 << PS_CALLINC_SHIFT) |			\
		       (USER_RING << PS_RING_SHIFT) |			\
		       (1 << PS_UM_BIT) |				\
		       (1 << PS_EXCM_BIT))
#endif

/* Clearing a0 terminates the backtrace. */
#define start_thread(regs, new_pc, new_sp) \
+34 −0
Original line number Diff line number Diff line
@@ -1003,6 +1003,40 @@ ENTRY(fast_alloca)
4:	j	_WindowUnderflow4
ENDPROC(fast_alloca)

#ifdef CONFIG_USER_ABI_CALL0_PROBE
/*
 * fast illegal instruction handler.
 *
 * This is used to fix up user PS.WOE on the exception caused
 * by the first opcode related to register window. If PS.WOE is
 * already set it goes directly to the common user exception handler.
 *
 * Entry condition:
 *
 *   a0:	trashed, original value saved on stack (PT_AREG0)
 *   a1:	a1
 *   a2:	new stack pointer, original in DEPC
 *   a3:	a3
 *   depc:	a2, original value saved on stack (PT_DEPC)
 *   excsave_1:	dispatch table
 */

ENTRY(fast_illegal_instruction_user)

	rsr	a0, ps
	bbsi.l	a0, PS_WOE_BIT, user_exception
	s32i	a3, a2, PT_AREG3
	movi	a3, PS_WOE_MASK
	or	a0, a0, a3
	wsr	a0, ps
	l32i	a3, a2, PT_AREG3
	l32i	a0, a2, PT_AREG0
	rsr	a2, depc
	rfe

ENDPROC(fast_illegal_instruction_user)
#endif

	/*
 * fast system calls.
 *
+18 −8
Original line number Diff line number Diff line
@@ -335,7 +335,8 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set,
{
	struct rt_sigframe *frame;
	int err = 0, sig = ksig->sig;
	unsigned long sp, ra, tp;
	unsigned long sp, ra, tp, ps;
	unsigned int base;

	sp = regs->areg[1];

@@ -385,17 +386,26 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set,

	/* Set up registers for signal handler; preserve the threadptr */
	tp = regs->threadptr;
	ps = regs->ps;
	start_thread(regs, (unsigned long) ksig->ka.sa.sa_handler,
		     (unsigned long) frame);

	/* Set up a stack frame for a call4
	 * Note: PS.CALLINC is set to one by start_thread
	 */
	regs->areg[4] = (((unsigned long) ra) & 0x3fffffff) | 0x40000000;
	regs->areg[6] = (unsigned long) sig;
	regs->areg[7] = (unsigned long) &frame->info;
	regs->areg[8] = (unsigned long) &frame->uc;
	/* Set up a stack frame for a call4 if userspace uses windowed ABI */
	if (ps & PS_WOE_MASK) {
		base = 4;
		regs->areg[base] =
			(((unsigned long) ra) & 0x3fffffff) | 0x40000000;
		ps = (ps & ~(PS_CALLINC_MASK | PS_OWB_MASK)) |
			(1 << PS_CALLINC_SHIFT);
	} else {
		base = 0;
		regs->areg[base] = (unsigned long) ra;
	}
	regs->areg[base + 2] = (unsigned long) sig;
	regs->areg[base + 3] = (unsigned long) &frame->info;
	regs->areg[base + 4] = (unsigned long) &frame->uc;
	regs->threadptr = tp;
	regs->ps = ps;

	pr_debug("SIG rt deliver (%s:%d): signal=%d sp=%p pc=%08lx\n",
		 current->comm, current->pid, sig, frame, regs->pc);
+5 −0
Original line number Diff line number Diff line
@@ -44,6 +44,11 @@ void xtensa_backtrace_user(struct pt_regs *regs, unsigned int depth,
	if (pc == 0 || pc >= TASK_SIZE || ufn(&frame, data))
		return;

	if (IS_ENABLED(CONFIG_USER_ABI_CALL0_ONLY) ||
	    (IS_ENABLED(CONFIG_USER_ABI_CALL0_PROBE) &&
	     !(regs->ps & PS_WOE_MASK)))
		return;

	/* Two steps:
	 *
	 * 1. Look through the register window for the
Loading