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 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 diff --git a/arch/xtensa/boot/dts/virt.dts b/arch/xtensa/boot/dts/virt.dts index 6aecbc0f3549..a9dcd87b6eb1 100644 --- a/arch/xtensa/boot/dts/virt.dts +++ b/arch/xtensa/boot/dts/virt.dts @@ -52,12 +52,12 @@ #size-cells = <2>; #interrupt-cells = <0x1>; - bus-range = <0x0 0x3f>; - reg = <0xc0000000 0x04000000>; + bus-range = <0x0 0x3e>; + reg = <0xf0100000 0x03f00000>; // BUS_ADDRESS(3) CPU_PHYSICAL(1) SIZE(2) - ranges = <0x01000000 0x0 0xc4000000 0xc4000000 0x0 0x04000000>, - <0x02000000 0x0 0xc8000000 0xc8000000 0x0 0x18000000>; + ranges = <0x01000000 0x0 0xf0000000 0xf0000000 0x0 0x00010000>, + <0x02000000 0x0 0xf4000000 0xf4000000 0x0 0x08000000>; // PCI_DEVICE(3) INT#(1) CONTROLLER(PHANDLE) CONTROLLER_DATA(2) interrupt-map = < diff --git a/arch/xtensa/include/asm/io.h b/arch/xtensa/include/asm/io.h index da3e783f896b..988e08530a5c 100644 --- a/arch/xtensa/include/asm/io.h +++ b/arch/xtensa/include/asm/io.h @@ -21,6 +21,7 @@ #define IOADDR(x) (XCHAL_KIO_BYPASS_VADDR + (x)) #define IO_SPACE_LIMIT ~0 +#define PCI_IOBASE ((void __iomem *)XCHAL_KIO_BYPASS_VADDR) #ifdef CONFIG_MMU diff --git a/arch/xtensa/include/asm/processor.h b/arch/xtensa/include/asm/processor.h index 19f6b54e358b..7495520d7a3e 100644 --- a/arch/xtensa/include/asm/processor.h +++ b/arch/xtensa/include/asm/processor.h @@ -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. */ -#define USER_PS_VALUE ((1 << PS_WOE_BIT) | \ +#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) \ diff --git a/arch/xtensa/include/asm/regs.h b/arch/xtensa/include/asm/regs.h index 477594e5817f..ce184e7dee91 100644 --- a/arch/xtensa/include/asm/regs.h +++ b/arch/xtensa/include/asm/regs.h @@ -81,6 +81,7 @@ /* PS register fields. */ #define PS_WOE_BIT 18 +#define PS_WOE_MASK 0x00040000 #define PS_CALLINC_SHIFT 16 #define PS_CALLINC_MASK 0x00030000 #define PS_OWB_SHIFT 8 diff --git a/arch/xtensa/kernel/entry.S b/arch/xtensa/kernel/entry.S index 183fa8e0bb5b..9e3676879168 100644 --- a/arch/xtensa/kernel/entry.S +++ b/arch/xtensa/kernel/entry.S @@ -414,7 +414,7 @@ common_exception: movi a3, LOCKLEVEL .Lexception: - movi a0, 1 << PS_WOE_BIT + movi a0, PS_WOE_MASK or a3, a3, a0 #else addi a2, a2, -EXCCAUSE_LEVEL1_INTERRUPT @@ -422,7 +422,7 @@ common_exception: extui a3, a3, PS_INTLEVEL_SHIFT, PS_INTLEVEL_WIDTH # a3 = PS.INTLEVEL moveqz a3, a0, a2 # a3 = LOCKLEVEL iff interrupt - movi a2, 1 << PS_WOE_BIT + movi a2, PS_WOE_MASK or a3, a3, a2 rsr a2, exccause #endif @@ -922,7 +922,7 @@ ENTRY(unrecoverable_exception) wsr a1, windowbase rsync - movi a1, (1 << PS_WOE_BIT) | LOCKLEVEL + movi a1, PS_WOE_MASK | LOCKLEVEL wsr a1, ps rsync @@ -1003,7 +1003,41 @@ 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. * * WARNING: The kernel doesn't save the entire user context before @@ -1359,7 +1393,7 @@ ENTRY(fast_syscall_spill_registers) rsr a3, excsave1 l32i a1, a3, EXC_TABLE_KSTK - movi a4, (1 << PS_WOE_BIT) | LOCKLEVEL + movi a4, PS_WOE_MASK | LOCKLEVEL wsr a4, ps rsync diff --git a/arch/xtensa/kernel/head.S b/arch/xtensa/kernel/head.S index 7f009719304e..4ae998b5a348 100644 --- a/arch/xtensa/kernel/head.S +++ b/arch/xtensa/kernel/head.S @@ -193,7 +193,7 @@ ENTRY(_startup) movi a1, start_info l32i a1, a1, 0 - movi a2, (1 << PS_WOE_BIT) | LOCKLEVEL + movi a2, PS_WOE_MASK | LOCKLEVEL # WOE=1, INTLEVEL=LOCKLEVEL, UM=0 wsr a2, ps # (enable reg-windows; progmode stack) rsync diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index 7c3106093c75..e0e1e1892b86 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -61,7 +61,6 @@ struct screen_info screen_info = { #ifdef CONFIG_BLK_DEV_INITRD extern unsigned long initrd_start; extern unsigned long initrd_end; -int initrd_is_mapped = 0; extern int initrd_below_start_ok; #endif @@ -332,13 +331,11 @@ void __init setup_arch(char **cmdline_p) /* Reserve some memory regions */ #ifdef CONFIG_BLK_DEV_INITRD - if (initrd_start < initrd_end) { - initrd_is_mapped = mem_reserve(__pa(initrd_start), - __pa(initrd_end)) == 0; + if (initrd_start < initrd_end && + !mem_reserve(__pa(initrd_start), __pa(initrd_end))) initrd_below_start_ok = 1; - } else { + else initrd_start = 0; - } #endif mem_reserve(__pa(_stext), __pa(_end)); 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, { 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); 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, 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 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 @@ extern void kernel_exception(void); extern void user_exception(void); +extern void fast_illegal_instruction_user(void); extern void fast_syscall_user(void); extern void fast_alloca(void); extern void fast_unaligned(void); @@ -87,6 +88,9 @@ typedef struct { static dispatch_init_table_t __initdata dispatch_init_table[] = { +#ifdef CONFIG_USER_ABI_CALL0_PROBE +{ EXCCAUSE_ILLEGAL_INSTRUCTION, USER, fast_illegal_instruction_user }, +#endif { EXCCAUSE_ILLEGAL_INSTRUCTION, 0, do_illegal_instruction}, { EXCCAUSE_SYSTEM_CALL, USER, fast_syscall_user }, { EXCCAUSE_SYSTEM_CALL, 0, system_call }, diff --git a/arch/xtensa/mm/init.c b/arch/xtensa/mm/init.c index 79467c749416..d898ed67d890 100644 --- a/arch/xtensa/mm/init.c +++ b/arch/xtensa/mm/init.c @@ -203,16 +203,6 @@ void __init mem_init(void) (unsigned long)(__bss_stop - __bss_start) >> 10); } -#ifdef CONFIG_BLK_DEV_INITRD -extern int initrd_is_mapped; - -void free_initrd_mem(unsigned long start, unsigned long end) -{ - if (initrd_is_mapped) - free_reserved_area((void *)start, (void *)end, -1, "initrd"); -} -#endif - static void __init parse_memmap_one(char *p) { char *oldp;