diff --git a/lib/nestedvm.jar b/lib/nestedvm.jar deleted file mode 100644 index 9291417..0000000 Binary files a/lib/nestedvm.jar and /dev/null differ diff --git a/pom.xml b/pom.xml index fb9e79f..36a2c2f 100644 --- a/pom.xml +++ b/pom.xml @@ -141,13 +141,6 @@ reflections 0.9.9 - - megacz - nestedvm - 2014.12.3 - system - ${project.basedir}/lib/nestedvm.jar - junit junit diff --git a/src/main/java/org/ibex/nestedvm/Interpreter.java b/src/main/java/org/ibex/nestedvm/Interpreter.java new file mode 100644 index 0000000..719aa74 --- /dev/null +++ b/src/main/java/org/ibex/nestedvm/Interpreter.java @@ -0,0 +1,787 @@ +// Copyright 2000-2005 the Contributors, as shown in the revision logs. +// Licensed under the Apache Public Source 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 + +package org.ibex.nestedvm; + +import org.ibex.nestedvm.util.*; +import java.io.*; + +public class Interpreter extends UnixRuntime implements Cloneable { + // Registers + private int[] registers = new int[32]; + private int hi,lo; + + // Floating Point Registers + private int[] fpregs = new int[32]; + // 24-31 - unused + // 23 - conditional bit + // 18-22 - unused + // 12-17 - cause bits (unimplemented) + // 7-11 - enables bits (unimplemented) + // 2-6 - flags (unimplemented) + // 0-1 - rounding mode (only implemented for fixed point conversions) + private int fcsr; + + private int pc; + + // The filename if the binary we're running + public String image; + private ELF.Symtab symtab; + + // Register Operations + private final void setFC(boolean b) { fcsr = (fcsr&~0x800000) | (b ? 0x800000 : 0x000000); } + private final int roundingMode() { return fcsr & 3; /* bits 0-1 */ } + private final double getDouble(int r) { + return Double.longBitsToDouble(((fpregs[r+1]&0xffffffffL) << 32) | (fpregs[r]&0xffffffffL)); + } + private final void setDouble(int r, double d) { + long l = Double.doubleToLongBits(d); + fpregs[r+1] = (int)(l >>> 32); fpregs[r] = (int)l; + } + private final float getFloat(int r) { return Float.intBitsToFloat(fpregs[r]); } + private final void setFloat(int r, float f) { fpregs[r] = Float.floatToRawIntBits(f); } + + protected void _execute() throws ExecutionException { + try { + runSome(); + } catch(ExecutionException e) { + e.setLocation(toHex(pc) + ": " + sourceLine(pc)); + throw e; + } + } + + protected Object clone() throws CloneNotSupportedException { + Interpreter r = (Interpreter) super.clone(); + r.registers = (int[]) registers.clone(); + r.fpregs = (int[]) fpregs.clone(); + return r; + } + + // 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<>2; + int[] r = registers; + int[] f = fpregs; + int pc = this.pc; + int nextPC = pc + 4; + try { + OUTER: for(;;) { + int insn; + try { + insn = readPages[pc>>>pageShift][(pc>>>2)&PAGE_WORDS-1]; + } catch (RuntimeException e) { + if(pc == 0xdeadbeef) throw new Error("fell off cpu: r2: " + r[2]); + insn = memRead(pc); + } + + int op = (insn >>> 26) & 0xff; // bits 26-31 + int rs = (insn >>> 21) & 0x1f; // bits 21-25 + int rt = (insn >>> 16) & 0x1f; // bits 16-20 + int ft = (insn >>> 16) & 0x1f; + int rd = (insn >>> 11) & 0x1f; // bits 11-15 + int fs = (insn >>> 11) & 0x1f; + int shamt = (insn >>> 6) & 0x1f; // bits 6-10 + int fd = (insn >>> 6) & 0x1f; + int subcode = insn & 0x3f; // bits 0-5 + + int jumpTarget = (insn & 0x03ffffff); // bits 0-25 + int unsignedImmediate = insn & 0xffff; + int signedImmediate = (insn << 16) >> 16; + int branchTarget = signedImmediate; + + int tmp, addr; // temporaries + + r[ZERO] = 0; + + switch(op) { + case 0: { + switch(subcode) { + case 0: // SLL + if(insn == 0) break; + r[rd] = r[rt] << shamt; + break; + case 2: // SRL + r[rd] = r[rt] >>> shamt; + break; + case 3: // SRA + r[rd] = r[rt] >> shamt; + break; + case 4: // SLLV + r[rd] = r[rt] << (r[rs]&0x1f); + break; + case 6: // SRLV + r[rd] = r[rt] >>> (r[rs]&0x1f); + break; + case 7: // SRAV + r[rd] = r[rt] >> (r[rs]&0x1f); + break; + case 8: // JR + tmp = r[rs]; pc += 4; nextPC = tmp; + continue OUTER; + case 9: // JALR + tmp = r[rs]; pc += 4; r[rd] = pc+4; nextPC = tmp; + continue OUTER; + case 12: // SYSCALL + this.pc = pc; + r[V0] = syscall(r[V0],r[A0],r[A1],r[A2],r[A3],r[T0],r[T1]); + if(state != RUNNING) { this.pc = nextPC; break OUTER; } + break; + case 13: // BREAK + throw new ExecutionException("Break"); + case 16: // MFHI + r[rd] = hi; + break; + case 17: // MTHI + hi = r[rs]; + break; + case 18: // MFLO + r[rd] = lo; + break; + case 19: // MTLO + lo = r[rs]; + break; + case 24: { // MULT + long hilo = ((long)r[rs]) * ((long)r[rt]); + hi = (int) (hilo >>> 32); + lo = (int) hilo; + break; + } + case 25: { // MULTU + long hilo = (r[rs] & 0xffffffffL) * (r[rt] & 0xffffffffL); + hi = (int) (hilo >>> 32); + lo = (int) hilo; + break; + } + case 26: // DIV + hi = r[rs]%r[rt]; + lo = r[rs]/r[rt]; + break; + case 27: // DIVU + if(rt != 0) { + hi = (int)((r[rs] & 0xffffffffL) % (r[rt] & 0xffffffffL)); + lo = (int)((r[rs] & 0xffffffffL) / (r[rt] & 0xffffffffL)); + } + break; + case 32: // ADD + throw new ExecutionException("ADD (add with oveflow trap) not suported"); + /*This must trap on overflow + r[rd] = r[rs] + r[rt]; + break;*/ + case 33: // ADDU + r[rd] = r[rs] + r[rt]; + break; + case 34: // SUB + throw new ExecutionException("SUB (sub with oveflow trap) not suported"); + /*This must trap on overflow + r[rd] = r[rs] - r[rt]; + break;*/ + case 35: // SUBU + r[rd] = r[rs] - r[rt]; + break; + case 36: // AND + r[rd] = r[rs] & r[rt]; + break; + case 37: // OR + r[rd] = r[rs] | r[rt]; + break; + case 38: // XOR + r[rd] = r[rs] ^ r[rt]; + break; + case 39: // NOR + r[rd] = ~(r[rs] | r[rt]); + break; + case 42: // SLT + r[rd] = r[rs] < r[rt] ? 1 : 0; + break; + case 43: // SLTU + r[rd] = ((r[rs] & 0xffffffffL) < (r[rt] & 0xffffffffL)) ? 1 : 0; + break; + default: + throw new ExecutionException("Illegal instruction 0/" + subcode); + } + break; + } + case 1: { + switch(rt) { + case 0: // BLTZ + if(r[rs] < 0) { + pc += 4; tmp = pc + branchTarget*4; nextPC = tmp; + continue OUTER; + } + break; + case 1: // BGEZ + if(r[rs] >= 0) { + pc += 4; tmp = pc + branchTarget*4; nextPC = tmp; + continue OUTER; + } + break; + case 16: // BLTZAL + if(r[rs] < 0) { + pc += 4; r[RA] = pc+4; tmp = pc + branchTarget*4; nextPC = tmp; + continue OUTER; + } + break; + case 17: // BGEZAL + if(r[rs] >= 0) { + pc += 4; r[RA] = pc+4; tmp = pc + branchTarget*4; nextPC = tmp; + continue OUTER; + } + break; + default: + throw new ExecutionException("Illegal Instruction"); + } + break; + } + case 2: { // J + tmp = (pc&0xf0000000) | (jumpTarget << 2); + pc+=4; nextPC = tmp; + continue OUTER; + } + case 3: { // JAL + tmp = (pc&0xf0000000) | (jumpTarget << 2); + pc+=4; r[RA] = pc+4; nextPC = tmp; + continue OUTER; + } + case 4: // BEQ + if(r[rs] == r[rt]) { + pc += 4; tmp = pc + branchTarget*4; nextPC = tmp; + continue OUTER; + } + break; + case 5: // BNE + if(r[rs] != r[rt]) { + pc += 4; tmp = pc + branchTarget*4; nextPC = tmp; + continue OUTER; + } + break; + case 6: //BLEZ + if(r[rs] <= 0) { + pc += 4; tmp = pc + branchTarget*4; nextPC = tmp; + continue OUTER; + } + break; + case 7: //BGTZ + if(r[rs] > 0) { + pc += 4; tmp = pc + branchTarget*4; nextPC = tmp; + continue OUTER; + } + break; + case 8: // ADDI + r[rt] = r[rs] + signedImmediate; + break; + case 9: // ADDIU + r[rt] = r[rs] + signedImmediate; + break; + case 10: // SLTI + r[rt] = r[rs] < signedImmediate ? 1 : 0; + break; + case 11: // SLTIU + r[rt] = (r[rs]&0xffffffffL) < (signedImmediate&0xffffffffL) ? 1 : 0; + break; + case 12: // ANDI + r[rt] = r[rs] & unsignedImmediate; + break; + case 13: // ORI + r[rt] = r[rs] | unsignedImmediate; + break; + case 14: // XORI + r[rt] = r[rs] ^ unsignedImmediate; + break; + case 15: // LUI + r[rt] = unsignedImmediate << 16; + break; + case 16: + throw new ExecutionException("TLB/Exception support not implemented"); + case 17: { // FPU + boolean debug = false; + String line = debug ? sourceLine(pc) : ""; + boolean debugon = debug && (line.indexOf("dtoa.c:51") >= 0 || line.indexOf("dtoa.c:52") >= 0 || line.indexOf("test.c") >= 0); + if(rs > 8 && debugon) + System.out.println(" FP Op: " + op + "/" + rs + "/" + subcode + " " + line); + if(roundingMode() != 0 && rs != 6 /*CTC.1*/ && !((rs==16 || rs==17) && subcode == 36 /* CVT.W.Z */)) + throw new ExecutionException("Non-cvt.w.z operation attempted with roundingMode != round to nearest"); + switch(rs) { + case 0: // MFC.1 + r[rt] = f[rd]; + break; + case 2: // CFC.1 + if(fs != 31) throw new ExecutionException("FCR " + fs + " unavailable"); + r[rt] = fcsr; + break; + case 4: // MTC.1 + f[rd] = r[rt]; + break; + case 6: // CTC.1 + if(fs != 31) throw new ExecutionException("FCR " + fs + " unavailable"); + fcsr = r[rt]; + break; + case 8: // BC1F, BC1T + if(((fcsr&0x800000)!=0) == (((insn>>>16)&1)!=0)) { + pc += 4; tmp = pc + branchTarget*4; nextPC = tmp; + continue OUTER; + } + break; + case 16: { // Single + switch(subcode) { + case 0: // ADD.S + setFloat(fd,getFloat(fs)+getFloat(ft)); + break; + case 1: // SUB.S + setFloat(fd,getFloat(fs)-getFloat(ft)); + break; + case 2: // MUL.S + setFloat(fd,getFloat(fs)*getFloat(ft)); + break; + case 3: // DIV.S + setFloat(fd,getFloat(fs)/getFloat(ft)); + break; + case 5: // ABS.S + setFloat(fd,Math.abs(getFloat(fs))); + break; + case 6: // MOV.S + f[fd] = f[fs]; + break; + case 7: // NEG.S + setFloat(fd,-getFloat(fs)); + break; + case 33: // CVT.D.S + setDouble(fd,getFloat(fs)); + break; + case 36: // CVT.W.S + switch(roundingMode()) { + case 0: f[fd] = (int)Math.floor(getFloat(fs)+0.5f); break; // Round to nearest + case 1: f[fd] = (int)getFloat(fs); break; // Round towards zero + case 2: f[fd] = (int)Math.ceil(getFloat(fs)); break; // Round towards plus infinity + case 3: f[fd] = (int)Math.floor(getFloat(fs)); break; // Round towards minus infinity + } + break; + case 50: // C.EQ.S + setFC(getFloat(fs) == getFloat(ft)); + break; + case 60: // C.LT.S + setFC(getFloat(fs) < getFloat(ft)); + break; + case 62: // C.LE.S + setFC(getFloat(fs) <= getFloat(ft)); + break; + default: throw new ExecutionException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc)); + } + break; + } + case 17: { // Double + switch(subcode) { + case 0: // ADD.D + setDouble(fd,getDouble(fs)+getDouble(ft)); + break; + case 1: // SUB.D + if(debugon) System.out.println("f" + fd + " = f" + fs + " (" + getDouble(fs) + ") - f" + ft + " (" + getDouble(ft) + ")"); + setDouble(fd,getDouble(fs)-getDouble(ft)); + break; + case 2: // MUL.D + if(debugon) System.out.println("f" + fd + " = f" + fs + " (" + getDouble(fs) + ") * f" + ft + " (" + getDouble(ft) + ")"); + setDouble(fd,getDouble(fs)*getDouble(ft)); + if(debugon) System.out.println("f" + fd + " = " + getDouble(fd)); + break; + case 3: // DIV.D + setDouble(fd,getDouble(fs)/getDouble(ft)); + break; + case 5: // ABS.D + setDouble(fd,Math.abs(getDouble(fs))); + break; + case 6: // MOV.D + f[fd] = f[fs]; + f[fd+1] = f[fs+1]; + break; + case 7: // NEG.D + setDouble(fd,-getDouble(fs)); + break; + case 32: // CVT.S.D + setFloat(fd,(float)getDouble(fs)); + break; + case 36: // CVT.W.D + if(debugon) System.out.println("CVT.W.D rm: " + roundingMode() + " f" + fs + ":" + getDouble(fs)); + switch(roundingMode()) { + case 0: f[fd] = (int)Math.floor(getDouble(fs)+0.5); break; // Round to nearest + case 1: f[fd] = (int)getDouble(fs); break; // Round towards zero + case 2: f[fd] = (int)Math.ceil(getDouble(fs)); break; // Round towards plus infinity + case 3: f[fd] = (int)Math.floor(getDouble(fs)); break; // Round towards minus infinity + } + if(debugon) System.out.println("CVT.W.D: f" + fd + ":" + f[fd]); + break; + case 50: // C.EQ.D + setFC(getDouble(fs) == getDouble(ft)); + break; + case 60: // C.LT.D + setFC(getDouble(fs) < getDouble(ft)); + break; + case 62: // C.LE.D + setFC(getDouble(fs) <= getDouble(ft)); + break; + default: throw new ExecutionException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc)); + } + break; + } + case 20: { // Integer + switch(subcode) { + case 32: // CVT.S.W + setFloat(fd,f[fs]); + break; + case 33: // CVT.D.W + setDouble(fd,f[fs]); + break; + default: throw new ExecutionException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc)); + } + break; + } + default: + throw new ExecutionException("Invalid Instruction 17/" + rs); + } + break; + } + case 18: case 19: + throw new ExecutionException("No coprocessor installed"); + case 32: { // LB + addr = r[rs] + signedImmediate; + try { + tmp = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)]; + } catch(RuntimeException e) { + tmp = memRead(addr&~3); + } + switch(addr&3) { + case 0: tmp = (tmp>>>24)&0xff; break; + case 1: tmp = (tmp>>>16)&0xff; break; + case 2: tmp = (tmp>>> 8)&0xff; break; + case 3: tmp = (tmp>>> 0)&0xff; break; + } + if((tmp&0x80)!=0) tmp |= 0xffffff00; // sign extend + r[rt] = tmp; + break; + } + case 33: { // LH + addr = r[rs] + signedImmediate; + try { + tmp = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)]; + } catch(RuntimeException e) { + tmp = memRead(addr&~3); + } + switch(addr&3) { + case 0: tmp = (tmp>>>16)&0xffff; break; + case 2: tmp = (tmp>>> 0)&0xffff; break; + default: throw new ReadFaultException(addr); + } + if((tmp&0x8000)!=0) tmp |= 0xffff0000; // sign extend + r[rt] = tmp; + break; + } + case 34: { // LWL; + addr = r[rs] + signedImmediate; + try { + tmp = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)]; + } catch(RuntimeException e) { + tmp = memRead(addr&~3); + } + switch(addr&3) { + case 0: r[rt] = (r[rt]&0x00000000)|(tmp<< 0); break; + case 1: r[rt] = (r[rt]&0x000000ff)|(tmp<< 8); break; + case 2: r[rt] = (r[rt]&0x0000ffff)|(tmp<<16); break; + case 3: r[rt] = (r[rt]&0x00ffffff)|(tmp<<24); break; + } + break; + } + case 35: // LW + addr = r[rs] + signedImmediate; + try { + r[rt] = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)]; + } catch(RuntimeException e) { + r[rt] = memRead(addr); + } + break; + case 36: { // LBU + addr = r[rs] + signedImmediate; + try { + tmp = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)]; + } catch(RuntimeException e) { + tmp = memRead(addr); + } + switch(addr&3) { + case 0: r[rt] = (tmp>>>24)&0xff; break; + case 1: r[rt] = (tmp>>>16)&0xff; break; + case 2: r[rt] = (tmp>>> 8)&0xff; break; + case 3: r[rt] = (tmp>>> 0)&0xff; break; + } + break; + } + case 37: { // LHU + addr = r[rs] + signedImmediate; + try { + tmp = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)]; + } catch(RuntimeException e) { + tmp = memRead(addr&~3); + } + switch(addr&3) { + case 0: r[rt] = (tmp>>>16)&0xffff; break; + case 2: r[rt] = (tmp>>> 0)&0xffff; break; + default: throw new ReadFaultException(addr); + } + break; + } + case 38: { // LWR + addr = r[rs] + signedImmediate; + try { + tmp = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)]; + } catch(RuntimeException e) { + tmp = memRead(addr&~3); + } + switch(addr&3) { + case 0: r[rt] = (r[rt]&0xffffff00)|(tmp>>>24); break; + case 1: r[rt] = (r[rt]&0xffff0000)|(tmp>>>16); break; + case 2: r[rt] = (r[rt]&0xff000000)|(tmp>>> 8); break; + case 3: r[rt] = (r[rt]&0x00000000)|(tmp>>> 0); break; + } + break; + } + case 40: { // SB + addr = r[rs] + signedImmediate; + try { + tmp = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)]; + } catch(RuntimeException e) { + tmp = memRead(addr&~3); + } + switch(addr&3) { + case 0: tmp = (tmp&0x00ffffff) | ((r[rt]&0xff)<<24); break; + case 1: tmp = (tmp&0xff00ffff) | ((r[rt]&0xff)<<16); break; + case 2: tmp = (tmp&0xffff00ff) | ((r[rt]&0xff)<< 8); break; + case 3: tmp = (tmp&0xffffff00) | ((r[rt]&0xff)<< 0); break; + } + try { + writePages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)] = tmp; + } catch(RuntimeException e) { + memWrite(addr&~3,tmp); + } + break; + } + case 41: { // SH + addr = r[rs] + signedImmediate; + try { + tmp = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)]; + } catch(RuntimeException e) { + tmp = memRead(addr&~3); + } + switch(addr&3) { + case 0: tmp = (tmp&0x0000ffff) | ((r[rt]&0xffff)<<16); break; + case 2: tmp = (tmp&0xffff0000) | ((r[rt]&0xffff)<< 0); break; + default: throw new WriteFaultException(addr); + } + try { + writePages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)] = tmp; + } catch(RuntimeException e) { + memWrite(addr&~3,tmp); + } + break; + } + case 42: { // SWL + addr = r[rs] + signedImmediate; + tmp = memRead(addr&~3); + switch(addr&3) { + case 0: tmp=(tmp&0x00000000)|(r[rt]>>> 0); break; + case 1: tmp=(tmp&0xff000000)|(r[rt]>>> 8); break; + case 2: tmp=(tmp&0xffff0000)|(r[rt]>>>16); break; + case 3: tmp=(tmp&0xffffff00)|(r[rt]>>>24); break; + } + try { + writePages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)] = tmp; + } catch(RuntimeException e) { + memWrite(addr&~3,tmp); + } + break; + } + case 43: // SW + addr = r[rs] + signedImmediate; + try { + writePages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)] = r[rt]; + } catch(RuntimeException e) { + memWrite(addr&~3,r[rt]); + } + break; + case 46: { // SWR + addr = r[rs] + signedImmediate; + tmp = memRead(addr&~3); + switch(addr&3) { + case 0: tmp=(tmp&0x00ffffff)|(r[rt]<<24); break; + case 1: tmp=(tmp&0x0000ffff)|(r[rt]<<16); break; + case 2: tmp=(tmp&0x000000ff)|(r[rt]<< 8); break; + case 3: tmp=(tmp&0x00000000)|(r[rt]<< 0); break; + } + memWrite(addr&~3,tmp); + break; + } + // Needs to be atomic w/ threads + case 48: // LWC0/LL + r[rt] = memRead(r[rs] + signedImmediate); + break; + case 49: // LWC1 + f[rt] = memRead(r[rs] + signedImmediate); + break; + // Needs to be atomic w/ threads + case 56: + memWrite(r[rs] + signedImmediate,r[rt]); + r[rt] = 1; + break; + case 57: // SWC1 + memWrite(r[rs] + signedImmediate,f[rt]); + break; + default: + throw new ExecutionException("Invalid Instruction: " + op); + } + pc = nextPC; + nextPC = pc + 4; + } // for(;;) + } catch(ExecutionException e) { + this.pc = pc; + throw e; + } + return 0; + } + + public int lookupSymbol(String name) { + ELF.Symbol sym = symtab.getSymbol(name); + 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 { + ELF elf = new ELF(data); + symtab = elf.getSymtab(); + + if(elf.header.type != ELF.ET_EXEC) throw new IOException("Binary is not an executable"); + if(elf.header.machine != ELF.EM_MIPS) throw new IOException("Binary is not for the MIPS I Architecture"); + if(elf.ident.data != ELF.ELFDATA2MSB) throw new IOException("Binary is not big endian"); + + entryPoint = elf.header.entry; + + ELF.Symtab symtab = elf.getSymtab(); + if(symtab == null) throw new IOException("No symtab in binary (did you strip it?)"); + userInfo = symtab.getSymbol("user_info"); + ELF.Symbol gpsym = symtab.getSymbol("_gp"); + + if(gpsym == null) throw new IOException("NO _gp symbol!"); + gp = gpsym.addr; + + entryPoint = elf.header.entry; + + ELF.PHeader[] pheaders = elf.pheaders; + int brk = 0; + int pageSize = (1<> 2; + for(int i=0;i>> pageShift; + if(readPages[page] == null) + 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 >>> pageShift][(addr >>> 2)&(pageWords-1)] = dis.readInt(); + addr+=4; + filesize-=4; + } while(filesize > 0); + dis.close(); + } + } + heapStart = (brk+pageSize-1)&~(pageSize-1); + } + + protected void setCPUState(CPUState state) { + for(int i=1;i<32;i++) registers[i] = state.r[i]; + for(int i=0;i<32;i++) fpregs[i] = state.f[i]; + hi=state.hi; lo=state.lo; fcsr=state.fcsr; + pc=state.pc; + } + + 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; + } + + 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; + } + public Interpreter(InputStream is) throws IOException { this(new Seekable.InputStream(is)); } + + // Debug functions + // NOTE: This probably requires a jdk > 1.1, however, it is only used for debugging + private java.util.HashMap sourceLineCache; + public String sourceLine(int pc) { + final String addr2line = "mips-unknown-elf-addr2line"; + String line = (String) (sourceLineCache == null ? null : sourceLineCache.get(new Integer(pc))); + if(line != null) return line; + if(image==null) return null; + try { + Process p = java.lang.Runtime.getRuntime().exec(new String[]{addr2line,"-e",image,toHex(pc)}); + line = new BufferedReader(new InputStreamReader(p.getInputStream())).readLine(); + if(line == null) return null; + while(line.startsWith("../")) line = line.substring(3); + if(sourceLineCache == null) sourceLineCache = new java.util.HashMap(); + sourceLineCache.put(new Integer(pc),line); + return line; + } catch(IOException e) { + return null; + } + } + + public class DebugShutdownHook implements Runnable { + public void run() { + int pc = Interpreter.this.pc; + if(getState() == RUNNING) + System.err.print("\nCPU Executing " + toHex(pc) + ": " + sourceLine(pc) + "\n"); + } + } + + public static void main(String[] argv) throws Exception { + String image = argv[0]; + Interpreter emu = new Interpreter(image); + java.lang.Runtime.getRuntime().addShutdownHook(new Thread(emu.new DebugShutdownHook())); + int status = emu.run(argv); + System.err.println("Exit status: " + status); + System.exit(status); + } +} diff --git a/src/main/java/org/ibex/nestedvm/Registers.java b/src/main/java/org/ibex/nestedvm/Registers.java new file mode 100644 index 0000000..a441af5 --- /dev/null +++ b/src/main/java/org/ibex/nestedvm/Registers.java @@ -0,0 +1,46 @@ +// Copyright 2000-2005 the Contributors, as shown in the revision logs. +// Licensed under the Apache Public Source License 2.0 ("the License"). +// You may not use this file except in compliance with the License. + +package org.ibex.nestedvm; + +interface Registers { + // Register Names + public final static int ZERO = 0; // Immutable, hardwired to 0 + public final static int AT = 1; // Reserved for assembler + public final static int K0 = 26; // Reserved for kernel + public final static int K1 = 27; // Reserved for kernel + public final static int GP = 28; // Global pointer (the middle of .sdata/.sbss) + public final static int SP = 29; // Stack pointer + public final static int FP = 30; // Frame Pointer + public final static int RA = 31; // Return Address + + // Return values (caller saved) + public final static int V0 = 2; + public final static int V1 = 3; + // Argument Registers (caller saved) + public final static int A0 = 4; + public final static int A1 = 5; + public final static int A2 = 6; + public final static int A3 = 7; + // Temporaries (caller saved) + public final static int T0 = 8; + public final static int T1 = 9; + public final static int T2 = 10; + public final static int T3 = 11; + public final static int T4 = 12; + public final static int T5 = 13; + public final static int T6 = 14; + public final static int T7 = 15; + public final static int T8 = 24; + public final static int T9 = 25; + // Saved (callee saved) + public final static int S0 = 16; + public final static int S1 = 17; + public final static int S2 = 18; + public final static int S3 = 19; + public final static int S4 = 20; + public final static int S5 = 21; + public final static int S6 = 22; + public final static int S7 = 23; +} diff --git a/src/main/java/org/ibex/nestedvm/Runtime.java b/src/main/java/org/ibex/nestedvm/Runtime.java new file mode 100644 index 0000000..2c599a0 --- /dev/null +++ b/src/main/java/org/ibex/nestedvm/Runtime.java @@ -0,0 +1,1566 @@ +// Copyright 2000-2005 the Contributors, as shown in the revision logs. +// Licensed under the Apache Public Source 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 + +package org.ibex.nestedvm; + +import org.ibex.nestedvm.util.*; +import java.io.*; + +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; + + /** Number of bits to shift to get the page number (1<<symbol or -1 it it doesn't exits in this method + This method is only required if the call() function is used */ + public int lookupSymbol(String symbol) { return -1; } + + /** 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 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; + r.startTime = 0; + r.fds = new FD[OPEN_MAX]; + for(int i=0;i>>_pageShift != 1) _pageShift++; + pageShift = _pageShift; + + int heapStart = heapStart(); + int totalMemory = totalPages * pageSize; + int stackSize = max(totalMemory/512,ARG_MAX+65536); + int stackPages = 0; + if(totalPages > 1) { + stackSize = max(stackSize,pageSize); + stackSize = (stackSize + pageSize - 1) & ~(pageSize-1); + stackPages = stackSize >>> pageShift; + heapStart = (heapStart + pageSize - 1) & ~(pageSize-1); + if(stackPages + STACK_GUARD_PAGES + (heapStart >>> pageShift) >= totalPages) + throw new IllegalArgumentException("total pages too small"); + } else { + 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>2]; + } + } + + 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. + Newly initalized pages will be marked read-only if ro is set */ + protected final void initPages(int[] src, int addr, boolean ro) { + int pageWords = (1<>>2; + int pageMask = (1<>> pageShift; + int start = (addr&pageMask)>>2; + int elements = min(pageWords-start,src.length-i); + if(readPages[page]==null) { + initPage(page,ro); + } else if(!ro) { + if(writePages[page] == null) writePages[page] = readPages[page]; + } + System.arraycopy(src,i,readPages[page],start,elements); + i += elements; + addr += elements*4; + } + } + + /** Initialize words of pages starting at addr to 0 */ + protected final void clearPages(int addr, int words) { + int pageWords = (1<>>2; + int pageMask = (1<>> pageShift; + int start = (addr&pageMask)>>2; + int elements = min(pageWords-start,words-i); + if(readPages[page]==null) { + readPages[page] = writePages[page] = new int[pageWords]; + } else { + if(writePages[page] == null) writePages[page] = readPages[page]; + for(int j=start;jlength bytes from the processes memory space starting at + addr INTO a java byte array a */ + public final void copyin(int addr, byte[] buf, int count) throws ReadFaultException { + int pageWords = (1<>>2; + int pageMask = pageWords - 1; + + int x=0; + if(count == 0) return; + if((addr&3)!=0) { + int word = memRead(addr&~3); + switch(addr&3) { + case 1: buf[x++] = (byte)((word>>>16)&0xff); if(--count==0) break; + case 2: buf[x++] = (byte)((word>>> 8)&0xff); if(--count==0) break; + case 3: buf[x++] = (byte)((word>>> 0)&0xff); if(--count==0) break; + } + addr = (addr&~3)+4; + } + if((count&~3) != 0) { + int c = count>>>2; + int a = addr>>>2; + while(c != 0) { + int[] page = readPages[a >>> (pageShift-2)]; + if(page == null) throw new ReadFaultException(a<<2); + int index = a&pageMask; + int n = min(c,pageWords-index); + for(int i=0;i>>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; + } + addr = a<<2; count &=3; + } + if(count != 0) { + int word = memRead(addr); + switch(count) { + case 3: buf[x+2] = (byte)((word>>>8)&0xff); + case 2: buf[x+1] = (byte)((word>>>16)&0xff); + case 1: buf[x+0] = (byte)((word>>>24)&0xff); + } + } + } + + /** Copies length bytes OUT OF the java array a into the processes memory + space at addr */ + public final void copyout(byte[] buf, int addr, int count) throws FaultException { + int pageWords = (1<>>2; + int pageWordMask = pageWords - 1; + + int x=0; + if(count == 0) return; + if((addr&3)!=0) { + int word = memRead(addr&~3); + switch(addr&3) { + case 1: word = (word&0xff00ffff)|((buf[x++]&0xff)<<16); if(--count==0) break; + case 2: word = (word&0xffff00ff)|((buf[x++]&0xff)<< 8); if(--count==0) break; + case 3: word = (word&0xffffff00)|((buf[x++]&0xff)<< 0); if(--count==0) break; + } + 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 >>> (pageShift-2)]; + if(page == null) throw new WriteFaultException(a<<2); + int index = a&pageWordMask; + int n = min(c,pageWords-index); + for(int i=0;i>>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>>>(pageShift-2)]; + if(srcPage == null) throw new ReadFaultException(s<<2); + int[] dstPage = writePages[d>>>(pageShift-2)]; + if(dstPage == null) throw new WriteFaultException(d<<2); + 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; + } + if(count != 0) { + int word1 = memRead(src); + int word2 = memRead(dst); + switch(count) { + case 1: memWrite(dst,(word1&0xff000000)|(word2&0x00ffffff)); break; + case 2: memWrite(dst,(word1&0xffff0000)|(word2&0x0000ffff)); break; + case 3: memWrite(dst,(word1&0xffffff00)|(word2&0x000000ff)); break; + } + } + } else { + while(count > 0) { + int n = min(count,MAX_CHUNK); + byte[] buf = byteBuf(n); + copyin(src,buf,n); + copyout(buf,dst,n); + count -= n; src += n; dst += n; + } + } + } + + public final void memset(int addr, int ch, int count) throws FaultException { + int pageWords = (1<>>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); + switch(addr&3) { + case 1: word = (word&0xff00ffff)|((ch&0xff)<<16); if(--count==0) break; + case 2: word = (word&0xffff00ff)|((ch&0xff)<< 8); if(--count==0) break; + case 3: word = (word&0xffffff00)|((ch&0xff)<< 0); if(--count==0) break; + } + memWrite(addr&~3,word); + addr = (addr&~3)+4; + } + if((count&~3) != 0) { + int c = count>>2; + int a = addr>>>2; + while(c != 0) { + int[] page = readPages[a>>>(pageShift-2)]; + 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);*/ + for(int i=index;iaddr */ + public final int memRead(int addr) throws ReadFaultException { + if((addr & 3) != 0) throw new ReadFaultException(addr); + return unsafeMemRead(addr); + } + + protected final int unsafeMemRead(int addr) throws ReadFaultException { + int page = addr >>> pageShift; + int entry = (addr&(1<>2; + try { + return readPages[page][entry]; + } catch(ArrayIndexOutOfBoundsException e) { + if(page < 0 || page >= readPages.length) throw new ReadFaultException(addr); + throw e; // should never happen + } catch(NullPointerException e) { + throw new ReadFaultException(addr); + } + } + + /** Writes a word to the processes memory at addr */ + public final void memWrite(int addr, int value) throws WriteFaultException { + if((addr & 3) != 0) throw new WriteFaultException(addr); + unsafeMemWrite(addr,value); + } + + protected final void unsafeMemWrite(int addr, int value) throws WriteFaultException { + int page = addr >>> pageShift; + int entry = (addr&(1<>2; + try { + writePages[page][entry] = value; + } catch(ArrayIndexOutOfBoundsException e) { + if(page < 0 || page >= writePages.length) throw new WriteFaultException(addr); + throw e; // should never happen + } catch(NullPointerException e) { + throw new WriteFaultException(addr); + } + } + + /** Created a new non-empty writable page at page number page */ + private final int[] initPage(int page) { return initPage(page,false); } + /** Created a new non-empty page at page number page. If ro is set the page will be read-only */ + private final int[] initPage(int page, boolean ro) { + int[] buf = new int[(1<>>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 != EXITED) throw new IllegalStateException("exitStatus() called in an inappropriate state"); + return exitStatus; + } + + private int addStringArray(String[] strings, int topAddr) throws FaultException { + int count = strings.length; + int total = 0; /* null last table entry */ + for(int i=0;iindex in the _user_info table to word + * The user_info table is a chunk of memory in the program's memory defined by the + * symbol "user_info". The compiler/interpreter automatically determine the size + * 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)); + try { + memWrite(userInfoBase()+index*4,word); + } catch(FaultException e) { throw new RuntimeException(e.toString()); } + } + + /** Returns the word in the _user_info table entry index + @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)); + try { + return memRead(userInfoBase()+index*4); + } catch(FaultException e) { throw new RuntimeException(e.toString()); } + } + + /** Calls _execute() (subclass's execute()) and catches exceptions */ + private void __execute() { + try { + _execute(); + } catch(FaultException e) { + if(STDERR_DIAG) e.printStackTrace(); + exit(128+11,true); // SIGSEGV + exitException = e; + } catch(ExecutionException e) { + if(STDERR_DIAG) e.printStackTrace(); + exit(128+4,true); // SIGILL + exitException = e; + } + } + + /** Executes the process until the PAUSE syscall is invoked or the process exits. Returns true if the process exited. */ + public final boolean execute() { + if(state != PAUSED) throw new IllegalStateException("execute() called in inappropriate state"); + if(startTime == 0) startTime = System.currentTimeMillis(); + state = RUNNING; + __execute(); + if(state != PAUSED && state != EXITED && state != EXECED) + throw new IllegalStateException("execute() ended up in an inappropriate state (" + state + ")"); + return state != PAUSED; + } + + 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. + If the process executes the PAUSE syscall execution will be paused for 500ms and a warning will be displayed */ + public final int run(String[] args, String[] env) { + start(args,env); + for(;;) { + if(execute()) break; + if(STDERR_DIAG) System.err.println("WARNING: Pause requested while executing run()"); + } + if(state == EXECED && STDERR_DIAG) System.err.println("WARNING: Process exec()ed while being run under run()"); + return state == EXITED ? exitStatus() : 0; + } + + public final void start() { start(null); } + public final void start(String[] args) { start(args,null); } + + /** Initializes the process and prepairs it to be executed with execute() */ + public final void start(String[] args, String[] environ) { + 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 = top = writePages.length*(1< 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< 7) throw new IllegalArgumentException("args.length > 7"); + CPUState state = new CPUState(); + getCPUState(state); + + int sp = state.r[SP]; + int[] ia = new int[args.length]; + for(int i=0;iaddr in the process setting A0-A3 and S0-S3 to the given arguments + and returns the contents of V1 when the the pause syscall is invoked */ + //public final int call(int addr, int a0, int a1, int a2, int a3, int s0, int s1, int s2, int s3) { + public final int call(int addr, int a0, int[] rest) throws CallException { + if(rest.length > 7) throw new IllegalArgumentException("rest.length > 7"); + if(state != PAUSED && state != CALLJAVA) throw new IllegalStateException("call() called in inappropriate state"); + int oldState = state; + 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; + switch(rest.length) { + case 7: cpustate.r[S3] = rest[6]; + case 6: cpustate.r[S2] = rest[5]; + case 5: cpustate.r[S1] = rest[4]; + case 4: cpustate.r[S0] = rest[3]; + case 3: cpustate.r[A3] = rest[2]; + case 2: cpustate.r[A2] = rest[1]; + case 1: cpustate.r[A1] = rest[0]; + } + cpustate.pc = addr; + + state = RUNNING; + + setCPUState(cpustate); + __execute(); + getCPUState(cpustate); + setCPUState(saved); + + if(state != PAUSED) throw new CallException("Process exit()ed while servicing a call() request"); + state = oldState; + + return cpustate.r[V1]; + } + + /** Allocated an entry in the FileDescriptor table for fd and returns the number. + Returns -1 if the table is full. This can be used by subclasses to use custom file + descriptors */ + public final int addFD(FD fd) { + if(state == EXITED || state == EXECED) throw new IllegalStateException("addFD called in inappropriate state"); + int i; + for(i=0;ifdn 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; + } + + /** Duplicates the file descriptor fdn and returns the new fs */ + public final int dupFD(int fdn) { + int i; + if(fdn < 0 || fdn >= OPEN_MAX) return -1; + if(fds[fdn] == null) return -1; + for(i=0;i= 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,sf,data); } }; + } + + FStat hostFStat(File f, Seekable.File sf, Object data) { return new HostFStat(f,sf); } + + FD hostFSDirFD(File f, Object data) { return null; } + + FD _open(String path, int flags, int mode) throws ErrnoException { + return hostFSOpen(new File(path),flags,mode,null); + } + + /** The open syscall */ + private int sys_open(int addr, int flags, int mode) throws ErrnoException, FaultException { + 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; } + return fdn; + } + + /** The write syscall */ + + private int sys_write(int fdn, int addr, int count) 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); + copyin(addr,buf,count); + try { + return fds[fdn].write(buf,0,count); + } catch(ErrnoException e) { + if(e.errno == EPIPE) sys_exit(128+13); + throw e; + } + } + + /** The read syscall */ + private int sys_read(int fdn, int addr, int count) 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].read(buf,0,count); + 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) { + return closeFD(fdn) ? 0 : -EBADFD; + } + + + /** The seek syscall */ + private int sys_lseek(int fdn, int offset, int whence) throws ErrnoException { + if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD; + if(fds[fdn] == null) return -EBADFD; + if(whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END) return -EINVAL; + int n = fds[fdn].seek(offset,whence); + return n < 0 ? -ESPIPE : n; + } + + /** The stat/fstat syscall helper */ + 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,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 + memWrite(addr+28,fs.mtime()); // st_mtime + // memWrite(addr+32,0) // st_spare2 + memWrite(addr+36,fs.ctime()); // st_ctime + // memWrite(addr+40,0) // st_spare3 + memWrite(addr+44,fs.blksize()); // st_bklsize; + memWrite(addr+48,fs.blocks()); // st_blocks + // memWrite(addr+52,0) // st_spare4[0] + // memWrite(addr+56,0) // st_spare4[1] + return 0; + } + + /** The fstat syscall */ + private int sys_fstat(int fdn, int addr) throws FaultException { + if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD; + if(fds[fdn] == null) return -EBADFD; + return stat(fds[fdn].fstat(),addr); + } + + /* + struct timeval { + long tv_sec; + long tv_usec; + }; + */ + private int sys_gettimeofday(int timevalAddr, int timezoneAddr) throws FaultException { + long now = System.currentTimeMillis(); + int tv_sec = (int)(now / 1000); + int tv_usec = (int)((now%1000)*1000); + memWrite(timevalAddr+0,tv_sec); + memWrite(timevalAddr+4,tv_usec); + return 0; + } + + private int sys_sleep(int sec) { + if(sec < 0) sec = Integer.MAX_VALUE; + try { + Thread.sleep((long)sec*1000); + return 0; + } catch(InterruptedException e) { + return -1; + } + } + + /* + #define _CLOCKS_PER_SEC_ 1000 + #define _CLOCK_T_ unsigned long + struct tms { + clock_t tms_utime; + clock_t tms_stime; + clock_t tms_cutime; + clock_t tms_cstime; + };*/ + + private int sys_times(int tms) { + long now = System.currentTimeMillis(); + int userTime = (int)((now - startTime)/16); + int sysTime = (int)((now - startTime)/16); + + try { + if(tms!=0) { + memWrite(tms+0,userTime); + memWrite(tms+4,sysTime); + memWrite(tms+8,userTime); + memWrite(tms+12,sysTime); + } + } catch(FaultException e) { + return -EFAULT; + } + return (int)now; + } + + private int sys_sysconf(int n) { + switch(n) { + case _SC_CLK_TCK: return 1000; + case _SC_PAGESIZE: return writePages.length == 1 ? 4096 : (1<incr is how much to increase the break by */ + public final int sbrk(int incr) { + if(incr < 0) return -ENOMEM; + if(incr==0) return heapEnd; + incr = (incr+3)&~3; + int oldEnd = heapEnd; + int newEnd = oldEnd + incr; + if(newEnd >= stackBottom) return -ENOMEM; + + if(writePages.length > 1) { + int pageMask = (1<>> 2; + int start = (oldEnd + pageMask) >>> pageShift; + int end = (newEnd + pageMask) >>> pageShift; + try { + for(int i=start;i= OPEN_MAX) return -EBADFD; + if(fds[fdn] == null) return -EBADFD; + FD fd = fds[fdn]; + + switch(cmd) { + case F_DUPFD: + if(arg < 0 || arg >= OPEN_MAX) return -EINVAL; + 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, int e, int f) { + try { + 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 ex) { + ex.printStackTrace(); + throw new Error("Internal Error in _syscall()"); + } + } + + protected 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); + case SYS_pause: return sys_pause(); + case SYS_write: return sys_write(a,b,c); + case SYS_fstat: return sys_fstat(a,b); + case SYS_sbrk: return sbrk(a); + case SYS_open: return sys_open(a,b,c); + 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); + case SYS_sleep: return sys_sleep(a); + case SYS_times: return sys_times(a); + 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; + + case SYS_kill: + case SYS_fork: + case SYS_pipe: + case SYS_dup2: + case SYS_waitpid: + case SYS_stat: + case SYS_mkdir: + case SYS_getcwd: + case SYS_chdir: + if(STDERR_DIAG) System.err.println("Attempted to use a UnixRuntime syscall in Runtime (" + syscall + ")"); + return -ENOSYS; + default: + if(STDERR_DIAG) System.err.println("Attempted to use unknown syscall: " + syscall); + return -ENOSYS; + } + } + + 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; } } + public int malloc(int size) { try { return call("malloc",size); } catch(CallException e) { return 0; } } + public void free(int p) { try { if(p!=0) call("free",p); } catch(CallException e) { /*noop*/ } } + + /** Helper function to create a cstring in main memory */ + public int strdup(String s) { + byte[] a; + if(s == null) s = "(null)"; + byte[] a2 = getBytes(s); + a = new byte[a2.length+1]; + System.arraycopy(a2,0,a,0,a2.length); + int addr = malloc(a.length); + if(addr == 0) return 0; + try { + copyout(a,addr,a.length); + } catch(FaultException e) { + free(addr); + return 0; + } + 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); + switch(addr&3) { + case 0: if(((word>>>24)&0xff)==0) return sb.toString(); sb.append((char)((word>>>24)&0xff)); addr++; + case 1: if(((word>>>16)&0xff)==0) return sb.toString(); sb.append((char)((word>>>16)&0xff)); addr++; + case 2: if(((word>>> 8)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 8)&0xff)); addr++; + case 3: if(((word>>> 0)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 0)&0xff)); addr++; + } + } + } + + /** 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); } + /** Write. Should return the number of bytes written or throw an IOException on error */ + public int write(byte[] a, int off, int length) throws ErrnoException { throw new ErrnoException(EBADFD); } + + /** Seek in the filedescriptor. Whence is SEEK_SET, SEEK_CUR, or SEEK_END. Should return -1 on error or the new position. */ + public int seek(int n, int whence) throws ErrnoException { return -1; } + + public int getdents(byte[] a, int off, int length) throws ErrnoException { throw new ErrnoException(EBADFD); } + + /** Return a Seekable object representing this file descriptor (can be read only) + This is required for exec() */ + Seekable seekable() { return null; } + + private FStat cachedFStat = null; + public final FStat fstat() { + if(cachedFStat == null) cachedFStat = _fstat(); + return cachedFStat; + } + + protected abstract FStat _fstat(); + public abstract int flags(); + + /** Closes the fd */ + public final void close() { if(--refCount==0) _close(); } + protected void _close() { /* noop*/ } + + FD dup() { refCount++; return this; } + } + + /** FileDescriptor class for normal files */ + public abstract static class SeekableFD extends FD { + private final int flags; + private final Seekable data; + + SeekableFD(Seekable data, int flags) { this.data = data; this.flags = flags; } + + protected abstract FStat _fstat(); + public int flags() { return flags; } + + Seekable seekable() { return data; } + + public int seek(int n, int whence) throws ErrnoException { + try { + switch(whence) { + case SEEK_SET: break; + case SEEK_CUR: n += data.pos(); break; + case SEEK_END: n += data.length(); break; + default: return -1; + } + data.seek(n); + return n; + } catch(IOException e) { + throw new ErrnoException(ESPIPE); + } + } + + public int write(byte[] a, int off, int length) throws ErrnoException { + if((flags&3) == RD_ONLY) throw new ErrnoException(EBADFD); + // NOTE: There is race condition here but we can't fix it in pure java + if((flags&O_APPEND) != 0) seek(0,SEEK_END); + try { + return data.write(a,off,length); + } catch(IOException e) { + throw new ErrnoException(EIO); + } + } + + public int read(byte[] a, int off, int length) throws ErrnoException { + if((flags&3) == WR_ONLY) throw new ErrnoException(EBADFD); + try { + int n = data.read(a,off,length); + return n < 0 ? 0 : n; + } catch(IOException e) { + throw new ErrnoException(EIO); + } + } + + protected void _close() { try { data.close(); } catch(IOException e) { /*ignore*/ } } + } + + 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 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 FStat _fstat() { return new SocketFStat(); } + } + + 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 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 0) buf[n++] = prev | (int)(l>>>(56-left)); + if(n < words) buf[n++] = (int) (l >>> (24-left)); + left = (left + 8) & 0x1f; + prev = (int)(l << left); + } + return buf; + } + + static byte[] getBytes(String s) { + try { + return s.getBytes("UTF-8"); + } catch(UnsupportedEncodingException e) { + return null; // should never happen + } + } + + static byte[] getNullTerminatedBytes(String s) { + byte[] buf1 = getBytes(s); + byte[] buf2 = new byte[buf1.length+1]; + System.arraycopy(buf1,0,buf2,0,buf1.length); + return buf2; + } + + final static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); } + final static int min(int a, int b) { return a < b ? a : b; } + final static int max(int a, int b) { return a > b ? a : b; } +} diff --git a/src/main/java/org/ibex/nestedvm/UnixRuntime.java b/src/main/java/org/ibex/nestedvm/UnixRuntime.java new file mode 100644 index 0000000..eaf3466 --- /dev/null +++ b/src/main/java/org/ibex/nestedvm/UnixRuntime.java @@ -0,0 +1,2512 @@ +// Copyright 2000-2005 the Contributors, as shown in the revision logs. +// Licensed under the Apache Public Source License 2.0 ("the License"). +// You may not use this file except in compliance with the License. + +package org.ibex.nestedvm; + +import org.ibex.nestedvm.util.*; +import java.io.*; +import java.util.*; +import java.net.*; +import java.nio.file.*; +import java.lang.reflect.*; // For lazily linked RuntimeCompiler + +// FEATURE: vfork + +public abstract class UnixRuntime extends Runtime implements Cloneable { + /** The pid of this "process" */ + private int pid; + private UnixRuntime parent; + public final int getPid() { + return pid; + } + + private static final GlobalState defaultGS = new GlobalState(); + private GlobalState gs; + public void setGlobalState(GlobalState gs) { + if(state != STOPPED) throw new IllegalStateException("can't change GlobalState when running"); + if(gs == null) throw new NullPointerException("gs is null"); + this.gs = gs; + } + + /** proceses' current working directory - absolute path WITHOUT leading slash + "" = root, "bin" = /bin "usr/bin" = /usr/bin */ + private String cwd; + + /** The runtime that should be run next when in state == EXECED */ + private UnixRuntime execedRuntime; + + private Object children; // used only for synchronizatin + private Vector activeChildren; + private Vector exitedChildren; + + protected UnixRuntime(int pageSize, int totalPages) { + this(pageSize,totalPages,false); + } + + protected UnixRuntime(int pageSize, int totalPages, boolean exec) { + super(pageSize,totalPages,exec); + + if(!exec) { + gs = defaultGS; + String userdir = Platform.getProperty("user.dir"); + cwd = userdir == null ? null : gs.mapHostPath(userdir); + if(cwd == null) cwd = "/"; + cwd = cwd.substring(1); + } + } + + private static String posixTZ() { + StringBuffer sb = new StringBuffer(); + TimeZone zone = TimeZone.getDefault(); + int off = zone.getRawOffset() / 1000; + sb.append(Platform.timeZoneGetDisplayName(zone,false,false)); + if(off > 0) sb.append("-"); + else off = -off; + sb.append(off/3600); + off = off%3600; + if(off > 0) sb.append(":").append(off/60); + off=off%60; + if(off > 0) sb.append(":").append(off); + if(zone.useDaylightTime()) + sb.append(Platform.timeZoneGetDisplayName(zone,true,false)); + return sb.toString(); + } + + private static boolean envHas(String key,String[] environ) { + for(int i=0; i= gs.tasks.length)) return -ECHILD; + if(children == null) return blocking ? -ECHILD : 0; + + UnixRuntime done = null; + + synchronized(children) { + for(;;) { + if(pid == -1) { + if(exitedChildren.size() > 0) { + done = (UnixRuntime)exitedChildren.elementAt(exitedChildren.size() - 1); + exitedChildren.removeElementAt(exitedChildren.size() - 1); + } + } else if(pid > 0) { + if(pid >= gs.tasks.length) return -ECHILD; + UnixRuntime t = gs.tasks[pid]; + if(t.parent != this) return -ECHILD; + if(t.state == EXITED) { + if(!exitedChildren.removeElement(t)) throw new Error("should never happen"); + done = t; + } + } else { + // process group stuff, EINVAL returned above + throw new Error("should never happen"); + } + 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(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.removeAllElements(); + for(Enumeration e = activeChildren.elements(); e.hasMoreElements(); ) { + UnixRuntime child = (UnixRuntime) e.nextElement(); + child.parent = null; + } + activeChildren.removeAllElements(); + } + + UnixRuntime _parent = parent; + if(_parent == null) { + gs.tasks[pid] = null; + } else { + synchronized(_parent.children) { + if(parent == null) { + gs.tasks[pid] = null; + } else { + if(!parent.activeChildren.removeElement(this)) throw new Error("should never happen _exited: pid: " + pid); + parent.exitedChildren.addElement(this); + parent.children.notify(); + } + } + } + } + + 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() { + final UnixRuntime r; + + try { + r = (UnixRuntime) clone(); + } catch(Exception e) { + e.printStackTrace(); + return -ENOMEM; + } + + r.parent = this; + + try { + r._started(); + } catch(ProcessTableFullExn e) { + return -ENOMEM; + } + + //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.addElement(r); + + CPUState state = new CPUState(); + getCPUState(state); + state.r[V0] = 0; // return 0 to child + state.pc += 4; // skip over syscall instruction + r.setCPUState(state); + r.state = PAUSED; + + 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 c,String[] argv, String[] envp) { + try { + UnixRuntime r = (UnixRuntime) c.getDeclaredConstructor(new Class[] {Boolean.TYPE}).newInstance(new Object[] {Boolean.TRUE}); + return exec(r,argv,envp); + } catch(Exception e) { + e.printStackTrace(); + return -ENOEXEC; + } + } + + private int exec(UnixRuntime r, String[] argv, String[] envp) { + //System.err.println("Execing " + r); + for(int i=0; i= OPEN_MAX) return -EBADFD; + if(newd < 0 || newd >= OPEN_MAX) return -EBADFD; + if(fds[oldd] == null) return -EBADFD; + if(fds[newd] != null) fds[newd].close(); + fds[newd] = fds[oldd].dup(); + return 0; + } + + 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_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_link(int cstring1, int cstring2) throws FaultException, ErrnoException { + gs.link(this,normalizePath(cstring(cstring1)), normalizePath(cstring(cstring2))); + return 0; + } + + private int sys_rmdir(int ptrPathName) throws ErrnoException, ReadFaultException { + gs.rmdir(this, normalizePath(cstring(ptrPathName))); + return 0; + } + + private int sys_getcwd(int addr, int size) throws FaultException, ErrnoException { + byte[] b = getBytes(cwd); + if(size == 0) return -EINVAL; + 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) throws ErrnoException, FaultException { + String path = normalizePath(cstring(addr)); + FStat st = gs.stat(this,path); + if(st == null) return -ENOENT; + if(st.type() != FStat.S_IFDIR) return -ENOTDIR; + cwd = path; + return 0; + } + + 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; + } + + void _preCloseFD(FD fd) { + // release all fcntl locks on this file + Seekable s = fd.seekable(); + if (s == null) return; + + try { + for (int i=0; i < gs.locks.length; i++) { + Seekable.Lock l = gs.locks[i]; + if (l == null) continue; + if (s.equals(l.seekable()) && l.getOwner() == this) { + l.release(); + gs.locks[i] = null; + } + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + void _postCloseFD(FD fd) { + if (fd.isMarkedForDeleteOnClose()) { + try { + gs.unlink(this, fd.getNormalizedPath()); + } + catch (Throwable t) {} + } + } + + /** Implements the F_GETLK and F_SETLK cases of fcntl syscall. + * If l_start = 0 and l_len = 0 the lock refers to the entire file. + * Uses GlobalState to ensure locking across processes in the same JVM. + struct flock { + short l_type; // lock type: F_UNLCK, F_RDLCK, F_WRLCK + short l_whence; // type of l_start: SEEK_SET, SEEK_CUR, SEEK_END + long l_start; // starting offset, bytes + long l_len; // len = 0 means until EOF + short l_pid; // lock owner + short l_xxx; // padding + }; + */ + private int sys_fcntl_lock(int fdn, int cmd, int arg) throws FaultException { + if (cmd != F_GETLK && cmd != F_SETLK) return sys_fcntl(fdn, cmd, arg); + + if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD; + if(fds[fdn] == null) return -EBADFD; + FD fd = fds[fdn]; + + if (arg == 0) return -EINVAL; + int word = memRead(arg); + int l_start = memRead(arg+4); + int l_len = memRead(arg+8); + int l_type = word>>16; + int l_whence = word&0x00ff; + + Seekable.Lock[] locks = gs.locks; + Seekable s = fd.seekable(); + if (s == null) return -EINVAL; + + try { + + switch (l_whence) { + case SEEK_SET: + break; + case SEEK_CUR: + l_start += s.pos(); + break; + case SEEK_END: + l_start += s.length(); + break; + default: + return -1; + } + + if (cmd == F_GETLK) { + // The simple Java file locking below will happily return + // a lock that overlaps one already held by the JVM. Thus + // we must check over all the locks held by other Runtimes + for (int i=0; i < locks.length; i++) { + if (locks[i] == null || !s.equals(locks[i].seekable())) + continue; + if (!locks[i].overlaps(l_start, l_len)) + continue; + if (locks[i].getOwner() == this) + continue; + if (locks[i].isShared() && l_type == F_RDLCK) + continue; + + // overlapping lock held by another process + return 0; + } + + // check if an area is lockable by attempting to obtain a lock + Seekable.Lock lock = s.lock(l_start, l_len, l_type == F_RDLCK); + + if (lock != null) { // no lock exists + memWrite(arg, SEEK_SET|(F_UNLCK<<16)); + lock.release(); + } + + return 0; + } + + // now processing F_SETLK + if (cmd != F_SETLK) return -EINVAL; + + if (l_type == F_UNLCK) { + // release all locks that fall within the boundaries given + for (int i=0; i < locks.length; i++) { + if (locks[i] == null || !s.equals(locks[i].seekable())) + continue; + if (locks[i].getOwner() != this) continue; + + int pos = (int)locks[i].position(); + if (pos < l_start) continue; + if (l_start != 0 && l_len != 0) // start/len 0 means unlock all + if (pos + locks[i].size() > l_start + l_len) + continue; + + locks[i].release(); + locks[i] = null; + } + return 0; + + } else if (l_type == F_RDLCK || l_type == F_WRLCK) { + // first see if a lock already exists + for (int i=0; i < locks.length; i++) { + if (locks[i] == null || !s.equals(locks[i].seekable())) + continue; + + if (locks[i].getOwner() == this) { + // if this Runtime owns an overlapping lock work with it + if (locks[i].contained(l_start, l_len)) { + locks[i].release(); + locks[i] = null; + } else if (locks[i].contains(l_start, l_len)) { + if (locks[i].isShared() == (l_type == F_RDLCK)) { + // return this more general lock + memWrite(arg+4, (int)locks[i].position()); + memWrite(arg+8, (int)locks[i].size()); + return 0; + } else { + locks[i].release(); + locks[i] = null; + } + } + } else { + // if another Runtime has an lock and it is exclusive or + // we want an exclusive lock then fail + if (locks[i].overlaps(l_start, l_len) + && (!locks[i].isShared() || l_type == F_WRLCK)) + return -EAGAIN; + } + } + + // create the lock + Seekable.Lock lock = s.lock(l_start, l_len, l_type == F_RDLCK); + if (lock == null) return -EAGAIN; + lock.setOwner(this); + + int i; + for (i=0; i < locks.length; i++) + if (locks[i] == null) break; + if (i == locks.length) return -ENOLCK; + locks[i] = lock; + return 0; + + } else { + return -EINVAL; + } + + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + static class SocketFD extends FD { + public static final int TYPE_STREAM = 0; + public static final int TYPE_DGRAM = 1; + public static final int LISTEN = 2; + public int type() { + return flags & 1; + } + public boolean listen() { + return (flags & 2) != 0; + } + + int flags; + int options; + + Socket s; + ServerSocket ss; + DatagramSocket ds; + + InetAddress bindAddr; + int bindPort = -1; + InetAddress connectAddr; + int connectPort = -1; + + DatagramPacket dp; + InputStream is; + OutputStream os; + + private static final byte[] EMPTY = new byte[0]; + public SocketFD(int type) { + flags = type; + if(type == TYPE_DGRAM) + dp = new DatagramPacket(EMPTY,0); + } + + public SocketAddress getLocalSocketAddress() { + int type = type(); + if(type == SocketFD.TYPE_STREAM) { + if(listen()) { + return ss.getLocalSocketAddress(); + } else { + return s.getLocalSocketAddress(); + } + } else { + return ds.getLocalSocketAddress(); + } + } + + public void setOptions() { + try { + if(s != null && type() == TYPE_STREAM && !listen()) { + Platform.socketSetKeepAlive(s,(options & SO_KEEPALIVE) != 0); + } + } catch(SocketException e) { + if(STDERR_DIAG) e.printStackTrace(); + } + } + + public void _close() { + try { + if(s != null) s.close(); + if(ss != null) ss.close(); + if(ds != null) ds.close(); + } catch(IOException e) { + /* ignore */ + } + } + + public int read(byte[] a, int off, int length) throws ErrnoException { + if(type() == TYPE_DGRAM) return recvfrom(a,off,length,null,null); + if(is == null) throw new ErrnoException(EPIPE); + try { + int n = is.read(a,off,length); + return n < 0 ? 0 : n; + } catch(IOException e) { + throw new ErrnoException(EIO); + } + } + + public int recvfrom(byte[] a, int off, int length, InetAddress[] sockAddr, int[] port) throws ErrnoException { + if(type() == TYPE_STREAM) return read(a,off,length); + + if(off != 0) throw new IllegalArgumentException("off must be 0"); + dp.setData(a); + dp.setLength(length); + try { + if(ds == null) ds = new DatagramSocket(); + ds.receive(dp); + } catch(IOException e) { + if(STDERR_DIAG) e.printStackTrace(); + throw new ErrnoException(EIO); + } + if(sockAddr != null) { + sockAddr[0] = dp.getAddress(); + port[0] = dp.getPort(); + } + return dp.getLength(); + } + + public int write(byte[] a, int off, int length) throws ErrnoException { + if(type() == TYPE_DGRAM) return sendto(a,off,length,null,-1); + + if(os == null) throw new ErrnoException(EPIPE); + try { + os.write(a,off,length); + return length; + } catch(IOException e) { + throw new ErrnoException(EIO); + } + } + + public int sendto(byte[] a, int off, int length, InetAddress destAddr, int destPort) throws ErrnoException { + if(off != 0) throw new IllegalArgumentException("off must be 0"); + if(type() == TYPE_STREAM) return write(a,off,length); + + if(destAddr == null) { + destAddr = connectAddr; + destPort = connectPort; + + if(destAddr == null) throw new ErrnoException(ENOTCONN); + } + + dp.setAddress(destAddr); + dp.setPort(destPort); + dp.setData(a); + dp.setLength(length); + + try { + if(ds == null) ds = new DatagramSocket(); + ds.send(dp); + } catch(IOException e) { + if(STDERR_DIAG) e.printStackTrace(); + if("Network is unreachable".equals(e.getMessage())) throw new ErrnoException(EHOSTUNREACH); + throw new ErrnoException(EIO); + } + return dp.getLength(); + } + + public int flags() { + return O_RDWR; + } + public FStat _fstat() { + return new SocketFStat(); + } + } + + private int sys_socket(int domain, int type, int proto) { + if(domain != AF_INET || (type != SOCK_STREAM && type != SOCK_DGRAM)) return -EPROTONOSUPPORT; + return addFD(new SocketFD(type == SOCK_STREAM ? SocketFD.TYPE_STREAM : SocketFD.TYPE_DGRAM)); + } + + private SocketFD getSocketFD(int fdn) throws ErrnoException { + if(fdn < 0 || fdn >= OPEN_MAX) throw new ErrnoException(EBADFD); + if(fds[fdn] == null) throw new ErrnoException(EBADFD); + if(!(fds[fdn] instanceof SocketFD)) throw new ErrnoException(ENOTSOCK); + + return (SocketFD) fds[fdn]; + } + + private int sys_connect(int fdn, int addr, int namelen) throws ErrnoException, FaultException { + SocketFD fd = getSocketFD(fdn); + + if(fd.type() == SocketFD.TYPE_STREAM && (fd.s != null || fd.ss != null)) return -EISCONN; + int word1 = memRead(addr); + if( ((word1 >>> 16)&0xff) != AF_INET) return -EAFNOSUPPORT; + int port = word1 & 0xffff; + byte[] ip = new byte[4]; + copyin(addr+4,ip,4); + + InetAddress inetAddr; + try { + inetAddr = Platform.inetAddressFromBytes(ip); + } catch(UnknownHostException e) { + return -EADDRNOTAVAIL; + } + + fd.connectAddr = inetAddr; + fd.connectPort = port; + + try { + switch(fd.type()) { + case SocketFD.TYPE_STREAM: { + Socket s = new Socket(inetAddr,port); + fd.s = s; + fd.setOptions(); + fd.is = s.getInputStream(); + fd.os = s.getOutputStream(); + break; + } + case SocketFD.TYPE_DGRAM: + break; + default: + throw new Error("should never happen"); + } + } catch(IOException e) { + return -ECONNREFUSED; + } + + return 0; + } + + private int sys_resolve_hostname(int chostname, int addr, int sizeAddr) throws FaultException { + String hostname = cstring(chostname); + int size = memRead(sizeAddr); + InetAddress[] inetAddrs; + try { + inetAddrs = InetAddress.getAllByName(hostname); + } catch(UnknownHostException e) { + return HOST_NOT_FOUND; + } + int count = min(size/4,inetAddrs.length); + for(int i=0; i>> 16)&0xff) != AF_INET) return -EAFNOSUPPORT; + int port = word1 & 0xffff; + InetAddress inetAddr = null; + if(memRead(addr+4) != 0) { + byte[] ip = new byte[4]; + copyin(addr+4,ip,4); + + try { + inetAddr = Platform.inetAddressFromBytes(ip); + } catch(UnknownHostException e) { + return -EADDRNOTAVAIL; + } + } + + switch(fd.type()) { + case SocketFD.TYPE_STREAM: { + fd.bindAddr = inetAddr; + fd.bindPort = port; + return 0; + } + case SocketFD.TYPE_DGRAM: { + if(fd.ds != null) fd.ds.close(); + try { + fd.ds = inetAddr != null ? new DatagramSocket(port,inetAddr) : new DatagramSocket(port); + } catch(IOException e) { + return -EADDRINUSE; + } + return 0; + } + default: + throw new Error("should never happen"); + } + } + + private int sys_listen(int fdn, int backlog) throws ErrnoException { + SocketFD fd = getSocketFD(fdn); + if(fd.type() != SocketFD.TYPE_STREAM) return -EOPNOTSUPP; + if(fd.ss != null || fd.s != null) return -EISCONN; + if(fd.bindPort < 0) return -EOPNOTSUPP; + + try { + fd.ss = new ServerSocket(fd.bindPort,backlog,fd.bindAddr); + fd.flags |= SocketFD.LISTEN; + return 0; + } catch(IOException e) { + return -EADDRINUSE; + } + + } + + private int sys_accept(int fdn, int addr, int lenaddr) throws ErrnoException, FaultException { + SocketFD fd = getSocketFD(fdn); + if(fd.type() != SocketFD.TYPE_STREAM) return -EOPNOTSUPP; + if(!fd.listen()) return -EOPNOTSUPP; + + int size = memRead(lenaddr); + + ServerSocket s = fd.ss; + Socket client; + try { + client = s.accept(); + } catch(IOException e) { + return -EIO; + } + + if(size >= 8) { + memWrite(addr,(6 << 24) | (AF_INET << 16) | client.getPort()); + byte[] b = client.getInetAddress().getAddress(); + copyout(b,addr+4,4); + memWrite(lenaddr,8); + } + + SocketFD clientFD = new SocketFD(SocketFD.TYPE_STREAM); + clientFD.s = client; + try { + clientFD.is = client.getInputStream(); + clientFD.os = client.getOutputStream(); + } catch(IOException e) { + return -EIO; + } + int n = addFD(clientFD); + if(n == -1) { + clientFD.close(); + return -ENFILE; + } + return n; + } + + private int sys_shutdown(int fdn, int how) throws ErrnoException { + SocketFD fd = getSocketFD(fdn); + if(fd.type() != SocketFD.TYPE_STREAM || fd.listen()) return -EOPNOTSUPP; + if(fd.s == null) return -ENOTCONN; + + Socket s = fd.s; + + try { + if(how == SHUT_RD || how == SHUT_RDWR) Platform.socketHalfClose(s,false); + if(how == SHUT_WR || how == SHUT_RDWR) Platform.socketHalfClose(s,true); + } catch(IOException e) { + return -EIO; + } + + return 0; + } + + private int sys_sendto(int fdn, int addr, int count, int flags, int destAddr, int socklen) throws ErrnoException,ReadFaultException { + SocketFD fd = getSocketFD(fdn); + if(flags != 0) throw new ErrnoException(EINVAL); + + int word1 = memRead(destAddr); + if( ((word1 >>> 16)&0xff) != AF_INET) return -EAFNOSUPPORT; + int port = word1 & 0xffff; + InetAddress inetAddr; + byte[] ip = new byte[4]; + copyin(destAddr+4,ip,4); + try { + inetAddr = Platform.inetAddressFromBytes(ip); + } catch(UnknownHostException e) { + return -EADDRNOTAVAIL; + } + + count = Math.min(count,MAX_CHUNK); + byte[] buf = byteBuf(count); + copyin(addr,buf,count); + try { + return fd.sendto(buf,0,count,inetAddr,port); + } catch(ErrnoException e) { + if(e.errno == EPIPE) exit(128+13,true); + throw e; + } + } + + private int sys_recvfrom(int fdn, int addr, int count, int flags, int sourceAddr, int socklenAddr) throws ErrnoException, FaultException { + SocketFD fd = getSocketFD(fdn); + if(flags != 0) throw new ErrnoException(EINVAL); + + InetAddress[] inetAddr = sourceAddr == 0 ? null : new InetAddress[1]; + int[] port = sourceAddr == 0 ? null : new int[1]; + + count = Math.min(count,MAX_CHUNK); + byte[] buf = byteBuf(count); + int n = fd.recvfrom(buf,0,count,inetAddr,port); + copyout(buf,addr,n); + + if(sourceAddr != 0) { + memWrite(sourceAddr,(AF_INET << 16) | port[0]); + byte[] ip = inetAddr[0].getAddress(); + copyout(ip,sourceAddr+4,4); + } + + return n; + } + + private int sys_select(int n, int readFDs, int writeFDs, int exceptFDs, int timevalAddr) throws ReadFaultException, ErrnoException { + return -ENOSYS; + } + + private static String hostName() { + try { + return InetAddress.getLocalHost().getHostName(); + } catch(UnknownHostException e) { + return "darkstar"; + } + } + + private int sys_sysctl(int nameaddr, int namelen, int oldp, int oldlenaddr, int newp, int newlen) throws FaultException { + if(newp != 0) return -EPERM; + if(namelen == 0) return -ENOENT; + if(oldp == 0) return 0; + + Object o = null; + switch(memRead(nameaddr)) { + case CTL_KERN: + if(namelen != 2) break; + switch(memRead(nameaddr+4)) { + case KERN_OSTYPE: + o = "NestedVM"; + break; + case KERN_HOSTNAME: + o = hostName(); + break; + case KERN_OSRELEASE: + o = VERSION; + break; + case KERN_VERSION: + o = "NestedVM Kernel Version " + VERSION; + break; + } + break; + case CTL_HW: + if(namelen != 2) break; + switch(memRead(nameaddr+4)) { + case HW_MACHINE: + o = "NestedVM Virtual Machine"; + break; + } + break; + } + if(o == null) return -ENOENT; + int len = memRead(oldlenaddr); + if(o instanceof String) { + byte[] b = getNullTerminatedBytes((String)o); + if(len < b.length) return -ENOMEM; + len = b.length; + copyout(b,oldp,len); + memWrite(oldlenaddr,len); + } else if(o instanceof Integer) { + if(len < 4) return -ENOMEM; + memWrite(oldp,((Integer)o).intValue()); + } else { + throw new Error("should never happen"); + } + return 0; + } + + public static final class GlobalState { + Hashtable execCache = new Hashtable(); + + final UnixRuntime[] tasks; + int nextPID = 1; + + /** Table of all current file locks held by this process. */ + Seekable.Lock[] locks = new Seekable.Lock[16]; + + private MP[] mps = new MP[0]; + private FS root; + + public GlobalState() { + this(255); + } + + public GlobalState(int maxProcs) { + this(maxProcs,true); + } + + public GlobalState(int maxProcs, boolean defaultMounts) { + tasks = new UnixRuntime[maxProcs+1]; + if(defaultMounts) { + File root = null; + if(Platform.getProperty("nestedvm.root") != null) { + root = new File(Platform.getProperty("nestedvm.root")); + if(!root.isDirectory()) throw new IllegalArgumentException("nestedvm.root is not a directory"); + } else { + String cwd = Platform.getProperty("user.dir"); + root = Platform.getRoot(new File(cwd != null ? cwd : ".")); + } + + addMount("/",new HostFS(root)); + + if(Platform.getProperty("nestedvm.root") == null) { + File[] roots = Platform.listRoots(); + for(int i=0; i=0; i--) { + FS fs = i == mps.length ? root : mps[i].fs; + String path = i == mps.length ? "" : mps[i].path; + if(!(fs instanceof HostFS)) continue; + File fsroot = ((HostFS)fs).getRoot(); + if(!fsroot.isAbsolute()) fsroot = new File(fsroot.getAbsolutePath()); + if(f.getPath().startsWith(fsroot.getPath())) { + char sep = File.separatorChar; + String child = f.getPath().substring(fsroot.getPath().length()); + if(sep != '/') { + char[] child_ = child.toCharArray(); + for(int j=0; j 0) outp--; + while(outp > 0 && out[outp] != '/') outp--; + //System.err.println("After ..: " + new String(out,0,outp)); + continue; + } + // Just read a /.[^.] or /..[^/$] + inp++; + out[outp++] = '/'; + out[outp++] = '.'; + } + if(outp > 0 && out[outp-1] == '/') outp--; + //System.err.println("normalize: " + path + " -> " + new String(out,0,outp) + " (cwd: " + cwd + ")"); + int outStart = out[0] == '/' ? 1 : 0; + return new String(out,outStart,outp - outStart); + } + + 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); + } + + public static class HostFS extends FS { + InodeCache inodes = new InodeCache(4000); + protected File root; + public File getRoot() { + return root; + } + + protected 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); + } + + 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 abstract int parentInode(); + protected abstract int myInode(); + public int flags() { + return O_RDONLY; + } + + 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 abstract 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; + } + public abstract int inode(); + } + + private abstract class DevDirFD extends DirFD { + public int myDev() { + return devno; + } + } + + private FD devZeroFD = new FD() { + public int read(byte[] a, int off, int length) { + /*Arrays.fill(a,off,off+length,(byte)0);*/ + for(int i=off; i= OPEN_MAX) return null; + if(r.fds[n] == null) return null; + return r.fds[n].dup(); + } + if(path.equals("fd")) { + int count=0; + for(int i=0; i= 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 inode() { + return FD_INODE; + } + public int dev() { + return devno; + } + public int type() { + return S_IFDIR; + } + public int mode() { + return 0444; + } + }; + if(path.equals("")) return new FStat() { + public int inode() { + return ROOT_INODE; + } + public int dev() { + return devno; + } + public int type() { + return S_IFDIR; + } + public int mode() { + return 0444; + } + }; + return null; + } + + public void mkdir(UnixRuntime r, String path, int mode) throws ErrnoException { + throw new ErrnoException(EROFS); + } + public void rmdir(UnixRuntime r, String path) throws ErrnoException { + throw new ErrnoException(EROFS); + } + public void unlink(UnixRuntime r, String path) throws ErrnoException { + throw new ErrnoException(EROFS); + } + public void link(UnixRuntime r, String oldpath, String newpath) throws ErrnoException { + throw new ErrnoException(EROFS); + } + } + + + public static class ResourceFS extends FS { + final InodeCache inodes = new InodeCache(500); + + public FStat lstat(UnixRuntime r, String path) throws ErrnoException { + return stat(r,path); + } + 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); + } + public void rmdir(UnixRuntime r, String path) throws ErrnoException { + throw new ErrnoException(EROFS); + } + public void link(UnixRuntime r, String oldpath, String newpath) throws ErrnoException { + throw new ErrnoException(EROFS); + } + + FStat connFStat(final URLConnection conn) { + return new FStat() { + public int type() { + return S_IFREG; + } + public int nlink() { + return 1; + } + public int mode() { + return 0444; + } + public int size() { + return conn.getContentLength(); + } + public int mtime() { + return (int)(conn.getDate() / 1000); + } + public int inode() { + return inodes.get(conn.getURL().toString()); + } + public int dev() { + return devno; + } + }; + } + + public FStat stat(UnixRuntime r, String path) throws ErrnoException { + URL url = r.getClass().getResource("/" + path); + if(url == null) return null; + try { + return connFStat(url.openConnection()); + } catch(IOException e) { + throw new ErrnoException(EIO); + } + } + + public FD open(UnixRuntime r, String path, int flags, int mode) throws ErrnoException { + if((flags & ~3) != 0) { + if(STDERR_DIAG) + System.err.println("WARNING: Unsupported flags passed to ResourceFS.open(\"" + path + "\"): " + toHex(flags & ~3)); + throw new ErrnoException(ENOTSUP); + } + if((flags&3) != RD_ONLY) throw new ErrnoException(EROFS); + URL url = r.getClass().getResource("/" + path); + if(url == null) return null; + try { + final URLConnection conn = url.openConnection(); + Seekable.InputStream si = new Seekable.InputStream(conn.getInputStream()); + return new SeekableFD(si,flags) { + protected FStat _fstat() { + return connFStat(conn); + } + }; + } 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); + } + } + } +} diff --git a/src/main/java/org/ibex/nestedvm/UsermodeConstants.java b/src/main/java/org/ibex/nestedvm/UsermodeConstants.java new file mode 100644 index 0000000..0385965 --- /dev/null +++ b/src/main/java/org/ibex/nestedvm/UsermodeConstants.java @@ -0,0 +1,480 @@ +// THIS FILE IS AUTOGENERATED! DO NOT EDIT! +// run "make rebuild-constants" if it needs to be updated + +package org.ibex.nestedvm; +public interface UsermodeConstants { + public static final int SYS_null = 0; + public static final int SYS_exit = 1; + public static final int SYS_pause = 2; + public static final int SYS_open = 3; + public static final int SYS_close = 4; + public static final int SYS_read = 5; + public static final int SYS_write = 6; + public static final int SYS_sbrk = 7; + public static final int SYS_fstat = 8; + public static final int SYS_lseek = 10; + public static final int SYS_kill = 11; + public static final int SYS_getpid = 12; + public static final int SYS_calljava = 13; + public static final int SYS_stat = 14; + public static final int SYS_gettimeofday = 15; + public static final int SYS_sleep = 16; + public static final int SYS_times = 17; + public static final int SYS_mkdir = 18; + public static final int SYS_getpagesize = 19; + public static final int SYS_unlink = 20; + public static final int SYS_utime = 21; + public static final int SYS_chdir = 22; + public static final int SYS_pipe = 23; + public static final int SYS_dup2 = 24; + public static final int SYS_fork = 25; + public static final int SYS_waitpid = 26; + public static final int SYS_getcwd = 27; + public static final int SYS_exec = 28; + public static final int SYS_fcntl = 29; + public static final int SYS_rmdir = 30; + public static final int SYS_sysconf = 31; + public static final int SYS_readlink = 32; + public static final int SYS_lstat = 33; + public static final int SYS_symlink = 34; + public static final int SYS_link = 35; + public static final int SYS_getdents = 36; + public static final int SYS_memcpy = 37; + public static final int SYS_memset = 38; + public static final int SYS_dup = 39; + public static final int SYS_vfork = 40; + public static final int SYS_chroot = 41; + public static final int SYS_mknod = 42; + public static final int SYS_lchown = 43; + public static final int SYS_ftruncate = 44; + public static final int SYS_usleep = 45; + public static final int SYS_getppid = 46; + public static final int SYS_mkfifo = 47; + public static final int SYS_klogctl = 51; + public static final int SYS_realpath = 52; + public static final int SYS_sysctl = 53; + public static final int SYS_setpriority = 54; + public static final int SYS_getpriority = 55; + public static final int SYS_socket = 56; + public static final int SYS_connect = 57; + public static final int SYS_resolve_hostname = 58; + public static final int SYS_accept = 59; + public static final int SYS_setsockopt = 60; + public static final int SYS_getsockopt = 61; + public static final int SYS_listen = 62; + public static final int SYS_bind = 63; + public static final int SYS_shutdown = 64; + public static final int SYS_sendto = 65; + public static final int SYS_recvfrom = 66; + public static final int SYS_select = 67; + public static final int SYS_getuid = 68; + public static final int SYS_getgid = 69; + public static final int SYS_geteuid = 70; + public static final int SYS_getegid = 71; + public static final int SYS_getgroups = 72; + public static final int SYS_umask = 73; + public static final int SYS_chmod = 74; + public static final int SYS_fchmod = 75; + public static final int SYS_chown = 76; + public static final int SYS_fchown = 77; + public static final int SYS_access = 78; + public static final int SYS_alarm = 79; + public static final int SYS_setuid = 80; + public static final int SYS_setgid = 81; + public static final int SYS_send = 82; + public static final int SYS_recv = 83; + public static final int SYS_getsockname = 84; + public static final int SYS_getpeername = 85; + public static final int SYS_seteuid = 86; + public static final int SYS_setegid = 87; + public static final int SYS_setgroups = 88; + public static final int SYS_resolve_ip = 89; + public static final int SYS_setsid = 90; + public static final int SYS_fsync = 91; + public static final int AF_UNIX = 1; + public static final int AF_INET = 2; + public static final int SOCK_STREAM = 1; + public static final int SOCK_DGRAM = 2; + public static final int HOST_NOT_FOUND = 1; + public static final int TRY_AGAIN = 2; + public static final int NO_RECOVERY = 3; + public static final int NO_DATA = 4; + public static final int SOL_SOCKET = 0xffff; + public static final int SO_REUSEADDR = 0x0004; + public static final int SO_KEEPALIVE = 0x0008; + public static final int SO_BROADCAST = 0x0020; + public static final int SO_TYPE = 0x1008; + public static final int SHUT_RD = 0; + public static final int SHUT_WR = 1; + public static final int SHUT_RDWR = 2; + public static final int INADDR_ANY = 0; + public static final int INADDR_LOOPBACK = 0x7f000001; + public static final int INADDR_BROADCAST = 0xffffffff; + public static final int EPERM = 1; /* Not super-user */ + public static final int ENOENT = 2; /* No such file or directory */ + public static final int ESRCH = 3; /* No such process */ + public static final int EINTR = 4; /* Interrupted system call */ + public static final int EIO = 5; /* I/O error */ + public static final int ENXIO = 6; /* No such device or address */ + public static final int E2BIG = 7; /* Arg list too long */ + public static final int ENOEXEC = 8; /* Exec format error */ + public static final int EBADF = 9; /* Bad file number */ + public static final int ECHILD = 10; /* No children */ + public static final int EAGAIN = 11; /* No more processes */ + public static final int ENOMEM = 12; /* Not enough core */ + public static final int EACCES = 13; /* Permission denied */ + public static final int EFAULT = 14; /* Bad address */ + public static final int ENOTBLK = 15; /* Block device required */ + public static final int EBUSY = 16; /* Mount device busy */ + public static final int EEXIST = 17; /* File exists */ + public static final int EXDEV = 18; /* Cross-device link */ + public static final int ENODEV = 19; /* No such device */ + public static final int ENOTDIR = 20; /* Not a directory */ + public static final int EISDIR = 21; /* Is a directory */ + public static final int EINVAL = 22; /* Invalid argument */ + public static final int ENFILE = 23; /* Too many open files in system */ + public static final int EMFILE = 24; /* Too many open files */ + public static final int ENOTTY = 25; /* Not a typewriter */ + public static final int ETXTBSY = 26; /* Text file busy */ + public static final int EFBIG = 27; /* File too large */ + public static final int ENOSPC = 28; /* No space left on device */ + public static final int ESPIPE = 29; /* Illegal seek */ + public static final int EROFS = 30; /* Read only file system */ + public static final int EMLINK = 31; /* Too many links */ + public static final int EPIPE = 32; /* Broken pipe */ + public static final int EDOM = 33; /* Math arg out of domain of func */ + public static final int ERANGE = 34; /* Math result not representable */ + public static final int ENOMSG = 35; /* No message of desired type */ + public static final int EIDRM = 36; /* Identifier removed */ + public static final int ECHRNG = 37; /* Channel number out of range */ + public static final int EL2NSYNC = 38; /* Level 2 not synchronized */ + public static final int EL3HLT = 39; /* Level 3 halted */ + public static final int EL3RST = 40; /* Level 3 reset */ + public static final int ELNRNG = 41; /* Link number out of range */ + public static final int EUNATCH = 42; /* Protocol driver not attached */ + public static final int ENOCSI = 43; /* No CSI structure available */ + public static final int EL2HLT = 44; /* Level 2 halted */ + public static final int EDEADLK = 45; /* Deadlock condition */ + public static final int ENOLCK = 46; /* No record locks available */ + public static final int EBADE = 50; /* Invalid exchange */ + public static final int EBADR = 51; /* Invalid request descriptor */ + public static final int EXFULL = 52; /* Exchange full */ + public static final int ENOANO = 53; /* No anode */ + public static final int EBADRQC = 54; /* Invalid request code */ + public static final int EBADSLT = 55; /* Invalid slot */ + public static final int EDEADLOCK = 56; /* File locking deadlock error */ + public static final int EBFONT = 57; /* Bad font file fmt */ + public static final int ENOSTR = 60; /* Device not a stream */ + public static final int ENODATA = 61; /* No data (for no delay io) */ + public static final int ETIME = 62; /* Timer expired */ + public static final int ENOSR = 63; /* Out of streams resources */ + public static final int ENONET = 64; /* Machine is not on the network */ + public static final int ENOPKG = 65; /* Package not installed */ + public static final int EREMOTE = 66; /* The object is remote */ + public static final int ENOLINK = 67; /* The link has been severed */ + public static final int EADV = 68; /* Advertise error */ + public static final int ESRMNT = 69; /* Srmount error */ + public static final int ECOMM = 70; /* Communication error on send */ + public static final int EPROTO = 71; /* Protocol error */ + public static final int EMULTIHOP = 74; /* Multihop attempted */ + public static final int ELBIN = 75; /* Inode is remote (not really error) */ + public static final int EDOTDOT = 76; /* Cross mount point (not really error) */ + public static final int EBADMSG = 77; /* Trying to read unreadable message */ + public static final int EFTYPE = 79; /* Inappropriate file type or format */ + public static final int ENOTUNIQ = 80; /* Given log. name not unique */ + public static final int EBADFD = 81; /* f.d. invalid for this operation */ + public static final int EREMCHG = 82; /* Remote address changed */ + public static final int ELIBACC = 83; /* Can't access a needed shared lib */ + public static final int ELIBBAD = 84; /* Accessing a corrupted shared lib */ + public static final int ELIBSCN = 85; /* .lib section in a.out corrupted */ + public static final int ELIBMAX = 86; /* Attempting to link in too many libs */ + public static final int ELIBEXEC = 87; /* Attempting to exec a shared library */ + public static final int ENOSYS = 88; /* Function not implemented */ + public static final int ENMFILE = 89; /* No more files */ + public static final int ENOTEMPTY = 90; /* Directory not empty */ + public static final int ENAMETOOLONG = 91; /* File or path name too long */ + public static final int ELOOP = 92; /* Too many symbolic links */ + public static final int EOPNOTSUPP = 95; /* Operation not supported on transport endpoint */ + public static final int EPFNOSUPPORT = 96; /* Protocol family not supported */ + public static final int ECONNRESET = 104; /* Connection reset by peer */ + public static final int ENOBUFS = 105; /* No buffer space available */ + public static final int EAFNOSUPPORT = 106; /* Address family not supported by protocol family */ + public static final int EPROTOTYPE = 107; /* Protocol wrong type for socket */ + public static final int ENOTSOCK = 108; /* Socket operation on non-socket */ + public static final int ENOPROTOOPT = 109; /* Protocol not available */ + public static final int ESHUTDOWN = 110; /* Can't send after socket shutdown */ + public static final int ECONNREFUSED = 111; /* Connection refused */ + public static final int EADDRINUSE = 112; /* Address already in use */ + public static final int ECONNABORTED = 113; /* Connection aborted */ + public static final int ENETUNREACH = 114; /* Network is unreachable */ + public static final int ENETDOWN = 115; /* Network interface is not configured */ + public static final int ETIMEDOUT = 116; /* Connection timed out */ + public static final int EHOSTDOWN = 117; /* Host is down */ + public static final int EHOSTUNREACH = 118; /* Host is unreachable */ + public static final int EINPROGRESS = 119; /* Connection already in progress */ + public static final int EALREADY = 120; /* Socket already connected */ + public static final int EDESTADDRREQ = 121; /* Destination address required */ + public static final int EMSGSIZE = 122; /* Message too long */ + public static final int EPROTONOSUPPORT = 123; /* Unknown protocol */ + public static final int ESOCKTNOSUPPORT = 124; /* Socket type not supported */ + public static final int EADDRNOTAVAIL = 125; /* Address not available */ + public static final int ENETRESET = 126; + public static final int EISCONN = 127; /* Socket is already connected */ + public static final int ENOTCONN = 128; /* Socket is not connected */ + public static final int ETOOMANYREFS = 129; + public static final int EPROCLIM = 130; + public static final int EUSERS = 131; + public static final int EDQUOT = 132; + public static final int ESTALE = 133; + public static final int ENOTSUP = 134; /* Not supported */ + public static final int ENOMEDIUM = 135; /* No medium (in tape drive) */ + public static final int ENOSHARE = 136; /* No such host or network path */ + public static final int ECASECLASH = 137; /* Filename exists with different case */ + public static final int EILSEQ = 138; + public static final int EOVERFLOW = 139; /* Value too large for defined data type */ + public static final int __ELASTERROR = 2000; /* Users can add values starting here */ + public static final int F_OK = 0; + public static final int R_OK = 4; + public static final int W_OK = 2; + public static final int X_OK = 1; + public static final int SEEK_SET = 0; + public static final int SEEK_CUR = 1; + public static final int SEEK_END = 2; + public static final int STDIN_FILENO = 0; /* standard input file descriptor */ + public static final int STDOUT_FILENO = 1; /* standard output file descriptor */ + public static final int STDERR_FILENO = 2; /* standard error file descriptor */ + public static final int _SC_ARG_MAX = 0; + public static final int _SC_CHILD_MAX = 1; + public static final int _SC_CLK_TCK = 2; + public static final int _SC_NGROUPS_MAX = 3; + public static final int _SC_OPEN_MAX = 4; + public static final int _SC_JOB_CONTROL = 5; + public static final int _SC_SAVED_IDS = 6; + public static final int _SC_VERSION = 7; + public static final int _SC_PAGESIZE = 8; + public static final int _SC_NPROCESSORS_CONF = 9; + public static final int _SC_NPROCESSORS_ONLN = 10; + public static final int _SC_PHYS_PAGES = 11; + public static final int _SC_AVPHYS_PAGES = 12; + public static final int _SC_MQ_OPEN_MAX = 13; + public static final int _SC_MQ_PRIO_MAX = 14; + public static final int _SC_RTSIG_MAX = 15; + public static final int _SC_SEM_NSEMS_MAX = 16; + public static final int _SC_SEM_VALUE_MAX = 17; + public static final int _SC_SIGQUEUE_MAX = 18; + public static final int _SC_TIMER_MAX = 19; + public static final int _SC_TZNAME_MAX = 20; + public static final int _SC_ASYNCHRONOUS_IO = 21; + public static final int _SC_FSYNC = 22; + public static final int _SC_MAPPED_FILES = 23; + public static final int _SC_MEMLOCK = 24; + public static final int _SC_MEMLOCK_RANGE = 25; + public static final int _SC_MEMORY_PROTECTION = 26; + public static final int _SC_MESSAGE_PASSING = 27; + public static final int _SC_PRIORITIZED_IO = 28; + public static final int _SC_REALTIME_SIGNALS = 29; + public static final int _SC_SEMAPHORES = 30; + public static final int _SC_SHARED_MEMORY_OBJECTS = 31; + public static final int _SC_SYNCHRONIZED_IO = 32; + public static final int _SC_TIMERS = 33; + public static final int _SC_AIO_LISTIO_MAX = 34; + public static final int _SC_AIO_MAX = 35; + public static final int _SC_AIO_PRIO_DELTA_MAX = 36; + public static final int _SC_DELAYTIMER_MAX = 37; + public static final int _SC_THREAD_KEYS_MAX = 38; + public static final int _SC_THREAD_STACK_MIN = 39; + public static final int _SC_THREAD_THREADS_MAX = 40; + public static final int _SC_TTY_NAME_MAX = 41; + public static final int _SC_THREADS = 42; + public static final int _SC_THREAD_ATTR_STACKADDR = 43; + public static final int _SC_THREAD_ATTR_STACKSIZE = 44; + public static final int _SC_THREAD_PRIORITY_SCHEDULING = 45; + public static final int _SC_THREAD_PRIO_INHERIT = 46; + public static final int _SC_THREAD_PRIO_PROTECT = 47; + public static final int _SC_THREAD_PROCESS_SHARED = 48; + public static final int _SC_THREAD_SAFE_FUNCTIONS = 49; + public static final int _SC_GETGR_R_SIZE_MAX = 50; + public static final int _SC_GETPW_R_SIZE_MAX = 51; + public static final int _SC_LOGIN_NAME_MAX = 52; + public static final int _SC_THREAD_DESTRUCTOR_ITERATIONS = 53; + public static final int _SC_STREAM_MAX = 100; + public static final int _SC_PRIORITY_SCHEDULING = 101; + public static final int _PC_LINK_MAX = 0; + public static final int _PC_MAX_CANON = 1; + public static final int _PC_MAX_INPUT = 2; + public static final int _PC_NAME_MAX = 3; + public static final int _PC_PATH_MAX = 4; + public static final int _PC_PIPE_BUF = 5; + public static final int _PC_CHOWN_RESTRICTED = 6; + public static final int _PC_NO_TRUNC = 7; + public static final int _PC_VDISABLE = 8; + public static final int _PC_ASYNC_IO = 9; + public static final int _PC_PRIO_IO = 10; + public static final int _PC_SYNC_IO = 11; + 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 */ + public static final int CTL_MAXNAME = 12; + public static final int CTL_UNSPEC = 0; /* unused */ + public static final int CTL_KERN = 1; /* "high kernel": proc, limits */ + public static final int CTL_VM = 2; /* virtual memory */ + public static final int CTL_VFS = 3; /* file system, mount type is next */ + public static final int CTL_NET = 4; /* network, see socket.h */ + public static final int CTL_DEBUG = 5; /* debugging parameters */ + public static final int CTL_HW = 6; /* generic cpu/io */ + public static final int CTL_MACHDEP = 7; /* machine dependent */ + public static final int CTL_USER = 8; /* user-level */ + public static final int CTL_P1003_1B = 9; /* POSIX 1003.1B */ + public static final int CTL_MAXID = 10; /* number of valid top-level ids */ + public static final int KERN_OSTYPE = 1; /* string: system version */ + public static final int KERN_OSRELEASE = 2; /* string: system release */ + public static final int KERN_OSREV = 3; /* int: system revision */ + public static final int KERN_VERSION = 4; /* string: compile time info */ + public static final int KERN_MAXVNODES = 5; /* int: max vnodes */ + public static final int KERN_MAXPROC = 6; /* int: max processes */ + public static final int KERN_MAXFILES = 7; /* int: max open files */ + public static final int KERN_ARGMAX = 8; /* int: max arguments to exec */ + public static final int KERN_SECURELVL = 9; /* int: system security level */ + public static final int KERN_HOSTNAME = 10; /* string: hostname */ + public static final int KERN_HOSTID = 11; /* int: host identifier */ + public static final int KERN_CLOCKRATE = 12; /* struct: struct clockrate */ + public static final int KERN_VNODE = 13; /* struct: vnode structures */ + public static final int KERN_PROC = 14; /* struct: process entries */ + public static final int KERN_FILE = 15; /* struct: file entries */ + public static final int KERN_PROF = 16; /* node: kernel profiling info */ + public static final int KERN_POSIX1 = 17; /* int: POSIX.1 version */ + public static final int KERN_NGROUPS = 18; /* int: # of supplemental group ids */ + public static final int KERN_JOB_CONTROL = 19; /* int: is job control available */ + public static final int KERN_SAVED_IDS = 20; /* int: saved set-user/group-ID */ + public static final int KERN_BOOTTIME = 21; /* struct: time kernel was booted */ + public static final int KERN_NISDOMAINNAME = 22; /* string: YP domain name */ + public static final int KERN_UPDATEINTERVAL = 23; /* int: update process sleep time */ + public static final int KERN_OSRELDATE = 24; /* int: OS release date */ + public static final int KERN_NTP_PLL = 25; /* node: NTP PLL control */ + public static final int KERN_BOOTFILE = 26; /* string: name of booted kernel */ + public static final int KERN_MAXFILESPERPROC = 27; /* int: max open files per proc */ + public static final int KERN_MAXPROCPERUID = 28; /* int: max processes per uid */ + public static final int KERN_DUMPDEV = 29; /* dev_t: device to dump on */ + public static final int KERN_IPC = 30; /* node: anything related to IPC */ + public static final int KERN_DUMMY = 31; /* unused */ + public static final int KERN_PS_STRINGS = 32; /* int: address of PS_STRINGS */ + public static final int KERN_USRSTACK = 33; /* int: address of USRSTACK */ + public static final int KERN_LOGSIGEXIT = 34; /* int: do we log sigexit procs? */ + public static final int KERN_MAXID = 35; /* number of valid kern ids */ + public static final int KERN_PROC_ALL = 0; /* everything */ + public static final int KERN_PROC_PID = 1; /* by process id */ + public static final int KERN_PROC_PGRP = 2; /* by process group id */ + public static final int KERN_PROC_SESSION = 3; /* by session of pid */ + public static final int KERN_PROC_TTY = 4; /* by controlling tty */ + public static final int KERN_PROC_UID = 5; /* by effective uid */ + public static final int KERN_PROC_RUID = 6; /* by real uid */ + public static final int KERN_PROC_ARGS = 7; /* get/set arguments/proctitle */ + public static final int KIPC_MAXSOCKBUF = 1; /* int: max size of a socket buffer */ + public static final int KIPC_SOCKBUF_WASTE = 2; /* int: wastage factor in sockbuf */ + public static final int KIPC_SOMAXCONN = 3; /* int: max length of connection q */ + public static final int KIPC_MAX_LINKHDR = 4; /* int: max length of link header */ + public static final int KIPC_MAX_PROTOHDR = 5; /* int: max length of network header */ + public static final int KIPC_MAX_HDR = 6; /* int: max total length of headers */ + public static final int KIPC_MAX_DATALEN = 7; /* int: max length of data? */ + public static final int KIPC_MBSTAT = 8; /* struct: mbuf usage statistics */ + public static final int KIPC_NMBCLUSTERS = 9; /* int: maximum mbuf clusters */ + public static final int HW_MACHINE = 1; /* string: machine class */ + public static final int HW_MODEL = 2; /* string: specific machine model */ + public static final int HW_NCPU = 3; /* int: number of cpus */ + public static final int HW_BYTEORDER = 4; /* int: machine byte order */ + public static final int HW_PHYSMEM = 5; /* int: total memory */ + public static final int HW_USERMEM = 6; /* int: non-kernel memory */ + public static final int HW_PAGESIZE = 7; /* int: software page size */ + public static final int HW_DISKNAMES = 8; /* strings: disk drive names */ + public static final int HW_DISKSTATS = 9; /* struct: diskstats[] */ + public static final int HW_FLOATINGPT = 10; /* int: has HW floating point? */ + public static final int HW_MACHINE_ARCH = 11; /* string: machine architecture */ + public static final int HW_MAXID = 12; /* number of valid hw ids */ + public static final int USER_CS_PATH = 1; /* string: _CS_PATH */ + public static final int USER_BC_BASE_MAX = 2; /* int: BC_BASE_MAX */ + public static final int USER_BC_DIM_MAX = 3; /* int: BC_DIM_MAX */ + public static final int USER_BC_SCALE_MAX = 4; /* int: BC_SCALE_MAX */ + public static final int USER_BC_STRING_MAX = 5; /* int: BC_STRING_MAX */ + public static final int USER_COLL_WEIGHTS_MAX = 6; /* int: COLL_WEIGHTS_MAX */ + public static final int USER_EXPR_NEST_MAX = 7; /* int: EXPR_NEST_MAX */ + public static final int USER_LINE_MAX = 8; /* int: LINE_MAX */ + public static final int USER_RE_DUP_MAX = 9; /* int: RE_DUP_MAX */ + public static final int USER_POSIX2_VERSION = 10; /* int: POSIX2_VERSION */ + public static final int USER_POSIX2_C_BIND = 11; /* int: POSIX2_C_BIND */ + public static final int USER_POSIX2_C_DEV = 12; /* int: POSIX2_C_DEV */ + public static final int USER_POSIX2_CHAR_TERM = 13; /* int: POSIX2_CHAR_TERM */ + public static final int USER_POSIX2_FORT_DEV = 14; /* int: POSIX2_FORT_DEV */ + public static final int USER_POSIX2_FORT_RUN = 15; /* int: POSIX2_FORT_RUN */ + public static final int USER_POSIX2_LOCALEDEF = 16; /* int: POSIX2_LOCALEDEF */ + public static final int USER_POSIX2_SW_DEV = 17; /* int: POSIX2_SW_DEV */ + public static final int USER_POSIX2_UPE = 18; /* int: POSIX2_UPE */ + public static final int USER_STREAM_MAX = 19; /* int: POSIX2_STREAM_MAX */ + public static final int USER_TZNAME_MAX = 20; /* int: POSIX2_TZNAME_MAX */ + public static final int USER_MAXID = 21; /* number of valid user ids */ + public static final int CTL_P1003_1B_ASYNCHRONOUS_IO = 1; /* boolean */ + public static final int CTL_P1003_1B_MAPPED_FILES = 2; /* boolean */ + public static final int CTL_P1003_1B_MEMLOCK = 3; /* boolean */ + public static final int CTL_P1003_1B_MEMLOCK_RANGE = 4; /* boolean */ + public static final int CTL_P1003_1B_MEMORY_PROTECTION = 5; /* boolean */ + public static final int CTL_P1003_1B_MESSAGE_PASSING = 6; /* boolean */ + public static final int CTL_P1003_1B_PRIORITIZED_IO = 7; /* boolean */ + public static final int CTL_P1003_1B_PRIORITY_SCHEDULING = 8; /* boolean */ + public static final int CTL_P1003_1B_REALTIME_SIGNALS = 9; /* boolean */ + public static final int CTL_P1003_1B_SEMAPHORES = 10; /* boolean */ + public static final int CTL_P1003_1B_FSYNC = 11; /* boolean */ + public static final int CTL_P1003_1B_SHARED_MEMORY_OBJECTS = 12; /* boolean */ + public static final int CTL_P1003_1B_SYNCHRONIZED_IO = 13; /* boolean */ + public static final int CTL_P1003_1B_TIMERS = 14; /* boolean */ + public static final int CTL_P1003_1B_AIO_LISTIO_MAX = 15; /* int */ + public static final int CTL_P1003_1B_AIO_MAX = 16; /* int */ + public static final int CTL_P1003_1B_AIO_PRIO_DELTA_MAX = 17; /* int */ + public static final int CTL_P1003_1B_DELAYTIMER_MAX = 18; /* int */ + public static final int CTL_P1003_1B_MQ_OPEN_MAX = 19; /* int */ + public static final int CTL_P1003_1B_PAGESIZE = 20; /* int */ + public static final int CTL_P1003_1B_RTSIG_MAX = 21; /* int */ + public static final int CTL_P1003_1B_SEM_NSEMS_MAX = 22; /* int */ + public static final int CTL_P1003_1B_SEM_VALUE_MAX = 23; /* int */ + public static final int CTL_P1003_1B_SIGQUEUE_MAX = 24; /* int */ + public static final int CTL_P1003_1B_TIMER_MAX = 25; /* int */ + public static final int CTL_P1003_1B_MAXID = 26; + public static final int F_UNLKSYS = 4; + public static final int F_CNVT = 12; + public static final int F_SETFD = 2; + public static final int F_SETFL = 4; + public static final int F_SETLK = 8; + public static final int F_SETOWN = 6; + public static final int F_RDLCK = 1; + public static final int F_WRLCK = 2; + public static final int F_SETLKW = 9; + public static final int F_GETFD = 1; + public static final int F_DUPFD = 0; + public static final int O_WRONLY = 1; + public static final int F_RSETLKW = 13; + public static final int O_RDWR = 2; + public static final int F_RGETLK = 10; + public static final int O_RDONLY = 0; + public static final int F_UNLCK = 3; + public static final int F_GETOWN = 5; + public static final int F_RSETLK = 11; + public static final int F_GETFL = 3; + public static final int F_GETLK = 7; +} diff --git a/src/main/java/org/ibex/nestedvm/util/ELF.java b/src/main/java/org/ibex/nestedvm/util/ELF.java new file mode 100644 index 0000000..2f97071 --- /dev/null +++ b/src/main/java/org/ibex/nestedvm/util/ELF.java @@ -0,0 +1,388 @@ +// Copyright 2000-2005 the Contributors, as shown in the revision logs. +// Licensed under the Apache Public Source License 2.0 ("the License"). +// You may not use this file except in compliance with the License. + +package org.ibex.nestedvm.util; + +import java.io.*; + +public class ELF { + private static final int ELF_MAGIC = 0x7f454c46; // '\177', 'E', 'L', 'F' + + public static final int ELFCLASSNONE = 0; + public static final int ELFCLASS32 = 1; + public static final int ELFCLASS64 = 2; + + public static final int ELFDATANONE = 0; + public static final int ELFDATA2LSB = 1; + public static final int ELFDATA2MSB = 2; + + public static final int SHT_SYMTAB = 2; + public static final int SHT_STRTAB = 3; + public static final int SHT_NOBITS = 8; + + public static final int SHF_WRITE = 1; + public static final int SHF_ALLOC = 2; + public static final int SHF_EXECINSTR = 4; + + public static final int PF_X = 0x1; + public static final int PF_W = 0x2; + public static final int PF_R = 0x4; + + public static final int PT_LOAD = 1; + + public static final short ET_EXEC = 2; + public static final short EM_MIPS = 8; + + + private Seekable data; + + public ELFIdent ident; + public ELFHeader header; + public PHeader[] pheaders; + public SHeader[] sheaders; + + private byte[] stringTable; + + private boolean sectionReaderActive; + + + private void readFully(byte[] buf) throws IOException { + int len = buf.length; + int pos = 0; + while(len > 0) { + int n = data.read(buf,pos,len); + if(n == -1) throw new IOException("EOF"); + pos += n; + len -= n; + } + } + + private int readIntBE() throws IOException { + byte[] buf = new byte[4]; + readFully(buf); + return ((buf[0]&0xff)<<24)|((buf[1]&0xff)<<16)|((buf[2]&0xff)<<8)|((buf[3]&0xff)<<0); + } + private int readInt() throws IOException { + int x = readIntBE(); + if(ident!=null && ident.data == ELFDATA2LSB) + x = ((x<<24)&0xff000000) | ((x<<8)&0xff0000) | ((x>>>8)&0xff00) | ((x>>24)&0xff); + return x; + } + + private short readShortBE() throws IOException { + byte[] buf = new byte[2]; + readFully(buf); + return (short)(((buf[0]&0xff)<<8)|((buf[1]&0xff)<<0)); + } + private short readShort() throws IOException { + short x = readShortBE(); + if(ident!=null && ident.data == ELFDATA2LSB) + x = (short)((((x<<8)&0xff00) | ((x>>8)&0xff))&0xffff); + return x; + } + + private byte readByte() throws IOException { + byte[] buf = new byte[1]; + readFully(buf); + return buf[0]; + } + + public class ELFIdent { + public byte klass; + public byte data; + public byte osabi; + public byte abiversion; + + ELFIdent() throws IOException { + if(readIntBE() != ELF_MAGIC) throw new ELFException("Bad Magic"); + + klass = readByte(); + if(klass != ELFCLASS32) throw new ELFException("org.ibex.nestedvm.util.ELF does not suport 64-bit binaries"); + + data = readByte(); + if(data != ELFDATA2LSB && data != ELFDATA2MSB) throw new ELFException("Unknown byte order"); + + readByte(); // version + osabi = readByte(); + abiversion = readByte(); + for(int i=0;i<7;i++) readByte(); // padding + } + } + + public class ELFHeader { + public short type; + public short machine; + public int version; + public int entry; + public int phoff; + public int shoff; + public int flags; + public short ehsize; + public short phentsize; + public short phnum; + public short shentsize; + public short shnum; + public short shstrndx; + + ELFHeader() throws IOException { + type = readShort(); + machine = readShort(); + version = readInt(); + if(version != 1) throw new ELFException("version != 1"); + entry = readInt(); + phoff = readInt(); + shoff = readInt(); + flags = readInt(); + ehsize = readShort(); + phentsize = readShort(); + phnum = readShort(); + shentsize = readShort(); + shnum = readShort(); + shstrndx = readShort(); + } + } + + public class PHeader { + public int type; + public int offset; + public int vaddr; + public int paddr; + public int filesz; + public int memsz; + public int flags; + public int align; + + PHeader() throws IOException { + type = readInt(); + offset = readInt(); + vaddr = readInt(); + paddr = readInt(); + filesz = readInt(); + memsz = readInt(); + flags = readInt(); + align = readInt(); + if(filesz > memsz) throw new ELFException("ELF inconsistency: filesz > memsz (" + toHex(filesz) + " > " + toHex(memsz) + ")"); + } + + public boolean writable() { return (flags & PF_W) != 0; } + + public InputStream getInputStream() throws IOException { + return new BufferedInputStream(new SectionInputStream( + offset,offset+filesz)); + } + } + + public class SHeader { + int nameidx; + public String name; + public int type; + public int flags; + public int addr; + public int offset; + public int size; + public int link; + public int info; + public int addralign; + public int entsize; + + SHeader() throws IOException { + nameidx = readInt(); + type = readInt(); + flags = readInt(); + addr = readInt(); + offset = readInt(); + size = readInt(); + link = readInt(); + info = readInt(); + addralign = readInt(); + entsize = readInt(); + } + + public InputStream getInputStream() throws IOException { + return new BufferedInputStream(new SectionInputStream( + offset, type == SHT_NOBITS ? 0 : offset+size)); + } + + public boolean isText() { return name.equals(".text"); } + public boolean isData() { return name.equals(".data") || name.equals(".sdata") || name.equals(".rodata") || name.equals(".ctors") || name.equals(".dtors"); } + public boolean isBSS() { return name.equals(".bss") || name.equals(".sbss"); } + } + + public ELF(String file) throws IOException, ELFException { this(new Seekable.File(file,false)); } + public ELF(Seekable data) throws IOException, ELFException { + this.data = data; + ident = new ELFIdent(); + header = new ELFHeader(); + pheaders = new PHeader[header.phnum]; + for(int i=0;i= header.shnum) throw new ELFException("Bad shstrndx"); + data.seek(sheaders[header.shstrndx].offset); + stringTable = new byte[sheaders[header.shstrndx].size]; + readFully(stringTable); + + for(int i=0;i= strtab.length) return ""; + while(off >= 0 && off < strtab.length && strtab[off] != 0) sb.append((char)strtab[off++]); + return sb.toString(); + } + + public SHeader sectionWithName(String name) { + for(int i=0;i 0) pos += n; return n; + } + public void close() { sectionReaderActive = false; } + } + + private Symtab _symtab; + public Symtab getSymtab() throws IOException { + if(_symtab != null) return _symtab; + + if(sectionReaderActive) throw new ELFException("Can't read the symtab while a section reader is active"); + + SHeader sh = sectionWithName(".symtab"); + if(sh == null || sh.type != SHT_SYMTAB) return null; + + SHeader sth = sectionWithName(".strtab"); + if(sth == null || sth.type != SHT_STRTAB) return null; + + byte[] strtab = new byte[sth.size]; + DataInputStream dis = new DataInputStream(sth.getInputStream()); + dis.readFully(strtab); + dis.close(); + + return _symtab = new Symtab(sh.offset, sh.size,strtab); + } + + public class Symtab { + public Symbol[] symbols; + + Symtab(int off, int size, byte[] strtab) throws IOException { + data.seek(off); + int count = size/16; + symbols = new Symbol[count]; + for(int i=0;i>4); + other = readByte(); + shndx = readShort(); + } + } + + private static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); } + + /*public static void main(String[] args) throws IOException { + ELF elf = new ELF(new Seekable.InputStream(new FileInputStream(args[0]))); + System.out.println("Type: " + toHex(elf.header.type)); + System.out.println("Machine: " + toHex(elf.header.machine)); + System.out.println("Entry: " + toHex(elf.header.entry)); + for(int i=0;i " + toHex(symtab.symbols[i].addr)); + } else { + System.out.println("Symbol table: None"); + } + }*/ +} diff --git a/src/main/java/org/ibex/nestedvm/util/InodeCache.java b/src/main/java/org/ibex/nestedvm/util/InodeCache.java new file mode 100644 index 0000000..02913de --- /dev/null +++ b/src/main/java/org/ibex/nestedvm/util/InodeCache.java @@ -0,0 +1,207 @@ +// Copyright 2000-2005 the Contributors, as shown in the revision logs. +// Licensed under the Apache Public Source License 2.0 ("the License"). +// You may not use this file except in compliance with the License. + +package org.ibex.nestedvm.util; + +// Based on the various org.xwt.util.* classes by Adam Megacz + +public class InodeCache { + private static final Object PLACEHOLDER = new Object(); + private static final short SHORT_PLACEHOLDER = -2; + private static final short SHORT_NULL = -1; + private static final int LOAD_FACTOR = 2; + + private final int maxSize; + private final int totalSlots; + private final int maxUsedSlots; + + private final Object[] keys; + private final short[] next; + private final short[] prev; + private final short[] inodes; + private final short[] reverse; + + private int size, usedSlots; + private short mru, lru; + + public InodeCache() { this(1024); } + public InodeCache(int maxSize) { + this.maxSize = maxSize; + totalSlots = maxSize*LOAD_FACTOR*2 + 3; + maxUsedSlots = totalSlots / LOAD_FACTOR; + if(totalSlots > Short.MAX_VALUE) throw new IllegalArgumentException("cache size too large"); + keys = new Object[totalSlots]; + next = new short[totalSlots]; + prev = new short[totalSlots]; + inodes = new short[totalSlots]; + reverse = new short[totalSlots]; + clear(); + } + + private static void fill(Object[] a,Object o) { for(int i=0;i " + inodes[i] + "(prev: " + prev[i] + " next: " + next[i] + ")"); + if(i == lru) break; + } + } + + private void stats() { + int freeKeys = 0; + int freeReverse = 0; + int placeholderKeys = 0; + int placeholderReverse = 0; + for(int i=0;i " + c.reverse(n)); + } else { + //System.err.println("Adding " + s); + short n = c.get(s); + System.err.println("Added " + s + " -> " + n); + //c.dump(); + } + } + good = true; + } finally { + if(!good) c.stats(); + } + }*/ +} diff --git a/src/main/java/org/ibex/nestedvm/util/Platform.java b/src/main/java/org/ibex/nestedvm/util/Platform.java new file mode 100644 index 0000000..775af41 --- /dev/null +++ b/src/main/java/org/ibex/nestedvm/util/Platform.java @@ -0,0 +1,241 @@ +// Copyright 2000-2005 the Contributors, as shown in the revision logs. +// Licensed under the Apache Public Source License 2.0 ("the License"). +// You may not use this file except in compliance with the License. + +package org.ibex.nestedvm.util; + +import java.io.*; +import java.nio.channels.*; +import java.net.*; +import java.util.*; + +import java.text.DateFormatSymbols; + +/* + GCCLASS_HINT: org.ibex.nestedvm.util.Platform. org.ibex.nestedvm.util.Platform$Jdk11. + GCCLASS_HINT: org.ibex.nestedvm.util.Platform. org.ibex.nestedvm.util.Platform$Jdk12. + GCCLASS_HINT: org.ibex.nestedvm.util.Platform. org.ibex.nestedvm.util.Platform$Jdk13. + GCCLASS_HINT: org.ibex.nestedvm.util.Platform. org.ibex.nestedvm.util.Platform$Jdk14. +*/ + +public abstract class Platform { + Platform() { } + private static final Platform p; + + static { + float version; + try { + if(getProperty("java.vm.name").equals("SableVM")) + version = 1.2f; + else + version = Float.valueOf(getProperty("java.specification.version")).floatValue(); + } catch(Exception e) { + System.err.println("WARNING: " + e + " while trying to find jvm version - assuming 1.1"); + version = 1.1f; + } + String platformClass; + if(version >= 1.4f) platformClass = "Jdk14"; + else if(version >= 1.3f) platformClass = "Jdk13"; + else if(version >= 1.2f) platformClass = "Jdk12"; + else if(version >= 1.1f) platformClass = "Jdk11"; + else throw new Error("JVM Specification version: " + version + " is too old. (see org.ibex.util.Platform to add support)"); + + try { + p = (Platform) Class.forName(Platform.class.getName() + "$" + platformClass).newInstance(); + } catch(Exception e) { + e.printStackTrace(); + throw new Error("Error instansiating platform class"); + } + } + + public static String getProperty(String key) { + try { + return System.getProperty(key); + } catch(SecurityException e) { + return null; + } + } + + + abstract boolean _atomicCreateFile(File f) throws IOException; + public static boolean atomicCreateFile(File f) throws IOException { return p._atomicCreateFile(f); } + + abstract Seekable.Lock _lockFile(Seekable s, RandomAccessFile raf, long pos, long size, boolean shared) throws IOException; + public static Seekable.Lock lockFile(Seekable s, RandomAccessFile raf, long pos, long size, boolean shared) throws IOException { + return p._lockFile(s, raf, pos, size, shared); } + + abstract void _socketHalfClose(Socket s, boolean output) throws IOException; + public static void socketHalfClose(Socket s, boolean output) throws IOException { p._socketHalfClose(s,output); } + + abstract void _socketSetKeepAlive(Socket s, boolean on) throws SocketException; + public static void socketSetKeepAlive(Socket s, boolean on) throws SocketException { p._socketSetKeepAlive(s,on); } + + abstract InetAddress _inetAddressFromBytes(byte[] a) throws UnknownHostException; + public static InetAddress inetAddressFromBytes(byte[] a) throws UnknownHostException { return p._inetAddressFromBytes(a); } + + abstract String _timeZoneGetDisplayName(TimeZone tz, boolean dst, boolean showlong, Locale l); + public static String timeZoneGetDisplayName(TimeZone tz, boolean dst, boolean showlong, Locale l) { return p._timeZoneGetDisplayName(tz,dst,showlong,l); } + public static String timeZoneGetDisplayName(TimeZone tz, boolean dst, boolean showlong) { return timeZoneGetDisplayName(tz,dst,showlong,Locale.getDefault()); } + + abstract void _setFileLength(RandomAccessFile f, int length) + throws IOException; + public static void setFileLength(RandomAccessFile f, int length) + throws IOException { p._setFileLength(f, length); } + + abstract File[] _listRoots(); + public static File[] listRoots() { return p._listRoots(); } + + abstract File _getRoot(File f); + public static File getRoot(File f) { return p._getRoot(f); } + + static class Jdk11 extends Platform { + boolean _atomicCreateFile(File f) throws IOException { + // This is not atomic, but its the best we can do on jdk 1.1 + if(f.exists()) return false; + new FileOutputStream(f).close(); + return true; + } + Seekable.Lock _lockFile(Seekable s, RandomAccessFile raf, long p, long size, boolean shared) throws IOException { + throw new IOException("file locking requires jdk 1.4+"); + } + void _socketHalfClose(Socket s, boolean output) throws IOException { + throw new IOException("half closing sockets not supported"); + } + InetAddress _inetAddressFromBytes(byte[] a) throws UnknownHostException { + if(a.length != 4) throw new UnknownHostException("only ipv4 addrs supported"); + return InetAddress.getByName(""+(a[0]&0xff)+"."+(a[1]&0xff)+"."+(a[2]&0xff)+"."+(a[3]&0xff)); + } + void _socketSetKeepAlive(Socket s, boolean on) throws SocketException { + if(on) throw new SocketException("keepalive not supported"); + } + String _timeZoneGetDisplayName(TimeZone tz, boolean dst, boolean showlong, Locale l) { + String[][] zs = new DateFormatSymbols(l).getZoneStrings(); + String id = tz.getID(); + for(int i=0;i 0) sb.append(":").append(off/60); off=off%60; + if(off > 0) sb.append(":").append(off); + return sb.toString(); + } + + void _setFileLength(RandomAccessFile f, int length) throws IOException{ + InputStream in = new FileInputStream(f.getFD()); + OutputStream out = new FileOutputStream(f.getFD()); + + byte[] buf = new byte[1024]; + for (int len; length > 0; length -= len) { + len = in.read(buf, 0, Math.min(length, buf.length)); + if (len == -1) break; + out.write(buf, 0, len); + } + if (length == 0) return; + + // fill the rest of the space with zeros + for (int i=0; i < buf.length; i++) buf[i] = 0; + while (length > 0) { + out.write(buf, 0, Math.min(length, buf.length)); + length -= buf.length; + } + } + + RandomAccessFile _truncatedRandomAccessFile(File f, String mode) throws IOException { + new FileOutputStream(f).close(); + return new RandomAccessFile(f,mode); + } + + File[] _listRoots() { + String[] rootProps = new String[]{"java.home","java.class.path","java.library.path","java.io.tmpdir","java.ext.dirs","user.home","user.dir" }; + Hashtable known = new Hashtable(); + for(int i=0;i " + root); + known.put(root,Boolean.TRUE); + if(p == -1) break; + } + } + File[] ret = new File[known.size()]; + int i=0; + for(Enumeration e = known.keys();e.hasMoreElements();) + ret[i++] = (File) e.nextElement(); + return ret; + } + + File _getRoot(File f) { + if(!f.isAbsolute()) f = new File(f.getAbsolutePath()); + String p; + while((p = f.getParent()) != null) f = new File(p); + if(f.getPath().length() == 0) f = new File("/"); // work around a classpath bug + return f; + } + } + + static class Jdk12 extends Jdk11 { + boolean _atomicCreateFile(File f) throws IOException { + return f.createNewFile(); + } + + String _timeZoneGetDisplayName(TimeZone tz, boolean dst, boolean showlong, Locale l) { + return tz.getDisplayName(dst,showlong ? TimeZone.LONG : TimeZone.SHORT, l); + } + + void _setFileLength(RandomAccessFile f, int length) throws IOException { + f.setLength(length); + } + + File[] _listRoots() { return File.listRoots(); } + } + + static class Jdk13 extends Jdk12 { + void _socketHalfClose(Socket s, boolean output) throws IOException { + if(output) s.shutdownOutput(); + else s.shutdownInput(); + } + + void _socketSetKeepAlive(Socket s, boolean on) throws SocketException { + s.setKeepAlive(on); + } + } + + static class Jdk14 extends Jdk13 { + InetAddress _inetAddressFromBytes(byte[] a) throws UnknownHostException { return InetAddress.getByAddress(a); } + + Seekable.Lock _lockFile(Seekable s, RandomAccessFile r, long pos, long size, boolean shared) throws IOException { + FileLock flock; + try { + flock = pos == 0 && size == 0 ? r.getChannel().lock() : + r.getChannel().tryLock(pos, size, shared); + } catch (OverlappingFileLockException e) { flock = null; } + if (flock == null) return null; // region already locked + return new Jdk14FileLock(s, flock); + } + } + + private static final class Jdk14FileLock extends Seekable.Lock { + private final Seekable s; + private final FileLock l; + + Jdk14FileLock(Seekable sk, FileLock flock) { s = sk; l = flock; } + public Seekable seekable() { return s; } + public boolean isShared() { return l.isShared(); } + public boolean isValid() { return l.isValid(); } + public void release() throws IOException { l.release(); } + public long position() { return l.position(); } + public long size() { return l.size(); } + public String toString() { return l.toString(); } + } +} diff --git a/src/main/java/org/ibex/nestedvm/util/Seekable.java b/src/main/java/org/ibex/nestedvm/util/Seekable.java new file mode 100644 index 0000000..485d364 --- /dev/null +++ b/src/main/java/org/ibex/nestedvm/util/Seekable.java @@ -0,0 +1,182 @@ +// Copyright 2000-2005 the Contributors, as shown in the revision logs. +// Licensed under the Apache Public Source License 2.0 ("the License"). +// You may not use this file except in compliance with the License. + +package org.ibex.nestedvm.util; + +import java.io.*; + +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 void sync() throws IOException { + throw new IOException("sync not implemented for " + getClass()); + } + public void resize(long length) throws IOException { + throw new IOException("resize not implemented for " + getClass()); + } + /** If pos == 0 and size == 0 lock covers whole file. */ + public Lock lock(long pos, long size, boolean shared) throws IOException { + throw new IOException("lock not implemented for " + getClass()); + } + + public int read() throws IOException { + byte[] buf = new byte[1]; + int n = read(buf,0,1); + return n == -1 ? -1 : buf[0]&0xff; + } + + public int tryReadFully(byte[] buf, int off, int len) throws IOException { + int total = 0; + while(len > 0) { + int n = read(buf,off,len); + if(n == -1) break; + off += n; + len -= n; + total += n; + } + return total == 0 ? -1 : total; + } + + public static class ByteArray extends Seekable { + protected byte[] data; + protected int pos; + private final boolean writable; + + public ByteArray(byte[] data, boolean writable) { + this.data = data; + this.pos = 0; + this.writable = writable; + } + + public int read(byte[] buf, int off, int len) { + len = Math.min(len,data.length-pos); + if(len <= 0) return -1; + System.arraycopy(data,pos,buf,off,len); + 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); + if(len <= 0) throw new IOException("no space"); + System.arraycopy(buf,off,data,pos,len); + pos += len; + return len; + } + + public int length() { return data.length; } + public int pos() { return pos; } + public void seek(int pos) { this.pos = pos; } + public void close() { /*noop*/ } + } + + public static class File extends Seekable { + private final java.io.File file; + private final RandomAccessFile raf; + + public File(String fileName) throws IOException { this(fileName,false); } + public File(String fileName, boolean writable) throws IOException { this(new java.io.File(fileName),writable,false); } + + public File(java.io.File file, boolean writable, boolean truncate) throws IOException { + this.file = file; + String mode = writable ? "rw" : "r"; + raf = new RandomAccessFile(file,mode); + if (truncate) Platform.setFileLength(raf, 0); + } + + public int read(byte[] buf, int offset, int length) throws IOException { return raf.read(buf,offset,length); } + public int write(byte[] buf, int offset, int length) throws IOException { raf.write(buf,offset,length); return length; } + public void sync() throws IOException { raf.getFD().sync(); } + public void seek(int pos) throws IOException{ raf.seek(pos); } + public int pos() throws IOException { return (int) raf.getFilePointer(); } + public int length() throws IOException { return (int)raf.length(); } + public void close() throws IOException { raf.close(); } + public void resize(long length) throws IOException { Platform.setFileLength(raf, (int)length); } + public boolean equals(Object o) { + return o != null && o instanceof File + && file.equals(((File)o).file); + } + public Lock lock(long pos, long size, boolean shared) + throws IOException { + return Platform.lockFile(this, raf, pos, size, shared); + } + } + + public static class InputStream extends Seekable { + private byte[] buffer = new byte[4096]; + private int bytesRead = 0; + private boolean eof = false; + private int pos; + private java.io.InputStream is; + + public InputStream(java.io.InputStream is) { this.is = is; } + + public int read(byte[] outbuf, int off, int len) throws IOException { + if(pos >= bytesRead && !eof) readTo(pos + 1); + len = Math.min(len,bytesRead-pos); + if(len <= 0) return -1; + System.arraycopy(buffer,pos,outbuf,off,len); + pos += len; + return len; + } + + private void readTo(int target) throws IOException { + if(target >= buffer.length) { + byte[] buf2 = new byte[Math.max(buffer.length+Math.min(buffer.length,65536),target)]; + System.arraycopy(buffer,0,buf2,0,bytesRead); + buffer = buf2; + } + while(bytesRead < target) { + int n = is.read(buffer,bytesRead,buffer.length-bytesRead); + if(n == -1) { + eof = true; + break; + } + bytesRead += n; + } + } + + public int length() throws IOException { + while(!eof) readTo(bytesRead+4096); + return bytesRead; + } + + public int write(byte[] buf, int off, int len) throws IOException { throw new IOException("read-only"); } + public void seek(int pos) { this.pos = pos; } + public int pos() { return pos; } + public void close() throws IOException { is.close(); } + } + + public abstract static class Lock { + private Object owner = null; + + public abstract Seekable seekable(); + public abstract boolean isShared(); + public abstract boolean isValid(); + public abstract void release() throws IOException; + public abstract long position(); + public abstract long size(); + + public void setOwner(Object o) { owner = o; } + public Object getOwner() { return owner; } + + public final boolean contains(int start, int len) { + return start >= position() && position() + size() >= start + len; + } + + public final boolean contained(int start, int len) { + return start < position() && position() + size() < start + len; + } + + public final boolean overlaps(int start, int len) { + return contains(start, len) || contained(start, len); + } + } +} diff --git a/src/main/java/org/ibex/nestedvm/util/Sort.java b/src/main/java/org/ibex/nestedvm/util/Sort.java new file mode 100644 index 0000000..79fe029 --- /dev/null +++ b/src/main/java/org/ibex/nestedvm/util/Sort.java @@ -0,0 +1,47 @@ +package org.ibex.nestedvm.util; + +public final class Sort { + private Sort() { } + + public interface Comparable { public int compareTo(Object o); } + public interface CompareFunc { public int compare(Object a, Object b); } + + private static final CompareFunc comparableCompareFunc = new CompareFunc() { + public int compare(Object a,Object b) { return ((Comparable)a).compareTo(b); } + }; + + public static void sort(Comparable[] a) { sort(a,comparableCompareFunc); } + public static void sort(Object[] a, CompareFunc c) { sort(a,c,0,a.length-1); } + + private static void sort(Object[] a, CompareFunc c, int start, int end) { + Object tmp; + if(start >= end) return; + if(end-start <= 6) { + for(int i=start+1;i<=end;i++) { + tmp = a[i]; + int j; + for(j=i-1;j>=start;j--) { + if(c.compare(a[j],tmp) <= 0) break; + a[j+1] = a[j]; + } + a[j+1] = tmp; + } + return; + } + + Object pivot = a[end]; + int lo = start - 1; + int hi = end; + + do { + while((lo < hi) && c.compare(a[++lo],pivot) < 0) { } + while((hi > lo) && c.compare(a[--hi],pivot) > 0) { } + tmp = a[lo]; a[lo] = a[hi]; a[hi] = tmp; + } while(lo < hi); + + tmp = a[lo]; a[lo] = a[end]; a[end] = tmp; + + sort(a, c, start, lo-1); + sort(a, c, lo+1, end); + } +}