1 /*
   2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 #include "precompiled.hpp"
  25 #include "gc/z/zMapper_windows.hpp"
  26 #include "gc/z/zSyscall_windows.hpp"
  27 #include "logging/log.hpp"
  28 #include "utilities/debug.hpp"
  29 
  30 #include <Windows.h>
  31 
  32 // Memory reservation, commit, views, and placeholders.
  33 //
  34 // To be able to up-front reserve address space for the heap views, and later
  35 // multi-map the heap views to the same physical memory, without ever losing the
  36 // reservation of the reserved address space, we use "placeholders".
  37 //
  38 // These placeholders block out the address space from being used by other parts
  39 // of the process. To commit memory in this address space, the placeholder must
  40 // be replaced by anonymous memory, or replaced by mapping a view against a
  41 // paging file mapping. We use the later to support multi-mapping.
  42 //
  43 // We want to be able to dynamically commit and uncommit the physical memory of
  44 // the heap (and also unmap ZPages), in granules of ZGranuleSize bytes. There is
  45 // no way to grow and shrink the committed memory of a paging file mapping.
  46 // Therefore, we create multiple granule-sized page file mappings. The memory is
  47 // committed by creating a page file mapping, map a view against it, commit the
  48 // memory, unmap the view. The memory will stay committed until all views are
  49 // unmapped, and the paging file mapping handle is closed.
  50 //
  51 // When replacing a placeholder address space reservation with a mapped view
  52 // against a paging file mapping, the virtual address space must exactly match
  53 // an existing placeholder's address and size. Therefore we only deal with
  54 // granule-sized placeholders at this layer. Higher layers that keep track of
  55 // reserved available address space can (and will) coalesce placeholders, but
  56 // they will be split before being used.
  57 
  58 #define fatal_error(msg, addr, size)                  \
  59   fatal(msg ": " PTR_FORMAT " " SIZE_FORMAT "M (%d)", \
  60         (addr), (size) / M, GetLastError())
  61 
  62 uintptr_t ZMapper::reserve(uintptr_t addr, size_t size) {
  63   void* const res = ZSyscall::VirtualAlloc2(
  64     GetCurrentProcess(),                   // Process
  65     (void*)addr,                           // BaseAddress
  66     size,                                  // Size
  67     MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, // AllocationType
  68     PAGE_NOACCESS,                         // PageProtection
  69     NULL,                                  // ExtendedParameters
  70     0                                      // ParameterCount
  71     );
  72 
  73   // Caller responsible for error handling
  74   return (uintptr_t)res;
  75 }
  76 
  77 void ZMapper::unreserve(uintptr_t addr, size_t size) {
  78   const bool res = ZSyscall::VirtualFreeEx(
  79     GetCurrentProcess(), // hProcess
  80     (void*)addr,         // lpAddress
  81     size,                // dwSize
  82     MEM_RELEASE          // dwFreeType
  83     );
  84 
  85   if (!res) {
  86     fatal_error("Failed to unreserve memory", addr, size);
  87   }
  88 }
  89 
  90 HANDLE ZMapper::create_paging_file_mapping(size_t size) {
  91   // Create mapping with SEC_RESERVE instead of SEC_COMMIT.
  92   //
  93   // We use MapViewOfFile3 for two different reasons:
  94   //  1) When commiting memory for the created paging file
  95   //  2) When mapping a view of the memory created in (2)
  96   //
  97   // The non-platform code is only setup to deal with out-of-memory
  98   // errors in (1). By using SEC_RESERVE, we prevent MapViewOfFile3
  99   // from failing because of "commit limit" checks. To actually commit
 100   // memory in (1), a call to VirtualAlloc2 is done.
 101 
 102   HANDLE const res = ZSyscall::CreateFileMappingW(
 103     INVALID_HANDLE_VALUE,         // hFile
 104     NULL,                         // lpFileMappingAttribute
 105     PAGE_READWRITE | SEC_RESERVE, // flProtect
 106     size >> 32,                   // dwMaximumSizeHigh
 107     size & 0xFFFFFFFF,            // dwMaximumSizeLow
 108     NULL                          // lpName
 109     );
 110 
 111   // Caller responsible for error handling
 112   return res;
 113 }
 114 
 115 bool ZMapper::commit_paging_file_mapping(HANDLE file_handle, uintptr_t file_offset, size_t size) {
 116   const uintptr_t addr = map_view_no_placeholder(file_handle, file_offset, size);
 117   if (addr == 0) {
 118     log_error(gc)("Failed to map view of paging file mapping (%d)", GetLastError());
 119     return false;
 120   }
 121 
 122   const uintptr_t res = commit(addr, size);
 123   if (res != addr) {
 124     log_error(gc)("Failed to commit memory (%d)", GetLastError());
 125   }
 126 
 127   unmap_view_no_placeholder(addr, size);
 128 
 129   return res == addr;
 130 }
 131 
 132 uintptr_t ZMapper::map_view_no_placeholder(HANDLE file_handle, uintptr_t file_offset, size_t size) {
 133   void* const res = ZSyscall::MapViewOfFile3(
 134     file_handle,         // FileMapping
 135     GetCurrentProcess(), // ProcessHandle
 136     NULL,                // BaseAddress
 137     file_offset,         // Offset
 138     size,                // ViewSize
 139     0,                   // AllocationType
 140     PAGE_NOACCESS,       // PageProtection
 141     NULL,                // ExtendedParameters
 142     0                    // ParameterCount
 143     );
 144 
 145   // Caller responsible for error handling
 146   return (uintptr_t)res;
 147 }
 148 
 149 void ZMapper::unmap_view_no_placeholder(uintptr_t addr, size_t size) {
 150   const bool res = ZSyscall::UnmapViewOfFile2(
 151     GetCurrentProcess(), // ProcessHandle
 152     (void*)addr,         // BaseAddress
 153     0                    // UnmapFlags
 154     );
 155 
 156   if (!res) {
 157     fatal_error("Failed to unmap memory", addr, size);
 158   }
 159 }
 160 
 161 uintptr_t ZMapper::commit(uintptr_t addr, size_t size) {
 162   void* const res = ZSyscall::VirtualAlloc2(
 163     GetCurrentProcess(), // Process
 164     (void*)addr,         // BaseAddress
 165     size,                // Size
 166     MEM_COMMIT,          // AllocationType
 167     PAGE_NOACCESS,       // PageProtection
 168     NULL,                // ExtendedParameters
 169     0                    // ParameterCount
 170     );
 171 
 172   // Caller responsible for error handling
 173   return (uintptr_t)res;
 174 }
 175 
 176 HANDLE ZMapper::create_and_commit_paging_file_mapping(size_t size) {
 177   HANDLE const file_handle = create_paging_file_mapping(size);
 178   if (file_handle == 0) {
 179     log_error(gc)("Failed to create paging file mapping (%d)", GetLastError());
 180     return 0;
 181   }
 182 
 183   const bool res = commit_paging_file_mapping(file_handle, 0 /* file_offset */, size);
 184   if (!res) {
 185     close_paging_file_mapping(file_handle);
 186     return 0;
 187   }
 188 
 189   return file_handle;
 190 }
 191 
 192 void ZMapper::close_paging_file_mapping(HANDLE file_handle) {
 193   const bool res = CloseHandle(
 194     file_handle // hObject
 195     );
 196 
 197   if (!res) {
 198     fatal("Failed to close paging file handle (%d)", GetLastError());
 199   }
 200 }
 201 
 202 void ZMapper::split_placeholder(uintptr_t addr, size_t size) {
 203   const bool res = VirtualFree(
 204     (void*)addr,                           // lpAddress
 205     size,                                  // dwSize
 206     MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER // dwFreeType
 207     );
 208 
 209   if (!res) {
 210     fatal_error("Failed to split placeholder", addr, size);
 211   }
 212 }
 213 
 214 void ZMapper::coalesce_placeholders(uintptr_t addr, size_t size) {
 215   const bool res = VirtualFree(
 216     (void*)addr,                            // lpAddress
 217     size,                                   // dwSize
 218     MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS // dwFreeType
 219     );
 220 
 221   if (!res) {
 222     fatal_error("Failed to coalesce placeholders", addr, size);
 223   }
 224 }
 225 
 226 void ZMapper::map_view_replace_placeholder(HANDLE file_handle, uintptr_t file_offset, uintptr_t addr, size_t size) {
 227   void* const res = ZSyscall::MapViewOfFile3(
 228     file_handle,             // FileMapping
 229     GetCurrentProcess(),     // ProcessHandle
 230     (void*)addr,             // BaseAddress
 231     file_offset,             // Offset
 232     size,                    // ViewSize
 233     MEM_REPLACE_PLACEHOLDER, // AllocationType
 234     PAGE_READWRITE,          // PageProtection
 235     NULL,                    // ExtendedParameters
 236     0                        // ParameterCount
 237     );
 238 
 239   if (res == NULL) {
 240     fatal_error("Failed to map memory", addr, size);
 241   }
 242 }
 243 
 244 void ZMapper::unmap_view_preserve_placeholder(uintptr_t addr, size_t size) {
 245   const bool res = ZSyscall::UnmapViewOfFile2(
 246     GetCurrentProcess(),     // ProcessHandle
 247     (void*)addr,             // BaseAddress
 248     MEM_PRESERVE_PLACEHOLDER // UnmapFlags
 249     );
 250 
 251   if (!res) {
 252     fatal_error("Failed to unmap memory", addr, size);
 253   }
 254 }