mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 00:12:40 +00:00
Rework guest memory tracking/virtual address space/direct and flexible memory (#135)
* Rework guest memory tracking * add unknwon flag * Fix macOS guest address-space reservation
This commit is contained in:
1 parent
ecb48f90bb
commit
2f5396c6a5
28 files changed
+3155
-2887
No files matched your search
@@ -0,0 +1,8 @@
|
||||
#if defined(__APPLE__) && defined(__x86_64__)
|
||||
|
||||
// Make the process own the guest ranges before any runtime initialization.
|
||||
asm(".zerofill SYSTEM_MANAGED,SYSTEM_MANAGED,__kyty_system_managed,0x7fffbc000");
|
||||
asm(".zerofill SYSTEM_RESERVED,SYSTEM_RESERVED,__kyty_system_reserved,0x7c0004000");
|
||||
asm(".zerofill USER_AREA,USER_AREA,__kyty_user_area,0x8c00000000");
|
||||
|
||||
#endif
|
||||
+1179
-1144
File diff suppressed because it is too large.
Load diff
+23
-10
@@ -145,7 +145,7 @@ int KYTY_SYSV_ABI KernelIsStack(void* addr, void** start, void** end);
|
||||
int KYTY_SYSV_ABI KernelReserveVirtualRange(void** addr, size_t len, int flags, size_t alignment);
|
||||
bool KernelHandleReservedRangeAccessViolation(uint64_t vaddr);
|
||||
int KYTY_SYSV_ABI KernelAvailableFlexibleMemorySize(size_t* size);
|
||||
int KYTY_SYSV_ABI KernelConfiguredFlexibleMemorySize(uint64_t* size);
|
||||
int KYTY_SYSV_ABI KernelConfiguredFlexibleMemorySize(size_t* size);
|
||||
int KYTY_SYSV_ABI KernelMprotect(const void* addr, size_t len, int prot);
|
||||
int KYTY_SYSV_ABI KernelMtypeprotect(const void* addr, size_t len, int type, int prot);
|
||||
int KYTY_SYSV_ABI KernelBatchMap(KernelBatchMapEntry* entries, int num_entries,
|
||||
@@ -163,17 +163,30 @@ int KYTY_SYSV_ABI KernelMemoryPoolBatch(const KernelMemoryPoolBatchEntry* entrie
|
||||
int KYTY_SYSV_ABI KernelMemoryPoolGetBlockStats(KernelMemoryPoolBlockStats* output,
|
||||
size_t output_size);
|
||||
|
||||
void RegisterProgramMemory(uint64_t vaddr, uint64_t size, Common::VirtualMemory::Mode mode,
|
||||
const char* name);
|
||||
void UpdateProgramMemoryProtection(uint64_t vaddr, uint64_t size, Common::VirtualMemory::Mode mode);
|
||||
void UnregisterProgramMemory(uint64_t vaddr, uint64_t size);
|
||||
uint64_t AllocateProgramMemory(uint64_t search_addr, uint64_t size,
|
||||
Common::VirtualMemory::Mode mode, const char* name);
|
||||
void SetProgramMemoryProtection(uint64_t vaddr, uint64_t size, Common::VirtualMemory::Mode mode);
|
||||
uint64_t AllocateRuntimeMemory(uint64_t search_addr, uint64_t size,
|
||||
Common::VirtualMemory::Mode mode, const char* name,
|
||||
bool fixed = false);
|
||||
uint64_t AllocateGuestStackMemory(uint64_t search_addr, uint64_t size,
|
||||
Common::VirtualMemory::Mode mode, const char* name);
|
||||
bool ProtectGuestMemory(uint64_t vaddr, uint64_t size, Common::VirtualMemory::Mode mode,
|
||||
Common::VirtualMemory::Mode* old_mode = nullptr);
|
||||
// Transient PageManager watch state; does not change the guest mapping's semantic protection.
|
||||
bool ProtectGuestHostMemory(uint64_t vaddr, uint64_t size, Common::VirtualMemory::Mode mode);
|
||||
bool FreeGuestMemory(uint64_t vaddr, uint64_t size);
|
||||
|
||||
#if defined(KYTY_VIRTUAL_MEMORY_ALLOCATION_TESTS)
|
||||
void TestFailNextPhysicalMemoryUnmap();
|
||||
void TestFailPhysicalMemoryUnmapAfter(uint32_t successful_unmaps);
|
||||
void TestFailHostReservationAfter(uint32_t successful_pages);
|
||||
void TestFailNextFixedReserveRangeRegistration();
|
||||
bool TestPlaceholderRangeIsFree(uint64_t vaddr, uint64_t size);
|
||||
void TestFailNextPhysicalMemoryUnmap();
|
||||
void TestFailPhysicalMemoryUnmapAfter(uint32_t successful_unmaps);
|
||||
void TestFailGuestBackingStoreUnmapAfter(uint32_t successful_unmaps);
|
||||
void TestFailNextFixedReserveRangeRegistration();
|
||||
bool TestPlaceholderRangeIsFree(uint64_t vaddr, uint64_t size);
|
||||
bool TestGuestAddressRangeIsOwned(uint64_t vaddr, uint64_t size);
|
||||
bool TestGuestBackingOutsideAddressSpace();
|
||||
uint64_t TestGuestBackingSize();
|
||||
bool TestGuestFreeRangeBounds();
|
||||
#endif
|
||||
|
||||
} // namespace Libs::LibKernel::Memory
|
||||
|
||||
+590
-798
File diff suppressed because it is too large.
Load diff
+122
-37
@@ -75,16 +75,16 @@ LIB_NAME("libkernel", "libkernel");
|
||||
#undef PTHREAD_STACK_MIN
|
||||
#endif
|
||||
|
||||
constexpr int KEYS_MAX = 256;
|
||||
constexpr int DESTRUCTOR_ITERATIONS = 4;
|
||||
constexpr size_t PTHREAD_STACK_DEFAULT = 0x100000;
|
||||
constexpr size_t GUEST_PTHREAD_STACK_MIN = 0x4000;
|
||||
constexpr size_t PTHREAD_STACK_PAGE = 0x4000;
|
||||
constexpr size_t PTHREAD_STACK_GRANULARITY = 0x10000;
|
||||
constexpr size_t PTHREAD_STACK_INITIAL = 0x200000;
|
||||
constexpr size_t PTHREAD_STACK_EXTRA = 0x100000;
|
||||
constexpr uint64_t PTHREAD_STACK_TOP = 0x7efff8000ull;
|
||||
constexpr uint32_t SIGNAL_APC_POLL_MICROS = 10000;
|
||||
constexpr int KEYS_MAX = 256;
|
||||
constexpr int DESTRUCTOR_ITERATIONS = 4;
|
||||
constexpr size_t PTHREAD_STACK_DEFAULT = 0x100000;
|
||||
constexpr size_t GUEST_PTHREAD_STACK_MIN = 0x4000;
|
||||
constexpr size_t PTHREAD_STACK_PAGE = 0x4000;
|
||||
constexpr size_t PTHREAD_STACK_INITIAL = 0x200000;
|
||||
constexpr size_t PTHREAD_STACK_EXTRA = 0x100000;
|
||||
constexpr uint64_t PTHREAD_STACK_TOP = 0x7efff8000ull;
|
||||
constexpr uint64_t PTHREAD_STACK_BOTTOM = 0x0000040000ull;
|
||||
constexpr uint32_t SIGNAL_APC_POLL_MICROS = 10000;
|
||||
|
||||
static constexpr KernelClockid KERNEL_CLOCK_REALTIME = 0;
|
||||
static constexpr KernelClockid KERNEL_CLOCK_VIRTUAL = 1;
|
||||
@@ -697,16 +697,17 @@ static std::atomic<int32_t> g_pthread_thread_id = 0;
|
||||
|
||||
static Common::Mutex g_guest_stack_mutex;
|
||||
static uint64_t g_guest_stack_last = 0;
|
||||
struct CachedGuestStack {
|
||||
uint64_t address;
|
||||
size_t map_size;
|
||||
size_t guard_size;
|
||||
};
|
||||
static std::vector<CachedGuestStack> g_guest_stack_cache;
|
||||
|
||||
static size_t RoundStackSize(size_t size) {
|
||||
return ((size + PTHREAD_STACK_PAGE - 1) / PTHREAD_STACK_PAGE) * PTHREAD_STACK_PAGE;
|
||||
}
|
||||
|
||||
static size_t RoundStackMappingSize(size_t size) {
|
||||
return ((size + PTHREAD_STACK_GRANULARITY - 1) / PTHREAD_STACK_GRANULARITY) *
|
||||
PTHREAD_STACK_GRANULARITY;
|
||||
}
|
||||
|
||||
static int CreateGuestStack(PthreadAttr attr) {
|
||||
if (attr == nullptr) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
@@ -722,34 +723,41 @@ static int CreateGuestStack(PthreadAttr attr) {
|
||||
|
||||
const auto stack_size = RoundStackSize(attr->stack_size);
|
||||
const auto guard_size = RoundStackSize(attr->guard_size);
|
||||
const auto map_size = RoundStackMappingSize(stack_size + guard_size);
|
||||
const auto map_size = stack_size + guard_size;
|
||||
|
||||
uint64_t stack_addr = 0;
|
||||
bool cached = false;
|
||||
{
|
||||
Common::LockGuard lock(g_guest_stack_mutex);
|
||||
|
||||
if (g_guest_stack_last == 0) {
|
||||
g_guest_stack_last = (PTHREAD_STACK_TOP - PTHREAD_STACK_INITIAL - PTHREAD_STACK_PAGE) &
|
||||
~(static_cast<uint64_t>(PTHREAD_STACK_GRANULARITY) - 1);
|
||||
auto cached_stack =
|
||||
std::find_if(g_guest_stack_cache.begin(), g_guest_stack_cache.end(),
|
||||
[map_size, guard_size](const auto& stack) {
|
||||
return stack.map_size == map_size && stack.guard_size == guard_size;
|
||||
});
|
||||
if (cached_stack != g_guest_stack_cache.end()) {
|
||||
stack_addr = cached_stack->address;
|
||||
g_guest_stack_cache.erase(cached_stack);
|
||||
cached = true;
|
||||
} else {
|
||||
if (g_guest_stack_last == 0) {
|
||||
g_guest_stack_last = PTHREAD_STACK_TOP - PTHREAD_STACK_INITIAL - PTHREAD_STACK_PAGE;
|
||||
}
|
||||
if (map_size > g_guest_stack_last - PTHREAD_STACK_BOTTOM) {
|
||||
return KERNEL_ERROR_EAGAIN;
|
||||
}
|
||||
stack_addr = g_guest_stack_last - map_size;
|
||||
g_guest_stack_last -= map_size;
|
||||
}
|
||||
|
||||
stack_addr = g_guest_stack_last - map_size;
|
||||
g_guest_stack_last -= map_size;
|
||||
}
|
||||
|
||||
void* mapped_addr = reinterpret_cast<void*>(stack_addr);
|
||||
|
||||
constexpr int GUEST_PROT_READ_WRITE = 0x03;
|
||||
constexpr int GUEST_MAP_PRIVATE = 0x02;
|
||||
constexpr int GUEST_MAP_FIXED = 0x10;
|
||||
constexpr int GUEST_MAP_STACK = 0x400;
|
||||
constexpr int GUEST_MAP_ANON = 0x1000;
|
||||
|
||||
int result = Memory::KernelMapNamedFlexibleMemory(
|
||||
&mapped_addr, map_size, GUEST_PROT_READ_WRITE,
|
||||
GUEST_MAP_PRIVATE | GUEST_MAP_FIXED | GUEST_MAP_STACK | GUEST_MAP_ANON, "stack");
|
||||
if (result != OK) {
|
||||
return KERNEL_ERROR_EAGAIN;
|
||||
int result = OK;
|
||||
if (!cached) {
|
||||
stack_addr = Memory::AllocateGuestStackMemory(
|
||||
stack_addr, map_size, Common::VirtualMemory::Mode::ReadWrite, "stack");
|
||||
if (stack_addr == 0) {
|
||||
return KERNEL_ERROR_EAGAIN;
|
||||
}
|
||||
}
|
||||
|
||||
if (guard_size != 0) {
|
||||
@@ -761,7 +769,7 @@ static int CreateGuestStack(PthreadAttr attr) {
|
||||
}
|
||||
|
||||
attr->stack_addr = reinterpret_cast<void*>(stack_addr + guard_size);
|
||||
attr->stack_size = map_size - guard_size;
|
||||
attr->stack_size = stack_size;
|
||||
attr->stack_user = false;
|
||||
attr->stack_map_addr = stack_addr;
|
||||
attr->stack_map_size = map_size;
|
||||
@@ -777,13 +785,90 @@ static void FreeGuestStack(PthreadAttr attr) {
|
||||
return;
|
||||
}
|
||||
|
||||
Memory::KernelMunmap(attr->stack_map_addr, attr->stack_map_size);
|
||||
const auto guard_size = attr->stack_map_size - attr->stack_size;
|
||||
{
|
||||
Common::LockGuard lock(g_guest_stack_mutex);
|
||||
g_guest_stack_cache.push_back({attr->stack_map_addr, attr->stack_map_size, guard_size});
|
||||
}
|
||||
|
||||
attr->stack_addr = nullptr;
|
||||
attr->stack_map_addr = 0;
|
||||
attr->stack_map_size = 0;
|
||||
}
|
||||
|
||||
#if defined(KYTY_VIRTUAL_MEMORY_ALLOCATION_TESTS)
|
||||
bool TestGuestStackOwnerLifecycle(uint64_t* first_address, uint64_t* second_address,
|
||||
uint64_t* map_size) {
|
||||
if (first_address == nullptr || second_address == nullptr || map_size == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t flexible_before = 0;
|
||||
if (Memory::KernelAvailableFlexibleMemorySize(&flexible_before) != OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PthreadAttr attr = nullptr;
|
||||
if (PthreadAttrInit(&attr) != OK) {
|
||||
return false;
|
||||
}
|
||||
if (CreateGuestStack(attr) != OK) {
|
||||
PthreadAttrDestroy(&attr);
|
||||
return false;
|
||||
}
|
||||
*first_address = attr->stack_map_addr;
|
||||
*map_size = attr->stack_map_size;
|
||||
const bool first_owned =
|
||||
Memory::TestGuestAddressRangeIsOwned(*first_address, static_cast<uint64_t>(*map_size));
|
||||
uint64_t backing_value = 0;
|
||||
const bool first_private =
|
||||
!Memory::TryReadBacking(*first_address, &backing_value, sizeof(backing_value));
|
||||
size_t flexible_during_first = 0;
|
||||
const bool first_capacity_unchanged =
|
||||
Memory::KernelAvailableFlexibleMemorySize(&flexible_during_first) == OK &&
|
||||
flexible_during_first == flexible_before;
|
||||
FreeGuestStack(attr);
|
||||
|
||||
if (CreateGuestStack(attr) != OK) {
|
||||
PthreadAttrDestroy(&attr);
|
||||
return false;
|
||||
}
|
||||
*second_address = attr->stack_map_addr;
|
||||
const bool second_owned =
|
||||
Memory::TestGuestAddressRangeIsOwned(*second_address, static_cast<uint64_t>(*map_size));
|
||||
const bool second_private =
|
||||
!Memory::TryReadBacking(*second_address, &backing_value, sizeof(backing_value));
|
||||
size_t flexible_during_second = 0;
|
||||
const bool second_capacity_unchanged =
|
||||
Memory::KernelAvailableFlexibleMemorySize(&flexible_during_second) == OK &&
|
||||
flexible_during_second == flexible_before;
|
||||
FreeGuestStack(attr);
|
||||
|
||||
CachedGuestStack cached {};
|
||||
bool found = false;
|
||||
{
|
||||
Common::LockGuard lock(g_guest_stack_mutex);
|
||||
const auto entry = std::find_if(
|
||||
g_guest_stack_cache.begin(), g_guest_stack_cache.end(),
|
||||
[second_address](const auto& stack) { return stack.address == *second_address; });
|
||||
if (entry != g_guest_stack_cache.end()) {
|
||||
cached = *entry;
|
||||
g_guest_stack_cache.erase(entry);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
const bool unmapped = found && Memory::KernelMunmap(cached.address, cached.map_size) == OK;
|
||||
size_t flexible_after = 0;
|
||||
const bool final_capacity_unchanged =
|
||||
Memory::KernelAvailableFlexibleMemorySize(&flexible_after) == OK &&
|
||||
flexible_after == flexible_before;
|
||||
return PthreadAttrDestroy(&attr) == OK && first_owned && first_private &&
|
||||
first_capacity_unchanged && second_owned && second_private &&
|
||||
second_capacity_unchanged && unmapped && final_capacity_unchanged;
|
||||
}
|
||||
#endif
|
||||
|
||||
static KYTY_SYSV_ABI void* RunOnGuestStack(void* arg, pthread_entry_func_t func, void* stack_top) {
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
void* ret = nullptr;
|
||||
|
||||
@@ -112,11 +112,15 @@ void PthreadQueuePendingSignal(Pthread thread, int signum);
|
||||
bool PthreadHasPendingSignal(Pthread thread, int signum);
|
||||
bool PthreadTakePendingSignal(Pthread thread, int signum);
|
||||
bool PthreadGetGuestStack(Pthread thread, uint64_t* stack_addr, uint64_t* stack_size);
|
||||
#if defined(KYTY_VIRTUAL_MEMORY_ALLOCATION_TESTS)
|
||||
bool TestGuestStackOwnerLifecycle(uint64_t* first_address, uint64_t* second_address,
|
||||
uint64_t* map_size);
|
||||
#endif
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||
bool PthreadKillHost(Pthread thread, int host_signal);
|
||||
#endif
|
||||
int PthreadGetPriorityForKernel(Pthread thread);
|
||||
int PthreadGetCurrentPriorityForKernel();
|
||||
int PthreadGetPriorityForKernel(Pthread thread);
|
||||
int PthreadGetCurrentPriorityForKernel();
|
||||
|
||||
int KYTY_SYSV_ABI KernelUsleep(KernelUseconds microseconds);
|
||||
unsigned int KYTY_SYSV_ABI KernelSleep(unsigned int seconds);
|
||||
|
||||
Reference in new issue
Block a user