upstream_clean_%:
$(MAKE) -C upstream clean_$* usr="$(usr)"
-errno_h = $(usr)/mips-unknown-elf/include/sys/errno.h
-$(errno_h): $(tasks)/build_newlib
-
-unistd_h = $(usr)/mips-unknown-elf/include/sys/unistd.h
-$(unistd_h): $(tasks)/build_newlib
-
#
# Interpreter/Compiler/Runtime Java Compilation
#
cd build && jar cf ../$@ $(unixruntime_classes:%=org/ibex/nestedvm/%*.class)
# This is only for Brian to use... don't mess with it
-rebuild-constants: src/org/ibex/nestedvm/syscalls.h $(errno_h) $(unistd_h)
+rebuild-constants: $(tasks)/build_newlib
@mkdir -p `dirname $@`
- cat $^ | ( \
+ cat \
+ src/org/ibex/nestedvm/syscalls.h \
+ $(usr)/mips-unknown-elf/include/sys/{errno.h,unistd.h,syslimits.h} \
+ | ( \
echo "// THIS FILE IS AUTOGENERATED! DO NOT EDIT!"; \
echo "// run \"make rebuild-constants\" if it needs to be updated"; \
echo ""; \
if(!pruneCases) throw new Exn("-o prunecases MUST be enabled for ClassFileCompiler");
// Class
- cl = new ClassGen(fullClassName,runtimeClass,source,ACC_SUPER|ACC_PUBLIC,null);
+ cl = new ClassGen(fullClassName,runtimeClass,source,ACC_SUPER|ACC_PUBLIC|ACC_FINAL,null);
cp = cl.getConstantPool();
fac = new InstructionFactory(cl,cp);
throw new Exn("Generation of the trampoline method failed. Try increasing maxInsnPerMethod");
}
+ addConstReturnMethod("gp",gp.addr);
+ addConstReturnMethod("entryPoint",elf.header.entry);
+ addConstReturnMethod("heapStart",highestAddr);
+
+ if(userInfo != null) {
+ addConstReturnMethod("userInfoBase",userInfo.addr);
+ addConstReturnMethod("userInfoSize",userInfo.size);
+ }
+
+ // FEATURE: Allow specification of memory size at runtime (numpages)
+ // Constructor
MethodGen init = newMethod(ACC_PUBLIC,Type.VOID, Type.NO_ARGS, "<init>");
selectMethod(init);
- // Constructor
a(InstructionConstants.ALOAD_0);
pushConst(pageSize);
pushConst(totalPages);
- pushConst(fastMem ? 0 : 1);
- a(fac.createInvoke(runtimeClass,"<init>",Type.VOID,new Type[]{Type.INT,Type.INT,Type.BOOLEAN},INVOKESPECIAL));
- a(InstructionConstants.ALOAD_0);
- pushConst(gp.addr);
- a(fac.createFieldAccess(fullClassName,"gp",Type.INT, PUTFIELD));
+ a(fac.createInvoke(runtimeClass,"<init>",Type.VOID,new Type[]{Type.INT,Type.INT},INVOKESPECIAL));
- a(InstructionConstants.ALOAD_0);
- pushConst(elf.header.entry);
- a(fac.createFieldAccess(fullClassName,"entryPoint",Type.INT, PUTFIELD));
-
- a(InstructionConstants.ALOAD_0);
- pushConst(onePage ? ((highestAddr+4095)&~4095) : ((highestAddr+pageSize-1)&~(pageSize-1)));
- a(fac.createFieldAccess(fullClassName,"brkAddr",Type.INT, PUTFIELD));
-
- if(userInfo != null) {
- a(InstructionConstants.ALOAD_0);
- pushConst(userInfo.addr);
- a(fac.createFieldAccess(fullClassName,"userInfoBase",Type.INT, PUTFIELD));
- a(InstructionConstants.ALOAD_0);
- pushConst(userInfo.size);
- a(fac.createFieldAccess(fullClassName,"userInfoSize",Type.INT, PUTFIELD));
- }
a(initExtras);
- a(InstructionConstants.ALOAD_0);
- pushConst(Runtime.INITIALIZED);
- a(fac.createFieldAccess(fullClassName,"state",Type.INT, PUTFIELD));
+
a(InstructionConstants.RETURN);
+
init.setMaxLocals();
init.setMaxStack();
cl.addMethod(init.getMethod());
+
MethodGen clinit = newMethod(ACC_PRIVATE|ACC_STATIC,Type.VOID, Type.NO_ARGS, "<clinit>");
selectMethod(clinit);
a(clinitExtras);
a(InstructionConstants.ALOAD_1);
a(fac.createFieldAccess("org.ibex.nestedvm.Runtime$CPUState","pc",Type.INT,GETFIELD));
a(fac.createFieldAccess(fullClassName,"pc",Type.INT, PUTFIELD));
+
a(InstructionConstants.RETURN);
setCPUState.setMaxLocals();
setCPUState.setMaxStack();
cl.addMethod(setCPUState.getMethod());
- MethodGen getCPUState = newMethod(ACC_PROTECTED,Type.getType("Lorg/ibex/nestedvm/Runtime$CPUState;"),Type.NO_ARGS,"getCPUState");
+ MethodGen getCPUState = newMethod(ACC_PROTECTED,Type.VOID,new Type[]{Type.getType("Lorg/ibex/nestedvm/Runtime$CPUState;")},"getCPUState");
selectMethod(getCPUState);
- a(fac.createNew("org.ibex.nestedvm.Runtime$CPUState"));
- a(InstructionConstants.DUP);
- a(fac.createInvoke("org.ibex.nestedvm.Runtime$CPUState","<init>",Type.VOID,Type.NO_ARGS,INVOKESPECIAL));
- a(InstructionConstants.ASTORE_1);
-
a(InstructionConstants.ALOAD_1);
a(fac.createFieldAccess("org.ibex.nestedvm.Runtime$CPUState","r",new ArrayType(Type.INT,1),GETFIELD));
a(InstructionConstants.ASTORE_2);
a(fac.createFieldAccess(fullClassName,"pc",Type.INT, GETFIELD));
a(fac.createFieldAccess("org.ibex.nestedvm.Runtime$CPUState","pc",Type.INT,PUTFIELD));
- a(InstructionConstants.ALOAD_1);
- a(InstructionConstants.ARETURN);
+ a(InstructionConstants.RETURN);
getCPUState.setMaxLocals();
getCPUState.setMaxStack();
cl.addMethod(getCPUState.getMethod());
+ // FEATURE: Catch RuntimeException and turn it into a fault exception
MethodGen execute = newMethod(ACC_PROTECTED,Type.VOID,Type.NO_ARGS,"_execute");
selectMethod(execute);
a(InstructionConstants.ALOAD_0);
a(fac.createInvoke(fullClassName,"<init>",Type.VOID,Type.NO_ARGS,INVOKESPECIAL));
a(new PUSH(cp,fullClassName));
a(InstructionConstants.ALOAD_0);
- a(fac.createInvoke(fullClassName,"run",Type.INT,new Type[]{Type.STRING,new ArrayType(Type.STRING,1)},INVOKEVIRTUAL));
+ if(unixRuntime)
+ a(fac.createInvoke("org.ibex.nestedvm.UnixRuntime","runAndExec",Type.INT,
+ new Type[]{Type.getType("Lorg/ibex/nestedvm/UnixRuntime;"),Type.STRING,new ArrayType(Type.STRING,1)},
+ INVOKESTATIC));
+ else
+ a(fac.createInvoke(fullClassName,"run",Type.INT,new Type[]{Type.STRING,new ArrayType(Type.STRING,1)},INVOKEVIRTUAL));
a(fac.createInvoke("java.lang.System","exit",Type.VOID,new Type[]{Type.INT},INVOKESTATIC));
a(InstructionConstants.RETURN);
main.setMaxLocals();
cl.getJavaClass().dump(os);
}
+
+ private void addConstReturnMethod(String name, int val) {
+ MethodGen method = newMethod(ACC_PROTECTED,Type.INT, Type.NO_ARGS,name);
+ selectMethod(method);
+ pushConst(val);
+ a(InstructionConstants.IRETURN);
+ method.setMaxLocals();
+ method.setMaxStack();
+ cl.addMethod(method.getMethod());
+ }
private static int initDataCount;
private void emitData(int addr, DataInputStream dis, int size, boolean readOnly) throws Exn,IOException {
package org.ibex.nestedvm;
import java.io.*;
+import java.util.*;
+
+import org.ibex.nestedvm.util.*;
+
+// FIXME: This is totally broken now
// FEATURE: This is just a quick hack, it is really ugly and broken
// NOTE: Need to handle binaries spanned accross many classfiles
public class ClassLoader extends java.lang.ClassLoader {
+ private Hashtable cache = new Hashtable();
+
public Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class c;
- if(name.startsWith("mips.")) {
- String path = name.substring(5).replace('.','/') + ".mips";
+ if(name.startsWith("nestedvm.")) {
+ throw new Error("probably shouldn't be here");
+ /*String path = name.substring(5).replace('.','/') + ".mips";
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
new ClassFileCompiler(path,name,bos).go();
throw new ClassNotFoundException(name);
} catch(Compiler.Exn e) {
throw new ClassNotFoundException(e.getMessage());
- }
+ }*/
} else {
c = findSystemClass(name);
}
return c;
}
- public Class classFromBinary(String path) throws ClassNotFoundException {
+ /*public synchronized void clearCache() {
+ cache.clear();
+ }
+
+ public synchronized Class getCachedClass(String key, long timeStamp) {
+ String name = "nestedvm." + Base64.encode(key);
+ CacheEnt ent = (CacheEnt) cache.get(name);
+ if(ent.timeStamp < timeStamp) {
+ cache.remove(key);
+ return null;
+ }
+ return ent.klass;
+ }
+
+ public synchronized Class createClass(String key, long timeStamp, Seekable data) {
+ Class klass = getCachedClass(key,timeStamp);
+ if(klass != null) return klass;
+ try {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ new ClassFileCompiler(data,name,bos).go();
+ bos.close();
+ byte[] buf = bos.toByteArray();
+ klass = defineClass(name,buf,0,buf.length);
+ resolveClass(klass);
+ cache.put(key,new CacheEnt(klass,timeStamp));
+ return klass;
+ } catch(Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ private class CacheEnt {
+ public CacheEnt(Class klass, long timeStamp) { this.klass = klass; this.timeStamp = timeStamp; }
+ public Class klass;
+ public long timeStamp;
+ }*/
+
+ /*public Class classFromBinary(String path) throws ClassNotFoundException {
if(!path.endsWith(".mips")) throw new IllegalArgumentException("isn't a .mips");
return loadClass("mips." + path.substring(0,path.length()-5).replace('/','.'));
}
public static void main(String[] args) throws Exception {
System.exit(new ClassLoader().runtimeFromBinary(args[0]).run(args));
- }
+ }*/
}
protected boolean onePage;
protected void pageSizeInit() throws Exn {
- try {
- Runtime.checkPageSize(pageSize,totalPages);
- } catch(IllegalArgumentException e) {
- throw new Exn(e.getMessage());
- }
+ if((pageSize&(pageSize-1)) != 0) throw new Exn("pageSize not a multiple of two");
+ if((totalPages&(totalPages-1)) != 0) throw new Exn("totalPages not a multiple of two");
while(pageSize>>>pageShift != 1) pageShift++;
}
// Main interpretor
// the return value is meaningless, its just to catch people typing "return" by accident
private final int runSome() throws FaultException,ExecutionException {
+ final int PAGE_WORDS = (1<<pageShift)>>2;
int[] r = registers;
int[] f = fpregs;
int pc = this.pc;
OUTER: for(;;) {
int insn;
try {
- insn = readPages[pc>>>PAGE_SHIFT][(pc>>>2)&PAGE_WORDS-1];
+ insn = readPages[pc>>>pageShift][(pc>>>2)&PAGE_WORDS-1];
} catch (RuntimeException e) {
insn = memRead(pc);
}
case 32: { // LB
addr = r[rs] + signedImmediate;
try {
- tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
+ tmp = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)];
} catch(RuntimeException e) {
tmp = memRead(addr&~3);
}
case 33: { // LH
addr = r[rs] + signedImmediate;
try {
- tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
+ tmp = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)];
} catch(RuntimeException e) {
tmp = memRead(addr&~3);
}
case 34: { // LWL;
addr = r[rs] + signedImmediate;
try {
- tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
+ tmp = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)];
} catch(RuntimeException e) {
tmp = memRead(addr&~3);
}
case 35: // LW
addr = r[rs] + signedImmediate;
try {
- r[rt] = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
+ r[rt] = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)];
} catch(RuntimeException e) {
r[rt] = memRead(addr);
}
case 36: { // LBU
addr = r[rs] + signedImmediate;
try {
- tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
+ tmp = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)];
} catch(RuntimeException e) {
tmp = memRead(addr);
}
case 37: { // LHU
addr = r[rs] + signedImmediate;
try {
- tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
+ tmp = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)];
} catch(RuntimeException e) {
tmp = memRead(addr&~3);
}
case 38: { // LWR
addr = r[rs] + signedImmediate;
try {
- tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
+ tmp = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)];
} catch(RuntimeException e) {
tmp = memRead(addr&~3);
}
case 40: { // SB
addr = r[rs] + signedImmediate;
try {
- tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
+ tmp = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)];
} catch(RuntimeException e) {
tmp = memRead(addr&~3);
}
case 3: tmp = (tmp&0xffffff00) | ((r[rt]&0xff)<< 0); break;
}
try {
- writePages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)] = tmp;
+ writePages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)] = tmp;
} catch(RuntimeException e) {
memWrite(addr&~3,tmp);
}
case 41: { // SH
addr = r[rs] + signedImmediate;
try {
- tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
+ tmp = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)];
} catch(RuntimeException e) {
tmp = memRead(addr&~3);
}
case 2: tmp = (tmp&0xffff0000) | ((r[rt]&0xffff)<< 0); break;
}
try {
- writePages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)] = tmp;
+ writePages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)] = tmp;
} catch(RuntimeException e) {
memWrite(addr&~3,tmp);
}
case 3: tmp=(tmp&0xffffff00)|(r[rt]>>>24); break;
}
try {
- writePages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)] = tmp;
+ writePages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)] = tmp;
} catch(RuntimeException e) {
memWrite(addr&~3,tmp);
}
case 43: // SW
addr = r[rs] + signedImmediate;
try {
- writePages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)] = r[rt];
+ writePages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)] = r[rt];
} catch(RuntimeException e) {
memWrite(addr&~3,r[rt]);
}
return sym == null ? -1 : sym.addr;
}
+ private int gp;
+ protected int gp() { return gp; }
+
+ private ELF.Symbol userInfo;
+ protected int userInfoBae() { return userInfo == null ? 0 : userInfo.addr; }
+ protected int userInfoSize() { return userInfo == null ? 0 : userInfo.size; }
+
+ private int entryPoint;
+ protected int entryPoint() { return entryPoint; }
+
+ private int heapStart;
+ protected int heapStart() { return heapStart; }
+
// Image loading function
private void loadImage(Seekable data) throws IOException {
- if(state != UNINITIALIZED) throw new IllegalStateException("loadImage called on initialized runtime");
-
ELF elf = new ELF(data);
symtab = elf.getSymtab();
ELF.Symtab symtab = elf.getSymtab();
if(symtab == null) throw new IOException("No symtab in binary (did you strip it?)");
- ELF.Symbol userInfo = symtab.getGlobalSymbol("user_info");
+ userInfo = symtab.getGlobalSymbol("user_info");
ELF.Symbol gpsym = symtab.getGlobalSymbol("_gp");
if(gpsym == null) throw new IOException("NO _gp symbol!");
gp = gpsym.addr;
- if(userInfo != null) {
- userInfoBase = userInfo.addr;
- userInfoSize = userInfo.size;
- }
+ entryPoint = elf.header.entry;
ELF.PHeader[] pheaders = elf.pheaders;
int brk = 0;
+ int pageSize = (1<<pageShift);
+ int pageWords = (1<<pageShift) >> 2;
for(int i=0;i<pheaders.length;i++) {
ELF.PHeader ph = pheaders[i];
if(ph.type != ELF.PHeader.PT_LOAD) continue;
if(addr == 0x0) throw new IOException("pheader vaddr == 0x0");
brk = max(addr+memsize,brk);
- for(int j=0;j<memsize+PAGE_SIZE-1;j+=PAGE_SIZE) {
- int page = (j+addr) >>> PAGE_SHIFT;
+ for(int j=0;j<memsize+pageSize-1;j+=pageSize) {
+ int page = (j+addr) >>> pageShift;
if(readPages[page] == null)
- readPages[page] = new int[PAGE_WORDS];
+ readPages[page] = new int[pageWords];
if(ph.writable()) writePages[page] = readPages[page];
}
if(filesize != 0) {
filesize = filesize & ~3;
DataInputStream dis = new DataInputStream(ph.getInputStream());
do {
- readPages[addr >>> PAGE_SHIFT][(addr >>> 2)&(PAGE_WORDS-1)] = dis.readInt();
+ readPages[addr >>> pageShift][(addr >>> 2)&(pageWords-1)] = dis.readInt();
addr+=4;
filesize-=4;
} while(filesize > 0);
dis.close();
}
}
- brkAddr = (brk+PAGE_SIZE-1)&~(PAGE_SIZE-1);
- state = INITIALIZED;
+ heapStart = (brk+pageSize-1)&~(pageSize-1);
}
protected void setCPUState(CPUState state) {
pc=state.pc;
}
- protected CPUState getCPUState() {
- CPUState state = new CPUState();
+ protected void getCPUState(CPUState state) {
for(int i=1;i<32;i++) state.r[i] = registers[i];
for(int i=0;i<32;i++) state.f[i] = fpregs[i];
state.hi=hi; state.lo=lo; state.fcsr=fcsr;
state.pc=pc;
- return state;
}
- // This is package private for fork() which does all kinds of ugly things behind the scenes
- Interpreter() { super(4096,65536,true); }
- public Interpreter(Seekable data) throws IOException { this(); loadImage(data); }
+ public Interpreter(Seekable data) throws IOException {
+ super(4096,65536);
+ loadImage(data);
+ }
public Interpreter(String filename) throws IOException {
this(new Seekable.File(filename,false));
image = filename;
if (packageName != null) p("package " + packageName + ";");
if(runtimeStats) p("import java.util.*;");
p();
- p("public class " + className + " extends " + runtimeClass + " {");
+ p("public final class " + className + " extends " + runtimeClass + " {");
indent++;
p("/* program counter */");
// Constructor
p("public " + className + "() {");
indent++;
- p("super(" + pageSize + "," + totalPages + "," + (fastMem?"false":"true") + ");");
- p("entryPoint = " + toHex(elf.header.entry) + ";");
- if(userInfo != null) {
- p("userInfoBase=" + toHex(userInfo.addr) + ";");
- p("userInfoSize=" + userInfo.size + ";");
- }
- p("gp = " + toHex(gp.addr) + ";");
- if(onePage)
- p("brkAddr = " + toHex((highestAddr+4095)&~4095) + ";");
- else
- p("brkAddr = " + toHex((highestAddr+pageSize-1)&~(pageSize-1)) + ";");
+ p("super(" + pageSize + "," + totalPages + ");");
pblock(inits);
- p("state = INITIALIZED;");
indent--;
p("}");
p();
+ p("protected int entryPoint() { return " + toHex(elf.header.entry) + "; }");
+ p("protected int heapStart() { return " + toHex(highestAddr) + "; }");
+ p("protected int gp() { return " + toHex(gp.addr) + "; }");
+ if(userInfo != null) {
+ p("protected int userInfoBase() { return " + toHex(userInfo.addr) + "; }");
+ p("protected int userInfoSize() { return " + toHex(userInfo.size) + "; }");
+ }
+
// main() function
p("public static void main(String[] args) throws Exception {");
indent++;
p("pc=state.pc;");
indent--;
p("}");
- p("protected CPUState getCPUState() {");
+ p("protected void getCPUState(CPUState state) {");
indent++;
- p("CPUState state = new CPUState();");
for(int i=1;i<32;i++) p("state.r[" + i + "]=r" + i+ ";");
for(int i=0;i<32;i++) p("state.f[" + i + "]=f" + i +";");
p("state.hi=hi; state.lo=lo; state.fcsr=fcsr;");
p("state.pc=pc;");
- p("return state;");
indent--;
p("}");
p();
// FEATURE: Look over the public API, make sure we're exposing a bare minimum
// (we might make this an interface in the future)
-public abstract class Runtime implements UsermodeConstants,Registers {
- /** Pages are 4k in size */
- protected final int PAGE_SIZE;
- protected final int PAGE_WORDS;
- protected final int PAGE_SHIFT;
- protected final int TOTAL_PAGES;
- /** This is the upper limit of the pages allocated by the sbrk() syscall. */
- protected final int BRK_LIMIT;
- protected final int STACK_BOTTOM;
-
- /** This is the maximum size of command line arguments */
- public final static int ARGS_MAX = 1024*1024;
-
- /** True if we allow empty pages (_emptyPage) to exist in memory.
- Empty pages are pages which are allocated by the program but do not contain any
- data yet (they are all 0s). If empty pages are allowed subclasses must always
- access main memory with the memRead and memWrite functions */
- private final boolean allowEmptyPages;
- /** the "empty page" */
- private final static int[] _emptyPage = new int[0];
-
- protected final static boolean isEmptyPage(int[] page) { return page == _emptyPage; }
-
- /** Returns a new empty page (_emptyPage is empty pages are enabled or a new zero'd page) */
- private final int[] emptyPage() { return allowEmptyPages ? _emptyPage : new int[PAGE_WORDS]; }
+public abstract class Runtime implements UsermodeConstants,Registers,Cloneable {
+ /** Number of bits to shift to get the page number (1<<<pageShift == pageSize) */
+ protected final int pageShift;
+ /** Bottom of region of memory allocated to the stack */
+ protected final int stackBottom;
/** Readable main memory pages */
- protected final int[][] readPages;
+ protected int[][] readPages;
/** Writable main memory pages.
If the page is writable writePages[x] == readPages[x]; if not writePages[x] == null. */
- protected final int[][] writePages;
+ protected int[][] writePages;
- /** The current break between the heap and unallocated memory */
- protected int brkAddr;
+ /** The address of the end of the heap */
+ private int heapEnd;
+
+ /** Number of guard pages to keep between the stack and the heap */
+ private static final int STACK_GUARD_PAGES = 4;
+
+ /** The last address the executable uses (other than the heap/stack) */
+ protected abstract int heapStart();
/** The program's entry point */
- protected int entryPoint;
+ protected abstract int entryPoint();
/** The location of the _user_info block (or 0 is there is none) */
- protected int userInfoBase;
- protected int userInfoSize;
+ protected int userInfoBase() { return 0; }
+ protected int userInfoSize() { return 0; }
/** The location of the global pointer */
- protected int gp;
+ protected abstract int gp();
/** When the process started */
private long startTime;
- /** State constant: There is no program loaded in memory */
- public final static int UNINITIALIZED = 0;
/** Text/Data loaded in memory */
- public final static int INITIALIZED = 1;
+ public final static int STOPPED = 0;
/** Program is executing instructions */
- public final static int RUNNING = 2;
+ public final static int RUNNING = 1;
/** Prgram has been started but is paused */
- public final static int PAUSED = 3;
+ public final static int PAUSED = 2;
/** Program is executing a callJava() method */
- public final static int CALLJAVA = 4;
+ public final static int CALLJAVA = 3;
/** Program has exited (it cannot currently be restarted) */
- public final static int DONE = 5;
-
- /** The current state (UNINITIALIZED, INITIALIZED, RUNNING, PAUSED, or DONE) */
- protected int state = UNINITIALIZED;
+ public final static int EXITED = 4;
+ /** Program has executed a successful exec(), a new Runtime needs to be run (used by UnixRuntime) */
+ public final static int EXECED = 5;
+
+ /** The current state */
+ protected int state = STOPPED;
/** @see Runtime#state state */
public final int getState() { return state; }
/** The exit status if the process (only valid if state==DONE)
@see Runtime#state */
- protected int exitStatus;
+ private int exitStatus;
public ExecutionException exitException;
- /** Maximum number of open file descriptors */
- final static int OPEN_MAX = 256;
/** Table containing all open file descriptors. (Entries are null if the fd is not in use */
- FD[] fds = new FD[OPEN_MAX];
+ FD[] fds = new FD[OPEN_MAX]; // package-private for UnixRuntime
+ boolean closeOnExec[] = new boolean[OPEN_MAX];
/** Pointer to a callback for the call_java syscall */
protected CallJavaCB callJavaCB;
public CallJavaCB getCallJavaCB() { return callJavaCB; }
/** Temporary buffer for read/write operations */
- private byte[] _byteBuf = null;
+ private byte[] _byteBuf;
/** Max size of temporary buffer
@see Runtime#_byteBuf */
- private final static int MAX_CHUNK = 15*1024*1024;
+ private final static int MAX_CHUNK = 16*1024*1024 - 1024;
/** Subclasses should actually execute program in this method. They should continue
executing until state != RUNNING. Only syscall() can modify state. It is safe
This method is only required if the call() function is used */
protected int lookupSymbol(String symbol) { return -1; }
- /** Subclasses should returns a CPUState object representing the cpu state */
- protected abstract CPUState getCPUState();
+ /** Subclasses should populate a CPUState object representing the cpu state */
+ protected abstract void getCPUState(CPUState state);
/** Subclasses should set the CPUState to the state held in <i>state</i> */
protected abstract void setCPUState(CPUState state);
-
- static void checkPageSize(int pageSize, int totalPages) throws IllegalArgumentException {
- if(pageSize < 256) throw new IllegalArgumentException("pageSize too small");
- if((pageSize&(pageSize-1)) != 0) throw new IllegalArgumentException("pageSize must be a power of two");
- if((totalPages&(totalPages-1)) != 0) throw new IllegalArgumentException("totalPages must be a power of two");
- if(totalPages != 1 && totalPages < 256) throw new IllegalArgumentException("totalPages too small");
- if(totalPages * pageSize < 4*1024*1024) throw new IllegalArgumentException("total memory too small (" + totalPages + "*" + pageSize + ")");
+
+ protected Object clone() throws CloneNotSupportedException {
+ Runtime r = (Runtime) super.clone();
+ r._byteBuf = null;
+ r.startTime = 0;
+ r.fds = new FD[OPEN_MAX];
+ for(int i=0;i<OPEN_MAX;i++) if(fds[i] != null) r.fds[i] = fds[i].dup();
+ int totalPages = writePages.length;
+ r.readPages = new int[totalPages][];
+ r.writePages = new int[totalPages][];
+ for(int i=0;i<totalPages;i++) {
+ if(readPages[i] == null) continue;
+ if(writePages[i] == null) r.readPages[i] = readPages[i];
+ else r.readPages[i] = r.writePages[i] = (int[])writePages[i].clone();
+ }
+ return r;
}
- protected Runtime(int pageSize, int totalPages, boolean allowEmptyPages) {
- this.allowEmptyPages = allowEmptyPages;
-
- checkPageSize(pageSize,totalPages);
-
- PAGE_SIZE = pageSize;
- PAGE_WORDS = pageSize>>>2;
- int pageShift = 0;
- while(pageSize>>>pageShift != 1) pageShift++;
- PAGE_SHIFT = pageShift;
-
- TOTAL_PAGES = totalPages;
-
- readPages = new int[TOTAL_PAGES][];
- writePages = new int[TOTAL_PAGES][];
+ protected Runtime(int pageSize, int totalPages) {
+ if(pageSize <= 0) throw new IllegalArgumentException("pageSize <= 0");
+ if(totalPages <= 0) throw new IllegalArgumentException("totalPages <= 0");
+ if((pageSize&(pageSize-1)) != 0) throw new IllegalArgumentException("pageSize not a power of two");
+
+ int _pageShift = 0;
+ while(pageSize>>>_pageShift != 1) _pageShift++;
+ pageShift = _pageShift;
- if(TOTAL_PAGES == 1) {
- readPages[0] = writePages[0] = new int[PAGE_WORDS];
- BRK_LIMIT = STACK_BOTTOM = 0;
+ int heapStart = heapStart();
+ int totalMemory = totalPages * pageSize;
+ int stackSize = max(totalMemory/64,ARG_MAX+65536);
+ int stackPages = 0;
+ if(totalPages > 1) {
+ stackSize = max(stackSize,pageSize);
+ stackSize = (stackSize + pageSize) & ~(pageSize-1);
+ stackPages = stackSize >>> pageShift;
+ heapStart = (heapStart + pageSize) & ~(pageSize-1);
+ if(stackPages + STACK_GUARD_PAGES + (heapStart >>> pageShift) >= totalPages)
+ throw new IllegalArgumentException("total pages too small");
} else {
- int stackPages = max(TOTAL_PAGES>>>8,(1024*1024)>>>PAGE_SHIFT);
- STACK_BOTTOM = (TOTAL_PAGES - stackPages) * PAGE_SIZE;
- // leave some unmapped pages between the stack and the heap
- BRK_LIMIT = STACK_BOTTOM - 4*PAGE_SIZE;
-
- for(int i=0;i<stackPages;i++)
- readPages[TOTAL_PAGES-1-i] = writePages[TOTAL_PAGES-1-i] = emptyPage();
+ if(pageSize < heapStart + stackSize) throw new IllegalArgumentException("total memory too small");
+ heapStart = (heapStart + 4095) & ~4096;
}
+ stackBottom = totalMemory - stackSize;
+ heapEnd = heapStart;
+
+ readPages = new int[totalPages][];
+ writePages = new int[totalPages][];
+
+ if(totalPages == 1)
+ readPages[0] = writePages[0] = new int[pageSize>>2];
+ else
+ for(int i=stackBottom >>> pageShift;i<writePages.length;i++)
+ readPages[i] = writePages[i] = new int[pageSize>>2];
+
addFD(new StdinFD(System.in));
addFD(new StdoutFD(System.out));
addFD(new StdoutFD(System.err));
/** Copy everything from <i>src</i> to <i>addr</i> initializing uninitialized pages if required.
Newly initalized pages will be marked read-only if <i>ro</i> is set */
protected final void initPages(int[] src, int addr, boolean ro) {
+ int pageWords = (1<<pageShift)>>>2;
+ int pageMask = (1<<pageShift) - 1;
+
for(int i=0;i<src.length;) {
- int page = addr >>> PAGE_SHIFT;
- int start = (addr&(PAGE_SIZE-1))>>2;
- int elements = min(PAGE_WORDS-start,src.length-i);
+ int page = addr >>> pageShift;
+ int start = (addr&pageMask)>>2;
+ int elements = min(pageWords-start,src.length-i);
if(readPages[page]==null) {
initPage(page,ro);
} else if(!ro) {
/** Initialize <i>words</i> of pages starting at <i>addr</i> to 0 */
protected final void clearPages(int addr, int words) {
+ int pageWords = (1<<pageShift)>>>2;
+ int pageMask = (1<<pageShift) - 1;
+
for(int i=0;i<words;) {
- int page = addr >>> PAGE_SHIFT;
- int start = (addr&(PAGE_SIZE-1))>>2;
- int elements = min(PAGE_WORDS-start,words-i);
+ int page = addr >>> pageShift;
+ int start = (addr&pageMask)>>2;
+ int elements = min(pageWords-start,words-i);
if(readPages[page]==null) {
- readPages[page] = writePages[page] = emptyPage();
+ readPages[page] = writePages[page] = new int[pageWords];
} else {
if(writePages[page] == null) writePages[page] = readPages[page];
for(int j=start;j<start+elements;j++) writePages[page][j] = 0;
/** Copies <i>length</i> bytes from the processes memory space starting at
<i>addr</i> INTO a java byte array <i>a</i> */
public final void copyin(int addr, byte[] buf, int count) throws ReadFaultException {
+ int pageWords = (1<<pageShift)>>>2;
+ int pageMask = pageWords - 1;
+
int x=0;
if(count == 0) return;
if((addr&3)!=0) {
int c = count>>>2;
int a = addr>>>2;
while(c != 0) {
- int[] page = readPages[a >>> (PAGE_SHIFT-2)];
+ int[] page = readPages[a >>> (pageShift-2)];
if(page == null) throw new ReadFaultException(a<<2);
- int index = a&(PAGE_WORDS-1);
- int n = min(c,PAGE_WORDS-index);
- if(page != _emptyPage) {
- for(int i=0;i<n;i++,x+=4) {
- int word = page[index+i];
- buf[x+0] = (byte)((word>>>24)&0xff); buf[x+1] = (byte)((word>>>16)&0xff);
- buf[x+2] = (byte)((word>>> 8)&0xff); buf[x+3] = (byte)((word>>> 0)&0xff);
- }
+ int index = a&pageMask;
+ int n = min(c,pageWords-index);
+ for(int i=0;i<n;i++,x+=4) {
+ int word = page[index+i];
+ buf[x+0] = (byte)((word>>>24)&0xff); buf[x+1] = (byte)((word>>>16)&0xff);
+ buf[x+2] = (byte)((word>>> 8)&0xff); buf[x+3] = (byte)((word>>> 0)&0xff);
}
a += n; c -=n;
}
/** Copies <i>length</i> bytes OUT OF the java array <i>a</i> into the processes memory
space at <i>addr</i> */
public final void copyout(byte[] buf, int addr, int count) throws FaultException {
+ int pageWords = (1<<pageShift)>>>2;
+ int pageWordMask = pageWords - 1;
+
int x=0;
if(count == 0) return;
if((addr&3)!=0) {
memWrite(addr&~3,word);
addr += x;
}
+
if((count&~3) != 0) {
int c = count>>>2;
int a = addr>>>2;
while(c != 0) {
- int[] page = writePages[a >>> (PAGE_SHIFT-2)];
+ int[] page = writePages[a >>> (pageShift-2)];
if(page == null) throw new WriteFaultException(a<<2);
- if(page == _emptyPage) page = initPage(a >>> (PAGE_SHIFT-2));
- int index = a&(PAGE_WORDS-1);
- int n = min(c,PAGE_WORDS-index);
+ int index = a&pageWordMask;
+ int n = min(c,pageWords-index);
for(int i=0;i<n;i++,x+=4)
page[index+i] = ((buf[x+0]&0xff)<<24)|((buf[x+1]&0xff)<<16)|((buf[x+2]&0xff)<<8)|((buf[x+3]&0xff)<<0);
a += n; c -=n;
}
addr = a<<2; count&=3;
}
+
if(count != 0) {
int word = memRead(addr);
switch(count) {
}
public final void memcpy(int dst, int src, int count) throws FaultException {
+ int pageWords = (1<<pageShift)>>>2;
+ int pageWordMask = pageWords - 1;
+
if((dst&3) == 0 && (src&3)==0) {
if((count&~3) != 0) {
int c = count>>2;
int s = src>>>2;
int d = dst>>>2;
while(c != 0) {
- int[] srcPage = readPages[s>>>(PAGE_SHIFT-2)];
+ int[] srcPage = readPages[s>>>(pageShift-2)];
if(srcPage == null) throw new ReadFaultException(s<<2);
- int[] dstPage = writePages[d>>>(PAGE_SHIFT-2)];
+ int[] dstPage = writePages[d>>>(pageShift-2)];
if(dstPage == null) throw new WriteFaultException(d<<2);
- int srcIndex = (s&(PAGE_WORDS-1));
- int dstIndex = (d&(PAGE_WORDS-1));
- int n = min(c,PAGE_WORDS-max(srcIndex,dstIndex));
- if(srcPage != _emptyPage) {
- if(dstPage == _emptyPage) dstPage = initPage(d>>>(PAGE_SHIFT-2));
- System.arraycopy(srcPage,srcIndex,dstPage,dstIndex,n);
- } else if(srcPage == _emptyPage && dstPage != _emptyPage) {
- Arrays.fill(dstPage,dstIndex,dstIndex+n,0);
- }
+ int srcIndex = s&pageWordMask;
+ int dstIndex = d&pageWordMask;
+ int n = min(c,pageWords-max(srcIndex,dstIndex));
+ System.arraycopy(srcPage,srcIndex,dstPage,dstIndex,n);
s += n; d += n; c -= n;
}
src = s<<2; dst = d<<2; count&=3;
}
public final void memset(int addr, int ch, int count) throws FaultException {
+ int pageWords = (1<<pageShift)>>>2;
+ int pageWordMask = pageWords - 1;
+
int fourBytes = ((ch&0xff)<<24)|((ch&0xff)<<16)|((ch&0xff)<<8)|((ch&0xff)<<0);
if((addr&3)!=0) {
int word = memRead(addr&~3);
int c = count>>2;
int a = addr>>>2;
while(c != 0) {
- int[] page = readPages[a>>>(PAGE_SHIFT-2)];
+ int[] page = readPages[a>>>(pageShift-2)];
if(page == null) throw new WriteFaultException(a<<2);
- int index = (a&(PAGE_WORDS-1));
- int n = min(c,PAGE_WORDS-index);
- if(page != _emptyPage || ch != 0) {
- if(page == _emptyPage) page = initPage(a>>>(PAGE_SHIFT-2));
- Arrays.fill(page,index,index+n,fourBytes);
- }
+ int index = a&pageWordMask;
+ int n = min(c,pageWords-index);
+ Arrays.fill(page,index,index+n,fourBytes);
a += n; c -= n;
}
addr = a<<2; count&=3;
}
protected final int unsafeMemRead(int addr) throws ReadFaultException {
- int page = addr >>> PAGE_SHIFT;
- int entry = (addr >>> 2) & (PAGE_WORDS-1);
+ int page = addr >>> pageShift;
+ int entry = (addr&(1<<pageShift) - 1)>>2;
try {
return readPages[page][entry];
} catch(ArrayIndexOutOfBoundsException e) {
- if(page < 0) throw e; // should never happen
- if(page >= readPages.length) throw new ReadFaultException(addr);
- if(readPages[page] != _emptyPage) throw e; // should never happen
- initPage(page);
- return 0;
+ if(page < 0 || page >= readPages.length) throw new ReadFaultException(addr);
+ throw e; // should never happen
} catch(NullPointerException e) {
throw new ReadFaultException(addr);
}
}
protected final void unsafeMemWrite(int addr, int value) throws WriteFaultException {
- int page = addr >>> PAGE_SHIFT;
- int entry = (addr>>>2)&(PAGE_WORDS-1);
+ int page = addr >>> pageShift;
+ int entry = (addr&(1<<pageShift) - 1)>>2;
try {
writePages[page][entry] = value;
} catch(ArrayIndexOutOfBoundsException e) {
- if(page < 0) throw e;// should never happen
- if(page >= writePages.length) throw new WriteFaultException(addr);
- if(readPages[page] != _emptyPage) throw e; // should never happen
- initPage(page);
- writePages[page][entry] = value;
+ if(page < 0 || page >= writePages.length) throw new WriteFaultException(addr);
+ throw e; // should never happen
} catch(NullPointerException e) {
throw new WriteFaultException(addr);
}
private final int[] initPage(int page) { return initPage(page,false); }
/** Created a new non-empty page at page number <i>page</i>. If <i>ro</i> is set the page will be read-only */
private final int[] initPage(int page, boolean ro) {
- int[] buf = new int[PAGE_WORDS];
+ int[] buf = new int[(1<<pageShift)>>>2];
writePages[page] = ro ? null : buf;
readPages[page] = buf;
return buf;
/** Returns the exit status of the process. (only valid if state == DONE)
@see Runtime#state */
public final int exitStatus() {
- if(state != DONE) throw new IllegalStateException("exitStatus() called in an inappropriate state");
+ if(state != EXITED) throw new IllegalStateException("exitStatus() called in an inappropriate state");
return exitStatus;
}
- private int addStringArray(String[] strings, int topAddr) {
+ private int addStringArray(String[] strings, int topAddr) throws FaultException {
int count = strings.length;
int total = 0; /* null last table entry */
for(int i=0;i<count;i++) total += strings[i].length() + 1;
- if(total >= ARGS_MAX) throw new IllegalArgumentException("arguments/environ too big");
total += (count+1)*4;
int start = (topAddr - total)&~3;
int addr = start + (count+1)*4;
* and location of the user_info table from the ELF symbol table. setUserInfo and
* getUserInfo are used to modify the words in the user_info table. */
public void setUserInfo(int index, int word) {
- if(index < 0 || index >= userInfoSize/4) throw new IndexOutOfBoundsException("setUserInfo called with index >= " + (userInfoSize/4));
+ if(index < 0 || index >= userInfoSize()/4) throw new IndexOutOfBoundsException("setUserInfo called with index >= " + (userInfoSize()/4));
try {
- memWrite(userInfoBase+index*4,word);
- } catch(FaultException e) { throw new Error("should never happen: " + e); }
+ memWrite(userInfoBase()+index*4,word);
+ } catch(FaultException e) { throw new RuntimeException(e.toString()); }
}
/** Returns the word in the _user_info table entry <i>index</i>
@see Runtime#setUserInfo(int,int) setUserInfo */
public int getUserInfo(int index) {
- if(index < 0 || index >= userInfoSize/4) throw new IndexOutOfBoundsException("setUserInfo called with index >= " + (userInfoSize/4));
+ if(index < 0 || index >= userInfoSize()/4) throw new IndexOutOfBoundsException("setUserInfo called with index >= " + (userInfoSize()/4));
try {
- return memRead(userInfoBase+index*4);
- } catch(FaultException e) { throw new Error("should never happen: " + e); }
+ return memRead(userInfoBase()+index*4);
+ } catch(FaultException e) { throw new RuntimeException(e.toString()); }
}
/** Calls _execute() (subclass's execute()) and catches exceptions */
if(startTime == 0) startTime = System.currentTimeMillis();
state = RUNNING;
__execute();
- if(state != PAUSED && state != DONE) throw new IllegalStateException("execute() ended up in an inappropriate state (" + state + ")");
- return state == DONE;
+ if(state != PAUSED && state != EXITED && state != EXECED)
+ throw new IllegalStateException("execute() ended up in an inappropriate state (" + state + ")");
+ return state != PAUSED;
}
- public final int run() { return run(null); }
- public final int run(String argv0, String[] rest) {
- String[] args = new String[rest.length+1];
- System.arraycopy(rest,0,args,1,rest.length);
- args[0] = argv0;
- return run(args);
+ protected static String[] concatArgv(String argv0, String[] rest) {
+ String[] argv = new String[rest.length+1];
+ System.arraycopy(rest,0,argv,1,rest.length);
+ argv[0] = argv0;
+ return argv;
}
+
+ public final int run() { return run(null); }
+ public final int run(String argv0, String[] rest) { return run(concatArgv(argv0,rest)); }
public final int run(String[] args) { return run(args,null); }
/** Runs the process until it exits and returns the exit status.
for(;;) {
if(execute()) break;
System.err.println("WARNING: Pause requested while executing run()");
- try { Thread.sleep(500); } catch(InterruptedException e) { /* noop */ }
}
- return exitStatus();
+ if(state == EXECED) System.err.println("WARNING: Process exec()ed while being run under run()");
+ return state == EXITED ? exitStatus() : 0;
}
public final void start() { start(null); }
/** Initializes the process and prepairs it to be executed with execute() */
public final void start(String[] args, String[] environ) {
- int sp, argsAddr, envAddr;
- if(state != INITIALIZED) throw new IllegalStateException("start() called in inappropriate state");
+ int top, sp, argsAddr, envAddr;
+ if(state != STOPPED) throw new IllegalStateException("start() called in inappropriate state");
if(args == null) args = new String[]{getClass().getName()};
- sp = TOTAL_PAGES*PAGE_SIZE-512;
- sp = argsAddr = addStringArray(args,sp);
- sp = envAddr = addStringArray(createEnv(environ),sp);
+ sp = top = writePages.length*(1<<pageShift);
+ try {
+ sp = argsAddr = addStringArray(args,sp);
+ sp = envAddr = addStringArray(createEnv(environ),sp);
+ } catch(FaultException e) {
+ throw new IllegalArgumentException("args/environ too big");
+ }
sp &= ~15;
-
+ if(top - sp > ARG_MAX) throw new IllegalArgumentException("args/environ too big");
+
CPUState cpuState = new CPUState();
cpuState.r[A0] = argsAddr;
cpuState.r[A1] = envAddr;
cpuState.r[SP] = sp;
cpuState.r[RA] = 0xdeadbeef;
- cpuState.r[GP] = gp;
- cpuState.pc = entryPoint;
+ cpuState.r[GP] = gp();
+ cpuState.pc = entryPoint();
setCPUState(cpuState);
-
+
state = PAUSED;
- _start();
+ _start();
}
/** Hook for subclasses to do their own startup */
public final int call(int addr, int a0, int a1, int a2, int a3, int s0, int s1, int s2, int s3) {
if(state != PAUSED && state != CALLJAVA) throw new IllegalStateException("call() called in inappropriate state");
int oldState = state;
- CPUState saved = getCPUState();
- CPUState cpustate = new CPUState();
- cpustate.r[SP] = saved.r[SP]&~15;
+ CPUState saved = new CPUState();
+ getCPUState(saved);
+ CPUState cpustate = saved.dup();
+
+ cpustate.r[SP] = cpustate.r[SP]&~15;
cpustate.r[RA] = 0xdeadbeef;
cpustate.r[A0] = a0;
cpustate.r[A1] = a1;
cpustate.r[S1] = s1;
cpustate.r[S2] = s2;
cpustate.r[S3] = s3;
- cpustate.r[GP] = gp;
cpustate.pc = addr;
state = RUNNING;
setCPUState(cpustate);
__execute();
- cpustate = getCPUState();
+ getCPUState(cpustate);
setCPUState(saved);
if(state != PAUSED)
Returns -1 if the table is full. This can be used by subclasses to use custom file
descriptors */
public int addFD(FD fd) {
+ if(state == EXITED || state == EXECED) throw new IllegalStateException("addFD called in inappropriate state");
int i;
for(i=0;i<OPEN_MAX;i++) if(fds[i] == null) break;
if(i==OPEN_MAX) return -1;
fds[i] = fd;
+ closeOnExec[i] = false;
return i;
}
/** Closes file descriptor <i>fdn</i> and removes it from the file descriptor table */
public boolean closeFD(int fdn) {
+ if(state == EXITED || state == EXECED) throw new IllegalStateException("closeFD called in inappropriate state");
if(fdn < 0 || fdn >= OPEN_MAX) return false;
if(fds[fdn] == null) return false;
fds[fdn].close();
<i>incr</i> is how much to increase the break by */
public int sbrk(int incr) {
if(incr < 0) return -ENOMEM;
- if(incr==0) return brkAddr;
+ if(incr==0) return heapEnd;
incr = (incr+3)&~3;
- int oldBrk = brkAddr;
- int newBrk = oldBrk + incr;
- if(TOTAL_PAGES == 1) {
- CPUState state = getCPUState();
- if(newBrk >= state.r[SP] - 65536) {
- System.err.println("WARNING: brk too close to stack pointer");
- return -ENOMEM;
- }
- } else if(newBrk >= BRK_LIMIT) {
- System.err.println("WARNING: Hit BRK_LIMIT");
- return -ENOMEM;
- }
- if(TOTAL_PAGES != 1) {
+ int oldEnd = heapEnd;
+ int newEnd = oldEnd + incr;
+ if(newEnd >= stackBottom) return -ENOMEM;
+
+ if(writePages.length > 1) {
+ int pageMask = (1<<pageShift) - 1;
+ int pageWords = (1<<pageShift) >>> 2;
+ int start = (oldEnd + pageMask) >>> pageShift;
+ int end = (newEnd + pageMask) >>> pageShift;
try {
- for(int i=(oldBrk+PAGE_SIZE-1)>>>PAGE_SHIFT;i<((newBrk+PAGE_SIZE-1)>>>PAGE_SHIFT);i++)
- readPages[i] = writePages[i] = emptyPage();
+ for(int i=start;i<end;i++) readPages[i] = writePages[i] = new int[pageWords];
} catch(OutOfMemoryError e) {
System.err.println("WARNING: Caught OOM Exception in sbrk: " + e);
return -ENOMEM;
}
}
- brkAddr = newBrk;
- return oldBrk;
+ heapEnd = newEnd;
+ return oldEnd;
}
/** The getpid syscall */
return 0;
}
- private int sys_getpagesize() { return TOTAL_PAGES == 1 ? 4096 : PAGE_SIZE; }
+ private int sys_getpagesize() { return writePages.length == 1 ? 4096 : (1<<pageShift); }
private int sys_isatty(int fdn) {
if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
}
- /** Hook for subclasses to do something when the process exits (MUST set state = DONE) */
- protected void _exit() { state = DONE; }
+ /** Hook for subclasses to do something when the process exits (MUST set state = EXITED) */
+ protected void _exit() { state = EXITED; }
private int sys_exit(int status) {
exitStatus = status;
- for(int i=0;i<fds.length;i++) if(fds[i] != null) sys_close(i);
+ for(int i=0;i<fds.length;i++) if(fds[i] != null) closeFD(i);
_exit();
return 0;
}
private int sys_fcntl(int fdn, int cmd, int arg) {
+ // FEATURE: Pull these from fcntl.h
final int F_DUPFD = 0;
+ final int F_GETFD = 1;
+ final int F_SETFD = 2;
final int F_GETFL = 3;
int i;
if(fd.writable() && fd.readable()) flags = 2;
else if(fd.writable()) flags = 1;
return flags;
+ case F_SETFD:
+ closeOnExec[fdn] = arg != 0;
+ return 0;
+ case F_GETFD:
+ return closeOnExec[fdn] ? 1 : 0;
default:
System.err.println("WARNING: Unknown fcntl command: " + cmd);
return -ENOSYS;
public int seek(int n, int whence) throws IOException { return -1; }
/** Should return true if this is a tty */
+ // FEATURE: get rid of the isatty syscall and just do with newlib's dumb isatty.c
public boolean isatty() { return false; }
private FStat cachedFStat = null;
public int hi, lo;
public int fcsr;
public int pc;
+
+ public CPUState dup() {
+ CPUState c = new CPUState();
+ c.hi = hi;
+ c.lo = lo;
+ c.fcsr = fcsr;
+ c.pc = pc;
+ for(int i=0;i<32;i++) {
+ c.r[i] = r[i];
+ c.f[i] = f[i];
+ }
+ return c;
+ }
}
// Null pointer check helper function
protected final void nullPointerCheck(int addr) throws ExecutionException {
- if(TOTAL_PAGES==1 ? addr < 65536 : (addr>>>PAGE_SHIFT) < 16)
+ if(addr < 65536)
throw new ExecutionException("Attempted to dereference a null pointer " + toHex(addr));
}
// FEATURE: BusyBox's ASH doesn't like \r\n at the end of lines
// is ash just broken or are many apps like this? if so workaround in nestedvm
-public abstract class UnixRuntime extends Runtime {
+// FEATURE: Throw ErrnoException and catch in syscall whereever possible
+// (only in cases where we've already paid the price for a throw)
+
+public abstract class UnixRuntime extends Runtime implements Cloneable {
/** The pid of this "process" */
private int pid;
private int ppid;
private FS fs;
public FS getFS() { return fs; }
public void setFS(FS fs) {
- if(state >= RUNNING) throw new IllegalStateException("Can't change fs while process is running");
+ if(state != STOPPED) throw new IllegalStateException("Can't change fs while process is running");
this.fs = fs;
}
"" = root, "bin" = /bin "usr/bin" = /usr/bin */
private String cwd;
+ /** The runtime that should be run next when in state == EXECED */
+ private UnixRuntime execedRuntime;
+
/* Static stuff */
// FEATURE: Most of this is O(n) or worse - fix it
private final Object waitNotification = new Object();
}
}
- public UnixRuntime(int pageSize, int totalPages, boolean allowEmptyPages) {
- super(pageSize,totalPages,allowEmptyPages);
+ public UnixRuntime(int pageSize, int totalPages) {
+ super(pageSize,totalPages);
FS root = new HostFS();
FS dev = new DevFS();
if(ppid == 0) removeTask(this);
for(int i=0;i<MAX_TASKS;i++) {
if(tasks[i] != null && tasks[i].ppid == pid) {
- if(tasks[i].state == DONE) removeTask(tasks[i]);
+ if(tasks[i].state == EXITED) removeTask(tasks[i]);
else tasks[i].ppid = 0;
}
}
- state = DONE;
+ state = EXITED;
if(ppid != 0) synchronized(tasks[ppid].waitNotification) { tasks[ppid].waitNotification.notify(); }
}
}
case 23: // SIGIO
case 28: // SIGWINCH
break;
- default: {
- String msg = "Terminating on signal: " + signal + "\n";
- exitStatus = 1;
- state = DONE;
- if(fds[2]==null) {
- System.out.print(msg);
- } else {
- try {
- byte[] b = getBytes(msg);
- fds[2].write(b,0,b.length);
- }
- catch(IOException e) { /* ignore */ }
- }
- }
+ default:
+ return syscall(SYS_exit,128+signal,0,0,0);
}
return 0;
}
UnixRuntime task = null;
if(pid == -1) {
for(int i=0;i<MAX_TASKS;i++) {
- if(tasks[i] != null && tasks[i].ppid == this.pid && tasks[i].state == DONE) {
+ if(tasks[i] != null && tasks[i].ppid == this.pid && tasks[i].state == EXITED) {
task = tasks[i];
break;
}
}
- } else if(tasks[pid] != null && tasks[pid].ppid == this.pid && tasks[pid].state == DONE) {
+ } else if(tasks[pid] != null && tasks[pid].ppid == this.pid && tasks[pid].state == EXITED) {
task = tasks[pid];
}
}
}
- // FEATURE: Make this cleaner
- // Great ugliness lies within.....
+ protected Object clone() throws CloneNotSupportedException {
+ UnixRuntime r = (UnixRuntime) super.clone();
+ r.pid = r.ppid = 0;
+ return r;
+ }
+
private int sys_fork() {
- CPUState state = getCPUState();
+ CPUState state = new CPUState();
+ getCPUState(state);
int sp = state.r[SP];
final UnixRuntime r;
+
try {
- r = (UnixRuntime) getClass().newInstance();
+ r = (UnixRuntime) clone();
} catch(Exception e) {
- System.err.println(e);
+ e.printStackTrace();
return -ENOMEM;
}
- int child_pid = addTask(r);
- if(child_pid < 0) return -ENOMEM;
+
+ int childPID = addTask(r);
+ if(childPID < 0) return -ENOMEM;
r.ppid = pid;
- r.brkAddr = brkAddr;
- r.fds = new FD[OPEN_MAX];
- for(int i=0;i<OPEN_MAX;i++) if(fds[i] != null) r.fds[i] = fds[i].dup();
- r.cwd = cwd;
- r.fs = fs;
- for(int i=0;i<TOTAL_PAGES;i++) {
- if(readPages[i] == null) continue;
- if(isEmptyPage(writePages[i])) {
- r.readPages[i] = r.writePages[i] = writePages[i];
- } else if(writePages[i] != null) {
- r.readPages[i] = r.writePages[i] = new int[PAGE_WORDS];
- if(STACK_BOTTOM == 0 || i*PAGE_SIZE < STACK_BOTTOM || i*PAGE_SIZE >= sp-PAGE_SIZE*2)
- System.arraycopy(writePages[i],0,r.writePages[i],0,PAGE_WORDS);
- } else {
- r.readPages[i] = r.readPages[i];
- }
- }
- state.r[V0] = 0;
- state.pc += 4;
+
+ state.r[V0] = 0; // return 0 to child
+ state.pc += 4; // skip over syscall instruction
r.setCPUState(state);
r.state = PAUSED;
}
}.start();
- return child_pid;
+ return childPID;
+ }
+
+ public static int runAndExec(UnixRuntime r, String argv0, String[] rest) { return runAndExec(r,concatArgv(argv0,rest)); }
+ public static int runAndExec(UnixRuntime r, String[] argv) { r.start(argv); return executeAndExec(r); }
+
+ public static int executeAndExec(UnixRuntime r) {
+ for(;;) {
+ for(;;) {
+ if(r.execute()) break;
+ System.err.println("WARNING: Pause requested while executing runAndExec()");
+ }
+ if(r.state != EXECED) return r.exitStatus();
+ r = r.execedRuntime;
+ }
+ }
+
+ private String[] readStringArray(int addr) throws ReadFaultException {
+ int count = 0;
+ for(int p=addr;memRead(p) != 0;p+=4) count++;
+ String[] a = new String[count];
+ for(int i=0,p=addr;i<count;i++,p+=4) a[i] = cstring(memRead(p));
+ return a;
+ }
+
+ // FEATURE: call the syscall just "exec"
+ private int sys_execve(int cpath, int cargv, int cenvp) {
+ try {
+ return exec(normalizePath(cstring(cpath)),readStringArray(cargv),readStringArray(cenvp));
+ } catch(FaultException e) {
+ return -EFAULT;
+ }
}
+
+ private int exec(String path, String[] argv, String[] envp) {
+ final UnixRuntime r;
+
+ throw new Error("FIXME exec() not finished");
+ //Class klass = fs.getClass(path);
+ //if(klass != null) return exec(klass,argv,envp);
+
+ /*try {
+ Seekable s = fs.getSeekable(path);
+ boolean elf = false;
+ boolean script = false;
+ switch(s.read()) {
+ case '\177': elf = s.read() == 'E' && s.read() == 'L' && s.read() == 'F'; break;
+ case '#': script = s.read() == '!';
+ }
+ s.close();
+ if(script) {
+ // FIXME #! support
+ throw new Error("FIXME can't exec scripts yet");
+ // append args, etc
+ // return exec(...)
+ } else if(elf) {
+ klass = fs.(path);
+ if(klass == null) return -ENOEXEC;
+ return exec(klass,argv,envp);
+ } else {
+ return -ENOEXEC;
+ }
+ }*/
+ // FEATURE: Way too much overlap in the handling of ioexeptions everhwere
+ /*catch(ErrnoException e) { return -e.errno; }
+ catch(FileNotFoundException e) {
+ if(e.getMessage() != null && e.getMessage().indexOf("Permission denied") >= 0) return -EACCES;
+ return -ENOENT;
+ }
+ catch(IOException e) { return -EIO; }
+ catch(FaultException e) { return -EFAULT; }*/
+ }
+
+ private int exec(Class c, String[] argv, String[] envp) {
+ UnixRuntime r;
- private int sys_execve(int cstring, int argv, int envp) {
- /*
try {
- String path = cstring(cstring);
- FStat stat = fs.stat(path);
-
-
+ r = (UnixRuntime) c.newInstance();
+ } catch(Exception e) {
+ return -ENOMEM;
}
- catch(FaultException e) { return -EFAULT; }
- catch(FileNotFoundException e) { return -ENOENT; }
- catch(IOException e) { return -EIO; }*/
- throw new Error("FIXME - exec() isn't finished");
+
+ for(int i=0;i<OPEN_MAX;i++) if(closeOnExec[i]) closeFD(i);
+ r.fds = fds;
+ r.closeOnExec = closeOnExec;
+ // make sure this doesn't get messed with these since we didn't copy them
+ fds = null;
+ closeOnExec = null;
+
+ r.cwd = cwd;
+ r.fs = fs;
+ r.pid = pid;
+ r.ppid = ppid;
+ r.start(argv,envp);
+
+ state = EXECED;
+ execedRuntime = r;
+
+ return 0;
}
-
+
+ // FEATURE: Use custom PipeFD - be sure to support PIPE_BUF of data
private int sys_pipe(int addr) {
PipedOutputStream writerStream = new PipedOutputStream();
PipedInputStream readerStream;
}
public void chdir(String dir) throws FileNotFoundException {
- if(state >= RUNNING) throw new IllegalStateException("Can't chdir while process is running");
+ if(state != STOPPED) throw new IllegalStateException("Can't chdir while process is running");
try {
dir = normalizePath(dir);
if(fs.stat(dir).type() != FStat.S_IFDIR) throw new FileNotFoundException();
public final FD open(String path, int flags, int mode) throws IOException { return (FD) op(OPEN,path,flags,mode); }
public final FStat stat(String path) throws IOException { return (FStat) op(STAT,path,0,0); }
public final void mkdir(String path) throws IOException { op(MKDIR,path,0,0); }
+
- // FIXME: inode stuff
+ // FEATURE: inode stuff
+ // FEATURE: Implement whatever is needed to get newlib's opendir and friends to work - that patch is a pain
protected static FD directoryFD(String[] files, int hashCode) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
continue;
}
inp++;
+ out[outp++] = '/';
out[outp++] = '.';
}
if(outp > 0 && out[outp-1] == '/') outp--;
return new String(out,0,outp);
}
- // FIXME: This is probably still buggy
- // FEATURE: Remove some of the "should never happen checks"
- /*protected static String cleanupPath(String p) throws ErrnoException {
- if(p.length() == 0) throw new ErrnoException(ENOENT);
- if(needsCleanup(p)) {
- char[] in = p.toCharArray();
- char[] out;
- int outp ;
- if(in[0] == '/') {
- out = new char[in.length];
- outp = 0;
- } else {
- out = new char[cwd.length() + in.length + 1];
- outp = cwd.length();
- for(int i=0;i<outp;i++) out[i] = cwd.charAt(i);
- if(outp == 0 || out[0] != '/') throw new Error("should never happen");
- }
- int inLength = in.length;
- int inp = 0;
- while(inp<inLength) {
- if(inp == 0 || in[inp] == '/') {
- while(inp < inLength && in[inp] == '/') inp++;
- if(inp == inLength) break;
- if(in[inp] == '.') {
- if(inp+1 == inLength) break;
- if(in[inp+1] == '.' && (inp+2 == inLength || in[inp+2] == '/')) {
- inp+=2;
- if(outp == 0) continue;
- do { outp--; } while(outp > 0 && out[outp] != '/');
- } else if(in[inp+1] == '/') {
- inp++;
- } else {
- out[outp++] = '/';
- }
- } else {
- out[outp++] = '/';
- out[outp++] = in[inp++];
- }
- } else {
- out[outp++] = in[inp++];
- }
- }
- if(outp == 0) out[outp++] = '/';
- return new String(out,0,outp);
- } else {
- if(p.startsWith("/")) return p;
- StringBuffer sb = new StringBuffer(cwd);
- if(!cwd.equals("/")) sb.append('/');
- return sb.append(p).toString();
- }
- }*/
-
public static class MountPointFS extends FS {
private static class MP {
public MP(String path, FS fs) { this.path = path; this.fs = fs; }
if(path.length() == 0) throw new IllegalArgumentException("Zero length mount point path");
return path;
}
- public FS get(String path) {
+ public synchronized FS get(String path) {
path = fixup(path);
int f = path.charAt(0) & 0x7f;
for(int i=0;mps[f] != null && i < mps[f].length;i++)
return null;
}
- public void add(String path, FS fs) {
+ public synchronized void add(String path, FS fs) {
if(get(path) != null) throw new IllegalArgumentException("mount point already exists");
path = fixup(path);
int f = path.charAt(0) & 0x7f;
mps[f] = newList;
}
- public void remove(String path) {
+ public synchronized void remove(String path) {
path = fixup(path);
if(get(path) == null) throw new IllegalArgumentException("mount point doesn't exist");
int f = path.charAt(0) & 0x7f;
return root.op(op,path,arg1,arg2);
}
}
-
- // FEATURE: Probably should make this more general - support mountpoints, etc
- /*public class UnixOverlayFS extends FS {
- private final FS root;
- private final FS dev = new DevFS();
- public UnixOverlayFS(FS root) {
- this.root = root;
- }
- private String devPath(String path) {
- if(path.startsWith("/dev")) {
- if(path.length() == 4) return "/";
- if(path.charAt(4) == '/') return path.substring(4);
- }
- return null;
- }
- public FD open(String path, int flags, int mode) throws IOException{
- String dp = devPath(path);
- return dp == null ? root.open(path,flags,mode) : dev.open(dp,flags,mode);
- }
- public FStat stat(String path) throws IOException {
- String dp = devPath(path);
- return dp == null ? root.stat(path) : dev.stat(dp);
- }
- public void mkdir(String path) throws IOException {
- String dp = devPath(path);
- if(dp == null) root.mkdir(path);
- else dev.mkdir(dp);
- }
- }*/
-
+
public static class HostFS extends FS {
protected File root;
public File getRoot() { return root; }
return f;
}
- private File hostFile(String path) {
- if(File.separatorChar != '/') path = path.replace('/',File.separatorChar);
+ File hostFile(String path) {
+ char sep = File.separatorChar;
+ if(sep != '/') {
+ char buf[] = path.toCharArray();
+ for(int i=0;i<buf.length;i++) {
+ char c = buf[i];
+ if(c == '/') buf[i] = sep;
+ else if(c == sep) buf[i] = '/';
+ }
+ path = new String(buf);
+ }
return new File(root,path);
}
public FStat _fstat() { return new DevFStat(); }
};
- public class DevFS extends FS {
+ // FIXME: Support /dev/fd (need to have syscalls pass along Runtime instance)
+ public static class DevFS extends FS {
public FD _open(String path, int mode, int flags) throws IOException {
if(path.equals("null")) return devNullFD;
if(path.equals("zero")) return devZeroFD;
- if(path.startsWith("fd/")) {
+ /*if(path.startsWith("fd/")) {
int n;
try {
n = Integer.parseInt(path.substring(4));
count = 0;
for(int i=0;i<OPEN_MAX;i++) if(fds[i] != null) files[count++] = Integer.toString(i);
return directoryFD(files,hashCode());
- }
+ }*/
if(path.equals("")) {
String[] files = { "null", "zero", "fd" };
return directoryFD(files,hashCode());
public FStat _stat(String path) throws IOException {
if(path.equals("null")) return devNullFD.fstat();
if(path.equals("zero")) return devZeroFD.fstat();
- if(path.startsWith("fd/")) {
+ /*if(path.startsWith("fd/")) {
int n;
try {
n = Integer.parseInt(path.substring(4));
return fds[n].fstat();
}
if(path.equals("fd")) return new FStat() { public int type() { return S_IFDIR; } public int mode() { return 0444; }};
+ */
if(path.equals("")) return new FStat() { public int type() { return S_IFDIR; } public int mode() { return 0444; }};
throw new FileNotFoundException();
}
public static final int _PC_POSIX_PERMISSIONS = 90;
public static final int _PC_POSIX_SECURITY = 91;
public static final int MAXPATHLEN = 1024;
+ public static final int ARG_MAX = 65536; /* max bytes for an exec function */
+ public static final int CHILD_MAX = 40; /* max simultaneous processes */
+ public static final int LINK_MAX = 32767; /* max file link count */
+ public static final int MAX_CANON = 255; /* max bytes in term canon input line */
+ public static final int MAX_INPUT = 255; /* max bytes in terminal input */
+ public static final int NAME_MAX = 255; /* max bytes in a file name */
+ public static final int NGROUPS_MAX = 16; /* max supplemental group id's */
+ public static final int OPEN_MAX = 64; /* max open files per process */
+ public static final int PATH_MAX = 1024; /* max bytes in pathname */
+ public static final int PIPE_BUF = 512; /* max bytes for atomic pipe writes */
+ public static final int IOV_MAX = 1024; /* max elements in i/o vector */
+ public static final int BC_BASE_MAX = 99; /* max ibase/obase values in bc(1) */
+ public static final int BC_DIM_MAX = 2048; /* max array elements in bc(1) */
+ public static final int BC_SCALE_MAX = 99; /* max scale value in bc(1) */
+ public static final int BC_STRING_MAX = 1000; /* max const string length in bc(1) */
+ public static final int COLL_WEIGHTS_MAX = 0; /* max weights for order keyword */
+ public static final int EXPR_NEST_MAX = 32; /* max expressions nested in expr(1) */
+ public static final int LINE_MAX = 2048; /* max bytes in an input line */
+ public static final int RE_DUP_MAX = 255; /* max RE's in interval notation */
}
import java.io.*;
-public interface Seekable {
- public int read(byte[] buf, int offset, int length) throws IOException;
- public int write(byte[] buf, int offset, int length) throws IOException;
- public int length() throws IOException;
- public void seek(int pos) throws IOException;
- public void close() throws IOException;
- public int pos() throws IOException;
+public abstract class Seekable {
+ public abstract int read(byte[] buf, int offset, int length) throws IOException;
+ public abstract int write(byte[] buf, int offset, int length) throws IOException;
+ public abstract int length() throws IOException;
+ public abstract void seek(int pos) throws IOException;
+ public abstract void close() throws IOException;
+ public abstract int pos() throws IOException;
- public class ByteArray implements Seekable {
+ public int read() throws IOException {
+ byte[] buf = new byte[1];
+ int n = read(buf,0,1);
+ return n == -1 ? -1 : buf[0]&0xff;
+ }
+
+ public static class ByteArray extends Seekable {
protected byte[] data;
protected int pos;
private final boolean writable;
pos += len;
return len;
}
-
+
public int write(byte[] buf, int off, int len) throws IOException {
if(!writable) throw new IOException("read-only data");
len = Math.min(len,data.length-pos);
public void close() { /*noop*/ }
}
- public class File implements Seekable {
+ public static class File extends Seekable {
private final RandomAccessFile raf;
public File(String fileName) throws IOException { this(fileName,false); }
public void close() throws IOException { raf.close(); }
}
- public class InputStream implements Seekable {
+ public static class InputStream extends Seekable {
private byte[] buffer = new byte[4096];
private int bytesRead = 0;
private boolean eof = false;
printf("Tyring to mkdir .mkdirtest\n");
if(mkdir(".mkdirtest",0700) < 0) perror("mkdir");
- printf("Trying to opendir /\n");
- dir = opendir("/");
+ printf("Trying to opendir .\n");
+ dir = opendir(".");
if(dir) {
while((dent=readdir(dir))!=NULL)
printf("\t[%s] %lu\n",dent->d_name,dent->d_ino);