/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.vmmagic.unboxed.harness;
import java.util.ArrayList;
import org.mmtk.harness.lang.Trace;
import org.mmtk.harness.lang.Trace.Item;
import org.mmtk.harness.scheduler.Scheduler;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.Extent;
import org.vmmagic.unboxed.Word;
import static org.vmmagic.unboxed.harness.MemoryConstants.*;
/**
* Simulated memory
*/
public final class SimulatedMemory {
static final boolean ALIGN_CHECK_LONG = false;
static final ArrayList<Address> watches = new ArrayList<Address>();
static final PageTable pageTable = new PageTable();
/**
* @param addr address
* @return {@code true} is the address is aligned to a page boundary
*/
private static boolean isPageAligned(Address addr) {
return addr.toWord().and(Word.fromIntSignExtend(~PAGE_MASK)).EQ(Word.zero());
}
/**
* Align an address downward to the nearest multiple of modulus.
* @param addr An address
* @param modulus A power of 2
* @return addr rounded down accordingly
*/
private static Address alignDown(Address addr, int modulus) {
Word mask = Word.fromIntSignExtend(modulus-1).not();
return addr.toWord().and(mask).toAddress();
}
/**
* Are two addresses on the same page ?
* @param addr1
* @param addr2
* @return
*/
static boolean onSamePage(Address addr1, Address addr2) {
return addr2.toWord().xor(addr1.toWord()).LT(Word.fromIntSignExtend(BYTES_IN_PAGE));
}
/************************************************************************************
*
* Watch points
*
*/
/**
* Watch mutations to an address (watches 4 byte values)
* @param watchAddress Address to watch
*/
public static void addWatch(Address watchAddress) {
watches.add(watchAddress);
}
/**
* Watch an address range ('bytes' bytes starting from watchAddress).
* @param watchAddress Address to watch
* @param bytes Number of bytes to watch
*/
public static void addWatch(Address watchAddress, int bytes) {
while (bytes > 0) {
addWatch(watchAddress);
bytes -= BYTES_IN_INT;
watchAddress = watchAddress.plus(BYTES_IN_INT);
}
}
/**
* @param alignedAddress An address, aligned to a word boundary
* @return {@code true} if the address is in the current watch set
*/
public static boolean isWatched(Address alignedAddress) {
return watches.contains(alignedAddress);
}
/**
* @param watchAddress Base address
* @param bytes Size of region
* @return {@code true} if any address in the given range is being watched
*/
public static boolean isWatched(Address watchAddress, int bytes) {
Address base = alignDown(watchAddress,BYTES_IN_INT);
Address cursor = base;
Address limit = base.plus(bytes);
if (onSamePage(base,limit) && !getPage(cursor).hasWatches())
return false;
while (cursor.LT(limit)) {
if (getPage(cursor).hasWatches()) {
if (isWatched(cursor))
return true;
cursor = cursor.plus(BYTES_IN_INT);
} else {
cursor = alignDown(cursor,BYTES_IN_PAGE).plus(BYTES_IN_PAGE);
}
}
return false;
}
/**
* Get a page with an int offset
* @param address
* @param offset
* @return
*/
private static MemoryPage getPage(Address address) {
Clock.tick();
Scheduler.yield();
if (address.isZero()) {
throw new RuntimeException("Attempted to dereference a null address");
}
return pageTable.getPage(address);
}
/**
* @param address Address to load
* @return The byte at <code>address</code>
*/
public static byte getByte(Address address) {
return getPage(address).getByte(address);
}
/**
* @param address Address to load
* @return The char at <code>address</code>
*/
public static char getChar(Address address) {
return getPage(address).getChar(address);
}
/**
* @param address Address to load
* @return The short at <code>address</code>
*/
public static short getShort(Address address) {
return (short)getChar(address);
}
/**
* @param address Address to load
* @return The int at <code>address</code>
*/
public static int getInt(Address address) {
return getPage(address).getInt(address);
}
/**
* @param address Address to load
* @return The float at <code>address</code>
*/
public static float getFloat(Address address) {
return Float.intBitsToFloat(getInt(address));
}
/**
* @param address Address to load
* @return The long at <code>address</code>
*/
public static long getLong(Address address) {
return getPage(address).getLong(address);
}
/**
* @param address Address to load
* @return The double at <code>address</code>
*/
public static double getDouble(Address address) {
return Double.longBitsToDouble(getLong(address));
}
/**
* @param address Address to load
* @return The ArchitecturalWord at <code>address</code>
*/
public static ArchitecturalWord getWord(Address address) {
switch (ArchitecturalWord.getModel()) {
case BITS32:
return ArchitecturalWord.fromIntSignExtend(getInt(address));
case BITS64:
return ArchitecturalWord.fromLong(getLong(address));
}
throw new AssertionError("ArchitecturalWord.model is neither 32 or 64 bits");
}
/**
* @param address Address to store into
* @param value The new value
* @return The byte previously at <code>address</code>
*/
public static byte setByte(Address address, byte value) {
return getPage(address).setByte(address, value);
}
/**
* @param address Address to store into
* @param value The new value
* @return The previous value of <code>address</code>
*/
public static char setChar(Address address, char value) {
return getPage(address).setChar(address, value);
}
/**
* @param address Address to store into
* @param value The new value
* @return The previous value of <code>address</code>
*/
public static short setShort(Address address, short value) {
return (short)setChar(address, (char)value);
}
/**
* @param address Address to store into
* @param value The new value
* @return The previous value of <code>address</code>
*/
public static int setInt(Address address, int value) {
return getPage(address).setInt(address, value);
}
/**
* @param address Address to store into
* @param value The new value
* @return The previous value of <code>address</code>
*/
public static float setFloat(Address address, float value) {
return Float.intBitsToFloat(setInt(address, Float.floatToIntBits(value)));
}
/**
* @param address Address to store into
* @param value The new value
* @return The previous value of <code>address</code>
*/
public static long setLong(Address address, long value) {
return getPage(address).setLong(address, value);
}
/**
* @param address Address to store into
* @param value The new value
* @return The previous value of <code>address</code>
*/
public static double setDouble(Address address, double value) {
return Double.longBitsToDouble(setLong(address, Double.doubleToLongBits(value)));
}
/**
* @param address Address to store into
* @param value The new value
* @return The previous value of <code>address</code>
*/
public static ArchitecturalWord setWord(Address address, ArchitecturalWord value) {
switch (ArchitecturalWord.getModel()) {
case BITS32:
return ArchitecturalWord.fromLong(setInt(address, value.toInt()));
case BITS64:
return ArchitecturalWord.fromLong(setLong(address, value.toLongSignExtend()));
}
throw new RuntimeException("ArchitecturalWord.model is neither 32 or 64 bits");
}
/**
* Atomic compare-and-swap operation
*
* @param address Address to swap
* @param oldValue Expected value of (address)
* @param value The new value
* @return Whether the exchange succeeded
*/
public static boolean exchangeInt(Address address, int oldValue, int value) {
return getPage(address).exchangeInt(address, oldValue, value);
}
/**
* Atomic compare-and-swap operation
*
* @param address Address to swap
* @param oldValue Expected value of (address)
* @param value The new value
* @return Whether the exchange succeeded
*/
public static boolean exchangeLong(Address address, long oldValue, long value) {
return getPage(address).exchangeLong(address, oldValue, value);
}
/**
* Atomic compare-and-swap operation
*
* @param address Address to swap
* @param oldValue Expected value of (address)
* @param value The new value
* @return Whether the exchange succeeded
*/
public static boolean exchangeWord(Address address, ArchitecturalWord oldValue, ArchitecturalWord value) {
switch (ArchitecturalWord.getModel()) {
case BITS32:
return getPage(address).exchangeInt(address, oldValue.toInt(), value.toInt());
case BITS64:
return getPage(address).exchangeLong(address, oldValue.toLongSignExtend(), value.toLongSignExtend());
}
throw new RuntimeException("ArchitecturalWord.model is neither 32 or 64 bits");
}
/**
* Demand zero mmaps an area of virtual memory.
*
* @param start the address of the start of the area to be mapped
* @param size the size, in bytes, of the area to be mapped
* @return <code>true</code> if successful, otherwise
* <code>false</code>
*/
public static boolean map(Address start, int size) {
Object[] args = { start.toString(), size };
Trace.trace(Item.MEMORY,"map(%s,%d)\n", args);
Address last = start.plus(size);
assert start.toWord().and(Word.fromIntSignExtend(~PAGE_MASK)).EQ(Word.zero());
for(Address p=start; p.LT(last); p = p.plus(BYTES_IN_PAGE)) {
pageTable.mapPage(p);
}
return true;
}
/**
* Protects access to an area of virtual memory.
*
* @param start the address of the start of the area to be mapped
* @param size the size, in bytes, of the area to be mapped
* @return <code>true</code> if successful, otherwise
* <code>false</code>
*/
public static boolean protect(Address start, int size) {
assert start.toWord().and(Word.fromIntSignExtend(~PAGE_MASK)).EQ(Word.zero());
Trace.trace(Item.MEMORY,"protect(%s,%d)\n", start.toString(), size);
Address last = start.plus(size);
for(Address p=start; p.LT(last); p = p.plus(BYTES_IN_PAGE)) {
pageTable.setNonReadable(p);
}
return true;
}
/**
* Allows access to an area of virtual memory.
*
* @param start the address of the start of the area to be mapped
* @param size the size, in bytes, of the area to be mapped
* @return <code>true</code> if successful, otherwise
* <code>false</code>
*/
public static boolean unprotect(Address start, int size) {
assert start.toWord().and(Word.fromIntSignExtend(~PAGE_MASK)).EQ(Word.zero());
Trace.trace(Item.MEMORY,"unprotect(%s,%d)\n", start.toString(), size);
Address last = start.plus(size);
for(Address p=start; p.LT(last); p = p.plus(BYTES_IN_PAGE)) {
pageTable.setReadable(p);
}
return true;
}
/**
* Zero a region of memory.
* @param start Start of address range (inclusive)
* @param size Length in bytes of range to zero
* Returned: nothing
*/
public static void zero(Address start, Extent size) {
Trace.trace(Item.MEMORY,"zero(%s,%s)\n", start.toString(), size.toString());
zero(start, size.toInt());
}
/**
* Zero a region of memory.
* @param start Start of address range (inclusive)
* @param size Length in bytes of range to zero
* Returned: nothing
*/
public static void zero(Address start, int size) {
Trace.trace(Item.MEMORY,"zero(%s,%d)\n", start.toString(), size);
assert (size % BYTES_IN_WORD == 0) : "Must zero word rounded bytes";
MemoryPage page = getPage(start);
Address pageAddress = start;
for(int i=0; i < size; i += BYTES_IN_INT) {
Address curAddr = start.plus(i);
if (!onSamePage(pageAddress, curAddr)) {
page = getPage(curAddr);
pageAddress=curAddr;
}
page.setInt(curAddr, 0);
}
}
/**
* Zero a region of memory.
* @param start Start of address range (inclusive)
* @param size Length in bytes of range to zero
* Returned: nothing
*/
public static void zeroNew(Address start, int size) {
Trace.trace(Item.MEMORY,"zero(%s,%d)\n", start.toString(), size);
assert (size % BYTES_IN_WORD == 0) : "Must zero word rounded bytes";
Address base = start;
final Address limit = start.plus(size);
if (!isPageAligned(base)) {
Address nextPage = alignDown(base,BYTES_IN_PAGE).plus(BYTES_IN_PAGE);
zeroIntraPage(base,nextPage);
base = nextPage;
if (base.GE(limit))
return;
}
final Address lastPage = alignDown(limit,BYTES_IN_PAGE);
zeroPages(base,lastPage.diff(base).toInt());
if (lastPage.LT(limit)) {
zeroIntraPage(lastPage,limit);
}
}
private static void zeroIntraPage(Address base, Address limit) {
MemoryPage page = getPage(base);
for(Address addr=base; addr.LT(limit); addr = addr.plus(BYTES_IN_INT)) {
page.setInt(addr, 0);
}
}
/**
* Zero a range of pages of memory.
* @param start Start of address range (must be a page address)
* @param size Length in bytes of range (must be multiple of page size)
*/
public static void zeroPages(Address start, int size) {
assert isPageAligned(start);
assert (size > 0) && ((size & ~PAGE_MASK) == 0);
Trace.trace(Item.MEMORY,"zeroPages(%s,%d)\n", start.toString(), size);
Address last = start.plus(size);
for(Address p=start; p.LT(last); p = p.plus(BYTES_IN_PAGE)) {
pageTable.zeroPage(p);
}
}
/**
* Logs the contents of an address and the surrounding memory to the
* error output.
*
* @param start the address of the memory to be dumped
* @param beforeBytes the number of bytes before the address to be
* included
* @param afterBytes the number of bytes after the address to be
* included
*/
public static void dumpMemory(Address start, int beforeBytes, int afterBytes) {
Address begin = Address.fromIntZeroExtend((start.toInt() - beforeBytes) & WORD_MASK);
int bytes = (beforeBytes + afterBytes);
for(int i=0; i < bytes; i += BYTES_IN_WORD) {
Address cur = begin.plus(i);
System.err.println(cur + ": " + getWord(cur));
}
}
}