m68k v4.4 backport fixes
Subsystems (3)
v3 cover m68k v4.4 backport fixes
IP v4.4 backport patches for m68k. Basically everything needed to make v4.4 work on my Falcon. This includes my iounmap() fix, Al Viro's signal handling and uaccess page fault fixes, my 030 buserr_c fix for exception handling during page faults, and Finn's 030 signal frame fix.
Patch 1 should be applied to v4.4-cip, or else ioremap / iounmap has no chance to work on 030 processors.
Patches 2 and 3 have been applied to v4.4-cip.
Patches 4-6 are a prerequisite for patch 8, Finn Thain's 'm68k:
Move signal frame following exception on 68020/030' patch which had been applied but failed to compile (see error reported in
https://lore.kernel.org/oe-kbuild-all/[email protected]/
which has gone away with patches 4-6 in place).
Patch 7 is another page fault handling fix which should be applied (though I have found no test case for it, and cannot say how common that livelock issue is on m68k).
Patches 1,3,4,5,6 and 7 are sent to cip-dev - note patch 3 has already been applied!
Tested on my 68030 Atari Falcon.
Cheers,
Michael
PATCH v3 1/8 m68k/mm: Adjust VM area to be unmapped by gap size for __iounmap()
commit 3f90f9ef2dda316d64e420d5d51ba369587ccc55 upstream.
If 020/030 support is enabled, get_io_area() leaves an IO_SIZE gap between mappings which is added to the vm_struct representing the mapping. __ioremap() uses the actual requested size (after alignment), while __iounmap() is passed the size from the vm_struct.
On 020/030, early termination descriptors are used to set up mappings of extent 'size', which are validated on unmapping. The unmapped gap of size IO_SIZE defeats the sanity check of the pmd tables, causing __iounmap() to loop forever on 030.
On 040/060, unmapping of page table entries does not check for a valid mapping, so the umapping loop always completes there.
Adjust size to be unmapped by the gap that had been added in the vm_struct prior.
This fixes the hang in atari_platform_init() reported a long time ago, and a similar one reported by Finn recently (addressed by removing ioremap() use from the SWIM driver.
Tested on my Falcon in 030 mode - untested but should work the same on 040/060 (the extra page tables cleared there would never have been set up anyway).
--- arch/m68k/mm/kmap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
arch/m68k/mm/kmap.c +2 -1
@@ -88,7 +88,8 @@ static inline void free_io_area(void *addr)for (p = &iolist ; (tmp = *p) ; p = &tmp->next) {if (tmp->addr == addr) { *p = tmp->next; -__iounmap (tmp->addr, tmp->size); +/* remove gap added in get_io_area() */ +__iounmap (tmp->addr, tmp->size -IO_SIZE );kfree (tmp);return ; }
Signature · 1 line
2.17.1
PATCH v3 2/8 m68k: Only force 030 bus error if PC not in exception table
In reply to Michael Schmitzcommit 513138a14063760ef611f18ba76c5c7915ff8cdd upstream.
__get_kernel_nofault() does copy data in supervisor mode when forcing a task backtrace log through /proc/sysrq_trigger. This is expected cause a bus error exception on e.g. NULL pointer dereferencing when logging a kernel task has no workqueue associated. This bus error ought to be ignored.
Our 030 bus error handler is ill equipped to deal with this:
Whenever ssw indicates a kernel mode access on a data fault, we don't even attempt to handle the fault and instead always send a SEGV signal (or panic). As a result, the check for exception handling at the fault PC (buried in send_sig_fault() which gets called from do_page_fault() eventually) is never used.
In contrast, both 040 and 060 access error handlers do not care whether a fault happened on supervisor mode access, and will call do_page_fault() on those, ultimately honoring the exception table.
Add a check in bus_error030 to call do_page_fault() in case we do have an entry for the fault PC in our exception table.
I had attempted a fix for this earlier in 2019 that did rely on testing pagefault_disabled() (see link below) to achieve the same thing, but this patch should be more generic.
Tested on 030 Atari Falcon.
--- arch/m68k/kernel/traps.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
arch/m68k/kernel/traps.c +3 -1
@@ -29,6 +29,7 @@#include <linux/init.h> #include <linux/ptrace.h> #include <linux/kallsyms.h> +#include <linux/extable.h> #include <asm/setup.h> #include <asm/fpu.h>
@@ -549,7 +550,8 @@ static inline void bus_error030 (struct frame *fp) errorcode |=2 ;if (mmusr & (MMU_I |MMU_WP )) { -if (ssw &4 ) { +/* We might have an exception table for this PC */ +if (ssw &4 && !search_exception_tables (fp->ptregs.pc)) { pr_err("Data %s fault at %#010lx in %s (pc=%#lx)\n", ssw & RW ? "read" : "write", fp->un.fmtb.daddr,
Signature · 1 line
2.17.1
PATCH v3 3/8 m68k: include module.h to make use of exception handling in traps.c
In reply to Michael SchmitzBackporting commit 513138a14063760e (m68k: Only force 030 bus error if PC not in exception table) to v4.4 by the CIP project revealed a build error due to extable.h not yet available.
extable.h was split off module.h in v4.8 (commit 0ef7653797addea8) so amend 513138a14063760e accordingly.
--- arch/m68k/kernel/traps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/m68k/kernel/traps.c +1 -1
@@ -29,7 +29,7 @@#include <linux/init.h> #include <linux/ptrace.h> #include <linux/kallsyms.h> -#include <linux/extable.h> +#include <linux/module.h> #include <asm/setup.h> #include <asm/fpu.h>
Signature · 1 line
2.17.1
PATCH v3 4/8 m68k: Handle arrivals of multiple signals correctly
In reply to Michael SchmitzFrom: Al Viro <[email protected]>
commit 4bb0bd81ce5e97092dfda6a106d414b703ec0ee8 upstream.
When we have several pending signals, have entered with the kernel with large exception frame *and* have already built at least one sigframe, regs->stkadj is going to be non-zero and regs->format/sr/pc are going to be junk - the real values are in shifted exception stack frame we'd built when putting together the first sigframe.
If that happens, subsequent sigframes are going to be garbage. Not hard to fix - just need to find the "adjusted" frame first and look for format/vector/sr/pc in it.
--- arch/m68k/kernel/signal.c | 100 +++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 55 deletions(-)
arch/m68k/kernel/signal.c +45 -55
@@ -465,7 +465,7 @@ static inline void save_fpu_state(struct sigcontext *sc, struct pt_regs *regs)if (CPU_IS_060 ? sc->sc_fpstate[2 ] : sc->sc_fpstate[0 ]) { fpu_version = sc->sc_fpstate[0 ]; -if (CPU_IS_020_OR_030 && +if (CPU_IS_020_OR_030 && !regs->stkadj && regs->vector >= (VEC_FPBRUC *4 ) && regs->vector <= (VEC_FPNAN *4 )) {/* Clear pending exception in 68882 idle frame */
@@ -528,7 +528,7 @@ static inline int rt_save_fpu_state(struct ucontext __user *uc, struct pt_regs *if (!(CPU_IS_060 ||CPU_IS_COLDFIRE )) context_size = fpstate[1 ]; fpu_version = fpstate[0 ]; -if (CPU_IS_020_OR_030 && +if (CPU_IS_020_OR_030 && !regs->stkadj && regs->vector >= (VEC_FPBRUC *4 ) && regs->vector <= (VEC_FPNAN *4 )) {/* Clear pending exception in 68882 idle frame */
@@ -788,18 +788,24 @@ badframe:return 0 ; } +static inline struct pt_regs *rte_regs (struct pt_regs *regs) +{ +return (void *)regs + regs->stkadj; +} +static void setup_sigcontext (struct sigcontext *sc,struct pt_regs *regs,unsigned long mask) { +struct pt_regs *tregs =rte_regs (regs); sc->sc_mask = mask; sc->sc_usp =rdusp (); sc->sc_d0 = regs->d0; sc->sc_d1 = regs->d1; sc->sc_a0 = regs->a0; sc->sc_a1 = regs->a1; - sc->sc_sr = regs->sr; - sc->sc_pc = regs->pc; - sc->sc_formatvec = regs->format <<12 | regs->vector; + sc->sc_sr = tregs->sr; + sc->sc_pc = tregs->pc; + sc->sc_formatvec = tregs->format <<12 | tregs->vector;save_a5_state (sc, regs);save_fpu_state (sc, regs); }
@@ -807,6 +813,7 @@ static void setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs,static inline int rt_setup_ucontext (struct ucontext __user *uc,struct pt_regs *regs) {struct switch_stack *sw = (struct switch_stack *)regs -1 ; +struct pt_regs *tregs =rte_regs (regs);greg_t __user *gregs = uc->uc_mcontext.gregs;int err =0 ;
@@ -827,9 +834,9 @@ static inline int rt_setup_ucontext(struct ucontext __user *uc, struct pt_regs * err |=__put_user (sw->a5, &gregs[13 ]); err |=__put_user (sw->a6, &gregs[14 ]); err |=__put_user (rdusp (), &gregs[15 ]); - err |=__put_user (regs->pc, &gregs[16 ]); - err |=__put_user (regs->sr, &gregs[17 ]); - err |=__put_user ((regs->format <<12 ) | regs->vector, &uc->uc_formatvec); + err |=__put_user (tregs->pc, &gregs[16 ]); + err |=__put_user (tregs->sr, &gregs[17 ]); + err |=__put_user ((tregs->format <<12 ) | tregs->vector, &uc->uc_formatvec); err |=rt_save_fpu_state (uc, regs);return err; }
@@ -846,15 +853,14 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) {struct sigframe __user *frame; -int fsize =frame_extra_sizes (regs->format); +struct pt_regs *tregs =rte_regs (regs); +int fsize =frame_extra_sizes (tregs->format);struct sigcontext context;int err =0 , sig = ksig->sig;if (fsize <0 ) { -#ifdef DEBUG -printk ("setup_frame: Unknown frame format %#x\n" , - regs->format); -#endif +pr_debug ("setup_frame: Unknown frame format %#x\n" , + tregs->format);return -EFAULT ; }
@@ -865,7 +871,7 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set, err |=__put_user (sig, &frame->sig); - err |=__put_user (regs->vector, &frame->code); + err |=__put_user (tregs->vector, &frame->code); err |=__put_user (&frame->sc, &frame->psc);if (_NSIG_WORDS >1 )
@@ -890,36 +896,28 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set,push_cache ((unsigned long ) &frame->retcode); -/* -* Set up registers for signal handler. All the state we are about -* to destroy is successfully copied to sigframe. -*/ -wrusp ((unsigned long ) frame); - regs->pc = (unsigned long ) ksig->ka.sa.sa_handler; -adjustformat (regs); -/* * This is subtle; if we build more than one sigframe, all but the * first one will see frame format 0 and have fsize == 0, so we won't * screw stkadj. */ -if (fsize) +if (fsize) { regs->stkadj = fsize; - -/* Prepare to skip over the extra stuff in the exception frame. */ -if (regs->stkadj) { -struct pt_regs *tregs = - (struct pt_regs *)((ulong )regs + regs->stkadj); -#ifdef DEBUG -printk ("Performing stackadjust=%04x\n" , regs->stkadj); -#endif -/* This must be copied with decreasing addresses to -handle overlaps. */ + tregs =rte_regs (regs); +pr_debug ("Performing stackadjust=%04lx\n" , regs->stkadj); tregs->vector =0 ; tregs->format =0 ; - tregs->pc = regs->pc; tregs->sr = regs->sr; } + +/* +* Set up registers for signal handler. All the state we are about +* to destroy is successfully copied to sigframe. +*/ +wrusp ((unsigned long ) frame); + tregs->pc = (unsigned long ) ksig->ka.sa.sa_handler; +adjustformat (regs); +return 0 ; }
@@ -927,7 +925,8 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) {struct rt_sigframe __user *frame; -int fsize =frame_extra_sizes (regs->format); +struct pt_regs *tregs =rte_regs (regs); +int fsize =frame_extra_sizes (tregs->format);int err =0 , sig = ksig->sig;if (fsize <0 ) {
@@ -978,36 +977,27 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,push_cache ((unsigned long ) &frame->retcode); -/* -* Set up registers for signal handler. All the state we are about -* to destroy is successfully copied to sigframe. -*/ -wrusp ((unsigned long ) frame); - regs->pc = (unsigned long ) ksig->ka.sa.sa_handler; -adjustformat (regs); -/* * This is subtle; if we build more than one sigframe, all but the * first one will see frame format 0 and have fsize == 0, so we won't * screw stkadj. */ -if (fsize) +if (fsize) { regs->stkadj = fsize; - -/* Prepare to skip over the extra stuff in the exception frame. */ -if (regs->stkadj) { -struct pt_regs *tregs = - (struct pt_regs *)((ulong )regs + regs->stkadj); -#ifdef DEBUG -printk ("Performing stackadjust=%04x\n" , regs->stkadj); -#endif -/* This must be copied with decreasing addresses to -handle overlaps. */ + tregs =rte_regs (regs); +pr_debug ("Performing stackadjust=%04lx\n" , regs->stkadj); tregs->vector =0 ; tregs->format =0 ; - tregs->pc = regs->pc; tregs->sr = regs->sr; } + +/* +* Set up registers for signal handler. All the state we are about +* to destroy is successfully copied to sigframe. +*/ +wrusp ((unsigned long ) frame); + tregs->pc = (unsigned long ) ksig->ka.sa.sa_handler; +adjustformat (regs);return 0 ; }
Signature · 1 line
2.17.1
PATCH v3 5/8 m68k: Update ->thread.esp0 before calling syscall_trace() in ret_from_signal
In reply to Michael SchmitzFrom: Al Viro <[email protected]>
commit 50e43a57334400668952f8e551c9d87d3ed2dfef upstream.
We get there when sigreturn has performed obscene acts on kernel stack; in particular, the location of pt_regs has shifted. We are about to call syscall_trace(), which might stop for tracer. If that happens, we'd better have task_pt_regs() returning correct result...
--- arch/m68k/kernel/entry.S | 2 ++ 1 file changed, 2 insertions(+)
arch/m68k/kernel/entry.S +2 -0
@@ -173,6 +173,8 @@ ENTRY(ret_from_signal) movel %curptr@(TASK_STACK),%a1 tstb %a1@(TINFO_FLAGS+2) jge 1f + lea %sp@(SWITCH_STACK_SIZE),%a1 + movel %a1,%curptr@(TASK_THREAD+THREAD_ESP0) jbsr syscall_trace 1: RESTORE_SWITCH_STACK addql #4,%sp
Signature · 1 line
2.17.1
PATCH v3 6/8 m68k: Leave stack mangling to asm wrapper of sigreturn()
In reply to Michael SchmitzFrom: Al Viro <[email protected]>
commit 0d20abde987bed05a8963c8aa4276019d54ff9e7 upstream.
sigreturn has to deal with an unpleasant problem - exception stack frames have different sizes, depending upon the exception (and processor model, as well) and variable-sized part of exception frame may contain information needed for instruction restart. So when signal handler terminates and calls sigreturn to resume the execution at the place where we'd been when we caught the signal, it has to rearrange the frame at the bottom of kernel stack. Worse, it might need to open a gap in the kernel stack, shifting pt_regs towards lower addresses.
Doing that from C is insane - we'd need to shift stack frames (return addresses, local variables, etc.) of C call chain, right under the nose of compiler and hope it won't fall apart horribly. What had been actually done is only slightly less insane - an inline asm in mangle_kernel_stack() moved the stuff around, then reset stack pointer and jumped to label in asm glue.
However, we can avoid all that mess if the asm wrapper we have to use anyway would reserve some space on the stack between switch_stack and the C stack
frame of do_{rt_,}sigreturn(). Then C part can simply memmove() pt_regs +switch_stack, memcpy() the variable part of exception frame into the opened gap - all of that without inline asm, buggering C call chain, magical jumps to asm labels, etc.
Asm wrapper would need to know where the moved switch_stack has ended up - it might have been shifted into the gap we'd reserved before do_rt_sigreturn() call. That's where it needs to set the stack pointer to. So let the C part return just that and be done with that.
While we are at it, the call of berr_040cleanup() we need to do when returning via 68040 bus error exception frame can be moved into C part as well.
--- arch/m68k/68000/entry.S | 3 - arch/m68k/coldfire/entry.S | 3 - arch/m68k/include/asm/traps.h | 4 ++ arch/m68k/kernel/entry.S | 49 +++++++------- arch/m68k/kernel/signal.c | 118 ++++++++++++---------------------- 5 files changed, 72 insertions(+), 105 deletions(-)
arch/m68k/68000/entry.S +0 -3
@@ -25,7 +25,6 @@ .globl system_call .globl resume .globl ret_from_exception -.globl ret_from_signal .globl sys_call_table .globl bad_interrupt .globl inthandler1
@@ -59,8 +58,6 @@ do_trace: subql #4,%sp /* dummy return address */ SAVE_SWITCH_STACK jbsr syscall_trace_leave - -ret_from_signal: RESTORE_SWITCH_STACK addql #4,%sp jra ret_from_exception
arch/m68k/coldfire/entry.S +0 -3
@@ -51,7 +51,6 @@ sw_usp: .globl system_call .globl resume .globl ret_from_exception -.globl ret_from_signal .globl sys_call_table .globl inthandler
@@ -98,8 +97,6 @@ ENTRY(system_call) subql #4,%sp /* dummy return address */ SAVE_SWITCH_STACK jbsr syscall_trace_leave - -ret_from_signal: RESTORE_SWITCH_STACK addql #4,%sp
arch/m68k/include/asm/traps.h +4 -0
@@ -267,6 +267,10 @@ struct frame { } un; }; +#ifdef CONFIG_M68040 +asmlinkage void berr_040cleanup(struct frame *fp); +#endif +#endif /* __ASSEMBLY__ */ #endif /* _M68K_TRAPS_H */
arch/m68k/kernel/entry.S +26 -23
67 lines not on this page · raw message
arch/m68k/kernel/signal.c +42 -76
194 lines not on this page · raw message
Signature · 1 line
2.17.1
PATCH v3 7/8 m68k: fix livelock in uaccess
In reply to Michael SchmitzFrom: Al Viro <[email protected]>
commit bd75497a77ccae9a5e1b0f1a3fa8283d67b21f4c upstream.
m68k equivalent of 26178ec11ef3 "x86: mm: consolidate VM_FAULT_RETRY handling" If e.g. get_user() triggers a page fault and a fatal signal is caught, we might end up with handle_mm_fault() returning VM_FAULT_RETRY and not doing anything to page tables. In such case we must *not* return to the faulting insn - that would repeat the entire thing without making any progress; what we need instead is to treat that as failed (user) memory access.
--- arch/m68k/mm/fault.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
arch/m68k/mm/fault.c +4 -1
@@ -139,8 +139,11 @@ good_area: fault =handle_mm_fault (mm, vma, address, flags);pr_debug ("handle_mm_fault returns %d\n" , fault); -if ((fault &VM_FAULT_RETRY ) &&fatal_signal_pending (current)) +if ((fault &VM_FAULT_RETRY ) &&fatal_signal_pending (current)) { +if (!user_mode (regs)) + goto no_context;return 0 ; + }if (unlikely (fault &VM_FAULT_ERROR )) {if (fault &VM_FAULT_OOM )
Signature · 1 line
2.17.1
PATCH v3 8/8 m68k: Move signal frame following exception on 68020/030
In reply to Michael SchmitzFrom: Finn Thain <[email protected]>
commit b845b574f86dcb6a70dfa698aa87a237b0878d2a upstream.
On 68030/020, an instruction such as, moveml %a2-%a3/%a5,%sp@- may cause a stack page fault during instruction execution (i.e. not at an instruction boundary) and produce a format 0xB exception frame.
In this situation, the value of USP will be unreliable. If a signal is to be delivered following the exception, this USP value is used to calculate the location for a signal frame. This can result in a corrupted user stack.
The corruption was detected in dash (actually in glibc) where it showed up as an intermittent "stack smashing detected" message and crash following signal delivery for SIGCHLD.
It was hard to reproduce that failure because delivery of the signal raced with the page fault and because the kernel places an unpredictable gap of up to 7 bytes between the USP and the signal frame.
A format 0xB exception frame can be produced by a bus error or an address error. The 68030 Users Manual says that address errors occur immediately upon detection during instruction prefetch. The instruction pipeline allows prefetch to overlap with other instructions, which means an address error can arise during the execution of a different instruction. So it seems likely that this patch may help in the address error case also.
--- arch/m68k/kernel/signal.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
arch/m68k/kernel/signal.c +10 -4
@@ -808,11 +808,17 @@ static inline int rt_setup_ucontext(struct ucontext __user *uc, struct pt_regs * }static inline void __user * -get_sigframe (struct ksignal *ksig,size_t frame_size) +get_sigframe (struct ksignal *ksig,struct pt_regs *tregs,size_t frame_size) {unsigned long usp =sigsp (rdusp (), ksig); +unsigned long gap =0 ; -return (void __user *)((usp - frame_size) &-8UL ); +if (CPU_IS_020_OR_030 && tregs->format ==0xb ) { +/* USP is unreliable so use worst-case value */ + gap =256 ; + } + +return (void __user *)((usp - gap - frame_size) &-8UL ); } static int setup_frame(struct ksignal *ksig, sigset_t *set,
@@ -830,7 +836,7 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set,return -EFAULT ; } - frame =get_sigframe (ksig,sizeof (*frame) + fsize); + frame =get_sigframe (ksig, tregs,sizeof (*frame) + fsize);if (fsize) err |=copy_to_user (frame +1 , regs +1 , fsize);
@@ -903,7 +909,7 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,return -EFAULT ; } - frame =get_sigframe (ksig,sizeof (*frame)); + frame =get_sigframe (ksig, tregs,sizeof (*frame));if (fsize) err |=copy_to_user (&frame->uc.uc_extra, regs +1 , fsize);
Signature · 1 line
2.17.1
Hi Michael,
Quoted 5 lines · Michael Schmitz
IP v4.4 backport patches for m68k. Basically everything needed to make v4.4 work on my Falcon. This includes my
iounmap()fix, Al Viro's signal handling and uaccess page fault fixes, my 030buserr_cfix for exception handling during page faults, and Finn's 030 signal frame fix.
Correct me if I'm wrong, but 4.4 is no longer maintained, is it?
It seems that the last LTS release for 4.4 was 4.4.302 [1].
Adrian
Signature · 4 lines
.''`. John Paul Adrian Glaubitz : :' : Debian Developer `. `' Physicist `- GPG: 62FF 8A75 84E0 2956 9546 0006 7426 3B37 F5B5 F913
Hi Adrian,
Quoted 11 lines · John Paul Adrian Glaubitz
Hi Michael,
Quoted 5 lines · Michael Schmitz
IP v4.4 backport patches for m68k. Basically everything needed to make v4.4 work on my Falcon. This includes my
iounmap()fix, Al Viro's signal handling and uaccess page fault fixes, my 030buserr_cfix for exception handling during page faults, and Finn's 030 signal frame fix.Correct me if I'm wrong, but 4.4 is no longer maintained, is it?
It seems that the last LTS release for 4.4 was 4.4.302 [1].
That is correct. You will note that none of my patches did contain a Cc: [email protected] tag.
This is about CIP releases - see https://wiki.linuxfoundation.org/civilinfrastructureplatform/start for more information. EOL for the CIP 4.4 kernel is projected to be 01/2027. That's still some way off.
Cheers,
Michael
Quoted 4 lines
Adrian