X-Git-Url: http://git.megacz.com/?p=nestedvm.git;a=blobdiff_plain;f=src%2Forg%2Fibex%2Fnestedvm%2FRuntime.java;h=a745084176b0c32375ca143ec6263686f2b57894;hp=902e377ce2365a27675fc1c18330caa7bbf028f1;hb=c59b7cfc7a6b67574d38c5c8eb7732bad37236b0;hpb=777acd138d857049f58620b5c2003733798e69f0 diff --git a/src/org/ibex/nestedvm/Runtime.java b/src/org/ibex/nestedvm/Runtime.java index 902e377..a745084 100644 --- a/src/org/ibex/nestedvm/Runtime.java +++ b/src/org/ibex/nestedvm/Runtime.java @@ -11,92 +11,80 @@ import java.util.Arrays; // FEATURE: Look over the public API, make sure we're exposing a bare minimum // (we might make this an interface in the future) -public abstract class Runtime implements UsermodeConstants,Registers { - /** Pages are 4k in size */ - protected final int PAGE_SIZE; - protected final int PAGE_WORDS; - protected final int PAGE_SHIFT; - protected final int TOTAL_PAGES; - /** This is the upper limit of the pages allocated by the sbrk() syscall. */ - protected final int BRK_LIMIT; - protected final int STACK_BOTTOM; - - /** This is the maximum size of command line arguments */ - public final static int ARGS_MAX = 1024*1024; - - /** True if we allow empty pages (_emptyPage) to exist in memory. - Empty pages are pages which are allocated by the program but do not contain any - data yet (they are all 0s). If empty pages are allowed subclasses must always - access main memory with the memRead and memWrite functions */ - private final boolean allowEmptyPages; - /** the "empty page" */ - private final static int[] _emptyPage = new int[0]; - - protected final static boolean isEmptyPage(int[] page) { return page == _emptyPage; } - - /** Returns a new empty page (_emptyPage is empty pages are enabled or a new zero'd page) */ - private final int[] emptyPage() { return allowEmptyPages ? _emptyPage : new int[PAGE_WORDS]; } +public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { + /** Number of bits to shift to get the page number (1<<state */ protected abstract void setCPUState(CPUState state); - - static void checkPageSize(int pageSize, int totalPages) throws IllegalArgumentException { - if(pageSize < 256) throw new IllegalArgumentException("pageSize too small"); - if((pageSize&(pageSize-1)) != 0) throw new IllegalArgumentException("pageSize must be a power of two"); - if((totalPages&(totalPages-1)) != 0) throw new IllegalArgumentException("totalPages must be a power of two"); - if(totalPages != 1 && totalPages < 256) throw new IllegalArgumentException("totalPages too small"); - if(totalPages * pageSize < 4*1024*1024) throw new IllegalArgumentException("total memory too small (" + totalPages + "*" + pageSize + ")"); + + protected Object clone() throws CloneNotSupportedException { + Runtime r = (Runtime) super.clone(); + r._byteBuf = null; + r.startTime = 0; + r.fds = new FD[OPEN_MAX]; + for(int i=0;i>>_pageShift != 1) _pageShift++; + pageShift = _pageShift; - PAGE_SIZE = pageSize; - PAGE_WORDS = pageSize>>>2; - int pageShift = 0; - while(pageSize>>>pageShift != 1) pageShift++; - PAGE_SHIFT = pageShift; + int heapStart = heapStart(); + int totalMemory = totalPages * pageSize; + int stackSize = max(totalMemory/512,ARG_MAX+65536); + int stackPages = 0; + if(totalPages > 1) { + stackSize = max(stackSize,pageSize); + stackSize = (stackSize + pageSize - 1) & ~(pageSize-1); + stackPages = stackSize >>> pageShift; + heapStart = (heapStart + pageSize) & ~(pageSize-1); + if(stackPages + STACK_GUARD_PAGES + (heapStart >>> pageShift) >= totalPages) + throw new IllegalArgumentException("total pages too small"); + } else { + if(pageSize < heapStart + stackSize) throw new IllegalArgumentException("total memory too small"); + heapStart = (heapStart + 4095) & ~4096; + } - TOTAL_PAGES = totalPages; + stackBottom = totalMemory - stackSize; + heapEnd = heapStart; - readPages = new int[TOTAL_PAGES][]; - writePages = new int[TOTAL_PAGES][]; + readPages = new int[totalPages][]; + writePages = new int[totalPages][]; - if(TOTAL_PAGES == 1) { - readPages[0] = writePages[0] = new int[PAGE_WORDS]; - BRK_LIMIT = STACK_BOTTOM = 0; + if(totalPages == 1) { + readPages[0] = writePages[0] = new int[pageSize>>2]; } else { - int stackPages = max(TOTAL_PAGES>>>8,(1024*1024)>>>PAGE_SHIFT); - STACK_BOTTOM = (TOTAL_PAGES - stackPages) * PAGE_SIZE; - // leave some unmapped pages between the stack and the heap - BRK_LIMIT = STACK_BOTTOM - 4*PAGE_SIZE; - - for(int i=0;i>> pageShift);i>2]; + } } - + addFD(new StdinFD(System.in)); addFD(new StdoutFD(System.out)); addFD(new StdoutFD(System.err)); @@ -158,10 +165,13 @@ public abstract class Runtime implements UsermodeConstants,Registers { /** Copy everything from src to addr initializing uninitialized pages if required. Newly initalized pages will be marked read-only if ro is set */ protected final void initPages(int[] src, int addr, boolean ro) { + int pageWords = (1<>>2; + int pageMask = (1<>> PAGE_SHIFT; - int start = (addr&(PAGE_SIZE-1))>>2; - int elements = min(PAGE_WORDS-start,src.length-i); + int page = addr >>> pageShift; + int start = (addr&pageMask)>>2; + int elements = min(pageWords-start,src.length-i); if(readPages[page]==null) { initPage(page,ro); } else if(!ro) { @@ -175,12 +185,15 @@ public abstract class Runtime implements UsermodeConstants,Registers { /** Initialize words of pages starting at addr to 0 */ protected final void clearPages(int addr, int words) { + int pageWords = (1<>>2; + int pageMask = (1<>> PAGE_SHIFT; - int start = (addr&(PAGE_SIZE-1))>>2; - int elements = min(PAGE_WORDS-start,words-i); + int page = addr >>> pageShift; + int start = (addr&pageMask)>>2; + int elements = min(pageWords-start,words-i); if(readPages[page]==null) { - readPages[page] = writePages[page] = emptyPage(); + readPages[page] = writePages[page] = new int[pageWords]; } else { if(writePages[page] == null) writePages[page] = readPages[page]; for(int j=start;jlength bytes from the processes memory space starting at addr INTO a java byte array a */ public final void copyin(int addr, byte[] buf, int count) throws ReadFaultException { + int pageWords = (1<>>2; + int pageMask = pageWords - 1; + int x=0; + if(count == 0) return; if((addr&3)!=0) { int word = memRead(addr&~3); switch(addr&3) { @@ -207,16 +224,14 @@ public abstract class Runtime implements UsermodeConstants,Registers { int c = count>>>2; int a = addr>>>2; while(c != 0) { - int[] page = readPages[a >>> (PAGE_SHIFT-2)]; + int[] page = readPages[a >>> (pageShift-2)]; if(page == null) throw new ReadFaultException(a<<2); - int index = a&(PAGE_WORDS-1); - int n = min(c,PAGE_WORDS-index); - if(page != _emptyPage) { - for(int i=0;i>>24)&0xff); buf[x+1] = (byte)((word>>>16)&0xff); - buf[x+2] = (byte)((word>>> 8)&0xff); buf[x+3] = (byte)((word>>> 0)&0xff); - } + int index = a&pageMask; + int n = min(c,pageWords-index); + for(int i=0;i>>24)&0xff); buf[x+1] = (byte)((word>>>16)&0xff); + buf[x+2] = (byte)((word>>> 8)&0xff); buf[x+3] = (byte)((word>>> 0)&0xff); } a += n; c -=n; } @@ -235,7 +250,11 @@ public abstract class Runtime implements UsermodeConstants,Registers { /** Copies length bytes OUT OF the java array a into the processes memory space at addr */ public final void copyout(byte[] buf, int addr, int count) throws FaultException { + int pageWords = (1<>>2; + int pageWordMask = pageWords - 1; + int x=0; + if(count == 0) return; if((addr&3)!=0) { int word = memRead(addr&~3); switch(addr&3) { @@ -246,21 +265,22 @@ public abstract class Runtime implements UsermodeConstants,Registers { memWrite(addr&~3,word); addr += x; } + if((count&~3) != 0) { int c = count>>>2; int a = addr>>>2; while(c != 0) { - int[] page = writePages[a >>> (PAGE_SHIFT-2)]; + int[] page = writePages[a >>> (pageShift-2)]; if(page == null) throw new WriteFaultException(a<<2); - if(page == _emptyPage) page = initPage(a >>> (PAGE_SHIFT-2)); - int index = a&(PAGE_WORDS-1); - int n = min(c,PAGE_WORDS-index); + int index = a&pageWordMask; + int n = min(c,pageWords-index); for(int i=0;i>>2; + int pageWordMask = pageWords - 1; + if((dst&3) == 0 && (src&3)==0) { if((count&~3) != 0) { int c = count>>2; int s = src>>>2; int d = dst>>>2; while(c != 0) { - int[] srcPage = readPages[s>>>(PAGE_SHIFT-2)]; + int[] srcPage = readPages[s>>>(pageShift-2)]; if(srcPage == null) throw new ReadFaultException(s<<2); - int[] dstPage = writePages[d>>>(PAGE_SHIFT-2)]; + int[] dstPage = writePages[d>>>(pageShift-2)]; if(dstPage == null) throw new WriteFaultException(d<<2); - int srcIndex = (s&(PAGE_WORDS-1)); - int dstIndex = (d&(PAGE_WORDS-1)); - int n = min(c,PAGE_WORDS-max(srcIndex,dstIndex)); - if(srcPage != _emptyPage) { - if(dstPage == _emptyPage) dstPage = initPage(d>>>(PAGE_SHIFT-2)); - System.arraycopy(srcPage,srcIndex,dstPage,dstIndex,n); - } else if(srcPage == _emptyPage && dstPage != _emptyPage) { - Arrays.fill(dstPage,dstIndex,dstIndex+n,0); - } + int srcIndex = s&pageWordMask; + int dstIndex = d&pageWordMask; + int n = min(c,pageWords-max(srcIndex,dstIndex)); + System.arraycopy(srcPage,srcIndex,dstPage,dstIndex,n); s += n; d += n; c -= n; } src = s<<2; dst = d<<2; count&=3; @@ -317,6 +335,9 @@ public abstract class Runtime implements UsermodeConstants,Registers { } public final void memset(int addr, int ch, int count) throws FaultException { + int pageWords = (1<>>2; + int pageWordMask = pageWords - 1; + int fourBytes = ((ch&0xff)<<24)|((ch&0xff)<<16)|((ch&0xff)<<8)|((ch&0xff)<<0); if((addr&3)!=0) { int word = memRead(addr&~3); @@ -332,14 +353,11 @@ public abstract class Runtime implements UsermodeConstants,Registers { int c = count>>2; int a = addr>>>2; while(c != 0) { - int[] page = readPages[a>>>(PAGE_SHIFT-2)]; + int[] page = readPages[a>>>(pageShift-2)]; if(page == null) throw new WriteFaultException(a<<2); - int index = (a&(PAGE_WORDS-1)); - int n = min(c,PAGE_WORDS-index); - if(page != _emptyPage || ch != 0) { - if(page == _emptyPage) page = initPage(a>>>(PAGE_SHIFT-2)); - Arrays.fill(page,index,index+n,fourBytes); - } + int index = a&pageWordMask; + int n = min(c,pageWords-index); + Arrays.fill(page,index,index+n,fourBytes); a += n; c -= n; } addr = a<<2; count&=3; @@ -362,16 +380,13 @@ public abstract class Runtime implements UsermodeConstants,Registers { } protected final int unsafeMemRead(int addr) throws ReadFaultException { - int page = addr >>> PAGE_SHIFT; - int entry = (addr >>> 2) & (PAGE_WORDS-1); + int page = addr >>> pageShift; + int entry = (addr&(1<>2; try { return readPages[page][entry]; } catch(ArrayIndexOutOfBoundsException e) { - if(page < 0) throw e; // should never happen - if(page >= readPages.length) throw new ReadFaultException(addr); - if(readPages[page] != _emptyPage) throw e; // should never happen - initPage(page); - return 0; + if(page < 0 || page >= readPages.length) throw new ReadFaultException(addr); + throw e; // should never happen } catch(NullPointerException e) { throw new ReadFaultException(addr); } @@ -384,16 +399,13 @@ public abstract class Runtime implements UsermodeConstants,Registers { } protected final void unsafeMemWrite(int addr, int value) throws WriteFaultException { - int page = addr >>> PAGE_SHIFT; - int entry = (addr>>>2)&(PAGE_WORDS-1); + int page = addr >>> pageShift; + int entry = (addr&(1<>2; try { writePages[page][entry] = value; } catch(ArrayIndexOutOfBoundsException e) { - if(page < 0) throw e;// should never happen - if(page >= writePages.length) throw new WriteFaultException(addr); - if(readPages[page] != _emptyPage) throw e; // should never happen - initPage(page); - writePages[page][entry] = value; + if(page < 0 || page >= writePages.length) throw new WriteFaultException(addr); + throw e; // should never happen } catch(NullPointerException e) { throw new WriteFaultException(addr); } @@ -403,7 +415,7 @@ public abstract class Runtime implements UsermodeConstants,Registers { private final int[] initPage(int page) { return initPage(page,false); } /** Created a new non-empty page at page number page. If ro is set the page will be read-only */ private final int[] initPage(int page, boolean ro) { - int[] buf = new int[PAGE_WORDS]; + int[] buf = new int[(1<>>2]; writePages[page] = ro ? null : buf; readPages[page] = buf; return buf; @@ -412,15 +424,14 @@ public abstract class Runtime implements UsermodeConstants,Registers { /** Returns the exit status of the process. (only valid if state == DONE) @see Runtime#state */ public final int exitStatus() { - if(state != DONE) throw new IllegalStateException("exitStatus() called in an inappropriate state"); + if(state != EXITED) throw new IllegalStateException("exitStatus() called in an inappropriate state"); return exitStatus; } - private int addStringArray(String[] strings, int topAddr) { + private int addStringArray(String[] strings, int topAddr) throws FaultException { int count = strings.length; int total = 0; /* null last table entry */ for(int i=0;i= ARGS_MAX) throw new IllegalArgumentException("arguments/environ too big"); total += (count+1)*4; int start = (topAddr - total)&~3; int addr = start + (count+1)*4; @@ -453,19 +464,19 @@ public abstract class Runtime implements UsermodeConstants,Registers { * and location of the user_info table from the ELF symbol table. setUserInfo and * getUserInfo are used to modify the words in the user_info table. */ public void setUserInfo(int index, int word) { - if(index < 0 || index >= userInfoSize/4) throw new IndexOutOfBoundsException("setUserInfo called with index >= " + (userInfoSize/4)); + if(index < 0 || index >= userInfoSize()/4) throw new IndexOutOfBoundsException("setUserInfo called with index >= " + (userInfoSize()/4)); try { - memWrite(userInfoBase+index*4,word); - } catch(FaultException e) { throw new Error("should never happen: " + e); } + memWrite(userInfoBase()+index*4,word); + } catch(FaultException e) { throw new RuntimeException(e.toString()); } } /** Returns the word in the _user_info table entry index @see Runtime#setUserInfo(int,int) setUserInfo */ public int getUserInfo(int index) { - if(index < 0 || index >= userInfoSize/4) throw new IndexOutOfBoundsException("setUserInfo called with index >= " + (userInfoSize/4)); + if(index < 0 || index >= userInfoSize()/4) throw new IndexOutOfBoundsException("setUserInfo called with index >= " + (userInfoSize()/4)); try { - return memRead(userInfoBase+index*4); - } catch(FaultException e) { throw new Error("should never happen: " + e); } + return memRead(userInfoBase()+index*4); + } catch(FaultException e) { throw new RuntimeException(e.toString()); } } /** Calls _execute() (subclass's execute()) and catches exceptions */ @@ -490,17 +501,20 @@ public abstract class Runtime implements UsermodeConstants,Registers { if(startTime == 0) startTime = System.currentTimeMillis(); state = RUNNING; __execute(); - if(state != PAUSED && state != DONE) throw new IllegalStateException("execute() ended up in an inappropriate state (" + state + ")"); - return state == DONE; + if(state != PAUSED && state != EXITED && state != EXECED) + throw new IllegalStateException("execute() ended up in an inappropriate state (" + state + ")"); + return state != PAUSED; } - public final int run() { return run(null); } - public final int run(String argv0, String[] rest) { - String[] args = new String[rest.length+1]; - System.arraycopy(rest,0,args,1,rest.length); - args[0] = argv0; - return run(args); + protected static String[] concatArgv(String argv0, String[] rest) { + String[] argv = new String[rest.length+1]; + System.arraycopy(rest,0,argv,1,rest.length); + argv[0] = argv0; + return argv; } + + public final int run() { return run(null); } + public final int run(String argv0, String[] rest) { return run(concatArgv(argv0,rest)); } public final int run(String[] args) { return run(args,null); } /** Runs the process until it exits and returns the exit status. @@ -510,9 +524,9 @@ public abstract class Runtime implements UsermodeConstants,Registers { for(;;) { if(execute()) break; System.err.println("WARNING: Pause requested while executing run()"); - try { Thread.sleep(500); } catch(InterruptedException e) { /* noop */ } } - return exitStatus(); + if(state == EXECED) System.err.println("WARNING: Process exec()ed while being run under run()"); + return state == EXITED ? exitStatus() : 0; } public final void start() { start(null); } @@ -520,82 +534,120 @@ public abstract class Runtime implements UsermodeConstants,Registers { /** Initializes the process and prepairs it to be executed with execute() */ public final void start(String[] args, String[] environ) { - int sp, argsAddr, envAddr; - if(state != INITIALIZED) throw new IllegalStateException("start() called in inappropriate state"); + int top, sp, argsAddr, envAddr; + if(state != STOPPED) throw new IllegalStateException("start() called in inappropriate state"); if(args == null) args = new String[]{getClass().getName()}; - sp = TOTAL_PAGES*PAGE_SIZE-512; - sp = argsAddr = addStringArray(args,sp); - sp = envAddr = addStringArray(createEnv(environ),sp); + sp = top = writePages.length*(1< ARG_MAX) throw new IllegalArgumentException("args/environ too big"); + CPUState cpuState = new CPUState(); cpuState.r[A0] = argsAddr; cpuState.r[A1] = envAddr; cpuState.r[SP] = sp; cpuState.r[RA] = 0xdeadbeef; - cpuState.r[GP] = gp; - cpuState.pc = entryPoint; + cpuState.r[GP] = gp(); + cpuState.pc = entryPoint(); setCPUState(cpuState); - + state = PAUSED; - _start(); + _started(); } /** Hook for subclasses to do their own startup */ - protected void _start() { /* noop */ } + protected void _started() { } - // FEATURE: call() that accepts an Object[] array and automatically allocates strings/arrays/etc on the stack - public final int call(String sym) throws CallException { return call(sym,0,0,0,0,0,0,0); } - public final int call(String sym, int a0) throws CallException { return call(sym,a0,0,0,0,0,0,0); } - public final int call(String sym, int a0, int a1) throws CallException { return call(sym,a0,a1,0,0,0,0,0); } - public final int call(String sym, int a0, int a1, int a2) throws CallException { return call(sym,a0,a1,a2,0,0,0,0); } - public final int call(String sym, int a0, int a1, int a2, int a3) throws CallException { return call(sym,a0,a1,a2,a3,0,0,0); } - public final int call(String sym, int a0, int a1, int a2, int a3, int a4) throws CallException { return call(sym,a0,a1,a2,a3,a4,0,0); } - public final int call(String sym, int a0, int a1, int a2, int a3, int a4, int a5) throws CallException { return call(sym,a0,a1,a2,a3,a4,a5,0); } + public final int call(String sym, Object[] args) throws CallException, FaultException { + if(state != PAUSED && state != CALLJAVA) throw new IllegalStateException("call() called in inappropriate state"); + if(args.length > 7) throw new IllegalArgumentException("args.length > 7"); + CPUState state = new CPUState(); + getCPUState(state); + + int sp = state.r[SP]; + int[] ia = new int[args.length]; + for(int i=0;iaddr in the process setting A0-A3 and S0-S3 to the given arguments and returns the contents of V1 when the the pause syscall is invoked */ - public final int call(int addr, int a0, int a1, int a2, int a3, int s0, int s1, int s2, int s3) { + //public final int call(int addr, int a0, int a1, int a2, int a3, int s0, int s1, int s2, int s3) { + public final int call(int addr, int a0, int[] rest) throws CallException { + if(rest.length > 7) throw new IllegalArgumentException("rest.length > 7"); if(state != PAUSED && state != CALLJAVA) throw new IllegalStateException("call() called in inappropriate state"); int oldState = state; - CPUState saved = getCPUState(); - CPUState cpustate = new CPUState(); - cpustate.r[SP] = saved.r[SP]&~15; + CPUState saved = new CPUState(); + getCPUState(saved); + CPUState cpustate = saved.dup(); + + cpustate.r[SP] = cpustate.r[SP]&~15; cpustate.r[RA] = 0xdeadbeef; cpustate.r[A0] = a0; - cpustate.r[A1] = a1; - cpustate.r[A2] = a2; - cpustate.r[A3] = a3; - cpustate.r[S0] = s0; - cpustate.r[S1] = s1; - cpustate.r[S2] = s2; - cpustate.r[S3] = s3; - cpustate.r[GP] = gp; + switch(rest.length) { + case 7: cpustate.r[S3] = rest[6]; + case 6: cpustate.r[S2] = rest[5]; + case 5: cpustate.r[S1] = rest[4]; + case 4: cpustate.r[S0] = rest[3]; + case 3: cpustate.r[A3] = rest[2]; + case 2: cpustate.r[A2] = rest[1]; + case 1: cpustate.r[A1] = rest[0]; + } cpustate.pc = addr; state = RUNNING; setCPUState(cpustate); __execute(); - cpustate = getCPUState(); + getCPUState(cpustate); setCPUState(saved); - if(state != PAUSED) - System.out.println("WARNING: Process exit()ed while servicing a call() request"); - else - state = oldState; + if(state != PAUSED) throw new CallException("Process exit()ed while servicing a call() request"); + state = oldState; return cpustate.r[V1]; } @@ -611,15 +663,18 @@ public abstract class Runtime implements UsermodeConstants,Registers { Returns -1 if the table is full. This can be used by subclasses to use custom file descriptors */ public int addFD(FD fd) { + if(state == EXITED || state == EXECED) throw new IllegalStateException("addFD called in inappropriate state"); int i; for(i=0;ifdn and removes it from the file descriptor table */ public boolean closeFD(int fdn) { + if(state == EXITED || state == EXECED) throw new IllegalStateException("closeFD called in inappropriate state"); if(fdn < 0 || fdn >= OPEN_MAX) return false; if(fds[fdn] == null) return false; fds[fdn].close(); @@ -638,7 +693,6 @@ public abstract class Runtime implements UsermodeConstants,Registers { return i; } - // FEATURE: These should be pulled in from UsermodeConstants but fcntl.h is hard to parse public static final int RD_ONLY = 0; public static final int WR_ONLY = 1; public static final int RDWR = 2; @@ -649,53 +703,57 @@ public abstract class Runtime implements UsermodeConstants,Registers { public static final int O_TRUNC = 0x0400; public static final int O_NONBLOCK = 0x4000; - // FEATURE: Lots of duplicate code between this and UnixRuntime.HostFS.open() - protected FD open(String path, int flags, int mode) throws IOException { - final File f = new File(path); - // NOTE: createNewFile is a Java2 function - if((flags & (O_EXCL|O_CREAT)) == (O_EXCL|O_CREAT)) - if(!f.createNewFile()) throw new ErrnoException(EEXIST); - if(!f.exists() && (flags&O_CREAT) == 0) return null; - if(f.isDirectory()) return null; - final Seekable.File sf = new Seekable.File(path,mode!=RD_ONLY); - if((flags&O_TRUNC)!=0) sf.setLength(0); - return new SeekableFD(sf,flags) { - protected FStat _fstat() { return new HostFStat(f) { - public int size() { - try { return sf.length(); } catch(IOException e) { return 0; } - } - };} - }; - } - - /** The open syscall */ - private int sys_open(int addr, int flags, int mode) { - if((flags & O_NONBLOCK) != 0) { - System.err.println("WARNING: O_NONBLOCK not supported"); - return -EOPNOTSUPP; + FD hostFSOpen(final File f, int flags, int mode) throws ErrnoException { + if((flags & ~(3|O_CREAT|O_EXCL|O_APPEND|O_TRUNC)) != 0) { + System.err.println("WARNING: Unsupported flags passed to open(): " + toHex(flags & ~(3|O_CREAT|O_EXCL|O_APPEND|O_TRUNC))); + throw new ErrnoException(ENOTSUP); } + boolean write = mode!=RD_ONLY; - try { - FD fd = open(cstring(addr),flags,mode); - if(fd == null) return -ENOENT; - int fdn = addFD(fd); - if(fdn == -1) { - fd.close(); - return -ENFILE; + if(sm != null && !(write ? sm.allowWrite(f) : sm.allowRead(f))) throw new ErrnoException(EACCES); + + if((flags & (O_EXCL|O_CREAT)) == (O_EXCL|O_CREAT)) { + try { + // NOTE: createNewFile is a Java2 function + if(!f.createNewFile()) throw new ErrnoException(EEXIST); + } catch(IOException e) { + throw new ErrnoException(EIO); } - return fdn; + } else if(!f.exists()) { + if((flags&O_CREAT)==0) return null; + } else if(f.isDirectory()) { + return hostFSDirFD(f); } - catch(ErrnoException e) { return -e.errno; } - catch(FileNotFoundException e) { - if(e.getMessage() != null && e.getMessage().indexOf("Permission denied") >= 0) return -EACCES; - return -ENOENT; - } - catch(IOException e) { return -EIO; } - catch(FaultException e) { return -EFAULT; } + + final Seekable.File sf; + try { + sf = new Seekable.File(f,write); + } catch(FileNotFoundException e) { + if(e.getMessage() != null && e.getMessage().indexOf("Permission denied") >= 0) throw new ErrnoException(EACCES); + return null; + } catch(IOException e) { throw new ErrnoException(EIO); } + + return new SeekableFD(sf,flags) { protected FStat _fstat() { return hostFStat(f); } }; + } + + FStat hostFStat(File f) { return new HostFStat(f); } + FD hostFSDirFD(File f) { return null; } + + FD _open(String path, int flags, int mode) throws ErrnoException { + return hostFSOpen(new File(path),flags,mode); + } + + /** The open syscall */ + private int sys_open(int addr, int flags, int mode) throws ErrnoException, FaultException { + FD fd = _open(cstring(addr),flags,mode); + if(fd == null) return -ENOENT; + int fdn = addFD(fd); + if(fdn == -1) { fd.close(); return -ENFILE; } + return fdn; } /** The write syscall */ - private int sys_write(int fdn, int addr, int count) { + private int sys_write(int fdn, int addr, int count) throws FaultException { count = Math.min(count,MAX_CHUNK); if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD; if(fds[fdn] == null || !fds[fdn].writable()) return -EBADFD; @@ -703,19 +761,15 @@ public abstract class Runtime implements UsermodeConstants,Registers { byte[] buf = byteBuf(count); copyin(addr,buf,count); return fds[fdn].write(buf,0,count); - } catch(FaultException e) { - System.err.println(e); - return -EFAULT; } catch(IOException e) { - // FEATURE: We should support signals and send a SIGPIPE + // NOTE: This should really send a SIGPIPE if(e.getMessage().equals("Pipe closed")) return sys_exit(128+13); - System.err.println(e); return -EIO; } } /** The read syscall */ - private int sys_read(int fdn, int addr, int count) { + private int sys_read(int fdn, int addr, int count) throws FaultException { count = Math.min(count,MAX_CHUNK); if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD; if(fds[fdn] == null || !fds[fdn].readable()) return -EBADFD; @@ -724,11 +778,7 @@ public abstract class Runtime implements UsermodeConstants,Registers { int n = fds[fdn].read(buf,0,count); copyout(buf,addr,n); return n; - } catch(FaultException e) { - System.err.println(e); - return -EFAULT; } catch(IOException e) { - System.err.println(e); return -EIO; } } @@ -753,32 +803,27 @@ public abstract class Runtime implements UsermodeConstants,Registers { } /** The stat/fstat syscall helper */ - int stat(FStat fs, int addr) { - try { - memWrite(addr+0,(fs.dev()<<16)|(fs.inode()&0xffff)); // st_dev (top 16), // st_ino (bottom 16) - memWrite(addr+4,((fs.type()&0xf000))|(fs.mode()&0xfff)); // st_mode - memWrite(addr+8,1<<16); // st_nlink (top 16) // st_uid (bottom 16) - memWrite(addr+12,0); // st_gid (top 16) // st_rdev (bottom 16) - memWrite(addr+16,fs.size()); // st_size - memWrite(addr+20,fs.atime()); // st_atime - // memWrite(addr+24,0) // st_spare1 - memWrite(addr+28,fs.mtime()); // st_mtime - // memWrite(addr+32,0) // st_spare2 - memWrite(addr+36,fs.ctime()); // st_ctime - // memWrite(addr+40,0) // st_spare3 - memWrite(addr+44,fs.blksize()); // st_bklsize; - memWrite(addr+48,fs.blocks()); // st_blocks - // memWrite(addr+52,0) // st_spare4[0] - // memWrite(addr+56,0) // st_spare4[1] - } catch(FaultException e) { - System.err.println(e); - return -EFAULT; - } + int stat(FStat fs, int addr) throws FaultException { + memWrite(addr+0,(fs.dev()<<16)|(fs.inode()&0xffff)); // st_dev (top 16), // st_ino (bottom 16) + memWrite(addr+4,((fs.type()&0xf000))|(fs.mode()&0xfff)); // st_mode + memWrite(addr+8,1<<16); // st_nlink (top 16) // st_uid (bottom 16) + memWrite(addr+12,0); // st_gid (top 16) // st_rdev (bottom 16) + memWrite(addr+16,fs.size()); // st_size + memWrite(addr+20,fs.atime()); // st_atime + // memWrite(addr+24,0) // st_spare1 + memWrite(addr+28,fs.mtime()); // st_mtime + // memWrite(addr+32,0) // st_spare2 + memWrite(addr+36,fs.ctime()); // st_ctime + // memWrite(addr+40,0) // st_spare3 + memWrite(addr+44,fs.blksize()); // st_bklsize; + memWrite(addr+48,fs.blocks()); // st_blocks + // memWrite(addr+52,0) // st_spare4[0] + // memWrite(addr+56,0) // st_spare4[1] return 0; } /** The fstat syscall */ - private int sys_fstat(int fdn, int addr) { + private int sys_fstat(int fdn, int addr) throws FaultException { if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD; if(fds[fdn] == null) return -EBADFD; return stat(fds[fdn].fstat(),addr); @@ -790,17 +835,13 @@ public abstract class Runtime implements UsermodeConstants,Registers { long tv_usec; }; */ - private int sys_gettimeofday(int timevalAddr, int timezoneAddr) { + private int sys_gettimeofday(int timevalAddr, int timezoneAddr) throws FaultException { long now = System.currentTimeMillis(); int tv_sec = (int)(now / 1000); int tv_usec = (int)((now%1000)*1000); - try { - memWrite(timevalAddr+0,tv_sec); - memWrite(timevalAddr+4,tv_usec); - return 0; - } catch(FaultException e) { - return -EFAULT; - } + memWrite(timevalAddr+0,tv_sec); + memWrite(timevalAddr+4,tv_usec); + return 0; } private int sys_sleep(int sec) { @@ -854,31 +895,26 @@ public abstract class Runtime implements UsermodeConstants,Registers { incr is how much to increase the break by */ public int sbrk(int incr) { if(incr < 0) return -ENOMEM; - if(incr==0) return brkAddr; + if(incr==0) return heapEnd; incr = (incr+3)&~3; - int oldBrk = brkAddr; - int newBrk = oldBrk + incr; - if(TOTAL_PAGES == 1) { - CPUState state = getCPUState(); - if(newBrk >= state.r[SP] - 65536) { - System.err.println("WARNING: brk too close to stack pointer"); - return -ENOMEM; - } - } else if(newBrk >= BRK_LIMIT) { - System.err.println("WARNING: Hit BRK_LIMIT"); - return -ENOMEM; - } - if(TOTAL_PAGES != 1) { + int oldEnd = heapEnd; + int newEnd = oldEnd + incr; + if(newEnd >= stackBottom) return -ENOMEM; + + if(writePages.length > 1) { + int pageMask = (1<>> 2; + int start = (oldEnd + pageMask) >>> pageShift; + int end = (newEnd + pageMask) >>> pageShift; try { - for(int i=(oldBrk+PAGE_SIZE-1)>>>PAGE_SHIFT;i<((newBrk+PAGE_SIZE-1)>>>PAGE_SHIFT);i++) - readPages[i] = writePages[i] = emptyPage(); + for(int i=start;i= OPEN_MAX) return -EBADFD; @@ -914,18 +957,18 @@ public abstract class Runtime implements UsermodeConstants,Registers { } - /** Hook for subclasses to do something when the process exits (MUST set state = DONE) */ - protected void _exit() { state = DONE; } + /** Hook for subclasses to do something when the process exits */ + protected void _exited() { } + private int sys_exit(int status) { exitStatus = status; - for(int i=0;i= OPEN_MAX) return -EBADFD; @@ -944,6 +987,11 @@ public abstract class Runtime implements UsermodeConstants,Registers { if(fd.writable() && fd.readable()) flags = 2; else if(fd.writable()) flags = 1; return flags; + case F_SETFD: + closeOnExec[fdn] = arg != 0; + return 0; + case F_GETFD: + return closeOnExec[fdn] ? 1 : 0; default: System.err.println("WARNING: Unknown fcntl command: " + cmd); return -ENOSYS; @@ -955,7 +1003,21 @@ public abstract class Runtime implements UsermodeConstants,Registers { syscall should be the contents of V0 and a, b, c, and d should be the contenst of A0, A1, A2, and A3. The call MAY change the state @see Runtime#state state */ - protected int syscall(int syscall, int a, int b, int c, int d) { + protected final int syscall(int syscall, int a, int b, int c, int d) { + try { + return _syscall(syscall,a,b,c,d); + } catch(ErrnoException e) { + e.printStackTrace(); + return -e.errno; + } catch(FaultException e) { + return -EFAULT; + } catch(RuntimeException e) { + e.printStackTrace(); + throw new Error("Internal Error in _syscall()"); + } + } + + int _syscall(int syscall, int a, int b, int c, int d) throws ErrnoException, FaultException { switch(syscall) { case SYS_null: return 0; case SYS_exit: return sys_exit(a); @@ -1049,7 +1111,12 @@ public abstract class Runtime implements UsermodeConstants,Registers { /** Seek in the filedescriptor. Whence is SEEK_SET, SEEK_CUR, or SEEK_END. Should return -1 on error or the new position. */ public int seek(int n, int whence) throws IOException { return -1; } + /** Return a Seekable object representing this file descriptor (can be read only) + This is required for exec() */ + Seekable seekable() { return null; } + /** Should return true if this is a tty */ + // FEATURE: get rid of the isatty syscall and just do with newlib's dumb isatty.c public boolean isatty() { return false; } private FStat cachedFStat = null; @@ -1078,6 +1145,8 @@ public abstract class Runtime implements UsermodeConstants,Registers { protected abstract FStat _fstat(); + Seekable seekable() { return data; } + public int seek(int n, int whence) throws IOException { switch(whence) { case SEEK_SET: break; @@ -1161,9 +1230,7 @@ public abstract class Runtime implements UsermodeConstants,Registers { private final boolean executable; public HostFStat(File f) { this.f = f; - String name = f.getName(); - // FEATURE: This is ugly.. maybe we should do a file(1) type check - executable = name.endsWith(".mips") || name.endsWith(".sh"); + executable = executable(); } public int dev() { return 1; } public int inode() { return f.getName().hashCode() & 0xffff; } @@ -1179,6 +1246,8 @@ public abstract class Runtime implements UsermodeConstants,Registers { } public int size() { return (int) f.length(); } public int mtime() { return (int)(f.lastModified()/1000); } + + boolean executable() { return false; } } // Exceptions @@ -1204,7 +1273,7 @@ public abstract class Runtime implements UsermodeConstants,Registers { public CallException(String s) { super(s); } } - protected static class ErrnoException extends IOException { + protected static class ErrnoException extends Exception { public int errno; public ErrnoException(int errno) { super("Errno: " + errno); this.errno = errno; } } @@ -1219,11 +1288,30 @@ public abstract class Runtime implements UsermodeConstants,Registers { public int hi, lo; public int fcsr; public int pc; + + public CPUState dup() { + CPUState c = new CPUState(); + c.hi = hi; + c.lo = lo; + c.fcsr = fcsr; + c.pc = pc; + for(int i=0;i<32;i++) { + c.r[i] = r[i]; + c.f[i] = f[i]; + } + return c; + } + } + + public static class SecurityManager { + public boolean allowRead(File f) { return true; } + public boolean allowWrite(File f) { return true; } + public boolean allowStat(File f) { return true; } } // Null pointer check helper function protected final void nullPointerCheck(int addr) throws ExecutionException { - if(TOTAL_PAGES==1 ? addr < 65536 : (addr>>>PAGE_SHIFT) < 16) + if(addr < 65536) throw new ExecutionException("Attempted to dereference a null pointer " + toHex(addr)); } @@ -1243,7 +1331,7 @@ public abstract class Runtime implements UsermodeConstants,Registers { } } - /** Decode an packed string.. FEATURE: document this better */ + /** Decode a packed string */ protected static final int[] decodeData(String s, int words) { if(s.length() % 8 != 0) throw new IllegalArgumentException("string length must be a multiple of 8"); if((s.length() / 8) * 7 < words*4) throw new IllegalArgumentException("string isn't big enough");