summaryrefslogtreecommitdiffstats
path: root/arch/xtensa
diff options
context:
space:
mode:
authorMax Filippov <jcmvbkbc@gmail.com>2015-01-12 01:44:44 -0500
committerMax Filippov <jcmvbkbc@gmail.com>2019-09-01 16:11:57 -0400
commit09f8a6db20e6ed8eab1b2b23d09d2458f6e15062 (patch)
tree1c3b6baee5c51968f10d2af9c97e35208542ed53 /arch/xtensa
parent9e1e41c44782741c727688a19e5624d039b0de7e (diff)
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: Max Filippov <jcmvbkbc@gmail.com>
Diffstat (limited to 'arch/xtensa')
-rw-r--r--arch/xtensa/Kconfig48
-rw-r--r--arch/xtensa/include/asm/processor.h9
-rw-r--r--arch/xtensa/kernel/entry.S34
-rw-r--r--arch/xtensa/kernel/signal.c26
-rw-r--r--arch/xtensa/kernel/stacktrace.c5
-rw-r--r--arch/xtensa/kernel/traps.c4
6 files changed, 117 insertions, 9 deletions
diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig
index ebc135bda921..fb64469ca8f0 100644
--- a/arch/xtensa/Kconfig
+++ b/arch/xtensa/Kconfig
@@ -385,6 +385,54 @@ config FAST_SYSCALL_SPILL_REGISTERS
385 385
386 If unsure, say N. 386 If unsure, say N.
387 387
388config USER_ABI_CALL0
389 bool
390
391choice
392 prompt "Userspace ABI"
393 default USER_ABI_DEFAULT
394 help
395 Select supported userspace ABI.
396
397 If unsure, choose the default ABI.
398
399config USER_ABI_DEFAULT
400 bool "Default ABI only"
401 help
402 Assume default userspace ABI. For XEA2 cores it is windowed ABI.
403 call0 ABI binaries may be run on such kernel, but signal delivery
404 will not work correctly for them.
405
406config USER_ABI_CALL0_ONLY
407 bool "Call0 ABI only"
408 select USER_ABI_CALL0
409 help
410 Select this option to support only call0 ABI in userspace.
411 Windowed ABI binaries will crash with a segfault caused by
412 an illegal instruction exception on the first 'entry' opcode.
413
414 Choose this option if you're planning to run only user code
415 built with call0 ABI.
416
417config USER_ABI_CALL0_PROBE
418 bool "Support both windowed and call0 ABI by probing"
419 select USER_ABI_CALL0
420 help
421 Select this option to support both windowed and call0 userspace
422 ABIs. When enabled all processes are started with PS.WOE disabled
423 and a fast user exception handler for an illegal instruction is
424 used to turn on PS.WOE bit on the first 'entry' opcode executed by
425 the userspace.
426
427 This option should be enabled for the kernel that must support
428 both call0 and windowed ABIs in userspace at the same time.
429
430 Note that Xtensa ISA does not guarantee that entry opcode will
431 raise an illegal instruction exception on cores with XEA2 when
432 PS.WOE is disabled, check whether the target core supports it.
433
434endchoice
435
388endmenu 436endmenu
389 437
390config XTENSA_CALIBRATE_CCOUNT 438config XTENSA_CALIBRATE_CCOUNT
diff --git a/arch/xtensa/include/asm/processor.h b/arch/xtensa/include/asm/processor.h
index 0b4efec9e19e..7495520d7a3e 100644
--- a/arch/xtensa/include/asm/processor.h
+++ b/arch/xtensa/include/asm/processor.h
@@ -176,14 +176,21 @@ struct thread_struct {
176 176
177/* 177/*
178 * Do necessary setup to start up a newly executed thread. 178 * Do necessary setup to start up a newly executed thread.
179 * Note: We set-up ps as if we did a call4 to the new pc. 179 * Note: When windowed ABI is used for userspace we set-up ps
180 * as if we did a call4 to the new pc.
180 * set_thread_state in signal.c depends on it. 181 * set_thread_state in signal.c depends on it.
181 */ 182 */
183#if IS_ENABLED(CONFIG_USER_ABI_CALL0)
184#define USER_PS_VALUE ((USER_RING << PS_RING_SHIFT) | \
185 (1 << PS_UM_BIT) | \
186 (1 << PS_EXCM_BIT))
187#else
182#define USER_PS_VALUE (PS_WOE_MASK | \ 188#define USER_PS_VALUE (PS_WOE_MASK | \
183 (1 << PS_CALLINC_SHIFT) | \ 189 (1 << PS_CALLINC_SHIFT) | \
184 (USER_RING << PS_RING_SHIFT) | \ 190 (USER_RING << PS_RING_SHIFT) | \
185 (1 << PS_UM_BIT) | \ 191 (1 << PS_UM_BIT) | \
186 (1 << PS_EXCM_BIT)) 192 (1 << PS_EXCM_BIT))
193#endif
187 194
188/* Clearing a0 terminates the backtrace. */ 195/* Clearing a0 terminates the backtrace. */
189#define start_thread(regs, new_pc, new_sp) \ 196#define start_thread(regs, new_pc, new_sp) \
diff --git a/arch/xtensa/kernel/entry.S b/arch/xtensa/kernel/entry.S
index 9afe8f612f23..9e3676879168 100644
--- a/arch/xtensa/kernel/entry.S
+++ b/arch/xtensa/kernel/entry.S
@@ -1003,7 +1003,41 @@ ENTRY(fast_alloca)
10034: j _WindowUnderflow4 10034: j _WindowUnderflow4
1004ENDPROC(fast_alloca) 1004ENDPROC(fast_alloca)
1005 1005
1006#ifdef CONFIG_USER_ABI_CALL0_PROBE
1006/* 1007/*
1008 * fast illegal instruction handler.
1009 *
1010 * This is used to fix up user PS.WOE on the exception caused
1011 * by the first opcode related to register window. If PS.WOE is
1012 * already set it goes directly to the common user exception handler.
1013 *
1014 * Entry condition:
1015 *
1016 * a0: trashed, original value saved on stack (PT_AREG0)
1017 * a1: a1
1018 * a2: new stack pointer, original in DEPC
1019 * a3: a3
1020 * depc: a2, original value saved on stack (PT_DEPC)
1021 * excsave_1: dispatch table
1022 */
1023
1024ENTRY(fast_illegal_instruction_user)
1025
1026 rsr a0, ps
1027 bbsi.l a0, PS_WOE_BIT, user_exception
1028 s32i a3, a2, PT_AREG3
1029 movi a3, PS_WOE_MASK
1030 or a0, a0, a3
1031 wsr a0, ps
1032 l32i a3, a2, PT_AREG3
1033 l32i a0, a2, PT_AREG0
1034 rsr a2, depc
1035 rfe
1036
1037ENDPROC(fast_illegal_instruction_user)
1038#endif
1039
1040 /*
1007 * fast system calls. 1041 * fast system calls.
1008 * 1042 *
1009 * WARNING: The kernel doesn't save the entire user context before 1043 * WARNING: The kernel doesn't save the entire user context before
diff --git a/arch/xtensa/kernel/signal.c b/arch/xtensa/kernel/signal.c
index fbedf2aba09d..dae83cddd6ca 100644
--- a/arch/xtensa/kernel/signal.c
+++ b/arch/xtensa/kernel/signal.c
@@ -335,7 +335,8 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set,
335{ 335{
336 struct rt_sigframe *frame; 336 struct rt_sigframe *frame;
337 int err = 0, sig = ksig->sig; 337 int err = 0, sig = ksig->sig;
338 unsigned long sp, ra, tp; 338 unsigned long sp, ra, tp, ps;
339 unsigned int base;
339 340
340 sp = regs->areg[1]; 341 sp = regs->areg[1];
341 342
@@ -385,17 +386,26 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set,
385 386
386 /* Set up registers for signal handler; preserve the threadptr */ 387 /* Set up registers for signal handler; preserve the threadptr */
387 tp = regs->threadptr; 388 tp = regs->threadptr;
389 ps = regs->ps;
388 start_thread(regs, (unsigned long) ksig->ka.sa.sa_handler, 390 start_thread(regs, (unsigned long) ksig->ka.sa.sa_handler,
389 (unsigned long) frame); 391 (unsigned long) frame);
390 392
391 /* Set up a stack frame for a call4 393 /* Set up a stack frame for a call4 if userspace uses windowed ABI */
392 * Note: PS.CALLINC is set to one by start_thread 394 if (ps & PS_WOE_MASK) {
393 */ 395 base = 4;
394 regs->areg[4] = (((unsigned long) ra) & 0x3fffffff) | 0x40000000; 396 regs->areg[base] =
395 regs->areg[6] = (unsigned long) sig; 397 (((unsigned long) ra) & 0x3fffffff) | 0x40000000;
396 regs->areg[7] = (unsigned long) &frame->info; 398 ps = (ps & ~(PS_CALLINC_MASK | PS_OWB_MASK)) |
397 regs->areg[8] = (unsigned long) &frame->uc; 399 (1 << PS_CALLINC_SHIFT);
400 } else {
401 base = 0;
402 regs->areg[base] = (unsigned long) ra;
403 }
404 regs->areg[base + 2] = (unsigned long) sig;
405 regs->areg[base + 3] = (unsigned long) &frame->info;
406 regs->areg[base + 4] = (unsigned long) &frame->uc;
398 regs->threadptr = tp; 407 regs->threadptr = tp;
408 regs->ps = ps;
399 409
400 pr_debug("SIG rt deliver (%s:%d): signal=%d sp=%p pc=%08lx\n", 410 pr_debug("SIG rt deliver (%s:%d): signal=%d sp=%p pc=%08lx\n",
401 current->comm, current->pid, sig, frame, regs->pc); 411 current->comm, current->pid, sig, frame, regs->pc);
diff --git a/arch/xtensa/kernel/stacktrace.c b/arch/xtensa/kernel/stacktrace.c
index b9f82510c650..c822abb93d20 100644
--- a/arch/xtensa/kernel/stacktrace.c
+++ b/arch/xtensa/kernel/stacktrace.c
@@ -44,6 +44,11 @@ void xtensa_backtrace_user(struct pt_regs *regs, unsigned int depth,
44 if (pc == 0 || pc >= TASK_SIZE || ufn(&frame, data)) 44 if (pc == 0 || pc >= TASK_SIZE || ufn(&frame, data))
45 return; 45 return;
46 46
47 if (IS_ENABLED(CONFIG_USER_ABI_CALL0_ONLY) ||
48 (IS_ENABLED(CONFIG_USER_ABI_CALL0_PROBE) &&
49 !(regs->ps & PS_WOE_MASK)))
50 return;
51
47 /* Two steps: 52 /* Two steps:
48 * 53 *
49 * 1. Look through the register window for the 54 * 1. Look through the register window for the
diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c
index f060348c1b23..4a6c495ce9b6 100644
--- a/arch/xtensa/kernel/traps.c
+++ b/arch/xtensa/kernel/traps.c
@@ -51,6 +51,7 @@
51extern void kernel_exception(void); 51extern void kernel_exception(void);
52extern void user_exception(void); 52extern void user_exception(void);
53 53
54extern void fast_illegal_instruction_user(void);
54extern void fast_syscall_user(void); 55extern void fast_syscall_user(void);
55extern void fast_alloca(void); 56extern void fast_alloca(void);
56extern void fast_unaligned(void); 57extern void fast_unaligned(void);
@@ -87,6 +88,9 @@ typedef struct {
87 88
88static dispatch_init_table_t __initdata dispatch_init_table[] = { 89static dispatch_init_table_t __initdata dispatch_init_table[] = {
89 90
91#ifdef CONFIG_USER_ABI_CALL0_PROBE
92{ EXCCAUSE_ILLEGAL_INSTRUCTION, USER, fast_illegal_instruction_user },
93#endif
90{ EXCCAUSE_ILLEGAL_INSTRUCTION, 0, do_illegal_instruction}, 94{ EXCCAUSE_ILLEGAL_INSTRUCTION, 0, do_illegal_instruction},
91{ EXCCAUSE_SYSTEM_CALL, USER, fast_syscall_user }, 95{ EXCCAUSE_SYSTEM_CALL, USER, fast_syscall_user },
92{ EXCCAUSE_SYSTEM_CALL, 0, system_call }, 96{ EXCCAUSE_SYSTEM_CALL, 0, system_call },