X-Git-Url: http://git.megacz.com/?p=nestedvm.git;a=blobdiff_plain;f=src%2Forg%2Fibex%2Fnestedvm%2FRuntime.java;h=2a91e3613ebda8733909fef05888a0aa83b9ce50;hp=b7903741ceed0e707314af31e5d257f22bbbea9b;hb=b11e7c6c29f2b5f7b0828bf93eb741c4a30ec411;hpb=897fa3c89674460aa4fad275f263cea2b341420d diff --git a/src/org/ibex/nestedvm/Runtime.java b/src/org/ibex/nestedvm/Runtime.java index b790374..2a91e36 100644 --- a/src/org/ibex/nestedvm/Runtime.java +++ b/src/org/ibex/nestedvm/Runtime.java @@ -1,3 +1,7 @@ +// Copyright 2000-2005 the Contributors, as shown in the revision logs. +// Licensed under the Apache License 2.0 ("the License"). +// You may not use this file except in compliance with the License. + // Copyright 2003 Brian Alliet // Based on org.xwt.imp.MIPS by Adam Megacz // Portions Copyright 2003 Adam Megacz @@ -6,9 +10,10 @@ package org.ibex.nestedvm; import org.ibex.nestedvm.util.*; import java.io.*; -import java.util.Arrays; public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { + public static final String VERSION = "1.0"; + /** True to write useful diagnostic information to stderr when things go wrong */ final static boolean STDERR_DIAG = true; @@ -45,10 +50,10 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { /** When the process started */ private long startTime; - /** Text/Data loaded in memory */ - public final static int STOPPED = 0; /** Program is executing instructions */ - public final static int RUNNING = 1; + public final static int RUNNING = 0; // Horrible things will happen if this isn't 0 + /** Text/Data loaded in memory */ + public final static int STOPPED = 1; /** Prgram has been started but is paused */ public final static int PAUSED = 2; /** Program is executing a callJava() method */ @@ -69,8 +74,8 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { public ExecutionException exitException; /** Table containing all open file descriptors. (Entries are null if the fd is not in use */ - FD[] fds = new FD[OPEN_MAX]; // package-private for UnixRuntime - boolean closeOnExec[] = new boolean[OPEN_MAX]; + FD[] fds; // package-private for UnixRuntime + boolean closeOnExec[]; /** Pointer to a SecurityManager for this process */ SecurityManager sm; @@ -93,7 +98,7 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { /** Subclasses should return the address of the symbol symbol or -1 it it doesn't exits in this method This method is only required if the call() function is used */ - protected int lookupSymbol(String symbol) { return -1; } + public int lookupSymbol(String symbol) { return -1; } /** Subclasses should populate a CPUState object representing the cpu state */ protected abstract void getCPUState(CPUState state); @@ -101,6 +106,16 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { /** Subclasses should set the CPUState to the state held in state */ protected abstract void setCPUState(CPUState state); + /** True to enabled a few hacks to better support the win32 console */ + final static boolean win32Hacks; + + static { + String os = Platform.getProperty("os.name"); + String prop = Platform.getProperty("nestedvm.win32hacks"); + if(prop != null) { win32Hacks = Boolean.valueOf(prop).booleanValue(); } + else { win32Hacks = os != null && os.toLowerCase().indexOf("windows") != -1; } + } + protected Object clone() throws CloneNotSupportedException { Runtime r = (Runtime) super.clone(); r._byteBuf = null; @@ -118,7 +133,8 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { return r; } - protected Runtime(int pageSize, int totalPages) { + protected Runtime(int pageSize, int totalPages) { this(pageSize, totalPages,false); } + protected Runtime(int pageSize, int totalPages, boolean exec) { 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"); @@ -135,7 +151,7 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { stackSize = max(stackSize,pageSize); stackSize = (stackSize + pageSize - 1) & ~(pageSize-1); stackPages = stackSize >>> pageShift; - heapStart = (heapStart + pageSize) & ~(pageSize-1); + heapStart = (heapStart + pageSize - 1) & ~(pageSize-1); if(stackPages + STACK_GUARD_PAGES + (heapStart >>> pageShift) >= totalPages) throw new IllegalArgumentException("total pages too small"); } else { @@ -156,10 +172,16 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { readPages[i] = writePages[i] = new int[pageSize>>2]; } } - - addFD(new StdinFD(System.in)); - addFD(new StdoutFD(System.out)); - addFD(new StdoutFD(System.err)); + + if(!exec) { + fds = new FD[OPEN_MAX]; + closeOnExec = new boolean[OPEN_MAX]; + + InputStream stdin = win32Hacks ? new Win32ConsoleIS(System.in) : System.in; + addFD(new TerminalFD(stdin)); + addFD(new TerminalFD(System.out)); + addFD(new TerminalFD(System.err)); + } } /** Copy everything from src to addr initializing uninitialized pages if required. @@ -356,7 +378,8 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { if(page == null) throw new WriteFaultException(a<<2); int index = a&pageWordMask; int n = min(c,pageWords-index); - Arrays.fill(page,index,index+n,fourBytes); + /* Arrays.fill(page,index,index+n,fourBytes);*/ + for(int i=index;i ARG_MAX) throw new IllegalArgumentException("args/environ too big"); + // HACK: heapStart() isn't always available when the constructor + // is run and this sometimes doesn't get initialized + if(heapEnd == 0) { + heapEnd = heapStart(); + if(heapEnd == 0) throw new Error("heapEnd == 0"); + int pageSize = writePages.length == 1 ? 4096 : (1<fdn and removes it from the file descriptor table */ public final 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; + _preCloseFD(fds[fdn]); fds[fdn].close(); + _postCloseFD(fds[fdn]); fds[fdn] = null; return true; } @@ -693,10 +734,13 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { public static final int O_APPEND = 0x0008; public static final int O_TRUNC = 0x0400; public static final int O_NONBLOCK = 0x4000; + public static final int O_NOCTTY = 0x8000; + FD hostFSOpen(final File f, int flags, int mode, final Object data) throws ErrnoException { if((flags & ~(3|O_CREAT|O_EXCL|O_APPEND|O_TRUNC)) != 0) { - if(STDERR_DIAG) System.err.println("WARNING: Unsupported flags passed to open(): " + toHex(flags & ~(3|O_CREAT|O_EXCL|O_APPEND|O_TRUNC))); + if(STDERR_DIAG) + System.err.println("WARNING: Unsupported flags passed to open(\"" + f + "\"): " + toHex(flags & ~(3|O_CREAT|O_EXCL|O_APPEND|O_TRUNC))); throw new ErrnoException(ENOTSUP); } boolean write = (flags&3) != RD_ONLY; @@ -705,8 +749,7 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { if((flags & (O_EXCL|O_CREAT)) == (O_EXCL|O_CREAT)) { try { - // NOTE: createNewFile is a Java2 function - if(!f.createNewFile()) throw new ErrnoException(EEXIST); + if(!Platform.atomicCreateFile(f)) throw new ErrnoException(EEXIST); } catch(IOException e) { throw new ErrnoException(EIO); } @@ -718,16 +761,16 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { final Seekable.File sf; try { - sf = new Seekable.File(f,write); + sf = new Seekable.File(f,write,(flags & O_TRUNC) != 0); } catch(FileNotFoundException e) { if(e.getMessage() != null && e.getMessage().indexOf("Permission denied") >= 0) throw new ErrnoException(EACCES); return null; } catch(IOException e) { throw new ErrnoException(EIO); } - return new SeekableFD(sf,flags) { protected FStat _fstat() { return hostFStat(f,data); } }; + return new SeekableFD(sf,flags) { protected FStat _fstat() { return hostFStat(f,sf,data); } }; } - FStat hostFStat(File f, Object data) { return new HostFStat(f); } + FStat hostFStat(File f, Seekable.File sf, Object data) { return new HostFStat(f,sf); } FD hostFSDirFD(File f, Object data) { return null; } @@ -737,7 +780,13 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { /** The open syscall */ private int sys_open(int addr, int flags, int mode) throws ErrnoException, FaultException { - FD fd = _open(cstring(addr),flags,mode); + String name = cstring(addr); + + // HACK: TeX, or GPC, or something really sucks + if(name.length() == 1024 && getClass().getName().equals("tests.TeX")) name = name.trim(); + + flags &= ~O_NOCTTY; // this is meaningless under nestedvm + FD fd = _open(name,flags,mode); if(fd == null) return -ENOENT; int fdn = addFD(fd); if(fdn == -1) { fd.close(); return -ENFILE; } @@ -770,6 +819,17 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { copyout(buf,addr,n); return n; } + + /** The ftruncate syscall */ + private int sys_ftruncate(int fdn, long length) { + if (fdn < 0 || fdn >= OPEN_MAX) return -EBADFD; + if (fds[fdn] == null) return -EBADFD; + + Seekable seekable = fds[fdn].seekable(); + if (length < 0 || seekable == null) return -EINVAL; + try { seekable.resize(length); } catch (IOException e) { return -EIO; } + return 0; + } /** The close syscall */ private int sys_close(int fdn) { @@ -790,8 +850,8 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { int stat(FStat fs, int addr) throws FaultException { memWrite(addr+0,(fs.dev()<<16)|(fs.inode()&0xffff)); // st_dev (top 16), // st_ino (bottom 16) memWrite(addr+4,((fs.type()&0xf000))|(fs.mode()&0xfff)); // st_mode - memWrite(addr+8,1<<16); // st_nlink (top 16) // st_uid (bottom 16) - memWrite(addr+12,0); // st_gid (top 16) // st_rdev (bottom 16) + memWrite(addr+8,fs.nlink()<<16|fs.uid()&0xffff); // st_nlink (top 16) // st_uid (bottom 16) + memWrite(addr+12,fs.gid()<<16|0); // st_gid (top 16) // st_rdev (bottom 16) memWrite(addr+16,fs.size()); // st_size memWrite(addr+20,fs.atime()); // st_atime // memWrite(addr+24,0) // st_spare1 @@ -869,6 +929,8 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { private int sys_sysconf(int n) { switch(n) { case _SC_CLK_TCK: return 1000; + case _SC_PAGESIZE: return writePages.length == 1 ? 4096 : (1<= OPEN_MAX) return -EBADFD; @@ -958,7 +1030,7 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { for(i=arg;i= OPEN_MAX) return -EBADFD; + if(fds[fdn] == null) return -EBADFD; + FD fd = fds[fdn]; + + Seekable s = fd.seekable(); + if (s == null) return -EINVAL; + + try { + s.sync(); + return 0; + } catch (IOException e) { + return -EIO; + } + } + /** The syscall dispatcher. The should be called by subclasses when the syscall instruction is invoked. syscall should be the contents of V0 and a, b, c, and d should be the contenst of A0, A1, A2, and A3. The call MAY change the state @see Runtime#state state */ - protected final int syscall(int syscall, int a, int b, int c, int d) { + protected final int syscall(int syscall, int a, int b, int c, int d, int e, int f) { try { - return _syscall(syscall,a,b,c,d); - } catch(ErrnoException e) { - return -e.errno; - } catch(FaultException e) { + int n = _syscall(syscall,a,b,c,d,e,f); + //if(n<0) throw new ErrnoException(-n); + return n; + } catch(ErrnoException ex) { + //System.err.println("While executing syscall: " + syscall + ":"); + //if(syscall == SYS_open) try { System.err.println("Failed to open " + cstring(a) + " errno " + ex.errno); } catch(Exception e2) { } + //ex.printStackTrace(); + return -ex.errno; + } catch(FaultException ex) { return -EFAULT; - } catch(RuntimeException e) { - e.printStackTrace(); + } catch(RuntimeException ex) { + ex.printStackTrace(); throw new Error("Internal Error in _syscall()"); } } - int _syscall(int syscall, int a, int b, int c, int d) throws ErrnoException, FaultException { + int _syscall(int syscall, int a, int b, int c, int d, int e, int f) throws ErrnoException, FaultException { switch(syscall) { case SYS_null: return 0; case SYS_exit: return sys_exit(a); @@ -1002,6 +1099,7 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { case SYS_close: return sys_close(a); case SYS_read: return sys_read(a,b,c); case SYS_lseek: return sys_lseek(a,b,c); + case SYS_ftruncate: return sys_ftruncate(a,b); case SYS_getpid: return sys_getpid(); case SYS_calljava: return sys_calljava(a,b,c,d); case SYS_gettimeofday: return sys_gettimeofday(a,b); @@ -1010,7 +1108,12 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { case SYS_getpagesize: return sys_getpagesize(); case SYS_fcntl: return sys_fcntl(a,b,c); case SYS_sysconf: return sys_sysconf(a); + case SYS_getuid: return sys_getuid(); + case SYS_geteuid: return sys_geteuid(); + case SYS_getgid: return sys_getgid(); + case SYS_getegid: return sys_getegid(); + case SYS_fsync: return fsync(a); case SYS_memcpy: memcpy(a,b,c); return a; case SYS_memset: memset(a,b,c); return a; @@ -1031,6 +1134,11 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { } } + private int sys_getuid() { return 0; } + private int sys_geteuid() { return 0; } + private int sys_getgid() { return 0; } + private int sys_getegid() { return 0; } + public int xmalloc(int size) { int p=malloc(size); if(p==0) throw new RuntimeException("malloc() failed"); return p; } public int xrealloc(int addr,int newsize) { int p=realloc(addr,newsize); if(p==0) throw new RuntimeException("realloc() failed"); return p; } public int realloc(int addr, int newsize) { try { return call("realloc",addr,newsize); } catch(CallException e) { return 0; } } @@ -1054,9 +1162,38 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { } return addr; } - + + // TODO: less memory copying (custom utf-8 reader) + // or at least roll strlen() into copyin() + public final String utfstring(int addr) throws ReadFaultException { + if (addr == 0) return null; + + // determine length + int i=addr; + for(int word = 1; word != 0; i++) { + word = memRead(i&~3); + switch(i&3) { + case 0: word = (word>>>24)&0xff; break; + case 1: word = (word>>>16)&0xff; break; + case 2: word = (word>>> 8)&0xff; break; + case 3: word = (word>>> 0)&0xff; break; + } + } + if (i > addr) i--; // do not count null + + byte[] bytes = new byte[i-addr]; + copyin(addr, bytes, bytes.length); + + try { + return new String(bytes, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); // should never happen with UTF-8 + } + } + /** Helper function to read a cstring from main memory */ public final String cstring(int addr) throws ReadFaultException { + if (addr == 0) return null; StringBuffer sb = new StringBuffer(); for(;;) { int word = memRead(addr&~3); @@ -1072,6 +1209,14 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { /** File Descriptor class */ public static abstract class FD { private int refCount = 1; + private String normalizedPath = null; + private boolean deleteOnClose = false; + + public void setNormalizedPath(String path) { normalizedPath = path; } + public String getNormalizedPath() { return normalizedPath; } + + public void markDeleteOnClose() { deleteOnClose = true; } + public boolean isMarkedForDeleteOnClose() { return deleteOnClose; } /** Read some bytes. Should return the number of bytes read, 0 on EOF, or throw an IOException on error */ public int read(byte[] a, int off, int length) throws ErrnoException { throw new ErrnoException(EBADFD); } @@ -1083,8 +1228,6 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { public int getdents(byte[] a, int off, int length) throws ErrnoException { throw new ErrnoException(EBADFD); } - public int flags() { return O_RDONLY; } - /** Return a Seekable object representing this file descriptor (can be read only) This is required for exec() */ Seekable seekable() { return null; } @@ -1096,6 +1239,7 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { } protected abstract FStat _fstat(); + public abstract int flags(); /** Closes the fd */ public final void close() { if(--refCount==0) _close(); } @@ -1155,60 +1299,105 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { protected void _close() { try { data.close(); } catch(IOException e) { /*ignore*/ } } } - public static class OutputStreamFD extends FD { - private OutputStream os; - public int flags() { return O_WRONLY; } - public OutputStreamFD(OutputStream os) { this.os = os; } - public int write(byte[] a, int off, int length) throws ErrnoException { - try { - os.write(a,off,length); - return length; - } catch(IOException e) { - throw new ErrnoException(EIO); - } + public static class InputOutputStreamFD extends FD { + private final InputStream is; + private final OutputStream os; + + public InputOutputStreamFD(InputStream is) { this(is,null); } + public InputOutputStreamFD(OutputStream os) { this(null,os); } + public InputOutputStreamFD(InputStream is, OutputStream os) { + this.is = is; + this.os = os; + if(is == null && os == null) throw new IllegalArgumentException("at least one stream must be supplied"); } - public void _close() { try { os.close(); } catch(IOException e) { /*ignore*/ } } - public FStat _fstat() { return new FStat(); } - } - - public static class InputStreamFD extends FD { - private InputStream is; - public int flags() { return O_RDONLY; } - public InputStreamFD(InputStream is) { this.is = is; } + + public int flags() { + if(is != null && os != null) return O_RDWR; + if(is != null) return O_RDONLY; + if(os != null) return O_WRONLY; + throw new Error("should never happen"); + } + + public void _close() { + if(is != null) try { is.close(); } catch(IOException e) { /*ignore*/ } + if(os != null) try { os.close(); } catch(IOException e) { /*ignore*/ } + } + public int read(byte[] a, int off, int length) throws ErrnoException { + if(is == null) return super.read(a,off,length); try { int n = is.read(a,off,length); return n < 0 ? 0 : n; } catch(IOException e) { throw new ErrnoException(EIO); } + } + + public int write(byte[] a, int off, int length) throws ErrnoException { + if(os == null) return super.write(a,off,length); + try { + os.write(a,off,length); + return length; + } catch(IOException e) { + throw new ErrnoException(EIO); + } } - public void _close() { try { is.close(); } catch(IOException e) { /*ignore*/ } } - public FStat _fstat() { return new FStat(); } - } - - static class StdinFD extends InputStreamFD { - public StdinFD(InputStream is) { super(is); } - public void _close() { /* noop */ } - public FStat _fstat() { return new FStat() { public int type() { return S_IFCHR; } }; } + + public FStat _fstat() { return new SocketFStat(); } } - static class StdoutFD extends OutputStreamFD { - public StdoutFD(OutputStream os) { super(os); } + static class TerminalFD extends InputOutputStreamFD { + public TerminalFD(InputStream is) { this(is,null); } + public TerminalFD(OutputStream os) { this(null,os); } + public TerminalFD(InputStream is, OutputStream os) { super(is,os); } public void _close() { /* noop */ } - public FStat _fstat() { return new FStat() { public int type() { return S_IFCHR; } }; } + public FStat _fstat() { return new SocketFStat() { public int type() { return S_IFCHR; } public int mode() { return 0600; } }; } + } + + // This is pretty inefficient but it is only used for reading from the console on win32 + static class Win32ConsoleIS extends InputStream { + private int pushedBack = -1; + private final InputStream parent; + public Win32ConsoleIS(InputStream parent) { this.parent = parent; } + public int read() throws IOException { + if(pushedBack != -1) { int c = pushedBack; pushedBack = -1; return c; } + int c = parent.read(); + if(c == '\r' && (c = parent.read()) != '\n') { pushedBack = c; return '\r'; } + return c; + } + public int read(byte[] buf, int pos, int len) throws IOException { + boolean pb = false; + if(pushedBack != -1 && len > 0) { + buf[0] = (byte) pushedBack; + pushedBack = -1; + pos++; len--; pb = true; + } + int n = parent.read(buf,pos,len); + if(n == -1) return pb ? 1 : -1; + for(int i=0;i b ? a : b; }