new fs stuff
[nestedvm.git] / src / org / ibex / nestedvm / Runtime.java
1 // Copyright 2003 Brian Alliet
2 // Based on org.xwt.imp.MIPS by Adam Megacz
3 // Portions Copyright 2003 Adam Megacz
4
5 package org.ibex.nestedvm;
6
7 import org.ibex.nestedvm.util.*;
8 import java.io.*;
9 import java.util.Arrays;
10
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)
13
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;
23
24     /** This is the maximum size of command line arguments */
25     public final static int ARGS_MAX = 1024*1024;
26     
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];
34     
35     protected final static boolean isEmptyPage(int[] page) { return page == _emptyPage; }
36     
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]; }
39     
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;
45     
46     /** The current break between the heap and unallocated memory */
47     protected int brkAddr;
48         
49     /** The program's entry point */
50     protected int entryPoint;
51
52     /** The location of the _user_info block (or 0 is there is none) */
53     protected int userInfoBase;
54     protected int userInfoSize;
55     
56     /** The location of the global pointer */
57     protected int gp;
58     
59     /** When the process started */
60     private long startTime;
61     
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;
74         
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; }
79     
80     /** The exit status if the process (only valid if state==DONE) 
81         @see Runtime#state */
82     protected int exitStatus;
83     public ExecutionException exitException;
84     
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];
89     
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; }
94         
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;
100         
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;
105     
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; }
109     
110     /** Subclasses should returns a CPUState object representing the cpu state */
111     protected abstract CPUState getCPUState();
112     
113     /** Subclasses should set the CPUState to the state held in <i>state</i> */
114     protected abstract void setCPUState(CPUState state);
115
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 + ")");
122     }
123     
124     protected Runtime(int pageSize, int totalPages, boolean allowEmptyPages) {
125         this.allowEmptyPages = allowEmptyPages;
126         
127         checkPageSize(pageSize,totalPages);
128         
129         PAGE_SIZE = pageSize;
130         PAGE_WORDS = pageSize>>>2;
131         int pageShift = 0;
132         while(pageSize>>>pageShift != 1) pageShift++;
133         PAGE_SHIFT = pageShift;
134         
135         TOTAL_PAGES = totalPages;
136         
137         readPages = new int[TOTAL_PAGES][];
138         writePages = new int[TOTAL_PAGES][];
139         
140         if(TOTAL_PAGES == 1) {
141             readPages[0] = writePages[0] = new int[PAGE_WORDS];
142             BRK_LIMIT = STACK_BOTTOM = 0;
143         } else {
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;
148         
149             for(int i=0;i<stackPages;i++)
150                 readPages[TOTAL_PAGES-1-i] = writePages[TOTAL_PAGES-1-i] = emptyPage();
151         }
152         
153         addFD(new StdinFD(System.in));
154         addFD(new StdoutFD(System.out));
155         addFD(new StdoutFD(System.err));
156     }
157     
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) {
166                 initPage(page,ro);
167             } else if(!ro) {
168                 if(writePages[page] == null) writePages[page] = readPages[page];
169             }
170             System.arraycopy(src,i,readPages[page],start,elements);
171             i += elements;
172             addr += elements*4;
173         }
174     }
175     
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();
184             } else {
185                 if(writePages[page] == null) writePages[page] = readPages[page];
186                 for(int j=start;j<start+elements;j++) writePages[page][j] = 0;
187             }
188             i += elements;
189             addr += elements*4;
190         }
191     }
192     
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 {
196         int x=0;
197         if(count == 0) return;
198         if((addr&3)!=0) {
199             int word = memRead(addr&~3);
200             switch(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;
204             }
205             addr = (addr&~3)+4;
206         }
207         if((count&~3) != 0) {
208             int c = count>>>2;
209             int a = addr>>>2;
210             while(c != 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);                        
220                     }
221                 }
222                 a += n; c -=n;
223             }
224             addr = a<<2; count &=3;
225         }
226         if(count != 0) {
227             int word = memRead(addr);
228             switch(count) {
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);
232             }
233         }
234     }
235     
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 {
239         int x=0;
240         if(count == 0) return;
241         if((addr&3)!=0) {
242             int word = memRead(addr&~3);
243             switch(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;
247             }
248             memWrite(addr&~3,word);
249             addr += x;
250         }
251         if((count&~3) != 0) {
252             int c = count>>>2;
253             int a = addr>>>2;
254             while(c != 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);
262                 a += n; c -=n;
263             }
264             addr = a<<2; count&=3;
265         }
266         if(count != 0) {
267             int word = memRead(addr);
268             switch(count) {
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;
272             }
273             memWrite(addr,word);
274         }
275     }
276     
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) {
280                 int c = count>>2;
281                 int s = src>>>2;
282                 int d = dst>>>2;
283                 while(c != 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);
296                     }
297                     s += n; d += n; c -= n;
298                 }
299                 src = s<<2; dst = d<<2; count&=3;
300             }
301             if(count != 0) {
302                 int word1 = memRead(src);
303                 int word2 = memRead(dst);
304                 switch(count) {
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;
308                 }
309             }
310         } else {
311             while(count > 0) {
312                 int n = min(count,MAX_CHUNK);
313                 byte[] buf = byteBuf(n);
314                 copyin(src,buf,n);
315                 copyout(buf,dst,n);
316                 count -= n; src += n; dst += n;
317             }
318         }
319     }
320     
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);
323         if((addr&3)!=0) {
324             int word = memRead(addr&~3);
325             switch(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;
329             }
330             memWrite(addr&~3,word);
331             addr = (addr&~3)+4;
332         }
333         if((count&~3) != 0) {
334             int c = count>>2;
335             int a = addr>>>2;
336             while(c != 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);
344                 }
345                 a += n; c -= n;
346             }
347             addr = a<<2; count&=3;
348         }
349         if(count != 0) {
350             int word = memRead(addr);
351             switch(count) {
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;
355             }
356             memWrite(addr,word);
357         }
358     }
359     
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);
364     }
365        
366     protected final int unsafeMemRead(int addr) throws ReadFaultException {
367         int page = addr >>> PAGE_SHIFT;
368         int entry = (addr >>> 2) & (PAGE_WORDS-1);
369         try {
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
375             initPage(page);
376             return 0;
377         } catch(NullPointerException e) {
378             throw new ReadFaultException(addr);
379         }
380     }
381     
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);
386     }
387     
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);
391         try {
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
397             initPage(page);
398             writePages[page][entry] = value;
399         } catch(NullPointerException e) {
400             throw new WriteFaultException(addr);
401         }
402     }
403     
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;
411         return buf;
412     }
413     
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");
418         return exitStatus;
419     }
420         
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];
430         try {
431             for(int i=0;i<count;i++) {
432                 byte[] a = getBytes(strings[i]);
433                 table[i] = addr;
434                 copyout(a,addr,a.length);
435                 memset(addr+a.length,0,1);
436                 addr += a.length + 1;
437             }
438             addr=start;
439             for(int i=0;i<count+1;i++) {
440                 memWrite(addr,table[i]);
441                 addr += 4;
442             }
443         } catch(FaultException e) {
444             // should never happen
445             throw new Error(e.toString());
446         }
447         return start;
448     }
449     
450     protected String[] createEnv(String[] extra) { if(extra == null) extra = new String[0]; return extra; }
451     
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));
459         try {
460             memWrite(userInfoBase+index*4,word);
461         } catch(FaultException e) { throw new Error("should never happen: " + e); }
462     }
463     
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));
468         try {
469             return memRead(userInfoBase+index*4);
470         } catch(FaultException e) { throw new Error("should never happen: " + e); }
471     }
472     
473     /** Calls _execute() (subclass's execute()) and catches exceptions */
474     private void __execute() {
475         try {
476             _execute();
477         } catch(FaultException e) {
478             e.printStackTrace();
479             sys_exit(128+11); // SIGSEGV
480             exitException = e;
481         } catch(ExecutionException e) {
482             e.printStackTrace();
483             System.err.println(e);
484             sys_exit(128+4); // SIGILL
485             exitException = e;
486         }
487     }
488     
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();
493         state = RUNNING;
494         __execute();
495         if(state != PAUSED && state != DONE) throw new IllegalStateException("execute() ended up in an inappropriate state (" + state + ")");
496         return state == DONE;
497     }
498     
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);
503         args[0] = argv0;
504         return run(args);
505     }
506     public final int run(String[] args) { return run(args,null); }
507     
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) {
511         start(args,env);
512         for(;;) {
513             if(execute()) break;
514             System.err.println("WARNING: Pause requested while executing run()");
515             try { Thread.sleep(500); } catch(InterruptedException e) { /* noop */ }
516         }
517         return exitStatus();
518     }
519
520     public final void start() { start(null); }
521     public final void start(String[] args) { start(args,null); }
522     
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");
527
528         if(args == null) args = new String[]{getClass().getName()};
529         
530         sp = TOTAL_PAGES*PAGE_SIZE-512;
531         sp = argsAddr = addStringArray(args,sp);
532         sp = envAddr = addStringArray(createEnv(environ),sp);
533         sp &= ~15;
534         
535         CPUState cpuState = new CPUState();
536         cpuState.r[A0] = argsAddr;
537         cpuState.r[A1] = envAddr;
538         cpuState.r[SP] = sp;
539         cpuState.r[RA] = 0xdeadbeef;
540         cpuState.r[GP] = gp;
541         cpuState.pc = entryPoint;
542         setCPUState(cpuState);
543                 
544         state = PAUSED;
545         
546         _start();
547     }
548     
549     /** Hook for subclasses to do their own startup */
550     protected void _start() { /* noop */ }
551     
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); }
560     
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);
568     }
569     
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;
579         cpustate.r[A0] = a0;
580         cpustate.r[A1] = a1;
581         cpustate.r[A2] = a2;
582         cpustate.r[A3] = a3;
583         cpustate.r[S0] = s0;
584         cpustate.r[S1] = s1;
585         cpustate.r[S2] = s2;
586         cpustate.r[S3] = s3;
587         cpustate.r[GP] = gp;
588         cpustate.pc = addr;
589         
590         state = RUNNING;
591
592         setCPUState(cpustate);
593         __execute();
594         cpustate = getCPUState();
595         setCPUState(saved);
596
597         if(state != PAUSED)
598             System.out.println("WARNING: Process exit()ed while servicing a call() request");
599         else
600             state = oldState;
601         
602         return cpustate.r[V1];
603     }
604     
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);
609         return true;
610     }
611     
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
614         descriptors */
615     public int addFD(FD fd) {
616         int i;
617         for(i=0;i<OPEN_MAX;i++) if(fds[i] == null) break;
618         if(i==OPEN_MAX) return -1;
619         fds[i] = fd;
620         return i;
621     }
622
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;
627         fds[fdn].close();
628         fds[fdn] = null;        
629         return true;
630     }
631     
632     /** Duplicates the file descriptor <i>fdn</i> and returns the new fs */
633     public int dupFD(int fdn) {
634                 int i;
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();
640         return i;
641     }
642
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;
647     
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;
653     
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) {
666                 public int size() {
667                     try { return sf.length(); } catch(IOException e) { return 0; }
668                 }
669             };}
670         };
671     }
672     
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");
677             return -EOPNOTSUPP;
678         }
679
680         try {
681             FD fd = open(cstring(addr),flags,mode);
682             if(fd == null) return -ENOENT;
683             int fdn = addFD(fd);
684             if(fdn == -1) {
685                 fd.close();
686                 return -ENFILE;
687             }
688             return fdn;
689         }
690         catch(ErrnoException e) { return -e.errno; }
691         catch(FileNotFoundException e) {
692             if(e.getMessage() != null && e.getMessage().indexOf("Permission denied") >= 0) return -EACCES;
693             return -ENOENT;
694         }
695         catch(IOException e) { return -EIO; }
696         catch(FaultException e) { return -EFAULT; }
697     }
698
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;
704         try {
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);
710             return -EFAULT;
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);
715             return -EIO;
716         }
717     }
718
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;
724         try {
725             byte[] buf = byteBuf(count);
726             int n = fds[fdn].read(buf,0,count);
727             copyout(buf,addr,n);
728             return n;
729         } catch(FaultException e) {
730             System.err.println(e);
731             return -EFAULT;
732         } catch(IOException e) {
733             System.err.println(e);
734             return -EIO;
735         }
736     }
737     
738     /** The close syscall */
739     private int sys_close(int fdn) {
740         return closeFD(fdn) ? 0 : -EBADFD;
741     }
742
743     
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;
749         try {
750             int n = fds[fdn].seek(offset,whence);
751             return n < 0 ? -ESPIPE : n;
752         } catch(IOException e) {
753             return -ESPIPE;
754         }
755     }
756     
757     /** The stat/fstat syscall helper */
758     int stat(FStat fs, int addr) {
759         try {
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);
777             return -EFAULT;
778         }
779         return 0;
780     }
781     
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);
787     }
788     
789     /*
790     struct timeval {
791     long tv_sec;
792     long tv_usec;
793     };
794     */
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);
799         try {
800             memWrite(timevalAddr+0,tv_sec);
801             memWrite(timevalAddr+4,tv_usec);
802             return 0;
803         } catch(FaultException e) {
804             return -EFAULT;
805         }
806     }
807     
808     private int sys_sleep(int sec) {
809         if(sec < 0) sec = Integer.MAX_VALUE;
810         try {
811             Thread.sleep((long)sec*1000);
812             return 0;
813         } catch(InterruptedException e) {
814             return -1;
815         }
816     }
817     
818     /*
819       #define _CLOCKS_PER_SEC_ 1000
820       #define    _CLOCK_T_    unsigned long
821     struct tms {
822       clock_t   tms_utime;
823       clock_t   tms_stime;
824       clock_t   tms_cutime;    
825       clock_t   tms_cstime;
826     };*/
827    
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);
832         
833         try {
834             if(tms!=0) {
835                 memWrite(tms+0,userTime);
836                 memWrite(tms+4,sysTime);
837                 memWrite(tms+8,userTime);
838                 memWrite(tms+12,sysTime);
839             }
840         } catch(FaultException e) {
841             return -EFAULT;
842         }
843         return (int)now;
844     }
845     
846     private int sys_sysconf(int n) {
847         switch(n) {
848             case _SC_CLK_TCK: return 1000;
849             default:
850                 System.err.println("WARNING: Attempted to use unknown sysconf key: " + n);
851                 return -EINVAL;
852         }
853     }
854     
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;
860         incr = (incr+3)&~3;
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");
867                 return -ENOMEM;
868             }
869         } else if(newBrk >= BRK_LIMIT) {
870             System.err.println("WARNING: Hit BRK_LIMIT");
871             return -ENOMEM;
872         }
873         if(TOTAL_PAGES != 1) {
874             try {
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);
879                 return -ENOMEM;
880             }
881         }
882         brkAddr = newBrk;
883         return oldBrk;
884     }
885
886     /** The getpid syscall */
887     private int sys_getpid() { return getPid(); }
888     protected int getPid() { return 1; }
889     
890     public static interface CallJavaCB { public int call(int a, int b, int c, int d); }
891     
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) {
895             state = CALLJAVA;
896             int ret = callJavaCB.call(a,b,c,d);
897             state = RUNNING;
898             return ret;
899         } else {
900                         System.err.println("WARNING: calljava syscall invoked without a calljava callback set");
901                         return 0;
902         }
903     }
904         
905     private int sys_pause() {
906         state = PAUSED;
907         return 0;
908     }
909     
910     private int sys_getpagesize() { return TOTAL_PAGES == 1 ? 4096 : PAGE_SIZE; }
911     
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;
916     }
917
918     
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) {
922         exitStatus = status;
923         for(int i=0;i<fds.length;i++) if(fds[i] != null) sys_close(i);
924         _exit();
925         return 0;
926     }
927        
928     private int sys_fcntl(int fdn, int cmd, int arg) {
929         final int F_DUPFD = 0;
930         final int F_GETFL = 3;
931         int i;
932             
933         if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
934         if(fds[fdn] == null) return -EBADFD;
935         FD fd = fds[fdn];
936         
937         switch(cmd) {
938             case F_DUPFD:
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;
942                 fds[i] = fd.dup();
943                 return 0;
944             case F_GETFL:
945                 int flags = 0;
946                 if(fd.writable() && fd.readable())  flags = 2;
947                 else if(fd.writable()) flags = 1;
948                 return flags;
949             default:
950                 System.err.println("WARNING: Unknown fcntl command: " + cmd);
951                 return -ENOSYS;
952         }
953     }
954             
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) {
961         switch(syscall) {
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);
981
982             case SYS_kill:
983             case SYS_fork:
984             case SYS_pipe:
985             case SYS_dup2:
986             case SYS_waitpid:
987             case SYS_stat:
988             case SYS_mkdir:
989             case SYS_getcwd:
990             case SYS_chdir:
991                 System.err.println("Attempted to use a UnixRuntime syscall in Runtime (" + syscall + ")");
992                 return -ENOSYS;
993             default:
994                 System.err.println("Attempted to use unknown syscall: " + syscall);
995                 return -ENOSYS;
996         }
997     }
998     
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*/ } }
1004     
1005     /** Helper function to create a cstring in main memory */
1006     public int strdup(String s) {
1007         byte[] a;
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;
1014         try {
1015             copyout(a,addr,a.length);
1016         } catch(FaultException e) {
1017             free(addr);
1018             return 0;
1019         }
1020         return addr;
1021     }
1022     
1023     /** Helper function to read a cstring from main memory */
1024     public String cstring(int addr) throws ReadFaultException {
1025         StringBuffer sb = new StringBuffer();
1026         for(;;) {
1027             int word = memRead(addr&~3);
1028             switch(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++;
1033             }
1034         }
1035     }
1036     
1037     /** File Descriptor class */
1038     public static abstract class FD {
1039         private int refCount = 1;
1040     
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; }
1045         
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"); }
1050
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; }
1053         
1054         /** Should return true if this is a tty */
1055         public boolean isatty() { return false; }
1056         
1057         private FStat cachedFStat = null;
1058         public final FStat fstat() {
1059             if(cachedFStat == null) cachedFStat = _fstat(); 
1060             return cachedFStat;
1061         }
1062         
1063         protected abstract FStat _fstat();
1064         
1065         /** Closes the fd */
1066         public final void close() { if(--refCount==0) _close(); }
1067         protected void _close() { /* noop*/ }
1068         
1069         FD dup() { refCount++; return this; }
1070     }
1071         
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; }
1078         
1079         SeekableFD(Seekable data, int flags) { this.data = data; this.flags = flags; }
1080         
1081         protected abstract FStat _fstat();
1082
1083         public int seek(int n, int whence) throws IOException {
1084             switch(whence) {
1085                 case SEEK_SET: break;
1086                 case SEEK_CUR: n += data.pos(); break;
1087                 case SEEK_END: n += data.length(); break;
1088                 default: return -1;
1089             }
1090             data.seek(n);
1091             return n;
1092         }
1093         
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);
1098         }
1099         
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;
1103         }
1104         
1105         protected void _close() { try { data.close(); } catch(IOException e) { /*ignore*/ } }        
1106     }
1107     
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(); }
1115     }
1116     
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(); }
1124     }
1125     
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; }
1131     }
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; }
1137     }
1138     
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;
1144         
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(); }        
1159     }
1160     
1161     protected static class HostFStat extends FStat {
1162         private final File f;
1163         private final boolean executable; 
1164         public HostFStat(File f) {
1165             this.f = f;
1166             boolean _executable = false;
1167             // FEATURE: This might be too expensive
1168             try {
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() == '!';
1173                 }
1174                 fis.close();
1175             } catch(IOException e) { /* ignore */ }
1176             executable = _executable;
1177         }
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; }
1182         public int mode() {
1183             int mode = 0;
1184             boolean canread = f.canRead();
1185             if(canread && (executable || f.isDirectory())) mode |= 0111;
1186             if(canread) mode |= 0444;
1187             if(f.canWrite()) mode |= 0222;
1188             return mode;
1189         }
1190         public int size() { return (int) f.length(); }
1191         public int mtime() { return (int)(f.lastModified()/1000); }
1192     }
1193     
1194     // Exceptions
1195     public class ReadFaultException extends FaultException {
1196         public ReadFaultException(int addr) { super(addr); }
1197     }
1198     public class WriteFaultException extends FaultException {
1199         public WriteFaultException(int addr) { super(addr); }
1200     }
1201     public abstract class FaultException extends ExecutionException {
1202         public int addr;
1203         public FaultException(int addr) { super("fault at: " + toHex(addr)); this.addr = addr; }
1204     }
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; }
1212     }
1213     public static class CallException extends Exception {
1214         public CallException(String s) { super(s); }
1215     }
1216     
1217     protected static class ErrnoException extends IOException {
1218         public int errno;
1219         public ErrnoException(int errno) { super("Errno: " + errno); this.errno = errno; }
1220     }
1221     
1222     // CPU State
1223     protected static class CPUState {
1224         public CPUState() { /* noop */ }
1225         /* GPRs */
1226         public int[] r = new int[32];
1227         /* Floating point regs */
1228         public int[] f = new int[32];
1229         public int hi, lo;
1230         public int fcsr;
1231         public int pc;
1232     }
1233     
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));
1238     }
1239     
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)];
1245         return _byteBuf;
1246     }
1247     
1248     protected static String getSystemProperty(String key) {
1249         try {
1250             return System.getProperty(key);
1251         } catch(SecurityException e) {
1252             return null;
1253         }
1254     }
1255     
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) {
1263             long l = 0;
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);
1269         }
1270         return buf;
1271     }
1272     
1273     protected static byte[] getBytes(String s) {
1274         try {
1275             return s.getBytes("ISO-8859-1");
1276         } catch(UnsupportedEncodingException e) {
1277             return null; // should never happen
1278         }
1279     }
1280     
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; }
1284 }