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 {
197 if(count == 0) return;
199 int word = memRead(addr&~3);
201 case 1: buf[x++] = (byte)((word>>>16)&0xff); if(--count==0) break;
202 case 2: buf[x++] = (byte)((word>>> 8)&0xff); if(--count==0) break;
203 case 3: buf[x++] = (byte)((word>>> 0)&0xff); if(--count==0) break;
207 if((count&~3) != 0) {
211 int[] page = readPages[a >>> (PAGE_SHIFT-2)];
212 if(page == null) throw new ReadFaultException(a<<2);
213 int index = a&(PAGE_WORDS-1);
214 int n = min(c,PAGE_WORDS-index);
215 if(page != _emptyPage) {
216 for(int i=0;i<n;i++,x+=4) {
217 int word = page[index+i];
218 buf[x+0] = (byte)((word>>>24)&0xff); buf[x+1] = (byte)((word>>>16)&0xff);
219 buf[x+2] = (byte)((word>>> 8)&0xff); buf[x+3] = (byte)((word>>> 0)&0xff);
224 addr = a<<2; count &=3;
227 int word = memRead(addr);
229 case 3: buf[x+2] = (byte)((word>>>8)&0xff);
230 case 2: buf[x+1] = (byte)((word>>>16)&0xff);
231 case 1: buf[x+0] = (byte)((word>>>24)&0xff);
236 /** Copies <i>length</i> bytes OUT OF the java array <i>a</i> into the processes memory
237 space at <i>addr</i> */
238 public final void copyout(byte[] buf, int addr, int count) throws FaultException {
240 if(count == 0) return;
242 int word = memRead(addr&~3);
244 case 1: word = (word&0xff00ffff)|((buf[x++]&0xff)<<16); if(--count==0) break;
245 case 2: word = (word&0xffff00ff)|((buf[x++]&0xff)<< 8); if(--count==0) break;
246 case 3: word = (word&0xffffff00)|((buf[x++]&0xff)<< 0); if(--count==0) break;
248 memWrite(addr&~3,word);
251 if((count&~3) != 0) {
255 int[] page = writePages[a >>> (PAGE_SHIFT-2)];
256 if(page == null) throw new WriteFaultException(a<<2);
257 if(page == _emptyPage) page = initPage(a >>> (PAGE_SHIFT-2));
258 int index = a&(PAGE_WORDS-1);
259 int n = min(c,PAGE_WORDS-index);
260 for(int i=0;i<n;i++,x+=4)
261 page[index+i] = ((buf[x+0]&0xff)<<24)|((buf[x+1]&0xff)<<16)|((buf[x+2]&0xff)<<8)|((buf[x+3]&0xff)<<0);
264 addr = a<<2; count&=3;
267 int word = memRead(addr);
269 case 1: word = (word&0x00ffffff)|((buf[x+0]&0xff)<<24); break;
270 case 2: word = (word&0x0000ffff)|((buf[x+0]&0xff)<<24)|((buf[x+1]&0xff)<<16); break;
271 case 3: word = (word&0x000000ff)|((buf[x+0]&0xff)<<24)|((buf[x+1]&0xff)<<16)|((buf[x+2]&0xff)<<8); break;
277 public final void memcpy(int dst, int src, int count) throws FaultException {
278 if((dst&3) == 0 && (src&3)==0) {
279 if((count&~3) != 0) {
284 int[] srcPage = readPages[s>>>(PAGE_SHIFT-2)];
285 if(srcPage == null) throw new ReadFaultException(s<<2);
286 int[] dstPage = writePages[d>>>(PAGE_SHIFT-2)];
287 if(dstPage == null) throw new WriteFaultException(d<<2);
288 int srcIndex = (s&(PAGE_WORDS-1));
289 int dstIndex = (d&(PAGE_WORDS-1));
290 int n = min(c,PAGE_WORDS-max(srcIndex,dstIndex));
291 if(srcPage != _emptyPage) {
292 if(dstPage == _emptyPage) dstPage = initPage(d>>>(PAGE_SHIFT-2));
293 System.arraycopy(srcPage,srcIndex,dstPage,dstIndex,n);
294 } else if(srcPage == _emptyPage && dstPage != _emptyPage) {
295 Arrays.fill(dstPage,dstIndex,dstIndex+n,0);
297 s += n; d += n; c -= n;
299 src = s<<2; dst = d<<2; count&=3;
302 int word1 = memRead(src);
303 int word2 = memRead(dst);
305 case 1: memWrite(dst,(word1&0xff000000)|(word2&0x00ffffff)); break;
306 case 2: memWrite(dst,(word1&0xffff0000)|(word2&0x0000ffff)); break;
307 case 3: memWrite(dst,(word1&0xffffff00)|(word2&0x000000ff)); break;
312 int n = min(count,MAX_CHUNK);
313 byte[] buf = byteBuf(n);
316 count -= n; src += n; dst += n;
321 public final void memset(int addr, int ch, int count) throws FaultException {
322 int fourBytes = ((ch&0xff)<<24)|((ch&0xff)<<16)|((ch&0xff)<<8)|((ch&0xff)<<0);
324 int word = memRead(addr&~3);
326 case 1: word = (word&0xff00ffff)|((ch&0xff)<<16); if(--count==0) break;
327 case 2: word = (word&0xffff00ff)|((ch&0xff)<< 8); if(--count==0) break;
328 case 3: word = (word&0xffffff00)|((ch&0xff)<< 0); if(--count==0) break;
330 memWrite(addr&~3,word);
333 if((count&~3) != 0) {
337 int[] page = readPages[a>>>(PAGE_SHIFT-2)];
338 if(page == null) throw new WriteFaultException(a<<2);
339 int index = (a&(PAGE_WORDS-1));
340 int n = min(c,PAGE_WORDS-index);
341 if(page != _emptyPage || ch != 0) {
342 if(page == _emptyPage) page = initPage(a>>>(PAGE_SHIFT-2));
343 Arrays.fill(page,index,index+n,fourBytes);
347 addr = a<<2; count&=3;
350 int word = memRead(addr);
352 case 1: word = (word&0x00ffffff)|(fourBytes&0xff000000); break;
353 case 2: word = (word&0x0000ffff)|(fourBytes&0xffff0000); break;
354 case 3: word = (word&0x000000ff)|(fourBytes&0xffffff00); break;
360 /** Read a word from the processes memory at <i>addr</i> */
361 public final int memRead(int addr) throws ReadFaultException {
362 if((addr & 3) != 0) throw new ReadFaultException(addr);
363 return unsafeMemRead(addr);
366 protected final int unsafeMemRead(int addr) throws ReadFaultException {
367 int page = addr >>> PAGE_SHIFT;
368 int entry = (addr >>> 2) & (PAGE_WORDS-1);
370 return readPages[page][entry];
371 } catch(ArrayIndexOutOfBoundsException e) {
372 if(page < 0) throw e; // should never happen
373 if(page >= readPages.length) throw new ReadFaultException(addr);
374 if(readPages[page] != _emptyPage) throw e; // should never happen
377 } catch(NullPointerException e) {
378 throw new ReadFaultException(addr);
382 /** Writes a word to the processes memory at <i>addr</i> */
383 public final void memWrite(int addr, int value) throws WriteFaultException {
384 if((addr & 3) != 0) throw new WriteFaultException(addr);
385 unsafeMemWrite(addr,value);
388 protected final void unsafeMemWrite(int addr, int value) throws WriteFaultException {
389 int page = addr >>> PAGE_SHIFT;
390 int entry = (addr>>>2)&(PAGE_WORDS-1);
392 writePages[page][entry] = value;
393 } catch(ArrayIndexOutOfBoundsException e) {
394 if(page < 0) throw e;// should never happen
395 if(page >= writePages.length) throw new WriteFaultException(addr);
396 if(readPages[page] != _emptyPage) throw e; // should never happen
398 writePages[page][entry] = value;
399 } catch(NullPointerException e) {
400 throw new WriteFaultException(addr);
404 /** Created a new non-empty writable page at page number <i>page</i> */
405 private final int[] initPage(int page) { return initPage(page,false); }
406 /** Created a new non-empty page at page number <i>page</i>. If <i>ro</i> is set the page will be read-only */
407 private final int[] initPage(int page, boolean ro) {
408 int[] buf = new int[PAGE_WORDS];
409 writePages[page] = ro ? null : buf;
410 readPages[page] = buf;
414 /** Returns the exit status of the process. (only valid if state == DONE)
415 @see Runtime#state */
416 public final int exitStatus() {
417 if(state != DONE) throw new IllegalStateException("exitStatus() called in an inappropriate state");
421 private int addStringArray(String[] strings, int topAddr) {
422 int count = strings.length;
423 int total = 0; /* null last table entry */
424 for(int i=0;i<count;i++) total += strings[i].length() + 1;
425 if(total >= ARGS_MAX) throw new IllegalArgumentException("arguments/environ too big");
426 total += (count+1)*4;
427 int start = (topAddr - total)&~3;
428 int addr = start + (count+1)*4;
429 int[] table = new int[count+1];
431 for(int i=0;i<count;i++) {
432 byte[] a = getBytes(strings[i]);
434 copyout(a,addr,a.length);
435 memset(addr+a.length,0,1);
436 addr += a.length + 1;
439 for(int i=0;i<count+1;i++) {
440 memWrite(addr,table[i]);
443 } catch(FaultException e) {
444 // should never happen
445 throw new Error(e.toString());
450 protected String[] createEnv(String[] extra) { if(extra == null) extra = new String[0]; return extra; }
452 /** Sets word number <i>index</i> in the _user_info table to <i>word</i>
453 * The user_info table is a chunk of memory in the program's memory defined by the
454 * symbol "user_info". The compiler/interpreter automatically determine the size
455 * and location of the user_info table from the ELF symbol table. setUserInfo and
456 * getUserInfo are used to modify the words in the user_info table. */
457 public void setUserInfo(int index, int word) {
458 if(index < 0 || index >= userInfoSize/4) throw new IndexOutOfBoundsException("setUserInfo called with index >= " + (userInfoSize/4));
460 memWrite(userInfoBase+index*4,word);
461 } catch(FaultException e) { throw new Error("should never happen: " + e); }
464 /** Returns the word in the _user_info table entry <i>index</i>
465 @see Runtime#setUserInfo(int,int) setUserInfo */
466 public int getUserInfo(int index) {
467 if(index < 0 || index >= userInfoSize/4) throw new IndexOutOfBoundsException("setUserInfo called with index >= " + (userInfoSize/4));
469 return memRead(userInfoBase+index*4);
470 } catch(FaultException e) { throw new Error("should never happen: " + e); }
473 /** Calls _execute() (subclass's execute()) and catches exceptions */
474 private void __execute() {
477 } catch(FaultException e) {
479 sys_exit(128+11); // SIGSEGV
481 } catch(ExecutionException e) {
483 System.err.println(e);
484 sys_exit(128+4); // SIGILL
489 /** Executes the process until the PAUSE syscall is invoked or the process exits. Returns true if the process exited. */
490 public final boolean execute() {
491 if(state != PAUSED) throw new IllegalStateException("execute() called in inappropriate state");
492 if(startTime == 0) startTime = System.currentTimeMillis();
495 if(state != PAUSED && state != DONE) throw new IllegalStateException("execute() ended up in an inappropriate state (" + state + ")");
496 return state == DONE;
499 public final int run() { return run(null); }
500 public final int run(String argv0, String[] rest) {
501 String[] args = new String[rest.length+1];
502 System.arraycopy(rest,0,args,1,rest.length);
506 public final int run(String[] args) { return run(args,null); }
508 /** Runs the process until it exits and returns the exit status.
509 If the process executes the PAUSE syscall execution will be paused for 500ms and a warning will be displayed */
510 public final int run(String[] args, String[] env) {
514 System.err.println("WARNING: Pause requested while executing run()");
515 try { Thread.sleep(500); } catch(InterruptedException e) { /* noop */ }
520 public final void start() { start(null); }
521 public final void start(String[] args) { start(args,null); }
523 /** Initializes the process and prepairs it to be executed with execute() */
524 public final void start(String[] args, String[] environ) {
525 int sp, argsAddr, envAddr;
526 if(state != INITIALIZED) throw new IllegalStateException("start() called in inappropriate state");
528 if(args == null) args = new String[]{getClass().getName()};
530 sp = TOTAL_PAGES*PAGE_SIZE-512;
531 sp = argsAddr = addStringArray(args,sp);
532 sp = envAddr = addStringArray(createEnv(environ),sp);
535 CPUState cpuState = new CPUState();
536 cpuState.r[A0] = argsAddr;
537 cpuState.r[A1] = envAddr;
539 cpuState.r[RA] = 0xdeadbeef;
541 cpuState.pc = entryPoint;
542 setCPUState(cpuState);
549 /** Hook for subclasses to do their own startup */
550 protected void _start() { /* noop */ }
552 // FEATURE: call() that accepts an Object[] array and automatically allocates strings/arrays/etc on the stack
553 public final int call(String sym) throws CallException { return call(sym,0,0,0,0,0,0,0); }
554 public final int call(String sym, int a0) throws CallException { return call(sym,a0,0,0,0,0,0,0); }
555 public final int call(String sym, int a0, int a1) throws CallException { return call(sym,a0,a1,0,0,0,0,0); }
556 public final int call(String sym, int a0, int a1, int a2) throws CallException { return call(sym,a0,a1,a2,0,0,0,0); }
557 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); }
558 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); }
559 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); }
561 /** Calls a function in the process with the given arguments */
562 public final int call(String sym, int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws CallException {
563 int func = lookupSymbol(sym);
564 if(func == -1) throw new CallException(sym + " not found");
565 int helper = lookupSymbol("_call_helper");
566 if(helper == -1) throw new CallException("_call_helper not found");
567 return call(helper,func,a0,a1,a2,a3,a4,a5,a6);
570 /** Executes the code at <i>addr</i> in the process setting A0-A3 and S0-S3 to the given arguments
571 and returns the contents of V1 when the the pause syscall is invoked */
572 public final int call(int addr, int a0, int a1, int a2, int a3, int s0, int s1, int s2, int s3) {
573 if(state != PAUSED && state != CALLJAVA) throw new IllegalStateException("call() called in inappropriate state");
574 int oldState = state;
575 CPUState saved = getCPUState();
576 CPUState cpustate = new CPUState();
577 cpustate.r[SP] = saved.r[SP]&~15;
578 cpustate.r[RA] = 0xdeadbeef;
592 setCPUState(cpustate);
594 cpustate = getCPUState();
598 System.out.println("WARNING: Process exit()ed while servicing a call() request");
602 return cpustate.r[V1];
605 /** Determines if the process can access <i>fileName</i>. The default implementation simply logs
606 the request and allows it */
607 protected boolean allowFileAccess(String fileName, boolean write) {
608 //System.err.println("Allowing " + (write?"write":"read-only") + " access to " + fileName);
612 /** Allocated an entry in the FileDescriptor table for <i>fd</i> and returns the number.
613 Returns -1 if the table is full. This can be used by subclasses to use custom file
615 public int addFD(FD fd) {
617 for(i=0;i<OPEN_MAX;i++) if(fds[i] == null) break;
618 if(i==OPEN_MAX) return -1;
623 /** Closes file descriptor <i>fdn</i> and removes it from the file descriptor table */
624 public boolean closeFD(int fdn) {
625 if(fdn < 0 || fdn >= OPEN_MAX) return false;
626 if(fds[fdn] == null) return false;
632 /** Duplicates the file descriptor <i>fdn</i> and returns the new fs */
633 public int dupFD(int fdn) {
635 if(fdn < 0 || fdn >= OPEN_MAX) return -1;
636 if(fds[fdn] == null) return -1;
637 for(i=0;i<OPEN_MAX;i++) if(fds[i] == null) break;
638 if(i==OPEN_MAX) return -1;
639 fds[i] = fds[fdn].dup();
643 // FEATURE: These should be pulled in from UsermodeConstants but fcntl.h is hard to parse
644 public static final int RD_ONLY = 0;
645 public static final int WR_ONLY = 1;
646 public static final int RDWR = 2;
648 public static final int O_CREAT = 0x0200;
649 public static final int O_EXCL = 0x0800;
650 public static final int O_APPEND = 0x0008;
651 public static final int O_TRUNC = 0x0400;
652 public static final int O_NONBLOCK = 0x4000;
654 // FEATURE: Lots of duplicate code between this and UnixRuntime.HostFS.open()
655 protected FD open(String path, int flags, int mode) throws IOException {
656 final File f = new File(path);
657 // NOTE: createNewFile is a Java2 function
658 if((flags & (O_EXCL|O_CREAT)) == (O_EXCL|O_CREAT))
659 if(!f.createNewFile()) throw new ErrnoException(EEXIST);
660 if(!f.exists() && (flags&O_CREAT) == 0) return null;
661 if(f.isDirectory()) return null;
662 final Seekable.File sf = new Seekable.File(path,mode!=RD_ONLY);
663 if((flags&O_TRUNC)!=0) sf.setLength(0);
664 return new SeekableFD(sf,flags) {
665 protected FStat _fstat() { return new HostFStat(f) {
667 try { return sf.length(); } catch(IOException e) { return 0; }
673 /** The open syscall */
674 private int sys_open(int addr, int flags, int mode) {
675 if((flags & O_NONBLOCK) != 0) {
676 System.err.println("WARNING: O_NONBLOCK not supported");
681 FD fd = open(cstring(addr),flags,mode);
682 if(fd == null) return -ENOENT;
690 catch(ErrnoException e) { return -e.errno; }
691 catch(FileNotFoundException e) {
692 if(e.getMessage() != null && e.getMessage().indexOf("Permission denied") >= 0) return -EACCES;
695 catch(IOException e) { return -EIO; }
696 catch(FaultException e) { return -EFAULT; }
699 /** The write syscall */
700 private int sys_write(int fdn, int addr, int count) {
701 count = Math.min(count,MAX_CHUNK);
702 if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
703 if(fds[fdn] == null || !fds[fdn].writable()) return -EBADFD;
705 byte[] buf = byteBuf(count);
706 copyin(addr,buf,count);
707 return fds[fdn].write(buf,0,count);
708 } catch(FaultException e) {
709 System.err.println(e);
711 } catch(IOException e) {
712 // FEATURE: We should support signals and send a SIGPIPE
713 if(e.getMessage().equals("Pipe closed")) return sys_exit(128+13);
714 System.err.println(e);
719 /** The read syscall */
720 private int sys_read(int fdn, int addr, int count) {
721 count = Math.min(count,MAX_CHUNK);
722 if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
723 if(fds[fdn] == null || !fds[fdn].readable()) return -EBADFD;
725 byte[] buf = byteBuf(count);
726 int n = fds[fdn].read(buf,0,count);
729 } catch(FaultException e) {
730 System.err.println(e);
732 } catch(IOException e) {
733 System.err.println(e);
738 /** The close syscall */
739 private int sys_close(int fdn) {
740 return closeFD(fdn) ? 0 : -EBADFD;
744 /** The seek syscall */
745 private int sys_lseek(int fdn, int offset, int whence) {
746 if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
747 if(fds[fdn] == null) return -EBADFD;
748 if(whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END) return -EINVAL;
750 int n = fds[fdn].seek(offset,whence);
751 return n < 0 ? -ESPIPE : n;
752 } catch(IOException e) {
757 /** The stat/fstat syscall helper */
758 int stat(FStat fs, int addr) {
760 memWrite(addr+0,(fs.dev()<<16)|(fs.inode()&0xffff)); // st_dev (top 16), // st_ino (bottom 16)
761 memWrite(addr+4,((fs.type()&0xf000))|(fs.mode()&0xfff)); // st_mode
762 memWrite(addr+8,1<<16); // st_nlink (top 16) // st_uid (bottom 16)
763 memWrite(addr+12,0); // st_gid (top 16) // st_rdev (bottom 16)
764 memWrite(addr+16,fs.size()); // st_size
765 memWrite(addr+20,fs.atime()); // st_atime
766 // memWrite(addr+24,0) // st_spare1
767 memWrite(addr+28,fs.mtime()); // st_mtime
768 // memWrite(addr+32,0) // st_spare2
769 memWrite(addr+36,fs.ctime()); // st_ctime
770 // memWrite(addr+40,0) // st_spare3
771 memWrite(addr+44,fs.blksize()); // st_bklsize;
772 memWrite(addr+48,fs.blocks()); // st_blocks
773 // memWrite(addr+52,0) // st_spare4[0]
774 // memWrite(addr+56,0) // st_spare4[1]
775 } catch(FaultException e) {
776 System.err.println(e);
782 /** The fstat syscall */
783 private int sys_fstat(int fdn, int addr) {
784 if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
785 if(fds[fdn] == null) return -EBADFD;
786 return stat(fds[fdn].fstat(),addr);
795 private int sys_gettimeofday(int timevalAddr, int timezoneAddr) {
796 long now = System.currentTimeMillis();
797 int tv_sec = (int)(now / 1000);
798 int tv_usec = (int)((now%1000)*1000);
800 memWrite(timevalAddr+0,tv_sec);
801 memWrite(timevalAddr+4,tv_usec);
803 } catch(FaultException e) {
808 private int sys_sleep(int sec) {
809 if(sec < 0) sec = Integer.MAX_VALUE;
811 Thread.sleep((long)sec*1000);
813 } catch(InterruptedException e) {
819 #define _CLOCKS_PER_SEC_ 1000
820 #define _CLOCK_T_ unsigned long
828 private int sys_times(int tms) {
829 long now = System.currentTimeMillis();
830 int userTime = (int)((now - startTime)/16);
831 int sysTime = (int)((now - startTime)/16);
835 memWrite(tms+0,userTime);
836 memWrite(tms+4,sysTime);
837 memWrite(tms+8,userTime);
838 memWrite(tms+12,sysTime);
840 } catch(FaultException e) {
846 private int sys_sysconf(int n) {
848 case _SC_CLK_TCK: return 1000;
850 System.err.println("WARNING: Attempted to use unknown sysconf key: " + n);
855 /** The sbrk syscall. This can also be used by subclasses to allocate memory.
856 <i>incr</i> is how much to increase the break by */
857 public int sbrk(int incr) {
858 if(incr < 0) return -ENOMEM;
859 if(incr==0) return brkAddr;
861 int oldBrk = brkAddr;
862 int newBrk = oldBrk + incr;
863 if(TOTAL_PAGES == 1) {
864 CPUState state = getCPUState();
865 if(newBrk >= state.r[SP] - 65536) {
866 System.err.println("WARNING: brk too close to stack pointer");
869 } else if(newBrk >= BRK_LIMIT) {
870 System.err.println("WARNING: Hit BRK_LIMIT");
873 if(TOTAL_PAGES != 1) {
875 for(int i=(oldBrk+PAGE_SIZE-1)>>>PAGE_SHIFT;i<((newBrk+PAGE_SIZE-1)>>>PAGE_SHIFT);i++)
876 readPages[i] = writePages[i] = emptyPage();
877 } catch(OutOfMemoryError e) {
878 System.err.println("WARNING: Caught OOM Exception in sbrk: " + e);
886 /** The getpid syscall */
887 private int sys_getpid() { return getPid(); }
888 protected int getPid() { return 1; }
890 public static interface CallJavaCB { public int call(int a, int b, int c, int d); }
892 private int sys_calljava(int a, int b, int c, int d) {
893 if(state != RUNNING) throw new IllegalStateException("wound up calling sys_calljava while not in RUNNING");
894 if(callJavaCB != null) {
896 int ret = callJavaCB.call(a,b,c,d);
900 System.err.println("WARNING: calljava syscall invoked without a calljava callback set");
905 private int sys_pause() {
910 private int sys_getpagesize() { return TOTAL_PAGES == 1 ? 4096 : PAGE_SIZE; }
912 private int sys_isatty(int fdn) {
913 if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
914 if(fds[fdn] == null) return -EBADFD;
915 return fds[fdn].isatty() ? 1 : 0;
919 /** Hook for subclasses to do something when the process exits (MUST set state = DONE) */
920 protected void _exit() { state = DONE; }
921 private int sys_exit(int status) {
923 for(int i=0;i<fds.length;i++) if(fds[i] != null) sys_close(i);
928 private int sys_fcntl(int fdn, int cmd, int arg) {
929 final int F_DUPFD = 0;
930 final int F_GETFL = 3;
933 if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
934 if(fds[fdn] == null) return -EBADFD;
939 if(arg < 0 || arg >= OPEN_MAX) return -EINVAL;
940 for(i=arg;i<OPEN_MAX;i++) if(fds[i]==null) break;
941 if(i==OPEN_MAX) return -EMFILE;
946 if(fd.writable() && fd.readable()) flags = 2;
947 else if(fd.writable()) flags = 1;
950 System.err.println("WARNING: Unknown fcntl command: " + cmd);
955 /** The syscall dispatcher.
956 The should be called by subclasses when the syscall instruction is invoked.
957 <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
958 the contenst of A0, A1, A2, and A3. The call MAY change the state
959 @see Runtime#state state */
960 protected int syscall(int syscall, int a, int b, int c, int d) {
962 case SYS_null: return 0;
963 case SYS_exit: return sys_exit(a);
964 case SYS_pause: return sys_pause();
965 case SYS_write: return sys_write(a,b,c);
966 case SYS_fstat: return sys_fstat(a,b);
967 case SYS_sbrk: return sbrk(a);
968 case SYS_open: return sys_open(a,b,c);
969 case SYS_close: return sys_close(a);
970 case SYS_read: return sys_read(a,b,c);
971 case SYS_lseek: return sys_lseek(a,b,c);
972 case SYS_getpid: return sys_getpid();
973 case SYS_calljava: return sys_calljava(a,b,c,d);
974 case SYS_gettimeofday: return sys_gettimeofday(a,b);
975 case SYS_sleep: return sys_sleep(a);
976 case SYS_times: return sys_times(a);
977 case SYS_getpagesize: return sys_getpagesize();
978 case SYS_isatty: return sys_isatty(a);
979 case SYS_fcntl: return sys_fcntl(a,b,c);
980 case SYS_sysconf: return sys_sysconf(a);
991 System.err.println("Attempted to use a UnixRuntime syscall in Runtime (" + syscall + ")");
994 System.err.println("Attempted to use unknown syscall: " + syscall);
999 public int xmalloc(int size) { int p=malloc(size); if(p==0) throw new RuntimeException("malloc() failed"); return p; }
1000 public int xrealloc(int addr,int newsize) { int p=realloc(addr,newsize); if(p==0) throw new RuntimeException("realloc() failed"); return p; }
1001 public int realloc(int addr, int newsize) { try { return call("realloc",addr,newsize); } catch(CallException e) { return 0; } }
1002 public int malloc(int size) { try { return call("malloc",size); } catch(CallException e) { return 0; } }
1003 public void free(int p) { try { if(p!=0) call("free",p); } catch(CallException e) { /*noop*/ } }
1005 /** Helper function to create a cstring in main memory */
1006 public int strdup(String s) {
1008 if(s == null) s = "(null)";
1009 byte[] a2 = getBytes(s);
1010 a = new byte[a2.length+1];
1011 System.arraycopy(a2,0,a,0,a2.length);
1012 int addr = malloc(a.length);
1013 if(addr == 0) return 0;
1015 copyout(a,addr,a.length);
1016 } catch(FaultException e) {
1023 /** Helper function to read a cstring from main memory */
1024 public String cstring(int addr) throws ReadFaultException {
1025 StringBuffer sb = new StringBuffer();
1027 int word = memRead(addr&~3);
1029 case 0: if(((word>>>24)&0xff)==0) return sb.toString(); sb.append((char)((word>>>24)&0xff)); addr++;
1030 case 1: if(((word>>>16)&0xff)==0) return sb.toString(); sb.append((char)((word>>>16)&0xff)); addr++;
1031 case 2: if(((word>>> 8)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 8)&0xff)); addr++;
1032 case 3: if(((word>>> 0)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 0)&0xff)); addr++;
1037 /** File Descriptor class */
1038 public static abstract class FD {
1039 private int refCount = 1;
1041 /** returns true if the fd is readable */
1042 public boolean readable() { return false; }
1043 /** returns true if the fd is writable */
1044 public boolean writable() { return false; }
1046 /** Read some bytes. Should return the number of bytes read, 0 on EOF, or throw an IOException on error */
1047 public int read(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); }
1048 /** Write. Should return the number of bytes written or throw an IOException on error */
1049 public int write(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); }
1051 /** Seek in the filedescriptor. Whence is SEEK_SET, SEEK_CUR, or SEEK_END. Should return -1 on error or the new position. */
1052 public int seek(int n, int whence) throws IOException { return -1; }
1054 /** Should return true if this is a tty */
1055 public boolean isatty() { return false; }
1057 private FStat cachedFStat = null;
1058 public final FStat fstat() {
1059 if(cachedFStat == null) cachedFStat = _fstat();
1063 protected abstract FStat _fstat();
1065 /** Closes the fd */
1066 public final void close() { if(--refCount==0) _close(); }
1067 protected void _close() { /* noop*/ }
1069 FD dup() { refCount++; return this; }
1072 /** FileDescriptor class for normal files */
1073 public abstract static class SeekableFD extends FD {
1074 private final int flags;
1075 private final Seekable data;
1076 public boolean readable() { return (flags&3) != WR_ONLY; }
1077 public boolean writable() { return (flags&3) != RD_ONLY; }
1079 SeekableFD(Seekable data, int flags) { this.data = data; this.flags = flags; }
1081 protected abstract FStat _fstat();
1083 public int seek(int n, int whence) throws IOException {
1085 case SEEK_SET: break;
1086 case SEEK_CUR: n += data.pos(); break;
1087 case SEEK_END: n += data.length(); break;
1094 public int write(byte[] a, int off, int length) throws IOException {
1095 // NOTE: There is race condition here but we can't fix it in pure java
1096 if((flags&O_APPEND) != 0) seek(0,SEEK_END);
1097 return data.write(a,off,length);
1100 public int read(byte[] a, int off, int length) throws IOException {
1101 int n = data.read(a,off,length);
1102 return n < 0 ? 0 : n;
1105 protected void _close() { try { data.close(); } catch(IOException e) { /*ignore*/ } }
1108 public static class OutputStreamFD extends FD {
1109 private OutputStream os;
1110 public boolean writable() { return true; }
1111 public OutputStreamFD(OutputStream os) { this.os = os; }
1112 public int write(byte[] a, int off, int length) throws IOException { os.write(a,off,length); return length; }
1113 public void _close() { try { os.close(); } catch(IOException e) { /*ignore*/ } }
1114 public FStat _fstat() { return new FStat(); }
1117 public static class InputStreamFD extends FD {
1118 private InputStream is;
1119 public boolean readable() { return true; }
1120 public InputStreamFD(InputStream is) { this.is = is; }
1121 public int read(byte[] a, int off, int length) throws IOException { int n = is.read(a,off,length); return n < 0 ? 0 : n; }
1122 public void _close() { try { is.close(); } catch(IOException e) { /*ignore*/ } }
1123 public FStat _fstat() { return new FStat(); }
1126 protected static class StdinFD extends InputStreamFD {
1127 public StdinFD(InputStream is) { super(is); }
1128 public void _close() { /* noop */ }
1129 public FStat _fstat() { return new FStat() { public int type() { return S_IFCHR; } }; }
1130 public boolean isatty() { return true; }
1132 protected static class StdoutFD extends OutputStreamFD {
1133 public StdoutFD(OutputStream os) { super(os); }
1134 public void _close() { /* noop */ }
1135 public FStat _fstat() { return new FStat() { public int type() { return S_IFCHR; } }; }
1136 public boolean isatty() { return true; }
1139 public static class FStat {
1140 public static final int S_IFIFO = 0010000;
1141 public static final int S_IFCHR = 0020000;
1142 public static final int S_IFDIR = 0040000;
1143 public static final int S_IFREG = 0100000;
1145 public int dev() { return -1; }
1146 // FEATURE: inode numbers are calculated inconsistently throught the runtime
1147 public int inode() { return hashCode() & 0xfffff; }
1148 public int mode() { return 0; }
1149 public int type() { return S_IFIFO; }
1150 public int nlink() { return 0; }
1151 public int uid() { return 0; }
1152 public int gid() { return 0; }
1153 public int size() { return 0; }
1154 public int atime() { return 0; }
1155 public int mtime() { return 0; }
1156 public int ctime() { return 0; }
1157 public int blksize() { return 512; }
1158 public int blocks() { return (size()+blksize()-1)/blksize(); }
1161 protected static class HostFStat extends FStat {
1162 private final File f;
1163 private final boolean executable;
1164 public HostFStat(File f) {
1166 boolean _executable = false;
1167 // FEATURE: This might be too expensive
1169 FileInputStream fis = new FileInputStream(f);
1170 switch(fis.read()) {
1171 case '\177': _executable = fis.read() == 'E' && fis.read() == 'L' && fis.read() == 'F'; break;
1172 case '#': _executable = fis.read() == '!';
1175 } catch(IOException e) { /* ignore */ }
1176 executable = _executable;
1178 public int dev() { return 1; }
1179 public int inode() { return f.getName().hashCode() & 0xffff; }
1180 public int type() { return f.isDirectory() ? S_IFDIR : S_IFREG; }
1181 public int nlink() { return 1; }
1184 boolean canread = f.canRead();
1185 if(canread && (executable || f.isDirectory())) mode |= 0111;
1186 if(canread) mode |= 0444;
1187 if(f.canWrite()) mode |= 0222;
1190 public int size() { return (int) f.length(); }
1191 public int mtime() { return (int)(f.lastModified()/1000); }
1195 public class ReadFaultException extends FaultException {
1196 public ReadFaultException(int addr) { super(addr); }
1198 public class WriteFaultException extends FaultException {
1199 public WriteFaultException(int addr) { super(addr); }
1201 public abstract class FaultException extends ExecutionException {
1203 public FaultException(int addr) { super("fault at: " + toHex(addr)); this.addr = addr; }
1205 public static class ExecutionException extends Exception {
1206 private String message = "(null)";
1207 private String location = "(unknown)";
1208 public ExecutionException() { /* noop */ }
1209 public ExecutionException(String s) { if(s != null) message = s; }
1210 void setLocation(String s) { location = s == null ? "(unknown)" : s; }
1211 public final String getMessage() { return message + " at " + location; }
1213 public static class CallException extends Exception {
1214 public CallException(String s) { super(s); }
1217 protected static class ErrnoException extends IOException {
1219 public ErrnoException(int errno) { super("Errno: " + errno); this.errno = errno; }
1223 protected static class CPUState {
1224 public CPUState() { /* noop */ }
1226 public int[] r = new int[32];
1227 /* Floating point regs */
1228 public int[] f = new int[32];
1234 // Null pointer check helper function
1235 protected final void nullPointerCheck(int addr) throws ExecutionException {
1236 if(TOTAL_PAGES==1 ? addr < 65536 : (addr>>>PAGE_SHIFT) < 16)
1237 throw new ExecutionException("Attempted to dereference a null pointer " + toHex(addr));
1240 // Utility functions
1241 private byte[] byteBuf(int size) {
1242 if(_byteBuf==null) _byteBuf = new byte[size];
1243 else if(_byteBuf.length < size)
1244 _byteBuf = new byte[min(max(_byteBuf.length*2,size),MAX_CHUNK)];
1248 protected static String getSystemProperty(String key) {
1250 return System.getProperty(key);
1251 } catch(SecurityException e) {
1256 /** Decode an packed string.. FEATURE: document this better */
1257 protected static final int[] decodeData(String s, int words) {
1258 if(s.length() % 8 != 0) throw new IllegalArgumentException("string length must be a multiple of 8");
1259 if((s.length() / 8) * 7 < words*4) throw new IllegalArgumentException("string isn't big enough");
1260 int[] buf = new int[words];
1261 int prev = 0, left=0;
1262 for(int i=0,n=0;n<words;i+=8) {
1264 for(int j=0;j<8;j++) { l <<= 7; l |= s.charAt(i+j) & 0x7f; }
1265 if(left > 0) buf[n++] = prev | (int)(l>>>(56-left));
1266 if(n < words) buf[n++] = (int) (l >>> (24-left));
1267 left = (left + 8) & 0x1f;
1268 prev = (int)(l << left);
1273 protected static byte[] getBytes(String s) {
1275 return s.getBytes("ISO-8859-1");
1276 } catch(UnsupportedEncodingException e) {
1277 return null; // should never happen
1281 protected final static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); }
1282 protected final static int min(int a, int b) { return a < b ? a : b; }
1283 protected final static int max(int a, int b) { return a > b ? a : b; }