• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "ecmascript/mem/heap-inl.h"
17 #include "ecmascript/mem/jit_fort.h"
18 #include "ecmascript/jit/jit.h"
19 #include "ecmascript/platform/file.h"
20 #include "ecmascript/platform/os.h"
21 #if defined(JIT_ENABLE_CODE_SIGN) && !defined(JIT_FORT_DISABLE)
22 #include <sys/ioctl.h>
23 #include <sys/prctl.h>
24 
25 #define XPM_JITFORT_ENABLE_OPCODE 3
26 #define XPM_MAGIC 'x'
27 #define XPM_SET_JITFORT_ENABLE _IOW(XPM_MAGIC, XPM_JITFORT_ENABLE_OPCODE, unsigned long)
28 #endif
29 
30 namespace panda::ecmascript {
31 
32 template <>
FreeListAllocator(BaseHeap * heap,MemDescPool * pool,JitFort * fort)33 FreeListAllocator<MemDesc>::FreeListAllocator(BaseHeap *heap, MemDescPool *pool, JitFort *fort)
34     : memDescPool_(pool), heap_(heap)
35 {
36     freeList_ = std::make_unique<FreeObjectList<MemDesc>>(fort);
37 }
38 
JitFort()39 JitFort::JitFort()
40 {
41     jitFortMem_ = PageMap(JIT_FORT_REG_SPACE_MAX,
42                           PageProtectProt(Jit::GetInstance()->IsDisableCodeSign() || !IsResourceAvailable()),
43                           DEFAULT_REGION_SIZE, nullptr, MAP_JITFORT);
44     jitFortBegin_ = reinterpret_cast<uintptr_t>(jitFortMem_.GetMem());
45     jitFortSize_ = JIT_FORT_REG_SPACE_MAX;
46     memDescPool_ = new MemDescPool(jitFortBegin_, jitFortSize_);
47     allocator_ = new FreeListAllocator<MemDesc>(nullptr, memDescPool_, this);
48     InitRegions();
49     PrctlSetVMA(jitFortMem_.GetMem(), jitFortSize_, "ArkTS Code Jit");
50     LOG_JIT(DEBUG) << "JitFort Begin " << (void *)JitFortBegin() << " end " << (void *)(JitFortBegin() + JitFortSize());
51 }
52 
~JitFort()53 JitFort::~JitFort()
54 {
55     constexpr size_t numRegions = JIT_FORT_REG_SPACE_MAX / DEFAULT_REGION_SIZE;
56     for (size_t i = 0; i < numRegions; i++) {
57         if (regions_[i] != nullptr) {
58             regions_[i]->DestroyFreeObjectSets();
59             delete regions_[i];
60         }
61     }
62     if (allocator_ != nullptr) {
63         delete allocator_;
64     }
65     if (memDescPool_ != nullptr) {
66         delete memDescPool_;
67     }
68     PageUnmap(jitFortMem_);
69 }
70 
InitRegions()71 void JitFort::InitRegions()
72 {
73     constexpr size_t numRegions = JIT_FORT_REG_SPACE_MAX / DEFAULT_REGION_SIZE;
74     for (size_t i = 0; i < numRegions; i++) {
75         uintptr_t mem = reinterpret_cast<uintptr_t>(jitFortMem_.GetMem()) + i*DEFAULT_REGION_SIZE;
76         uintptr_t end = mem + DEFAULT_REGION_SIZE;
77         JitFortRegion *region = new JitFortRegion(nullptr, mem, end, RegionSpaceFlag::IN_MACHINE_CODE_SPACE,
78             memDescPool_);
79         regions_[i] = region;
80     }
81     AddRegion();
82 }
83 
AddRegion()84 bool JitFort::AddRegion()
85 {
86     if (nextFreeRegionIdx_ < MAX_JIT_FORT_REGIONS) {
87         allocator_->AddFree(regions_[nextFreeRegionIdx_]);
88         regionList_.AddNode(regions_[nextFreeRegionIdx_]);
89         nextFreeRegionIdx_++;
90         return true;
91     }
92     return false;
93 }
94 
95 // Fort buf allocation is in multiples of FORT_BUF_ALIGN
FortAllocSize(size_t instrSize)96 size_t JitFort::FortAllocSize(size_t instrSize)
97 {
98     return(AlignUp(instrSize, FORT_BUF_ALIGN));
99 }
100 
Allocate(MachineCodeDesc * desc)101 uintptr_t JitFort::Allocate(MachineCodeDesc *desc)
102 {
103     LockHolder lock(mutex_);
104 
105     size_t size = FortAllocSize(desc->instructionsSize);
106     auto ret = allocator_->Allocate(size);
107     if (ret == ToUintPtr(nullptr)) {
108         if (AddRegion()) {
109             LOG_JIT(DEBUG) << "JitFort: Allocate - AddRegion";
110             ret = allocator_->Allocate(size);
111         }
112     }
113     if (ret == ToUintPtr(nullptr)) {
114         LOG_JIT(DEBUG) << "JitFort:: Allocate return nullptr for size " << size;
115         return ret;
116     }
117     // Record allocation to keep it from being collected by the next
118     // JitFort::UpdateFreeSpace in case corresponding Machine code object is not
119     // marked for sweep yet by then.
120     ASSERT((ret & FORT_BUF_ADDR_MASK) == 0);
121     MarkJitFortMemAwaitInstall(ret, size);
122     LOG_JIT(DEBUG) << "JitFort:: Allocate " << (void *)ret << " - " << (void *)(ret+size-1) <<
123         " size " << size << " instructionsSize " << desc->instructionsSize;
124     return ret;
125 }
126 
127 // Called by GC thread during Mark to mark Fort buf alive.
128 // Encoding details in GCBitset for marking live Fort buffers:
129 //   Begin of a live Fort buf:
130 //     - 10: a live Jit Fort buf that has been installed
131 //     - 11: a live Jit Fort buf that has not been installed yet
132 //   End of a live Fort buf:
133 //     - 01
134 // This encoding requires 4 GCBitset bits to mark a live JitFort
135 // buffer, which makes the minimum Fort buffer allocation size 32 bytes.
MarkJitFortMemAlive(MachineCode * obj)136 void JitFort::MarkJitFortMemAlive(MachineCode *obj)
137 {
138     size_t size = FortAllocSize(obj->GetInstructionsSize());
139     uintptr_t addr = obj->GetText();
140     uintptr_t endAddr = addr + size - 1;
141     uint32_t regionIdx = AddrToFortRegionIdx(addr);
142     regions_[regionIdx]->AtomicMark(reinterpret_cast<void *>(addr));
143     regions_[regionIdx]->AtomicMark(reinterpret_cast<void *>(endAddr));
144     LOG_JIT(DEBUG) << "MarkFortMemAlive: addr " << (void *)addr << " size " << size
145         << " regionIdx " << regionIdx << " instructionsSize " << obj->GetInstructionsSize();
146 }
147 
148 // Called by Jit Compile thread during JitFort Allocate to mark Fort buf
149 // awaiting install. Need mutex (JitGCLockHolder) for region gcBitset access.
150 // See JitFort::MarkJitFortMemAlive comments for mark bit encoding in GC bitset.
MarkJitFortMemAwaitInstall(uintptr_t addr,size_t size)151 void JitFort::MarkJitFortMemAwaitInstall(uintptr_t addr, size_t size)
152 {
153     uintptr_t endAddr = addr + size - 1;
154     uint32_t regionIdx = AddrToFortRegionIdx(addr);
155     regions_[regionIdx]->AtomicMark(reinterpret_cast<void *>(addr));
156     regions_[regionIdx]->AtomicMark(reinterpret_cast<void *>(addr + sizeof(uint64_t))); // mark next bit
157     regions_[regionIdx]->AtomicMark(reinterpret_cast<void *>(endAddr));
158     LOG_JIT(DEBUG) << "MarkFortMemAwaitInstall: addr " << (void *)addr << " size " << size
159         << " regionIdx " << regionIdx;
160 }
161 
162 // Called by JS/Main thread during SafePoint to clear Fort buf AwaitInstall bit
163 // See JitFort::MarkJitFortMemAlive comments for mark bit encoding in GC bitset.
MarkJitFortMemInstalled(MachineCode * obj)164 void JitFort::MarkJitFortMemInstalled(MachineCode *obj)
165 {
166     size_t size = FortAllocSize(obj->GetInstructionsSize());
167     uintptr_t addr = obj->GetText();
168     uint32_t regionIdx = AddrToFortRegionIdx(addr);
169     regions_[regionIdx]->GetGCBitset()->ClearMark(addr + sizeof(uint64_t)); // clear next bit
170     LOG_JIT(DEBUG) << "MarkFortMemInstalled: addr " << (void *)addr << " size " << size
171         << " regionIdx " << regionIdx << " instructionsSize " << obj->GetInstructionsSize();
172 }
173 
AddrToFortRegionIdx(uint64_t addr)174 uint32_t JitFort::AddrToFortRegionIdx(uint64_t addr)
175 {
176     ASSERT(InRange(addr));
177     uint32_t regionIdx = ((addr - JitFortBegin()) & ~(DEFAULT_REGION_MASK)) >> REGION_SIZE_LOG2;
178     ASSERT(regionIdx < MAX_JIT_FORT_REGIONS);
179     return regionIdx;
180 }
181 
182 /*
183  * Called from GC worker thread duing Old/Full GC Sweep (AsyncSweep). Mutex is needed
184  * to ensure exclusive access to JitFort memory by GC thread when it frees JitFort mem
185  * blocks, and by Jit compiled thread when it allocates Fort mem.
186  */
UpdateFreeSpace()187 void JitFort::UpdateFreeSpace()
188 {
189     if (!Jit::GetInstance()->IsEnableJitFort()) {
190         return;
191     }
192 
193     LockHolder lock(mutex_);
194 
195     if (!regionList_.GetLength()) { // LCOV_EXCL_BR_LINE
196         return;
197     }
198     LOG_JIT(DEBUG) << "UpdateFreeSpace enter: " << "Fort space allocated: "
199         << allocator_->GetAllocatedSize()
200         << " available: " << allocator_->GetAvailableSize();
201     allocator_->RebuildFreeList();
202     JitFortRegion *region = regionList_.GetFirst();
203     while (region) {
204         FreeRegion(region);
205         region = region->GetNext();
206     }
207     LOG_JIT(DEBUG) << "UpdateFreeSpace exit: allocator_->GetAvailableSize  "
208         << allocator_->GetAvailableSize();
209 }
210 
ClearMarkBits()211 void JitFort::ClearMarkBits()
212 {
213     ASSERT(g_isEnableCMCGC);
214     JitFortRegion *region = regionList_.GetFirst();
215     while (region) {
216         region->GetGCBitset()->IterateMarkedBitsConst(
217             region->GetBegin(), region->GetGCBitsetSize(),
218             [](void *, size_t) {}); // Dummy vistor, only need to trigger bits clearing.
219         region = region->GetNext();
220     }
221 }
222 
FreeRegion(JitFortRegion * region)223 void JitFort::FreeRegion(JitFortRegion *region)
224 {
225     LOG_JIT(DEBUG) << "JitFort FreeRegion " << (void*)(region->GetBegin());
226 
227     uintptr_t freeStart = region->GetBegin();
228     region->GetGCBitset()->IterateMarkedBitsConst(
229         region->GetBegin(), region->GetGCBitsetSize(),
230         [this, &region, &freeStart](void *mem, size_t size) {
231             ASSERT(region->InRange(ToUintPtr(mem)));
232             (void) region;
233             uintptr_t freeEnd = ToUintPtr(mem);
234             if (freeStart != freeEnd) {
235                 allocator_->Free(freeStart, freeEnd - freeStart, true);
236             }
237             freeStart = freeEnd + size;
238         });
239     uintptr_t freeEnd = region->GetEnd();
240     if (freeStart != freeEnd) {
241         allocator_->Free(freeStart, freeEnd - freeStart, true);
242     }
243 }
244 
InRange(uintptr_t address) const245 bool JitFort::InRange(uintptr_t address) const
246 {
247     return address >= jitFortBegin_ && (jitFortBegin_ + jitFortSize_) > 1 &&
248         address <= (jitFortBegin_ + jitFortSize_ - 1);
249 }
250 
PrepareSweeping()251 void JitFort::PrepareSweeping()
252 {
253     isSweeping_.store(false, std::memory_order_release);
254 }
255 
256 // concurrent sweep - only one of the AsyncSweep task will do JitFort sweep
AsyncSweep()257 void JitFort::AsyncSweep()
258 {
259     bool expect = false;
260     if (isSweeping_.compare_exchange_strong(expect, true, std::memory_order_seq_cst)) {
261         LOG_JIT(DEBUG) << "JitFort::AsyncSweep";
262         UpdateFreeSpace();
263     }
264 }
265 
266 // non-concurrent sweep
Sweep()267 void JitFort::Sweep()
268 {
269     LOG_JIT(DEBUG) << "JitFort::Sweep";
270     UpdateFreeSpace();
271 }
272 
273 // Used by JitFort::UpdateFreeSpace call path to find corresponding
274 // JitFortRegion for a free block in JitFort space, in order to put the blk into
275 // the corresponding free set of the JitFortRegion the free block belongs.
ObjectAddressToRange(uintptr_t addr)276 JitFortRegion *JitFort::ObjectAddressToRange(uintptr_t addr)
277 {
278     return regions_[AddrToFortRegionIdx(addr)];
279 }
280 
MarkStartAddr(bool awaitInstall,uintptr_t startAddr,uint32_t index,uint32_t & word)281 void JitFortGCBitset::MarkStartAddr(bool awaitInstall, uintptr_t startAddr, uint32_t index, uint32_t &word)
282 {
283     if (!awaitInstall) {
284         ClearMark(startAddr);
285         word &= ~(1u << index);
286     } else {
287         word &= ~(1u << index);
288         word &= ~(1u << (index+1));
289     }
290 }
291 
MarkEndAddr(bool awaitInstall,uintptr_t endAddr,uint32_t index,uint32_t & word)292 void JitFortGCBitset::MarkEndAddr(bool awaitInstall, uintptr_t endAddr, uint32_t index, uint32_t &word)
293 {
294     if (!awaitInstall) {
295         ClearMark(endAddr - 1);
296     }
297     word &= ~(1u << index);
298 }
299 
300 // See JitFort::MarkJitFortMemAlive comments for mark bit encoding in JitFort GC bitset.
301 template <typename Visitor>
IterateMarkedBitsConst(uintptr_t regionAddr,size_t bitsetSize,Visitor visitor)302 void JitFortGCBitset::IterateMarkedBitsConst(uintptr_t regionAddr, size_t bitsetSize, Visitor visitor)
303 {
304     bool awaitInstall = false;
305     uintptr_t startAddr = 0;
306     uintptr_t endAddr = 0;
307 
308     auto words = Words();
309     uint32_t wordCount = WordCount(bitsetSize);
310     uint32_t index = BIT_PER_WORD;
311     for (uint32_t i = 0; i < wordCount; i++) {
312         uint32_t word = words[i];
313         while (word != 0) {
314             index = static_cast<uint32_t>(__builtin_ctz(word));
315             ASSERT(index < BIT_PER_WORD);
316             if (!startAddr) {
317                 startAddr = regionAddr + (index << TAGGED_TYPE_SIZE_LOG);
318                 awaitInstall = Test(regionAddr + ((index+1) << TAGGED_TYPE_SIZE_LOG));
319                 MarkStartAddr(awaitInstall, startAddr, index, word);
320             } else {
321                 endAddr = regionAddr + ((index+1) << TAGGED_TYPE_SIZE_LOG);
322                 LOG_JIT(DEBUG) << "Live Jit Mem " << (void *)startAddr << " size " << endAddr-startAddr;
323                 visitor(reinterpret_cast<void *>(startAddr), endAddr - startAddr);
324                 MarkEndAddr(awaitInstall, endAddr, index, word);
325                 awaitInstall = false;
326                 startAddr = 0;
327             }
328         }
329         regionAddr += TAGGED_TYPE_SIZE * BIT_PER_WORD;
330     }
331 }
332 
333 bool JitFort::isResourceAvailable_ = true;
IsResourceAvailable()334 bool JitFort::IsResourceAvailable()
335 {
336     return isResourceAvailable_;
337 }
InitJitFortResource()338 void JitFort::InitJitFortResource()
339 {
340 #if defined(JIT_ENABLE_CODE_SIGN) && !defined(JIT_FORT_DISABLE)
341     ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "JIT::InitJitFortResource", "");
342     if (!Jit::GetInstance()->IsAppJit()) {
343         int fd = open("/dev/xpm", O_RDWR);
344         if (fd < 0) {
345             isResourceAvailable_ = false;
346             LOG_JIT(ERROR) << "Failed to init jitfort resource, open xpm failed: " << strerror(errno);
347             return;
348         }
349         FdsanExchangeOwnerTag(reinterpret_cast<fd_t>(fd));
350         int rc = ioctl(fd, XPM_SET_JITFORT_ENABLE, 0);
351         if (rc < 0) {
352             isResourceAvailable_ = false;
353             LOG_JIT(ERROR) << "Failed to init jitfort resource, enable xpm failed: " << strerror(errno);
354             Close(reinterpret_cast<fd_t>(fd));
355             return;
356         }
357         Close(reinterpret_cast<fd_t>(fd));
358     }
359     constexpr int prSetJitFort = 0x6a6974;
360     constexpr int jitFortInit = 5;
361     int res = prctl(prSetJitFort, jitFortInit, 0);
362     if (res < 0) {
363         isResourceAvailable_ = false;
364         LOG_JIT(ERROR) << "Failed to init jitfort resource: " << strerror(errno);
365         return;
366     }
367     res = prctl(prSetJitFort, jitFortInit, 0);
368     if (res >= 0 || errno != EEXIST) {
369         isResourceAvailable_ = false;
370         LOG_JIT(ERROR) << "jitfort not support";
371     }
372 #endif
373 }
374 
MemDescPool(uintptr_t fortBegin,size_t fortSize)375 MemDescPool::MemDescPool(uintptr_t fortBegin, size_t fortSize)
376     : fortBegin_(fortBegin), fortSize_(fortSize)
377 {
378     Expand();
379 }
380 
~MemDescPool()381 MemDescPool::~MemDescPool()
382 {
383     for (const auto& block : memDescBlocks_) {
384         if (block) {
385             free(block);
386         }
387     }
388 }
389 
GetDesc()390 MemDesc *MemDescPool::GetDesc()
391 {
392     if (IsEmpty(freeList_)) {
393         Expand();
394     }
395     if (!IsEmpty(freeList_)) { // LCOV_EXCL_BR_LINE
396         MemDesc *res = freeList_;
397         freeList_ = freeList_->GetNext();
398         allocated_++;
399         if (allocated_-returned_ > highwater_) {
400             highwater_ = allocated_ - returned_;
401         }
402         return res;
403     }
404     return nullptr;
405 }
406 
Expand()407 void MemDescPool::Expand()
408 {
409     void *block = malloc(sizeof(MemDesc) * MEMDESCS_PER_BLOCK);
410     if (block) {
411         memDescBlocks_.push_back(block);
412         for (size_t i = 0; i < MEMDESCS_PER_BLOCK; ++i) {
413             Add(new (ToVoidPtr(reinterpret_cast<uintptr_t>(block) + i*sizeof(MemDesc))) MemDesc());
414         }
415     }
416 }
417 
Add(MemDesc * desc)418 void MemDescPool::Add(MemDesc *desc)
419 {
420     desc->SetNext(freeList_);
421     freeList_ = desc;
422 }
423 
424 }  // namespace panda::ecmascript
425