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