diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-05-01 11:52:32 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-05-01 11:52:32 -0700 |
commit | d75439d64a1e2b35e0f08906205b00279753cbed (patch) | |
tree | a1dbe1281ef228f7999cd21ca3380931eacd8dd5 /arch/openrisc | |
parent | 3f2a1903af06672f416efd506f029066b9243cbd (diff) | |
parent | c91b4a07655d5ba67962a08dfac8bd7f45ad049c (diff) |
Merge tag 'for-linus' of https://github.com/openrisc/linux
Pull OpenRISC updates from Stafford Horne:
"Two things for OpenRISC this cycle:
- Small cleanup for device tree cpu iteration from Rob Herring
- Add support for storing, restoring and accessing user space FPU
state, to allow for libc to support the FPU on OpenRISC"
* tag 'for-linus' of https://github.com/openrisc/linux:
openrisc: Add floating point regset
openrisc: Support floating point user api
openrisc: Support storing and restoring fpu state
openrisc: Properly store r31 to pt_regs on unhandled exceptions
openrisc: Use common of_get_cpu_node() instead of open-coding
Diffstat (limited to 'arch/openrisc')
-rw-r--r-- | arch/openrisc/include/asm/ptrace.h | 4 | ||||
-rw-r--r-- | arch/openrisc/include/uapi/asm/elf.h | 3 | ||||
-rw-r--r-- | arch/openrisc/include/uapi/asm/ptrace.h | 4 | ||||
-rw-r--r-- | arch/openrisc/include/uapi/asm/sigcontext.h | 1 | ||||
-rw-r--r-- | arch/openrisc/kernel/entry.S | 31 | ||||
-rw-r--r-- | arch/openrisc/kernel/head.S | 4 | ||||
-rw-r--r-- | arch/openrisc/kernel/ptrace.c | 37 | ||||
-rw-r--r-- | arch/openrisc/kernel/setup.c | 19 | ||||
-rw-r--r-- | arch/openrisc/kernel/signal.c | 2 | ||||
-rw-r--r-- | arch/openrisc/kernel/traps.c | 27 |
10 files changed, 101 insertions, 31 deletions
diff --git a/arch/openrisc/include/asm/ptrace.h b/arch/openrisc/include/asm/ptrace.h index 01f81d4e97dc..375147ff71fc 100644 --- a/arch/openrisc/include/asm/ptrace.h +++ b/arch/openrisc/include/asm/ptrace.h @@ -59,7 +59,7 @@ struct pt_regs { * -1 for all other exceptions. */ long orig_gpr11; /* For restarting system calls */ - long dummy; /* Cheap alignment fix */ + long fpcsr; /* Floating point control status register. */ long dummy2; /* Cheap alignment fix */ }; @@ -115,6 +115,6 @@ static inline long regs_return_value(struct pt_regs *regs) #define PT_GPR31 124 #define PT_PC 128 #define PT_ORIG_GPR11 132 -#define PT_SYSCALLNO 136 +#define PT_FPCSR 136 #endif /* __ASM_OPENRISC_PTRACE_H */ diff --git a/arch/openrisc/include/uapi/asm/elf.h b/arch/openrisc/include/uapi/asm/elf.h index e892d5061685..6868f81c281e 100644 --- a/arch/openrisc/include/uapi/asm/elf.h +++ b/arch/openrisc/include/uapi/asm/elf.h @@ -53,8 +53,7 @@ typedef unsigned long elf_greg_t; #define ELF_NGREG (sizeof(struct user_regs_struct) / sizeof(elf_greg_t)) typedef elf_greg_t elf_gregset_t[ELF_NGREG]; -/* A placeholder; OR32 does not have fp support yes, so no fp regs for now. */ -typedef unsigned long elf_fpregset_t; +typedef struct __or1k_fpu_state elf_fpregset_t; /* EM_OPENRISC is defined in linux/elf-em.h */ #define EM_OR32 0x8472 diff --git a/arch/openrisc/include/uapi/asm/ptrace.h b/arch/openrisc/include/uapi/asm/ptrace.h index d4fab268f6aa..a77cc9915ca8 100644 --- a/arch/openrisc/include/uapi/asm/ptrace.h +++ b/arch/openrisc/include/uapi/asm/ptrace.h @@ -30,6 +30,10 @@ struct user_regs_struct { unsigned long pc; unsigned long sr; }; + +struct __or1k_fpu_state { + unsigned long fpcsr; +}; #endif diff --git a/arch/openrisc/include/uapi/asm/sigcontext.h b/arch/openrisc/include/uapi/asm/sigcontext.h index 8ab775fc3450..ca585e4af6b8 100644 --- a/arch/openrisc/include/uapi/asm/sigcontext.h +++ b/arch/openrisc/include/uapi/asm/sigcontext.h @@ -28,6 +28,7 @@ struct sigcontext { struct user_regs_struct regs; /* needs to be first */ + struct __or1k_fpu_state fpu; unsigned long oldmask; }; diff --git a/arch/openrisc/kernel/entry.S b/arch/openrisc/kernel/entry.S index 54a87bba35ca..c9f48e750b72 100644 --- a/arch/openrisc/kernel/entry.S +++ b/arch/openrisc/kernel/entry.S @@ -106,6 +106,8 @@ l.mtspr r0,r3,SPR_EPCR_BASE ;\ l.lwz r3,PT_SR(r1) ;\ l.mtspr r0,r3,SPR_ESR_BASE ;\ + l.lwz r3,PT_FPCSR(r1) ;\ + l.mtspr r0,r3,SPR_FPCSR ;\ l.lwz r2,PT_GPR2(r1) ;\ l.lwz r3,PT_GPR3(r1) ;\ l.lwz r4,PT_GPR4(r1) ;\ @@ -173,9 +175,10 @@ handler: ;\ l.sw PT_GPR28(r1),r28 ;\ l.sw PT_GPR29(r1),r29 ;\ /* r30 already save */ ;\ -/* l.sw PT_GPR30(r1),r30*/ ;\ l.sw PT_GPR31(r1),r31 ;\ TRACE_IRQS_OFF_ENTRY ;\ + l.mfspr r30,r0,SPR_FPCSR ;\ + l.sw PT_FPCSR(r1),r30 ;\ /* Store -1 in orig_gpr11 for non-syscall exceptions */ ;\ l.addi r30,r0,-1 ;\ l.sw PT_ORIG_GPR11(r1),r30 @@ -211,12 +214,13 @@ handler: ;\ l.sw PT_GPR27(r1),r27 ;\ l.sw PT_GPR28(r1),r28 ;\ l.sw PT_GPR29(r1),r29 ;\ - /* r31 already saved */ ;\ - l.sw PT_GPR30(r1),r30 ;\ -/* l.sw PT_GPR31(r1),r31 */ ;\ + /* r30 already saved */ ;\ + l.sw PT_GPR31(r1),r31 ;\ /* Store -1 in orig_gpr11 for non-syscall exceptions */ ;\ l.addi r30,r0,-1 ;\ l.sw PT_ORIG_GPR11(r1),r30 ;\ + l.mfspr r30,r0,SPR_FPCSR ;\ + l.sw PT_FPCSR(r1),r30 ;\ l.addi r3,r1,0 ;\ /* r4 is exception EA */ ;\ l.addi r5,r0,vector ;\ @@ -844,9 +848,16 @@ _syscall_badsys: /******* END SYSCALL HANDLING *******/ -/* ---[ 0xd00: Trap exception ]------------------------------------------ */ +/* ---[ 0xd00: Floating Point exception ]-------------------------------- */ -UNHANDLED_EXCEPTION(_vector_0xd00,0xd00) +EXCEPTION_ENTRY(_fpe_trap_handler) + CLEAR_LWA_FLAG(r3) + /* r4: EA of fault (set by EXCEPTION_HANDLE) */ + l.jal do_fpe_trap + l.addi r3,r1,0 /* pt_regs */ + + l.j _ret_from_exception + l.nop /* ---[ 0xe00: Trap exception ]------------------------------------------ */ @@ -1089,6 +1100,10 @@ ENTRY(_switch) l.sw PT_GPR28(r1),r28 l.sw PT_GPR30(r1),r30 + /* Store the old FPU state to new pt_regs */ + l.mfspr r29,r0,SPR_FPCSR + l.sw PT_FPCSR(r1),r29 + l.addi r11,r10,0 /* Save old 'current' to 'last' return value*/ /* We use thread_info->ksp for storing the address of the above @@ -1111,6 +1126,10 @@ ENTRY(_switch) l.lwz r29,PT_SP(r1) l.sw TI_KSP(r10),r29 + /* Restore the old value of FPCSR */ + l.lwz r29,PT_FPCSR(r1) + l.mtspr r0,r29,SPR_FPCSR + /* ...and restore the registers, except r11 because the return value * has already been set above. */ diff --git a/arch/openrisc/kernel/head.S b/arch/openrisc/kernel/head.S index e11699f3d6bd..439e00f81e5d 100644 --- a/arch/openrisc/kernel/head.S +++ b/arch/openrisc/kernel/head.S @@ -424,9 +424,9 @@ _dispatch_do_ipage_fault: .org 0xc00 EXCEPTION_HANDLE(_sys_call_handler) -/* ---[ 0xd00: Trap exception ]------------------------------------------ */ +/* ---[ 0xd00: Floating point exception ]--------------------------------- */ .org 0xd00 - UNHANDLED_EXCEPTION(_vector_0xd00) + EXCEPTION_HANDLE(_fpe_trap_handler) /* ---[ 0xe00: Trap exception ]------------------------------------------ */ .org 0xe00 diff --git a/arch/openrisc/kernel/ptrace.c b/arch/openrisc/kernel/ptrace.c index 85ace93fc251..0b7d2ca6ba3b 100644 --- a/arch/openrisc/kernel/ptrace.c +++ b/arch/openrisc/kernel/ptrace.c @@ -85,10 +85,39 @@ static int genregs_set(struct task_struct *target, } /* + * As OpenRISC shares GPRs and floating point registers we don't need to export + * the floating point registers again. So here we only export the fpcsr special + * purpose register. + */ +static int fpregs_get(struct task_struct *target, + const struct user_regset *regset, + struct membuf to) +{ + const struct pt_regs *regs = task_pt_regs(target); + + return membuf_store(&to, regs->fpcsr); +} + +static int fpregs_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + struct pt_regs *regs = task_pt_regs(target); + int ret; + + /* FPCSR */ + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + ®s->fpcsr, 0, 4); + return ret; +} + +/* * Define the register sets available on OpenRISC under Linux */ enum or1k_regset { REGSET_GENERAL, + REGSET_FPU, }; static const struct user_regset or1k_regsets[] = { @@ -100,6 +129,14 @@ static const struct user_regset or1k_regsets[] = { .regset_get = genregs_get, .set = genregs_set, }, + [REGSET_FPU] = { + .core_note_type = NT_PRFPREG, + .n = sizeof(struct __or1k_fpu_state) / sizeof(long), + .size = sizeof(long), + .align = sizeof(long), + .regset_get = fpregs_get, + .set = fpregs_set, + }, }; static const struct user_regset_view user_or1k_native_view = { diff --git a/arch/openrisc/kernel/setup.c b/arch/openrisc/kernel/setup.c index 0cd04d936a7a..9cf7fb60441f 100644 --- a/arch/openrisc/kernel/setup.c +++ b/arch/openrisc/kernel/setup.c @@ -152,21 +152,6 @@ static void print_cpuinfo(void) printk(KERN_INFO "-- custom unit(s)\n"); } -static struct device_node *setup_find_cpu_node(int cpu) -{ - u32 hwid; - struct device_node *cpun; - - for_each_of_cpu_node(cpun) { - if (of_property_read_u32(cpun, "reg", &hwid)) - continue; - if (hwid == cpu) - return cpun; - } - - return NULL; -} - void __init setup_cpuinfo(void) { struct device_node *cpu; @@ -175,7 +160,7 @@ void __init setup_cpuinfo(void) int cpu_id = smp_processor_id(); struct cpuinfo_or1k *cpuinfo = &cpuinfo_or1k[cpu_id]; - cpu = setup_find_cpu_node(cpu_id); + cpu = of_get_cpu_node(cpu_id, NULL); if (!cpu) panic("Couldn't find CPU%d in device tree...\n", cpu_id); @@ -255,7 +240,7 @@ static inline unsigned long extract_value(unsigned long reg, unsigned long mask) void calibrate_delay(void) { const int *val; - struct device_node *cpu = setup_find_cpu_node(smp_processor_id()); + struct device_node *cpu = of_get_cpu_node(smp_processor_id(), NULL); val = of_get_property(cpu, "clock-frequency", NULL); if (!val) diff --git a/arch/openrisc/kernel/signal.c b/arch/openrisc/kernel/signal.c index 80f69740c731..4664a18f0787 100644 --- a/arch/openrisc/kernel/signal.c +++ b/arch/openrisc/kernel/signal.c @@ -50,6 +50,7 @@ static int restore_sigcontext(struct pt_regs *regs, err |= __copy_from_user(regs, sc->regs.gpr, 32 * sizeof(unsigned long)); err |= __copy_from_user(®s->pc, &sc->regs.pc, sizeof(unsigned long)); err |= __copy_from_user(®s->sr, &sc->regs.sr, sizeof(unsigned long)); + err |= __copy_from_user(®s->fpcsr, &sc->fpu.fpcsr, sizeof(unsigned long)); /* make sure the SM-bit is cleared so user-mode cannot fool us */ regs->sr &= ~SPR_SR_SM; @@ -112,6 +113,7 @@ static int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) err |= __copy_to_user(sc->regs.gpr, regs, 32 * sizeof(unsigned long)); err |= __copy_to_user(&sc->regs.pc, ®s->pc, sizeof(unsigned long)); err |= __copy_to_user(&sc->regs.sr, ®s->sr, sizeof(unsigned long)); + err |= __copy_to_user(&sc->fpu.fpcsr, ®s->fpcsr, sizeof(unsigned long)); return err; } diff --git a/arch/openrisc/kernel/traps.c b/arch/openrisc/kernel/traps.c index fd9a0f2b66c4..0aa6b07efda1 100644 --- a/arch/openrisc/kernel/traps.c +++ b/arch/openrisc/kernel/traps.c @@ -75,8 +75,9 @@ void show_registers(struct pt_regs *regs) in_kernel = 0; printk("CPU #: %d\n" - " PC: %08lx SR: %08lx SP: %08lx\n", - smp_processor_id(), regs->pc, regs->sr, regs->sp); + " PC: %08lx SR: %08lx SP: %08lx FPCSR: %08lx\n", + smp_processor_id(), regs->pc, regs->sr, regs->sp, + regs->fpcsr); printk("GPR00: %08lx GPR01: %08lx GPR02: %08lx GPR03: %08lx\n", 0L, regs->gpr[1], regs->gpr[2], regs->gpr[3]); printk("GPR04: %08lx GPR05: %08lx GPR06: %08lx GPR07: %08lx\n", @@ -242,6 +243,28 @@ asmlinkage void unhandled_exception(struct pt_regs *regs, int ea, int vector) die("Oops", regs, 9); } +asmlinkage void do_fpe_trap(struct pt_regs *regs, unsigned long address) +{ + int code = FPE_FLTUNK; + unsigned long fpcsr = regs->fpcsr; + + if (fpcsr & SPR_FPCSR_IVF) + code = FPE_FLTINV; + else if (fpcsr & SPR_FPCSR_OVF) + code = FPE_FLTOVF; + else if (fpcsr & SPR_FPCSR_UNF) + code = FPE_FLTUND; + else if (fpcsr & SPR_FPCSR_DZF) + code = FPE_FLTDIV; + else if (fpcsr & SPR_FPCSR_IXF) + code = FPE_FLTRES; + + /* Clear all flags */ + regs->fpcsr &= ~SPR_FPCSR_ALLF; + + force_sig_fault(SIGFPE, code, (void __user *)regs->pc); +} + asmlinkage void do_trap(struct pt_regs *regs, unsigned long address) { force_sig_fault(SIGTRAP, TRAP_BRKPT, (void __user *)regs->pc); |