tiny68k: adapt C code to proposed new exception logic
authorAlan Cox <alan@linux.intel.com>
Sun, 20 Jan 2019 23:31:16 +0000 (23:31 +0000)
committerAlan Cox <alan@linux.intel.com>
Sun, 20 Jan 2019 23:31:16 +0000 (23:31 +0000)
Kernel/platform-tiny68k/main.c

index e91de50..2aec656 100644 (file)
@@ -98,43 +98,200 @@ void install_vdso(void)
 extern void *get_usp(void);
 extern void set_usp(void *p);
 
-void signal_frame(uint8_t *trapframe, uint32_t d0, uint32_t d1, uint32_t a0,
-       uint32_t a1)
+/* We do as much exception processing in software as we can. This code
+  is called from the asm trap hooks on the supervisor stack and expected
+  to sort the mess out */
+
+/* The stackframe we work with looks like this when exception is called */
+
+struct trapdata {
+       /* MOVEM.L with predecrement stores A7 to A0 then D7 to D0. In other
+          words viewed in memory order it is D0-D7 then A0-A7 (A6 for us
+          as we don't store A7) */
+       uint32_t d[8];
+       uint32_t a[7];
+       uint16_t trap;          /* Pushed second */
+       uint16_t sr;            /* Condition codes */
+       /* The CPU pushed the frame which sits above the MOVEM */
+       uint16_t exception[0];  /* CPU exception frame */
+};
+
+#define FRAME_A                1
+#define FRAME_B                2
+
+static void explode(struct trapdata *framedata, int type)
+{
+       uint16_t *excp = framedata->exception;
+       uint32_t *fv;
+       int i;
+
+       kprintf("Trap: %d\n", framedata->trap);
+       kprintf("Register Dump\nA: ");
+       for (i = 0; i < 8; i++)
+               kprintf("%p ", framedata->a[i]);
+       kprintf("\nD: ");
+       for (i = 0; i < 8; i++)
+               kprintf("%p ", framedata->d[i]);
+       kprintf("\nUSP: %p   SR: %x\n", get_usp(), framedata->sr);
+
+       /* For now we only do 68000 */
+       if (type == FRAME_A) {
+               kputs((excp[0] & 0x10)?"R":"W");
+               kprintf(" FC %x\n", excp[0] & 7);
+               fv = (uint32_t *)(excp + 1);
+               kprintf(" Addr %p IR %x", *fv, excp[3]);
+               excp += 4;
+       }
+       if (type == FRAME_A || type == FRAME_B) {
+               fv = (uint32_t *)(excp + 1);
+               kprintf("PC %p SR %x\n", *fv, excp[1]);
+       }
+}
+
+/* Our caller did a movem of the registers to kstack then pushed an
+   exception and other info. This all sits on the kstack but pass
+   a pointer to it for flexibility.
+
+   On return it will pull the state back and rte. We can thus adjust
+   what happens according to need.
+
+   To keep common code we push the same frame for all cases */
+
+static uint8_t pushw(uint16_t **usp, uint16_t v)
 {
-       extern void *udata_shadow;
-       uint8_t *usp = get_usp();
-       udata_ptr = udata_shadow;
-       uint16_t ccr = *(uint16_t *)trapframe;
-       uint32_t addr = *(uint32_t *)(trapframe + 2);
-       int err = 0;
-
-       /* Build the user stack frame */
-
-       /* FIXME: eventually we should put the trap frame details and trap
-          info into the frame */
-       usp -= 4;
-       err |= uputl(addr, usp);
-       usp -= 4;
-       err |= uputw(ccr, usp);
-       usp -= 2;
-       err |=uputl(a1, usp);
-       usp -= 4;
-       err |= uputl(a0, usp);
-       usp -= 4;
-       err |= uputl(d1, usp);
-       usp -= 4;
-       err |= uputl(d0, usp);
-       usp -= 4;
-       err |= uputl(udata.u_codebase + 4, usp);
+       (*usp)--;
+       return uputw(v, *usp);
+}
+
+static uint8_t pushl(uint16_t **usp, uint32_t v)
+{
+       (*usp) -= 2;
+       return uputl(v, *usp);
+}
+
+extern uint8_t kernel_flag;
+
+int exception(struct trapdata *framedata)
+{
+       ptptr proc;
+       unsigned int frame, fsize;
+       uint8_t trap = framedata->trap;
+       unsigned int sig = 0;
+       uint16_t m;
+       uint16_t *sp = (uint16_t *)framedata;
+       uint16_t *usp = get_usp();
+       uint16_t *unwind, *context;
+       uint8_t err = 0;
+       int i;
+
+       /* We translate debug to SIGIOT and A and F line to SIGILL
+          We will need to fix up 68000 v 68010 move to/from SR */
+       static const uint8_t trap_to_sig[] = {
+               0, 0, SIGSEGV, SIGBUS, SIGILL, SIGFPE,
+               SIGABRT/*CHK*/, SIGTRAP/*TRAPV */,
+               SIGILL, SIGIOT, SIGILL, SIGILL };
+
+       proc = udata.u_ptab;
+
+       /* Most synchronous exceptions are type B */
+       /* FIXME: sizes need to become chip aware */
+       frame = FRAME_B;
+       fsize = 3;              /* Three words on 68000 */
+       /* TODO: On the 68010 there are at least 4 words as word 4 always holds
+          the vector and format */
+       if (trap < 4) {
+               /* TODO: On a 68010 this frame is 29 words and the event is
+                  restartable (although not always usefully). We need to
+                  decide whether to set the restart flag case by case */
+               frame = FRAME_A;
+               fsize = 7;
+       }
+       if (trap < 12)
+               sig = trap_to_sig[sig];
+       else if (trap >= 32 && trap < 48)
+               sig = SIGTRAP;
+       else if (trap == 0) {
+               sig = udata.u_cursig;
+               udata.u_cursig = 0;
+       }
+
+       /* This processing only applies to synchronous hardware exceptions */
+       if (trap) {
+               /* Went boom in kernel space or without a user recovery */
+               if (kernel_flag || sig == 0) {
+                       explode(framedata, frame);
+                       panic("trap");
+               }
+
+               /* Cheating here .. all our exceptions are low 16 signal */
+               m = 1 << sig;
+               /*
+                *      The caller is ignoring our signal. In some cases this is fine
+                *      but in others it's less clear (eg division by zero) and we
+                *      may need to take different action.
+                */
+               if (proc->p_sig[0].s_ignored & m)
+                       return 0;
+               /* Weird case - we took a sync signal and the caller wants us to
+                  report it later. */
+               if (proc->p_sig[0].s_held & m) {
+                       /* TODO: if it's not meaningfully restartable we should
+                          probably treat this as a kill */
+                       ssig(proc, sig);
+                       return 0;
+               }
+               recalc_cursig();        /* Put any async signal back */
+       }
+       if (udata.u_sigvec[sig] == SIG_DFL) {
+               /* Default action for our signal ? */
+               doexit(dump_core(sig));
+               /* This will never return. We will go schedule new work */
+               panic("exret");
+       }
+       /* build signal frame
+
+               Our unwinder code does
+               move.l 8(sp),sp
+               movem.l a0-a1/d0-d1,(sp)+
+               move.w (sp)+,ccr
+               rts */
+
+       /* Push the recovery PC */
+       err |= pushw(&usp, sp[31 + fsize]);
+       err |= pushw(&usp, sp[30 + fsize]);
+       /* FIXME: when we do ptrace we will need to support adding the T
+          flag back here as the exception cleared it */
+       err |= pushw(&usp,  framedata->sr);
+       /* Push A1 A0 D1 D0 to match MOVEM.L */
+       err |= pushl(&usp, framedata->a[1]);
+       err |= pushl(&usp, framedata->a[0]);
+       err |= pushl(&usp, framedata->d[1]);
+       err |= pushl(&usp, framedata->d[0]);
+
+       /* Remember the target for undoing the frame */
+       unwind = get_usp();
+
+       /* Copy in the signal context itself. 30 words of registers, 2 of
+          trap code and then the hardware exception */
+       for (i = 0; i < 30 + 2 + fsize; i++)
+               err |= pushw(&usp, *sp++);
+       context = usp;
+       err |= pushl(&usp, (uint32_t)unwind);
+       /* Signal context is a secret extra non portable argument */
+       err |= pushl(&usp, (uint32_t)context);
+       /* We end it with the call frame as seen from the signal handler, a
+          single argument and a return address */
+       err |= pushl(&usp, sig);
+       err |= pushl(&usp, udata.u_codebase + 0x04);
+
        set_usp(usp);
 
        if (err) {
-               kprintf("%d: stack fault\n", udata.u_ptab->p_pid);
-               doexit(SIGKILL/* FIXME dump_core(SIGKILL)*/);
+               doexit(dump_core(SIGSTKFLT));
+               panic("exret2");
        }
-       /* Now patch up the kernel frame */
-       *(uint16_t *)trapframe = 0;
-       *(uint32_t *)(trapframe + 2) = (uint32_t)udata.u_sigvec[udata.u_cursig];
-       udata.u_sigvec[udata.u_cursig] = SIG_DFL;
-       udata.u_cursig = 0;
+       /* Once built clear the restart state */
+       udata.u_sigvec[sig] = SIG_DFL;
+       /* Return, RTE and end up on the signal frame */
+       return 1;
 }