12f09f445SMaksim Panchenko //===- bolt/runtime/hugify.cpp --------------------------------------------===//
29bd71615SXun Li //
3da752c9cSRafael Auler // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4da752c9cSRafael Auler // See https://llvm.org/LICENSE.txt for license information.
5da752c9cSRafael Auler // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
69bd71615SXun Li //
79bd71615SXun Li //===----------------------------------------------------------------------===//
89bd71615SXun Li 
9cb8d701bSVladislav Khmelevsky #if defined (__x86_64__)
10bbd9d610SAlexander Shaposhnikov #if !defined(__APPLE__)
11bbd9d610SAlexander Shaposhnikov 
129bd71615SXun Li #include "common.h"
139bd71615SXun Li #include <sys/mman.h>
149bd71615SXun Li 
159bd71615SXun Li // Enables a very verbose logging to stderr useful when debugging
169bd71615SXun Li //#define ENABLE_DEBUG
179bd71615SXun Li 
189bd71615SXun Li // Function pointers to init routines in the binary, so we can resume
199bd71615SXun Li // regular execution of the function that we hooked.
209bd71615SXun Li extern void (*__bolt_hugify_init_ptr)();
219bd71615SXun Li 
229bd71615SXun Li // The __hot_start and __hot_end symbols set by Bolt. We use them to figure
239bd71615SXun Li // out the rage for marking huge pages.
249bd71615SXun Li extern uint64_t __hot_start;
259bd71615SXun Li extern uint64_t __hot_end;
269bd71615SXun Li 
279bd71615SXun Li #ifdef MADV_HUGEPAGE
282da5b12aSAmir Ayupov /// Check whether the kernel supports THP via corresponding sysfs entry.
has_pagecache_thp_support()2984eae1a4SXun Li static bool has_pagecache_thp_support() {
302da5b12aSAmir Ayupov   char buf[256] = {0};
312da5b12aSAmir Ayupov   const char *madviseStr = "always [madvise] never";
329bd71615SXun Li 
332da5b12aSAmir Ayupov   int fd = __open("/sys/kernel/mm/transparent_hugepage/enabled",
342da5b12aSAmir Ayupov                   0 /* O_RDONLY */, 0);
352da5b12aSAmir Ayupov   if (fd < 0)
369bd71615SXun Li     return false;
372da5b12aSAmir Ayupov 
382da5b12aSAmir Ayupov   size_t res = __read(fd, buf, 256);
392da5b12aSAmir Ayupov   if (res < 0)
409bd71615SXun Li     return false;
412da5b12aSAmir Ayupov 
422da5b12aSAmir Ayupov   int cmp = strnCmp(buf, madviseStr, strLen(madviseStr));
432da5b12aSAmir Ayupov   return cmp == 0;
449bd71615SXun Li }
459bd71615SXun Li 
hugify_for_old_kernel(uint8_t * from,uint8_t * to)4684eae1a4SXun Li static void hugify_for_old_kernel(uint8_t *from, uint8_t *to) {
479bd71615SXun Li   size_t size = to - from;
489bd71615SXun Li 
499bd71615SXun Li   uint8_t *mem = reinterpret_cast<uint8_t *>(
509bd71615SXun Li       __mmap(0, size, 0x3 /* PROT_READ | PROT_WRITE*/,
519bd71615SXun Li              0x22 /* MAP_PRIVATE | MAP_ANONYMOUS*/, -1, 0));
529bd71615SXun Li 
539bd71615SXun Li   if (mem == (void *)MAP_FAILED) {
549bd71615SXun Li     char msg[] = "Could not allocate memory for text move\n";
559bd71615SXun Li     reportError(msg, sizeof(msg));
569bd71615SXun Li   }
579bd71615SXun Li #ifdef ENABLE_DEBUG
589bd71615SXun Li   reportNumber("Allocated temporary space: ", (uint64_t)mem, 16);
599bd71615SXun Li #endif
609bd71615SXun Li 
619bd71615SXun Li   // Copy the hot code to a temproary location.
62*ea2182feSMaksim Panchenko   memcpy(mem, from, size);
639bd71615SXun Li 
649bd71615SXun Li   // Maps out the existing hot code.
659bd71615SXun Li   if (__mmap(reinterpret_cast<uint64_t>(from), size,
669bd71615SXun Li              PROT_READ | PROT_WRITE | PROT_EXEC,
679bd71615SXun Li              MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1,
689bd71615SXun Li              0) == (void *)MAP_FAILED) {
699bd71615SXun Li     char msg[] = "failed to mmap memory for large page move terminating\n";
709bd71615SXun Li     reportError(msg, sizeof(msg));
719bd71615SXun Li   }
729bd71615SXun Li 
739bd71615SXun Li   // Mark the hot code page to be huge page.
749bd71615SXun Li   if (__madvise(from, size, MADV_HUGEPAGE) == -1) {
759bd71615SXun Li     char msg[] = "failed to allocate large page\n";
769bd71615SXun Li     reportError(msg, sizeof(msg));
779bd71615SXun Li   }
789bd71615SXun Li 
799bd71615SXun Li   // Copy the hot code back.
80*ea2182feSMaksim Panchenko   memcpy(from, mem, size);
819bd71615SXun Li 
829bd71615SXun Li   // Change permission back to read-only, ignore failure
839bd71615SXun Li   __mprotect(from, size, PROT_READ | PROT_EXEC);
849bd71615SXun Li 
859bd71615SXun Li   __munmap(mem, size);
869bd71615SXun Li }
879bd71615SXun Li #endif
889bd71615SXun Li 
__bolt_hugify_self_impl()899bd71615SXun Li extern "C" void __bolt_hugify_self_impl() {
909bd71615SXun Li #ifdef MADV_HUGEPAGE
919bd71615SXun Li   uint8_t *hotStart = (uint8_t *)&__hot_start;
929bd71615SXun Li   uint8_t *hotEnd = (uint8_t *)&__hot_end;
939bd71615SXun Li   // Make sure the start and end are aligned with huge page address
949bd71615SXun Li   const size_t hugePageBytes = 2L * 1024 * 1024;
959bd71615SXun Li   uint8_t *from = hotStart - ((intptr_t)hotStart & (hugePageBytes - 1));
969bd71615SXun Li   uint8_t *to = hotEnd + (hugePageBytes - 1);
979bd71615SXun Li   to -= (intptr_t)to & (hugePageBytes - 1);
989bd71615SXun Li 
999bd71615SXun Li #ifdef ENABLE_DEBUG
1009bd71615SXun Li   reportNumber("[hugify] hot start: ", (uint64_t)hotStart, 16);
1019bd71615SXun Li   reportNumber("[hugify] hot end: ", (uint64_t)hotEnd, 16);
1029bd71615SXun Li   reportNumber("[hugify] aligned huge page from: ", (uint64_t)from, 16);
1039bd71615SXun Li   reportNumber("[hugify] aligned huge page to: ", (uint64_t)to, 16);
1049bd71615SXun Li #endif
1059bd71615SXun Li 
1069bd71615SXun Li   if (!has_pagecache_thp_support()) {
1079bd71615SXun Li     hugify_for_old_kernel(from, to);
1089bd71615SXun Li     return;
1099bd71615SXun Li   }
1109bd71615SXun Li 
1119bd71615SXun Li   if (__madvise(from, (to - from), MADV_HUGEPAGE) == -1) {
1129bd71615SXun Li     char msg[] = "failed to allocate large page\n";
1139bd71615SXun Li     // TODO: allow user to control the failure behavior.
1149bd71615SXun Li     reportError(msg, sizeof(msg));
1159bd71615SXun Li   }
1169bd71615SXun Li #endif
1179bd71615SXun Li }
1189bd71615SXun Li 
1199bd71615SXun Li /// This is hooking ELF's entry, it needs to save all machine state.
__bolt_hugify_self()1209bd71615SXun Li extern "C" __attribute((naked)) void __bolt_hugify_self() {
121c6799a68SRafael Auler   __asm__ __volatile__(SAVE_ALL
122c6799a68SRafael Auler                        "call __bolt_hugify_self_impl\n"
123c6799a68SRafael Auler                        RESTORE_ALL
124c6799a68SRafael Auler                        "jmp *__bolt_hugify_init_ptr(%%rip)\n"
125c6799a68SRafael Auler                        :::);
1269bd71615SXun Li }
127bbd9d610SAlexander Shaposhnikov 
128bbd9d610SAlexander Shaposhnikov #endif
129cb8d701bSVladislav Khmelevsky #endif
130