1 // Copyright 2003 Brian Alliet
2 // Based on org.xwt.imp.MIPS by Adam Megacz
3 // Portions Copyright 2003 Adam Megacz
7 import org.xwt.mips.util.*;
9 import java.util.Arrays;
11 // FEATURE: Look over the public API, make sure we're exposing a bare minimum
12 // (we might make this an interface in the future)
14 public abstract class Runtime implements UsermodeConstants,Registers {
15 /** Pages are 4k in size */
16 protected final int PAGE_SIZE;
17 protected final int PAGE_WORDS;
18 protected final int PAGE_SHIFT;
19 protected final int TOTAL_PAGES;
20 /** This is the upper limit of the pages allocated by the sbrk() syscall. */
21 protected final int BRK_LIMIT;
22 protected final int STACK_BOTTOM;
24 /** This is the maximum size of command line arguments */
25 public final static int ARGS_MAX = 1024*1024;
27 /** True if we allow empty pages (_emptyPage) to exist in memory.
28 Empty pages are pages which are allocated by the program but do not contain any
29 data yet (they are all 0s). If empty pages are allowed subclasses must always
30 access main memory with the memRead and memWrite functions */
31 private final boolean allowEmptyPages;
32 /** the "empty page" */
33 private final static int[] _emptyPage = new int[0];
35 protected final static boolean isEmptyPage(int[] page) { return page == _emptyPage; }
37 /** Returns a new empty page (_emptyPage is empty pages are enabled or a new zero'd page) */
38 private final int[] emptyPage() { return allowEmptyPages ? _emptyPage : new int[PAGE_WORDS]; }
40 /** Readable main memory pages */
41 protected final int[][] readPages;
42 /** Writable main memory pages.
43 If the page is writable writePages[x] == readPages[x]; if not writePages[x] == null. */
44 protected final int[][] writePages;
46 /** The current break between the heap and unallocated memory */
47 protected int brkAddr;
49 /** The program's entry point */
50 protected int entryPoint;
52 /** The location of the _user_info block (or 0 is there is none) */
53 protected int userInfoBase;
54 protected int userInfoSize;
56 /** The location of the global pointer */
59 /** When the process started */
60 private long startTime;
62 /** State constant: There is no program loaded in memory */
63 public final static int UNINITIALIZED = 0;
64 /** Text/Data loaded in memory */
65 public final static int INITIALIZED = 1;
66 /** Program is executing instructions */
67 public final static int RUNNING = 2;
68 /** Prgram has been started but is paused */
69 public final static int PAUSED = 3;
70 /** Program is executing a callJava() method */
71 public final static int CALLJAVA = 4;
72 /** Program has exited (it cannot currently be restarted) */
73 public final static int DONE = 5;
75 /** The current state (UNINITIALIZED, INITIALIZED, RUNNING, PAUSED, or DONE) */
76 protected int state = UNINITIALIZED;
77 /** @see Runtime#state state */
78 public final int getState() { return state; }
80 /** The exit status if the process (only valid if state==DONE)
82 protected int exitStatus;
83 public ExecutionException exitException;
85 /** Maximum number of open file descriptors */
86 final static int OPEN_MAX = 256;
87 /** Table containing all open file descriptors. (Entries are null if the fd is not in use */
88 FD[] fds = new FD[OPEN_MAX];
90 /** Temporary buffer for read/write operations */
91 private byte[] _byteBuf = null;
92 /** Max size of temporary buffer
93 @see Runtime#_byteBuf */
94 private final static int MAX_CHUNK = 15*1024*1024;
96 /** Subclasses should actually execute program in this method. They should continue
97 executing until state != RUNNING. Only syscall() can modify state. It is safe
98 to only check the state attribute after a call to syscall() */
99 protected abstract void _execute() throws ExecutionException;
101 /** Subclasses should return the address of the symbol <i>symbol</i> or -1 it it doesn't exits in this method
102 This method is only required if the call() function is used */
103 protected int lookupSymbol(String symbol) { return -1; }
105 /** Subclasses should returns a CPUState object representing the cpu state */
106 protected abstract CPUState getCPUState();
108 /** Subclasses should set the CPUState to the state held in <i>state</i> */
109 protected abstract void setCPUState(CPUState state);
111 static void checkPageSize(int pageSize, int totalPages) throws IllegalArgumentException {
112 if(pageSize < 256) throw new IllegalArgumentException("pageSize too small");
113 if((pageSize&(pageSize-1)) != 0) throw new IllegalArgumentException("pageSize must be a power of two");
114 if((totalPages&(totalPages-1)) != 0) throw new IllegalArgumentException("totalPages must be a power of two");
115 if(totalPages != 1 && totalPages < 256) throw new IllegalArgumentException("totalPages too small");
116 if(totalPages * pageSize < 4*1024*1024) throw new IllegalArgumentException("total memory too small (" + totalPages + "*" + pageSize + ")");
119 protected Runtime(int pageSize, int totalPages, boolean allowEmptyPages) {
120 this.allowEmptyPages = allowEmptyPages;
122 checkPageSize(pageSize,totalPages);
124 PAGE_SIZE = pageSize;
125 PAGE_WORDS = pageSize>>>2;
127 while(pageSize>>>pageShift != 1) pageShift++;
128 PAGE_SHIFT = pageShift;
130 TOTAL_PAGES = totalPages;
132 readPages = new int[TOTAL_PAGES][];
133 writePages = new int[TOTAL_PAGES][];
135 if(TOTAL_PAGES == 1) {
136 readPages[0] = writePages[0] = new int[PAGE_WORDS];
137 BRK_LIMIT = STACK_BOTTOM = 0;
139 int stackPages = max(TOTAL_PAGES>>>8,(1024*1024)>>>PAGE_SHIFT);
140 STACK_BOTTOM = (TOTAL_PAGES - stackPages) * PAGE_SIZE;
141 // leave some unmapped pages between the stack and the heap
142 BRK_LIMIT = STACK_BOTTOM - 4*PAGE_SIZE;
144 for(int i=0;i<stackPages;i++)
145 readPages[TOTAL_PAGES-1-i] = writePages[TOTAL_PAGES-1-i] = emptyPage();
148 addFD(new StdinFD(System.in));
149 addFD(new StdoutFD(System.out));
150 addFD(new StdoutFD(System.err));
153 /** Copy everything from <i>src</i> to <i>addr</i> initializing uninitialized pages if required.
154 Newly initalized pages will be marked read-only if <i>ro</i> is set */
155 protected final void initPages(int[] src, int addr, boolean ro) {
156 for(int i=0;i<src.length;) {
157 int page = addr >>> PAGE_SHIFT;
158 int start = (addr&(PAGE_SIZE-1))>>2;
159 int elements = min(PAGE_WORDS-start,src.length-i);
160 if(readPages[page]==null) {
163 if(writePages[page] == null) writePages[page] = readPages[page];
165 System.arraycopy(src,i,readPages[page],start,elements);
171 /** Initialize <i>words</i> of pages starting at <i>addr</i> to 0 */
172 protected final void clearPages(int addr, int words) {
173 for(int i=0;i<words;) {
174 int page = addr >>> PAGE_SHIFT;
175 int start = (addr&(PAGE_SIZE-1))>>2;
176 int elements = min(PAGE_WORDS-start,words-i);
177 if(readPages[page]==null) {
178 readPages[page] = writePages[page] = emptyPage();
180 if(writePages[page] == null) writePages[page] = readPages[page];
181 for(int j=start;j<start+elements;j++) writePages[page][j] = 0;
188 /** Copies <i>length</i> bytes from the processes memory space starting at
189 <i>addr</i> INTO a java byte array <i>a</i> */
190 public final void copyin(int addr, byte[] buf, int count) throws ReadFaultException {
193 int word = memRead(addr&~3);
195 case 1: buf[x++] = (byte)((word>>>16)&0xff); if(--count==0) break;
196 case 2: buf[x++] = (byte)((word>>> 8)&0xff); if(--count==0) break;
197 case 3: buf[x++] = (byte)((word>>> 0)&0xff); if(--count==0) break;
201 if((count&~3) != 0) {
205 int[] page = readPages[a >>> (PAGE_SHIFT-2)];
206 if(page == null) throw new ReadFaultException(a<<2);
207 int index = a&(PAGE_WORDS-1);
208 int n = min(c,PAGE_WORDS-index);
209 if(page != _emptyPage) {
210 for(int i=0;i<n;i++,x+=4) {
211 int word = page[index+i];
212 buf[x+0] = (byte)((word>>>24)&0xff); buf[x+1] = (byte)((word>>>16)&0xff);
213 buf[x+2] = (byte)((word>>> 8)&0xff); buf[x+3] = (byte)((word>>> 0)&0xff);
218 addr = a<<2; count &=3;
221 int word = memRead(addr);
223 case 3: buf[x+2] = (byte)((word>>>8)&0xff);
224 case 2: buf[x+1] = (byte)((word>>>16)&0xff);
225 case 1: buf[x+0] = (byte)((word>>>24)&0xff);
230 /** Copies <i>length</i> bytes OUT OF the java array <i>a</i> into the processes memory
231 space at <i>addr</i> */
232 public final void copyout(byte[] buf, int addr, int count) throws FaultException {
235 int word = memRead(addr&~3);
237 case 1: word = (word&0xff00ffff)|((buf[x++]&0xff)<<16); if(--count==0) break;
238 case 2: word = (word&0xffff00ff)|((buf[x++]&0xff)<< 8); if(--count==0) break;
239 case 3: word = (word&0xffffff00)|((buf[x++]&0xff)<< 0); if(--count==0) break;
241 memWrite(addr&~3,word);
244 if((count&~3) != 0) {
248 int[] page = writePages[a >>> (PAGE_SHIFT-2)];
249 if(page == null) throw new WriteFaultException(a<<2);
250 if(page == _emptyPage) page = initPage(a >>> (PAGE_SHIFT-2));
251 int index = a&(PAGE_WORDS-1);
252 int n = min(c,PAGE_WORDS-index);
253 for(int i=0;i<n;i++,x+=4)
254 page[index+i] = ((buf[x+0]&0xff)<<24)|((buf[x+1]&0xff)<<16)|((buf[x+2]&0xff)<<8)|((buf[x+3]&0xff)<<0);
257 addr = a<<2; count&=3;
260 int word = memRead(addr);
262 case 1: word = (word&0x00ffffff)|((buf[x+0]&0xff)<<24); break;
263 case 2: word = (word&0x0000ffff)|((buf[x+0]&0xff)<<24)|((buf[x+1]&0xff)<<16); break;
264 case 3: word = (word&0x000000ff)|((buf[x+0]&0xff)<<24)|((buf[x+1]&0xff)<<16)|((buf[x+2]&0xff)<<8); break;
270 public final void memcpy(int dst, int src, int count) throws FaultException {
271 if((dst&3) == 0 && (src&3)==0) {
272 if((count&~3) != 0) {
277 int[] srcPage = readPages[s>>>(PAGE_SHIFT-2)];
278 if(srcPage == null) throw new ReadFaultException(s<<2);
279 int[] dstPage = writePages[d>>>(PAGE_SHIFT-2)];
280 if(dstPage == null) throw new WriteFaultException(d<<2);
281 int srcIndex = (s&(PAGE_WORDS-1));
282 int dstIndex = (d&(PAGE_WORDS-1));
283 int n = min(c,PAGE_WORDS-max(srcIndex,dstIndex));
284 if(srcPage != _emptyPage) {
285 if(dstPage == _emptyPage) dstPage = initPage(d>>>(PAGE_SHIFT-2));
286 System.arraycopy(srcPage,srcIndex,dstPage,dstIndex,n);
287 } else if(srcPage == _emptyPage && dstPage != _emptyPage) {
288 Arrays.fill(dstPage,dstIndex,dstIndex+n,0);
290 s += n; d += n; c -= n;
292 src = s<<2; dst = d<<2; count&=3;
295 int word1 = memRead(src);
296 int word2 = memRead(dst);
298 case 1: memWrite(dst,(word1&0xff000000)|(word2&0x00ffffff)); break;
299 case 2: memWrite(dst,(word1&0xffff0000)|(word2&0x0000ffff)); break;
300 case 3: memWrite(dst,(word1&0xffffff00)|(word2&0x000000ff)); break;
305 int n = min(count,MAX_CHUNK);
306 byte[] buf = byteBuf(n);
309 count -= n; src += n; dst += n;
314 public final void memset(int addr, int ch, int count) throws FaultException {
315 int fourBytes = ((ch&0xff)<<24)|((ch&0xff)<<16)|((ch&0xff)<<8)|((ch&0xff)<<0);
317 int word = memRead(addr&~3);
319 case 1: word = (word&0xff00ffff)|((ch&0xff)<<16); if(--count==0) break;
320 case 2: word = (word&0xffff00ff)|((ch&0xff)<< 8); if(--count==0) break;
321 case 3: word = (word&0xffffff00)|((ch&0xff)<< 0); if(--count==0) break;
323 memWrite(addr&~3,word);
326 if((count&~3) != 0) {
330 int[] page = readPages[a>>>(PAGE_SHIFT-2)];
331 if(page == null) throw new WriteFaultException(a<<2);
332 int index = (a&(PAGE_WORDS-1));
333 int n = min(c,PAGE_WORDS-index);
334 if(page != _emptyPage || ch != 0) {
335 if(page == _emptyPage) page = initPage(a>>>(PAGE_SHIFT-2));
336 Arrays.fill(page,index,index+n,fourBytes);
340 addr = a<<2; count&=3;
343 int word = memRead(addr);
345 case 1: word = (word&0x00ffffff)|(fourBytes&0xff000000); break;
346 case 2: word = (word&0x0000ffff)|(fourBytes&0xffff0000); break;
347 case 3: word = (word&0x000000ff)|(fourBytes&0xffffff00); break;
353 /** Read a word from the processes memory at <i>addr</i> */
354 public final int memRead(int addr) throws ReadFaultException {
355 if((addr & 3) != 0) throw new ReadFaultException(addr);
356 return unsafeMemRead(addr);
359 protected final int unsafeMemRead(int addr) throws ReadFaultException {
360 int page = addr >>> PAGE_SHIFT;
361 int entry = (addr >>> 2) & (PAGE_WORDS-1);
363 return readPages[page][entry];
364 } catch(ArrayIndexOutOfBoundsException e) {
365 if(page < 0) throw e; // should never happen
366 if(page >= readPages.length) throw new ReadFaultException(addr);
367 if(readPages[page] != _emptyPage) throw e; // should never happen
370 } catch(NullPointerException e) {
371 throw new ReadFaultException(addr);
375 /** Writes a word to the processes memory at <i>addr</i> */
376 public final void memWrite(int addr, int value) throws WriteFaultException {
377 if((addr & 3) != 0) throw new WriteFaultException(addr);
378 unsafeMemWrite(addr,value);
381 protected final void unsafeMemWrite(int addr, int value) throws WriteFaultException {
382 int page = addr >>> PAGE_SHIFT;
383 int entry = (addr>>>2)&(PAGE_WORDS-1);
385 writePages[page][entry] = value;
386 } catch(ArrayIndexOutOfBoundsException e) {
387 if(page < 0) throw e;// should never happen
388 if(page >= writePages.length) throw new WriteFaultException(addr);
389 if(readPages[page] != _emptyPage) throw e; // should never happen
391 writePages[page][entry] = value;
392 } catch(NullPointerException e) {
393 throw new WriteFaultException(addr);
397 /** Created a new non-empty writable page at page number <i>page</i> */
398 private final int[] initPage(int page) { return initPage(page,false); }
399 /** Created a new non-empty page at page number <i>page</i>. If <i>ro</i> is set the page will be read-only */
400 private final int[] initPage(int page, boolean ro) {
401 int[] buf = new int[PAGE_WORDS];
402 writePages[page] = ro ? null : buf;
403 readPages[page] = buf;
407 /** Returns the exit status of the process. (only valid if state == DONE)
408 @see Runtime#state */
409 public final int exitStatus() {
410 if(state != DONE) throw new IllegalStateException("exitStatus() called in an inappropriate state");
414 private int addStringArray(String[] strings, int topAddr) {
415 int count = strings.length;
416 int total = 0; /* null last table entry */
417 for(int i=0;i<count;i++) total += strings[i].length() + 1;
418 if(total >= ARGS_MAX) throw new IllegalArgumentException("arguments/environ too big");
419 total += (count+1)*4;
420 int start = (topAddr - total)&~3;
421 int addr = start + (count+1)*4;
422 int[] table = new int[count+1];
424 for(int i=0;i<count;i++) {
425 byte[] a = getBytes(strings[i]);
427 copyout(a,addr,a.length);
428 memset(addr+a.length,0,1);
429 addr += a.length + 1;
432 for(int i=0;i<count+1;i++) {
433 memWrite(addr,table[i]);
436 } catch(FaultException e) {
437 // should never happen
443 protected String[] createEnv(String[] extra) { if(extra == null) extra = new String[0]; return extra; }
445 /** Sets word number <i>index</i> in the _user_info table to <i>word</i>
446 * The user_info table is a chunk of memory in the program's memory defined by the
447 * symbol "user_info". The compiler/interpreter automatically determine the size
448 * and location of the user_info table from the ELF symbol table. setUserInfo and
449 * getUserInfo are used to modify the words in the user_info table. */
450 public void setUserInfo(int index, int word) {
451 if(index < 0 || index >= userInfoSize/4) throw new IndexOutOfBoundsException("setUserInfo called with index >= " + (userInfoSize/4));
453 memWrite(userInfoBase+index*4,word);
454 } catch(FaultException e) { throw new Error("should never happen: " + e); }
457 /** Returns the word in the _user_info table entry <i>index</i>
458 @see Runtime#setUserInfo(int,int) setUserInfo */
459 public int getUserInfo(int index) {
460 if(index < 0 || index >= userInfoSize/4) throw new IndexOutOfBoundsException("setUserInfo called with index >= " + (userInfoSize/4));
462 return memRead(userInfoBase+index*4);
463 } catch(FaultException e) { throw new Error("should never happen: " + e); }
466 /** Calls _execute() (subclass's execute()) and catches exceptions */
467 private void __execute() {
470 } catch(FaultException e) {
472 sys_exit(128+11); // SIGSEGV
474 } catch(ExecutionException e) {
476 System.err.println(e);
477 sys_exit(128+4); // SIGILL
482 /** Executes the process until the PAUSE syscall is invoked or the process exits. Returns true if the process exited. */
483 public final boolean execute() {
484 if(state != PAUSED) throw new IllegalStateException("execute() called in inappropriate state");
485 if(startTime == 0) startTime = System.currentTimeMillis();
488 if(state != PAUSED && state != DONE) throw new IllegalStateException("execute() ended up in an inappropriate state (" + state + ")");
489 return state == DONE;
492 public final int run() { return run(null); }
493 public final int run(String argv0, String[] rest) {
494 String[] args = new String[rest.length+1];
495 System.arraycopy(rest,0,args,1,rest.length);
499 public final int run(String[] args) { return run(args,null); }
501 /** Runs the process until it exits and returns the exit status.
502 If the process executes the PAUSE syscall execution will be paused for 500ms and a warning will be displayed */
503 public final int run(String[] args, String[] env) {
507 System.err.println("WARNING: Pause requested while executing run()");
508 try { Thread.sleep(500); } catch(InterruptedException e) { /* noop */ }
513 public final void start() { start(null); }
514 public final void start(String[] args) { start(args,null); }
516 /** Initializes the process and prepairs it to be executed with execute() */
517 public final void start(String[] args, String[] environ) {
518 int sp, argsAddr, envAddr;
519 if(state != INITIALIZED) throw new IllegalStateException("start() called in inappropriate state");
521 if(args == null) args = new String[]{getClass().getName()};
523 sp = TOTAL_PAGES*PAGE_SIZE-512;
524 sp = argsAddr = addStringArray(args,sp);
525 sp = envAddr = addStringArray(createEnv(environ),sp);
528 CPUState cpuState = new CPUState();
529 cpuState.r[A0] = argsAddr;
530 cpuState.r[A1] = envAddr;
532 cpuState.r[RA] = 0xdeadbeef;
534 cpuState.pc = entryPoint;
535 setCPUState(cpuState);
542 /** Hook for subclasses to do their own startup */
543 protected void _start() { /* noop */ }
545 public final int call(String sym) throws CallException { return call(sym,0,0,0,0,0,0,0); }
546 public final int call(String sym, int a0) throws CallException { return call(sym,a0,0,0,0,0,0,0); }
547 public final int call(String sym, int a0, int a1) throws CallException { return call(sym,a0,a1,0,0,0,0,0); }
548 public final int call(String sym, int a0, int a1, int a2) throws CallException { return call(sym,a0,a1,a2,0,0,0,0); }
549 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); }
550 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); }
551 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); }
553 /** Calls a function in the process with the given arguments */
554 public final int call(String sym, int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws CallException {
555 int func = lookupSymbol(sym);
556 if(func == -1) throw new CallException(sym + " not found");
557 int helper = lookupSymbol("_call_helper");
558 if(helper == -1) throw new CallException("_call_helper not found");
559 return call(helper,func,a0,a1,a2,a3,a4,a5,a6);
562 /** Executes the code at <i>addr</i> in the process setting A0-A3 and S0-S3 to the given arguments
563 and returns the contents of V1 when the the pause syscall is invoked */
564 public final int call(int addr, int a0, int a1, int a2, int a3, int s0, int s1, int s2, int s3) {
565 if(state != PAUSED && state != CALLJAVA) throw new IllegalStateException("call() called in inappropriate state");
566 int oldState = state;
567 CPUState saved = getCPUState();
568 CPUState cpustate = new CPUState();
569 cpustate.r[SP] = saved.r[SP]&~15;
570 cpustate.r[RA] = 0xdeadbeef;
584 setCPUState(cpustate);
586 cpustate = getCPUState();
590 System.out.println("WARNING: Process exit()ed while servicing a call() request");
594 return cpustate.r[V1];
597 // FEATURE: This is ugly - we should have some kind of way to specify a callback rather than requiring subclassing
598 protected int callJava(int a, int b, int c, int d) {
599 System.err.println("WARNING: Default implementation of callJava() called with args " + toHex(a) + "," + toHex(b) + "," + toHex(c) + "," + toHex(d));
603 /** Determines if the process can access <i>fileName</i>. The default implementation simply logs
604 the request and allows it */
605 protected boolean allowFileAccess(String fileName, boolean write) {
606 //System.err.println("Allowing " + (write?"write":"read-only") + " access to " + fileName);
610 /** Allocated an entry in the FileDescriptor table for <i>fd</i> and returns the number.
611 Returns -1 if the table is full. This can be used by subclasses to use custom file
613 public int addFD(FD fd) {
615 for(i=0;i<OPEN_MAX;i++) if(fds[i] == null) break;
616 if(i==OPEN_MAX) return -1;
621 /** Closes file descriptor <i>fdn</i> and removes it from the file descriptor table */
622 public boolean closeFD(int fdn) {
623 if(fdn < 0 || fdn >= OPEN_MAX) return false;
624 if(fds[fdn] == null) return false;
630 // FEATURE: These should be pulled in from UsermodeConstants but fcntl.h is hard to parse
631 public static final int RD_ONLY = 0;
632 public static final int WR_ONLY = 1;
633 public static final int RDWR = 2;
635 public static final int O_CREAT = 0x0200;
636 public static final int O_EXCL = 0x0800;
637 public static final int O_APPEND = 0x0008;
638 public static final int O_TRUNC = 0x0400;
639 public static final int O_NONBLOCK = 0x4000;
641 // FEATURE: Lots of duplicate code between this and UnixRuntime.HostFS.open()
642 protected FD open(String path, int flags, int mode) throws IOException {
643 final File f = new File(path);
644 // NOTE: createNewFile is a Java2 function
645 if((flags & (O_EXCL|O_CREAT)) == (O_EXCL|O_CREAT))
646 if(!f.createNewFile()) throw new ErrnoException(EEXIST);
647 if(!f.exists() && (flags&O_CREAT) == 0) return null;
648 if(f.isDirectory()) return null;
649 final SeekableFile sf = new SeekableFile(path,mode!=RD_ONLY);
650 if((flags&O_TRUNC)!=0) sf.setLength(0);
651 return new SeekableFD(sf,flags) {
652 protected FStat _fstat() { return new HostFStat(f) {
654 try { return sf.length(); } catch(IOException e) { return 0; }
660 /** The open syscall */
661 private int sys_open(int addr, int flags, int mode) {
662 if((flags & O_NONBLOCK) != 0) {
663 System.err.println("WARNING: O_NONBLOCK not supported");
668 FD fd = open(cstring(addr),flags,mode);
669 if(fd == null) return -ENOENT;
677 catch(ErrnoException e) { return -e.errno; }
678 catch(FileNotFoundException e) {
679 if(e.getMessage() != null && e.getMessage().indexOf("Permission denied") >= 0) return -EACCES;
682 catch(IOException e) { return -EIO; }
683 catch(FaultException e) { return -EFAULT; }
686 /** The write syscall */
687 private int sys_write(int fdn, int addr, int count) {
688 count = Math.min(count,MAX_CHUNK);
689 if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
690 if(fds[fdn] == null || !fds[fdn].writable()) return -EBADFD;
692 byte[] buf = byteBuf(count);
693 copyin(addr,buf,count);
694 return fds[fdn].write(buf,0,count);
695 } catch(FaultException e) {
696 System.err.println(e);
698 } catch(IOException e) {
699 // FEATURE: We should support signals and send a SIGPIPE
700 if(e.getMessage().equals("Pipe closed")) return sys_exit(128+13);
701 System.err.println(e);
706 /** The read syscall */
707 private int sys_read(int fdn, int addr, int count) {
708 count = Math.min(count,MAX_CHUNK);
709 if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
710 if(fds[fdn] == null || !fds[fdn].readable()) return -EBADFD;
712 byte[] buf = byteBuf(count);
713 int n = fds[fdn].read(buf,0,count);
716 } catch(FaultException e) {
717 System.err.println(e);
719 } catch(IOException e) {
720 System.err.println(e);
725 /** The close syscall */
726 private int sys_close(int fdn) {
727 return closeFD(fdn) ? 0 : -EBADFD;
731 /** The seek syscall */
732 private int sys_lseek(int fdn, int offset, int whence) {
733 if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
734 if(fds[fdn] == null) return -EBADFD;
735 if(whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END) return -EINVAL;
737 int n = fds[fdn].seek(offset,whence);
738 return n < 0 ? -ESPIPE : n;
739 } catch(IOException e) {
744 /** The stat/fstat syscall helper */
745 int stat(FStat fs, int addr) {
747 memWrite(addr+0,(fs.dev()<<16)|(fs.inode()&0xffff)); // st_dev (top 16), // st_ino (bottom 16)
748 memWrite(addr+4,((fs.type()&0xf000))|(fs.mode()&0xfff)); // st_mode
749 memWrite(addr+8,1<<16); // st_nlink (top 16) // st_uid (bottom 16)
750 memWrite(addr+12,0); // st_gid (top 16) // st_rdev (bottom 16)
751 memWrite(addr+16,fs.size()); // st_size
752 memWrite(addr+20,fs.atime()); // st_atime
753 // memWrite(addr+24,0) // st_spare1
754 memWrite(addr+28,fs.mtime()); // st_mtime
755 // memWrite(addr+32,0) // st_spare2
756 memWrite(addr+36,fs.ctime()); // st_ctime
757 // memWrite(addr+40,0) // st_spare3
758 memWrite(addr+44,fs.blksize()); // st_bklsize;
759 memWrite(addr+48,fs.blocks()); // st_blocks
760 // memWrite(addr+52,0) // st_spare4[0]
761 // memWrite(addr+56,0) // st_spare4[1]
762 } catch(FaultException e) {
763 System.err.println(e);
769 /** The fstat syscall */
770 private int sys_fstat(int fdn, int addr) {
771 if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
772 if(fds[fdn] == null) return -EBADFD;
773 return stat(fds[fdn].fstat(),addr);
782 private int sys_gettimeofday(int timevalAddr, int timezoneAddr) {
783 long now = System.currentTimeMillis();
784 int tv_sec = (int)(now / 1000);
785 int tv_usec = (int)((now%1000)*1000);
787 memWrite(timevalAddr+0,tv_sec);
788 memWrite(timevalAddr+4,tv_usec);
790 } catch(FaultException e) {
795 private int sys_sleep(int sec) {
796 if(sec < 0) sec = Integer.MAX_VALUE;
798 Thread.sleep((long)sec*1000);
800 } catch(InterruptedException e) {
806 #define _CLOCKS_PER_SEC_ 1000
807 #define _CLOCK_T_ unsigned long
815 private int sys_times(int tms) {
816 long now = System.currentTimeMillis();
817 int userTime = (int)((now - startTime)/16);
818 int sysTime = (int)((now - startTime)/16);
822 memWrite(tms+0,userTime);
823 memWrite(tms+4,sysTime);
824 memWrite(tms+8,userTime);
825 memWrite(tms+12,sysTime);
827 } catch(FaultException e) {
833 private int sys_sysconf(int n) {
835 case _SC_CLK_TCK: return 1000;
837 System.err.println("WARNING: Attempted to use unknown sysconf key: " + n);
842 /** The sbrk syscall. This can also be used by subclasses to allocate memory.
843 <i>incr</i> is how much to increase the break by */
844 public int sbrk(int incr) {
845 if(incr < 0) return -ENOMEM;
846 if(incr==0) return brkAddr;
848 int oldBrk = brkAddr;
849 int newBrk = oldBrk + incr;
850 if(TOTAL_PAGES == 1) {
851 CPUState state = getCPUState();
852 if(newBrk >= state.r[SP] - 65536) {
853 System.err.println("WARNING: brk too close to stack pointer");
856 } else if(newBrk >= BRK_LIMIT) {
857 System.err.println("WARNING: Hit BRK_LIMIT");
860 if(TOTAL_PAGES != 1) {
862 for(int i=(oldBrk+PAGE_SIZE-1)>>>PAGE_SHIFT;i<((newBrk+PAGE_SIZE-1)>>>PAGE_SHIFT);i++)
863 readPages[i] = writePages[i] = emptyPage();
864 } catch(OutOfMemoryError e) {
865 System.err.println("WARNING: Caught OOM Exception in sbrk: " + e);
873 /** The getpid syscall */
874 private int sys_getpid() { return getPid(); }
875 protected int getPid() { return 1; }
877 private int sys_calljava(int a, int b, int c, int d) {
878 if(state != RUNNING) throw new IllegalStateException("wound up calling sys_calljava while not in RUNNING");
880 int ret = callJava(a,b,c,d);
885 private int sys_pause() {
890 private int sys_getpagesize() { return TOTAL_PAGES == 1 ? 4096 : PAGE_SIZE; }
892 private int sys_isatty(int fdn) {
893 if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
894 if(fds[fdn] == null) return -EBADFD;
895 return fds[fdn].isatty() ? 1 : 0;
899 /** Hook for subclasses to do something when the process exits (MUST set state = DONE) */
900 protected void _exit() { state = DONE; }
901 private int sys_exit(int status) {
903 for(int i=0;i<fds.length;i++) if(fds[i] != null) sys_close(i);
908 private int sys_fcntl(int fdn, int cmd, int arg) {
909 final int F_DUPFD = 0;
910 final int F_GETFL = 3;
913 if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
914 if(fds[fdn] == null) return -EBADFD;
919 if(arg < 0 || arg >= OPEN_MAX) return -EINVAL;
920 for(i=arg;i<OPEN_MAX;i++) if(fds[i]==null) break;
921 if(i==OPEN_MAX) return -EMFILE;
926 if(fd.writable() && fd.readable()) flags = 2;
927 else if(fd.writable()) flags = 1;
930 System.err.println("WARNING: Unknown fcntl command: " + cmd);
935 /** The syscall dispatcher.
936 The should be called by subclasses when the syscall instruction is invoked.
937 <i>syscall</i> should be the contents of V0 and <i>a</i>, <i>b</i>, <i>c</i>, and <i>d</i> should be
938 the contenst of A0, A1, A2, and A3. The call MAY change the state
939 @see Runtime#state state */
940 protected int syscall(int syscall, int a, int b, int c, int d) {
942 case SYS_null: return 0;
943 case SYS_exit: return sys_exit(a);
944 case SYS_pause: return sys_pause();
945 case SYS_write: return sys_write(a,b,c);
946 case SYS_fstat: return sys_fstat(a,b);
947 case SYS_sbrk: return sbrk(a);
948 case SYS_open: return sys_open(a,b,c);
949 case SYS_close: return sys_close(a);
950 case SYS_read: return sys_read(a,b,c);
951 case SYS_lseek: return sys_lseek(a,b,c);
952 case SYS_getpid: return sys_getpid();
953 case SYS_calljava: return sys_calljava(a,b,c,d);
954 case SYS_gettimeofday: return sys_gettimeofday(a,b);
955 case SYS_sleep: return sys_sleep(a);
956 case SYS_times: return sys_times(a);
957 case SYS_getpagesize: return sys_getpagesize();
958 case SYS_isatty: return sys_isatty(a);
959 case SYS_fcntl: return sys_fcntl(a,b,c);
960 case SYS_sysconf: return sys_sysconf(a);
971 System.err.println("Attempted to use a UnixRuntime syscall in Runtime (" + syscall + ")");
974 System.err.println("Attempted to use unknown syscall: " + syscall);
979 public int xmalloc(int size) { int p=malloc(size); if(p==0) throw new RuntimeException("malloc() failed"); return p; }
980 public int xrealloc(int addr,int newsize) { int p=realloc(addr,newsize); if(p==0) throw new RuntimeException("realloc() failed"); return p; }
981 public int realloc(int addr, int newsize) { try { return call("realloc",addr,newsize); } catch(CallException e) { return 0; } }
982 public int malloc(int size) { try { return call("malloc",size); } catch(CallException e) { return 0; } }
983 public void free(int p) { try { if(p!=0) call("free",p); } catch(CallException e) { /*noop*/ } }
985 /** Helper function to create a cstring in main memory */
986 public int strdup(String s) {
988 if(s == null) s = "(null)";
989 byte[] a2 = getBytes(s);
990 a = new byte[a2.length+1];
991 System.arraycopy(a2,0,a,0,a2.length);
992 int addr = malloc(a.length);
993 if(addr == 0) return 0;
995 copyout(a,addr,a.length);
996 } catch(FaultException e) {
1003 /** Helper function to read a cstring from main memory */
1004 public String cstring(int addr) throws ReadFaultException {
1005 StringBuffer sb = new StringBuffer();
1007 int word = memRead(addr&~3);
1009 case 0: if(((word>>>24)&0xff)==0) return sb.toString(); sb.append((char)((word>>>24)&0xff)); addr++;
1010 case 1: if(((word>>>16)&0xff)==0) return sb.toString(); sb.append((char)((word>>>16)&0xff)); addr++;
1011 case 2: if(((word>>> 8)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 8)&0xff)); addr++;
1012 case 3: if(((word>>> 0)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 0)&0xff)); addr++;
1017 /** File Descriptor class */
1018 public static abstract class FD {
1019 private int refCount = 1;
1021 /** returns true if the fd is readable */
1022 public boolean readable() { return false; }
1023 /** returns true if the fd is writable */
1024 public boolean writable() { return false; }
1026 /** Read some bytes. Should return the number of bytes read, 0 on EOF, or throw an IOException on error */
1027 public int read(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); }
1028 /** Write. Should return the number of bytes written or throw an IOException on error */
1029 public int write(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); }
1031 /** Seek in the filedescriptor. Whence is SEEK_SET, SEEK_CUR, or SEEK_END. Should return -1 on error or the new position. */
1032 public int seek(int n, int whence) throws IOException { return -1; }
1034 /** Should return true if this is a tty */
1035 public boolean isatty() { return false; }
1037 private FStat cachedFStat = null;
1038 public final FStat fstat() {
1039 if(cachedFStat == null) cachedFStat = _fstat();
1043 protected abstract FStat _fstat();
1045 /** Closes the fd */
1046 public final void close() { if(--refCount==0) _close(); }
1047 protected void _close() { /* noop*/ }
1049 FD dup() { refCount++; return this; }
1052 /** FileDescriptor class for normal files */
1053 public abstract static class SeekableFD extends FD {
1054 private final int flags;
1055 private final SeekableData data;
1056 public boolean readable() { return (flags&3) != WR_ONLY; }
1057 public boolean writable() { return (flags&3) != RD_ONLY; }
1059 SeekableFD(SeekableData data, int flags) { this.data = data; this.flags = flags; }
1061 protected abstract FStat _fstat();
1063 public int seek(int n, int whence) throws IOException {
1065 case SEEK_SET: break;
1066 case SEEK_CUR: n += data.pos(); break;
1067 case SEEK_END: n += data.length(); break;
1074 public int write(byte[] a, int off, int length) throws IOException {
1075 // NOTE: There is race condition here but we can't fix it in pure java
1076 if((flags&O_APPEND) != 0) seek(0,SEEK_END);
1077 return data.write(a,off,length);
1080 public int read(byte[] a, int off, int length) throws IOException {
1081 int n = data.read(a,off,length);
1082 return n < 0 ? 0 : n;
1085 protected void _close() { try { data.close(); } catch(IOException e) { /*ignore*/ } }
1088 public static class OutputStreamFD extends FD {
1089 private OutputStream os;
1090 public boolean writable() { return true; }
1091 public OutputStreamFD(OutputStream os) { this.os = os; }
1092 public int write(byte[] a, int off, int length) throws IOException { os.write(a,off,length); return length; }
1093 public void _close() { try { os.close(); } catch(IOException e) { /*ignore*/ } }
1094 public FStat _fstat() { return new FStat(); }
1097 public static class InputStreamFD extends FD {
1098 private InputStream is;
1099 public boolean readable() { return true; }
1100 public InputStreamFD(InputStream is) { this.is = is; }
1101 public int read(byte[] a, int off, int length) throws IOException { int n = is.read(a,off,length); return n < 0 ? 0 : n; }
1102 public void _close() { try { is.close(); } catch(IOException e) { /*ignore*/ } }
1103 public FStat _fstat() { return new FStat(); }
1106 protected static class StdinFD extends InputStreamFD {
1107 public StdinFD(InputStream is) { super(is); }
1108 public void _close() { /* noop */ }
1109 public FStat _fstat() { return new FStat() { public int type() { return S_IFCHR; } }; }
1110 public boolean isatty() { return true; }
1112 protected static class StdoutFD extends OutputStreamFD {
1113 public StdoutFD(OutputStream os) { super(os); }
1114 public void _close() { /* noop */ }
1115 public FStat _fstat() { return new FStat() { public int type() { return S_IFCHR; } }; }
1116 public boolean isatty() { return true; }
1119 public static class FStat {
1120 public static final int S_IFIFO = 0010000;
1121 public static final int S_IFCHR = 0020000;
1122 public static final int S_IFDIR = 0040000;
1123 public static final int S_IFREG = 0100000;
1125 public int dev() { return -1; }
1126 // FEATURE: inode numbers are calculated inconsistently throught the runtime
1127 public int inode() { return hashCode() & 0xfffff; }
1128 public int mode() { return 0; }
1129 public int type() { return S_IFIFO; }
1130 public int nlink() { return 0; }
1131 public int uid() { return 0; }
1132 public int gid() { return 0; }
1133 public int size() { return 0; }
1134 public int atime() { return 0; }
1135 public int mtime() { return 0; }
1136 public int ctime() { return 0; }
1137 public int blksize() { return 512; }
1138 public int blocks() { return (size()+blksize()-1)/blksize(); }
1141 protected static class HostFStat extends FStat {
1142 private final File f;
1143 private final boolean executable;
1144 public HostFStat(File f) {
1146 String name = f.getName();
1147 // FEATURE: This is ugly.. maybe we should do a file(1) type check
1148 executable = name.endsWith(".mips") || name.endsWith(".sh");
1150 public int dev() { return 1; }
1151 public int inode() { return f.getName().hashCode() & 0xffff; }
1152 public int type() { return f.isDirectory() ? S_IFDIR : S_IFREG; }
1153 public int nlink() { return 1; }
1156 boolean canread = f.canRead();
1157 if(canread && (executable || f.isDirectory())) mode |= 0111;
1158 if(canread) mode |= 0444;
1159 if(f.canWrite()) mode |= 0222;
1162 public int size() { return (int) f.length(); }
1163 public int mtime() { return (int)(f.lastModified()/1000); }
1167 public class ReadFaultException extends FaultException {
1168 public ReadFaultException(int addr) { super(addr); }
1170 public class WriteFaultException extends FaultException {
1171 public WriteFaultException(int addr) { super(addr); }
1173 public abstract class FaultException extends ExecutionException {
1175 public FaultException(int addr) { super("fault at: " + toHex(addr)); this.addr = addr; }
1177 public static class ExecutionException extends Exception {
1178 private String message = "(null)";
1179 private String location = "(unknown)";
1180 public ExecutionException() { /* noop */ }
1181 public ExecutionException(String s) { if(s != null) message = s; }
1182 void setLocation(String s) { location = s == null ? "(unknown)" : s; }
1183 public final String getMessage() { return message + " at " + location; }
1185 public static class CallException extends Exception {
1186 public CallException(String s) { super(s); }
1189 protected static class ErrnoException extends IOException {
1191 public ErrnoException(int errno) { super("Errno: " + errno); this.errno = errno; }
1195 protected static class CPUState {
1196 public CPUState() { /* noop */ }
1198 public int[] r = new int[32];
1199 /* Floating point regs */
1200 public int[] f = new int[32];
1206 // Null pointer check helper function
1207 protected final void nullPointerCheck(int addr) throws ExecutionException {
1208 if(TOTAL_PAGES==1 ? addr < 65536 : (addr>>>PAGE_SHIFT) < 16)
1209 throw new ExecutionException("Attempted to dereference a null pointer " + toHex(addr));
1212 // Utility functions
1213 private byte[] byteBuf(int size) {
1214 if(_byteBuf==null) _byteBuf = new byte[size];
1215 else if(_byteBuf.length < size)
1216 _byteBuf = new byte[min(max(_byteBuf.length*2,size),MAX_CHUNK)];
1220 protected static String getSystemProperty(String key) {
1222 return System.getProperty(key);
1223 } catch(SecurityException e) {
1228 /** Decode an packed string.. FEATURE: document this better */
1229 protected static final int[] decodeData(String s, int words) {
1230 if(s.length() % 8 != 0) throw new IllegalArgumentException("string length must be a multiple of 8");
1231 if((s.length() / 8) * 7 < words*4) throw new IllegalArgumentException("string isn't big enough");
1232 int[] buf = new int[words];
1233 int prev = 0, left=0;
1234 for(int i=0,n=0;n<words;i+=8) {
1236 for(int j=0;j<8;j++) { l <<= 7; l |= s.charAt(i+j) & 0x7f; }
1237 if(left > 0) buf[n++] = prev | (int)(l>>>(56-left));
1238 if(n < words) buf[n++] = (int) (l >>> (24-left));
1239 left = (left + 8) & 0x1f;
1240 prev = (int)(l << left);
1245 protected static byte[] getBytes(String s) {
1247 return s.getBytes("ISO-8859-1");
1248 } catch(UnsupportedEncodingException e) {
1249 return null; // should never happen
1253 protected final static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); }
1254 protected final static int min(int a, int b) { return a < b ? a : b; }
1255 protected final static int max(int a, int b) { return a > b ? a : b; }