X-Git-Url: http://git.megacz.com/?p=nestedvm.git;a=blobdiff_plain;f=src%2Forg%2Fxwt%2Fmips%2FRuntime.java;fp=src%2Forg%2Fxwt%2Fmips%2FRuntime.java;h=0000000000000000000000000000000000000000;hp=b659317c761609b27b953f7ca4b8ff5d8e18453e;hb=c2b2704764af1ade923ba8f15d517b87f9d16189;hpb=90f1aff73ed698ab5992fbd7eb53c1ba7329b1a5 diff --git a/src/org/xwt/mips/Runtime.java b/src/org/xwt/mips/Runtime.java deleted file mode 100644 index b659317..0000000 --- a/src/org/xwt/mips/Runtime.java +++ /dev/null @@ -1,1256 +0,0 @@ -// Copyright 2003 Brian Alliet -// Based on org.xwt.imp.MIPS by Adam Megacz -// Portions Copyright 2003 Adam Megacz - -package org.xwt.mips; - -import org.xwt.mips.util.*; -import java.io.*; -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]; } - - /** Readable main memory pages */ - protected final int[][] readPages; - /** Writable main memory pages. - If the page is writable writePages[x] == readPages[x]; if not writePages[x] == null. */ - protected final int[][] writePages; - - /** The current break between the heap and unallocated memory */ - protected int brkAddr; - - /** The program's entry point */ - protected int entryPoint; - - /** The location of the _user_info block (or 0 is there is none) */ - protected int userInfoBase; - protected int userInfoSize; - - /** The location of the global pointer */ - protected int gp; - - /** When the process started */ - private long startTime; - - /** State constant: There is no program loaded in memory */ - public final static int UNINITIALIZED = 0; - /** Text/Data loaded in memory */ - public final static int INITIALIZED = 1; - /** Program is executing instructions */ - public final static int RUNNING = 2; - /** Prgram has been started but is paused */ - public final static int PAUSED = 3; - /** Program is executing a callJava() method */ - public final static int CALLJAVA = 4; - /** Program has exited (it cannot currently be restarted) */ - public final static int DONE = 5; - - /** The current state (UNINITIALIZED, INITIALIZED, RUNNING, PAUSED, or DONE) */ - protected int state = UNINITIALIZED; - /** @see Runtime#state state */ - public final int getState() { return state; } - - /** The exit status if the process (only valid if state==DONE) - @see Runtime#state */ - protected int exitStatus; - public ExecutionException exitException; - - /** Maximum number of open file descriptors */ - final static int OPEN_MAX = 256; - /** Table containing all open file descriptors. (Entries are null if the fd is not in use */ - FD[] fds = new FD[OPEN_MAX]; - - /** Temporary buffer for read/write operations */ - private byte[] _byteBuf = null; - /** Max size of temporary buffer - @see Runtime#_byteBuf */ - private final static int MAX_CHUNK = 15*1024*1024; - - /** Subclasses should actually execute program in this method. They should continue - executing until state != RUNNING. Only syscall() can modify state. It is safe - to only check the state attribute after a call to syscall() */ - protected abstract void _execute() throws ExecutionException; - - /** Subclasses should return the address of the symbol symbol or -1 it it doesn't exits in this method - This method is only required if the call() function is used */ - protected int lookupSymbol(String symbol) { return -1; } - - /** Subclasses should returns a CPUState object representing the cpu state */ - protected abstract CPUState getCPUState(); - - /** Subclasses should set the CPUState to the state held in 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 Runtime(int pageSize, int totalPages, boolean allowEmptyPages) { - this.allowEmptyPages = allowEmptyPages; - - checkPageSize(pageSize,totalPages); - - PAGE_SIZE = pageSize; - PAGE_WORDS = pageSize>>>2; - int pageShift = 0; - while(pageSize>>>pageShift != 1) pageShift++; - PAGE_SHIFT = pageShift; - - TOTAL_PAGES = totalPages; - - readPages = new int[TOTAL_PAGES][]; - writePages = new int[TOTAL_PAGES][]; - - if(TOTAL_PAGES == 1) { - readPages[0] = writePages[0] = new int[PAGE_WORDS]; - BRK_LIMIT = STACK_BOTTOM = 0; - } 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;isrc 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) { - for(int i=0;i>> PAGE_SHIFT; - int start = (addr&(PAGE_SIZE-1))>>2; - int elements = min(PAGE_WORDS-start,src.length-i); - if(readPages[page]==null) { - initPage(page,ro); - } else if(!ro) { - if(writePages[page] == null) writePages[page] = readPages[page]; - } - System.arraycopy(src,i,readPages[page],start,elements); - i += elements; - addr += elements*4; - } - } - - /** Initialize words of pages starting at addr to 0 */ - protected final void clearPages(int addr, int words) { - for(int i=0;i>> PAGE_SHIFT; - int start = (addr&(PAGE_SIZE-1))>>2; - int elements = min(PAGE_WORDS-start,words-i); - if(readPages[page]==null) { - readPages[page] = writePages[page] = emptyPage(); - } 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 x=0; - if((addr&3)!=0) { - int word = memRead(addr&~3); - switch(addr&3) { - case 1: buf[x++] = (byte)((word>>>16)&0xff); if(--count==0) break; - case 2: buf[x++] = (byte)((word>>> 8)&0xff); if(--count==0) break; - case 3: buf[x++] = (byte)((word>>> 0)&0xff); if(--count==0) break; - } - addr = (addr&~3)+4; - } - if((count&~3) != 0) { - int c = count>>>2; - int a = addr>>>2; - while(c != 0) { - int[] page = readPages[a >>> (PAGE_SHIFT-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); - } - } - a += n; c -=n; - } - addr = a<<2; count &=3; - } - if(count != 0) { - int word = memRead(addr); - switch(count) { - case 3: buf[x+2] = (byte)((word>>>8)&0xff); - case 2: buf[x+1] = (byte)((word>>>16)&0xff); - case 1: buf[x+0] = (byte)((word>>>24)&0xff); - } - } - } - - /** 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 x=0; - if((addr&3)!=0) { - int word = memRead(addr&~3); - switch(addr&3) { - case 1: word = (word&0xff00ffff)|((buf[x++]&0xff)<<16); if(--count==0) break; - case 2: word = (word&0xffff00ff)|((buf[x++]&0xff)<< 8); if(--count==0) break; - case 3: word = (word&0xffffff00)|((buf[x++]&0xff)<< 0); if(--count==0) break; - } - 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)]; - 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); - for(int i=0;i>2; - int s = src>>>2; - int d = dst>>>2; - while(c != 0) { - int[] srcPage = readPages[s>>>(PAGE_SHIFT-2)]; - if(srcPage == null) throw new ReadFaultException(s<<2); - int[] dstPage = writePages[d>>>(PAGE_SHIFT-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); - } - s += n; d += n; c -= n; - } - src = s<<2; dst = d<<2; count&=3; - } - if(count != 0) { - int word1 = memRead(src); - int word2 = memRead(dst); - switch(count) { - case 1: memWrite(dst,(word1&0xff000000)|(word2&0x00ffffff)); break; - case 2: memWrite(dst,(word1&0xffff0000)|(word2&0x0000ffff)); break; - case 3: memWrite(dst,(word1&0xffffff00)|(word2&0x000000ff)); break; - } - } - } else { - while(count > 0) { - int n = min(count,MAX_CHUNK); - byte[] buf = byteBuf(n); - copyin(src,buf,n); - copyout(buf,dst,n); - count -= n; src += n; dst += n; - } - } - } - - public final void memset(int addr, int ch, int count) throws FaultException { - int fourBytes = ((ch&0xff)<<24)|((ch&0xff)<<16)|((ch&0xff)<<8)|((ch&0xff)<<0); - if((addr&3)!=0) { - int word = memRead(addr&~3); - switch(addr&3) { - case 1: word = (word&0xff00ffff)|((ch&0xff)<<16); if(--count==0) break; - case 2: word = (word&0xffff00ff)|((ch&0xff)<< 8); if(--count==0) break; - case 3: word = (word&0xffffff00)|((ch&0xff)<< 0); if(--count==0) break; - } - memWrite(addr&~3,word); - addr = (addr&~3)+4; - } - if((count&~3) != 0) { - int c = count>>2; - int a = addr>>>2; - while(c != 0) { - int[] page = readPages[a>>>(PAGE_SHIFT-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); - } - a += n; c -= n; - } - addr = a<<2; count&=3; - } - if(count != 0) { - int word = memRead(addr); - switch(count) { - case 1: word = (word&0x00ffffff)|(fourBytes&0xff000000); break; - case 2: word = (word&0x0000ffff)|(fourBytes&0xffff0000); break; - case 3: word = (word&0x000000ff)|(fourBytes&0xffffff00); break; - } - memWrite(addr,word); - } - } - - /** Read a word from the processes memory at addr */ - public final int memRead(int addr) throws ReadFaultException { - if((addr & 3) != 0) throw new ReadFaultException(addr); - return unsafeMemRead(addr); - } - - protected final int unsafeMemRead(int addr) throws ReadFaultException { - int page = addr >>> PAGE_SHIFT; - int entry = (addr >>> 2) & (PAGE_WORDS-1); - 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; - } catch(NullPointerException e) { - throw new ReadFaultException(addr); - } - } - - /** Writes a word to the processes memory at addr */ - public final void memWrite(int addr, int value) throws WriteFaultException { - if((addr & 3) != 0) throw new WriteFaultException(addr); - unsafeMemWrite(addr,value); - } - - protected final void unsafeMemWrite(int addr, int value) throws WriteFaultException { - int page = addr >>> PAGE_SHIFT; - int entry = (addr>>>2)&(PAGE_WORDS-1); - 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; - } catch(NullPointerException e) { - throw new WriteFaultException(addr); - } - } - - /** Created a new non-empty writable page at page number page */ - 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]; - writePages[page] = ro ? null : buf; - readPages[page] = buf; - return buf; - } - - /** 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"); - return exitStatus; - } - - private int addStringArray(String[] strings, int topAddr) { - 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; - int[] table = new int[count+1]; - try { - for(int i=0;iindex in the _user_info table to word - * The user_info table is a chunk of memory in the program's memory defined by the - * symbol "user_info". The compiler/interpreter automatically determine the size - * 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)); - try { - memWrite(userInfoBase+index*4,word); - } catch(FaultException e) { throw new Error("should never happen: " + e); } - } - - /** 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)); - try { - return memRead(userInfoBase+index*4); - } catch(FaultException e) { throw new Error("should never happen: " + e); } - } - - /** Calls _execute() (subclass's execute()) and catches exceptions */ - private void __execute() { - try { - _execute(); - } catch(FaultException e) { - e.printStackTrace(); - sys_exit(128+11); // SIGSEGV - exitException = e; - } catch(ExecutionException e) { - e.printStackTrace(); - System.err.println(e); - sys_exit(128+4); // SIGILL - exitException = e; - } - } - - /** Executes the process until the PAUSE syscall is invoked or the process exits. Returns true if the process exited. */ - public final boolean execute() { - if(state != PAUSED) throw new IllegalStateException("execute() called in inappropriate state"); - 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; - } - - 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); - } - public final int run(String[] args) { return run(args,null); } - - /** Runs the process until it exits and returns the exit status. - If the process executes the PAUSE syscall execution will be paused for 500ms and a warning will be displayed */ - public final int run(String[] args, String[] env) { - start(args,env); - for(;;) { - if(execute()) break; - System.err.println("WARNING: Pause requested while executing run()"); - try { Thread.sleep(500); } catch(InterruptedException e) { /* noop */ } - } - return exitStatus(); - } - - public final void start() { start(null); } - public final void start(String[] args) { start(args,null); } - - /** 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"); - - 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 &= ~15; - - 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; - setCPUState(cpuState); - - state = PAUSED; - - _start(); - } - - /** Hook for subclasses to do their own startup */ - protected void _start() { /* noop */ } - - 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); } - - /** Calls a function in the process with the given arguments */ - public final int call(String sym, int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws CallException { - int func = lookupSymbol(sym); - if(func == -1) throw new CallException(sym + " not found"); - int helper = lookupSymbol("_call_helper"); - if(helper == -1) throw new CallException("_call_helper not found"); - return call(helper,func,a0,a1,a2,a3,a4,a5,a6); - } - - /** Executes the code at addr 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) { - 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.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; - cpustate.pc = addr; - - state = RUNNING; - - setCPUState(cpustate); - __execute(); - cpustate = getCPUState(); - setCPUState(saved); - - if(state != PAUSED) - System.out.println("WARNING: Process exit()ed while servicing a call() request"); - else - state = oldState; - - return cpustate.r[V1]; - } - - // FEATURE: This is ugly - we should have some kind of way to specify a callback rather than requiring subclassing - protected int callJava(int a, int b, int c, int d) { - System.err.println("WARNING: Default implementation of callJava() called with args " + toHex(a) + "," + toHex(b) + "," + toHex(c) + "," + toHex(d)); - return 0; - } - - /** Determines if the process can access fileName. The default implementation simply logs - the request and allows it */ - protected boolean allowFileAccess(String fileName, boolean write) { - //System.err.println("Allowing " + (write?"write":"read-only") + " access to " + fileName); - return true; - } - - /** Allocated an entry in the FileDescriptor table for fd and returns the number. - Returns -1 if the table is full. This can be used by subclasses to use custom file - descriptors */ - public int addFD(FD fd) { - int i; - for(i=0;ifdn and removes it from the file descriptor table */ - public boolean closeFD(int fdn) { - if(fdn < 0 || fdn >= OPEN_MAX) return false; - if(fds[fdn] == null) return false; - fds[fdn].close(); - fds[fdn] = null; - return true; - } - - // 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; - - public static final int O_CREAT = 0x0200; - public static final int O_EXCL = 0x0800; - public static final int O_APPEND = 0x0008; - 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 SeekableFile sf = new SeekableFile(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; - } - - try { - 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; - } - 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; } - } - - /** The write syscall */ - private int sys_write(int fdn, int addr, int count) { - count = Math.min(count,MAX_CHUNK); - if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD; - if(fds[fdn] == null || !fds[fdn].writable()) return -EBADFD; - try { - 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 - 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) { - count = Math.min(count,MAX_CHUNK); - if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD; - if(fds[fdn] == null || !fds[fdn].readable()) return -EBADFD; - try { - byte[] buf = byteBuf(count); - 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; - } - } - - /** The close syscall */ - private int sys_close(int fdn) { - return closeFD(fdn) ? 0 : -EBADFD; - } - - - /** The seek syscall */ - private int sys_lseek(int fdn, int offset, int whence) { - if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD; - if(fds[fdn] == null) return -EBADFD; - if(whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END) return -EINVAL; - try { - int n = fds[fdn].seek(offset,whence); - return n < 0 ? -ESPIPE : n; - } catch(IOException e) { - return -ESPIPE; - } - } - - /** 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; - } - return 0; - } - - /** The fstat syscall */ - private int sys_fstat(int fdn, int addr) { - if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD; - if(fds[fdn] == null) return -EBADFD; - return stat(fds[fdn].fstat(),addr); - } - - /* - struct timeval { - long tv_sec; - long tv_usec; - }; - */ - private int sys_gettimeofday(int timevalAddr, int timezoneAddr) { - 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; - } - } - - private int sys_sleep(int sec) { - if(sec < 0) sec = Integer.MAX_VALUE; - try { - Thread.sleep((long)sec*1000); - return 0; - } catch(InterruptedException e) { - return -1; - } - } - - /* - #define _CLOCKS_PER_SEC_ 1000 - #define _CLOCK_T_ unsigned long - struct tms { - clock_t tms_utime; - clock_t tms_stime; - clock_t tms_cutime; - clock_t tms_cstime; - };*/ - - private int sys_times(int tms) { - long now = System.currentTimeMillis(); - int userTime = (int)((now - startTime)/16); - int sysTime = (int)((now - startTime)/16); - - try { - if(tms!=0) { - memWrite(tms+0,userTime); - memWrite(tms+4,sysTime); - memWrite(tms+8,userTime); - memWrite(tms+12,sysTime); - } - } catch(FaultException e) { - return -EFAULT; - } - return (int)now; - } - - private int sys_sysconf(int n) { - switch(n) { - case _SC_CLK_TCK: return 1000; - default: - System.err.println("WARNING: Attempted to use unknown sysconf key: " + n); - return -EINVAL; - } - } - - /** The sbrk syscall. This can also be used by subclasses to allocate memory. - incr is how much to increase the break by */ - public int sbrk(int incr) { - if(incr < 0) return -ENOMEM; - if(incr==0) return brkAddr; - 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) { - try { - for(int i=(oldBrk+PAGE_SIZE-1)>>>PAGE_SHIFT;i<((newBrk+PAGE_SIZE-1)>>>PAGE_SHIFT);i++) - readPages[i] = writePages[i] = emptyPage(); - } catch(OutOfMemoryError e) { - System.err.println("WARNING: Caught OOM Exception in sbrk: " + e); - return -ENOMEM; - } - } - brkAddr = newBrk; - return oldBrk; - } - - /** The getpid syscall */ - private int sys_getpid() { return getPid(); } - protected int getPid() { return 1; } - - private int sys_calljava(int a, int b, int c, int d) { - if(state != RUNNING) throw new IllegalStateException("wound up calling sys_calljava while not in RUNNING"); - state = CALLJAVA; - int ret = callJava(a,b,c,d); - state = RUNNING; - return ret; - } - - private int sys_pause() { - state = PAUSED; - return 0; - } - - private int sys_getpagesize() { return TOTAL_PAGES == 1 ? 4096 : PAGE_SIZE; } - - private int sys_isatty(int fdn) { - if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD; - if(fds[fdn] == null) return -EBADFD; - return fds[fdn].isatty() ? 1 : 0; - } - - - /** Hook for subclasses to do something when the process exits (MUST set state = DONE) */ - protected void _exit() { state = DONE; } - private int sys_exit(int status) { - exitStatus = status; - for(int i=0;i= OPEN_MAX) return -EBADFD; - if(fds[fdn] == null) return -EBADFD; - FD fd = fds[fdn]; - - switch(cmd) { - case F_DUPFD: - if(arg < 0 || arg >= OPEN_MAX) return -EINVAL; - for(i=arg;isyscall 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) { - switch(syscall) { - case SYS_null: return 0; - case SYS_exit: return sys_exit(a); - case SYS_pause: return sys_pause(); - case SYS_write: return sys_write(a,b,c); - case SYS_fstat: return sys_fstat(a,b); - case SYS_sbrk: return sbrk(a); - case SYS_open: return sys_open(a,b,c); - case SYS_close: return sys_close(a); - case SYS_read: return sys_read(a,b,c); - case SYS_lseek: return sys_lseek(a,b,c); - case SYS_getpid: return sys_getpid(); - case SYS_calljava: return sys_calljava(a,b,c,d); - case SYS_gettimeofday: return sys_gettimeofday(a,b); - case SYS_sleep: return sys_sleep(a); - case SYS_times: return sys_times(a); - case SYS_getpagesize: return sys_getpagesize(); - case SYS_isatty: return sys_isatty(a); - case SYS_fcntl: return sys_fcntl(a,b,c); - case SYS_sysconf: return sys_sysconf(a); - - case SYS_kill: - case SYS_fork: - case SYS_pipe: - case SYS_dup2: - case SYS_waitpid: - case SYS_stat: - case SYS_mkdir: - case SYS_getcwd: - case SYS_chdir: - System.err.println("Attempted to use a UnixRuntime syscall in Runtime (" + syscall + ")"); - return -ENOSYS; - default: - System.err.println("Attempted to use unknown syscall: " + syscall); - return -ENOSYS; - } - } - - public int xmalloc(int size) { int p=malloc(size); if(p==0) throw new RuntimeException("malloc() failed"); return p; } - public int xrealloc(int addr,int newsize) { int p=realloc(addr,newsize); if(p==0) throw new RuntimeException("realloc() failed"); return p; } - public int realloc(int addr, int newsize) { try { return call("realloc",addr,newsize); } catch(CallException e) { return 0; } } - public int malloc(int size) { try { return call("malloc",size); } catch(CallException e) { return 0; } } - public void free(int p) { try { if(p!=0) call("free",p); } catch(CallException e) { /*noop*/ } } - - /** Helper function to create a cstring in main memory */ - public int strdup(String s) { - byte[] a; - if(s == null) s = "(null)"; - byte[] a2 = getBytes(s); - a = new byte[a2.length+1]; - System.arraycopy(a2,0,a,0,a2.length); - int addr = malloc(a.length); - if(addr == 0) return 0; - try { - copyout(a,addr,a.length); - } catch(FaultException e) { - free(addr); - return 0; - } - return addr; - } - - /** Helper function to read a cstring from main memory */ - public String cstring(int addr) throws ReadFaultException { - StringBuffer sb = new StringBuffer(); - for(;;) { - int word = memRead(addr&~3); - switch(addr&3) { - case 0: if(((word>>>24)&0xff)==0) return sb.toString(); sb.append((char)((word>>>24)&0xff)); addr++; - case 1: if(((word>>>16)&0xff)==0) return sb.toString(); sb.append((char)((word>>>16)&0xff)); addr++; - case 2: if(((word>>> 8)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 8)&0xff)); addr++; - case 3: if(((word>>> 0)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 0)&0xff)); addr++; - } - } - } - - /** File Descriptor class */ - public static abstract class FD { - private int refCount = 1; - - /** returns true if the fd is readable */ - public boolean readable() { return false; } - /** returns true if the fd is writable */ - public boolean writable() { return false; } - - /** Read some bytes. Should return the number of bytes read, 0 on EOF, or throw an IOException on error */ - public int read(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); } - /** Write. Should return the number of bytes written or throw an IOException on error */ - public int write(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); } - - /** 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; } - - /** Should return true if this is a tty */ - public boolean isatty() { return false; } - - private FStat cachedFStat = null; - public final FStat fstat() { - if(cachedFStat == null) cachedFStat = _fstat(); - return cachedFStat; - } - - protected abstract FStat _fstat(); - - /** Closes the fd */ - public final void close() { if(--refCount==0) _close(); } - protected void _close() { /* noop*/ } - - FD dup() { refCount++; return this; } - } - - /** FileDescriptor class for normal files */ - public abstract static class SeekableFD extends FD { - private final int flags; - private final SeekableData data; - public boolean readable() { return (flags&3) != WR_ONLY; } - public boolean writable() { return (flags&3) != RD_ONLY; } - - SeekableFD(SeekableData data, int flags) { this.data = data; this.flags = flags; } - - protected abstract FStat _fstat(); - - public int seek(int n, int whence) throws IOException { - switch(whence) { - case SEEK_SET: break; - case SEEK_CUR: n += data.pos(); break; - case SEEK_END: n += data.length(); break; - default: return -1; - } - data.seek(n); - return n; - } - - public int write(byte[] a, int off, int length) throws IOException { - // NOTE: There is race condition here but we can't fix it in pure java - if((flags&O_APPEND) != 0) seek(0,SEEK_END); - return data.write(a,off,length); - } - - public int read(byte[] a, int off, int length) throws IOException { - int n = data.read(a,off,length); - return n < 0 ? 0 : n; - } - - protected void _close() { try { data.close(); } catch(IOException e) { /*ignore*/ } } - } - - public static class OutputStreamFD extends FD { - private OutputStream os; - public boolean writable() { return true; } - public OutputStreamFD(OutputStream os) { this.os = os; } - public int write(byte[] a, int off, int length) throws IOException { os.write(a,off,length); return length; } - public void _close() { try { os.close(); } catch(IOException e) { /*ignore*/ } } - public FStat _fstat() { return new FStat(); } - } - - public static class InputStreamFD extends FD { - private InputStream is; - public boolean readable() { return true; } - public InputStreamFD(InputStream is) { this.is = is; } - public int read(byte[] a, int off, int length) throws IOException { int n = is.read(a,off,length); return n < 0 ? 0 : n; } - public void _close() { try { is.close(); } catch(IOException e) { /*ignore*/ } } - public FStat _fstat() { return new FStat(); } - } - - protected static class StdinFD extends InputStreamFD { - public StdinFD(InputStream is) { super(is); } - public void _close() { /* noop */ } - public FStat _fstat() { return new FStat() { public int type() { return S_IFCHR; } }; } - public boolean isatty() { return true; } - } - protected static class StdoutFD extends OutputStreamFD { - public StdoutFD(OutputStream os) { super(os); } - public void _close() { /* noop */ } - public FStat _fstat() { return new FStat() { public int type() { return S_IFCHR; } }; } - public boolean isatty() { return true; } - } - - public static class FStat { - public static final int S_IFIFO = 0010000; - public static final int S_IFCHR = 0020000; - public static final int S_IFDIR = 0040000; - public static final int S_IFREG = 0100000; - - public int dev() { return -1; } - // FEATURE: inode numbers are calculated inconsistently throught the runtime - public int inode() { return hashCode() & 0xfffff; } - public int mode() { return 0; } - public int type() { return S_IFIFO; } - public int nlink() { return 0; } - public int uid() { return 0; } - public int gid() { return 0; } - public int size() { return 0; } - public int atime() { return 0; } - public int mtime() { return 0; } - public int ctime() { return 0; } - public int blksize() { return 512; } - public int blocks() { return (size()+blksize()-1)/blksize(); } - } - - protected static class HostFStat extends FStat { - private final File f; - 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"); - } - public int dev() { return 1; } - public int inode() { return f.getName().hashCode() & 0xffff; } - public int type() { return f.isDirectory() ? S_IFDIR : S_IFREG; } - public int nlink() { return 1; } - public int mode() { - int mode = 0; - boolean canread = f.canRead(); - if(canread && (executable || f.isDirectory())) mode |= 0111; - if(canread) mode |= 0444; - if(f.canWrite()) mode |= 0222; - return mode; - } - public int size() { return (int) f.length(); } - public int mtime() { return (int)(f.lastModified()/1000); } - } - - // Exceptions - public class ReadFaultException extends FaultException { - public ReadFaultException(int addr) { super(addr); } - } - public class WriteFaultException extends FaultException { - public WriteFaultException(int addr) { super(addr); } - } - public abstract class FaultException extends ExecutionException { - public int addr; - public FaultException(int addr) { super("fault at: " + toHex(addr)); this.addr = addr; } - } - public static class ExecutionException extends Exception { - private String message = "(null)"; - private String location = "(unknown)"; - public ExecutionException() { /* noop */ } - public ExecutionException(String s) { if(s != null) message = s; } - void setLocation(String s) { location = s == null ? "(unknown)" : s; } - public final String getMessage() { return message + " at " + location; } - } - public static class CallException extends Exception { - public CallException(String s) { super(s); } - } - - protected static class ErrnoException extends IOException { - public int errno; - public ErrnoException(int errno) { super("Errno: " + errno); this.errno = errno; } - } - - // CPU State - protected static class CPUState { - public CPUState() { /* noop */ } - /* GPRs */ - public int[] r = new int[32]; - /* Floating point regs */ - public int[] f = new int[32]; - public int hi, lo; - public int fcsr; - public int pc; - } - - // Null pointer check helper function - protected final void nullPointerCheck(int addr) throws ExecutionException { - if(TOTAL_PAGES==1 ? addr < 65536 : (addr>>>PAGE_SHIFT) < 16) - throw new ExecutionException("Attempted to dereference a null pointer " + toHex(addr)); - } - - // Utility functions - private byte[] byteBuf(int size) { - if(_byteBuf==null) _byteBuf = new byte[size]; - else if(_byteBuf.length < size) - _byteBuf = new byte[min(max(_byteBuf.length*2,size),MAX_CHUNK)]; - return _byteBuf; - } - - protected static String getSystemProperty(String key) { - try { - return System.getProperty(key); - } catch(SecurityException e) { - return null; - } - } - - /** Decode an packed string.. FEATURE: document this better */ - 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"); - int[] buf = new int[words]; - int prev = 0, left=0; - for(int i=0,n=0;n 0) buf[n++] = prev | (int)(l>>>(56-left)); - if(n < words) buf[n++] = (int) (l >>> (24-left)); - left = (left + 8) & 0x1f; - prev = (int)(l << left); - } - return buf; - } - - protected static byte[] getBytes(String s) { - try { - return s.getBytes("ISO-8859-1"); - } catch(UnsupportedEncodingException e) { - return null; // should never happen - } - } - - protected final static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); } - protected final static int min(int a, int b) { return a < b ? a : b; } - protected final static int max(int a, int b) { return a > b ? a : b; } -}