1 // Copyright 2003 Brian Alliet
2 // Based on org.xwt.imp.MIPS by Adam Megacz
3 // Portions Copyright 2003 Adam Megacz
5 package org.ibex.nestedvm;
7 import org.ibex.nestedvm.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
438 throw new Error(e.toString());
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 /** Duplicates the file descriptor <i>fdn</i> and returns the new fs */
631 public int dupFD(int fdn) {
633 if(fdn < 0 || fdn >= OPEN_MAX) return -1;
634 if(fds[fdn] == null) return -1;
635 for(i=0;i<OPEN_MAX;i++) if(fds[i] == null) break;
636 if(i==OPEN_MAX) return -1;
637 fds[i] = fds[fdn].dup();
641 // FEATURE: These should be pulled in from UsermodeConstants but fcntl.h is hard to parse
642 public static final int RD_ONLY = 0;
643 public static final int WR_ONLY = 1;
644 public static final int RDWR = 2;
646 public static final int O_CREAT = 0x0200;
647 public static final int O_EXCL = 0x0800;
648 public static final int O_APPEND = 0x0008;
649 public static final int O_TRUNC = 0x0400;
650 public static final int O_NONBLOCK = 0x4000;
652 // FEATURE: Lots of duplicate code between this and UnixRuntime.HostFS.open()
653 protected FD open(String path, int flags, int mode) throws IOException {
654 final File f = new File(path);
655 // NOTE: createNewFile is a Java2 function
656 if((flags & (O_EXCL|O_CREAT)) == (O_EXCL|O_CREAT))
657 if(!f.createNewFile()) throw new ErrnoException(EEXIST);
658 if(!f.exists() && (flags&O_CREAT) == 0) return null;
659 if(f.isDirectory()) return null;
660 final Seekable.File sf = new Seekable.File(path,mode!=RD_ONLY);
661 if((flags&O_TRUNC)!=0) sf.setLength(0);
662 return new SeekableFD(sf,flags) {
663 protected FStat _fstat() { return new HostFStat(f) {
665 try { return sf.length(); } catch(IOException e) { return 0; }
671 /** The open syscall */
672 private int sys_open(int addr, int flags, int mode) {
673 if((flags & O_NONBLOCK) != 0) {
674 System.err.println("WARNING: O_NONBLOCK not supported");
679 FD fd = open(cstring(addr),flags,mode);
680 if(fd == null) return -ENOENT;
688 catch(ErrnoException e) { return -e.errno; }
689 catch(FileNotFoundException e) {
690 if(e.getMessage() != null && e.getMessage().indexOf("Permission denied") >= 0) return -EACCES;
693 catch(IOException e) { return -EIO; }
694 catch(FaultException e) { return -EFAULT; }
697 /** The write syscall */
698 private int sys_write(int fdn, int addr, int count) {
699 count = Math.min(count,MAX_CHUNK);
700 if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
701 if(fds[fdn] == null || !fds[fdn].writable()) return -EBADFD;
703 byte[] buf = byteBuf(count);
704 copyin(addr,buf,count);
705 return fds[fdn].write(buf,0,count);
706 } catch(FaultException e) {
707 System.err.println(e);
709 } catch(IOException e) {
710 // FEATURE: We should support signals and send a SIGPIPE
711 if(e.getMessage().equals("Pipe closed")) return sys_exit(128+13);
712 System.err.println(e);
717 /** The read syscall */
718 private int sys_read(int fdn, int addr, int count) {
719 count = Math.min(count,MAX_CHUNK);
720 if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
721 if(fds[fdn] == null || !fds[fdn].readable()) return -EBADFD;
723 byte[] buf = byteBuf(count);
724 int n = fds[fdn].read(buf,0,count);
727 } catch(FaultException e) {
728 System.err.println(e);
730 } catch(IOException e) {
731 System.err.println(e);
736 /** The close syscall */
737 private int sys_close(int fdn) {
738 return closeFD(fdn) ? 0 : -EBADFD;
742 /** The seek syscall */
743 private int sys_lseek(int fdn, int offset, int whence) {
744 if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
745 if(fds[fdn] == null) return -EBADFD;
746 if(whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END) return -EINVAL;
748 int n = fds[fdn].seek(offset,whence);
749 return n < 0 ? -ESPIPE : n;
750 } catch(IOException e) {
755 /** The stat/fstat syscall helper */
756 int stat(FStat fs, int addr) {
758 memWrite(addr+0,(fs.dev()<<16)|(fs.inode()&0xffff)); // st_dev (top 16), // st_ino (bottom 16)
759 memWrite(addr+4,((fs.type()&0xf000))|(fs.mode()&0xfff)); // st_mode
760 memWrite(addr+8,1<<16); // st_nlink (top 16) // st_uid (bottom 16)
761 memWrite(addr+12,0); // st_gid (top 16) // st_rdev (bottom 16)
762 memWrite(addr+16,fs.size()); // st_size
763 memWrite(addr+20,fs.atime()); // st_atime
764 // memWrite(addr+24,0) // st_spare1
765 memWrite(addr+28,fs.mtime()); // st_mtime
766 // memWrite(addr+32,0) // st_spare2
767 memWrite(addr+36,fs.ctime()); // st_ctime
768 // memWrite(addr+40,0) // st_spare3
769 memWrite(addr+44,fs.blksize()); // st_bklsize;
770 memWrite(addr+48,fs.blocks()); // st_blocks
771 // memWrite(addr+52,0) // st_spare4[0]
772 // memWrite(addr+56,0) // st_spare4[1]
773 } catch(FaultException e) {
774 System.err.println(e);
780 /** The fstat syscall */
781 private int sys_fstat(int fdn, int addr) {
782 if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
783 if(fds[fdn] == null) return -EBADFD;
784 return stat(fds[fdn].fstat(),addr);
793 private int sys_gettimeofday(int timevalAddr, int timezoneAddr) {
794 long now = System.currentTimeMillis();
795 int tv_sec = (int)(now / 1000);
796 int tv_usec = (int)((now%1000)*1000);
798 memWrite(timevalAddr+0,tv_sec);
799 memWrite(timevalAddr+4,tv_usec);
801 } catch(FaultException e) {
806 private int sys_sleep(int sec) {
807 if(sec < 0) sec = Integer.MAX_VALUE;
809 Thread.sleep((long)sec*1000);
811 } catch(InterruptedException e) {
817 #define _CLOCKS_PER_SEC_ 1000
818 #define _CLOCK_T_ unsigned long
826 private int sys_times(int tms) {
827 long now = System.currentTimeMillis();
828 int userTime = (int)((now - startTime)/16);
829 int sysTime = (int)((now - startTime)/16);
833 memWrite(tms+0,userTime);
834 memWrite(tms+4,sysTime);
835 memWrite(tms+8,userTime);
836 memWrite(tms+12,sysTime);
838 } catch(FaultException e) {
844 private int sys_sysconf(int n) {
846 case _SC_CLK_TCK: return 1000;
848 System.err.println("WARNING: Attempted to use unknown sysconf key: " + n);
853 /** The sbrk syscall. This can also be used by subclasses to allocate memory.
854 <i>incr</i> is how much to increase the break by */
855 public int sbrk(int incr) {
856 if(incr < 0) return -ENOMEM;
857 if(incr==0) return brkAddr;
859 int oldBrk = brkAddr;
860 int newBrk = oldBrk + incr;
861 if(TOTAL_PAGES == 1) {
862 CPUState state = getCPUState();
863 if(newBrk >= state.r[SP] - 65536) {
864 System.err.println("WARNING: brk too close to stack pointer");
867 } else if(newBrk >= BRK_LIMIT) {
868 System.err.println("WARNING: Hit BRK_LIMIT");
871 if(TOTAL_PAGES != 1) {
873 for(int i=(oldBrk+PAGE_SIZE-1)>>>PAGE_SHIFT;i<((newBrk+PAGE_SIZE-1)>>>PAGE_SHIFT);i++)
874 readPages[i] = writePages[i] = emptyPage();
875 } catch(OutOfMemoryError e) {
876 System.err.println("WARNING: Caught OOM Exception in sbrk: " + e);
884 /** The getpid syscall */
885 private int sys_getpid() { return getPid(); }
886 protected int getPid() { return 1; }
888 private int sys_calljava(int a, int b, int c, int d) {
889 if(state != RUNNING) throw new IllegalStateException("wound up calling sys_calljava while not in RUNNING");
891 int ret = callJava(a,b,c,d);
896 private int sys_pause() {
901 private int sys_getpagesize() { return TOTAL_PAGES == 1 ? 4096 : PAGE_SIZE; }
903 private int sys_isatty(int fdn) {
904 if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
905 if(fds[fdn] == null) return -EBADFD;
906 return fds[fdn].isatty() ? 1 : 0;
910 /** Hook for subclasses to do something when the process exits (MUST set state = DONE) */
911 protected void _exit() { state = DONE; }
912 private int sys_exit(int status) {
914 for(int i=0;i<fds.length;i++) if(fds[i] != null) sys_close(i);
919 private int sys_fcntl(int fdn, int cmd, int arg) {
920 final int F_DUPFD = 0;
921 final int F_GETFL = 3;
924 if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
925 if(fds[fdn] == null) return -EBADFD;
930 if(arg < 0 || arg >= OPEN_MAX) return -EINVAL;
931 for(i=arg;i<OPEN_MAX;i++) if(fds[i]==null) break;
932 if(i==OPEN_MAX) return -EMFILE;
937 if(fd.writable() && fd.readable()) flags = 2;
938 else if(fd.writable()) flags = 1;
941 System.err.println("WARNING: Unknown fcntl command: " + cmd);
946 /** The syscall dispatcher.
947 The should be called by subclasses when the syscall instruction is invoked.
948 <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
949 the contenst of A0, A1, A2, and A3. The call MAY change the state
950 @see Runtime#state state */
951 protected int syscall(int syscall, int a, int b, int c, int d) {
953 case SYS_null: return 0;
954 case SYS_exit: return sys_exit(a);
955 case SYS_pause: return sys_pause();
956 case SYS_write: return sys_write(a,b,c);
957 case SYS_fstat: return sys_fstat(a,b);
958 case SYS_sbrk: return sbrk(a);
959 case SYS_open: return sys_open(a,b,c);
960 case SYS_close: return sys_close(a);
961 case SYS_read: return sys_read(a,b,c);
962 case SYS_lseek: return sys_lseek(a,b,c);
963 case SYS_getpid: return sys_getpid();
964 case SYS_calljava: return sys_calljava(a,b,c,d);
965 case SYS_gettimeofday: return sys_gettimeofday(a,b);
966 case SYS_sleep: return sys_sleep(a);
967 case SYS_times: return sys_times(a);
968 case SYS_getpagesize: return sys_getpagesize();
969 case SYS_isatty: return sys_isatty(a);
970 case SYS_fcntl: return sys_fcntl(a,b,c);
971 case SYS_sysconf: return sys_sysconf(a);
982 System.err.println("Attempted to use a UnixRuntime syscall in Runtime (" + syscall + ")");
985 System.err.println("Attempted to use unknown syscall: " + syscall);
990 public int xmalloc(int size) { int p=malloc(size); if(p==0) throw new RuntimeException("malloc() failed"); return p; }
991 public int xrealloc(int addr,int newsize) { int p=realloc(addr,newsize); if(p==0) throw new RuntimeException("realloc() failed"); return p; }
992 public int realloc(int addr, int newsize) { try { return call("realloc",addr,newsize); } catch(CallException e) { return 0; } }
993 public int malloc(int size) { try { return call("malloc",size); } catch(CallException e) { return 0; } }
994 public void free(int p) { try { if(p!=0) call("free",p); } catch(CallException e) { /*noop*/ } }
996 /** Helper function to create a cstring in main memory */
997 public int strdup(String s) {
999 if(s == null) s = "(null)";
1000 byte[] a2 = getBytes(s);
1001 a = new byte[a2.length+1];
1002 System.arraycopy(a2,0,a,0,a2.length);
1003 int addr = malloc(a.length);
1004 if(addr == 0) return 0;
1006 copyout(a,addr,a.length);
1007 } catch(FaultException e) {
1014 /** Helper function to read a cstring from main memory */
1015 public String cstring(int addr) throws ReadFaultException {
1016 StringBuffer sb = new StringBuffer();
1018 int word = memRead(addr&~3);
1020 case 0: if(((word>>>24)&0xff)==0) return sb.toString(); sb.append((char)((word>>>24)&0xff)); addr++;
1021 case 1: if(((word>>>16)&0xff)==0) return sb.toString(); sb.append((char)((word>>>16)&0xff)); addr++;
1022 case 2: if(((word>>> 8)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 8)&0xff)); addr++;
1023 case 3: if(((word>>> 0)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 0)&0xff)); addr++;
1028 /** File Descriptor class */
1029 public static abstract class FD {
1030 private int refCount = 1;
1032 /** returns true if the fd is readable */
1033 public boolean readable() { return false; }
1034 /** returns true if the fd is writable */
1035 public boolean writable() { return false; }
1037 /** Read some bytes. Should return the number of bytes read, 0 on EOF, or throw an IOException on error */
1038 public int read(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); }
1039 /** Write. Should return the number of bytes written or throw an IOException on error */
1040 public int write(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); }
1042 /** Seek in the filedescriptor. Whence is SEEK_SET, SEEK_CUR, or SEEK_END. Should return -1 on error or the new position. */
1043 public int seek(int n, int whence) throws IOException { return -1; }
1045 /** Should return true if this is a tty */
1046 public boolean isatty() { return false; }
1048 private FStat cachedFStat = null;
1049 public final FStat fstat() {
1050 if(cachedFStat == null) cachedFStat = _fstat();
1054 protected abstract FStat _fstat();
1056 /** Closes the fd */
1057 public final void close() { if(--refCount==0) _close(); }
1058 protected void _close() { /* noop*/ }
1060 FD dup() { refCount++; return this; }
1063 /** FileDescriptor class for normal files */
1064 public abstract static class SeekableFD extends FD {
1065 private final int flags;
1066 private final Seekable data;
1067 public boolean readable() { return (flags&3) != WR_ONLY; }
1068 public boolean writable() { return (flags&3) != RD_ONLY; }
1070 SeekableFD(Seekable data, int flags) { this.data = data; this.flags = flags; }
1072 protected abstract FStat _fstat();
1074 public int seek(int n, int whence) throws IOException {
1076 case SEEK_SET: break;
1077 case SEEK_CUR: n += data.pos(); break;
1078 case SEEK_END: n += data.length(); break;
1085 public int write(byte[] a, int off, int length) throws IOException {
1086 // NOTE: There is race condition here but we can't fix it in pure java
1087 if((flags&O_APPEND) != 0) seek(0,SEEK_END);
1088 return data.write(a,off,length);
1091 public int read(byte[] a, int off, int length) throws IOException {
1092 int n = data.read(a,off,length);
1093 return n < 0 ? 0 : n;
1096 protected void _close() { try { data.close(); } catch(IOException e) { /*ignore*/ } }
1099 public static class OutputStreamFD extends FD {
1100 private OutputStream os;
1101 public boolean writable() { return true; }
1102 public OutputStreamFD(OutputStream os) { this.os = os; }
1103 public int write(byte[] a, int off, int length) throws IOException { os.write(a,off,length); return length; }
1104 public void _close() { try { os.close(); } catch(IOException e) { /*ignore*/ } }
1105 public FStat _fstat() { return new FStat(); }
1108 public static class InputStreamFD extends FD {
1109 private InputStream is;
1110 public boolean readable() { return true; }
1111 public InputStreamFD(InputStream is) { this.is = is; }
1112 public int read(byte[] a, int off, int length) throws IOException { int n = is.read(a,off,length); return n < 0 ? 0 : n; }
1113 public void _close() { try { is.close(); } catch(IOException e) { /*ignore*/ } }
1114 public FStat _fstat() { return new FStat(); }
1117 protected static class StdinFD extends InputStreamFD {
1118 public StdinFD(InputStream is) { super(is); }
1119 public void _close() { /* noop */ }
1120 public FStat _fstat() { return new FStat() { public int type() { return S_IFCHR; } }; }
1121 public boolean isatty() { return true; }
1123 protected static class StdoutFD extends OutputStreamFD {
1124 public StdoutFD(OutputStream os) { super(os); }
1125 public void _close() { /* noop */ }
1126 public FStat _fstat() { return new FStat() { public int type() { return S_IFCHR; } }; }
1127 public boolean isatty() { return true; }
1130 public static class FStat {
1131 public static final int S_IFIFO = 0010000;
1132 public static final int S_IFCHR = 0020000;
1133 public static final int S_IFDIR = 0040000;
1134 public static final int S_IFREG = 0100000;
1136 public int dev() { return -1; }
1137 // FEATURE: inode numbers are calculated inconsistently throught the runtime
1138 public int inode() { return hashCode() & 0xfffff; }
1139 public int mode() { return 0; }
1140 public int type() { return S_IFIFO; }
1141 public int nlink() { return 0; }
1142 public int uid() { return 0; }
1143 public int gid() { return 0; }
1144 public int size() { return 0; }
1145 public int atime() { return 0; }
1146 public int mtime() { return 0; }
1147 public int ctime() { return 0; }
1148 public int blksize() { return 512; }
1149 public int blocks() { return (size()+blksize()-1)/blksize(); }
1152 protected static class HostFStat extends FStat {
1153 private final File f;
1154 private final boolean executable;
1155 public HostFStat(File f) {
1157 String name = f.getName();
1158 // FEATURE: This is ugly.. maybe we should do a file(1) type check
1159 executable = name.endsWith(".mips") || name.endsWith(".sh");
1161 public int dev() { return 1; }
1162 public int inode() { return f.getName().hashCode() & 0xffff; }
1163 public int type() { return f.isDirectory() ? S_IFDIR : S_IFREG; }
1164 public int nlink() { return 1; }
1167 boolean canread = f.canRead();
1168 if(canread && (executable || f.isDirectory())) mode |= 0111;
1169 if(canread) mode |= 0444;
1170 if(f.canWrite()) mode |= 0222;
1173 public int size() { return (int) f.length(); }
1174 public int mtime() { return (int)(f.lastModified()/1000); }
1178 public class ReadFaultException extends FaultException {
1179 public ReadFaultException(int addr) { super(addr); }
1181 public class WriteFaultException extends FaultException {
1182 public WriteFaultException(int addr) { super(addr); }
1184 public abstract class FaultException extends ExecutionException {
1186 public FaultException(int addr) { super("fault at: " + toHex(addr)); this.addr = addr; }
1188 public static class ExecutionException extends Exception {
1189 private String message = "(null)";
1190 private String location = "(unknown)";
1191 public ExecutionException() { /* noop */ }
1192 public ExecutionException(String s) { if(s != null) message = s; }
1193 void setLocation(String s) { location = s == null ? "(unknown)" : s; }
1194 public final String getMessage() { return message + " at " + location; }
1196 public static class CallException extends Exception {
1197 public CallException(String s) { super(s); }
1200 protected static class ErrnoException extends IOException {
1202 public ErrnoException(int errno) { super("Errno: " + errno); this.errno = errno; }
1206 protected static class CPUState {
1207 public CPUState() { /* noop */ }
1209 public int[] r = new int[32];
1210 /* Floating point regs */
1211 public int[] f = new int[32];
1217 // Null pointer check helper function
1218 protected final void nullPointerCheck(int addr) throws ExecutionException {
1219 if(TOTAL_PAGES==1 ? addr < 65536 : (addr>>>PAGE_SHIFT) < 16)
1220 throw new ExecutionException("Attempted to dereference a null pointer " + toHex(addr));
1223 // Utility functions
1224 private byte[] byteBuf(int size) {
1225 if(_byteBuf==null) _byteBuf = new byte[size];
1226 else if(_byteBuf.length < size)
1227 _byteBuf = new byte[min(max(_byteBuf.length*2,size),MAX_CHUNK)];
1231 protected static String getSystemProperty(String key) {
1233 return System.getProperty(key);
1234 } catch(SecurityException e) {
1239 /** Decode an packed string.. FEATURE: document this better */
1240 protected static final int[] decodeData(String s, int words) {
1241 if(s.length() % 8 != 0) throw new IllegalArgumentException("string length must be a multiple of 8");
1242 if((s.length() / 8) * 7 < words*4) throw new IllegalArgumentException("string isn't big enough");
1243 int[] buf = new int[words];
1244 int prev = 0, left=0;
1245 for(int i=0,n=0;n<words;i+=8) {
1247 for(int j=0;j<8;j++) { l <<= 7; l |= s.charAt(i+j) & 0x7f; }
1248 if(left > 0) buf[n++] = prev | (int)(l>>>(56-left));
1249 if(n < words) buf[n++] = (int) (l >>> (24-left));
1250 left = (left + 8) & 0x1f;
1251 prev = (int)(l << left);
1256 protected static byte[] getBytes(String s) {
1258 return s.getBytes("ISO-8859-1");
1259 } catch(UnsupportedEncodingException e) {
1260 return null; // should never happen
1264 protected final static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); }
1265 protected final static int min(int a, int b) { return a < b ? a : b; }
1266 protected final static int max(int a, int b) { return a > b ? a : b; }