ReXGlue SDK
Loading...
Searching...
No Matches
heap.h
Go to the documentation of this file.
1
13
14#pragma once
15
16#include <atomic>
17#include <cassert>
18#include <cstddef>
19#include <cstdint>
20#include <memory>
21#include <mutex>
22#include <string>
23#include <type_traits>
24#include <utility>
25#include <vector>
26
27#include <rex/platform.h>
32#include <rex/types.h>
33
34namespace rex::kernel {
35
36inline constexpr u32 kPageSize4KiB = 4 * 1024;
37
39inline constexpr u32 kPageSize64KiB = 64 * 1024;
40
41inline constexpr u32 kPageSize16MiB = 16 * 1024 * 1024;
42
43} // namespace rex::kernel
44
45namespace rex::kernel::detail {
46
52template <typename T, bool IsOpaque = std::is_void_v<T> || std::is_function_v<T>>
53struct PointeeAlignment {
54 static constexpr u32 kValue = 1;
55};
56
58template <typename T>
59struct PointeeAlignment<T, false> {
60 static constexpr u32 kValue = static_cast<u32>(alignof(T));
61};
62
63} // namespace rex::kernel::detail
64
65namespace rex::kernel {
66
67class GuestAllocator;
69class WorkspaceHeap;
70
72enum SystemHeapFlag : u8 {
73 kSystemHeapVirtual = 1 << 0,
74 kSystemHeapPhysical = 1 << 1,
75
76 kSystemHeapDefault = kSystemHeapVirtual,
77};
78
81 public:
82 virtual ~ISystemHeap() = default;
83
89 virtual u32 SystemHeapAlloc(u32 size, u32 alignment = 0x20,
90 u32 system_heap_flags = kSystemHeapDefault) = 0;
91
97 virtual void SystemHeapFree(u32 address, u32* out_region_size = nullptr) = 0;
98
99 [[nodiscard]] virtual bool IsAddressMapped(u32 address) const = 0;
100
112 virtual u32 AllocPhysical(u32 size, u32 page_size, u32 protect, u32 min_address, u32 max_address,
113 u32 alignment) = 0;
114};
115
116enum class HeapType : u8 {
117 kGuestVirtual,
119 kGuestPhysical,
120};
121
123 kMemoryAllocationReserve = 1 << 0,
124 kMemoryAllocationCommit = 1 << 1,
127};
128
129enum MemoryProtectFlag : u8 {
130 kMemoryProtectRead = 1 << 0,
131 kMemoryProtectWrite = 1 << 1,
132 kMemoryProtectNoCache = 1 << 2,
133 kMemoryProtectWriteCombine = 1 << 3,
134
135 kMemoryProtectNoAccess = 0,
136};
137
153
155 u64 qword;
156 struct {
158 u32 base_address : 20;
160 u32 region_page_count : 20;
162 u32 allocation_protect : 4;
164 u32 current_protect : 4;
166 u32 state : 2;
168 u32 title : 1;
169 u32 reserved : 13;
170 };
171};
172
173class REX_KERNEL_API BaseHeap {
174 public:
176 virtual ~BaseHeap();
177
179 u32 GetHeapBase() const { return heap_base_; }
180
181 u32 GetHeapSize() const { return heap_size_; }
182
183 u32 GetPageSize() const { return page_size_; }
184
185 HeapType GetHeapType() const { return heap_type_; }
186
191
193 template <typename T = u8*>
194 T TranslateRelative(size_t relative_address) const {
195 return reinterpret_cast<T>(membase_ + heap_base_ + host_address_offset_ + relative_address);
196 }
197
199 virtual void Dispose();
200
201 u32 GetTotalPageCount() const { return static_cast<u32>(page_table_.size()); }
202
203 u32 GetUnreservedPageCount() const { return unreserved_page_count_; }
204
205 u32 GetReservedPageCount() const { return GetTotalPageCount() - GetUnreservedPageCount(); }
206
207 u32 GetCommittedPages() const { return committed_pages_.load(std::memory_order_relaxed); }
208
212 void PlotProfile() const;
213
222 virtual bool Alloc(u32 size, u32 alignment, u32 allocation_type, u32 protect, bool top_down,
223 u32* out_address);
224
232 virtual bool AllocFixed(u32 base_address, u32 size, u32 alignment, u32 allocation_type,
233 u32 protect);
234
243 virtual bool AllocRange(u32 low_address, u32 high_address, u32 size, u32 alignment,
244 u32 allocation_type, u32 protect, bool top_down, u32* out_address);
245
254 virtual bool AllocSystemHeap(u32 size, u32 alignment, u32 allocation_type, u32 protect,
255 bool top_down, u32* out_address);
256
258 virtual bool Decommit(u32 address, u32 size);
259
265 virtual bool Release(u32 address, u32* out_region_size = nullptr);
266
276 bool ReleaseRange(u32 address, u32 size, u32* out_address, u32* out_size);
277
284 virtual bool Protect(u32 address, u32 size, u32 protect, u32* old_protect = nullptr);
285
291 bool QueryRegionInfo(u32 base_address, HeapAllocation* out_info);
292
298 bool QuerySize(u32 address, u32* out_size);
299
305 bool QueryProtect(u32 address, u32* out_protect);
306
308 rex::memory::PageAccess QueryRangeAccess(u32 low_address, u32 high_address);
309
317 rex::memory::PageAccess GetHostPageAccess(u32 host_page_number) const;
318
326 bool ReapplyHostPageAccess(void* host_address, bool is_write);
327
328 protected:
329 friend class PhysicalHeap;
330
331 BaseHeap();
332
340 rex::memory::PageAccess GetHostPageGuestAccess(u32 host_page_number) const;
341
350 bool SyncHostPageAccess(u32 start_page_number, u32 end_page_number);
351
357 virtual std::unique_lock<rex::platform::RankedMutex> AcquireHostPageReconcileLock() const;
358
364
365 bool AllocFixedLocked(u32 base_address, u32 size, u32 alignment, u32 allocation_type,
366 u32 protect);
367
368 bool AllocRangeLocked(u32 low_address, u32 high_address, u32 size, u32 alignment,
369 u32 allocation_type, u32 protect, bool top_down, u32* out_address);
370
371 bool DecommitLocked(u32 address, u32 size);
372
373 bool ReleaseLocked(u32 address, u32* out_region_size);
374
375 bool ReleaseRangeLocked(u32 address, u32 size, u32* out_address, u32* out_size);
376
377 bool ProtectLocked(u32 address, u32 size, u32 protect, u32* old_protect);
378
389 bool CheckProtectRangeLocked(u32 address, u32 size, u32* out_start_page, u32* out_end_page);
390
397 virtual bool IsHostPageWriteWatched([[maybe_unused]] u32 host_page_number) const { return false; }
398
402 void OnPageCommitted(u32 page_number);
403
407 void OnPageDecommitted(u32 page_number);
408
415 void Initialize(kernel::GuestAddressSpace* memory, u8* membase, HeapType heap_type, u32 heap_base,
416 u32 heap_size, u32 page_size, u32 host_address_offset = 0);
417
420 u8* membase_;
421 HeapType heap_type_;
422 u32 heap_base_;
423 u32 heap_size_;
424 u32 page_size_;
429 u32 unreserved_page_count_ = 0;
431 std::atomic<u32> committed_pages_{0};
433 const char* profile_pool_ = nullptr;
434 const char* profile_plot_ = nullptr;
435 std::recursive_mutex heap_mutex_;
436 std::vector<PageEntry> page_table_;
437};
438
439class REX_KERNEL_API VirtualHeap : public BaseHeap {
440 public:
441 VirtualHeap();
442 ~VirtualHeap() override;
443
449 void Initialize(kernel::GuestAddressSpace* memory, u8* membase, HeapType heap_type, u32 heap_base,
450 u32 heap_size, u32 page_size);
451};
452
457class REX_KERNEL_API PhysicalHeap : public BaseHeap {
458 public:
459 PhysicalHeap();
460 ~PhysicalHeap() override;
461
468 void Initialize(kernel::GuestAddressSpace* memory, u8* membase, HeapType heap_type, u32 heap_base,
469 u32 heap_size, u32 page_size, VirtualHeap* parent_heap);
470
479 bool Alloc(u32 size, u32 alignment, u32 allocation_type, u32 protect, bool top_down,
480 u32* out_address) override;
488 bool AllocFixed(u32 base_address, u32 size, u32 alignment, u32 allocation_type,
489 u32 protect) override;
498 bool AllocRange(u32 low_address, u32 high_address, u32 size, u32 alignment, u32 allocation_type,
499 u32 protect, bool top_down, u32* out_address) override;
508 bool AllocSystemHeap(u32 size, u32 alignment, u32 allocation_type, u32 protect, bool top_down,
509 u32* out_address) override;
511 bool Decommit(u32 address, u32 size) override;
517 bool Release(u32 base_address, u32* out_region_size = nullptr) override;
525 bool Protect(u32 address, u32 size, u32 protect, u32* old_protect = nullptr) override;
526
533 void EnableAccessCallbacks(u32 physical_address, u32 length,
534 bool enable_invalidation_notifications, bool enable_data_providers);
542 bool TriggerCallbacks(u32 virtual_address, u32 length, bool is_write, bool unwatch_exact_range,
543 bool unprotect = true);
544
545 bool TriggerCallbacksLocked(u32 virtual_address, u32 length, bool is_write,
546 bool unwatch_exact_range, bool unprotect = true);
547
553 u32 GetPhysicalAddress(u32 address) const;
554
555 protected:
556 bool IsHostPageWriteWatched(u32 host_page_number) const override;
557
564 std::unique_lock<rex::platform::RankedMutex> AcquireHostPageReconcileLock() const override;
565
567
568 u32 system_page_size_;
571
577
578 std::vector<SystemPageFlagsBlock> system_page_flags_;
579};
580
589
595 public:
599
606
608 const std::filesystem::path& GetFileName() const { return file_name_; }
609
610 u8* GetVirtualMembase() const { return virtual_membase_; }
611
614 return rex::memory::GuestMembase(virtual_membase_);
615 }
616
621 template <typename T = u8*>
622 T TranslateVirtual(u32 guest_address) const {
623 assert(!LookupHeap(guest_address) ||
624 LookupHeap(guest_address)->GetHostAddressOffset() ==
625 rex::memory::detail::PhysicalHostOffset(guest_address));
626 return rex::memory::GuestPtr<T>(virtual_membase_, guest_address);
627 }
628
636 template <typename T = u8*>
637 T TryTranslateVirtual(u32 guest_address, u32 required = kMemoryProtectRead) const {
638 static_assert(std::is_pointer_v<T>, "TryTranslateVirtual returns a pointer type");
639 constexpr u32 kAlignment = detail::PointeeAlignment<std::remove_pointer_t<T>>::kValue;
640 if (guest_address == 0 || guest_address % kAlignment != 0) {
641 return nullptr;
642 }
643 const BaseHeap* heap = LookupHeap(guest_address);
644 u32 protect = 0;
645 if (heap == nullptr || !const_cast<BaseHeap*>(heap)->QueryProtect(guest_address, &protect) ||
646 (protect & required) != required) {
647 return nullptr;
648 }
649 return rex::memory::GuestPtr<T>(virtual_membase_, guest_address);
650 }
651
652 u8* GetPhysicalMembase() const { return physical_membase_; }
653
655 template <typename T = u8*>
656 T TranslatePhysical(u32 guest_address) const {
657 return reinterpret_cast<T>(physical_membase_ + (guest_address & 0x1FFFFFFF));
658 }
659
663 u32 HostToGuestVirtual(const void* host_address) const;
664
669 u32 GetPhysicalAddress(u32 address) const;
670
671 void Zero(u32 address, u32 size) const;
672
673 void Fill(u32 address, u32 size, u8 value) const;
674
676 void Copy(u32 dest, u32 src, u32 size) const;
677
686 AccessViolationResult HandleAccessViolation(void* host_address, bool is_write);
687
703 using PhysicalMemoryInvalidationCallback = std::pair<u32, u32> (*)(void* context_ptr,
704 u32 physical_address_start,
705 u32 length, bool exact_range);
712 void* callback_context);
719
723 void EnablePhysicalMemoryAccessCallbacks(u32 physical_address, u32 length,
724 bool enable_invalidation_notifications,
725 bool enable_data_providers);
726
733 u32 SystemHeapAlloc(u32 size, u32 alignment = 0x20,
734 u32 system_heap_flags = kSystemHeapDefault) override;
735
736 void SystemHeapFree(u32 address, u32* out_region_size = nullptr) override;
737
738 u32 AllocPhysical(u32 size, u32 page_size, u32 protect, u32 min_address, u32 max_address,
739 u32 alignment) override;
740
741 bool IsAddressMapped(u32 address) const override { return LookupHeap(address) != nullptr; }
742
743 const BaseHeap* LookupHeap(u32 address) const;
744
745 BaseHeap* LookupHeap(u32 address) {
746 return const_cast<BaseHeap*>(
747 const_cast<const kernel::GuestAddressSpace*>(this)->LookupHeap(address));
748 }
749
750 BaseHeap* LookupHeapByType(bool physical, u32 page_size);
751
758 void GetHeapsPageStats(const BaseHeap* const* provided_heaps, size_t heaps_count,
759 u32& unreserved_pages, u32& reserved_pages, u32& used_pages,
760 u32& reserved_bytes);
761
762 VirtualHeap* GetPhysicalHeap();
763
769 void SetBudget(u32 megabytes);
770
772 [[nodiscard]] u32 GetTitlePages() const;
773
777 [[nodiscard]] u32 GetBudgetPages() const;
778
783
791 bool InitializeFunctionTable(u32 code_base, u32 code_size, u32 image_base, u32 image_size);
798 bool DestroyFunctionTable(u32 code_base);
804 bool SetFunction(u32 guest_address, PPCFunc* host_function);
805
811 [[nodiscard]] std::unique_lock<rex::platform::RankedMutex> LockWatches() {
812 return std::unique_lock(watch_mutex_);
813 }
814
815 private:
816#if REX_PLATFORM_MAC
817 int MapViewsMac();
818#endif
819 int MapViews(u8* mapping_base);
820 void UnmapViews();
821 bool ReserveMMIOBlocks() const;
822
823 bool AccessViolationCallback(void* host_address, bool is_write);
824
831 bool ReserveBudgetPages(u32 pages);
832
836 void ReleaseBudgetPages(u32 pages);
837
838 std::filesystem::path file_name_;
839 u32 system_page_size_ = 0;
840 u32 system_allocation_granularity_ = 0;
841 u8* virtual_membase_ = nullptr;
842 u8* physical_membase_ = nullptr;
843
844 struct FunctionTableEntry {
845 u32 table_base;
846 u32 code_base;
847 u32 code_size;
848 u32 thunk_reserve;
849 };
850 std::vector<FunctionTableEntry> function_tables_;
851 mutable std::mutex function_tables_mutex_;
852
854 u8* mapping_base_ = nullptr;
855 union {
856 struct {
857 u8* v00000000;
858 u8* v40000000;
859 u8* v7f000000;
860 u8* v80000000;
861 u8* v90000000;
862 u8* va0000000;
863 u8* vc0000000;
864 u8* ve0000000;
865 u8* physical;
866 };
867 u8* all_views[9];
868 } views_ = {{0, 0, 0, 0, 0, 0, 0, 0, 0}};
869
870 struct {
871 VirtualHeap v00000000;
872 VirtualHeap v40000000;
873 VirtualHeap v80000000;
874 VirtualHeap v90000000;
875
876 VirtualHeap physical;
877 PhysicalHeap va0000000;
878 PhysicalHeap vc0000000;
879 PhysicalHeap ve0000000;
880 } heaps_;
881
882 friend class BaseHeap;
883 friend class GuestAllocator;
884 friend class WorkspaceHeap;
885
886 friend class PhysicalHeap;
887 rex::platform::RankedMutex watch_mutex_{rex::platform::LockRank::kLeaf};
888 std::vector<std::unique_ptr<std::pair<PhysicalMemoryInvalidationCallback, void*>>>
889 physical_memory_invalidation_callbacks_;
890 std::atomic<u32> title_pages_{0};
891 std::atomic<u32> budget_pages_{0};
892 std::mutex allocators_mutex_;
893 std::vector<GuestAllocator*> allocators_;
894 std::vector<const WorkspaceHeap*> workspaces_;
895};
896
897} // namespace rex::kernel
Definition heap.h:173
virtual bool AllocSystemHeap(u32 size, u32 alignment, u32 allocation_type, u32 protect, bool top_down, u32 *out_address)
const char * profile_pool_
Definition heap.h:433
std::atomic< u32 > committed_pages_
Definition heap.h:431
u32 GetHostAddressOffset() const
Definition heap.h:190
u32 host_address_offset_
Definition heap.h:428
void OnPageCommitted(u32 page_number)
rex::memory::PageAccess QueryRangeAccess(u32 low_address, u32 high_address)
bool QueryRegionInfo(u32 base_address, HeapAllocation *out_info)
bool ReapplyHostPageAccess(void *host_address, bool is_write)
u32 page_size_shift_
Definition heap.h:426
virtual bool AllocRange(u32 low_address, u32 high_address, u32 size, u32 alignment, u32 allocation_type, u32 protect, bool top_down, u32 *out_address)
virtual bool Protect(u32 address, u32 size, u32 protect, u32 *old_protect=nullptr)
bool QueryProtect(u32 address, u32 *out_protect)
bool CheckProtectRangeLocked(u32 address, u32 size, u32 *out_start_page, u32 *out_end_page)
void Initialize(kernel::GuestAddressSpace *memory, u8 *membase, HeapType heap_type, u32 heap_base, u32 heap_size, u32 page_size, u32 host_address_offset=0)
virtual bool Release(u32 address, u32 *out_region_size=nullptr)
void PlotProfile() const
rex::memory::PageAccess GetHostPageAccess(u32 host_page_number) const
virtual bool AllocFixed(u32 base_address, u32 size, u32 alignment, u32 allocation_type, u32 protect)
bool ReleaseRange(u32 address, u32 size, u32 *out_address, u32 *out_size)
kernel::GuestAddressSpace * memory_
Definition heap.h:419
T TranslateRelative(size_t relative_address) const
Definition heap.h:194
rex::memory::PageAccess GetHostPageGuestAccess(u32 host_page_number) const
virtual void Dispose()
void OnPageDecommitted(u32 page_number)
virtual bool IsHostPageWriteWatched(u32 host_page_number) const
Definition heap.h:397
virtual std::unique_lock< rex::platform::RankedMutex > AcquireHostPageReconcileLock() const
u32 GetSystemRangeBase() const
bool QuerySize(u32 address, u32 *out_size)
virtual bool Decommit(u32 address, u32 size)
u32 GetHeapBase() const
Definition heap.h:179
std::recursive_mutex heap_mutex_
Definition heap.h:435
virtual bool Alloc(u32 size, u32 alignment, u32 allocation_type, u32 protect, bool top_down, u32 *out_address)
bool SyncHostPageAccess(u32 start_page_number, u32 end_page_number)
Definition heap.h:594
T TranslatePhysical(u32 guest_address) const
Definition heap.h:656
bool DestroyFunctionTable(u32 code_base)
bool SetFunction(u32 guest_address, PPCFunc *host_function)
std::pair< u32, u32 >(*)(void *context_ptr, u32 physical_address_start, u32 length, bool exact_range) PhysicalMemoryInvalidationCallback
Definition heap.h:703
AccessViolationResult HandleAccessViolation(void *host_address, bool is_write)
std::unique_lock< rex::platform::RankedMutex > LockWatches()
Definition heap.h:811
const std::filesystem::path & GetFileName() const
Definition heap.h:608
u32 SystemHeapAlloc(u32 size, u32 alignment=0x20, u32 system_heap_flags=kSystemHeapDefault) override
void GetHeapsPageStats(const BaseHeap *const *provided_heaps, size_t heaps_count, u32 &unreserved_pages, u32 &reserved_pages, u32 &used_pages, u32 &reserved_bytes)
u32 GetPhysicalAddress(u32 address) const
void EnablePhysicalMemoryAccessCallbacks(u32 physical_address, u32 length, bool enable_invalidation_notifications, bool enable_data_providers)
void SetBudget(u32 megabytes)
void * RegisterPhysicalMemoryInvalidationCallback(PhysicalMemoryInvalidationCallback callback, void *callback_context)
u32 AllocPhysical(u32 size, u32 page_size, u32 protect, u32 min_address, u32 max_address, u32 alignment) override
void Copy(u32 dest, u32 src, u32 size) const
u32 HostToGuestVirtual(const void *host_address) const
T TranslateVirtual(u32 guest_address) const
Definition heap.h:622
T TryTranslateVirtual(u32 guest_address, u32 required=kMemoryProtectRead) const
Definition heap.h:637
void SystemHeapFree(u32 address, u32 *out_region_size=nullptr) override
rex::memory::GuestMembase GetMembase() const
Definition heap.h:613
bool InitializeFunctionTable(u32 code_base, u32 code_size, u32 image_base, u32 image_size)
void UnregisterPhysicalMemoryInvalidationCallback(void *callback_handle)
Definition guest_allocator.h:49
Definition heap.h:80
virtual u32 AllocPhysical(u32 size, u32 page_size, u32 protect, u32 min_address, u32 max_address, u32 alignment)=0
virtual u32 SystemHeapAlloc(u32 size, u32 alignment=0x20, u32 system_heap_flags=kSystemHeapDefault)=0
virtual void SystemHeapFree(u32 address, u32 *out_region_size=nullptr)=0
std::unique_lock< rex::platform::RankedMutex > AcquireHostPageReconcileLock() const override
bool Protect(u32 address, u32 size, u32 protect, u32 *old_protect=nullptr) override
bool Release(u32 base_address, u32 *out_region_size=nullptr) override
bool Decommit(u32 address, u32 size) override
bool AllocSystemHeap(u32 size, u32 alignment, u32 allocation_type, u32 protect, bool top_down, u32 *out_address) override
void EnableAccessCallbacks(u32 physical_address, u32 length, bool enable_invalidation_notifications, bool enable_data_providers)
bool AllocFixed(u32 base_address, u32 size, u32 alignment, u32 allocation_type, u32 protect) override
VirtualHeap * parent_heap_
Definition heap.h:566
bool TriggerCallbacks(u32 virtual_address, u32 length, bool is_write, bool unwatch_exact_range, bool unprotect=true)
bool IsHostPageWriteWatched(u32 host_page_number) const override
bool Alloc(u32 size, u32 alignment, u32 allocation_type, u32 protect, bool top_down, u32 *out_address) override
u32 system_page_count_
Definition heap.h:570
bool AllocRange(u32 low_address, u32 high_address, u32 size, u32 alignment, u32 allocation_type, u32 protect, bool top_down, u32 *out_address) override
void Initialize(kernel::GuestAddressSpace *memory, u8 *membase, HeapType heap_type, u32 heap_base, u32 heap_size, u32 page_size, VirtualHeap *parent_heap)
u32 GetPhysicalAddress(u32 address) const
std::vector< SystemPageFlagsBlock > system_page_flags_
Definition heap.h:578
Definition heap.h:439
void Initialize(kernel::GuestAddressSpace *memory, u8 *membase, HeapType heap_type, u32 heap_base, u32 heap_size, u32 page_size)
Definition guest_allocator.h:179
Definition mapped_ptr.h:412
Definition export.h:24
AccessViolationResult
Definition heap.h:581
@ kUnhandled
Definition heap.h:587
@ kMMIOMiss
Definition heap.h:585
@ kResumed
Definition heap.h:583
MemoryAllocationFlag
Definition heap.h:122
@ kMemoryAllocationTitle
Definition heap.h:126
HeapType
Definition heap.h:116
@ kGuestXEX
Definition heap.h:118
constexpr u32 kPageSize64KiB
Definition heap.h:39
SystemHeapFlag
Definition heap.h:72
Definition endian.h:27
int FileMappingHandle
Definition virtual_memory.h:148
T GuestPtr(u8 *base, u32 guest_address) noexcept
Definition mapped_ptr.h:393
constexpr FileMappingHandle kFileMappingHandleInvalid
Definition virtual_memory.h:150
@ kLeaf
Definition lock_order.h:27
Platform flags, packing macros and export markers.
#define REX_KERNEL_API
Definition platform.h:297
Definition heap.h:139
u32 allocation_protect
Definition heap.h:144
u32 allocation_base
Definition heap.h:142
u32 protect
Definition heap.h:151
u32 state
Definition heap.h:150
u32 allocation_size
Definition heap.h:146
u32 region_size
Definition heap.h:148
u32 base_address
Definition heap.h:140
Integer types, byte order wrappers and size literals.
Definition heap.h:154
u64 qword
Definition heap.h:155