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