001/* 002 * This file is part of the Jikes RVM project (http://jikesrvm.org). 003 * 004 * This file is licensed to You under the Eclipse Public License (EPL); 005 * You may not use this file except in compliance with the License. You 006 * may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/eclipse-1.0.php 009 * 010 * See the COPYRIGHT.txt file distributed with this work for information 011 * regarding copyright ownership. 012 */ 013package org.mmtk.utility.heap; 014 015import static org.mmtk.utility.Constants.*; 016 017import org.mmtk.utility.*; 018 019import org.mmtk.vm.Lock; 020import org.mmtk.vm.VM; 021 022import org.vmmagic.unboxed.*; 023import org.vmmagic.pragma.*; 024 025/** 026 * This class implements mmapping and protection of virtual memory. 027 */ 028@Uninterruptible public final class Mmapper { 029 030 /**************************************************************************** 031 * Constants 032 */ 033 034 /** 035 * 036 */ 037 public static final byte UNMAPPED = 0; 038 public static final byte MAPPED = 1; 039 public static final byte PROTECTED = 2; // mapped but not accessible 040 public static final int LOG_MMAP_CHUNK_BYTES = 20; 041 public static final int MMAP_CHUNK_BYTES = 1 << LOG_MMAP_CHUNK_BYTES; // the granularity VMResource operates at 042 //TODO: 64-bit: this is not OK: value does not fit in int, but should, we do not want to create such big array 043 private static final int MMAP_CHUNK_MASK = MMAP_CHUNK_BYTES - 1; 044 /** 045 * Number of chunks that can be mmapped, 64bit work around allowing 8GB 046 * of addressable memory 047 */ 048 private static final int MMAP_NUM_CHUNKS = (LOG_BYTES_IN_ADDRESS_SPACE == 32) ? 049 1 << (LOG_BYTES_IN_ADDRESS_SPACE - LOG_MMAP_CHUNK_BYTES) : 050 1 << (33 - LOG_MMAP_CHUNK_BYTES); 051 public static final boolean verbose = false; 052 053 /**************************************************************************** 054 * Class variables 055 */ 056 057 /** 058 * 059 */ 060 public static final Lock lock = VM.newLock("Mmapper"); 061 private static final byte[] mapped; 062 063 064 /**************************************************************************** 065 * Initialization 066 */ 067 068 /** 069 * Class initializer. This is executed <i>prior</i> to bootstrap 070 * (i.e. at "build" time). 071 */ 072 static { 073 mapped = new byte[MMAP_NUM_CHUNKS]; 074 for (int c = 0; c < MMAP_NUM_CHUNKS; c++) { 075 mapped[c] = UNMAPPED; 076 } 077 } 078 079 /**************************************************************************** 080 * Generic mmap and protection functionality 081 */ 082 083 /** 084 * Given an address array describing the regions of virtual memory to be used 085 * by MMTk, demand zero map all of them if they are not already mapped. 086 * 087 * @param spaceMap An address array containing a pairs of start and end 088 * addresses for each of the regions to be mappe3d 089 */ 090 public static void eagerlyMmapAllSpaces(AddressArray spaceMap) { 091 092 /*for (int i = 0; i < spaceMap.length() / 2; i++) { 093 Address regionStart = spaceMap.get(i * 2); 094 Address regionEnd = spaceMap.get(i * 2 + 1); 095 Log.write(regionStart); Log.write(" "); Log.writeln(regionEnd); 096 if (regionEnd.EQ(Address.zero()) || regionStart.EQ(Address.fromIntSignExtend(-1)) ||regionEnd.EQ(Address.fromIntSignExtend(-1))) 097 break; 098 if (VM.VERIFY_ASSERTIONS) { 099 VM.assertions._assert(regionStart.EQ(chunkAlignDown(regionStart))); 100 VM.assertions._assert(regionEnd.EQ(chunkAlignDown(regionEnd))); 101 } 102 int pages = Conversions.bytesToPages(regionEnd.diff(regionStart)); 103 ensureMapped(regionStart, pages); 104 }*/ 105 } 106 107 /** 108 * Mark a range of pages as having (already) been mapped. This is useful 109 * where the VM has performed the mapping of the pages itself. 110 * 111 * @param start The start of the range to be marked as mapped 112 * @param bytes The size of the range, in bytes. 113 */ 114 public static void markAsMapped(Address start, int bytes) { 115 int startChunk = Conversions.addressToMmapChunksDown(start); 116 int endChunk = Conversions.addressToMmapChunksUp(start.plus(bytes)); 117 for (int i = startChunk; i <= endChunk; i++) 118 mapped[i] = MAPPED; 119 } 120 121 /** 122 * Ensure that a range of pages is mmapped (or equivalent). If the 123 * pages are not yet mapped, demand-zero map them. Note that mapping 124 * occurs at chunk granularity, not page granularity.<p> 125 * 126 * NOTE: There is a monotonicity assumption so that only updates require lock 127 * acquisition. 128 * TODO: Fix the above to support unmapping. 129 * 130 * @param start The start of the range to be mapped. 131 * @param pages The size of the range to be mapped, in pages 132 */ 133 public static void ensureMapped(Address start, int pages) { 134 int startChunk = Conversions.addressToMmapChunksDown(start); 135 int endChunk = Conversions.addressToMmapChunksUp(start.plus(Conversions.pagesToBytes(pages))); 136 for (int chunk = startChunk; chunk < endChunk; chunk++) { 137 if (mapped[chunk] == MAPPED) continue; 138 Address mmapStart = Conversions.mmapChunksToAddress(chunk); 139 lock.acquire(); 140// Log.writeln(mmapStart); 141 // might have become MAPPED here 142 if (mapped[chunk] == UNMAPPED) { 143 int errno = VM.memory.dzmmap(mmapStart, MMAP_CHUNK_BYTES); 144 if (errno != 0) { 145 lock.release(); 146 Log.write("ensureMapped failed with errno "); Log.write(errno); 147 Log.write(" on address "); Log.writeln(mmapStart); 148 VM.assertions.fail("Can't get more space with mmap()"); 149 } else { 150 if (verbose) { 151 Log.write("mmap succeeded at chunk "); Log.write(chunk); Log.write(" "); Log.write(mmapStart); 152 Log.write(" with len = "); Log.writeln(MMAP_CHUNK_BYTES); 153 } 154 } 155 } 156 if (mapped[chunk] == PROTECTED) { 157 if (!VM.memory.munprotect(mmapStart, MMAP_CHUNK_BYTES)) { 158 lock.release(); 159 VM.assertions.fail("Mmapper.ensureMapped (unprotect) failed"); 160 } else { 161 if (verbose) { 162 Log.write("munprotect succeeded at chunk "); Log.write(chunk); Log.write(" "); Log.write(mmapStart); 163 Log.write(" with len = "); Log.writeln(MMAP_CHUNK_BYTES); 164 } 165 } 166 } 167 mapped[chunk] = MAPPED; 168 lock.release(); 169 } 170 171 } 172 173 /** 174 * Memory protect a range of pages (using mprotect or equivalent). Note 175 * that protection occurs at chunk granularity, not page granularity. 176 * 177 * @param start The start of the range to be protected. 178 * @param pages The size of the range to be protected, in pages 179 */ 180 public static void protect(Address start, int pages) { 181 int startChunk = Conversions.addressToMmapChunksDown(start); 182 int chunks = Conversions.pagesToMmapChunksUp(pages); 183 int endChunk = startChunk + chunks; 184 lock.acquire(); 185 for (int chunk = startChunk; chunk < endChunk; chunk++) { 186 if (mapped[chunk] == MAPPED) { 187 Address mmapStart = Conversions.mmapChunksToAddress(chunk); 188 if (!VM.memory.mprotect(mmapStart, MMAP_CHUNK_BYTES)) { 189 lock.release(); 190 VM.assertions.fail("Mmapper.mprotect failed"); 191 } else { 192 if (verbose) { 193 Log.write("mprotect succeeded at chunk "); Log.write(chunk); Log.write(" "); Log.write(mmapStart); 194 Log.write(" with len = "); Log.writeln(MMAP_CHUNK_BYTES); 195 } 196 } 197 mapped[chunk] = PROTECTED; 198 } else { 199 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(mapped[chunk] == PROTECTED); 200 } 201 } 202 lock.release(); 203 } 204 205 /**************************************************************************** 206 * Utility functions 207 */ 208 209 /** 210 * Return {@code true} if the given address has been mmapped 211 * 212 * @param addr The address in question. 213 * @return {@code true} if the given address has been mmapped 214 */ 215 @Uninterruptible 216 public static boolean addressIsMapped(Address addr) { 217 int chunk = Conversions.addressToMmapChunksDown(addr); 218 return mapped[chunk] == MAPPED; 219 } 220 221 /** 222 * Return {@code true} if the given object has been mmapped 223 * 224 * @param object The object in question. 225 * @return {@code true} if the given object has been mmapped 226 */ 227 @Uninterruptible 228 public static boolean objectIsMapped(ObjectReference object) { 229 return addressIsMapped(VM.objectModel.refToAddress(object)); 230 } 231 232 /** 233 * Return a given address rounded up to an mmap chunk size 234 * 235 * @param addr The address to be aligned 236 * @return The given address rounded up to an mmap chunk size 237 */ 238 @SuppressWarnings("unused") // but might be useful someday 239 private static Address chunkAlignUp(Address addr) { 240 return chunkAlignDown(addr.plus(MMAP_CHUNK_MASK)); 241 } 242 243 /** 244 * Return a given address rounded down to an mmap chunk size 245 * 246 * @param addr The address to be aligned 247 * @return The given address rounded down to an mmap chunk size 248 */ 249 private static Address chunkAlignDown(Address addr) { 250 return addr.toWord().and(Word.fromIntSignExtend(MMAP_CHUNK_MASK).not()).toAddress(); 251 } 252} 253