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