summaryrefslogtreecommitdiff
path: root/arch/openrisc
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-05-01 21:52:32 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2023-05-01 21:52:32 +0300
commitd75439d64a1e2b35e0f08906205b00279753cbed (patch)
treea1dbe1281ef228f7999cd21ca3380931eacd8dd5 /arch/openrisc
parent3f2a1903af06672f416efd506f029066b9243cbd (diff)
parentc91b4a07655d5ba67962a08dfac8bd7f45ad049c (diff)
downloadlinux-d75439d64a1e2b35e0f08906205b00279753cbed.tar.xz
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.h4
-rw-r--r--arch/openrisc/include/uapi/asm/elf.h3
-rw-r--r--arch/openrisc/include/uapi/asm/ptrace.h4
-rw-r--r--arch/openrisc/include/uapi/asm/sigcontext.h1
-rw-r--r--arch/openrisc/kernel/entry.S31
-rw-r--r--arch/openrisc/kernel/head.S4
-rw-r--r--arch/openrisc/kernel/ptrace.c37
-rw-r--r--arch/openrisc/kernel/setup.c19
-rw-r--r--arch/openrisc/kernel/signal.c2
-rw-r--r--arch/openrisc/kernel/traps.c27
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,
+ &regs->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(&regs->pc, &sc->regs.pc, sizeof(unsigned long));
err |= __copy_from_user(&regs->sr, &sc->regs.sr, sizeof(unsigned long));
+ err |= __copy_from_user(&regs->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, &regs->pc, sizeof(unsigned long));
err |= __copy_to_user(&sc->regs.sr, &regs->sr, sizeof(unsigned long));
+ err |= __copy_to_user(&sc->fpu.fpcsr, &regs->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);