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 /** Pointer to a callback for the call_java syscall */
91 protected CallJavaCB callJavaCB;
92 public void setCallJavaCB(CallJavaCB callJavaCB) { this.callJavaCB = callJavaCB; }
93 public CallJavaCB getCallJavaCB() { return callJavaCB; }
95 /** Temporary buffer for read/write operations */
96 private byte[] _byteBuf = null;
97 /** Max size of temporary buffer
98 @see Runtime#_byteBuf */
99 private final static int MAX_CHUNK = 15*1024*1024;
101 /** Subclasses should actually execute program in this method. They should continue
102 executing until state != RUNNING. Only syscall() can modify state. It is safe
103 to only check the state attribute after a call to syscall() */
104 protected abstract void _execute() throws ExecutionException;
106 /** Subclasses should return the address of the symbol <i>symbol</i> or -1 it it doesn't exits in this method
107 This method is only required if the call() function is used */
108 protected int lookupSymbol(String symbol) { return -1; }
110 /** Subclasses should returns a CPUState object representing the cpu state */
111 protected abstract CPUState getCPUState();
113 /** Subclasses should set the CPUState to the state held in <i>state</i> */
114 protected abstract void setCPUState(CPUState state);
116 static void checkPageSize(int pageSize, int totalPages) throws IllegalArgumentException {
117 if(pageSize < 256) throw new IllegalArgumentException("pageSize too small");
118 if((pageSize&(pageSize-1)) != 0) throw new IllegalArgumentException("pageSize must be a power of two");
119 if((totalPages&(totalPages-1)) != 0) throw new IllegalArgumentException("totalPages must be a power of two");
120 if(totalPages != 1 && totalPages < 256) throw new IllegalArgumentException("totalPages too small");
121 if(totalPages * pageSize < 4*1024*1024) throw new IllegalArgumentException("total memory too small (" + totalPages + "*" + pageSize + ")");
124 protected Runtime(int pageSize, int totalPages, boolean allowEmptyPages) {
125 this.allowEmptyPages = allowEmptyPages;
127 checkPageSize(pageSize,totalPages);
129 PAGE_SIZE = pageSize;
130 PAGE_WORDS = pageSize>>>2;
132 while(pageSize>>>pageShift != 1) pageShift++;
133 PAGE_SHIFT = pageShift;
135 TOTAL_PAGES = totalPages;
137 readPages = new int[TOTAL_PAGES][];
138 writePages = new int[TOTAL_PAGES][];
140 if(TOTAL_PAGES == 1) {
141 readPages[0] = writePages[0] = new int[PAGE_WORDS];
142 BRK_LIMIT = STACK_BOTTOM = 0;
144 int stackPages = max(TOTAL_PAGES>>>8,(1024*1024)>>>PAGE_SHIFT);
145 STACK_BOTTOM = (TOTAL_PAGES - stackPages) * PAGE_SIZE;
146 // leave some unmapped pages between the stack and the heap
147 BRK_LIMIT = STACK_BOTTOM - 4*PAGE_SIZE;
149 for(int i=0;i<stackPages;i++)
150 readPages[TOTAL_PAGES-1-i] = writePages[TOTAL_PAGES-1-i] = emptyPage();
153 addFD(new StdinFD(System.in));
154 addFD(new StdoutFD(System.out));
155 addFD(new StdoutFD(System.err));
158 /** Copy everything from <i>src</i> to <i>addr</i> initializing uninitialized pages if required.
159 Newly initalized pages will be marked read-only if <i>ro</i> is set */
160 protected final void initPages(int[] src, int addr, boolean ro) {
161 for(int i=0;i<src.length;) {
162 int page = addr >>> PAGE_SHIFT;
163 int start = (addr&(PAGE_SIZE-1))>>2;
164 int elements = min(PAGE_WORDS-start,src.length-i);
165 if(readPages[page]==null) {
168 if(writePages[page] == null) writePages[page] = readPages[page];
170 System.arraycopy(src,i,readPages[page],start,elements);
176 /** Initialize <i>words</i> of pages starting at <i>addr</i> to 0 */
177 protected final void clearPages(int addr, int words) {
178 for(int i=0;i<words;) {
179 int page = addr >>> PAGE_SHIFT;
180 int start = (addr&(PAGE_SIZE-1))>>2;
181 int elements = min(PAGE_WORDS-start,words-i);
182 if(readPages[page]==null) {
183 readPages[page] = writePages[page] = emptyPage();
185 if(writePages[page] == null) writePages[page] = readPages[page];
186 for(int j=start;j<start+elements;j++) writePages[page][j] = 0;
193 /** Copies <i>length</i> bytes from the processes memory space starting at
194 <i>addr</i> INTO a java byte array <i>a</i> */
195 public final void copyin(int addr, byte[] buf, int count) throws ReadFaultException {
198 int word = memRead(addr&~3);
200 case 1: buf[x++] = (byte)((word>>>16)&0xff); if(--count==0) break;
201 case 2: buf[x++] = (byte)((word>>> 8)&0xff); if(--count==0) break;
202 case 3: buf[x++] = (byte)((word>>> 0)&0xff); if(--count==0) break;
206 if((count&~3) != 0) {
210 int[] page = readPages[a >>> (PAGE_SHIFT-2)];
211 if(page == null) throw new ReadFaultException(a<<2);
212 int index = a&(PAGE_WORDS-1);
213 int n = min(c,PAGE_WORDS-index);
214 if(page != _emptyPage) {
215 for(int i=0;i<n;i++,x+=4) {
216 int word = page[index+i];
217 buf[x+0] = (byte)((word>>>24)&0xff); buf[x+1] = (byte)((word>>>16)&0xff);
218 buf[x+2] = (byte)((word>>> 8)&0xff); buf[x+3] = (byte)((word>>> 0)&0xff);
223 addr = a<<2; count &=3;
226 int word = memRead(addr);
228 case 3: buf[x+2] = (byte)((word>>>8)&0xff);
229 case 2: buf[x+1] = (byte)((word>>>16)&0xff);
230 case 1: buf[x+0] = (byte)((word>>>24)&0xff);
235 /** Copies <i>length</i> bytes OUT OF the java array <i>a</i> into the processes memory
236 space at <i>addr</i> */
237 public final void copyout(byte[] buf, int addr, int count) throws FaultException {
240 int word = memRead(addr&~3);
242 case 1: word = (word&0xff00ffff)|((buf[x++]&0xff)<<16); if(--count==0) break;
243 case 2: word = (word&0xffff00ff)|((buf[x++]&0xff)<< 8); if(--count==0) break;
244 case 3: word = (word&0xffffff00)|((buf[x++]&0xff)<< 0); if(--count==0) break;
246 memWrite(addr&~3,word);
249 if((count&~3) != 0) {
253 int[] page = writePages[a >>> (PAGE_SHIFT-2)];
254 if(page == null) throw new WriteFaultException(a<<2);
255 if(page == _emptyPage) page = initPage(a >>> (PAGE_SHIFT-2));
256 int index = a&(PAGE_WORDS-1);
257 int n = min(c,PAGE_WORDS-index);
258 for(int i=0;i<n;i++,x+=4)
259 page[index+i] = ((buf[x+0]&0xff)<<24)|((buf[x+1]&0xff)<<16)|((buf[x+2]&0xff)<<8)|((buf[x+3]&0xff)<<0);
262 addr = a<<2; count&=3;
265 int word = memRead(addr);
267 case 1: word = (word&0x00ffffff)|((buf[x+0]&0xff)<<24); break;
268 case 2: word = (word&0x0000ffff)|((buf[x+0]&0xff)<<24)|((buf[x+1]&0xff)<<16); break;
269 case 3: word = (word&0x000000ff)|((buf[x+0]&0xff)<<24)|((buf[x+1]&0xff)<<16)|((buf[x+2]&0xff)<<8); break;
275 public final void memcpy(int dst, int src, int count) throws FaultException {
276 if((dst&3) == 0 && (src&3)==0) {
277 if((count&~3) != 0) {
282 int[] srcPage = readPages[s>>>(PAGE_SHIFT-2)];
283 if(srcPage == null) throw new ReadFaultException(s<<2);
284 int[] dstPage = writePages[d>>>(PAGE_SHIFT-2)];
285 if(dstPage == null) throw new WriteFaultException(d<<2);
286 int srcIndex = (s&(PAGE_WORDS-1));
287 int dstIndex = (d&(PAGE_WORDS-1));
288 int n = min(c,PAGE_WORDS-max(srcIndex,dstIndex));
289 if(srcPage != _emptyPage) {
290 if(dstPage == _emptyPage) dstPage = initPage(d>>>(PAGE_SHIFT-2));
291 System.arraycopy(srcPage,srcIndex,dstPage,dstIndex,n);
292 } else if(srcPage == _emptyPage && dstPage != _emptyPage) {
293 Arrays.fill(dstPage,dstIndex,dstIndex+n,0);
295 s += n; d += n; c -= n;
297 src = s<<2; dst = d<<2; count&=3;
300 int word1 = memRead(src);
301 int word2 = memRead(dst);
303 case 1: memWrite(dst,(word1&0xff000000)|(word2&0x00ffffff)); break;
304 case 2: memWrite(dst,(word1&0xffff0000)|(word2&0x0000ffff)); break;
305 case 3: memWrite(dst,(word1&0xffffff00)|(word2&0x000000ff)); break;
310 int n = min(count,MAX_CHUNK);
311 byte[] buf = byteBuf(n);
314 count -= n; src += n; dst += n;
319 public final void memset(int addr, int ch, int count) throws FaultException {
320 int fourBytes = ((ch&0xff)<<24)|((ch&0xff)<<16)|((ch&0xff)<<8)|((ch&0xff)<<0);
322 int word = memRead(addr&~3);
324 case 1: word = (word&0xff00ffff)|((ch&0xff)<<16); if(--count==0) break;
325 case 2: word = (word&0xffff00ff)|((ch&0xff)<< 8); if(--count==0) break;
326 case 3: word = (word&0xffffff00)|((ch&0xff)<< 0); if(--count==0) break;
328 memWrite(addr&~3,word);
331 if((count&~3) != 0) {
335 int[] page = readPages[a>>>(PAGE_SHIFT-2)];
336 if(page == null) throw new WriteFaultException(a<<2);
337 int index = (a&(PAGE_WORDS-1));
338 int n = min(c,PAGE_WORDS-index);
339 if(page != _emptyPage || ch != 0) {
340 if(page == _emptyPage) page = initPage(a>>>(PAGE_SHIFT-2));
341 Arrays.fill(page,index,index+n,fourBytes);
345 addr = a<<2; count&=3;
348 int word = memRead(addr);
350 case 1: word = (word&0x00ffffff)|(fourBytes&0xff000000); break;
351 case 2: word = (word&0x0000ffff)|(fourBytes&0xffff0000); break;
352 case 3: word = (word&0x000000ff)|(fourBytes&0xffffff00); break;
358 /** Read a word from the processes memory at <i>addr</i> */
359 public final int memRead(int addr) throws ReadFaultException {
360 if((addr & 3) != 0) throw new ReadFaultException(addr);
361 return unsafeMemRead(addr);
364 protected final int unsafeMemRead(int addr) throws ReadFaultException {
365 int page = addr >>> PAGE_SHIFT;
366 int entry = (addr >>> 2) & (PAGE_WORDS-1);
368 return readPages[page][entry];
369 } catch(ArrayIndexOutOfBoundsException e) {
370 if(page < 0) throw e; // should never happen
371 if(page >= readPages.length) throw new ReadFaultException(addr);
372 if(readPages[page] != _emptyPage) throw e; // should never happen
375 } catch(NullPointerException e) {
376 throw new ReadFaultException(addr);
380 /** Writes a word to the processes memory at <i>addr</i> */
381 public final void memWrite(int addr, int value) throws WriteFaultException {
382 if((addr & 3) != 0) throw new WriteFaultException(addr);
383 unsafeMemWrite(addr,value);
386 protected final void unsafeMemWrite(int addr, int value) throws WriteFaultException {
387 int page = addr >>> PAGE_SHIFT;
388 int entry = (addr>>>2)&(PAGE_WORDS-1);
390 writePages[page][entry] = value;
391 } catch(ArrayIndexOutOfBoundsException e) {
392 if(page < 0) throw e;// should never happen
393 if(page >= writePages.length) throw new WriteFaultException(addr);
394 if(readPages[page] != _emptyPage) throw e; // should never happen
396 writePages[page][entry] = value;
397 } catch(NullPointerException e) {
398 throw new WriteFaultException(addr);
402 /** Created a new non-empty writable page at page number <i>page</i> */
403 private final int[] initPage(int page) { return initPage(page,false); }
404 /** Created a new non-empty page at page number <i>page</i>. If <i>ro</i> is set the page will be read-only */
405 private final int[] initPage(int page, boolean ro) {
406 int[] buf = new int[PAGE_WORDS];
407 writePages[page] = ro ? null : buf;
408 readPages[page] = buf;
412 /** Returns the exit status of the process. (only valid if state == DONE)
413 @see Runtime#state */
414 public final int exitStatus() {
415 if(state != DONE) throw new IllegalStateException("exitStatus() called in an inappropriate state");
419 private int addStringArray(String[] strings, int topAddr) {
420 int count = strings.length;
421 int total = 0; /* null last table entry */
422 for(int i=0;i<count;i++) total += strings[i].length() + 1;
423 if(total >= ARGS_MAX) throw new IllegalArgumentException("arguments/environ too big");
424 total += (count+1)*4;
425 int start = (topAddr - total)&~3;
426 int addr = start + (count+1)*4;
427 int[] table = new int[count+1];
429 for(int i=0;i<count;i++) {
430 byte[] a = getBytes(strings[i]);
432 copyout(a,addr,a.length);
433 memset(addr+a.length,0,1);
434 addr += a.length + 1;
437 for(int i=0;i<count+1;i++) {
438 memWrite(addr,table[i]);
441 } catch(FaultException e) {
442 // should never happen
443 throw new Error(e.toString());
448 protected String[] createEnv(String[] extra) { if(extra == null) extra = new String[0]; return extra; }
450 /** Sets word number <i>index</i> in the _user_info table to <i>word</i>
451 * The user_info table is a chunk of memory in the program's memory defined by the
452 * symbol "user_info". The compiler/interpreter automatically determine the size
453 * and location of the user_info table from the ELF symbol table. setUserInfo and
454 * getUserInfo are used to modify the words in the user_info table. */
455 public void setUserInfo(int index, int word) {
456 if(index < 0 || index >= userInfoSize/4) throw new IndexOutOfBoundsException("setUserInfo called with index >= " + (userInfoSize/4));
458 memWrite(userInfoBase+index*4,word);
459 } catch(FaultException e) { throw new Error("should never happen: " + e); }
462 /** Returns the word in the _user_info table entry <i>index</i>
463 @see Runtime#setUserInfo(int,int) setUserInfo */
464 public int getUserInfo(int index) {
465 if(index < 0 || index >= userInfoSize/4) throw new IndexOutOfBoundsException("setUserInfo called with index >= " + (userInfoSize/4));
467 return memRead(userInfoBase+index*4);
468 } catch(FaultException e) { throw new Error("should never happen: " + e); }
471 /** Calls _execute() (subclass's execute()) and catches exceptions */
472 private void __execute() {
475 } catch(FaultException e) {
477 sys_exit(128+11); // SIGSEGV
479 } catch(ExecutionException e) {
481 System.err.println(e);
482 sys_exit(128+4); // SIGILL
487 /** Executes the process until the PAUSE syscall is invoked or the process exits. Returns true if the process exited. */
488 public final boolean execute() {
489 if(state != PAUSED) throw new IllegalStateException("execute() called in inappropriate state");
490 if(startTime == 0) startTime = System.currentTimeMillis();
493 if(state != PAUSED && state != DONE) throw new IllegalStateException("execute() ended up in an inappropriate state (" + state + ")");
494 return state == DONE;
497 public final int run() { return run(null); }
498 public final int run(String argv0, String[] rest) {
499 String[] args = new String[rest.length+1];
500 System.arraycopy(rest,0,args,1,rest.length);
504 public final int run(String[] args) { return run(args,null); }
506 /** Runs the process until it exits and returns the exit status.
507 If the process executes the PAUSE syscall execution will be paused for 500ms and a warning will be displayed */
508 public final int run(String[] args, String[] env) {
512 System.err.println("WARNING: Pause requested while executing run()");
513 try { Thread.sleep(500); } catch(InterruptedException e) { /* noop */ }
518 public final void start() { start(null); }
519 public final void start(String[] args) { start(args,null); }
521 /** Initializes the process and prepairs it to be executed with execute() */
522 public final void start(String[] args, String[] environ) {
523 int sp, argsAddr, envAddr;
524 if(state != INITIALIZED) throw new IllegalStateException("start() called in inappropriate state");
526 if(args == null) args = new String[]{getClass().getName()};
528 sp = TOTAL_PAGES*PAGE_SIZE-512;
529 sp = argsAddr = addStringArray(args,sp);
530 sp = envAddr = addStringArray(createEnv(environ),sp);
533 CPUState cpuState = new CPUState();
534 cpuState.r[A0] = argsAddr;
535 cpuState.r[A1] = envAddr;
537 cpuState.r[RA] = 0xdeadbeef;
539 cpuState.pc = entryPoint;
540 setCPUState(cpuState);
547 /** Hook for subclasses to do their own startup */
548 protected void _start() { /* noop */ }
550 // FEATURE: call() that accepts an Object[] array and automatically allocates strings/arrays/etc on the stack
551 public final int call(String sym) throws CallException { return call(sym,0,0,0,0,0,0,0); }
552 public final int call(String sym, int a0) throws CallException { return call(sym,a0,0,0,0,0,0,0); }
553 public final int call(String sym, int a0, int a1) throws CallException { return call(sym,a0,a1,0,0,0,0,0); }
554 public final int call(String sym, int a0, int a1, int a2) throws CallException { return call(sym,a0,a1,a2,0,0,0,0); }
555 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); }
556 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); }
557 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); }
559 /** Calls a function in the process with the given arguments */
560 public final int call(String sym, int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws CallException {
561 int func = lookupSymbol(sym);
562 if(func == -1) throw new CallException(sym + " not found");
563 int helper = lookupSymbol("_call_helper");
564 if(helper == -1) throw new CallException("_call_helper not found");
565 return call(helper,func,a0,a1,a2,a3,a4,a5,a6);
568 /** Executes the code at <i>addr</i> in the process setting A0-A3 and S0-S3 to the given arguments
569 and returns the contents of V1 when the the pause syscall is invoked */
570 public final int call(int addr, int a0, int a1, int a2, int a3, int s0, int s1, int s2, int s3) {
571 if(state != PAUSED && state != CALLJAVA) throw new IllegalStateException("call() called in inappropriate state");
572 int oldState = state;
573 CPUState saved = getCPUState();
574 CPUState cpustate = new CPUState();
575 cpustate.r[SP] = saved.r[SP]&~15;
576 cpustate.r[RA] = 0xdeadbeef;
590 setCPUState(cpustate);
592 cpustate = getCPUState();
596 System.out.println("WARNING: Process exit()ed while servicing a call() request");
600 return cpustate.r[V1];
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 public static interface CallJavaCB { public int call(int a, int b, int c, int d); }
890 private int sys_calljava(int a, int b, int c, int d) {
891 if(state != RUNNING) throw new IllegalStateException("wound up calling sys_calljava while not in RUNNING");
892 if(callJavaCB != null) {
894 int ret = callJavaCB.call(a,b,c,d);
898 System.err.println("WARNING: calljava syscall invoked without a calljava callback set");
903 private int sys_pause() {
908 private int sys_getpagesize() { return TOTAL_PAGES == 1 ? 4096 : PAGE_SIZE; }
910 private int sys_isatty(int fdn) {
911 if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
912 if(fds[fdn] == null) return -EBADFD;
913 return fds[fdn].isatty() ? 1 : 0;
917 /** Hook for subclasses to do something when the process exits (MUST set state = DONE) */
918 protected void _exit() { state = DONE; }
919 private int sys_exit(int status) {
921 for(int i=0;i<fds.length;i++) if(fds[i] != null) sys_close(i);
926 private int sys_fcntl(int fdn, int cmd, int arg) {
927 final int F_DUPFD = 0;
928 final int F_GETFL = 3;
931 if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
932 if(fds[fdn] == null) return -EBADFD;
937 if(arg < 0 || arg >= OPEN_MAX) return -EINVAL;
938 for(i=arg;i<OPEN_MAX;i++) if(fds[i]==null) break;
939 if(i==OPEN_MAX) return -EMFILE;
944 if(fd.writable() && fd.readable()) flags = 2;
945 else if(fd.writable()) flags = 1;
948 System.err.println("WARNING: Unknown fcntl command: " + cmd);
953 /** The syscall dispatcher.
954 The should be called by subclasses when the syscall instruction is invoked.
955 <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
956 the contenst of A0, A1, A2, and A3. The call MAY change the state
957 @see Runtime#state state */
958 protected int syscall(int syscall, int a, int b, int c, int d) {
960 case SYS_null: return 0;
961 case SYS_exit: return sys_exit(a);
962 case SYS_pause: return sys_pause();
963 case SYS_write: return sys_write(a,b,c);
964 case SYS_fstat: return sys_fstat(a,b);
965 case SYS_sbrk: return sbrk(a);
966 case SYS_open: return sys_open(a,b,c);
967 case SYS_close: return sys_close(a);
968 case SYS_read: return sys_read(a,b,c);
969 case SYS_lseek: return sys_lseek(a,b,c);
970 case SYS_getpid: return sys_getpid();
971 case SYS_calljava: return sys_calljava(a,b,c,d);
972 case SYS_gettimeofday: return sys_gettimeofday(a,b);
973 case SYS_sleep: return sys_sleep(a);
974 case SYS_times: return sys_times(a);
975 case SYS_getpagesize: return sys_getpagesize();
976 case SYS_isatty: return sys_isatty(a);
977 case SYS_fcntl: return sys_fcntl(a,b,c);
978 case SYS_sysconf: return sys_sysconf(a);
989 System.err.println("Attempted to use a UnixRuntime syscall in Runtime (" + syscall + ")");
992 System.err.println("Attempted to use unknown syscall: " + syscall);
997 public int xmalloc(int size) { int p=malloc(size); if(p==0) throw new RuntimeException("malloc() failed"); return p; }
998 public int xrealloc(int addr,int newsize) { int p=realloc(addr,newsize); if(p==0) throw new RuntimeException("realloc() failed"); return p; }
999 public int realloc(int addr, int newsize) { try { return call("realloc",addr,newsize); } catch(CallException e) { return 0; } }
1000 public int malloc(int size) { try { return call("malloc",size); } catch(CallException e) { return 0; } }
1001 public void free(int p) { try { if(p!=0) call("free",p); } catch(CallException e) { /*noop*/ } }
1003 /** Helper function to create a cstring in main memory */
1004 public int strdup(String s) {
1006 if(s == null) s = "(null)";
1007 byte[] a2 = getBytes(s);
1008 a = new byte[a2.length+1];
1009 System.arraycopy(a2,0,a,0,a2.length);
1010 int addr = malloc(a.length);
1011 if(addr == 0) return 0;
1013 copyout(a,addr,a.length);
1014 } catch(FaultException e) {
1021 /** Helper function to read a cstring from main memory */
1022 public String cstring(int addr) throws ReadFaultException {
1023 StringBuffer sb = new StringBuffer();
1025 int word = memRead(addr&~3);
1027 case 0: if(((word>>>24)&0xff)==0) return sb.toString(); sb.append((char)((word>>>24)&0xff)); addr++;
1028 case 1: if(((word>>>16)&0xff)==0) return sb.toString(); sb.append((char)((word>>>16)&0xff)); addr++;
1029 case 2: if(((word>>> 8)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 8)&0xff)); addr++;
1030 case 3: if(((word>>> 0)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 0)&0xff)); addr++;
1035 /** File Descriptor class */
1036 public static abstract class FD {
1037 private int refCount = 1;
1039 /** returns true if the fd is readable */
1040 public boolean readable() { return false; }
1041 /** returns true if the fd is writable */
1042 public boolean writable() { return false; }
1044 /** Read some bytes. Should return the number of bytes read, 0 on EOF, or throw an IOException on error */
1045 public int read(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); }
1046 /** Write. Should return the number of bytes written or throw an IOException on error */
1047 public int write(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); }
1049 /** Seek in the filedescriptor. Whence is SEEK_SET, SEEK_CUR, or SEEK_END. Should return -1 on error or the new position. */
1050 public int seek(int n, int whence) throws IOException { return -1; }
1052 /** Should return true if this is a tty */
1053 public boolean isatty() { return false; }
1055 private FStat cachedFStat = null;
1056 public final FStat fstat() {
1057 if(cachedFStat == null) cachedFStat = _fstat();
1061 protected abstract FStat _fstat();
1063 /** Closes the fd */
1064 public final void close() { if(--refCount==0) _close(); }
1065 protected void _close() { /* noop*/ }
1067 FD dup() { refCount++; return this; }
1070 /** FileDescriptor class for normal files */
1071 public abstract static class SeekableFD extends FD {
1072 private final int flags;
1073 private final Seekable data;
1074 public boolean readable() { return (flags&3) != WR_ONLY; }
1075 public boolean writable() { return (flags&3) != RD_ONLY; }
1077 SeekableFD(Seekable data, int flags) { this.data = data; this.flags = flags; }
1079 protected abstract FStat _fstat();
1081 public int seek(int n, int whence) throws IOException {
1083 case SEEK_SET: break;
1084 case SEEK_CUR: n += data.pos(); break;
1085 case SEEK_END: n += data.length(); break;
1092 public int write(byte[] a, int off, int length) throws IOException {
1093 // NOTE: There is race condition here but we can't fix it in pure java
1094 if((flags&O_APPEND) != 0) seek(0,SEEK_END);
1095 return data.write(a,off,length);
1098 public int read(byte[] a, int off, int length) throws IOException {
1099 int n = data.read(a,off,length);
1100 return n < 0 ? 0 : n;
1103 protected void _close() { try { data.close(); } catch(IOException e) { /*ignore*/ } }
1106 public static class OutputStreamFD extends FD {
1107 private OutputStream os;
1108 public boolean writable() { return true; }
1109 public OutputStreamFD(OutputStream os) { this.os = os; }
1110 public int write(byte[] a, int off, int length) throws IOException { os.write(a,off,length); return length; }
1111 public void _close() { try { os.close(); } catch(IOException e) { /*ignore*/ } }
1112 public FStat _fstat() { return new FStat(); }
1115 public static class InputStreamFD extends FD {
1116 private InputStream is;
1117 public boolean readable() { return true; }
1118 public InputStreamFD(InputStream is) { this.is = is; }
1119 public int read(byte[] a, int off, int length) throws IOException { int n = is.read(a,off,length); return n < 0 ? 0 : n; }
1120 public void _close() { try { is.close(); } catch(IOException e) { /*ignore*/ } }
1121 public FStat _fstat() { return new FStat(); }
1124 protected static class StdinFD extends InputStreamFD {
1125 public StdinFD(InputStream is) { super(is); }
1126 public void _close() { /* noop */ }
1127 public FStat _fstat() { return new FStat() { public int type() { return S_IFCHR; } }; }
1128 public boolean isatty() { return true; }
1130 protected static class StdoutFD extends OutputStreamFD {
1131 public StdoutFD(OutputStream os) { super(os); }
1132 public void _close() { /* noop */ }
1133 public FStat _fstat() { return new FStat() { public int type() { return S_IFCHR; } }; }
1134 public boolean isatty() { return true; }
1137 public static class FStat {
1138 public static final int S_IFIFO = 0010000;
1139 public static final int S_IFCHR = 0020000;
1140 public static final int S_IFDIR = 0040000;
1141 public static final int S_IFREG = 0100000;
1143 public int dev() { return -1; }
1144 // FEATURE: inode numbers are calculated inconsistently throught the runtime
1145 public int inode() { return hashCode() & 0xfffff; }
1146 public int mode() { return 0; }
1147 public int type() { return S_IFIFO; }
1148 public int nlink() { return 0; }
1149 public int uid() { return 0; }
1150 public int gid() { return 0; }
1151 public int size() { return 0; }
1152 public int atime() { return 0; }
1153 public int mtime() { return 0; }
1154 public int ctime() { return 0; }
1155 public int blksize() { return 512; }
1156 public int blocks() { return (size()+blksize()-1)/blksize(); }
1159 protected static class HostFStat extends FStat {
1160 private final File f;
1161 private final boolean executable;
1162 public HostFStat(File f) {
1164 String name = f.getName();
1165 // FEATURE: This is ugly.. maybe we should do a file(1) type check
1166 executable = name.endsWith(".mips") || name.endsWith(".sh");
1168 public int dev() { return 1; }
1169 public int inode() { return f.getName().hashCode() & 0xffff; }
1170 public int type() { return f.isDirectory() ? S_IFDIR : S_IFREG; }
1171 public int nlink() { return 1; }
1174 boolean canread = f.canRead();
1175 if(canread && (executable || f.isDirectory())) mode |= 0111;
1176 if(canread) mode |= 0444;
1177 if(f.canWrite()) mode |= 0222;
1180 public int size() { return (int) f.length(); }
1181 public int mtime() { return (int)(f.lastModified()/1000); }
1185 public class ReadFaultException extends FaultException {
1186 public ReadFaultException(int addr) { super(addr); }
1188 public class WriteFaultException extends FaultException {
1189 public WriteFaultException(int addr) { super(addr); }
1191 public abstract class FaultException extends ExecutionException {
1193 public FaultException(int addr) { super("fault at: " + toHex(addr)); this.addr = addr; }
1195 public static class ExecutionException extends Exception {
1196 private String message = "(null)";
1197 private String location = "(unknown)";
1198 public ExecutionException() { /* noop */ }
1199 public ExecutionException(String s) { if(s != null) message = s; }
1200 void setLocation(String s) { location = s == null ? "(unknown)" : s; }
1201 public final String getMessage() { return message + " at " + location; }
1203 public static class CallException extends Exception {
1204 public CallException(String s) { super(s); }
1207 protected static class ErrnoException extends IOException {
1209 public ErrnoException(int errno) { super("Errno: " + errno); this.errno = errno; }
1213 protected static class CPUState {
1214 public CPUState() { /* noop */ }
1216 public int[] r = new int[32];
1217 /* Floating point regs */
1218 public int[] f = new int[32];
1224 // Null pointer check helper function
1225 protected final void nullPointerCheck(int addr) throws ExecutionException {
1226 if(TOTAL_PAGES==1 ? addr < 65536 : (addr>>>PAGE_SHIFT) < 16)
1227 throw new ExecutionException("Attempted to dereference a null pointer " + toHex(addr));
1230 // Utility functions
1231 private byte[] byteBuf(int size) {
1232 if(_byteBuf==null) _byteBuf = new byte[size];
1233 else if(_byteBuf.length < size)
1234 _byteBuf = new byte[min(max(_byteBuf.length*2,size),MAX_CHUNK)];
1238 protected static String getSystemProperty(String key) {
1240 return System.getProperty(key);
1241 } catch(SecurityException e) {
1246 /** Decode an packed string.. FEATURE: document this better */
1247 protected static final int[] decodeData(String s, int words) {
1248 if(s.length() % 8 != 0) throw new IllegalArgumentException("string length must be a multiple of 8");
1249 if((s.length() / 8) * 7 < words*4) throw new IllegalArgumentException("string isn't big enough");
1250 int[] buf = new int[words];
1251 int prev = 0, left=0;
1252 for(int i=0,n=0;n<words;i+=8) {
1254 for(int j=0;j<8;j++) { l <<= 7; l |= s.charAt(i+j) & 0x7f; }
1255 if(left > 0) buf[n++] = prev | (int)(l>>>(56-left));
1256 if(n < words) buf[n++] = (int) (l >>> (24-left));
1257 left = (left + 8) & 0x1f;
1258 prev = (int)(l << left);
1263 protected static byte[] getBytes(String s) {
1265 return s.getBytes("ISO-8859-1");
1266 } catch(UnsupportedEncodingException e) {
1267 return null; // should never happen
1271 protected final static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); }
1272 protected final static int min(int a, int b) { return a < b ? a : b; }
1273 protected final static int max(int a, int b) { return a > b ? a : b; }