68000: clean up most of the 16bitisms and sign stuff
authorAlan Cox <alan@linux.intel.com>
Fri, 30 Jan 2015 00:30:57 +0000 (00:30 +0000)
committerAlan Cox <alan@linux.intel.com>
Fri, 30 Jan 2015 00:30:57 +0000 (00:30 +0000)
Mostly this just tidies up all the introduced types. Various things are made
explicitly unsigned that were not called out before and which gcc rightly warned
on.

The 32bit execve needs are quite different, so we split into two execve() designs
rather than make a mess of the already quite nasty 8bit one.

Kernel/Makefile
Kernel/cpu-68000/cpu.h [new file with mode: 0644]
Kernel/filesys.c
Kernel/include/kdata.h
Kernel/include/kernel.h
Kernel/include/kernel32.h [new file with mode: 0644]
Kernel/inode.c
Kernel/kdata.c
Kernel/start.c
Kernel/syscall_exec16.c [moved from Kernel/syscall_exec.c with 97% similarity]
Kernel/syscall_exec32.c [new file with mode: 0644]

index a92127f..0d66a14 100644 (file)
@@ -4,8 +4,10 @@ TARGET_LIST = platform-nc100 platform-micropack platform-pcw8256 platform-socz80
 #export CPU = z80
 #export TARGET = dragon
 #export CPU = 6809
-export TARGET = tgl6502
-export CPU = 6502
+#export TARGET = tgl6502
+#export CPU = 6502
+export TARGET=atarist
+export CPU = 68000
 export VERSION = "0.1"
 export SUBVERSION = "ac1"
 
@@ -26,7 +28,9 @@ export CROSS_CC_SEGDISC=--codeseg DISCARD --constseg DISCARD
 export CROSS_CC_FONT=--constseg FONT
 export CROSS_CC_VIDEO=--codeseg VIDEO
 export ASOPTS=-plosff
+export ASMEXT = .s
 export BINEXT = .rel
+export BITS=16
 #
 #      Adjust this as needed for your platform (or contribute a script
 #      to look in the usual places !)
@@ -55,8 +59,10 @@ export CROSS_CC_SEG2=--code-name SEG2
 # 6502 we need a real SEG3 to make it fit
 export CROSS_CC_SEG3=--code-name SEG3
 export CROSS_CC_SEGDISC=--code-name DISCARD --rodata-name DISCARDDATA
+export ASMEXT = .s
 export BINEXT = .o
-else
+export BITS=16
+else ifeq ($(CPU),6809)
 export CROSS_AS=m6809-unknown-as
 export CROSS_LD=lwlink
 export CROSS_CC = m6809-unknown-gcc
@@ -69,7 +75,24 @@ export CROSS_CC_SEG3=-mcode-section=.text2 -mfar-code-page=2
 export CROSS_CC_SEGDISC=-mcode-section=.discard -mfar-code-page=3
 export CROSS_CC_VIDEO=-mcode-section=.video -mdata-section=.videodata -mfar-code-page=4
 export ASOPTS=
+export ASMEXT = .s
 export BINEXT = .o
+export BITS=16
+else
+export CROSS_LD=m68k-linux-gnu-ld
+export CROSS_CC = m68k-linux-gnu-gcc
+export CROSS_CCOPTS=-c -fno-builtin -Wall -Os -m68000 -mshort -I$(ROOT_DIR)/cpu-68000 -I$(ROOT_DIR)/platform-$(TARGET) -I$(ROOT_DIR)/include
+export CROSS_AS=$(CROSS_CC) $(CROSS_CCOPTS) #-Wa,-M
+export CROSS_CC_SEG1=
+export CROSS_CC_SEG2=
+export CROSS_CC_SEG3=
+# Fixme: we should split discard off
+export CROSS_CC_SEGDISC=
+export CROSS_CC_VIDEO=
+export ASOPTS=
+export ASMEXT = .S
+export BINEXT = .o
+export BITS=32
 endif
 
 ROOT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
@@ -98,8 +121,8 @@ C1SRCS += inode.c tty.c
 #      Keep all of the syscalls in C2SRCS
 #
 C2SRCS =  syscall_proc.c syscall_fs.c
-C2SRCS += syscall_fs2.c syscall_other.c syscall_exec.c process.c
-C2SRCS += simple.c single.c bank16k.c bank16k_low.c bank32k.c bankfixed.c
+C2SRCS += syscall_fs2.c syscall_other.c syscall_exec$(BITS).c process.c malloc.c
+C2SRCS += simple.c single.c bank16k.c bank16k_low.c bank32k.c bankfixed.c flat.c
 #
 #      Drop some bits into CODE3 so the 6502 banks fit nicely. May well
 #      need to do this on Z80 as well
@@ -109,7 +132,7 @@ CVIDEOSRCS = vt.c
 CFONTSRCS = font4x6.c font6x8.c font8x8.c
 
 
-ASRCS =  lowlevel-$(CPU).s usermem_std-$(CPU).s
+ASRCS =  lowlevel-$(CPU)$(ASMEXT) usermem_std-$(CPU)$(ASMEXT)
 
 
 CDOBJS = $(CDSRCS:.c=$(BINEXT))
@@ -118,20 +141,19 @@ C2OBJS = $(C2SRCS:.c=$(BINEXT))
 C3OBJS = $(C3SRCS:.c=$(BINEXT))
 CFONTOBJS = $(CFONTSRCS:.c=$(BINEXT))
 CVIDEOOBJS = $(CVIDEOSRCS:.c=$(BINEXT))
-AOBJS = $(ASRCS:.s=$(BINEXT))
+AOBJS = $(ASRCS:$(ASMEXT)=$(BINEXT))
 
 CSRCS = $(CDSRCS) $(C1SRCS) $(C2SRCS) $(C3SRCS) $(CFONTSRCS) $(CVIDEOSRCS)
 COBJS = $(CDOBJS) $(C1OBJS) $(C2OBJS) $(C3OBJS) $(CFONTOBJS) $(CVIDEOOBJS)
 
 OBJS  = $(COBJS) $(AOBJS)
 
-
-JUNK = $(CSRCS:.c=.lst) $(CSRCS:.c=.s) $(CSRCS:.c=.asm) $(CSRCS:.c=.sym) $(ASRCS:.s=.lst) $(ASRCS:.s=.sym) $(CSRCS:.c=.rst) $(CSRCS:.c=.o)  $(ASRCS:.s=.rst)
+JUNK = $(CSRCS:.c=.lst) $(CSRCS:.c=.s) $(CSRCS:.c=.asm) $(CSRCS:.c=.sym) $(ASRCS:$(ASMEXT)=.lst) $(ASRCS:$(ASMEXT)=.sym) $(CSRCS:.c=.rst) $(CSRCS:.c=.o)  $(ASRCS:$(ASMEXT)=.rst)
 
 all:   fuzix.bin
 
 .SUFFIXES:             # delete the default suffixes
-.SUFFIXES: .c .s .rel
+.SUFFIXES: .c .s .rel .S
 
 usermem_std-z180.rel:  usermem_std-z180.s usermem_std-z80.s
 
@@ -160,7 +182,7 @@ $(CFONTOBJS): %$(BINEXT): %.c
 $(CVIDEOOBJS): %$(BINEXT): %.c
        $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_VIDEO) $<
 
-$(AOBJS): %$(BINEXT): %.s
+$(AOBJS): %$(BINEXT): %$(ASMEXT)
        $(CROSS_AS) $(ASOPTS) -o $*$(BINEXT) $<
 
 version.c: makeversion
diff --git a/Kernel/cpu-68000/cpu.h b/Kernel/cpu-68000/cpu.h
new file mode 100644 (file)
index 0000000..4737c2c
--- /dev/null
@@ -0,0 +1,65 @@
+typedef unsigned long uint32_t;
+typedef signed long int32_t;
+typedef unsigned short uint16_t;
+typedef signed short int16_t;
+typedef unsigned char uint8_t;
+typedef signed char int8_t;
+typedef unsigned long size_t;
+
+typedef uint16_t irqflags_t;
+
+typedef int32_t arg_t;
+typedef uint32_t uarg_t;               /* Holds arguments */
+typedef uint32_t usize_t;              /* Largest value passed by userspace */
+typedef int32_t susize_t;
+typedef uint32_t uaddr_t;
+
+extern void ei(void);
+extern irqflags_t di(void);
+extern void irqrestore(irqflags_t f);
+
+extern void *memcpy(void *, const void  *, size_t);
+extern void *memset(void *, int, size_t);
+extern int memcmp(const void *, const void *, size_t);
+extern size_t strlen(const char *);
+
+#define EMAGIC    0x4C    /* Header of executable (JMP) */
+#define EMAGIC_2  0x38   /* SEC BCS foo */
+
+#define brk_limit() ((udata.u_syscall_sp) - 512)
+
+#define staticfast
+
+/* User's structure for times() system call */
+typedef unsigned long clock_t;
+
+typedef struct {
+  uint32_t low;
+  uint32_t high;
+} time_t;
+
+typedef union {            /* this structure is endian dependent */
+    clock_t  full;         /* 32-bit count of ticks since boot */
+    struct {
+      uint16_t high;
+      uint16_t low;         /* 16-bit count of ticks since boot */
+    } h;
+} ticks_t;
+
+/* We don't need any banking bits really */
+#define CODE1
+#define CODE2
+#define COMMON
+#define VIDEO
+#define DISCARD
+
+/* Sane behaviour for unused parameters */
+#define used(x)
+
+#define __fastcall__
+
+/* Our udata is handled slightly quirkily - use a register global */
+
+register struct u_data *udata_ptr asm ("a5");
+
+#define udata (*udata_ptr)
index 08575ec..490c969 100644 (file)
@@ -283,7 +283,7 @@ bool ch_link(inoptr wd, char *oldname, char *newname, inoptr nindex)
     for(;;)
     {
         udata.u_count = DIR_LEN;
-        udata.u_base  =(char *)&curentry;
+        udata.u_base  =(unsigned char *)&curentry;
         udata.u_sysio = true;
         readi(wd, 0);
 
@@ -314,7 +314,7 @@ bool ch_link(inoptr wd, char *oldname, char *newname, inoptr nindex)
     }
 
     udata.u_count = DIR_LEN;
-    udata.u_base  = (char*)&curentry;
+    udata.u_base  = (unsigned char*)&curentry;
     udata.u_sysio = true;
     writei(wd, 0);
 
index 3eaf81b..3336754 100644 (file)
@@ -3,12 +3,16 @@
 
 #include <stdbool.h>
 
-extern char *cmdline;
+extern unsigned char *cmdline;
 #define BOOTLINE_LEN 6
-extern char bootline[BOOTLINE_LEN];
+extern unsigned char bootline[BOOTLINE_LEN];
 
 extern struct u_block ub;
+
+/* Some platforms define udata for things like register globals */
+#ifndef udata
 extern struct u_data udata;
+#endif
 
 extern uint16_t maxproc;   /* Actual max number of processes */
 extern uint16_t ramsize;
@@ -52,7 +56,7 @@ typedef int (*dev_write_t)(uint8_t minor, uint8_t rawflag, uint8_t flag);
 typedef int (*dev_init_t)(void);
 typedef int (*dev_open_t)(uint8_t minor, uint16_t flag);
 typedef int (*dev_close_t)(uint8_t minor);
-typedef int (*dev_ioctl_t)(uint8_t minor, uint16_t request, char *data); // note: data is in userspace
+typedef int (*dev_ioctl_t)(uint8_t minor, uarg_t request, char *data); // note: data is in userspace
 
 typedef struct devsw {
     dev_open_t dev_open;  /* The routines for reading, etc */
@@ -75,7 +79,7 @@ extern struct runload loadavg[];
 
 // the system call dispatch table
 #define FUZIX_SYSCALL_COUNT 63
-typedef int16_t (*syscall_t)(void);
+typedef arg_t (*syscall_t)(void);
 extern const syscall_t syscall_dispatch[FUZIX_SYSCALL_COUNT];
 
 #endif
index 350f453..665d65f 100644 (file)
@@ -64,7 +64,7 @@ From UZI by Doug Braun and UZI280 by Stefan Nitschke.
 typedef struct s_queue {
     unsigned char *q_base;    /* Pointer to data */
     unsigned char *q_head;    /* Pointer to addr of next char to read. */
-    char *q_tail;    /* Pointer to where next char to insert goes. */
+    unsigned char *q_tail;    /* Pointer to where next char to insert goes. */
     int   q_size;    /* Max size of queue */
     int   q_count;   /* How many characters presently in queue */
     int   q_wakeup;  /* Threshold for waking up processes waiting on queue */
@@ -330,6 +330,9 @@ typedef struct p_tab {
     void *      p_wait;         /* Address of thing waited for */
     uint16_t    p_page;         /* Page mapping data */
     uint16_t   p_page2;        /* It's really four bytes for the platform */
+#ifdef udata
+    struct u_data *p_udata;    /* Udata pointer for platforms using dynamic udata */
+#endif
     /* Update kernel.def if you change fields above this comment */
     /* Everything below here is overlaid by time info at exit */
     uint16_t    p_priority;     /* Process priority */
@@ -377,7 +380,7 @@ typedef struct u_data {
     int     (*u_sigvec[NSIGS])(int);   /* Array of signal vectors */
     /**** If you change this top section, also update offsets in "kernel.def" ****/
 
-    char *      u_base;         /* Source or dest for I/O */
+    uint8_t *   u_base;         /* Source or dest for I/O */
     usize_t     u_count;        /* Amount for I/O */
     off_t       u_offset;       /* Place in file for I/O */
     struct blkbuf *u_buf;
@@ -744,7 +747,7 @@ CODE2 void updoff(void);
 CODE2 int stcpy(inoptr ino, char *buf);
 CODE2 bool rargs (char **userspace_argv, struct s_argblk *argbuf);
 CODE2 char **wargs(char *userspace_ptr, struct s_argblk *argbuf, int  *cnt);
-CODE2 extern int16_t unlinki(inoptr ino, inoptr pino, char *fname);
+CODE2 extern arg_t unlinki(inoptr ino, inoptr pino, char *fname);
 
 /* timer.c */
 CODE2 void rdtime(time_t *tloc);
@@ -759,8 +762,8 @@ CODE2 void pagemap_init(void);
 CODE2 void pagemap_add(uint8_t page);  /* FIXME: may need a page type for big boxes */
 CODE2 void pagemap_free(ptptr p);
 CODE2 int pagemap_alloc(ptptr p);
-CODE2 int pagemap_realloc(uint16_t p);
-CODE2 uint16_t pagemap_mem_used(void);
+CODE2 int pagemap_realloc(usize_t p);
+CODE2 uaddr_t pagemap_mem_used(void);
 CODE2 uint8_t *swapout_prepare_uarea(ptptr p);
 CODE2 uint8_t *swapin_prepare_uarea(ptptr p);
 CODE2 void map_init(void);
@@ -768,7 +771,7 @@ CODE2 void platform_idle(void);
 CODE2 uint8_t rtc_secs(void);
 
 /* Will need a uptr_t eventually */
-extern uint16_t ramtop;             /* Note: ramtop must be in common in some cases */
+extern uaddr_t ramtop;      /* Note: ramtop must be in common in some cases */
 CODE2 extern void platform_interrupt(void);
 COMMON void invalidate_cache(uint16_t page);
 COMMON void flush_cache(ptptr p);
@@ -837,4 +840,8 @@ CODE2 arg_t _flock(void);    /* FUZIX system call 60 */
 CODE2 arg_t _getpgrp(void);     /* FUZIX system call 61 */
 CODE2 arg_t _sched_yield(void);  /* FUZIX system call 62 */
 
+#if defined(CONFIG_32BIT)
+#include "kernel32.h"
+#endif
+
 #endif /* __FUZIX__KERNEL_DOT_H__ */
diff --git a/Kernel/include/kernel32.h b/Kernel/include/kernel32.h
new file mode 100644 (file)
index 0000000..bbac36b
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ *     Functionality only used in the 32bit ports
+ */
+
+#ifndef __FUZIX__KERNEL_32_H
+#define __FUZIX__KERNEL_32_H
+
+/* malloc.c */
+extern void *kmalloc(size_t);
+extern void *kzalloc(size_t);
+extern void kfree(void *);
+extern void kmemaddblk(void *, size_t);
+extern unsigned long kmemavail(void);
+extern unsigned long kmemused(void);
+
+/* flat.c */
+extern void pagemap_switch(ptptr p);
+extern void *pagemap_base(void);
+#define PROGLOAD (uint32_t)pagemap_base()
+extern arg_t _memalloc(void);
+extern arg_t _memfree(void);
+extern uint32_t ugetl(void *uaddr, int *err);
+extern int uputl(uint32_t val, void *uaddr);
+
+/* Dummy */
+#define PROGTOP        0
+
+#endif
index 7518ff2..ce34f2d 100644 (file)
@@ -232,7 +232,7 @@ inoptr rwsetup(bool is_read, uint8_t * flag)
        struct oft *oftp;
 
        udata.u_sysio = false;  /* I/O to user data space */
-       udata.u_base = (char *) udata.u_argn1;  /* buf */
+       udata.u_base = (unsigned char *) udata.u_argn1; /* buf */
        udata.u_count = (susize_t) udata.u_argn2;       /* nbytes */
 
        if ((ino = getinode(udata.u_argn)) == NULLINODE) {
index 4238220..565b76e 100644 (file)
@@ -3,8 +3,8 @@
 #include <kdata.h>
 
 p_tab *init_process;
-char *cmdline = (char *) CMDLINE;
-char bootline[BOOTLINE_LEN];
+unsigned char *cmdline = (unsigned char *) CMDLINE;
+unsigned char bootline[BOOTLINE_LEN];
 uint16_t ramsize, procmem, maxproc, nproc, nready;
 uint16_t runticks;
 bool inint;
index e96a7b0..0f56d18 100644 (file)
@@ -84,7 +84,7 @@ void create_init(void)
 
 /* to sensibly parse device names this needs to be platform-specific,
    so for now it only parses device numbers */
-uint16_t bootdevice(char *s)
+uint16_t bootdevice(unsigned char *s)
 {
     unsigned int r = 0;
 
similarity index 97%
rename from Kernel/syscall_exec.c
rename to Kernel/syscall_exec16.c
index 40079ac..aea4876 100644 (file)
@@ -316,3 +316,19 @@ char **wargs(char *ptr, struct s_argblk *argbuf, int *cnt) // ptr is in userspac
        uputw(0, argv);         /*;;26Feb- Add Null Pointer to end of array */
        return ((char **) argbase);
 }
+
+/*
+ *     Stub the 32bit only allocator calls
+ */
+
+arg_t _memalloc(void)
+{
+       udata.u_error = ENOMEM;
+       return -1;
+}
+
+arg_t _memfree(void)
+{
+       udata.u_error = ENOMEM;
+       return -1;
+}
diff --git a/Kernel/syscall_exec32.c b/Kernel/syscall_exec32.c
new file mode 100644 (file)
index 0000000..13c96a3
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ *     Implement binary loading for 32bit platforms. We use the ucLinux binflat
+ *     format with a simple magic number tweak to avoid confusion with ucLinux
+ */
+
+#include <kernel.h>
+#include <kernel32.h>
+#include <version.h>
+#include <kdata.h>
+#include <printf.h>
+
+#define ARGBUF_SIZE    2048
+
+struct binfmt_flat {
+       uint8_t magic[4];
+       uint32_t rev;
+       uint32_t entry;
+       uint32_t data_start;
+       uint32_t data_end;
+       uint32_t bss_end;
+       uint32_t stack_size;
+       uint32_t reloc_start;
+       uint32_t reloc_count;
+       uint32_t flags;
+       uint32_t filler[6];
+};
+
+static int bload(inoptr i, uint16_t bl, void *base, uint32_t len)
+{
+       blkno_t blk;
+       while(len) {
+               uint16_t cp = min(len, 512);
+               blk = bmap(i, bl, 1);
+               if (blk == NULLBLK)
+                       uzero(base, 512);
+               else {
+                       udata.u_offset = (off_t)blk << 9;
+                       udata.u_count = 512;
+                       udata.u_base = base;
+                       if (cdread(i->c_dev, 0) < 0) {
+                               kputs("bload failed.\n");
+                               return -1;
+                       }
+               }
+               base += cp;
+               len -= cp;
+               bl++;
+       }
+       return 0;
+}
+
+static void close_on_exec(void)
+{
+       int j;
+       for (j = UFTSIZE - 1; j >= 0; --j) {
+               if (udata.u_cloexec & (1 << j))
+                       doclose(j);
+       }
+       udata.u_cloexec = 0;
+}
+
+static int valid_hdr(inoptr ino, struct binfmt_flat *bf)
+{
+       if (bf->rev != 4)
+               return 0;
+       if (bf->entry >= bf->data_start)
+               return 0;
+       if (bf->data_start > bf->data_end)
+               return 0;
+       if (bf->data_end < bf->bss_end)
+               return 0;
+       if (bf->bss_end + bf->stack_size < bf->bss_end)
+               return 0;
+       if (bf->data_end > ino->c_node.i_size)
+               return 0;
+       if (bf->bss_end - bf->data_end < 4 * bf->reloc_count)
+               bf->bss_end = bf->data_end + 4 * bf->reloc_count;
+       if (bf->reloc_start + bf->reloc_count * 4 > ino->c_node.i_size ||
+               bf->reloc_start + bf->reloc_count * 4 < bf->reloc_start)
+               return 0;
+       if (bf->flags != 1)
+               return 0;
+       return 1;
+}
+
+/* For now we load the binary in one block, including code/data/bss. We can
+   look at better formats, split binaries etc later maybe */
+static void relocate(struct binfmt_flat *bf, void *progbase, uint32_t size)
+{
+       uint32_t *rp = progbase + bf->reloc_start;
+       uint32_t n = bf->reloc_count;
+       while (n--) {
+               uint32_t v = *rp++;
+               if (v < size && !(v&1)) /* Revisit for non 68K */
+                       *((uint32_t *)(rp + v)) += (uint32_t)progbase;
+       }
+}
+
+
+/* User's execve() call. All other flavors are library routines. */
+/*******************************************
+execve (name, argv, envp)        Function 23
+char *name;
+char *argv[];
+char *envp[];
+********************************************/
+#define name (char *)udata.u_argn
+#define argv (char **)udata.u_argn1
+#define envp (char **)udata.u_argn2
+
+arg_t _execve(void)
+{
+       struct binfmt_flat *binflat;
+       inoptr ino;
+       unsigned char *buf;
+       char **nargv;           /* In user space */
+       char **nenvp;           /* In user space */
+       struct s_argblk *abuf, *ebuf;
+       int argc;
+       uint32_t bin_size;      /* Will need to be bigger on some cpus */
+       void *progbase, *top;
+       uaddr_t go;
+
+       if (!(ino = n_open(name, NULLINOPTR)))
+               return (-1);
+
+       if (!((getperm(ino) & OTH_EX) &&
+             (ino->c_node.i_mode & F_REG) &&
+             (ino->c_node.i_mode & (OWN_EX | OTH_EX | GRP_EX)))) {
+               udata.u_error = EACCES;
+               goto nogood;
+       }
+
+       setftime(ino, A_TIME);
+
+       if (ino->c_node.i_size == 0) {
+               udata.u_error = ENOEXEC;
+               goto nogood;
+       }
+
+       /* Read in the first block of the new program */
+       buf = bread(ino->c_dev, bmap(ino, 0, 1), 0);
+       binflat = (struct binfmt_flat *)buf;
+
+       /* Hard coded for our 68K format. We don't quite use the ucLinux
+          names, we don't want to load a ucLinux binary in error! */
+       if (buf == NULL || memcmp(buf, "bF68", 4) ||
+               !valid_hdr(ino, binflat)) {
+               udata.u_error = ENOEXEC;
+               goto nogood2;
+       }
+
+       /* Memory needed */
+       bin_size = binflat->bss_end + binflat->stack_size;
+
+       /* Gather the arguments, and put them in temporary buffers. */
+       abuf = (struct s_argblk *) kmalloc(ARGBUF_SIZE);
+       if (abuf == NULL) {
+               udata.u_error = ENOMEM;
+               goto nogood2;
+       }
+       /* Put environment in another buffer. */
+       ebuf = (struct s_argblk *) kmalloc(ARGBUF_SIZE);
+       if (ebuf == NULL) {
+               kfree(abuf);
+               udata.u_error = ENOMEM;
+               goto nogood2;
+       }
+
+       /* Read args and environment from process memory */
+       if (rargs(argv, abuf) || rargs(envp, ebuf))
+               goto nogood3;
+
+       /* This must be the last test as it makes changes if it works */
+       if (pagemap_realloc(bin_size))
+               goto nogood3;
+
+       /* From this point on we are commmited to the exec() completing */
+
+       /* setuid, setgid if executable requires it */
+       if (ino->c_node.i_mode & SET_UID)
+               udata.u_euid = ino->c_node.i_uid;
+
+       if (ino->c_node.i_mode & SET_GID)
+               udata.u_egid = ino->c_node.i_gid;
+
+       /* We are definitely going to succeed with the exec,
+        * so we can start writing over the old program
+        */
+       
+       progbase = pagemap_base();
+       top = progbase + bin_size;
+
+       uput(buf, (uint8_t *)progbase, 512);    /* Move 1st Block to user bank */
+
+       /* At this point, we are committed to reading in and
+        * executing the program. */
+
+       close_on_exec();
+
+       /*
+        *  Read in the rest of the program, block by block
+        *  We use bufdiscard so that we load the entire app through the
+        *  same buffer to avoid cycling our small cache on this. Indirect blocks
+        *  will still be cached. - Hat tip to Steve Hosgood's OMU for that trick
+        */
+
+       /* Compute this once otherwise each loop we must recalculate this
+          as the compiler isn't entitled to assume the loop didn't change it */
+
+       bin_size = binflat->reloc_start + 4 * binflat->reloc_count;
+       if (bin_size > 512)
+               bload(ino, 1, progbase + 512, bin_size - 512);
+       
+       go = (uint32_t)progbase + binflat->entry;
+
+       relocate(binflat, progbase, bin_size);
+       /* This may wipe the relocations */     
+       uzero(progbase + binflat->data_end, 
+               binflat->bss_end - binflat->data_end + binflat->stack_size);
+
+       brelse(buf);
+
+       /* brk eats into the stack allocation */
+       udata.u_break = (uaddr_t)(progbase + binflat->bss_end);
+
+       /* Turn off caught signals */
+       memset(udata.u_sigvec, 0, sizeof(udata.u_sigvec));
+
+       /* place the arguments, environment and stack at the top of userspace memory. */
+
+       /* Write back the arguments and the environment */
+       nargv = wargs(((char *) top - 4), abuf, &argc);
+       nenvp = wargs((char *) (nargv), ebuf, NULL);
+
+       /* Fill in udata.u_name with Program invocation name */
+       uget((void *) ugetl(nargv, NULL), udata.u_name, 8);
+       memcpy(udata.u_ptab->p_name, udata.u_name, 8);
+
+       kfree(abuf);
+       kfree(ebuf);
+
+       /* Shove argc and the address of argv just below envp */
+       uputl((uint32_t) nargv, nenvp - 1);
+       uputl((uint32_t) argc, nenvp - 2);
+
+       // Set stack pointer for the program
+       udata.u_isp = nenvp - 4;
+
+       // Start execution (never returns)
+       doexec(go);
+
+       // tidy up in various failure modes:
+nogood3:
+       kfree(abuf);
+       kfree(ebuf);
+nogood2:
+       brelse(buf);
+nogood:
+       i_deref(ino);
+       return (-1);
+}
+
+#undef name
+#undef argv
+#undef envp
+
+/* TODO        max (1024) 512 bytes for argv
+ *             and max 512 bytes for environ
+ */
+
+bool rargs(char **userspace_argv, struct s_argblk * argbuf)
+{
+       char *ptr;              /* Address of base of arg strings in user space */
+       uint8_t c;
+       uint8_t *bufp;
+       int err;
+
+       argbuf->a_argc = 0;     /* Store argc in argbuf */
+       bufp = argbuf->a_buf;
+
+       while ((ptr = (char *) ugetl(userspace_argv++, &err)) != NULL) {
+               if (err)
+                       return true;
+               ++(argbuf->a_argc);     /* Store argc in argbuf. */
+               do {
+                       *bufp++ = c = ugetc(ptr++);
+                       if (bufp > argbuf->a_buf + ARGBUF_SIZE - 12) {
+                               udata.u_error = E2BIG;
+                               return true;    // failed
+                       }
+               }
+               while (c);
+       }
+       /*Store total string size. */
+       argbuf->a_arglen = bufp - (uint8_t *)argbuf->a_buf;
+       /* Success */
+       return false;
+}
+
+
+char **wargs(char *ptr, struct s_argblk *argbuf, int *cnt)     // ptr is in userspace
+{
+       char **argv;            /* Address of users argv[], just below ptr */
+       int argc, arglen;
+       char **argbase;
+       uint8_t *sptr;
+
+       sptr = argbuf->a_buf;
+
+       /* Move them into the users address space, at the very top */
+       ptr -= (arglen = argbuf->a_arglen);
+
+       if (arglen) {
+               uput(sptr, ptr, arglen);
+       }
+
+       /* Set argv to point below the argument strings */
+       argc = argbuf->a_argc;
+       argbase = argv = (char **) ptr - (argc + 1);
+
+       if (cnt) {
+               *cnt = argc;
+       }
+
+       /* Set each element of argv[] to point to its argument string */
+       while (argc--) {
+               uputl((uint32_t) ptr, argv++);
+               if (argc) {
+                       do
+                               ++ptr;
+                       while (*sptr++);
+               }
+       }
+       uputl(0, argv);
+       return ((char **) argbase);
+}
+