hlfw.ca

drawcpu

Download patch

ref: 9b8e21a6669c6312809b174220fca59728cbb68b
parent: 775400b52c5b473fe1528f3e31283fcfb730c18a
author: halfwit <michaelmisch1985@gmail.com>
date: Sat Jul 27 10:22:01 PDT 2024

Changes to facilitate mem.h and exec

--- a/Make.dragonfly
+++ b/Make.dragonfly
@@ -13,6 +13,7 @@
 LDFLAGS=$(PTHREAD)
 TARG=drawcpu
 AUDIO=none
+ARCH=$(shell uname -m|sed 's/i.86/386/;s/Power Macintosh/power/; s/x86_64/amd64/')
 
 all: default
 
--- a/Make.fbdev
+++ b/Make.fbdev
@@ -14,6 +14,7 @@
 TARG=drawcpu
 # AUDIO=none
 AUDIO=alsa
+ARCH=$(shell uname -m|sed 's/i.86/386/;s/Power Macintosh/power/; s/x86_64/amd64/; s/armv[567].*/arm/; s/aarch64/arm64/')
 
 all: default
 
--- a/Make.freebsd
+++ b/Make.freebsd
@@ -13,6 +13,7 @@
 LDFLAGS=$(PTHREAD)
 TARG=drawcpu
 AUDIO=unix
+ARCH=$(shell uname -m|sed 's/i.86/386/;s/Power Macintosh/power/; s/x86_64/amd64/')
 
 all: default
 
--- a/Make.irix
+++ b/Make.irix
@@ -17,6 +17,7 @@
 LDFLAGS=$(PTHREAD)
 TARG=drawcpu
 MAKE=gmake
+ARCH=mips
 
 all: default
 
--- a/Make.linux
+++ b/Make.linux
@@ -13,6 +13,7 @@
 TARG=drawcpu
 # AUDIO=none
 AUDIO=pipewire
+ARCH=$(shell uname -m|sed 's/i.86/386/;s/Power Macintosh/power/; s/x86_64/amd64/; s/armv[567].*/arm/; s/aarch64/arm64/')
 
 all: default
 
--- a/Make.linux386
+++ b/Make.linux386
@@ -14,6 +14,7 @@
 TARG=drawcpu
 # AUDIO=none
 AUDIO=unix
+ARCH=386
 
 all: default
 
--- a/Make.netbsd
+++ b/Make.netbsd
@@ -13,6 +13,7 @@
 LDFLAGS=$(PTHREAD)
 TARG=drawcpu
 AUDIO=unix
+ARCH=$(shell uname -m|sed 's/i.86/386/;s/Power Macintosh/power/; s/x86_64/amd64/')
 
 all: default
 
--- a/Make.openbsd
+++ b/Make.openbsd
@@ -12,6 +12,7 @@
 LDFLAGS=$(PTHREAD)
 TARG=drawcpu
 AUDIO=sndio
+ARCH=$(shell uname -m|sed 's/i.86/386/; s/macppc/power/; s/socppc/power/; s/x86_64/amd64/; s/sparc64/sun4u/')
 
 all: default
 
--- a/Make.osx-cocoa
+++ b/Make.osx-cocoa
@@ -12,6 +12,7 @@
 LDFLAGS=$(PTHREAD)
 TARG=drawcpu
 AUDIO=none
+ARCH=$(shell uname -m|sed 's/i.86/386/;s/x86_64/amd64/')
 
 all: default
 
--- a/Make.pthread
+++ b/Make.pthread
@@ -15,6 +15,7 @@
 TARG=drawcpu
 # AUDIO=none
 AUDIO=unix
+ARCH=$(shell uname -m|sed 's/i.86/386/;s/Power Macintosh/power/; s/x86_64/amd64/; s/armv[567].*/arm/; s/aarch64/arm64/')
 
 all: default
 
--- a/Make.unix
+++ b/Make.unix
@@ -15,6 +15,7 @@
 TARG=drawcpu
 # AUDIO=none
 AUDIO=unix
+ARCH=$(shell uname -m|sed 's/i.86/386/;s/Power Macintosh/power/; s/x86_64/amd64/; s/armv[567].*/arm/; s/aarch64/arm64/')
 
 all: default
 
--- a/Make.win32
+++ b/Make.win32
@@ -30,6 +30,7 @@
 #IP=win32
 #OS=win32
 #GUI=win32
+ARCH=386
 
 all: default
 
--- a/Make.win64
+++ b/Make.win64
@@ -20,6 +20,7 @@
 LDADD=-lgdi32 -lws2_32 -lwinmm -mwindows
 TARG=drawcpu.exe
 XOFILES=glenda-t.$O
+ARCH=386
 
 all: default
 
--- a/TODO
+++ b/TODO
@@ -1,10 +1,12 @@
 TODO:
+ - [ ] Look into args address space prior to hitting exec
+ - [ ] /env/fn# to init functions?
  - [ ] set up initial connection before handing over to an rc session, to set up binds and mounts
- - [x] remove rc builtins
- - [ ] Import $objtype/bin from 9front instead of builtins
- - [ ] Adapt github.com/michaelforney/nine to load and execute our binaries --> into the rc code for exec, instead of what they have
- - [ ] Check the magic, run a normal exec if it's not a plan9 or p9p binary, or try wrapping everything we run
+ - [ ] Import our $objtype/bin and /rc from 9front instead of builtins, -f $9filesys
+ - [x] remove rc builtins, possibly add os() as it uses devcmd locally, if needed
+ - [ ] Adapt github.com/michaelforney/nine to load and execute our binaries (exec)
  - [x] start.s in libmachdep for each posix-target, just do like tas.c
+       - investigate if we want to run this another way instead!
  - [ ] darwin will use mach_vm_-family of functions, else we use mmap
  - [ ] have it export a var service=unix
  - [ ] Some people probably want aan?
--- /dev/null
+++ b/include/a.out.h
@@ -1,0 +1,48 @@
+typedef	struct	Exec	Exec;
+struct	Exec
+{
+	long	magic;		/* magic number */
+	long	text;	 	/* size of text segment */
+	long	data;	 	/* size of initialized data */
+	long	bss;	  	/* size of uninitialized data */
+	long	syms;	 	/* size of symbol table */
+	long	entry;	 	/* entry point */
+	long	spsz;		/* size of pc/sp offset table */
+	long	pcsz;		/* size of pc/line number table */
+};
+
+#define HDR_MAGIC	0x00008000		/* header expansion */
+
+#define	_MAGIC(f, b)	((f)|((((4*(b))+0)*(b))+7))
+#define	A_MAGIC		_MAGIC(0, 8)		/* 68020 */
+#define	I_MAGIC		_MAGIC(0, 11)		/* intel 386 */
+#define	J_MAGIC		_MAGIC(0, 12)		/* intel 960 (retired) */
+#define	K_MAGIC		_MAGIC(0, 13)		/* sparc */
+#define	V_MAGIC		_MAGIC(0, 16)		/* mips 3000 BE */
+#define	X_MAGIC		_MAGIC(0, 17)		/* att dsp 3210 (retired) */
+#define	M_MAGIC		_MAGIC(0, 18)		/* mips 4000 BE */
+#define	D_MAGIC		_MAGIC(0, 19)		/* amd 29000 (retired) */
+#define	E_MAGIC		_MAGIC(0, 20)		/* arm */
+#define	Q_MAGIC		_MAGIC(0, 21)		/* powerpc */
+#define	N_MAGIC		_MAGIC(0, 22)		/* mips 4000 LE */
+#define	L_MAGIC		_MAGIC(0, 23)		/* dec alpha (retired) */
+#define	P_MAGIC		_MAGIC(0, 24)		/* mips 3000 LE */
+#define	U_MAGIC		_MAGIC(0, 25)		/* sparc64 */
+#define	S_MAGIC		_MAGIC(HDR_MAGIC, 26)	/* amd64 */
+#define	T_MAGIC		_MAGIC(HDR_MAGIC, 27)	/* powerpc64 */
+#define	R_MAGIC		_MAGIC(HDR_MAGIC, 28)	/* arm64 */
+
+#define	MIN_MAGIC	8
+#define	MAX_MAGIC	28			/* <= 90 */
+
+#define	DYN_MAGIC	0x80000000		/* dlm */
+
+typedef	struct	Sym	Sym;
+struct	Sym
+{
+	vlong	value;
+	uint	sig;
+	char	type;
+	char	*name;
+};
+
--- a/include/u.h
+++ b/include/u.h
@@ -10,9 +10,11 @@
 #undef close
 #undef create
 #undef dup
+#undef exec
 #undef export
 #undef fstat
 #undef fwstat
+#undef memchr
 #undef mount
 #undef open
 #undef start
--- a/include/user.h
+++ b/include/user.h
@@ -1,4 +1,4 @@
-/* sys calls */
+/* sys calls */ 
 #define	bind	sysbind
 #define	chdir	syschdir
 #define	close	sysclose
@@ -5,11 +5,13 @@
 #define create	syscreate
 #define dup	    sysdup
 #define export	sysexport
+#define exec    sysexec
 #define getwd   sysgetwd
 #define fd2path sysfd2path
 #define fstat	sysfstat
 #define fwstat	sysfwstat
 #define mount	sysmount
+#define memchr  sysmemchr
 #define	open	sysopen
 #define read	sysread
 #define remove	sysremove
@@ -38,10 +40,12 @@
 extern	int	create(char*, int, ulong);
 extern	int	dup(int, int);
 extern  int	export(int);
+extern  int exec(va_list);
 extern  int fd2path(int, char*, int);
 extern	int	fstat(int, uchar*, int);
 extern	int	fwstat(int, uchar*, int);
 extern  char* getwd(char *, int);
+extern  void* memchr(void *, int, ulong);
 extern	int	mount(int, int, char*, int, char*);
 extern	int	unmount(char*, char*);
 extern	int	open(char*, int);
--- a/kern/Makefile
+++ b/kern/Makefile
@@ -23,6 +23,7 @@
 	dirread.$O\
 	error.$O\
 	getwd.$O\
+	memchr.$O\
 	parse.$O\
 	pgrp.$O\
 	postnote.$O\
@@ -33,6 +34,7 @@
 	sleep.$O\
 	stub.$O\
 	sysfile.$O\
+	sysproc.$O\
 	time.$O\
 	qio.$O\
 	qlock.$O\
@@ -46,5 +48,5 @@
 	$(RANLIB) $(LIB)
 
 %.$O: %.c
-	$(CC) $(CFLAGS) $*.c
+	$(CC) $(CFLAGS) -I../$(OS)-$(ARCH) $*.c
 
--- a/kern/allocb.c
+++ b/kern/allocb.c
@@ -3,6 +3,7 @@
 #include	"dat.h"
 #include	"fns.h"
 #include	"error.h"
+#include	"mem.h"
 
 enum
 {
--- a/kern/dat.h
+++ b/kern/dat.h
@@ -1,6 +1,6 @@
 #define	KNAMELEN		28	/* max length of name held in kernel */
 
-#define	BLOCKALIGN		8
+//#define	BLOCKALIGN		8
 
 typedef struct Block	Block;
 typedef struct Chan	Chan;
@@ -29,6 +29,7 @@
 typedef struct Ref	Ref;
 typedef struct Rendez	Rendez;
 typedef struct Rgrp	Rgrp;
+typedef struct Segment Segment;
 typedef struct RWlock	RWlock;
 typedef struct Waitq	Waitq;
 typedef struct Walkqid	Walkqid;
@@ -347,6 +348,45 @@
 	Wakeme,
 };
 
+/* Segment types */
+enum
+{
+	SG_TYPE		= 07,		/* Mask type of segment */
+	SG_TEXT		= 00,
+	SG_DATA		= 01,
+	SG_BSS		= 02,
+	SG_STACK	= 03,
+	SG_SHARED	= 04,
+	SG_PHYSICAL	= 05,
+	SG_FIXED	= 06,
+	SG_STICKY	= 07,
+
+	SG_RONLY	= 0040,		/* Segment is read only */
+	SG_CEXEC	= 0100,		/* Detach at exec */
+	SG_FAULT	= 0200,		/* Fault on access */
+	SG_CACHED	= 0400,		/* Normal cached memory */
+	SG_DEVICE	= 01000,	/* Memory mapped device */
+	SG_NOEXEC	= 02000,	/* No execute */
+};
+
+/*
+ *  process memory segments - NSEG always last !
+ */
+enum
+{
+	SSEG, TSEG, DSEG, BSEG, ESEG, LSEG, SEG1, SEG2, SEG3, SEG4, NSEG
+};
+
+struct Segment
+{
+	QLock ql;
+	int	type;		/* segment type */
+	uintptr	base;		/* virtual base */
+	uintptr	top;		/* virtual top */
+	ulong	size;		/* size in pages */
+	int fd;
+};
+
 struct Proc
 {
 	uint	state;
@@ -367,6 +407,9 @@
 	void*	rendtag;	/* Tag for rendezvous */
 	void*	rendval;	/* Value for rendezvous */
 	Proc	*rendhash;	/* Hash list for tag values */
+	
+	QLock	seglock;	/* locked whenever seg[] changes */
+	Segment	*seg[NSEG];
 
 	int	nerrlab;
 	Label	errlab[NERR];
--- a/kern/fns.h
+++ b/kern/fns.h
@@ -1,4 +1,4 @@
-#define	ROUND(s, sz)	(((s)+((sz)-1))&~((sz)-1))
+//#define	ROUND(s, sz)	(((s)+((sz)-1))&~((sz)-1))
 
 Block*		adjustblock(Block*, int);
 Block*		allocb(int);
@@ -198,6 +198,9 @@
 void		setrealloctag(void*, uintptr);
 long		showfilewrite(char*, int);
 char*		skipslash(char*);
+Segment*	dupseg(Segment**, int, int);
+Segment*	newseg(int, uintptr, ulong);
+Segment*	seg(Proc*, uintptr, int);
 void		sleep(Rendez*, int(*)(void*), void*);
 void*		smalloc(ulong);
 int		splhi(void);
@@ -206,11 +209,11 @@
 Block*		trimblock(Block*, int, int);
 long		unionread(Chan*, void*, long);
 void		unlock(Lock*);
-#define	validaddr(a, b, c)
+void	    validaddr(uintptr, ulong, int);
 void		validname(char*, int);
 char*		validnamedup(char*, int);
 void		validstat(uchar*, int);
-void*		vmemchr(void*, int, int);
+void*		vmemchr(void*, int, ulong);
 Proc*		wakeup(Rendez*);
 int		walk(Chan**, char**, int, int, int*);
 #define	waserror()	(setjmp(pwaserror()->buf))
--- /dev/null
+++ b/kern/memchr.c
@@ -1,0 +1,21 @@
+#include	<u.h>
+#include	<libc.h>
+
+void*
+sysmemchr(void *ap, int c, ulong n)
+{
+	uchar *sp;
+	ulong i;
+
+	i = n;
+	
+	sp = (uchar *)ap;
+	c &= 0xFF;
+	while(i > 0) {
+		printf("%d %d %c\n", c, i, *sp);
+		if(*sp++ == c)
+			return sp-1;
+		i--;
+	}
+	return 0;
+}
\ No newline at end of file
--- a/kern/posix.c
+++ b/kern/posix.c
@@ -13,6 +13,7 @@
 #include <sys/wait.h>
 #include <sys/time.h>
 #include <sys/select.h>
+#include <sys/mman.h>
 #include <signal.h>
 #include <pwd.h>
 #include <errno.h>
@@ -21,6 +22,7 @@
 #include "lib.h"
 #include "dat.h"
 #include "fns.h"
+#include "mem.h"
 
 typedef struct Oproc Oproc;
 struct Oproc
@@ -324,3 +326,33 @@
 		t.c_lflag |= (ECHO|ICANON);
 	tcsetattr(0, TCSAFLUSH, &t);
 }
+
+Segment *
+newseg(int type, uintptr base, ulong size)
+{
+	Segment *s;
+	int fd;
+
+	// NOTE: This path may not be perfectly portable
+	fd = shm_open("drawcpu", O_CREAT | O_RDWR, 0600);
+	if(fd < 0)
+		error("shm_open failed");
+
+	if(ftruncate(fd, size) < 0)
+		error("truncate failed");
+	
+	// tidy up the fs entry
+	shm_unlink("drawcpu");
+
+	s = malloc(sizeof(Segment));
+	if(s == nil)
+		error("out of memory");
+
+	s->type = type;
+	s->base = base;
+	s->size = size;
+	s->top = base+(size*BY2WD);
+	s->fd = fd;
+
+	return s;
+}
\ No newline at end of file
--- a/kern/procinit.c
+++ b/kern/procinit.c
@@ -3,6 +3,7 @@
 #include "dat.h"
 #include "fns.h"
 #include "error.h"
+#include "mem.h"
 
 void
 procinit0(void)
@@ -34,6 +35,13 @@
 	p->syserrstr = p->errbuf0;
 	p->errstr = p->errbuf1;
 	strcpy(p->text, "drawcpu");
+	p->seg[SSEG] = newseg(SG_STACK | SG_NOEXEC, USTKTOP-USTKSIZE, USTKSIZE / BY2PG);
+	p->seg[TSEG] = newseg(SG_TEXT | SG_RONLY, UTZERO, 1);
+	
+	// See /sys/src/9/port/userinit.c
+	//page = newpage(1, 0, UTZERO);
+	// [...]
+	//segpage(p->seg[TSEG], p);
 	osnewproc(p);
 	return p;
 }
--- a/kern/sysfile.c
+++ b/kern/sysfile.c
@@ -19,6 +19,10 @@
 #undef fwstat
 #undef iounit
 
+// NOTE: We don't use the kernel segments outside of exec
+#undef validaddr
+#define validaddr(a, b, c)
+
 /*
  * The sys*() routines needn't poperror() as they return directly to syscall().
  */
@@ -293,8 +297,8 @@
 	return fd;
 }
 
-static void
-_fdclose(int fd, int flag)
+void
+fdclose(int fd, int flag)
 {
 	int i;
 	Chan *c;
@@ -326,7 +330,7 @@
 _sysclose(int fd)
 {
 	fdtochan(fd, -1, 0, 0);
-	_fdclose(fd, 0);
+	fdclose(fd, 0);
 
 	return 0;
 }
@@ -589,7 +593,7 @@
 	uint l;
 
 	l = n;
-	validaddr(buf, l, 1);
+	validaddr((uintptr)buf, l, 1);
 	c = fdtochan(fd, -1, 0, 1);
 	if(waserror()) {
 		cclose(c);
@@ -608,8 +612,8 @@
 	uint l;
 
 	l = n;
-	validaddr(buf, l, 1);
-	validaddr(name, 1, 0);
+	validaddr((uintptr)buf, l, 1);
+	validaddr((uintptr)name, 1, 0);
 	c = namec(name, Aaccess, 0, 0);
 	if(waserror()){
 		cclose(c);
@@ -626,7 +630,7 @@
 {
 	Chan *c;
 
-	validaddr(name, 1, 0);
+	validaddr((uintptr)name, 1, 0);
 
 	c = namec(name, Atodir, 0, 0);
 	cclose(up->dot);
@@ -696,7 +700,7 @@
 	poperror();
 	cclose(c0);
 	if(ismount){
-		_fdclose(fd, 0);
+		fdclose(fd, 0);
 		poperror();
 		free(spec);
 	}
@@ -729,7 +733,7 @@
 			cclose(cmount);
 			nexterror();
 		}
-		validaddr(old, 1, 0);
+		validaddr((uintptr)old, 1, 0);
 		/*
 		 * This has to be namec(..., Aopen, ...) because
 		 * if arg[0] is something like /srv/cs or /fd/0,
@@ -767,7 +771,7 @@
 			cclose(c);
 		nexterror();
 	}
-	validaddr(name, 1, 0);
+	validaddr((uintptr)name, 1, 0);
 	c = namec(name, Acreate, mode, perm);
 	fd = newfd(c);
 	if(fd < 0)
@@ -806,7 +810,7 @@
 
 	l = n;
 	validstat(buf, l);
-	validaddr(name, 1, 0);
+	validaddr((uintptr)name, 1, 0);
 	c = namec(name, Aaccess, 0, 0);
 	if(waserror()){
 		cclose(c);
@@ -825,7 +829,7 @@
 	uint l;
 
 	l = n;
-	validaddr(buf, l, 0);
+	validaddr((uintptr)buf, l, 0);
 	validstat(buf, l);
 	c = fdtochan(fd, -1, 1, 1);
 	if(waserror()) {
--- /dev/null
+++ b/kern/sysproc.c
@@ -1,0 +1,306 @@
+#include    "u.h"
+#include    "lib.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include    "user.h"
+#include	"mem.h"
+#include    <a.out.h>
+
+static int
+shargs(char *s, int n, char **ap, int nap)
+{
+	char *p;
+	int i;
+
+	if(n <= 2 || s[0] != '#' || s[1] != '!')
+		return -1;
+	s += 2;
+	n -= 2;		/* skip #! */
+	if((p = memchr(s, '\n', n)) == nil)
+		return 0;
+	*p = 0;
+	i = tokenize(s, ap, nap-1);
+	ap[i] = nil;
+	return i;
+}
+
+ulong
+beswal(ulong l)
+{
+	uchar *p;
+
+	p = (uchar*)&l;
+	return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
+}
+
+uvlong
+beswav(uvlong v)
+{
+	uchar *p;
+
+	p = (uchar*)&v;
+	return ((uvlong)p[0]<<56) | ((uvlong)p[1]<<48) | ((uvlong)p[2]<<40)
+				  | ((uvlong)p[3]<<32) | ((uvlong)p[4]<<24)
+				  | ((uvlong)p[5]<<16) | ((uvlong)p[6]<<8)
+				  | (uvlong)p[7];
+}
+
+void
+evenaddr(uintptr addr)
+{
+	if(addr & 3){
+		postnote(up, 1, "sys: odd address", NDebug);
+		error(Ebadarg);
+	}
+}
+
+Segment *
+seg(Proc *p, uintptr addr, int dolock)
+{
+	Segment **s, **et, *n;
+
+	et = &p->seg[NSEG];
+	for(s = p->seg; s < et; s++) {
+		if((n = *s) == nil)
+			continue;
+		print("Here %x %x %x\n", addr, n->base, n->top);
+		if(addr >= n->base && addr < n->top) {
+			if(dolock == 0)
+				return n;
+
+			qlock(&n->ql);
+			if(addr >= n->base && addr < n->top)
+				return n;
+			qunlock(&n->ql);
+		}
+	}
+
+	return nil;
+}
+
+int
+okaddr(uintptr addr, ulong len, int wr)
+{
+	Segment *s;
+
+	if((long)len >= 0 && len <= -addr)
+		for(;;) {
+			s = seg(up, addr, 0);
+			if(s == nil)
+				print("Nil seg\n");
+			if(s == nil || (wr && (s->type&SG_RONLY)))
+				break;
+			print("Here\n");
+			if((ulong)addr+len > s->top) {
+				len -= s->top - (ulong)addr;
+				addr = s->top;
+				print("Looping\n");
+				continue;
+			}
+			print("Good!\n");
+			return 1;
+		}
+
+	return 0;
+}
+
+void
+validaddr(uintptr addr, ulong len, int wr)
+{
+	if(!okaddr(addr, len, wr)) {
+		pprint("suicide: invalid address\n");
+		postnote(up, 1, "sys: bad address in syscall", NDebug);
+		error(Ebadarg);
+	}
+}
+
+/*
+ * &s[0] is known to be a valid address.
+ */
+void*
+vmemchr(void *s, int c, ulong n)
+{
+	uintptr a;
+	ulong m, i;
+	void *t;
+
+	i = n;
+	a = (uintptr)s;
+	for(;;){
+		m = BY2PG - (a & (BY2PG-1));
+		if(i <= m)
+			break;
+		/* spans pages; handle this page */
+		t = memchr((void*)a, c, m);
+		if(t != nil)
+			return t;
+		a += m;
+		i -= m;
+		if(a < KZERO)
+			validaddr(a, 1, 0);
+	}
+print(" returning\n");
+	/* fits in one page */
+	return memchr((void*)a, c, i);
+}
+
+#undef read
+int
+sysexec(va_list list)
+{
+	union {
+		struct {
+			Exec ex;
+			uvlong	hdr[1];
+		} ehdr;
+		char buf[256];
+	} u;
+	char line[256];
+	char *progarg[32+1];
+	volatile char *args, *file0, *elem;
+	char **argv, **argp, **argp0;
+	char *a, *e, *file;
+	Chan *tc;
+	ulong magic, nbytes, nargs;
+	uintptr t, d, b, entry, text, align, data, bss, bssend;
+	int n, indir;
+
+	args = elem = nil;
+	file0 = va_arg(list, char*);
+	validaddr((uintptr)file0, 1, 0);
+	argp0 = va_arg(list, char**);
+	evenaddr((uintptr)argp0);
+	validaddr((uintptr)argp0, 2*BY2WD, 0);
+	if(*argp0 == nil)
+		error(Ebadarg);
+	file0 = validnamedup(file0, 1);
+	if(waserror()){
+		free(file0);
+		free(elem);
+		free(args);
+		/* Disaster after commit */
+		if(up->seg[SSEG] == nil)
+			pexit(up->errstr, 1);
+		nexterror();
+	}
+
+	align = BY2PG-1;
+	indir = 0;
+	file = file0;
+	for(;;){
+		tc = namec(file, Aopen, OEXEC, 0);
+		if(waserror()){
+			cclose(tc);
+			nexterror();
+		}
+		if(!indir)
+			kstrdup(&elem, up->genbuf);
+
+		n = devtab[tc->type]->read(tc, u.buf, sizeof(u.buf), 0);
+		if (n >= sizeof(Exec)){
+			magic = beswal(u.ehdr.ex.magic);
+			if(magic & AOUT_MAGIC) {
+				if(magic & HDR_MAGIC){
+					if(n < sizeof(u.ehdr))
+						error(Ebadexec);
+					entry = beswav(u.ehdr.hdr[0]);
+					text = UTZERO+sizeof(u.ehdr);
+				} else {
+					entry = beswal(u.ehdr.ex.entry);
+					text = UTZERO+sizeof(Exec);
+				}
+				if(entry < text)
+					error(Ebadexec);
+
+				text += beswal(u.ehdr.ex.text);
+				// Shouldn't have to cast to ulong here, unsure why it's needed
+				if((ulong)text <= (ulong)entry || (ulong)text >= (USTKTOP-USTKSIZE))
+					error(Ebadexec);
+				switch(magic){
+				case S_MAGIC:	/* 2MB segment alignment for amd64 */
+					align = 0x1fffff;
+					break;
+				case P_MAGIC:	/* 16K segment alignment for spim */
+				case V_MAGIC:	/* 16K segment alignment for mips */
+					align = 0x3fff;
+					break;
+				case R_MAGIC:	/* 64K segment alignment for arm64 */
+					align = 0xffff;
+					break;
+				}
+
+				break; /* for binary */
+			}
+		}
+		if(indir++)
+			error(Ebadexec);
+
+		/* Process #!/bin/sh args */
+		memmove(line, u.buf, n);
+		n = shargs(line, n, progarg, nelem(progarg));
+		if(n < 1)
+			error(Ebadexec);
+
+		/* First arg becomes complete file name */
+		progarg[n++] = file;
+		progarg[n] = nil;
+		argp0++;
+		file = progarg[0];
+		progarg[0] = elem;
+		poperror();
+		cclose(tc);
+	}
+
+	t = (text+align) & ~align;
+	text -= UTZERO;
+	data = beswal(u.ehdr.ex.data);
+	bss = beswal(u.ehdr.ex.bss);
+	align = BY2PG-1;
+	d = (t + data + align) & ~align;
+	bssend = t + data + bss;
+	b = (bssend + align) & ~align;
+	if(t >= (ulong)(USTKTOP-USTKSIZE) || d >= (ulong)(USTKTOP-USTKSIZE) || b >= (ulong)(USTKTOP-USTKSIZE))
+		error(Ebadexec);
+
+	/* Args: pass 1: count */
+	nbytes = sizeof(Tos);
+	nargs = 0;
+	if(indir){
+		argp = progarg;
+		while(*argp != nil){
+			a = *argp++;
+			nbytes += strlen(a)+1;
+			nargs++;
+		}
+	}
+	argp = argp0;
+
+	while(*argp != nil){
+		a = *argp++;
+
+		if(((uintptr)argp&(BY2PG-1)) < BY2WD)
+			validaddr((uintptr)argp, BY2WD, 0);
+		validaddr((uintptr)a, 1, 0);
+
+		e = vmemchr(a, 0, USTKSIZE);
+		if(e == nil)
+			error(Ebadarg);
+
+		nbytes += (e - a) + 1;
+		if(nbytes >= USTKSIZE)
+			error(Enovmem);
+		nargs++;
+	}
+
+	print("%d args\n", nargs);
+	
+	// End of the road cleanup
+	poperror(); // tc
+	cclose(tc);
+
+	poperror(); // file0
+	free(file0);
+
+    return 0;
+}
\ No newline at end of file
--- a/main.c
+++ b/main.c
@@ -57,6 +57,7 @@
 	chandevinit();
 	quotefmtinstall();
 
+	// TODO: Add cons to dev tree so we can open them below correctly
 	if(bind("#c", "/dev", MBEFORE) < 0)
 		panic("bind #c: %r");
 	if(bind("#e", "/env", MREPL|MCREATE) < 0)
@@ -65,14 +66,13 @@
 		panic("bind #I: %r");
 	if(bind("#U", "/root", MREPL|MCREATE) < 0)
 		panic("bind #U: %r");
-    if(bind("/root", "/", MAFTER) < 0)
-		panic("bind /root: %r");
 	bind("#C", "/", MAFTER);
+	//bind("#F", "/fd", MBEFORE)
 
 	/* We have to be a bit pedantic about the fds, leave 0, 1, 2 alone */
 	ifd = open("/dev/null", OREAD);
 	ofd = open("/dev/null", OWRITE);
-	efd = open("/devnull", OWRITE);
+	efd = open("/dev/null", OWRITE);
 	if(argc == 2 && strcmp(argv[1], "-D") == 0)
 		dfd = create("/Users/halfwit/Work/drawcpu/error.log", OWRITE, 0644);
 	else
@@ -87,6 +87,9 @@
 		". <{n=`{read} && ! ~ $#n 0 && read -c $n} >[2=1]"	
 	};
 
+	if(bind("/root", "/", MAFTER) < 0)
+		panic("bind /root: %r");
+	
 	runcommand(3, cmd, dfd);
 	_exit(0);
 }
--- /dev/null
+++ b/posix-arm64/mem.h
@@ -1,0 +1,152 @@
+#define AOUT_MAGIC  (R_MAGIC)
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+#define KiB		1024u			/* Kibi 0x0000000000000400 */
+#define MiB		1048576u		/* Mebi 0x0000000000100000 */
+#define GiB		1073741824u		/* Gibi 000000000040000000 */
+
+/*
+ * Sizes:
+ * 	L0	L1	L2	L3
+ *	4K	2M	1G	512G
+ *	16K	32M	64G	128T
+ *	64K	512M	4T	-
+ */
+#define	PGSHIFT		12		/* log(BY2PG) */
+#define	BY2PG		(1ULL<<PGSHIFT)	/* bytes per page */
+#define	ROUND(s, sz)	(((s)+(sz-1))&~(sz-1))
+#define	PGROUND(s)	ROUND(s, BY2PG)
+
+/* effective virtual address space */
+#define EVASHIFT	36
+#define EVAMASK		((1ULL<<EVASHIFT)-1)
+
+#define PTSHIFT		(PGSHIFT-3)
+#define PTLEVELS	(((EVASHIFT-PGSHIFT)+PTSHIFT-1)/PTSHIFT)	
+#define PTLX(v, l)	((((v) & EVAMASK) >> (PGSHIFT + (l)*PTSHIFT)) & ((1 << PTSHIFT)-1))
+#define PGLSZ(l)	(1ULL << (PGSHIFT + (l)*PTSHIFT))
+
+#define PTL1X(v, l)	(L1TABLEX(v, l) | PTLX(v, l))
+#define L1TABLEX(v, l)	(L1TABLE(v, l) << PTSHIFT)
+#define L1TABLES	((-KSEG0+PGLSZ(2)-1)/PGLSZ(2))
+#define L1TABLE(v, l)	(L1TABLES - ((PTLX(v, 2) % L1TABLES) >> (((l)-1)*PTSHIFT)) + (l)-1)
+#define L1TOPSIZE	(1ULL << (EVASHIFT - PTLEVELS*PTSHIFT))
+
+#define	MAXMACH		16			/* max # cpus system can run */
+#define	MACHSIZE	(8*KiB)
+
+#define KSTACK		(8*KiB)
+#define STACKALIGN(sp)	((sp) & ~7)		/* bug: assure with alloc */
+#define TRAPFRAMESIZE	(38*8)
+
+#define DTBADDR		0x40000000
+
+#define VDRAM		(0xFFFFFFFFC0000000ULL)	/* 0x40000000 - 0x80000000 */
+#define	KTZERO		(VDRAM + 0x100000)	/* 0x40100000 - kernel text start */
+
+#define PHYSIO		0x8000000
+#define PHYSIOEND	0x10000000
+
+#define	VIRTIO		(0xFFFFFFFFB0000000ULL)
+
+#define	KZERO		(0xFFFFFFFF80000000ULL)	/* 0x00000000 - kernel address space */
+
+#define VMAP		(0xFFFFFFFF00000000ULL)	/* 0x00000000 - 0x40000000 */
+
+#define KMAPEND		(0xFFFFFFFF00000000ULL)	/* 0x140000000 */
+#define KMAP		(0xFFFFFFFE00000000ULL)	/*  0x40000000 */
+
+#define KLIMIT		(VDRAM - KZERO + KMAPEND - KMAP)	/* 0x140000000 */
+
+#define KSEG0		(0xFFFFFFFE00000000ULL)
+
+/* temporary identity map for TTBR0 (using only top-level) */
+#define L1BOT		((L1-L1TOPSIZE)&-BY2PG)
+
+/* shared kernel page table for TTBR1 */
+#define L1		(L1TOP-L1SIZE)
+#define L1SIZE		((L1TABLES+PTLEVELS-2)*BY2PG)
+#define L1TOP		((MACHADDR(MAXMACH-1)-L1TOPSIZE)&-BY2PG)
+
+#define MACHADDR(n)	(KTZERO-((n)+1)*MACHSIZE)
+
+#define CONFADDR	(VDRAM + 0x10000)	/* 0x40010000 */
+
+#define BOOTARGS	((char*)CONFADDR)
+#define BOOTARGSLEN	0x10000
+
+#define	REBOOTADDR	(VDRAM-KZERO + 0x20000)	/* 0x40020000 */
+
+#define	UZERO		0ULL			/* user segment */
+#define	UTZERO		(UZERO+0x10000)		/* user text start */
+#define	USTKTOP		((EVAMASK>>1)-0xFFFF)	/* user segment end +1 */
+#define	USTKSIZE	(16*1024*1024)		/* user stack size */
+
+#define BLOCKALIGN	64			/* only used in allocb.c */
+
+/*
+ * Sizes
+ */
+#define BI2BY		8			/* bits per byte */
+#define BY2SE		4
+#define BY2WD		8
+#define BY2V		8			/* only used in xalloc.c */
+
+#define	PTEMAPMEM	(1024*1024)
+#define	PTEPERTAB	(PTEMAPMEM/BY2PG)
+#define	SEGMAPSIZE	8192
+#define	SSEGMAPSIZE	16
+#define	PPN(x)		((x)&~(BY2PG-1))
+
+#define SHARE_NONE	0
+#define SHARE_OUTER	2
+#define SHARE_INNER	3
+
+#define CACHE_UC	0
+#define CACHE_WB	1
+#define CACHE_WT	2
+#define CACHE_WB_NA	3
+
+#define MA_MEM_WB	0
+#define MA_MEM_WT	1
+#define MA_MEM_UC	2
+#define MA_DEV_nGnRnE	3
+#define MA_DEV_nGnRE	4
+#define MA_DEV_nGRE	5
+#define MA_DEV_GRE	6
+
+#define	PTEVALID	1
+#define PTEBLOCK	0
+#define PTETABLE	2
+#define PTEPAGE		2
+
+#define PTEMA(x)	((x)<<2)
+#define PTEAP(x)	((x)<<6)
+#define PTESH(x)	((x)<<8)
+
+#define PTEAF		(1<<10)
+#define PTENG		(1<<11)
+#define PTEPXN		(1ULL<<53)
+#define PTEUXN		(1ULL<<54)
+
+#define PTEKERNEL	PTEAP(0)
+#define PTEUSER		PTEAP(1)
+#define PTEWRITE	PTEAP(0)
+#define PTERONLY	PTEAP(2)
+#define PTENOEXEC	(PTEPXN|PTEUXN)
+
+#define PTECACHED	PTEMA(MA_MEM_WB)
+#define PTEWT		PTEMA(MA_MEM_WT)
+#define PTEUNCACHED	PTEMA(MA_MEM_UC)
+#define PTEDEVICE	PTEMA(MA_DEV_nGnRE)
+
+/*
+ * Physical machine information from here on.
+ *	PHYS addresses as seen from the arm cpu.
+ *	BUS  addresses as seen from peripherals
+ */
+#define	PHYSDRAM	0
+
+#define MIN(a, b)	((a) < (b)? (a): (b))
+#define MAX(a, b)	((a) > (b)? (a): (b))
\ No newline at end of file
--- a/rc/drawcpu.c
+++ b/rc/drawcpu.c
@@ -56,12 +56,13 @@
 static void
 execfinit(void)
 {
-	char *cmds = estrdup("for(i in '/env/fn#'*){. -bq $i}\n");
-	int line = runq->line;
-	poplist();
-	execcmds(openiocore(cmds, strlen(cmds)), estrdup(srcfile(runq)), runq->local, runq->redir);
-	runq->lex->line = line;
-	runq->lex->qflag = 1;
+	// TODO: /env/fn# is empty
+	//char *cmds = estrdup("for(i in '/env/fn#'*){. -bq $i}\n");
+	//int line = runq->line;
+	//poplist();
+	//execcmds(openiocore(cmds, strlen(cmds)), estrdup(srcfile(runq)), runq->local, runq->redir);
+	//runq->lex->line = line;
+	//runq->lex->qflag = 1;
 }
 
 static void
@@ -263,9 +264,7 @@
 void
 Exec(char **argv)
 {
-	// TODO: this will call our vm code
-	// execvm();
-	execvp(argv[0], argv+1);
+	exec(argv);
 }
 
 int