1 // Copyright 2003 Brian Alliet
2 // Based on org.xwt.imp.MIPS by Adam Megacz
3 // Portions Copyright 2003 Adam Megacz
7 import org.xwt.mips.util.*;
9 import java.util.Arrays;
11 public abstract class Runtime implements Syscalls, Errno,Unistd,Registers {
12 /** Pages are 4k in size */
13 protected final int PAGE_SIZE;
14 protected final int PAGE_WORDS;
15 protected final int PAGE_SHIFT;
16 protected final int TOTAL_PAGES;
17 /** This is the upper limit of the pages allocated by the sbrk() syscall. */
18 protected final int BRK_LIMIT;
19 protected final int STACK_BOTTOM;
21 /** This is the maximum size of command line arguments */
22 public final static int ARGS_MAX = 1024*1024;
24 /** True if we allow empty pages (_emptyPage) to exist in memory.
25 Empty pages are pages which are allocated by the program but do not contain any
26 data yet (they are all 0s). If empty pages are allowed subclasses must always
27 access main memory with the memRead and memWrite functions */
28 private final boolean allowEmptyPages;
29 /** the "empty page" */
30 private final static int[] _emptyPage = new int[0];
32 protected final static boolean isEmptyPage(int[] page) { return page == _emptyPage; }
34 /** Returns a new empty page (_emptyPage is empty pages are enabled or a new zero'd page) */
35 private final int[] emptyPage() { return allowEmptyPages ? _emptyPage : new int[PAGE_WORDS]; }
37 /** Readable main memory pages */
38 protected final int[][] readPages;
39 /** Writable main memory pages.
40 If the page is writable writePages[x] == readPages[x]; if not writePages[x] == null. */
41 protected final int[][] writePages;
43 /** The current break between the heap and unallocated memory */
44 protected int brkAddr;
46 /** The program's entry point */
47 protected int entryPoint;
49 /** The location of the _user_info block (or 0 is there is none) */
50 protected int userInfoBase;
51 protected int userInfoSize;
53 /** The location of the global pointer */
56 /** When the process started */
57 private long startTime;
59 /** State contant: There is no program loaded in memory */
60 public final static int UNINITIALIZED = 0;
61 /** Text/Data loaded in memory */
62 public final static int INITIALIZED = 1;
63 /** Program is executing instructions */
64 public final static int RUNNING = 2;
65 /** Prgram has been started but is paused */
66 public final static int PAUSED = 3;
67 /** Program is executing a callJava() method */
68 public final static int CALLJAVA = 4;
69 /** Program has exited (it cannot currently be restarted) */
70 public final static int DONE = 5;
72 /** The current state (UNINITIALIZED, INITIALIZED, RUNNING, PAUSED, or DONE) */
73 protected int state = UNINITIALIZED;
74 /** @see Runtime#state state */
75 public final int getState() { return state; }
77 /** The exit status if the process (only valid if state==DONE)
79 protected int exitStatus;
80 public ExecutionException exitException;
82 /** Maximum number of open file descriptors */
83 /* package private */ final static int OPEN_MAX = 256;
84 /** Table containing all open file descriptors. (Entries are null if the fd is not in use */
85 /* package private */ FD[] fds = new FD[OPEN_MAX];
87 /** proceses current working directory */
88 /* package private */ String cwd;
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 attribyte 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 public 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 /*package private*/ 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 public 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();
149 /** Copy everything from <i>src</i> to <i>addr</i> initializing uninitialized pages if required.
150 Newly initalized pages will be marked read-only if <i>ro</i> is set */
151 protected final void initPages(int[] src, int addr, boolean ro) {
152 for(int i=0;i<src.length;) {
153 int page = addr >>> PAGE_SHIFT;
154 int start = (addr&(PAGE_SIZE-1))>>2;
155 int elements = min(PAGE_WORDS-start,src.length-i);
156 if(readPages[page]==null) {
159 if(writePages[page] == null) writePages[page] = readPages[page];
161 System.arraycopy(src,i,readPages[page],start,elements);
167 /** Initialize <i>words</i> of pages starting at <i>addr</i> to 0 */
168 protected final void clearPages(int addr, int words) {
169 for(int i=0;i<words;) {
170 int page = addr >>> PAGE_SHIFT;
171 int start = (addr&(PAGE_SIZE-1))>>2;
172 int elements = min(PAGE_WORDS-start,words-i);
173 if(readPages[page]==null) {
174 readPages[page] = writePages[page] = emptyPage();
176 if(writePages[page] == null) writePages[page] = readPages[page];
177 for(int j=start;j<start+elements;j++) writePages[page][j] = 0;
184 /** Copies <i>length</i> bytes from the processes memory space starting at
185 <i>addr</i> INTO a java byte array <i>a</i> */
186 public final void copyin(int addr, byte[] buf, int count) throws ReadFaultException {
189 int word = memRead(addr&~3);
191 case 1: buf[x++] = (byte)((word>>>16)&0xff); if(--count==0) break;
192 case 2: buf[x++] = (byte)((word>>> 8)&0xff); if(--count==0) break;
193 case 3: buf[x++] = (byte)((word>>> 0)&0xff); if(--count==0) break;
197 if((count&~3) != 0) {
201 int[] page = readPages[a >>> (PAGE_SHIFT-2)];
202 if(page == null) throw new ReadFaultException(a<<2);
203 int index = a&(PAGE_WORDS-1);
204 int n = min(c,PAGE_WORDS-index);
205 if(page != _emptyPage) {
206 for(int i=0;i<n;i++,x+=4) {
207 int word = page[index+i];
208 buf[x+0] = (byte)((word>>>24)&0xff); buf[x+1] = (byte)((word>>>16)&0xff);
209 buf[x+2] = (byte)((word>>> 8)&0xff); buf[x+3] = (byte)((word>>> 0)&0xff);
214 addr = a<<2; count &=3;
217 int word = memRead(addr);
219 case 3: buf[x+2] = (byte)((word>>>8)&0xff);
220 case 2: buf[x+1] = (byte)((word>>>16)&0xff);
221 case 1: buf[x+0] = (byte)((word>>>24)&0xff);
226 /** Copies <i>length</i> bytes OUT OF the java array <i>a</i> into the processes memory
227 space at <i>addr</i> */
228 public final void copyout(byte[] buf, int addr, int count) throws FaultException {
231 int word = memRead(addr&~3);
233 case 1: word = (word&0xff00ffff)|((buf[x++]&0xff)<<16); if(--count==0) break;
234 case 2: word = (word&0xffff00ff)|((buf[x++]&0xff)<< 8); if(--count==0) break;
235 case 3: word = (word&0xffffff00)|((buf[x++]&0xff)<< 0); if(--count==0) break;
237 memWrite(addr&~3,word);
240 if((count&~3) != 0) {
244 int[] page = writePages[a >>> (PAGE_SHIFT-2)];
245 if(page == null) throw new WriteFaultException(a<<2);
246 if(page == _emptyPage) page = initPage(a >>> (PAGE_SHIFT-2));
247 int index = a&(PAGE_WORDS-1);
248 int n = min(c,PAGE_WORDS-index);
249 for(int i=0;i<n;i++,x+=4)
250 page[index+i] = ((buf[x+0]&0xff)<<24)|((buf[x+1]&0xff)<<16)|((buf[x+2]&0xff)<<8)|((buf[x+3]&0xff)<<0);
253 addr = a<<2; count&=3;
256 int word = memRead(addr);
258 case 1: word = (word&0x00ffffff)|((buf[x+0]&0xff)<<24); break;
259 case 2: word = (word&0x0000ffff)|((buf[x+0]&0xff)<<24)|((buf[x+1]&0xff)<<16); break;
260 case 3: word = (word&0x000000ff)|((buf[x+0]&0xff)<<24)|((buf[x+1]&0xff)<<16)|((buf[x+2]&0xff)<<8); break;
266 public final void memcpy(int dst, int src, int count) throws FaultException {
267 if((dst&3) == 0 && (src&3)==0) {
268 if((count&~3) != 0) {
273 int[] srcPage = readPages[s>>>(PAGE_SHIFT-2)];
274 if(srcPage == null) throw new ReadFaultException(s<<2);
275 int[] dstPage = writePages[d>>>(PAGE_SHIFT-2)];
276 if(dstPage == null) throw new WriteFaultException(d<<2);
277 int srcIndex = (s&(PAGE_WORDS-1));
278 int dstIndex = (d&(PAGE_WORDS-1));
279 int n = min(c,PAGE_WORDS-max(srcIndex,dstIndex));
280 if(srcPage != _emptyPage) {
281 if(dstPage == _emptyPage) dstPage = initPage(d>>>(PAGE_SHIFT-2));
282 System.arraycopy(srcPage,srcIndex,dstPage,dstIndex,n);
283 } else if(srcPage == _emptyPage && dstPage != _emptyPage) {
284 Arrays.fill(dstPage,dstIndex,dstIndex+n,0);
286 s += n; d += n; c -= n;
288 src = s<<2; dst = d<<2; count&=3;
291 int word1 = memRead(src);
292 int word2 = memRead(dst);
294 case 1: memWrite(dst,(word1&0xff000000)|(word2&0x00ffffff)); break;
295 case 2: memWrite(dst,(word1&0xffff0000)|(word2&0x0000ffff)); break;
296 case 3: memWrite(dst,(word1&0xffffff00)|(word2&0x000000ff)); break;
301 int n = min(count,MAX_CHUNK);
302 byte[] buf = byteBuf(n);
305 count -= n; src += n; dst += n;
310 public final void memset(int addr, int ch, int count) throws FaultException {
311 int fourBytes = ((ch&0xff)<<24)|((ch&0xff)<<16)|((ch&0xff)<<8)|((ch&0xff)<<0);
313 int word = memRead(addr&~3);
315 case 1: word = (word&0xff00ffff)|((ch&0xff)<<16); if(--count==0) break;
316 case 2: word = (word&0xffff00ff)|((ch&0xff)<< 8); if(--count==0) break;
317 case 3: word = (word&0xffffff00)|((ch&0xff)<< 0); if(--count==0) break;
319 memWrite(addr&~3,word);
322 if((count&~3) != 0) {
326 int[] page = readPages[a>>>(PAGE_SHIFT-2)];
327 if(page == null) throw new WriteFaultException(a<<2);
328 int index = (a&(PAGE_WORDS-1));
329 int n = min(c,PAGE_WORDS-index);
330 if(page != _emptyPage || ch != 0) {
331 if(page == _emptyPage) page = initPage(a>>>(PAGE_SHIFT-2));
332 Arrays.fill(page,index,index+n,fourBytes);
336 addr = a<<2; count&=3;
339 int word = memRead(addr);
341 case 1: word = (word&0x00ffffff)|(fourBytes&0xff000000); break;
342 case 2: word = (word&0x0000ffff)|(fourBytes&0xffff0000); break;
343 case 3: word = (word&0x000000ff)|(fourBytes&0xffffff00); break;
349 /** Read a word from the processes memory at <i>addr</i> */
350 public final int memRead(int addr) throws ReadFaultException {
351 if((addr & 3) != 0) throw new ReadFaultException(addr);
352 int page = addr >>> PAGE_SHIFT;
353 int entry = (addr >>> 2) & (PAGE_WORDS-1);
355 return readPages[page][entry];
356 } catch(ArrayIndexOutOfBoundsException e) {
357 if(page < 0) throw e; // should never happen
358 if(page >= readPages.length) throw new ReadFaultException(addr);
359 if(readPages[page] != _emptyPage) throw e; // should never happen
362 } catch(NullPointerException e) {
363 throw new ReadFaultException(addr);
367 /** Writes a word to the processes memory at <i>addr</i> */
368 public final void memWrite(int addr, int value) throws WriteFaultException {
369 if((addr & 3) != 0) throw new WriteFaultException(addr);
370 int page = addr >>> PAGE_SHIFT;
371 int entry = (addr>>>2)&(PAGE_WORDS-1);
373 writePages[page][entry] = value;
374 } catch(ArrayIndexOutOfBoundsException e) {
375 if(page < 0) throw e;// should never happen
376 if(page >= writePages.length) throw new WriteFaultException(addr);
377 if(readPages[page] != _emptyPage) throw e; // should never happen
379 writePages[page][entry] = value;
380 } catch(NullPointerException e) {
381 throw new WriteFaultException(addr);
385 /** Created a new non-empty writable page at page number <i>page</i> */
386 private final int[] initPage(int page) { return initPage(page,false); }
387 /** Created a new non-empty page at page number <i>page</i>. If <i>ro</i> is set the page will be read-only */
388 private final int[] initPage(int page, boolean ro) {
389 int[] buf = new int[PAGE_WORDS];
390 writePages[page] = ro ? null : buf;
391 readPages[page] = buf;
395 /** Returns the exit status of the process. (only valid if state == DONE)
396 @see Runtime#state */
397 public final int exitStatus() {
398 if(state != DONE) throw new IllegalStateException("exitStatus() called in an inappropriate state");
402 private int addStringArray(String[] strings, int topAddr) {
403 int count = strings.length;
404 int total = 0; /* null last table entry */
405 for(int i=0;i<count;i++) total += strings[i].length() + 1;
406 if(total >= ARGS_MAX) throw new IllegalArgumentException("arguments/environ too big");
407 total += (count+1)*4;
408 int start = (topAddr - total)&~3;
409 int addr = start + (count+1)*4;
410 int[] table = new int[count+1];
412 for(int i=0;i<count;i++) {
414 try { a = strings[i].getBytes("US-ASCII"); } catch(UnsupportedEncodingException e){ throw new Error(e.getMessage()); }
416 copyout(a,addr,a.length);
417 memset(addr+a.length,0,1);
418 addr += a.length + 1;
421 for(int i=0;i<count+1;i++) {
422 memWrite(addr,table[i]);
425 } catch(FaultException e) {
426 // should never happen
432 protected String[] createEnv(String[] extra) { if(extra == null) extra = new String[0]; return extra; }
434 /** Sets word number <i>index</i> in the _user_info table to <i>word</i>
435 FIXME: Document the new user_info format */
436 public void setUserInfo(int index, int word) {
437 if(index < 0 || index >= userInfoSize/4) throw new IndexOutOfBoundsException("setUserInfo called with index >= " + (userInfoSize/4));
439 memWrite(userInfoBase+index*4,word);
440 } catch(FaultException e) { throw new Error("should never happen: " + e); }
443 /** Returns the word in the _user_info table entry <i>index</i>
444 @see Runtime#setUserInfo(int,int) setUserInfo */
445 public int getUserInfo(int index) {
446 if(index < 0 || index >= userInfoSize/4) throw new IndexOutOfBoundsException("setUserInfo called with index >= " + (userInfoSize/4));
448 return memRead(userInfoBase+index*4);
449 } catch(FaultException e) { throw new Error("should never happen: " + e); }
452 // FEATURE: Just document this
453 private void __execute() {
456 } catch(FaultException e) {
458 sys_exit(128+11); // SIGSEGV
460 } catch(ExecutionException e) {
462 System.err.println(e);
463 sys_exit(128+4); // SIGILL
468 /** Executes the process until the PAUSE syscall is invoked or the process exits. Returns true if the process exited. */
469 public final boolean execute() {
470 if(state != PAUSED) throw new IllegalStateException("execute() called in inappropriate state");
471 if(startTime == 0) startTime = System.currentTimeMillis();
474 if(state != PAUSED && state != DONE) throw new IllegalStateException("execute() ended up in an inappropriate state (" + state + ")");
475 return state == DONE;
478 public final int run() { return run(null); }
479 public final int run(String[] args) { return run(args,null); }
480 public final int run(String[] args, String[] env) { return run(args,env,null,null,null); }
482 /** Runs the process until it exits and returns the exit status.
483 If the process executes the PAUSE syscall execution will be paused for 500ms and a warning will be displayed */
484 public final int run(String[] args, String[] env, FD in, FD out, FD err) {
485 start(args,env,in,out,err);
488 System.err.println("WARNING: Pause requested while executing run()");
489 try { Thread.sleep(500); } catch(InterruptedException e) { }
494 public final void start() { start(null); }
495 public final void start(String[] args) { start(args,null); }
496 public final void start(String[] args, String[] environ) { start(args,environ,null,null,null); }
498 /** Initializes the process and prepairs it to be executed with execute() */
499 public final void start(String[] args, String[] environ, FD in, FD out, FD err) {
500 int sp, argsAddr, envAddr;
501 if(state != INITIALIZED) throw new IllegalStateException("start() called in inappropriate state");
503 if(args == null) args = new String[]{getClass().getName()};
505 sp = TOTAL_PAGES*PAGE_SIZE-512;
506 sp = argsAddr = addStringArray(args,sp);
507 sp = envAddr = addStringArray(createEnv(environ),sp);
510 CPUState cpuState = new CPUState();
511 cpuState.r[A0] = argsAddr;
512 cpuState.r[A1] = envAddr;
514 cpuState.r[RA] = 0xdeadbeef;
516 cpuState.pc = entryPoint;
517 setCPUState(cpuState);
519 cwd = getSystemProperty("user.dir");
520 if(cwd == null) cwd = "/";
522 if(in == null) in = new InputStreamFD(System.in) { public boolean isatty() { return true; } public void _close() { } };
524 if(out == null) out = new OutputStreamFD(System.out) { public boolean isatty() { return true; } public void _close() { } };
526 if(err == null) err = new OutputStreamFD(System.err) { public boolean isatty() { return true; } public void _close() { } };
534 /** Hook for subclasses to do their own startup */
535 protected void _start() { }
537 public final int call(String sym) throws CallException { return call(sym,0,0,0,0,0,0,0); }
538 public final int call(String sym, int a0) throws CallException { return call(sym,a0,0,0,0,0,0,0); }
539 public final int call(String sym, int a0, int a1) throws CallException { return call(sym,a0,a1,0,0,0,0,0); }
540 public final int call(String sym, int a0, int a1, int a2) throws CallException { return call(sym,a0,a1,a2,0,0,0,0); }
541 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); }
542 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); }
543 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); }
545 /** Calls a function in the process with the given arguments */
546 public final int call(String sym, int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws CallException {
547 int func = lookupSymbol(sym);
548 if(func == -1) throw new CallException(sym + " not found");
549 int helper = lookupSymbol("_call_helper");
550 if(helper == -1) throw new CallException("_call_helper not found");
551 return call(helper,func,a0,a1,a2,a3,a4,a5,a6);
554 /** Executes the code at <i>addr</i> in the process setting A0-A3 and S0-S3 to the given arguments
555 and returns the contents of V1 when the the pause syscall is invoked */
556 public final int call(int addr, int a0, int a1, int a2, int a3, int s0, int s1, int s2, int s3) {
557 if(state != PAUSED && state != CALLJAVA) throw new IllegalStateException("call() called in inappropriate state");
558 int oldState = state;
559 CPUState saved = getCPUState();
560 CPUState cpustate = new CPUState();
561 cpustate.r[SP] = saved.r[SP]&~15;
562 cpustate.r[RA] = 0xdeadbeef;
576 setCPUState(cpustate);
578 cpustate = getCPUState();
582 System.out.println("WARNING: Process exit()ed while servicing a call() request");
586 return cpustate.r[V1];
589 protected int callJava(int a, int b, int c, int d) {
590 System.err.println("WARNING: Default implementation of callJava() called with args " + toHex(a) + "," + toHex(b) + "," + toHex(c) + "," + toHex(d));
594 /** Determines if the process can access <i>fileName</i>. The default implementation simply logs
595 the request and allows it */
596 protected boolean allowFileAccess(String fileName, boolean write) {
597 //System.err.println("Allowing " + (write?"write":"read-only") + " access to " + fileName);
601 /** Allocated an entry in the FileDescriptor table for <i>fd</i> and returns the number.
602 Returns -1 if the table is full. This can be used by subclasses to use custom file
604 public int allocFDEnt(FD fd) {
606 for(i=state==INITIALIZED ? 3 : 0;i<OPEN_MAX;i++) if(fds[i] == null) break;
607 if(i==OPEN_MAX) return -1;
612 /** Closes file descriptor <i>fdn</i> and removes it from the file descriptor table */
613 public boolean closeFDEnt(int fdn) {
617 if(fd == null) return false;
618 } catch(ArrayIndexOutOfBoundsException e) {
626 public File constructFile(String path) {
627 File f = new File(path);
628 if(f.isAbsolute()) return f;
629 else return new File(cwd,f.getPath());
632 /** The open syscall */
633 private int sys_open(int addr, int flags, int mode) {
634 final int O_RDONLY = 0;
635 final int O_WRONLY = 1;
636 final int O_RDWR = 2;
637 final int O_APPEND = 0x0008;
638 final int O_TRUNC = 0x0400;
639 final int O_CREAT = 0x0200;
640 final int O_NONBLOCK = 0x4000;
641 final int O_EXCL = 0x0800;
645 if((flags & O_NONBLOCK) != 0) {
646 System.err.println("WARNING: O_NONBLOCK not supported");
651 fileName = cstring(addr);
652 } catch(FaultException e) {
656 if(fileName.startsWith("/dev/")) {
657 String node = fileName.substring(5);
658 if(node.equals("null")) fd = devNullFD;
659 else if(node.equals("zero")) fd = devZeroFD;
660 else if(node.startsWith("fd/")) {
662 int n = Integer.parseInt(node.substring(3));
664 if(fds[n] == null) return -ENOENT;
666 } catch(ArrayIndexOutOfBoundsException e) {
669 } catch(NumberFormatException e) {
677 // FEATURE: There are a few race conditions here - try to fix them
678 File f = constructFile(fileName);
679 fileName = f.getAbsolutePath();
680 if(!allowFileAccess(fileName,(flags&3)!=0)) return -EACCES;
682 if((flags & O_EXCL) != 0 && (flags & O_CREAT) != 0)
683 if(!f.createNewFile()) return -EEXIST;
685 if(f.length() >= Integer.MAX_VALUE) return -EOPNOTSUPP;
686 if((flags&O_TRUNC)!=0) {
687 if(!allowFileAccess(fileName,true)) return -EACCES;
688 new RandomAccessFile(fileName,"rw").setLength(0);
691 if((flags & O_CREAT) == 0) return -ENOENT;
694 if(f.isDirectory()) {
695 if((flags&3)!=0) return -EISDIR;
696 fd = new DirectoryFD(f);
698 fd = new RegularFD(f,flags&3,(flags&O_APPEND)!=0);
700 } catch(FileNotFoundException e) {
701 if(e.getMessage().indexOf("Permission denied") >= 0) return -EACCES;
703 } catch(IOException e) {
707 int fdn = allocFDEnt(fd);
715 /** The write syscall */
716 private int sys_write(int fdn, int addr, int count) {
718 count = Math.min(count,MAX_CHUNK);
721 if(fd == null || !fd.writable()) return -EBADFD;
722 } catch(ArrayIndexOutOfBoundsException e) {
726 byte[] buf = byteBuf(count);
727 copyin(addr,buf,count);
728 return fd.write(buf,0,count);
729 } catch(FaultException e) {
730 System.err.println(e);
732 } catch(IOException e) {
733 // HACK: We should support signals and send a SIGPIPE
734 if(e.getMessage().equals("Pipe closed")) return sys_exit(128+13);
735 System.err.println(e);
740 /** The read syscall */
741 private int sys_read(int fdn, int addr, int count) {
743 count = Math.min(count,MAX_CHUNK);
746 if(fd == null || !fd.readable()) return -EBADFD;
747 } catch(ArrayIndexOutOfBoundsException e) {
751 byte[] buf = byteBuf(count);
752 int n = fd.read(buf,0,count);
755 } catch(FaultException e) {
756 System.err.println(e);
758 } catch(IOException e) {
759 System.err.println(e);
764 /** The close syscall */
765 private int sys_close(int fdn) {
766 return closeFDEnt(fdn) ? 0 : -EBADFD;
770 /** The seek syscall */
771 private int sys_lseek(int fdn, int offset, int whence) {
775 if(fd == null || !fd.readable()) return -EBADFD;
776 } catch(ArrayIndexOutOfBoundsException e) {
779 if(whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END) return -EINVAL;
781 int n = fd.seek(offset,whence);
782 return n < 0 ? -ESPIPE : n;
783 } catch(IOException e) {
788 /** The stat/fstat syscall helper */
789 private int stat(FileInfo fi, FD fd, int addr) {
790 int size = fi==null ? 0 : fi.size();
791 int type = fi==null ? (fd.isatty() ? FileInfo.S_IFCHR : FileInfo.S_IFIFO) : fi.type();
792 long modtime = fi==null ? 0 : fi.modTime();
794 memWrite(addr+0,(1<<16)|1); // st_dev (top 16), // st_ino (bottom 16)
795 memWrite(addr+4,(type & 0xf000)|0644); // st_mode
796 memWrite(addr+8,1<<16); // st_nlink (top 16) // st_uid (bottom 16)
797 memWrite(addr+12,0); // st_gid (top 16) // st_rdev (bottom 16)
798 memWrite(addr+16,size); // st_size
799 memWrite(addr+20,0); // st_atime
800 // memWrite(addr+24,0) // st_spare1
801 memWrite(addr+28,(int)(modtime/1000)); // st_mtime
802 // memWrite(addr+32,0) // st_spare2
803 memWrite(addr+36,0); // st_ctime
804 // memWrite(addr+40,0) // st_spare3
805 memWrite(addr+44,512); // st_bklsize;
806 memWrite(addr+48,(size+511)&(~511)); // st_blocks
807 // memWrite(addr+52,0) // st_spare4[0]
808 // memWrite(addr+56,0) // st_spare4[1]
809 } catch(FaultException e) {
810 System.err.println(e);
816 /** The fstat syscall */
817 private int sys_fstat(int fdn, int addr) {
821 if(fd == null) return -EBADFD;
822 } catch(ArrayIndexOutOfBoundsException e) {
825 return stat(fd.fileInfo(),fd,addr);
828 private int sys_stat(int cstring, int addr) {
830 File f = constructFile(cstring(cstring));
831 String fileName = f.getAbsolutePath();
832 if(!allowFileAccess(fileName,false)) return -EACCES;
833 if(!f.exists()) return -ENOENT;
834 return stat(new FileFileInfo(f),null,addr);
835 } catch(FaultException e) {
846 private int sys_gettimeofday(int timevalAddr, int timezoneAddr) {
847 long now = System.currentTimeMillis();
848 int tv_sec = (int)(now / 1000);
849 int tv_usec = (int)((now%1000)*1000);
851 memWrite(timevalAddr+0,tv_sec);
852 memWrite(timevalAddr+4,tv_usec);
854 } catch(FaultException e) {
859 private int sys_sleep(int sec) {
860 if(sec < 0) sec = Integer.MAX_VALUE;
862 Thread.sleep((long)sec*1000);
864 } catch(InterruptedException e) {
870 #define _CLOCKS_PER_SEC_ 1000
871 #define _CLOCK_T_ unsigned long
879 private int sys_times(int tms) {
880 long now = System.currentTimeMillis();
881 int userTime = (int)((now - startTime)/16);
882 int sysTime = (int)((now - startTime)/16);
886 memWrite(tms+0,userTime);
887 memWrite(tms+4,sysTime);
888 memWrite(tms+8,userTime);
889 memWrite(tms+12,sysTime);
891 } catch(FaultException e) {
897 public int sys_sysconf(int n) {
899 case _SC_CLK_TCK: return 1000;
901 System.err.println("WARNING: Attempted to use unknown sysconf key: " + n);
906 /** The sbrk syscall. This can also be used by subclasses to allocate memory.
907 <i>incr</i> is how much to increase the break by */
908 public int sbrk(int incr) {
909 if(incr < 0) return -ENOMEM;
910 if(incr==0) return brkAddr;
912 int oldBrk = brkAddr;
913 int newBrk = oldBrk + incr;
914 if(TOTAL_PAGES == 1) {
915 CPUState state = getCPUState();
916 if(newBrk >= state.r[SP] - 65536) {
917 System.err.println("WARNING: brk too close to stack pointer");
920 } else if(newBrk >= BRK_LIMIT) {
921 System.err.println("WARNING: Hit BRK_LIMIT");
924 if(TOTAL_PAGES != 1) {
926 for(int i=(oldBrk+PAGE_SIZE-1)>>>PAGE_SHIFT;i<((newBrk+PAGE_SIZE-1)>>>PAGE_SHIFT);i++)
927 readPages[i] = writePages[i] = emptyPage();
928 } catch(OutOfMemoryError e) {
929 System.err.println("WARNING: Caught OOM Exception in sbrk: " + e);
937 /** The getpid syscall */
938 private int sys_getpid() { return getPid(); }
939 private int getPid() { return 1; }
941 private int sys_calljava(int a, int b, int c, int d) {
942 if(state != RUNNING) throw new IllegalStateException("wound up calling sys_calljava while not in RUNNING");
944 int ret = callJava(a,b,c,d);
949 private int sys_pause() {
954 private int sys_mkdir(int cstring, int mode) {
956 File f = constructFile(cstring(cstring));
957 String fileName = f.getAbsolutePath();
958 if(!allowFileAccess(fileName,true)) return -EACCES;
959 if(f.exists() && f.isDirectory()) return -EEXIST;
960 if(f.exists()) return -ENOTDIR;
961 File parent = f.getParentFile();
962 if(parent!=null && (!parent.exists() || !parent.isDirectory())) return -ENOTDIR;
963 if(f.mkdir()) return 0;
965 } catch(FaultException e) {
970 private int sys_getpagesize() { return TOTAL_PAGES == 1 ? 4096 : PAGE_SIZE; }
972 private int sys_isatty(int fdn) {
976 if(fd == null) return -EBADFD;
977 } catch(ArrayIndexOutOfBoundsException e) {
980 return fd.isatty() ? 1 : 0;
983 private int sys_getcwd(int addr, int size) {
984 byte[] b = cwd.getBytes();
985 if(size == 0) return -EINVAL;
986 if(size < b.length+1) return -ERANGE;
987 if(!new File(cwd).exists()) return -ENOENT;
989 copyout(b,addr,b.length);
990 memset(addr+b.length+1,0,1);
992 } catch(FaultException e) {
997 /** Hook for subclasses to do something when the process exits (MUST set state = DONE) */
998 protected void _exit() { state = DONE; }
999 private int sys_exit(int status) {
1000 exitStatus = status;
1001 for(int i=0;i<fds.length;i++) if(fds[i] != null) sys_close(i);
1006 private int sys_chdir(int addr) {
1008 File f = constructFile(cstring(addr));
1009 if(!f.exists()) return -ENOTDIR;
1010 cwd = f.getAbsolutePath();
1012 } catch(FaultException e){
1017 private int sys_fcntl(int fdn, int cmd, int arg) {
1018 final int F_DUPFD = 0;
1019 final int F_GETFL = 3;
1024 if(fd == null) return -EBADFD;
1025 } catch(ArrayIndexOutOfBoundsException e) {
1030 if(arg < 0 || arg >= OPEN_MAX) return -EINVAL;
1031 for(i=arg;i<OPEN_MAX;i++) if(fds[i]==null) break;
1032 if(i==OPEN_MAX) return -EMFILE;
1037 if(fd.writable() && fd.readable()) flags = 2;
1038 else if(fd.writable()) flags = 1;
1041 System.err.println("WARNING: Unknown fcntl command: " + cmd);
1046 /** The syscall dispatcher.
1047 The should be called by subclasses when the syscall instruction is invoked.
1048 <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
1049 the contenst of A0, A1, A2, and A3. The call MAY change the state
1050 @see Runtime#state state */
1051 protected int syscall(int syscall, int a, int b, int c, int d) {
1053 case SYS_null: return 0;
1054 case SYS_exit: return sys_exit(a);
1055 case SYS_pause: return sys_pause();
1056 case SYS_write: return sys_write(a,b,c);
1057 case SYS_fstat: return sys_fstat(a,b);
1058 case SYS_sbrk: return sbrk(a);
1059 case SYS_open: return sys_open(a,b,c);
1060 case SYS_close: return sys_close(a);
1061 case SYS_read: return sys_read(a,b,c);
1062 case SYS_lseek: return sys_lseek(a,b,c);
1063 case SYS_getpid: return sys_getpid();
1064 case SYS_calljava: return sys_calljava(a,b,c,d);
1065 case SYS_stat: return sys_stat(a,b);
1066 case SYS_gettimeofday: return sys_gettimeofday(a,b);
1067 case SYS_sleep: return sys_sleep(a);
1068 case SYS_times: return sys_times(a);
1069 case SYS_mkdir: return sys_mkdir(a,b);
1070 case SYS_getpagesize: return sys_getpagesize();
1071 case SYS_isatty: return sys_isatty(a);
1072 case SYS_getcwd: return sys_getcwd(a,b);
1073 case SYS_chdir: return sys_chdir(a);
1074 case SYS_fcntl: return sys_fcntl(a,b,c);
1075 case SYS_sysconf: return sys_sysconf(a);
1082 System.err.println("Attempted to use a UnixRuntime syscall in Runtime (" + syscall + ")");
1085 System.err.println("Attempted to use unknown syscall: " + syscall);
1090 public int xmalloc(int size) { int p=malloc(size); if(p==0) throw new OutOfMemoryError("malloc() failed"); return p; }
1091 public int xrealloc(int addr,int newsize) { int p=realloc(addr,newsize); if(p==0) throw new OutOfMemoryError("realloc() failed"); return p; }
1092 public int realloc(int addr, int newsize) { try { return call("realloc",addr,newsize); } catch(CallException e) { return 0; } }
1093 public int malloc(int size) { try { return call("malloc",size); } catch(CallException e) { return 0; } }
1094 public void free(int p) { try { if(p!=0) call("free",p); } catch(CallException e) { } }
1096 /** Helper function to create a cstring in main memory */
1097 public int strdup(String s) {
1099 if(s == null) s = "(null)";
1101 byte[] a2 = s.getBytes("US-ASCII");
1102 a = new byte[a2.length+1];
1103 System.arraycopy(a2,0,a,0,a2.length);
1104 } catch(UnsupportedEncodingException e){ throw new Error(e.getMessage()); }
1105 int addr = malloc(a.length);
1106 if(addr == 0) return 0;
1108 copyout(a,addr,a.length);
1109 } catch(FaultException e) {
1116 /** Helper function to read a cstring from main memory */
1117 public String cstring(int addr) throws ReadFaultException {
1118 StringBuffer sb = new StringBuffer();
1120 int word = memRead(addr&~3);
1122 case 0: if(((word>>>24)&0xff)==0) return sb.toString(); sb.append((char)((word>>>24)&0xff)); addr++;
1123 case 1: if(((word>>>16)&0xff)==0) return sb.toString(); sb.append((char)((word>>>16)&0xff)); addr++;
1124 case 2: if(((word>>> 8)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 8)&0xff)); addr++;
1125 case 3: if(((word>>> 0)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 0)&0xff)); addr++;
1130 /** FileInfo class - used by stat */
1131 public static class FileInfo {
1132 public static final int S_IFIFO = 0010000;
1133 public static final int S_IFCHR = 0020000;
1134 public static final int S_IFDIR = 0040000;
1135 public static final int S_IFREG = 0100000;
1138 public FileInfo() { this(S_IFIFO); }
1139 public FileInfo(int type) { this.type = type; }
1140 public int type() { return type; }
1141 public int size() { return 0; }
1142 public long modTime() { return 0; }
1145 /** FileInfo subclass for normal files */
1146 public static class FileFileInfo extends FileInfo {
1148 public FileFileInfo(File f) { super(f.isDirectory() ? S_IFDIR : S_IFREG); this.f = f; }
1149 public int size() { return (int)f.length(); }
1150 public long modTime() { return f.lastModified(); }
1153 private static FD devZeroFD = new FD() {
1154 public boolean readable() { return true; }
1155 public boolean writable() { return true; }
1156 public int read(byte[] a, int off, int length) { Arrays.fill(a,off,off+length,(byte)0); return length; }
1157 public int write(byte[] a, int off, int length) { return length; }
1158 public int seek(int n, int whence) { return 0; }
1160 private static FD devNullFD = new FD() {
1161 public boolean readable() { return true; }
1162 public boolean writable() { return true; }
1163 public int read(byte[] a, int off, int length) { return 0; }
1164 public int write(byte[] a, int off, int length) { return length; }
1165 public int seek(int n, int whence) { return 0; }
1168 /** File Descriptor class */
1169 public static abstract class FD {
1170 private int refCount = 1;
1171 private FileInfo fi;
1173 /** returns true if the fd is readable */
1174 public boolean readable() { return false; }
1175 /** returns true if the fd is writable */
1176 public boolean writable() { return false; }
1178 public FileInfo fileInfo() { return fi; }
1180 /** Initializes the FileDescriptor with no FileInfo struct */
1181 FD() { this(null); }
1182 /** Initializes the FileDescriptor with the given FileInfo struct (used by fstat) */
1183 FD(FileInfo fi) { this.fi = fi; }
1185 /** Read some bytes. Should return the number of bytes read, 0 on EOF, or throw an IOException on error */
1186 public int read(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); }
1187 /** Write. Should return the number of bytes written or throw an IOException on error */
1188 public int write(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); }
1190 /** Seek in the filedescriptor. Whence is SEEK_SET, SEEK_CUR, or SEEK_END. Should return -1 on error or the new position. */
1191 public int seek(int n, int whence) throws IOException { return -1; }
1193 /** Should return true if this is a tty */
1194 public boolean isatty() { return false; }
1196 /** Closes the fd */
1197 public final void close() { if(--refCount==0) _close(); }
1198 protected void _close() { }
1200 FD dup() { refCount++; return this; }
1203 public static class RegularFD extends SeekableFD {
1204 private final boolean append;
1205 public RegularFD(String file, int mode, boolean append) throws IOException { this(new File(file),mode,append); }
1206 public RegularFD(File file, int mode, boolean append) throws IOException {
1207 super(new SeekableFile(file,mode!=0),mode,new FileFileInfo(file));
1208 this.append = append;
1210 public int write(byte[] buf, int off, int len) throws IOException {
1211 // FEATURE: There is a race here... we probably can't fix it in pure java though
1212 if(append) seek(0,SEEK_END);
1213 return super.write(buf,off,len);
1217 /** FileDescriptor class for normal files */
1218 public static class SeekableFD extends FD {
1220 private SeekableData data;
1221 public boolean readable() { return mode != 1; }
1222 public boolean writable() { return mode != 0; }
1224 SeekableFD(final SeekableData data, int m) {
1225 this(data,m,new FileInfo() {
1226 public int type() { return S_IFREG; }
1227 public int length() throws IOException { return data.length(); }
1230 SeekableFD(SeekableData data,int m,FileInfo fi) {
1236 public int seek(int n, int whence) throws IOException {
1238 case SEEK_SET: break;
1239 case SEEK_CUR: n += data.pos(); break;
1240 case SEEK_END: n += data.length(); break;
1247 public int write(byte[] a, int off, int length) throws IOException { return data.write(a,off,length); }
1248 public int read(byte[] a, int off, int length) throws IOException { int n = data.read(a,off,length); return n < 0 ? 0 : n; }
1250 protected void _close() { try { data.close(); } catch(IOException e) { } }
1253 public static class OutputStreamFD extends FD {
1254 private OutputStream os;
1255 public boolean writable() { return true; }
1256 public OutputStreamFD(OutputStream os) { this.os = os; }
1257 public int write(byte[] a, int off, int length) throws IOException { os.write(a,off,length); return length; }
1258 public void _close() { try { os.close(); } catch(IOException e) { } }
1261 public static class InputStreamFD extends FD {
1262 private InputStream is;
1263 public boolean readable() { return true; }
1264 public InputStreamFD(InputStream is) { this.is = is; }
1265 public int read(byte[] a, int off, int length) throws IOException { int n = is.read(a,off,length); return n < 0 ? 0 : n; }
1266 public void _close() { try { is.close(); } catch(IOException e) { } }
1269 public static class ByteArrayFD extends SeekableFD {
1270 private boolean writable;
1271 public boolean readable() { return true; }
1272 public ByteArrayFD(byte[] data, boolean writable) { super(new SeekableByteArray(data,writable),writable?2:0); }
1273 public ByteArrayFD(byte[] data, boolean writable, FileInfo fi) { super(new SeekableByteArray(data,false),writable?2:0,fi); }
1276 public static class SeekableInputStreamFD extends SeekableFD {
1277 public SeekableInputStreamFD(InputStream is) { super(new SeekableInputStream(is),0); }
1280 private static class DirectoryFD extends ByteArrayFD {
1281 private static byte[] dirData(File f) throws IOException {
1282 ByteArrayOutputStream bos = new ByteArrayOutputStream();
1283 DataOutputStream dos = new DataOutputStream(bos);
1284 File[] files = f.listFiles();
1285 if(files == null) throw new IOException("not a directory");
1286 for(int i=0;i<files.length;i++) {
1287 byte[] b = files[i].getName().getBytes();
1288 int inode = files[i].getAbsolutePath().hashCode() & 0xfffff;
1289 dos.writeInt(inode);
1290 dos.writeInt(b.length);
1291 dos.write(b,0,b.length);
1293 return bos.toByteArray();
1295 public DirectoryFD(File f) throws IOException { super(dirData(f),false,new FileFileInfo(f)); }
1299 public class ReadFaultException extends FaultException {
1300 public ReadFaultException(int addr) { super(addr); }
1302 public class WriteFaultException extends FaultException {
1303 public WriteFaultException(int addr) { super(addr); }
1305 public abstract class FaultException extends ExecutionException {
1307 public FaultException(int addr) { super("fault at: " + toHex(addr)); this.addr = addr; }
1309 public static class ExecutionException extends Exception {
1310 private String message = "(null)";
1311 private String location = "(unknown)";
1312 public ExecutionException() { }
1313 public ExecutionException(String s) { if(s != null) message = s; }
1314 void setLocation(String s) { location = s == null ? "(unknown)" : s; }
1315 public final String getMessage() { return message + " at " + location; }
1317 public static class CallException extends Exception {
1318 public CallException(String s) { super(s); }
1322 public static class CPUState { // this is public to work around a GCJ bug
1323 public CPUState() { }
1325 public int[] r = new int[32];
1326 /* Floating point regs */
1327 public int[] f = new int[32];
1333 // Utility functions
1334 private byte[] byteBuf(int size) {
1335 if(_byteBuf==null) _byteBuf = new byte[size];
1336 else if(_byteBuf.length < size)
1337 _byteBuf = new byte[min(max(_byteBuf.length*2,size),MAX_CHUNK)];
1341 protected String getSystemProperty(String key) {
1343 return System.getProperty(key);
1344 } catch(SecurityException e) {
1349 /** Decode an packed string.. FIXME: document this better */
1350 protected static final int[] decodeData(String s, int words) {
1351 if(s.length() % 8 != 0) throw new IllegalArgumentException("string length must be a multiple of 8");
1352 if((s.length() / 8) * 7 < words*4) throw new IllegalArgumentException("string isn't big enough");
1353 int[] buf = new int[words];
1354 int prev = 0, left=0;
1355 for(int i=0,n=0;n<words;i+=8) {
1357 for(int j=0;j<8;j++) { l <<= 7; l |= s.charAt(i+j) & 0x7f; }
1358 if(left > 0) buf[n++] = prev | (int)(l>>>(56-left));
1359 if(n < words) buf[n++] = (int) (l >>> (24-left));
1360 left = (left + 8) & 0x1f;
1361 prev = (int)(l << left);
1366 protected final static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); }
1367 protected final static int min(int a, int b) { return a < b ? a : b; }
1368 protected final static int max(int a, int b) { return a > b ? a : b; }