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