X-Git-Url: http://git.megacz.com/?p=nestedvm.git;a=blobdiff_plain;f=src%2Forg%2Fibex%2Fnestedvm%2FUnixRuntime.java;h=e3afe98e3ae4e319fe310dfc36937603e3ced802;hp=3befb91aeaf744fe096c22a9630b73c0d631af36;hb=119cb20e2b9d594032653c8464717b1420b6abc7;hpb=c2b2704764af1ade923ba8f15d517b87f9d16189 diff --git a/src/org/ibex/nestedvm/UnixRuntime.java b/src/org/ibex/nestedvm/UnixRuntime.java index 3befb91..e3afe98 100644 --- a/src/org/ibex/nestedvm/UnixRuntime.java +++ b/src/org/ibex/nestedvm/UnixRuntime.java @@ -4,60 +4,40 @@ import org.ibex.nestedvm.util.*; import java.io.*; import java.util.*; -public abstract class UnixRuntime extends Runtime { +// FEATURE: vfork + +public abstract class UnixRuntime extends Runtime implements Cloneable { /** The pid of this "process" */ private int pid; - private int ppid; - protected int getPid() { return pid; } + private UnixRuntime parent; + public final int getPid() { return pid; } - /** processes filesystem */ - 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"); - this.fs = fs; + private static final GlobalState defaultGS = new GlobalState(); + private GlobalState gs = defaultGS; + public void setGlobalState(GlobalState gs) { + if(state != STOPPED) throw new IllegalStateException("can't change GlobalState when running"); + this.gs = gs; } - /** proceses current working directory */ + /** proceses' current working directory - absolute path WITHOUT leading slash + "" = root, "bin" = /bin "usr/bin" = /usr/bin */ private String cwd; - /* Static stuff */ - // FEATURE: Most of this is O(n) or worse - fix it - private final Object waitNotification = new Object(); - private final static int MAX_TASKS = 256; - private final static UnixRuntime[] tasks = new UnixRuntime[MAX_TASKS]; - private static int addTask(UnixRuntime rt) { - synchronized(tasks) { - for(int i=1;i= MAX_TASKS)) return -ECHILD; - for(;;) { - synchronized(tasks) { - UnixRuntime task = null; + if(pid == 0 || pid < -1) { + if(STDERR_DIAG) System.err.println("WARNING: waitpid called with a pid of " + pid); + return -ECHILD; + } + boolean blocking = (options&WNOHANG)==0; + + if(pid !=-1 && (pid <= 0 || pid >= gs.tasks.length)) return -ECHILD; + if(children == null) return blocking ? -ECHILD : 0; + + UnixRuntime done = null; + + synchronized(children) { + for(;;) { if(pid == -1) { - for(int i=0;i 0) done = (UnixRuntime)exitedChildren.remove(exitedChildren.size() - 1); + } else if(pid > 0) { + UnixRuntime t = gs.tasks[pid]; + if(t.parent != this) return -ECHILD; + if(t.state == EXITED) { + if(!exitedChildren.remove(t)) throw new Error("should never happen"); + done = t; } - } else if(tasks[pid] != null && tasks[pid].ppid == this.pid && tasks[pid].state == DONE) { - task = tasks[pid]; + } else { + // process group stuff, EINVAL returned above + throw new Error("should never happen"); } - - if(task != null) { - removeTask(task); - try { - if(statusAddr!=0) memWrite(statusAddr,task.exitStatus()<<8); - } catch(FaultException e) { - return -EFAULT; - } - - return task.pid; + if(done == null) { + if(!blocking) return 0; + try { children.wait(); } catch(InterruptedException e) {} + //System.err.println("waitpid woke up: " + exitedChildren.size()); + } else { + gs.tasks[done.pid] = null; + break; } } - if((options&WNOHANG)!=0) return 0; - synchronized(waitNotification) { - try { waitNotification.wait(); } catch(InterruptedException e) { /* ignore */ } + } + if(statusAddr!=0) memWrite(statusAddr,done.exitStatus()<<8); + return done.pid; + } + + + void _exited() { + if(children != null) synchronized(children) { + for(Enumeration e = exitedChildren.elements(); e.hasMoreElements(); ) { + UnixRuntime child = (UnixRuntime) e.nextElement(); + gs.tasks[child.pid] = null; + } + exitedChildren.clear(); + for(Enumeration e = activeChildren.elements(); e.hasMoreElements(); ) { + UnixRuntime child = (UnixRuntime) e.nextElement(); + child.parent = null; + } + activeChildren.clear(); + } + + UnixRuntime _parent = parent; + if(_parent == null) { + gs.tasks[pid] = null; + } else { + synchronized(_parent.children) { + if(parent == null) { + gs.tasks[pid] = null; + } else { + parent.activeChildren.remove(this); + parent.exitedChildren.add(this); + parent.children.notify(); + } } } } - // Great ugliness lies within..... + protected Object clone() throws CloneNotSupportedException { + UnixRuntime r = (UnixRuntime) super.clone(); + r.pid = 0; + r.parent = null; + r.children = null; + r.activeChildren = r.exitedChildren = null; + 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; - - r.ppid = pid; - r.brkAddr = brkAddr; - r.fds = new FD[OPEN_MAX]; - for(int i=0;i= sp-PAGE_SIZE*2) - System.arraycopy(writePages[i],0,r.writePages[i],0,PAGE_WORDS); - } else { - r.readPages[i] = r.readPages[i]; - } + + r.parent = this; + + try { + r._started(); + } catch(ProcessTableFullExn e) { + return -ENOMEM; } - state.r[V0] = 0; - state.pc += 4; + + //System.err.println("fork " + pid + " -> " + r.pid + " tasks[" + r.pid + "] = " + gd.tasks[r.pid]); + if(children == null) { + children = new Object(); + activeChildren = new Vector(); + exitedChildren = new Vector(); + } + activeChildren.add(r); + + state.r[V0] = 0; // return 0 to child + state.pc += 4; // skip over syscall instruction r.setCPUState(state); r.state = PAUSED; - new Thread() { - public void run() { - try { - while(!r.execute()); - } catch(Exception e) { - System.err.println("Forked process threw exception: "); - e.printStackTrace(); + new ForkedProcess(r); + + return r.pid; + } + + public static final class ForkedProcess extends Thread { + private final UnixRuntime initial; + public ForkedProcess(UnixRuntime initial) { this.initial = initial; start(); } + public void run() { UnixRuntime.executeAndExec(initial); } + } + + 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; + if(STDERR_DIAG) 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= 0) return -EACCES; - return -ENOENT; - } - catch(IOException e) { return -EIO; } - catch(FaultException e) { return -EFAULT; } + private int sys_dup(int oldd) { + if(oldd < 0 || oldd >= OPEN_MAX) return -EBADFD; + if(fds[oldd] == null) return -EBADFD; + FD fd = fds[oldd].dup(); + int newd = addFD(fd); + if(newd < 0) { fd.close(); return -ENFILE; } + return newd; } + private int sys_stat(int cstring, int addr) throws FaultException, ErrnoException { + FStat s = gs.stat(this,normalizePath(cstring(cstring))); + if(s == null) return -ENOENT; + return stat(s,addr); + } - private int sys_mkdir(int cstring, int mode) { - try { - fs.mkdir(cleanupPath(cstring(cstring))); - return 0; - } - catch(ErrnoException e) { return -e.errno; } - catch(FileNotFoundException e) { return -ENOENT; } - catch(IOException e) { return -EIO; } - catch(FaultException e) { return -EFAULT; } + private int sys_lstat(int cstring, int addr) throws FaultException, ErrnoException { + FStat s = gs.lstat(this,normalizePath(cstring(cstring))); + if(s == null) return -ENOENT; + return stat(s,addr); + } + + private int sys_mkdir(int cstring, int mode) throws FaultException, ErrnoException { + gs.mkdir(this,normalizePath(cstring(cstring)),mode); + return 0; } + private int sys_unlink(int cstring) throws FaultException, ErrnoException { + gs.unlink(this,normalizePath(cstring(cstring))); + return 0; + } - private int sys_getcwd(int addr, int size) { + private int sys_getcwd(int addr, int size) throws FaultException, ErrnoException { byte[] b = getBytes(cwd); if(size == 0) return -EINVAL; - if(size < b.length+1) return -ERANGE; - if(!new File(cwd).exists()) return -ENOENT; - try { - copyout(b,addr,b.length); - memset(addr+b.length+1,0,1); - return addr; - } catch(FaultException e) { - return -EFAULT; - } + if(size < b.length+2) return -ERANGE; + memset(addr,'/',1); + copyout(b,addr+1,b.length); + memset(addr+b.length+1,0,1); + return addr; } - private int sys_chdir(int addr) { - try { - String path = cleanupPath(cstring(addr)); - System.err.println("Chdir: " + cstring(addr) + " -> " + path + " pwd: " + cwd); - if(fs.stat(path).type() != FStat.S_IFDIR) return -ENOTDIR; - cwd = path; - System.err.println("Now: " + cwd); - return 0; - } - catch(ErrnoException e) { return -e.errno; } - catch(FileNotFoundException e) { return -ENOENT; } - catch(IOException e) { return -EIO; } - catch(FaultException e) { return -EFAULT; } + private int sys_chdir(int addr) throws ErrnoException, FaultException { + String path = normalizePath(cstring(addr)); + //System.err.println("Chdir: " + cstring(addr) + " -> " + path + " pwd: " + cwd); + if(gs.stat(this,path).type() != FStat.S_IFDIR) return -ENOTDIR; + cwd = path; + //System.err.println("Now: [" + cwd + "]"); + return 0; } - - public void chdir(String dir) throws FileNotFoundException { - if(state >= RUNNING) throw new IllegalStateException("Can't chdir while process is running"); - try { - dir = cleanupPath(dir); - if(fs.stat(dir).type() != FStat.S_IFDIR) throw new FileNotFoundException(); - } catch(IOException e) { - throw new FileNotFoundException(); - } - cwd = dir; + + private int sys_getdents(int fdn, int addr, int count, int seekptr) throws FaultException, ErrnoException { + count = Math.min(count,MAX_CHUNK); + if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD; + if(fds[fdn] == null) return -EBADFD; + byte[] buf = byteBuf(count); + int n = fds[fdn].getdents(buf,0,count); + copyout(buf,addr,n); + return n; } + + // FEATURE: Run through the fork/wait stuff one more time + public static class GlobalState { + protected static final int OPEN = 1; + protected static final int STAT = 2; + protected static final int LSTAT = 3; + protected static final int MKDIR = 4; + protected static final int UNLINK = 5; - public abstract static class FS { - public FD open(String path, int flags, int mode) throws IOException { throw new FileNotFoundException(); } - public FStat stat(String path) throws IOException { throw new FileNotFoundException(); } - public void mkdir(String path) throws IOException { throw new ErrnoException(ENOTDIR); } - - public static FD directoryFD(String[] files, int hashCode) throws IOException { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(bos); - for(int i=0;i 0 && out[outp] != '/'); - } else if(in[inp+1] == '/') { - inp++; + } + + private void removeMount(int index) { + if(index == -1) { root.owner = null; root = null; return; } + MP[] newMPS = new MP[mps.length - 1]; + System.arraycopy(mps,0,newMPS,0,index); + System.arraycopy(mps,0,newMPS,index,mps.length-index-1); + mps = newMPS; + } + + private Object fsop(int op, UnixRuntime r, String normalizedPath, int arg1, int arg2) throws ErrnoException { + int pl = normalizedPath.length(); + if(pl != 0) { + MP[] list; + synchronized(this) { list = mps; } + for(int i=0;i 0) outp--; + while(outp > 0 && out[outp] != '/') outp--; + //System.err.println("After ..: " + new String(out,0,outp)); + continue; + } + inp++; + out[outp++] = '/'; + out[outp++] = '.'; } + if(outp > 0 && out[outp-1] == '/') outp--; + //System.err.println("normalize: " + path + " -> " + new String(out,0,outp) + " (cwd: " + cwd + ")"); + return new String(out,0,outp); + } + + FStat hostFStat(final File f, Object data) { + boolean e = false; + try { + FileInputStream fis = new FileInputStream(f); + switch(fis.read()) { + case '\177': e = fis.read() == 'E' && fis.read() == 'L' && fis.read() == 'F'; break; + case '#': e = fis.read() == '!'; + } + fis.close(); + } catch(IOException e2) { } + HostFS fs = (HostFS) data; + final int inode = fs.inodes.get(f.getAbsolutePath()); + final int devno = fs.devno; + return new HostFStat(f,e) { + public int inode() { return inode; } + public int dev() { return devno; } + }; + } + + FD hostFSDirFD(File f, Object _fs) { + HostFS fs = (HostFS) _fs; + return fs.new HostDirFD(f); } - // FIXME: This is totally broken on non-unix hosts - need to do some kind of cygwin type mapping public static class HostFS extends FS { - public static String fixPath(String path) throws FileNotFoundException { - return path; - } + InodeCache inodes = new InodeCache(4000); + protected File root; + public File getRoot() { return root; } - public String hostCWD() { - return getSystemProperty("user.dir"); + private static File hostRootDir() { + if(getSystemProperty("nestedvm.root") != null) { + File f = new File(getSystemProperty("nestedvm.root")); + if(f.isDirectory()) return f; + // fall through to case below + } + String cwd = getSystemProperty("user.dir"); + File f = new File(cwd != null ? cwd : "."); + if(!f.exists()) throw new Error("Couldn't get File for cwd"); + f = new File(f.getAbsolutePath()); + while(f.getParent() != null) f = new File(f.getParent()); + return f; } - // FEATURE: This shares a lot with Runtime.open - public FD open(String path, int flags, int mode) throws IOException { - path = fixPath(path); - final File f = new File(path); - // NOTE: createNewFile is a Java2 function - if((flags & (O_EXCL|O_CREAT)) == (O_EXCL|O_CREAT)) - if(!f.createNewFile()) throw new ErrnoException(EEXIST); - if(!f.exists() && (flags&O_CREAT) == 0) return null; - if(f.isDirectory()) { - if((flags&3)!=RD_ONLY) throw new ErrnoException(EACCES); - return directoryFD(f.list(),path.hashCode()); + private File hostFile(String path) { + char sep = File.separatorChar; + if(sep != '/') { + char buf[] = path.toCharArray(); + for(int i=0;i>>24)&0xff); + buf[off+1] = (byte)((n>>>16)&0xff); + buf[off+2] = (byte)((n>>> 8)&0xff); + buf[off+3] = (byte)((n>>> 0)&0xff); } - private static class DevFStat extends FStat { - public int dev() { return 1; } - public int mode() { return 0666; } - public int type() { return S_IFCHR; } - public int nlink() { return 1; } - } - private static FD devZeroFD = new FD() { - public boolean readable() { return true; } - public boolean writable() { return true; } - public int read(byte[] a, int off, int length) { Arrays.fill(a,off,off+length,(byte)0); return length; } - public int write(byte[] a, int off, int length) { return length; } - public int seek(int n, int whence) { return 0; } - public FStat _fstat() { return new DevFStat(); } - }; - private static FD devNullFD = new FD() { - public boolean readable() { return true; } - public boolean writable() { return true; } - public int read(byte[] a, int off, int length) { return 0; } - public int write(byte[] a, int off, int length) { return length; } - public int seek(int n, int whence) { return 0; } - public FStat _fstat() { return new DevFStat(); } - }; - - public 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/")) { + public static abstract class DirFD extends FD { + private int pos = -2; + + protected abstract int size(); + protected abstract String name(int n); + protected abstract int inode(int n); + protected abstract int myDev(); + protected int parentInode() { return -1; } + protected int myInode() { return -1; } + + public int getdents(byte[] buf, int off, int len) { + int ooff = off; + int ino; + int reclen; + OUTER: for(;len > 0 && pos < size();pos++){ + switch(pos) { + case -2: + case -1: + ino = pos == -1 ? parentInode() : myInode(); + if(ino == -1) continue; + reclen = 9 + (pos == -1 ? 2 : 1); + if(reclen > len) break OUTER; + buf[off+8] = '.'; + if(pos == -1) buf[off+9] = '.'; + break; + default: { + String f = name(pos); + byte[] fb = getBytes(f); + reclen = fb.length + 9; + if(reclen > len) break OUTER; + ino = inode(pos); + System.arraycopy(fb,0,buf,off+8,fb.length); + } + } + buf[off+reclen-1] = 0; // null terminate + reclen = (reclen + 3) & ~3; // add padding + putInt(buf,off,reclen); + putInt(buf,off+4,ino); + off += reclen; + len -= reclen; + } + return off-ooff; + } + + protected FStat _fstat() { + return new FStat() { + public int type() { return S_IFDIR; } + public int inode() { return myInode(); } + public int dev() { return myDev(); } + }; + } + } + + public static class DevFS extends FS { + private static final int ROOT_INODE = 1; + private static final int NULL_INODE = 2; + private static final int ZERO_INODE = 3; + private static final int FD_INODE = 4; + private static final int FD_INODES = 32; + + private class DevFStat extends FStat { + public int dev() { return devno; } + public int mode() { return 0666; } + public int type() { return S_IFCHR; } + public int nlink() { return 1; } + } + + private abstract class DevDirFD extends DirFD { + public int myDev() { return devno; } + } + + private FD devZeroFD = new FD() { + public boolean readable() { return true; } + public boolean writable() { return true; } + public int read(byte[] a, int off, int length) { Arrays.fill(a,off,off+length,(byte)0); return length; } + public int write(byte[] a, int off, int length) { return length; } + public int seek(int n, int whence) { return 0; } + public FStat _fstat() { return new DevFStat(){ public int inode() { return ZERO_INODE; } }; } + }; + private FD devNullFD = new FD() { + public boolean readable() { return true; } + public boolean writable() { return true; } + public int read(byte[] a, int off, int length) { return 0; } + public int write(byte[] a, int off, int length) { return length; } + public int seek(int n, int whence) { return 0; } + public FStat _fstat() { return new DevFStat(){ public int inode() { return NULL_INODE; } }; } + }; + + public FD open(UnixRuntime r, String path, int mode, int flags) throws ErrnoException { + if(path.equals("null")) return devNullFD; + if(path.equals("zero")) return devZeroFD; + if(path.startsWith("fd/")) { int n; try { n = Integer.parseInt(path.substring(4)); } catch(NumberFormatException e) { - throw new FileNotFoundException(); + return null; } - if(n < 0 || n >= OPEN_MAX) throw new FileNotFoundException(); - if(fds[n] == null) throw new FileNotFoundException(); - return fds[n].dup(); + if(n < 0 || n >= OPEN_MAX) return null; + if(r.fds[n] == null) return null; + return r.fds[n].dup(); } - if(path.equals("/fd")) { + if(path.equals("fd")) { int count=0; - for(int i=0;i= OPEN_MAX) throw new FileNotFoundException(); - if(fds[n] == null) throw new FileNotFoundException(); - return fds[n].fstat(); + if(n < 0 || n >= OPEN_MAX) return null; + if(r.fds[n] == null) return null; + return r.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(); + // FEATURE: inode stuff + 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; }}; + return null; } - public void mkdir(String path) throws IOException { throw new ErrnoException(EACCES); } - } + public void mkdir(UnixRuntime r, String path, int mode) throws ErrnoException { throw new ErrnoException(EROFS); } + public void unlink(UnixRuntime r, String path) throws ErrnoException { throw new ErrnoException(EROFS); } + } }