/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.harmony.luni.platform; import org.jikesrvm.Configuration; import static org.jikesrvm.SizeConstants.BYTES_IN_ADDRESS; import org.jikesrvm.runtime.Memory; import org.jikesrvm.runtime.SysCall; import org.vmmagic.unboxed.Address; import org.vmmagic.unboxed.Extent; import org.vmmagic.unboxed.Word; import java.io.IOException; /** * This class enables direct access to OS memory. * <p> * Methods that take OS addresses define such parameters as a Java * <code>long</code>. The <code>long</code> value is interpreted based on * the underlying platform pointer size, such that only the lowest significant * <code>POINTER_SIZE</code> bytes of the <code>long</code> value are used. * In practice this means that methods on 64-bit platforms use the full eight * bytes of the address parameter, and on 32-bit platforms the same methods are * truncated to use only the low four bytes. * </p> * <p> * Methods that return OS addresses define the return type to be a Java * <code>long</code>. If the platform pointer size is less than eight bytes * the OS address value is zero-extended to an eight-byte int to correspond to * the subsequent interpretation of that jlong as an OS address as defined * above. * </p> */ final class OSMemory implements IMemorySystem { /** * Defines the size, in bytes, of a native pointer type for the underlying * platform. This will be 4 (for 32-bit machines) or 8 (for 64-bit * machines). */ public static final int POINTER_SIZE; /** * Defines the natural byte order for this machine. */ public static final Endianness NATIVE_ORDER; private static final OSMemory singleton = new OSMemory(); static { POINTER_SIZE = getPointerSizeImpl(); if (isLittleEndianImpl()) { NATIVE_ORDER = Endianness.LITTLE_ENDIAN; } else { NATIVE_ORDER = Endianness.BIG_ENDIAN; } } public static OSMemory getOSMemory() { return singleton; } /** * This class is not designed to be publically instantiated. * * @see #getOSMemory() */ private OSMemory() { super(); } /** * Answers whether the byte order of this machine is little endian or not.. * * @return <code>false</code> for Big Endian, and * <code>true</code. for Little Endian. */ public static boolean isLittleEndianImpl() { return Configuration.LittleEndian; } public boolean isLittleEndian() { return isLittleEndianImpl(); } /** * Answers the natural byte order for this machine. * * @return the native byte order for the current platform. */ public Endianness getNativeOrder() { return NATIVE_ORDER; } /** * Answers the size of a native pointer type for the underlying platform. * * @return the size of a pointer, in bytes. */ private static int getPointerSizeImpl() { return BYTES_IN_ADDRESS; } public int getPointerSize() { return POINTER_SIZE; } /** * Allocates and returns a pointer to space for a memory block of * <code>length</code> bytes. The space is uninitialized and may be larger * than the number of bytes requested; however, the guaranteed usable memory * block is exactly <code>length</code> bytes long. * * @param length * number of bytes requested. * @return the address of the start of the memory block. * @throws OutOfMemoryError * if the request cannot be satisfied. */ public long malloc(long length) throws OutOfMemoryError { OSResourcesMonitor.ensurePhysicalMemoryCapacity(); return mallocNative(length); } private long mallocNative(long length) throws OutOfMemoryError { Address result = SysCall.sysCall.sysMalloc((int)length); if (result.isZero()) { throw new OutOfMemoryError("Unable to satisfy malloc of "+length); } return result.toLong(); } /** * Deallocates space for a memory block that was previously allocated by a * call to {@link #malloc(long) malloc(long)}. The number of bytes freed is * identical to the number of bytes acquired when the memory block was * allocated. If <code>address</code> is zero the method does nothing. * <p> * Freeing a pointer to a memory block that was not allocated by * <code>malloc()</code> has unspecified effect. * </p> * * @param address * the address of the memory block to deallocate. */ public void free(long address) { SysCall.sysCall.sysFree(Address.fromLong(address)); } /** * Places <code>value</code> into first <code>length</code> bytes of the * memory block starting at <code>address</code>. * <p> * The behavior is unspecified if * <code>(address ... address + length)</code> is not wholly within the * range that was previously allocated using <code>malloc()</code>. * </p> * * @param address * the address of the first memory location. * @param value * the byte value to set at each location. * @param length * the number of byte-length locations to set. */ public void memset(long address, byte value, long length) { if (value == 0) { Memory.zero(Address.fromLong(address), Word.fromLong(length).toExtent()); } else { final Address start = Address.fromLong(address); final Address end = start.plus(Word.fromLong(length).toExtent()); for (Address cur=start; cur.LT(end); cur = cur.plus(1)) { cur.store(value); } } } /** * Copies <code>length</code> bytes from <code>srcAddress</code> to * <code>destAddress</code>. Where any part of the source memory block * and the destination memory block overlap <code>memmove()</code> ensures * that the original source bytes in the overlapping region are copied * before being overwritten. * <p> * The behavior is unspecified if * <code>(srcAddress ... srcAddress + length)</code> and * <code>(destAddress ... destAddress + length)</code> are not both wholly * within the range that was previously allocated using * <code>malloc()</code>. * </p> * * @param destAddress * the address of the destination memory block. * @param srcAddress * the address of the source memory block. * @param length * the number of bytes to move. */ public native void memmove(long destAddress, long srcAddress, long length); /** * Copies <code>length</code> bytes from the memory block at * <code>address</code> into the byte array <code>bytes</code> starting * at element <code>offset</code> within the byte array. * <p> * The behavior of this method is undefined if the range * <code>(address ... address + length)</code> is not within a memory * block that was allocated using {@link #malloc(long) malloc(long)}. * </p> * * @param address * the address of the OS memory block from which to copy bytes. * @param bytes * the byte array into which to copy the bytes. * @param offset * the index of the first element in <code>bytes</code> that * will be overwritten. * @param length * the total number of bytes to copy into the byte array. * @throws NullPointerException * if <code>bytes</code> is <code>null</code>. * @throws IndexOutOfBoundsException * if <code>offset + length > bytes.length</code>. */ public void getByteArray(long address, byte[] bytes, int offset, int length) throws NullPointerException, IndexOutOfBoundsException { Address curAddress = Address.fromLong(address); final int end = offset + length; for (int i=offset; i < end; i++) { bytes[i] = curAddress.loadByte(); curAddress = curAddress.plus(1); } } /** * Copies <code>length</code> bytes from the byte array <code>bytes</code> * into the memory block at <code>address</code>, starting at element * <code>offset</code> within the byte array. * <p> * The behavior of this method is undefined if the range * <code>(address ... address + length)</code> is not within a memory * block that was allocated using {@link #malloc(long) malloc(long)}. * </p> * * @param address * the address of the OS memory block into which to copy the * bytes. * @param bytes * the byte array from which to copy the bytes. * @param offset * the index of the first element in <code>bytes</code> that * will be read. * @param length * the total number of bytes to copy from <code>bytes</code> * into the memory block. * @throws NullPointerException * if <code>bytes</code> is <code>null</code>. * @throws IndexOutOfBoundsException * if <code>offset + length > bytes.length</code>. */ public void setByteArray(long address, byte[] bytes, int offset, int length) throws NullPointerException, IndexOutOfBoundsException { Address curAddress = Address.fromLong(address); final int end = offset + length; for (int i=offset; i < end; i++) { curAddress.store(bytes[i]); curAddress = curAddress.plus(1); } } // Primitive get & set methods /** * Gets the value of the single byte at the given address. * <p> * The behavior is unspecified if <code>address</code> is not in the range * that was previously allocated using <code>malloc()</code>. * </p> * * @param address * the platform address of the byte. * @return the byte value. */ public byte getByte(long address) { return Address.fromLong(address).loadByte(); } /** * Sets the given single byte value at the given address. * <p> * The behavior is unspecified if <code>address</code> is not in the range * that was previously allocated using <code>malloc()</code>. * </p> * * @param address * the address at which to set the byte value. * @param value * the value to set. */ public void setByte(long address, byte value) { Address.fromLong(address).store(value); } /** * Gets the value of the signed two-byte integer stored in platform byte * order at the given address. * <p> * The behavior is unspecified if <code>(address ... address + 2)</code> * is not wholly within the range that was previously allocated using * <code>malloc()</code>. * </p> * * @param address * the platform address of the start of the two-byte value. * @return the value of the two-byte integer as a Java <code>short</code>. */ public short getShort(long address) { return Address.fromLong(address).loadShort(); } public short getShort(long address, Endianness endianness) { return (endianness == NATIVE_ORDER) ? getShort(address) : swap(getShort(address)); } /** * Sets the value of the signed two-byte integer at the given address in * platform byte order. * <p> * The behavior is unspecified if <code>(address ... address + 2)</code> * is not wholly within the range that was previously allocated using * <code>malloc()</code>. * </p> * * @param address * the platform address of the start of the two-byte value. * @param value * the value of the two-byte integer as a Java <code>short</code>. */ public void setShort(long address, short value) { Address.fromLong(address).store(value); } public void setShort(long address, short value, Endianness endianness) { if (endianness == NATIVE_ORDER) { setShort(address, value); } else { setShort(address, swap(value)); } } /** * Gets the value of the signed four-byte integer stored in platform * byte-order at the given address. * <p> * The behavior is unspecified if <code>(address ... address + 4)</code> * is not wholly within the range that was previously allocated using * <code>malloc()</code>. * </p> * * @param address * the platform address of the start of the four-byte value. * @return the value of the four-byte integer as a Java <code>int</code>. */ public int getInt(long address) { return Address.fromLong(address).loadInt(); } public int getInt(long address, Endianness endianness) { return (endianness == NATIVE_ORDER) ? getInt(address) : swap(getInt(address)); } /** * Sets the value of the signed four-byte integer at the given address in * platform byte order. * <p> * The behavior is unspecified if <code>(address ... address + 4)</code> * is not wholly within the range that was previously allocated using * <code>malloc()</code>. * </p> * * @param address * the platform address of the start of the four-byte value. * @param value * the value of the four-byte integer as a Java <code>int</code>. */ public void setInt(long address, int value) { Address.fromLong(address).store(value); } public void setInt(long address, int value, Endianness endianness) { if (endianness == NATIVE_ORDER) { setInt(address, value); } else { setInt(address, swap(value)); } } /** * Gets the value of the signed eight-byte integer stored in platform byte * order at the given address. * <p> * The behavior is unspecified if <code>(address ... address + 8)</code> * is not wholly within the range that was previously allocated using * <code>malloc()</code>. * </p> * * @param address * the platform address of the start of the eight-byte value. * @return the value of the eight-byte integer as a Java <code>long</code>. */ public long getLong(long address) { return Address.fromLong(address).loadLong(); } public long getLong(long address, Endianness endianness) { return (endianness == NATIVE_ORDER) ? getLong(address) : swap(getLong(address)); } /** * Sets the value of the signed eight-byte integer at the given address in * the platform byte order. * <p> * The behavior is unspecified if <code>(address ... address + 8)</code> * is not wholly within the range that was previously allocated using * <code>malloc()</code>. * </p> * * @param address * the platform address of the start of the eight-byte value. * @param value * the value of the eight-byte integer as a Java * <code>long</code>. */ public void setLong(long address, long value) { Address.fromLong(address).store(value); } public void setLong(long address, long value, Endianness endianness) { if (endianness == NATIVE_ORDER) { setLong(address, value); } else { setLong(address, swap(value)); } } /** * Gets the value of the IEEE754-format four-byte float stored in platform * byte order at the given address. * <p> * The behavior is unspecified if <code>(address ... address + 4)</code> * is not wholly within the range that was previously allocated using * <code>malloc()</code>. * </p> * * @param address * the platform address of the start of the eight-byte value. * @return the value of the four-byte float as a Java <code>float</code>. */ public float getFloat(long address) { return Address.fromLong(address).loadFloat(); } public float getFloat(long address, Endianness endianness) { if (endianness == NATIVE_ORDER) { return getFloat(address); } int floatBits = swap(getInt(address)); return Float.intBitsToFloat(floatBits); } /** * Sets the value of the IEEE754-format four-byte float stored in platform * byte order at the given address. * <p> * The behavior is unspecified if <code>(address ... address + 4)</code> * is not wholly within the range that was previously allocated using * <code>malloc()</code>. * </p> * * @param address * the platform address of the start of the eight-byte value. * @param value * the value of the four-byte float as a Java <code>float</code>. */ public void setFloat(long address, float value) { Address.fromLong(address).store(value); } public void setFloat(long address, float value, Endianness endianness) { if (endianness == NATIVE_ORDER) { setFloat(address, value); } else { int floatBits = Float.floatToIntBits(value); setInt(address, swap(floatBits)); } } /** * Gets the value of the IEEE754-format eight-byte float stored in platform * byte order at the given address. * <p> * The behavior is unspecified if <code>(address ... address + 8)</code> * is not wholly within the range that was previously allocated using * <code>malloc()</code>. * </p> * * @param address * the platform address of the start of the eight-byte value. * @return the value of the eight-byte float as a Java <code>double</code>. */ public double getDouble(long address) { return Address.fromLong(address).loadDouble(); } public double getDouble(long address, Endianness endianness) { if (endianness == NATIVE_ORDER) { return getDouble(address); } long doubleBits = swap(getLong(address)); return Double.longBitsToDouble(doubleBits); } /** * Sets the value of the IEEE754-format eight-byte float store in platform * byte order at the given address. * <p> * The behavior is unspecified if <code>(address ... address + 8)</code> * is not wholly within the range that was previously allocated using * <code>malloc()</code>. * </p> * * @param address * the platform address of the start of the eight-byte value. * @param value * the value of the eight-byte float as a Java * <code>double</code>. */ public void setDouble(long address, double value) { Address.fromLong(address).store(value); } public void setDouble(long address, double value, Endianness endianness) { if (endianness == NATIVE_ORDER) { setDouble(address, value); } else { long doubleBits = Double.doubleToLongBits(value); setLong(address, swap(doubleBits)); } } /** * Gets the value of the platform pointer at the given address. * <p> * The length of the platform pointer is defined by * <code>POINTER_SIZE</code>. * </p> * The behavior is unspecified if * <code>(address ... address + POINTER_SIZE)</code> is not wholly within * the range that was previously allocated using <code>malloc()</code>. * </p> * * @param address * the platform address of the start of the platform pointer. * @return the value of the platform pointer as a Java <code>long</code>. */ public long getAddress(long address) { return Address.fromLong(address).loadAddress().toLong(); } /** * Sets the value of the platform pointer at the given address. * <p> * The length of the platform pointer is defined by * <code>POINTER_SIZE</code>. This method only sets * <code>POINTER_SIZE</code> bytes at the given address. * </p> * The behavior is unspecified if * <code>(address ... address + POINTER_SIZE)</code> is not wholly within * the range that was previously allocated using <code>malloc()</code>. * </p> * * @param address * the platform address of the start of the platform pointer. * @param value * the value of the platform pointer as a Java <code>long</code>. */ public void setAddress(long address, long value) { Address.fromLong(address).store(Address.fromLong(value)); } /* * Memory mapped file */ private native long mmapImpl(long fileDescriptor, long alignment, long size, int mapMode); public long mmap(long fileDescriptor, long alignment, long size, int mapMode) throws IOException { long address = mmapImpl(fileDescriptor, alignment, size, mapMode); if (address == -1) { throw new IOException(); } return address; } private native void unmapImpl(long addr, long size); public void unmap(long addr, long size) { unmapImpl(addr, size); } public void load(long addr, long size) { loadImpl(addr, size); } private native int loadImpl(long l, long size); public boolean isLoaded(long addr, long size) { return size == 0 ? true : isLoadedImpl(addr, size); } private native boolean isLoadedImpl(long l, long size); public void flush(long addr, long size) { flushImpl(addr, size); } private native int flushImpl(long l, long size); /* * Helper methods to change byte order. */ private short swap(short value) { int topEnd = value << 8; int btmEnd = (value >> 8) & 0xFF; return (short) (topEnd | btmEnd); } private int swap(int value) { short left = (short) (value >> 16); short right = (short) value; int topEnd = swap(right) << 16; int btmEnd = swap(left) & 0xFFFF; return topEnd | btmEnd; } private long swap(long value) { int left = (int) (value >> 32); int right = (int) value; long topEnd = ((long) swap(right)) << 32; long btmEnd = swap(left) & 0xFFFFFFFFL; return topEnd | btmEnd; } }