| /* |
| * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| #include "precompiled.hpp" |
| #include "gc/x/xMapper_windows.hpp" |
| #include "gc/x/xSyscall_windows.hpp" |
| #include "logging/log.hpp" |
| #include "utilities/debug.hpp" |
| |
| #include <Windows.h> |
| |
| // Memory reservation, commit, views, and placeholders. |
| // |
| // To be able to up-front reserve address space for the heap views, and later |
| // multi-map the heap views to the same physical memory, without ever losing the |
| // reservation of the reserved address space, we use "placeholders". |
| // |
| // These placeholders block out the address space from being used by other parts |
| // of the process. To commit memory in this address space, the placeholder must |
| // be replaced by anonymous memory, or replaced by mapping a view against a |
| // paging file mapping. We use the later to support multi-mapping. |
| // |
| // We want to be able to dynamically commit and uncommit the physical memory of |
| // the heap (and also unmap ZPages), in granules of ZGranuleSize bytes. There is |
| // no way to grow and shrink the committed memory of a paging file mapping. |
| // Therefore, we create multiple granule-sized page file mappings. The memory is |
| // committed by creating a page file mapping, map a view against it, commit the |
| // memory, unmap the view. The memory will stay committed until all views are |
| // unmapped, and the paging file mapping handle is closed. |
| // |
| // When replacing a placeholder address space reservation with a mapped view |
| // against a paging file mapping, the virtual address space must exactly match |
| // an existing placeholder's address and size. Therefore we only deal with |
| // granule-sized placeholders at this layer. Higher layers that keep track of |
| // reserved available address space can (and will) coalesce placeholders, but |
| // they will be split before being used. |
| |
| #define fatal_error(msg, addr, size) \ |
| fatal(msg ": " PTR_FORMAT " " SIZE_FORMAT "M (%d)", \ |
| (addr), (size) / M, GetLastError()) |
| |
| uintptr_t XMapper::reserve(uintptr_t addr, size_t size) { |
| void* const res = XSyscall::VirtualAlloc2( |
| GetCurrentProcess(), // Process |
| (void*)addr, // BaseAddress |
| size, // Size |
| MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, // AllocationType |
| PAGE_NOACCESS, // PageProtection |
| nullptr, // ExtendedParameters |
| 0 // ParameterCount |
| ); |
| |
| // Caller responsible for error handling |
| return (uintptr_t)res; |
| } |
| |
| void XMapper::unreserve(uintptr_t addr, size_t size) { |
| const bool res = XSyscall::VirtualFreeEx( |
| GetCurrentProcess(), // hProcess |
| (void*)addr, // lpAddress |
| size, // dwSize |
| MEM_RELEASE // dwFreeType |
| ); |
| |
| if (!res) { |
| fatal_error("Failed to unreserve memory", addr, size); |
| } |
| } |
| |
| HANDLE XMapper::create_paging_file_mapping(size_t size) { |
| // Create mapping with SEC_RESERVE instead of SEC_COMMIT. |
| // |
| // We use MapViewOfFile3 for two different reasons: |
| // 1) When committing memory for the created paging file |
| // 2) When mapping a view of the memory created in (2) |
| // |
| // The non-platform code is only setup to deal with out-of-memory |
| // errors in (1). By using SEC_RESERVE, we prevent MapViewOfFile3 |
| // from failing because of "commit limit" checks. To actually commit |
| // memory in (1), a call to VirtualAlloc2 is done. |
| |
| HANDLE const res = XSyscall::CreateFileMappingW( |
| INVALID_HANDLE_VALUE, // hFile |
| nullptr, // lpFileMappingAttribute |
| PAGE_READWRITE | SEC_RESERVE, // flProtect |
| size >> 32, // dwMaximumSizeHigh |
| size & 0xFFFFFFFF, // dwMaximumSizeLow |
| nullptr // lpName |
| ); |
| |
| // Caller responsible for error handling |
| return res; |
| } |
| |
| bool XMapper::commit_paging_file_mapping(HANDLE file_handle, uintptr_t file_offset, size_t size) { |
| const uintptr_t addr = map_view_no_placeholder(file_handle, file_offset, size); |
| if (addr == 0) { |
| log_error(gc)("Failed to map view of paging file mapping (%d)", GetLastError()); |
| return false; |
| } |
| |
| const uintptr_t res = commit(addr, size); |
| if (res != addr) { |
| log_error(gc)("Failed to commit memory (%d)", GetLastError()); |
| } |
| |
| unmap_view_no_placeholder(addr, size); |
| |
| return res == addr; |
| } |
| |
| uintptr_t XMapper::map_view_no_placeholder(HANDLE file_handle, uintptr_t file_offset, size_t size) { |
| void* const res = XSyscall::MapViewOfFile3( |
| file_handle, // FileMapping |
| GetCurrentProcess(), // ProcessHandle |
| nullptr, // BaseAddress |
| file_offset, // Offset |
| size, // ViewSize |
| 0, // AllocationType |
| PAGE_NOACCESS, // PageProtection |
| nullptr, // ExtendedParameters |
| 0 // ParameterCount |
| ); |
| |
| // Caller responsible for error handling |
| return (uintptr_t)res; |
| } |
| |
| void XMapper::unmap_view_no_placeholder(uintptr_t addr, size_t size) { |
| const bool res = XSyscall::UnmapViewOfFile2( |
| GetCurrentProcess(), // ProcessHandle |
| (void*)addr, // BaseAddress |
| 0 // UnmapFlags |
| ); |
| |
| if (!res) { |
| fatal_error("Failed to unmap memory", addr, size); |
| } |
| } |
| |
| uintptr_t XMapper::commit(uintptr_t addr, size_t size) { |
| void* const res = XSyscall::VirtualAlloc2( |
| GetCurrentProcess(), // Process |
| (void*)addr, // BaseAddress |
| size, // Size |
| MEM_COMMIT, // AllocationType |
| PAGE_NOACCESS, // PageProtection |
| nullptr, // ExtendedParameters |
| 0 // ParameterCount |
| ); |
| |
| // Caller responsible for error handling |
| return (uintptr_t)res; |
| } |
| |
| HANDLE XMapper::create_and_commit_paging_file_mapping(size_t size) { |
| HANDLE const file_handle = create_paging_file_mapping(size); |
| if (file_handle == 0) { |
| log_error(gc)("Failed to create paging file mapping (%d)", GetLastError()); |
| return 0; |
| } |
| |
| const bool res = commit_paging_file_mapping(file_handle, 0 /* file_offset */, size); |
| if (!res) { |
| close_paging_file_mapping(file_handle); |
| return 0; |
| } |
| |
| return file_handle; |
| } |
| |
| void XMapper::close_paging_file_mapping(HANDLE file_handle) { |
| const bool res = CloseHandle( |
| file_handle // hObject |
| ); |
| |
| if (!res) { |
| fatal("Failed to close paging file handle (%d)", GetLastError()); |
| } |
| } |
| |
| HANDLE XMapper::create_shared_awe_section() { |
| MEM_EXTENDED_PARAMETER parameter = { 0 }; |
| parameter.Type = MemSectionExtendedParameterUserPhysicalFlags; |
| parameter.ULong64 = 0; |
| |
| HANDLE section = XSyscall::CreateFileMapping2( |
| INVALID_HANDLE_VALUE, // File |
| nullptr, // SecurityAttributes |
| SECTION_MAP_READ | SECTION_MAP_WRITE, // DesiredAccess |
| PAGE_READWRITE, // PageProtection |
| SEC_RESERVE | SEC_LARGE_PAGES, // AllocationAttributes |
| 0, // MaximumSize |
| nullptr, // Name |
| ¶meter, // ExtendedParameters |
| 1 // ParameterCount |
| ); |
| |
| if (section == nullptr) { |
| fatal("Could not create shared AWE section (%d)", GetLastError()); |
| } |
| |
| return section; |
| } |
| |
| uintptr_t XMapper::reserve_for_shared_awe(HANDLE awe_section, uintptr_t addr, size_t size) { |
| MEM_EXTENDED_PARAMETER parameter = { 0 }; |
| parameter.Type = MemExtendedParameterUserPhysicalHandle; |
| parameter.Handle = awe_section; |
| |
| void* const res = XSyscall::VirtualAlloc2( |
| GetCurrentProcess(), // Process |
| (void*)addr, // BaseAddress |
| size, // Size |
| MEM_RESERVE | MEM_PHYSICAL, // AllocationType |
| PAGE_READWRITE, // PageProtection |
| ¶meter, // ExtendedParameters |
| 1 // ParameterCount |
| ); |
| |
| // Caller responsible for error handling |
| return (uintptr_t)res; |
| } |
| |
| void XMapper::unreserve_for_shared_awe(uintptr_t addr, size_t size) { |
| bool res = VirtualFree( |
| (void*)addr, // lpAddress |
| 0, // dwSize |
| MEM_RELEASE // dwFreeType |
| ); |
| |
| if (!res) { |
| fatal("Failed to unreserve memory: " PTR_FORMAT " " SIZE_FORMAT "M (%d)", |
| addr, size / M, GetLastError()); |
| } |
| } |
| |
| void XMapper::split_placeholder(uintptr_t addr, size_t size) { |
| const bool res = VirtualFree( |
| (void*)addr, // lpAddress |
| size, // dwSize |
| MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER // dwFreeType |
| ); |
| |
| if (!res) { |
| fatal_error("Failed to split placeholder", addr, size); |
| } |
| } |
| |
| void XMapper::coalesce_placeholders(uintptr_t addr, size_t size) { |
| const bool res = VirtualFree( |
| (void*)addr, // lpAddress |
| size, // dwSize |
| MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS // dwFreeType |
| ); |
| |
| if (!res) { |
| fatal_error("Failed to coalesce placeholders", addr, size); |
| } |
| } |
| |
| void XMapper::map_view_replace_placeholder(HANDLE file_handle, uintptr_t file_offset, uintptr_t addr, size_t size) { |
| void* const res = XSyscall::MapViewOfFile3( |
| file_handle, // FileMapping |
| GetCurrentProcess(), // ProcessHandle |
| (void*)addr, // BaseAddress |
| file_offset, // Offset |
| size, // ViewSize |
| MEM_REPLACE_PLACEHOLDER, // AllocationType |
| PAGE_READWRITE, // PageProtection |
| nullptr, // ExtendedParameters |
| 0 // ParameterCount |
| ); |
| |
| if (res == nullptr) { |
| fatal_error("Failed to map memory", addr, size); |
| } |
| } |
| |
| void XMapper::unmap_view_preserve_placeholder(uintptr_t addr, size_t size) { |
| const bool res = XSyscall::UnmapViewOfFile2( |
| GetCurrentProcess(), // ProcessHandle |
| (void*)addr, // BaseAddress |
| MEM_PRESERVE_PLACEHOLDER // UnmapFlags |
| ); |
| |
| if (!res) { |
| fatal_error("Failed to unmap memory", addr, size); |
| } |
| } |