11cb0ef41Sopenharmony_ci// Copyright (C) 2018 Intel Corporation
21cb0ef41Sopenharmony_ci//
31cb0ef41Sopenharmony_ci// Permission is hereby granted, free of charge, to any person obtaining a copy
41cb0ef41Sopenharmony_ci// of this software and associated documentation files (the "Software"),
51cb0ef41Sopenharmony_ci// to deal in the Software without restriction, including without limitation
61cb0ef41Sopenharmony_ci// the rights to use, copy, modify, merge, publish, distribute, sublicense,
71cb0ef41Sopenharmony_ci// and/or sell copies of the Software, and to permit persons to whom
81cb0ef41Sopenharmony_ci// the Software is furnished to do so, subject to the following conditions:
91cb0ef41Sopenharmony_ci//
101cb0ef41Sopenharmony_ci// The above copyright notice and this permission notice shall be included
111cb0ef41Sopenharmony_ci// in all copies or substantial portions of the Software.
121cb0ef41Sopenharmony_ci//
131cb0ef41Sopenharmony_ci// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
141cb0ef41Sopenharmony_ci// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
151cb0ef41Sopenharmony_ci// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
161cb0ef41Sopenharmony_ci// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
171cb0ef41Sopenharmony_ci// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
181cb0ef41Sopenharmony_ci// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
191cb0ef41Sopenharmony_ci// OR OTHER DEALINGS IN THE SOFTWARE.
201cb0ef41Sopenharmony_ci//
211cb0ef41Sopenharmony_ci// SPDX-License-Identifier: MIT
221cb0ef41Sopenharmony_ci
231cb0ef41Sopenharmony_ci// The functions in this file map the .text section of Node.js into 2MB pages.
241cb0ef41Sopenharmony_ci// They perform the following steps:
251cb0ef41Sopenharmony_ci//
261cb0ef41Sopenharmony_ci// 1: Find the Node.js binary's `.text` section in memory. This is done below in
271cb0ef41Sopenharmony_ci//    `FindNodeTextRegion`. It is accomplished in a platform-specific way. On
281cb0ef41Sopenharmony_ci//    Linux and FreeBSD, `dl_iterate_phdr(3)` is used. When the region is found,
291cb0ef41Sopenharmony_ci//    it is "trimmed" as follows:
301cb0ef41Sopenharmony_ci//    * Modify the start to point to the very beginning of the Node.js `.text`
311cb0ef41Sopenharmony_ci//      section (from symbol `__node_text_start` declared in node_text_start.S).
321cb0ef41Sopenharmony_ci//    * Possibly modify the end to account for the `lpstub` section which
331cb0ef41Sopenharmony_ci//      contains `MoveTextRegionToLargePages`, the function we do not wish to
341cb0ef41Sopenharmony_ci//      move (see below).
351cb0ef41Sopenharmony_ci//    * Align the address of the start to its nearest higher large page
361cb0ef41Sopenharmony_ci//      boundary.
371cb0ef41Sopenharmony_ci//    * Align the address of the end to its nearest lower large page boundary.
381cb0ef41Sopenharmony_ci//
391cb0ef41Sopenharmony_ci// 2: Move the text region to large pages. This is done below in
401cb0ef41Sopenharmony_ci//    `MoveTextRegionToLargePages`. We need to be very careful:
411cb0ef41Sopenharmony_ci//    a) `MoveTextRegionToLargePages` itself should not be moved.
421cb0ef41Sopenharmony_ci//       We use gcc attributes
431cb0ef41Sopenharmony_ci//       (__section__) to put it outside the `.text` section,
441cb0ef41Sopenharmony_ci//       (__aligned__) to align it at the 2M boundary, and
451cb0ef41Sopenharmony_ci//       (__noline__) to not inline this function.
461cb0ef41Sopenharmony_ci//    b) `MoveTextRegionToLargePages` should not call any function(s) that might
471cb0ef41Sopenharmony_ci//       be moved.
481cb0ef41Sopenharmony_ci//    To move the .text section, perform the following steps:
491cb0ef41Sopenharmony_ci//      * Map a new, temporary area and copy the original code there.
501cb0ef41Sopenharmony_ci//      * Use mmap using the start address with MAP_FIXED so we get exactly the
511cb0ef41Sopenharmony_ci//        same virtual address (except on OSX). On platforms other than Linux,
521cb0ef41Sopenharmony_ci//        use mmap flags to request hugepages.
531cb0ef41Sopenharmony_ci//      * On Linux use madvise with MADV_HUGEPAGE to use anonymous 2MB pages.
541cb0ef41Sopenharmony_ci//      * If successful copy the code to the newly mapped area and protect it to
551cb0ef41Sopenharmony_ci//        be readable and executable.
561cb0ef41Sopenharmony_ci//      * Unmap the temporary area.
571cb0ef41Sopenharmony_ci
581cb0ef41Sopenharmony_ci#include "node_large_page.h"
591cb0ef41Sopenharmony_ci
601cb0ef41Sopenharmony_ci#include <cerrno>   // NOLINT(build/include)
611cb0ef41Sopenharmony_ci
621cb0ef41Sopenharmony_ci// Besides returning ENOTSUP at runtime we do nothing if this define is missing.
631cb0ef41Sopenharmony_ci#if defined(NODE_ENABLE_LARGE_CODE_PAGES) && NODE_ENABLE_LARGE_CODE_PAGES
641cb0ef41Sopenharmony_ci#include "debug_utils-inl.h"
651cb0ef41Sopenharmony_ci
661cb0ef41Sopenharmony_ci#if defined(__linux__) || defined(__FreeBSD__)
671cb0ef41Sopenharmony_ci#if defined(__linux__)
681cb0ef41Sopenharmony_ci#ifndef _GNU_SOURCE
691cb0ef41Sopenharmony_ci#define _GNU_SOURCE
701cb0ef41Sopenharmony_ci#endif  // ifndef _GNU_SOURCE
711cb0ef41Sopenharmony_ci#include <sys/prctl.h>
721cb0ef41Sopenharmony_ci#if !defined(PR_SET_VMA)
731cb0ef41Sopenharmony_ci#define PR_SET_VMA 0x53564d41
741cb0ef41Sopenharmony_ci#define PR_SET_VMA_ANON_NAME 0
751cb0ef41Sopenharmony_ci#endif
761cb0ef41Sopenharmony_ci#elif defined(__FreeBSD__)
771cb0ef41Sopenharmony_ci#include "uv.h"  // uv_exepath
781cb0ef41Sopenharmony_ci#endif  // defined(__linux__)
791cb0ef41Sopenharmony_ci#include <link.h>
801cb0ef41Sopenharmony_ci#endif  // defined(__linux__) || defined(__FreeBSD__)
811cb0ef41Sopenharmony_ci
821cb0ef41Sopenharmony_ci#include <sys/types.h>
831cb0ef41Sopenharmony_ci#include <sys/mman.h>
841cb0ef41Sopenharmony_ci#if defined(__FreeBSD__)
851cb0ef41Sopenharmony_ci#include <sys/sysctl.h>
861cb0ef41Sopenharmony_ci#elif defined(__APPLE__)
871cb0ef41Sopenharmony_ci#include <mach/vm_map.h>
881cb0ef41Sopenharmony_ci#endif
891cb0ef41Sopenharmony_ci
901cb0ef41Sopenharmony_ci#include <climits>  // PATH_MAX
911cb0ef41Sopenharmony_ci#include <cstdlib>
921cb0ef41Sopenharmony_ci#include <cstdint>
931cb0ef41Sopenharmony_ci#include <cstring>
941cb0ef41Sopenharmony_ci#include <string>
951cb0ef41Sopenharmony_ci#include <fstream>
961cb0ef41Sopenharmony_ci
971cb0ef41Sopenharmony_ci#if defined(__linux__) || defined(__FreeBSD__)
981cb0ef41Sopenharmony_ciextern "C" {
991cb0ef41Sopenharmony_ci// This symbol must be declared weak because this file becomes part of all
1001cb0ef41Sopenharmony_ci// Node.js targets (like node_mksnapshot, node_mkcodecache, and cctest) and
1011cb0ef41Sopenharmony_ci// those files do not supply the symbol.
1021cb0ef41Sopenharmony_ciextern char __attribute__((weak)) __node_text_start;
1031cb0ef41Sopenharmony_ciextern char __start_lpstub;
1041cb0ef41Sopenharmony_ci}  // extern "C"
1051cb0ef41Sopenharmony_ci#endif  // defined(__linux__) || defined(__FreeBSD__)
1061cb0ef41Sopenharmony_ci
1071cb0ef41Sopenharmony_ci#endif  // defined(NODE_ENABLE_LARGE_CODE_PAGES) && NODE_ENABLE_LARGE_CODE_PAGES
1081cb0ef41Sopenharmony_cinamespace node {
1091cb0ef41Sopenharmony_ci#if defined(NODE_ENABLE_LARGE_CODE_PAGES) && NODE_ENABLE_LARGE_CODE_PAGES
1101cb0ef41Sopenharmony_ci
1111cb0ef41Sopenharmony_cinamespace {
1121cb0ef41Sopenharmony_ci
1131cb0ef41Sopenharmony_cistruct text_region {
1141cb0ef41Sopenharmony_ci  char* from = nullptr;
1151cb0ef41Sopenharmony_ci  char* to = nullptr;
1161cb0ef41Sopenharmony_ci  bool found_text_region = false;
1171cb0ef41Sopenharmony_ci};
1181cb0ef41Sopenharmony_ci
1191cb0ef41Sopenharmony_cistatic const size_t hps = 2L * 1024 * 1024;
1201cb0ef41Sopenharmony_ci
1211cb0ef41Sopenharmony_citemplate <typename... Args>
1221cb0ef41Sopenharmony_ciinline void Debug(std::string fmt, Args&&... args) {
1231cb0ef41Sopenharmony_ci  node::Debug(&per_process::enabled_debug_list,
1241cb0ef41Sopenharmony_ci              DebugCategory::HUGEPAGES,
1251cb0ef41Sopenharmony_ci              (std::string("Hugepages info: ") + fmt).c_str(),
1261cb0ef41Sopenharmony_ci              std::forward<Args>(args)...);
1271cb0ef41Sopenharmony_ci}
1281cb0ef41Sopenharmony_ci
1291cb0ef41Sopenharmony_ciinline void PrintWarning(const char* warn) {
1301cb0ef41Sopenharmony_ci  fprintf(stderr, "Hugepages WARNING: %s\n", warn);
1311cb0ef41Sopenharmony_ci}
1321cb0ef41Sopenharmony_ci
1331cb0ef41Sopenharmony_ciinline void PrintSystemError(int error) {
1341cb0ef41Sopenharmony_ci  PrintWarning(strerror(error));
1351cb0ef41Sopenharmony_ci}
1361cb0ef41Sopenharmony_ci
1371cb0ef41Sopenharmony_ciinline uintptr_t hugepage_align_up(uintptr_t addr) {
1381cb0ef41Sopenharmony_ci  return (((addr) + (hps) - 1) & ~((hps) - 1));
1391cb0ef41Sopenharmony_ci}
1401cb0ef41Sopenharmony_ci
1411cb0ef41Sopenharmony_ciinline uintptr_t hugepage_align_down(uintptr_t addr) {
1421cb0ef41Sopenharmony_ci  return ((addr) & ~((hps) - 1));
1431cb0ef41Sopenharmony_ci}
1441cb0ef41Sopenharmony_ci
1451cb0ef41Sopenharmony_ci#if defined(__linux__) || defined(__FreeBSD__)
1461cb0ef41Sopenharmony_ci#if defined(__FreeBSD__)
1471cb0ef41Sopenharmony_ci#ifndef ElfW
1481cb0ef41Sopenharmony_ci#define ElfW(name) Elf_##name
1491cb0ef41Sopenharmony_ci#endif  // ifndef ElfW
1501cb0ef41Sopenharmony_ci#endif  // defined(__FreeBSD__)
1511cb0ef41Sopenharmony_ci
1521cb0ef41Sopenharmony_cistruct dl_iterate_params {
1531cb0ef41Sopenharmony_ci  uintptr_t start = 0;
1541cb0ef41Sopenharmony_ci  uintptr_t end = 0;
1551cb0ef41Sopenharmony_ci  uintptr_t reference_sym = reinterpret_cast<uintptr_t>(&__node_text_start);
1561cb0ef41Sopenharmony_ci  std::string exename;
1571cb0ef41Sopenharmony_ci};
1581cb0ef41Sopenharmony_ci
1591cb0ef41Sopenharmony_ciint FindMapping(struct dl_phdr_info* info, size_t, void* data) {
1601cb0ef41Sopenharmony_ci  auto dl_params = static_cast<dl_iterate_params*>(data);
1611cb0ef41Sopenharmony_ci  if (dl_params->exename == std::string(info->dlpi_name)) {
1621cb0ef41Sopenharmony_ci    for (int idx = 0; idx < info->dlpi_phnum; idx++) {
1631cb0ef41Sopenharmony_ci      const ElfW(Phdr)* phdr = &info->dlpi_phdr[idx];
1641cb0ef41Sopenharmony_ci      if (phdr->p_type == PT_LOAD && (phdr->p_flags & PF_X)) {
1651cb0ef41Sopenharmony_ci        uintptr_t start = info->dlpi_addr + phdr->p_vaddr;
1661cb0ef41Sopenharmony_ci        uintptr_t end = start + phdr->p_memsz;
1671cb0ef41Sopenharmony_ci
1681cb0ef41Sopenharmony_ci        if (dl_params->reference_sym >= start &&
1691cb0ef41Sopenharmony_ci            dl_params->reference_sym <= end) {
1701cb0ef41Sopenharmony_ci          dl_params->start = start;
1711cb0ef41Sopenharmony_ci          dl_params->end = end;
1721cb0ef41Sopenharmony_ci          return 1;
1731cb0ef41Sopenharmony_ci        }
1741cb0ef41Sopenharmony_ci      }
1751cb0ef41Sopenharmony_ci    }
1761cb0ef41Sopenharmony_ci  }
1771cb0ef41Sopenharmony_ci  return 0;
1781cb0ef41Sopenharmony_ci}
1791cb0ef41Sopenharmony_ci#endif  // defined(__linux__) || defined(__FreeBSD__)
1801cb0ef41Sopenharmony_ci
1811cb0ef41Sopenharmony_cistruct text_region FindNodeTextRegion() {
1821cb0ef41Sopenharmony_ci  struct text_region nregion;
1831cb0ef41Sopenharmony_ci#if defined(__linux__) || defined(__FreeBSD__)
1841cb0ef41Sopenharmony_ci  dl_iterate_params dl_params;
1851cb0ef41Sopenharmony_ci  uintptr_t lpstub_start = reinterpret_cast<uintptr_t>(&__start_lpstub);
1861cb0ef41Sopenharmony_ci
1871cb0ef41Sopenharmony_ci#if defined(__FreeBSD__)
1881cb0ef41Sopenharmony_ci  // On FreeBSD we need the name of the binary, because `dl_iterate_phdr` does
1891cb0ef41Sopenharmony_ci  // not pass in an empty string as the `dlpi_name` of the binary but rather its
1901cb0ef41Sopenharmony_ci  // absolute path.
1911cb0ef41Sopenharmony_ci  {
1921cb0ef41Sopenharmony_ci    char selfexe[PATH_MAX];
1931cb0ef41Sopenharmony_ci    size_t count = sizeof(selfexe);
1941cb0ef41Sopenharmony_ci    if (uv_exepath(selfexe, &count))
1951cb0ef41Sopenharmony_ci      return nregion;
1961cb0ef41Sopenharmony_ci    dl_params.exename = std::string(selfexe, count);
1971cb0ef41Sopenharmony_ci  }
1981cb0ef41Sopenharmony_ci#endif  // defined(__FreeBSD__)
1991cb0ef41Sopenharmony_ci
2001cb0ef41Sopenharmony_ci  if (dl_iterate_phdr(FindMapping, &dl_params) == 1) {
2011cb0ef41Sopenharmony_ci    Debug("start: %p - sym: %p - end: %p\n",
2021cb0ef41Sopenharmony_ci          reinterpret_cast<void*>(dl_params.start),
2031cb0ef41Sopenharmony_ci          reinterpret_cast<void*>(dl_params.reference_sym),
2041cb0ef41Sopenharmony_ci          reinterpret_cast<void*>(dl_params.end));
2051cb0ef41Sopenharmony_ci
2061cb0ef41Sopenharmony_ci    dl_params.start = dl_params.reference_sym;
2071cb0ef41Sopenharmony_ci    if (lpstub_start > dl_params.start && lpstub_start <= dl_params.end) {
2081cb0ef41Sopenharmony_ci      Debug("Trimming end for lpstub: %p\n",
2091cb0ef41Sopenharmony_ci            reinterpret_cast<void*>(lpstub_start));
2101cb0ef41Sopenharmony_ci      dl_params.end = lpstub_start;
2111cb0ef41Sopenharmony_ci    }
2121cb0ef41Sopenharmony_ci
2131cb0ef41Sopenharmony_ci    if (dl_params.start < dl_params.end) {
2141cb0ef41Sopenharmony_ci      char* from = reinterpret_cast<char*>(hugepage_align_up(dl_params.start));
2151cb0ef41Sopenharmony_ci      char* to = reinterpret_cast<char*>(hugepage_align_down(dl_params.end));
2161cb0ef41Sopenharmony_ci      Debug("Aligned range is %p - %p\n", from, to);
2171cb0ef41Sopenharmony_ci      if (from < to) {
2181cb0ef41Sopenharmony_ci        size_t pagecount = (to - from) / hps;
2191cb0ef41Sopenharmony_ci        if (pagecount > 0) {
2201cb0ef41Sopenharmony_ci          nregion.found_text_region = true;
2211cb0ef41Sopenharmony_ci          nregion.from = from;
2221cb0ef41Sopenharmony_ci          nregion.to = to;
2231cb0ef41Sopenharmony_ci        }
2241cb0ef41Sopenharmony_ci      }
2251cb0ef41Sopenharmony_ci    }
2261cb0ef41Sopenharmony_ci  }
2271cb0ef41Sopenharmony_ci#elif defined(__APPLE__)
2281cb0ef41Sopenharmony_ci  struct vm_region_submap_info_64 map;
2291cb0ef41Sopenharmony_ci  mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;
2301cb0ef41Sopenharmony_ci  vm_address_t addr = 0UL;
2311cb0ef41Sopenharmony_ci  vm_size_t size = 0;
2321cb0ef41Sopenharmony_ci  natural_t depth = 1;
2331cb0ef41Sopenharmony_ci
2341cb0ef41Sopenharmony_ci  while (true) {
2351cb0ef41Sopenharmony_ci    if (vm_region_recurse_64(mach_task_self(), &addr, &size, &depth,
2361cb0ef41Sopenharmony_ci                             reinterpret_cast<vm_region_info_64_t>(&map),
2371cb0ef41Sopenharmony_ci                             &count) != KERN_SUCCESS) {
2381cb0ef41Sopenharmony_ci      break;
2391cb0ef41Sopenharmony_ci    }
2401cb0ef41Sopenharmony_ci
2411cb0ef41Sopenharmony_ci    if (map.is_submap) {
2421cb0ef41Sopenharmony_ci      depth++;
2431cb0ef41Sopenharmony_ci    } else {
2441cb0ef41Sopenharmony_ci      char* start = reinterpret_cast<char*>(hugepage_align_up(addr));
2451cb0ef41Sopenharmony_ci      char* end = reinterpret_cast<char*>(hugepage_align_down(addr+size));
2461cb0ef41Sopenharmony_ci
2471cb0ef41Sopenharmony_ci      if (end > start && (map.protection & VM_PROT_READ) != 0 &&
2481cb0ef41Sopenharmony_ci          (map.protection & VM_PROT_EXECUTE) != 0) {
2491cb0ef41Sopenharmony_ci        nregion.found_text_region = true;
2501cb0ef41Sopenharmony_ci        nregion.from = start;
2511cb0ef41Sopenharmony_ci        nregion.to = end;
2521cb0ef41Sopenharmony_ci        break;
2531cb0ef41Sopenharmony_ci      }
2541cb0ef41Sopenharmony_ci
2551cb0ef41Sopenharmony_ci      addr += size;
2561cb0ef41Sopenharmony_ci      size = 0;
2571cb0ef41Sopenharmony_ci    }
2581cb0ef41Sopenharmony_ci  }
2591cb0ef41Sopenharmony_ci#endif
2601cb0ef41Sopenharmony_ci  Debug("Found %d huge pages\n", (nregion.to - nregion.from) / hps);
2611cb0ef41Sopenharmony_ci  return nregion;
2621cb0ef41Sopenharmony_ci}
2631cb0ef41Sopenharmony_ci
2641cb0ef41Sopenharmony_ci#if defined(__linux__)
2651cb0ef41Sopenharmony_cibool IsTransparentHugePagesEnabled() {
2661cb0ef41Sopenharmony_ci  // File format reference:
2671cb0ef41Sopenharmony_ci  // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/mm/huge_memory.c?id=13391c60da3308ed9980de0168f74cce6c62ac1d#n163
2681cb0ef41Sopenharmony_ci  const char* filename = "/sys/kernel/mm/transparent_hugepage/enabled";
2691cb0ef41Sopenharmony_ci  std::ifstream config_stream(filename, std::ios::in);
2701cb0ef41Sopenharmony_ci  if (!config_stream.good()) {
2711cb0ef41Sopenharmony_ci    PrintWarning("could not open /sys/kernel/mm/transparent_hugepage/enabled");
2721cb0ef41Sopenharmony_ci    return false;
2731cb0ef41Sopenharmony_ci  }
2741cb0ef41Sopenharmony_ci
2751cb0ef41Sopenharmony_ci  std::string token;
2761cb0ef41Sopenharmony_ci  config_stream >> token;
2771cb0ef41Sopenharmony_ci  if ("[always]" == token) return true;
2781cb0ef41Sopenharmony_ci  config_stream >> token;
2791cb0ef41Sopenharmony_ci  if ("[madvise]" == token) return true;
2801cb0ef41Sopenharmony_ci  return false;
2811cb0ef41Sopenharmony_ci}
2821cb0ef41Sopenharmony_ci#elif defined(__FreeBSD__)
2831cb0ef41Sopenharmony_cibool IsSuperPagesEnabled() {
2841cb0ef41Sopenharmony_ci  // It is enabled by default on amd64.
2851cb0ef41Sopenharmony_ci  unsigned int super_pages = 0;
2861cb0ef41Sopenharmony_ci  size_t super_pages_length = sizeof(super_pages);
2871cb0ef41Sopenharmony_ci  return sysctlbyname("vm.pmap.pg_ps_enabled",
2881cb0ef41Sopenharmony_ci                      &super_pages,
2891cb0ef41Sopenharmony_ci                      &super_pages_length,
2901cb0ef41Sopenharmony_ci                      nullptr,
2911cb0ef41Sopenharmony_ci                      0) != -1 &&
2921cb0ef41Sopenharmony_ci         super_pages >= 1;
2931cb0ef41Sopenharmony_ci}
2941cb0ef41Sopenharmony_ci#endif
2951cb0ef41Sopenharmony_ci
2961cb0ef41Sopenharmony_ci// Functions in this class must always be inlined because they must end up in
2971cb0ef41Sopenharmony_ci// the `lpstub` section rather than the `.text` section.
2981cb0ef41Sopenharmony_ciclass MemoryMapPointer {
2991cb0ef41Sopenharmony_ci public:
3001cb0ef41Sopenharmony_ci  FORCE_INLINE explicit MemoryMapPointer() {}
3011cb0ef41Sopenharmony_ci  FORCE_INLINE bool operator==(void* rhs) const { return mem_ == rhs; }
3021cb0ef41Sopenharmony_ci  FORCE_INLINE void* mem() const { return mem_; }
3031cb0ef41Sopenharmony_ci  MemoryMapPointer(const MemoryMapPointer&) = delete;
3041cb0ef41Sopenharmony_ci  MemoryMapPointer(MemoryMapPointer&&) = delete;
3051cb0ef41Sopenharmony_ci  void operator= (const MemoryMapPointer&) = delete;
3061cb0ef41Sopenharmony_ci  void operator= (const MemoryMapPointer&&) = delete;
3071cb0ef41Sopenharmony_ci  FORCE_INLINE void Reset(void* start,
3081cb0ef41Sopenharmony_ci                          size_t size,
3091cb0ef41Sopenharmony_ci                          int prot,
3101cb0ef41Sopenharmony_ci                          int flags,
3111cb0ef41Sopenharmony_ci                          int fd = -1,
3121cb0ef41Sopenharmony_ci                          size_t offset = 0) {
3131cb0ef41Sopenharmony_ci    mem_ = mmap(start, size, prot, flags, fd, offset);
3141cb0ef41Sopenharmony_ci    size_ = size;
3151cb0ef41Sopenharmony_ci  }
3161cb0ef41Sopenharmony_ci  FORCE_INLINE void Reset() {
3171cb0ef41Sopenharmony_ci    mem_ = nullptr;
3181cb0ef41Sopenharmony_ci    size_ = 0;
3191cb0ef41Sopenharmony_ci  }
3201cb0ef41Sopenharmony_ci  static void SetName(void* mem, size_t size, const char* name) {
3211cb0ef41Sopenharmony_ci#if defined(__linux__)
3221cb0ef41Sopenharmony_ci    // Available since the 5.17 kernel release and if the
3231cb0ef41Sopenharmony_ci    // CONFIG_ANON_VMA_NAME option, we can set an identifier
3241cb0ef41Sopenharmony_ci    // to an anonymous mapped region. However if the kernel
3251cb0ef41Sopenharmony_ci    // option is not present or it s an older kernel, it is a no-op.
3261cb0ef41Sopenharmony_ci    if (mem != MAP_FAILED && mem != nullptr)
3271cb0ef41Sopenharmony_ci        prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME,
3281cb0ef41Sopenharmony_ci            reinterpret_cast<uintptr_t>(mem),
3291cb0ef41Sopenharmony_ci            size,
3301cb0ef41Sopenharmony_ci            reinterpret_cast<uintptr_t>(name));
3311cb0ef41Sopenharmony_ci#else
3321cb0ef41Sopenharmony_ci    (void)name;
3331cb0ef41Sopenharmony_ci#endif
3341cb0ef41Sopenharmony_ci  }
3351cb0ef41Sopenharmony_ci  FORCE_INLINE ~MemoryMapPointer() {
3361cb0ef41Sopenharmony_ci    if (mem_ == nullptr) return;
3371cb0ef41Sopenharmony_ci    if (mem_ == MAP_FAILED) return;
3381cb0ef41Sopenharmony_ci    if (munmap(mem_, size_) == 0) return;
3391cb0ef41Sopenharmony_ci    PrintSystemError(errno);
3401cb0ef41Sopenharmony_ci  }
3411cb0ef41Sopenharmony_ci
3421cb0ef41Sopenharmony_ci private:
3431cb0ef41Sopenharmony_ci  size_t size_ = 0;
3441cb0ef41Sopenharmony_ci  void* mem_ = nullptr;
3451cb0ef41Sopenharmony_ci};
3461cb0ef41Sopenharmony_ci
3471cb0ef41Sopenharmony_ci}  // End of anonymous namespace
3481cb0ef41Sopenharmony_ci
3491cb0ef41Sopenharmony_ciint
3501cb0ef41Sopenharmony_ci#if !defined(__APPLE__)
3511cb0ef41Sopenharmony_ci__attribute__((__section__("lpstub")))
3521cb0ef41Sopenharmony_ci#else
3531cb0ef41Sopenharmony_ci__attribute__((__section__("__TEXT,__lpstub")))
3541cb0ef41Sopenharmony_ci#endif
3551cb0ef41Sopenharmony_ci__attribute__((__aligned__(hps)))
3561cb0ef41Sopenharmony_ci__attribute__((__noinline__))
3571cb0ef41Sopenharmony_ciMoveTextRegionToLargePages(const text_region& r) {
3581cb0ef41Sopenharmony_ci  MemoryMapPointer nmem;
3591cb0ef41Sopenharmony_ci  MemoryMapPointer tmem;
3601cb0ef41Sopenharmony_ci  void* start = r.from;
3611cb0ef41Sopenharmony_ci  size_t size = r.to - r.from;
3621cb0ef41Sopenharmony_ci
3631cb0ef41Sopenharmony_ci  // Allocate a temporary region and back up the code we will re-map.
3641cb0ef41Sopenharmony_ci  nmem.Reset(nullptr, size,
3651cb0ef41Sopenharmony_ci             PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS);
3661cb0ef41Sopenharmony_ci  if (nmem.mem() == MAP_FAILED) goto fail;
3671cb0ef41Sopenharmony_ci  memcpy(nmem.mem(), r.from, size);
3681cb0ef41Sopenharmony_ci
3691cb0ef41Sopenharmony_ci#if defined(__linux__)
3701cb0ef41Sopenharmony_ci// We already know the original page is r-xp
3711cb0ef41Sopenharmony_ci// (PROT_READ, PROT_EXEC, MAP_PRIVATE)
3721cb0ef41Sopenharmony_ci// We want PROT_WRITE because we are writing into it.
3731cb0ef41Sopenharmony_ci// We want it at the fixed address and we use MAP_FIXED.
3741cb0ef41Sopenharmony_ci  tmem.Reset(start, size,
3751cb0ef41Sopenharmony_ci             PROT_READ | PROT_WRITE | PROT_EXEC,
3761cb0ef41Sopenharmony_ci             MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED);
3771cb0ef41Sopenharmony_ci  if (tmem.mem() == MAP_FAILED) goto fail;
3781cb0ef41Sopenharmony_ci  if (madvise(tmem.mem(), size, 14 /* MADV_HUGEPAGE */) == -1) goto fail;
3791cb0ef41Sopenharmony_ci  memcpy(start, nmem.mem(), size);
3801cb0ef41Sopenharmony_ci#elif defined(__FreeBSD__)
3811cb0ef41Sopenharmony_ci  tmem.Reset(start, size,
3821cb0ef41Sopenharmony_ci             PROT_READ | PROT_WRITE | PROT_EXEC,
3831cb0ef41Sopenharmony_ci             MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED |
3841cb0ef41Sopenharmony_ci             MAP_ALIGNED_SUPER);
3851cb0ef41Sopenharmony_ci  if (tmem.mem() == MAP_FAILED) goto fail;
3861cb0ef41Sopenharmony_ci  memcpy(start, nmem.mem(), size);
3871cb0ef41Sopenharmony_ci#elif defined(__APPLE__)
3881cb0ef41Sopenharmony_ci  // There is not enough room to reserve the mapping close
3891cb0ef41Sopenharmony_ci  // to the region address so we content to give a hint
3901cb0ef41Sopenharmony_ci  // without forcing the new address being closed to.
3911cb0ef41Sopenharmony_ci  // We explicitally gives all permission since we plan
3921cb0ef41Sopenharmony_ci  // to write into it.
3931cb0ef41Sopenharmony_ci  tmem.Reset(start, size,
3941cb0ef41Sopenharmony_ci             PROT_READ | PROT_WRITE | PROT_EXEC,
3951cb0ef41Sopenharmony_ci             MAP_PRIVATE | MAP_ANONYMOUS,
3961cb0ef41Sopenharmony_ci             VM_FLAGS_SUPERPAGE_SIZE_2MB);
3971cb0ef41Sopenharmony_ci  if (tmem.mem() == MAP_FAILED) goto fail;
3981cb0ef41Sopenharmony_ci  memcpy(tmem.mem(), nmem.mem(), size);
3991cb0ef41Sopenharmony_ci  if (mprotect(start, size, PROT_READ | PROT_WRITE | PROT_EXEC) == -1)
4001cb0ef41Sopenharmony_ci    goto fail;
4011cb0ef41Sopenharmony_ci  memcpy(start, tmem.mem(), size);
4021cb0ef41Sopenharmony_ci#endif
4031cb0ef41Sopenharmony_ci
4041cb0ef41Sopenharmony_ci  if (mprotect(start, size, PROT_READ | PROT_EXEC) == -1) goto fail;
4051cb0ef41Sopenharmony_ci  MemoryMapPointer::SetName(start, size, "nodejs Large Page");
4061cb0ef41Sopenharmony_ci
4071cb0ef41Sopenharmony_ci  // We need not `munmap(tmem, size)` on success.
4081cb0ef41Sopenharmony_ci  tmem.Reset();
4091cb0ef41Sopenharmony_ci  return 0;
4101cb0ef41Sopenharmony_cifail:
4111cb0ef41Sopenharmony_ci  PrintSystemError(errno);
4121cb0ef41Sopenharmony_ci  return -1;
4131cb0ef41Sopenharmony_ci}
4141cb0ef41Sopenharmony_ci#endif  // defined(NODE_ENABLE_LARGE_CODE_PAGES) && NODE_ENABLE_LARGE_CODE_PAGES
4151cb0ef41Sopenharmony_ci
4161cb0ef41Sopenharmony_ci// This is the primary API called from main.
4171cb0ef41Sopenharmony_ciint MapStaticCodeToLargePages() {
4181cb0ef41Sopenharmony_ci#if defined(NODE_ENABLE_LARGE_CODE_PAGES) && NODE_ENABLE_LARGE_CODE_PAGES
4191cb0ef41Sopenharmony_ci  bool have_thp = false;
4201cb0ef41Sopenharmony_ci#if defined(__linux__)
4211cb0ef41Sopenharmony_ci  have_thp = IsTransparentHugePagesEnabled();
4221cb0ef41Sopenharmony_ci#elif defined(__FreeBSD__)
4231cb0ef41Sopenharmony_ci  have_thp = IsSuperPagesEnabled();
4241cb0ef41Sopenharmony_ci#elif defined(__APPLE__)
4251cb0ef41Sopenharmony_ci  // pse-36 flag is present in recent mac x64 products.
4261cb0ef41Sopenharmony_ci  have_thp = true;
4271cb0ef41Sopenharmony_ci#endif
4281cb0ef41Sopenharmony_ci  if (!have_thp)
4291cb0ef41Sopenharmony_ci    return EACCES;
4301cb0ef41Sopenharmony_ci
4311cb0ef41Sopenharmony_ci  struct text_region r = FindNodeTextRegion();
4321cb0ef41Sopenharmony_ci  if (r.found_text_region == false)
4331cb0ef41Sopenharmony_ci    return ENOENT;
4341cb0ef41Sopenharmony_ci
4351cb0ef41Sopenharmony_ci  return MoveTextRegionToLargePages(r);
4361cb0ef41Sopenharmony_ci#else
4371cb0ef41Sopenharmony_ci  return ENOTSUP;
4381cb0ef41Sopenharmony_ci#endif
4391cb0ef41Sopenharmony_ci}
4401cb0ef41Sopenharmony_ci
4411cb0ef41Sopenharmony_ciconst char* LargePagesError(int status) {
4421cb0ef41Sopenharmony_ci  switch (status) {
4431cb0ef41Sopenharmony_ci    case ENOTSUP:
4441cb0ef41Sopenharmony_ci      return "Mapping to large pages is not supported.";
4451cb0ef41Sopenharmony_ci
4461cb0ef41Sopenharmony_ci    case EACCES:
4471cb0ef41Sopenharmony_ci      return "Large pages are not enabled.";
4481cb0ef41Sopenharmony_ci
4491cb0ef41Sopenharmony_ci    case ENOENT:
4501cb0ef41Sopenharmony_ci      return "failed to find text region";
4511cb0ef41Sopenharmony_ci
4521cb0ef41Sopenharmony_ci    case -1:
4531cb0ef41Sopenharmony_ci      return "Mapping code to large pages failed. Reverting to default page "
4541cb0ef41Sopenharmony_ci          "size.";
4551cb0ef41Sopenharmony_ci
4561cb0ef41Sopenharmony_ci    case 0:
4571cb0ef41Sopenharmony_ci      return "OK";
4581cb0ef41Sopenharmony_ci
4591cb0ef41Sopenharmony_ci    default:
4601cb0ef41Sopenharmony_ci      return "Unknown error";
4611cb0ef41Sopenharmony_ci  }
4621cb0ef41Sopenharmony_ci}
4631cb0ef41Sopenharmony_ci
4641cb0ef41Sopenharmony_ci}  // namespace node
465