memory: Cleanups and refactors (#324)
* memory: Various fixes * Added (Partial) sceKernelBatchMap/sceKernelBatchMap2 * memory: Rename and implement batch unmap * memory: Remove uneeded assert * memory: Commonize free search routine * memory: Contains check is inclusive * memory: Address some alignment issues * clang format --------- Co-authored-by: raziel1000 <ckraziel@gmail.com>
This commit is contained in:
parent
bc28ed66e8
commit
a2cd1669b6
|
@ -405,6 +405,9 @@ void LibKernel_Register(Core::Loader::SymbolsResolver* sym) {
|
||||||
LIB_FUNCTION("VOx8NGmHXTs", "libkernel", 1, "libkernel", 1, 1, sceKernelGetCpumode);
|
LIB_FUNCTION("VOx8NGmHXTs", "libkernel", 1, "libkernel", 1, 1, sceKernelGetCpumode);
|
||||||
LIB_FUNCTION("Xjoosiw+XPI", "libkernel", 1, "libkernel", 1, 1, sceKernelUuidCreate);
|
LIB_FUNCTION("Xjoosiw+XPI", "libkernel", 1, "libkernel", 1, 1, sceKernelUuidCreate);
|
||||||
|
|
||||||
|
LIB_FUNCTION("2SKEx6bSq-4", "libkernel", 1, "libkernel", 1, 1, sceKernelBatchMap);
|
||||||
|
LIB_FUNCTION("kBJzF8x4SyE", "libkernel", 1, "libkernel", 1, 1, sceKernelBatchMap2);
|
||||||
|
|
||||||
// equeue
|
// equeue
|
||||||
LIB_FUNCTION("D0OdFMjp46I", "libkernel", 1, "libkernel", 1, 1, sceKernelCreateEqueue);
|
LIB_FUNCTION("D0OdFMjp46I", "libkernel", 1, "libkernel", 1, 1, sceKernelCreateEqueue);
|
||||||
LIB_FUNCTION("jpFjmgAC5AE", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteEqueue);
|
LIB_FUNCTION("jpFjmgAC5AE", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteEqueue);
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <bit>
|
#include <bit>
|
||||||
#include "common/alignment.h"
|
#include "common/alignment.h"
|
||||||
|
#include "common/assert.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/singleton.h"
|
#include "common/singleton.h"
|
||||||
#include "core/libraries/error_codes.h"
|
#include "core/libraries/error_codes.h"
|
||||||
|
@ -225,4 +226,51 @@ int PS4_SYSV_ABI sceKernelGetDirectMemoryType(u64 addr, int* directMemoryTypeOut
|
||||||
directMemoryEndOut);
|
directMemoryEndOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, int numEntries,
|
||||||
|
int* numEntriesOut) {
|
||||||
|
return sceKernelBatchMap2(entries, numEntries, numEntriesOut, 0x10); // 0x10 : Fixed / 0x410
|
||||||
|
}
|
||||||
|
|
||||||
|
int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len);
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEntries,
|
||||||
|
int* numEntriesOut, int flags) {
|
||||||
|
int processed = 0;
|
||||||
|
int result = 0;
|
||||||
|
for (int i = 0; i < numEntries; i++) {
|
||||||
|
if (entries == nullptr || entries[i].length == 0 || entries[i].operation > 4) {
|
||||||
|
result = ORBIS_KERNEL_ERROR_EINVAL;
|
||||||
|
break; // break and assign a value to numEntriesOut.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entries[i].operation == 0) { // MAP_DIRECT
|
||||||
|
result = sceKernelMapNamedDirectMemory(&entries[i].start, entries[i].length,
|
||||||
|
entries[i].protection, flags,
|
||||||
|
static_cast<s64>(entries[i].offset), 0, "");
|
||||||
|
LOG_INFO(
|
||||||
|
Kernel_Vmm,
|
||||||
|
"BatchMap: entry = {}, operation = {}, len = {:#x}, offset = {:#x}, type = {}, "
|
||||||
|
"result = {}",
|
||||||
|
i, entries[i].operation, entries[i].length, entries[i].offset, (u8)entries[i].type,
|
||||||
|
result);
|
||||||
|
|
||||||
|
if (result == 0)
|
||||||
|
processed++;
|
||||||
|
} else if (entries[i].operation == 1) {
|
||||||
|
result = sceKernelMunmap(entries[i].start, entries[i].length);
|
||||||
|
LOG_INFO(Kernel_Vmm, "BatchMap: entry = {}, operation = {}, len = {:#x}, result = {}",
|
||||||
|
i, entries[i].operation, entries[i].length, result);
|
||||||
|
|
||||||
|
if (result == 0)
|
||||||
|
processed++;
|
||||||
|
} else {
|
||||||
|
UNREACHABLE_MSG("called: Unimplemented Operation = {}", entries[i].operation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (numEntriesOut != NULL) { // can be zero. do not return an error code.
|
||||||
|
*numEntriesOut = processed;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Libraries::Kernel
|
} // namespace Libraries::Kernel
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#include "common/bit_field.h"
|
#include "common/bit_field.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
|
|
||||||
constexpr u64 SCE_KERNEL_MAIN_DMEM_SIZE = 5376_MB; // ~ 6GB
|
constexpr u64 SCE_KERNEL_MAIN_DMEM_SIZE = 6_GB; // ~ 6GB
|
||||||
|
|
||||||
namespace Libraries::Kernel {
|
namespace Libraries::Kernel {
|
||||||
|
|
||||||
|
@ -53,6 +53,16 @@ struct OrbisVirtualQueryInfo {
|
||||||
std::array<char, 32> name;
|
std::array<char, 32> name;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct OrbisKernelBatchMapEntry {
|
||||||
|
void* start;
|
||||||
|
off_t offset;
|
||||||
|
size_t length;
|
||||||
|
char protection;
|
||||||
|
char type;
|
||||||
|
short reserved;
|
||||||
|
int operation;
|
||||||
|
};
|
||||||
|
|
||||||
u64 PS4_SYSV_ABI sceKernelGetDirectMemorySize();
|
u64 PS4_SYSV_ABI sceKernelGetDirectMemorySize();
|
||||||
int PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u64 len,
|
int PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u64 len,
|
||||||
u64 alignment, int memoryType, s64* physAddrOut);
|
u64 alignment, int memoryType, s64* physAddrOut);
|
||||||
|
@ -85,4 +95,9 @@ int PS4_SYSV_ABI sceKernelGetDirectMemoryType(u64 addr, int* directMemoryTypeOut
|
||||||
void** directMemoryStartOut,
|
void** directMemoryStartOut,
|
||||||
void** directMemoryEndOut);
|
void** directMemoryEndOut);
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, int numEntries,
|
||||||
|
int* numEntriesOut);
|
||||||
|
s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEntries,
|
||||||
|
int* numEntriesOut, int flags);
|
||||||
|
|
||||||
} // namespace Libraries::Kernel
|
} // namespace Libraries::Kernel
|
||||||
|
|
|
@ -785,7 +785,22 @@ int PS4_SYSV_ABI posix_pthread_mutex_destroy(ScePthreadMutex* mutex) {
|
||||||
int PS4_SYSV_ABI posix_pthread_cond_wait(ScePthreadCond* cond, ScePthreadMutex* mutex) {
|
int PS4_SYSV_ABI posix_pthread_cond_wait(ScePthreadCond* cond, ScePthreadMutex* mutex) {
|
||||||
int result = scePthreadCondWait(cond, mutex);
|
int result = scePthreadCondWait(cond, mutex);
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
UNREACHABLE();
|
int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP
|
||||||
|
? result + -SCE_KERNEL_ERROR_UNKNOWN
|
||||||
|
: POSIX_EOTHER;
|
||||||
|
return rt;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PS4_SYSV_ABI posix_pthread_cond_timedwait(ScePthreadCond* cond, ScePthreadMutex* mutex,
|
||||||
|
u64 usec) {
|
||||||
|
int result = scePthreadCondTimedwait(cond, mutex, usec);
|
||||||
|
if (result < 0) {
|
||||||
|
int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP
|
||||||
|
? result + -SCE_KERNEL_ERROR_UNKNOWN
|
||||||
|
: POSIX_EOTHER;
|
||||||
|
return rt;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -1350,6 +1365,11 @@ int PS4_SYSV_ABI scePthreadOnce(int* once_control, void (*init_routine)(void)) {
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[noreturn]] void PS4_SYSV_ABI posix_pthread_exit(void* value_ptr) {
|
||||||
|
pthread_exit(value_ptr);
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI scePthreadGetthreadid() {
|
int PS4_SYSV_ABI scePthreadGetthreadid() {
|
||||||
return (int)(size_t)g_pthread_self;
|
return (int)(size_t)g_pthread_self;
|
||||||
}
|
}
|
||||||
|
@ -1401,6 +1421,7 @@ void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
||||||
LIB_FUNCTION("4qGrR6eoP9Y", "libkernel", 1, "libkernel", 1, 1, scePthreadDetach);
|
LIB_FUNCTION("4qGrR6eoP9Y", "libkernel", 1, "libkernel", 1, 1, scePthreadDetach);
|
||||||
LIB_FUNCTION("3PtV6p3QNX4", "libkernel", 1, "libkernel", 1, 1, scePthreadEqual);
|
LIB_FUNCTION("3PtV6p3QNX4", "libkernel", 1, "libkernel", 1, 1, scePthreadEqual);
|
||||||
LIB_FUNCTION("3kg7rT0NQIs", "libkernel", 1, "libkernel", 1, 1, scePthreadExit);
|
LIB_FUNCTION("3kg7rT0NQIs", "libkernel", 1, "libkernel", 1, 1, scePthreadExit);
|
||||||
|
LIB_FUNCTION("FJrT5LuUBAU", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_exit);
|
||||||
LIB_FUNCTION("7Xl257M4VNI", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_equal);
|
LIB_FUNCTION("7Xl257M4VNI", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_equal);
|
||||||
LIB_FUNCTION("h9CcP3J0oVM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_join);
|
LIB_FUNCTION("h9CcP3J0oVM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_join);
|
||||||
LIB_FUNCTION("EI-5-jlq2dE", "libkernel", 1, "libkernel", 1, 1, scePthreadGetthreadid);
|
LIB_FUNCTION("EI-5-jlq2dE", "libkernel", 1, "libkernel", 1, 1, scePthreadGetthreadid);
|
||||||
|
@ -1462,6 +1483,7 @@ void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
||||||
LIB_FUNCTION("ltCfaGr2JGE", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_destroy);
|
LIB_FUNCTION("ltCfaGr2JGE", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_destroy);
|
||||||
LIB_FUNCTION("Op8TBGY5KHg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cond_wait);
|
LIB_FUNCTION("Op8TBGY5KHg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cond_wait);
|
||||||
LIB_FUNCTION("Op8TBGY5KHg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_wait);
|
LIB_FUNCTION("Op8TBGY5KHg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_wait);
|
||||||
|
LIB_FUNCTION("27bAgiJmOh0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_timedwait);
|
||||||
LIB_FUNCTION("mkx2fVhNMsg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_broadcast);
|
LIB_FUNCTION("mkx2fVhNMsg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_broadcast);
|
||||||
LIB_FUNCTION("dQHWEsJtoE4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutexattr_init);
|
LIB_FUNCTION("dQHWEsJtoE4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutexattr_init);
|
||||||
LIB_FUNCTION("mDmgMOGVUqg", "libScePosix", 1, "libkernel", 1, 1,
|
LIB_FUNCTION("mDmgMOGVUqg", "libScePosix", 1, "libkernel", 1, 1,
|
||||||
|
|
|
@ -8,8 +8,6 @@
|
||||||
#include "playgo.h"
|
#include "playgo.h"
|
||||||
|
|
||||||
namespace Libraries::PlayGo {
|
namespace Libraries::PlayGo {
|
||||||
// this lib is used to play as the game is being installed.
|
|
||||||
// can be skipped by just returning and assigning the correct values.
|
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceDbgPlayGoRequestNextChunk() {
|
s32 PS4_SYSV_ABI sceDbgPlayGoRequestNextChunk() {
|
||||||
LOG_ERROR(Lib_PlayGo, "(STUBBED)called");
|
LOG_ERROR(Lib_PlayGo, "(STUBBED)called");
|
||||||
|
|
|
@ -320,11 +320,15 @@ void Linker::InitTlsForThread(bool is_primary) {
|
||||||
static constexpr size_t TlsAllocAlign = 0x20;
|
static constexpr size_t TlsAllocAlign = 0x20;
|
||||||
const size_t total_tls_size = Common::AlignUp(static_tls_size, TlsAllocAlign) + TcbSize;
|
const size_t total_tls_size = Common::AlignUp(static_tls_size, TlsAllocAlign) + TcbSize;
|
||||||
|
|
||||||
|
// If sceKernelMapNamedFlexibleMemory is being called from libkernel and addr = 0
|
||||||
|
// it automatically places mappings in system reserved area instead of managed.
|
||||||
|
static constexpr VAddr KernelAllocBase = 0x880000000ULL;
|
||||||
|
|
||||||
// The kernel module has a few different paths for TLS allocation.
|
// The kernel module has a few different paths for TLS allocation.
|
||||||
// For SDK < 1.7 it allocates both main and secondary thread blocks using libc mspace/malloc.
|
// For SDK < 1.7 it allocates both main and secondary thread blocks using libc mspace/malloc.
|
||||||
// In games compiled with newer SDK, the main thread gets mapped from flexible memory,
|
// In games compiled with newer SDK, the main thread gets mapped from flexible memory,
|
||||||
// with addr = 0, so system managed area. Here we will only implement the latter.
|
// with addr = 0, so system managed area. Here we will only implement the latter.
|
||||||
void* addr_out{};
|
void* addr_out{reinterpret_cast<void*>(KernelAllocBase)};
|
||||||
if (is_primary) {
|
if (is_primary) {
|
||||||
const size_t tls_aligned = Common::AlignUp(total_tls_size, 16_KB);
|
const size_t tls_aligned = Common::AlignUp(total_tls_size, 16_KB);
|
||||||
const int ret = Libraries::Kernel::sceKernelMapNamedFlexibleMemory(
|
const int ret = Libraries::Kernel::sceKernelMapNamedFlexibleMemory(
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
#include "common/alignment.h"
|
#include "common/alignment.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/debug.h"
|
#include "common/debug.h"
|
||||||
#include "common/scope_exit.h"
|
|
||||||
#include "core/libraries/error_codes.h"
|
#include "core/libraries/error_codes.h"
|
||||||
#include "core/libraries/kernel/memory_management.h"
|
#include "core/libraries/kernel/memory_management.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
|
@ -55,7 +54,7 @@ PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size,
|
||||||
free_addr = alignment > 0 ? Common::AlignUp(free_addr, alignment) : free_addr;
|
free_addr = alignment > 0 ? Common::AlignUp(free_addr, alignment) : free_addr;
|
||||||
|
|
||||||
// Add the allocated region to the list and commit its pages.
|
// Add the allocated region to the list and commit its pages.
|
||||||
auto& area = AddDmemAllocation(free_addr, size);
|
auto& area = CarveDmemArea(free_addr, size);
|
||||||
area.memory_type = memory_type;
|
area.memory_type = memory_type;
|
||||||
area.is_free = false;
|
area.is_free = false;
|
||||||
return free_addr;
|
return free_addr;
|
||||||
|
@ -100,29 +99,32 @@ int MemoryManager::Reserve(void** out_addr, VAddr virtual_addr, size_t size, Mem
|
||||||
alignment = alignment > 0 ? alignment : 16_KB;
|
alignment = alignment > 0 ? alignment : 16_KB;
|
||||||
VAddr mapped_addr = alignment > 0 ? Common::AlignUp(virtual_addr, alignment) : virtual_addr;
|
VAddr mapped_addr = alignment > 0 ? Common::AlignUp(virtual_addr, alignment) : virtual_addr;
|
||||||
|
|
||||||
|
// Fixed mapping means the virtual address must exactly match the provided one.
|
||||||
|
if (True(flags & MemoryMapFlags::Fixed)) {
|
||||||
|
const auto& vma = FindVMA(mapped_addr)->second;
|
||||||
|
// If the VMA is mapped, unmap the region first.
|
||||||
|
if (vma.IsMapped()) {
|
||||||
|
ASSERT_MSG(vma.base == mapped_addr && vma.size == size,
|
||||||
|
"Region must match when reserving a mapped region");
|
||||||
|
UnmapMemory(mapped_addr, size);
|
||||||
|
}
|
||||||
|
const size_t remaining_size = vma.base + vma.size - mapped_addr;
|
||||||
|
ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size);
|
||||||
|
}
|
||||||
|
|
||||||
// Find the first free area starting with provided virtual address.
|
// Find the first free area starting with provided virtual address.
|
||||||
if (False(flags & MemoryMapFlags::Fixed)) {
|
if (False(flags & MemoryMapFlags::Fixed)) {
|
||||||
auto it = FindVMA(mapped_addr);
|
mapped_addr = SearchFree(mapped_addr, size, alignment);
|
||||||
// If the VMA is free and contains the requested mapping we are done.
|
|
||||||
if (it->second.type == VMAType::Free && it->second.Contains(virtual_addr, size)) {
|
|
||||||
mapped_addr = virtual_addr;
|
|
||||||
} else {
|
|
||||||
// Search for the first free VMA that fits our mapping.
|
|
||||||
while (it->second.type != VMAType::Free || it->second.size < size) {
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
ASSERT(it != vma_map.end());
|
|
||||||
const auto& vma = it->second;
|
|
||||||
mapped_addr = alignment > 0 ? Common::AlignUp(vma.base, alignment) : vma.base;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add virtual memory area
|
// Add virtual memory area
|
||||||
auto& new_vma = AddMapping(mapped_addr, size);
|
const auto new_vma_handle = CarveVMA(mapped_addr, size);
|
||||||
|
auto& new_vma = new_vma_handle->second;
|
||||||
new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
|
new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
|
||||||
new_vma.prot = MemoryProt::NoAccess;
|
new_vma.prot = MemoryProt::NoAccess;
|
||||||
new_vma.name = "";
|
new_vma.name = "";
|
||||||
new_vma.type = VMAType::Reserved;
|
new_vma.type = VMAType::Reserved;
|
||||||
|
MergeAdjacent(vma_map, new_vma_handle);
|
||||||
|
|
||||||
*out_addr = std::bit_cast<void*>(mapped_addr);
|
*out_addr = std::bit_cast<void*>(mapped_addr);
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
|
@ -132,6 +134,9 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M
|
||||||
MemoryMapFlags flags, VMAType type, std::string_view name,
|
MemoryMapFlags flags, VMAType type, std::string_view name,
|
||||||
bool is_exec, PAddr phys_addr, u64 alignment) {
|
bool is_exec, PAddr phys_addr, u64 alignment) {
|
||||||
std::scoped_lock lk{mutex};
|
std::scoped_lock lk{mutex};
|
||||||
|
|
||||||
|
// Certain games perform flexible mappings on loop to determine
|
||||||
|
// the available flexible memory size. Questionable but we need to handle this.
|
||||||
if (type == VMAType::Flexible && flexible_usage + size > total_flexible_size) {
|
if (type == VMAType::Flexible && flexible_usage + size > total_flexible_size) {
|
||||||
return SCE_KERNEL_ERROR_ENOMEM;
|
return SCE_KERNEL_ERROR_ENOMEM;
|
||||||
}
|
}
|
||||||
|
@ -140,10 +145,26 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M
|
||||||
// flag so we will take the branch that searches for free (or reserved) mappings.
|
// flag so we will take the branch that searches for free (or reserved) mappings.
|
||||||
virtual_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr;
|
virtual_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr;
|
||||||
alignment = alignment > 0 ? alignment : 16_KB;
|
alignment = alignment > 0 ? alignment : 16_KB;
|
||||||
|
|
||||||
VAddr mapped_addr = alignment > 0 ? Common::AlignUp(virtual_addr, alignment) : virtual_addr;
|
VAddr mapped_addr = alignment > 0 ? Common::AlignUp(virtual_addr, alignment) : virtual_addr;
|
||||||
SCOPE_EXIT {
|
|
||||||
auto& new_vma = AddMapping(mapped_addr, size);
|
// Fixed mapping means the virtual address must exactly match the provided one.
|
||||||
|
if (True(flags & MemoryMapFlags::Fixed)) {
|
||||||
|
// This should return SCE_KERNEL_ERROR_ENOMEM but shouldn't normally happen.
|
||||||
|
const auto& vma = FindVMA(mapped_addr)->second;
|
||||||
|
const size_t remaining_size = vma.base + vma.size - mapped_addr;
|
||||||
|
ASSERT_MSG(!vma.IsMapped() && remaining_size >= size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the first free area starting with provided virtual address.
|
||||||
|
if (False(flags & MemoryMapFlags::Fixed)) {
|
||||||
|
mapped_addr = SearchFree(mapped_addr, size, alignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform the mapping.
|
||||||
|
*out_addr = impl.Map(mapped_addr, size, alignment, phys_addr, is_exec);
|
||||||
|
TRACK_ALLOC(*out_addr, size, "VMEM");
|
||||||
|
|
||||||
|
auto& new_vma = CarveVMA(mapped_addr, size)->second;
|
||||||
new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
|
new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
|
||||||
new_vma.prot = prot;
|
new_vma.prot = prot;
|
||||||
new_vma.name = name;
|
new_vma.name = name;
|
||||||
|
@ -156,75 +177,31 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M
|
||||||
if (type == VMAType::Flexible) {
|
if (type == VMAType::Flexible) {
|
||||||
flexible_usage += size;
|
flexible_usage += size;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// Fixed mapping means the virtual address must exactly match the provided one.
|
|
||||||
if (True(flags & MemoryMapFlags::Fixed) && True(flags & MemoryMapFlags::NoOverwrite)) {
|
|
||||||
// This should return SCE_KERNEL_ERROR_ENOMEM but shouldn't normally happen.
|
|
||||||
const auto& vma = FindVMA(mapped_addr)->second;
|
|
||||||
const size_t remaining_size = vma.base + vma.size - mapped_addr;
|
|
||||||
ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the first free area starting with provided virtual address.
|
|
||||||
if (False(flags & MemoryMapFlags::Fixed)) {
|
|
||||||
auto it = FindVMA(mapped_addr);
|
|
||||||
// If the VMA is free and contains the requested mapping we are done.
|
|
||||||
if (it->second.type == VMAType::Free && it->second.Contains(virtual_addr, size)) {
|
|
||||||
mapped_addr = virtual_addr;
|
|
||||||
} else {
|
|
||||||
// Search for the first free VMA that fits our mapping.
|
|
||||||
while (it->second.type != VMAType::Free || it->second.size < size) {
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
ASSERT(it != vma_map.end());
|
|
||||||
const auto& vma = it->second;
|
|
||||||
mapped_addr = alignment > 0 ? Common::AlignUp(vma.base, alignment) : vma.base;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform the mapping.
|
|
||||||
*out_addr = impl.Map(mapped_addr, size, alignment, phys_addr, is_exec);
|
|
||||||
TRACK_ALLOC(*out_addr, size, "VMEM");
|
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot,
|
int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot,
|
||||||
MemoryMapFlags flags, uintptr_t fd, size_t offset) {
|
MemoryMapFlags flags, uintptr_t fd, size_t offset) {
|
||||||
if (virtual_addr == 0) {
|
VAddr mapped_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr;
|
||||||
virtual_addr = impl.SystemManagedVirtualBase();
|
|
||||||
} else {
|
|
||||||
LOG_INFO(Kernel_Vmm, "Virtual addr {:#x} with size {:#x}", virtual_addr, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
VAddr mapped_addr = 0;
|
|
||||||
const size_t size_aligned = Common::AlignUp(size, 16_KB);
|
const size_t size_aligned = Common::AlignUp(size, 16_KB);
|
||||||
|
|
||||||
// Find first free area to map the file.
|
// Find first free area to map the file.
|
||||||
if (False(flags & MemoryMapFlags::Fixed)) {
|
if (False(flags & MemoryMapFlags::Fixed)) {
|
||||||
auto it = FindVMA(virtual_addr);
|
mapped_addr = SearchFree(mapped_addr, size_aligned);
|
||||||
while (it->second.type != VMAType::Free || it->second.size < size_aligned) {
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
ASSERT(it != vma_map.end());
|
|
||||||
|
|
||||||
mapped_addr = it->second.base;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (True(flags & MemoryMapFlags::Fixed)) {
|
if (True(flags & MemoryMapFlags::Fixed)) {
|
||||||
const auto& vma = FindVMA(virtual_addr)->second;
|
const auto& vma = FindVMA(virtual_addr)->second;
|
||||||
const size_t remaining_size = vma.base + vma.size - virtual_addr;
|
const size_t remaining_size = vma.base + vma.size - virtual_addr;
|
||||||
ASSERT_MSG((vma.type == VMAType::Free || vma.type == VMAType::Reserved) &&
|
ASSERT_MSG(!vma.IsMapped() && remaining_size >= size);
|
||||||
remaining_size >= size);
|
|
||||||
|
|
||||||
mapped_addr = virtual_addr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map the file.
|
// Map the file.
|
||||||
impl.MapFile(mapped_addr, size, offset, std::bit_cast<u32>(prot), fd);
|
impl.MapFile(mapped_addr, size, offset, std::bit_cast<u32>(prot), fd);
|
||||||
|
|
||||||
// Add virtual memory area
|
// Add virtual memory area
|
||||||
auto& new_vma = AddMapping(mapped_addr, size_aligned);
|
auto& new_vma = CarveVMA(mapped_addr, size_aligned)->second;
|
||||||
new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
|
new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
|
||||||
new_vma.prot = prot;
|
new_vma.prot = prot;
|
||||||
new_vma.name = "File";
|
new_vma.name = "File";
|
||||||
|
@ -238,10 +215,9 @@ int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, Mem
|
||||||
void MemoryManager::UnmapMemory(VAddr virtual_addr, size_t size) {
|
void MemoryManager::UnmapMemory(VAddr virtual_addr, size_t size) {
|
||||||
std::scoped_lock lk{mutex};
|
std::scoped_lock lk{mutex};
|
||||||
|
|
||||||
// TODO: Partial unmaps are technically supported by the guest.
|
const auto it = FindVMA(virtual_addr);
|
||||||
const auto it = vma_map.find(virtual_addr);
|
ASSERT_MSG(it->second.Contains(virtual_addr, size),
|
||||||
ASSERT_MSG(it != vma_map.end() && it->first == virtual_addr,
|
"Existing mapping does not contain requested unmap range");
|
||||||
"Attempting to unmap partially mapped range");
|
|
||||||
|
|
||||||
const auto type = it->second.type;
|
const auto type = it->second.type;
|
||||||
const bool has_backing = type == VMAType::Direct || type == VMAType::File;
|
const bool has_backing = type == VMAType::Direct || type == VMAType::File;
|
||||||
|
@ -253,11 +229,13 @@ void MemoryManager::UnmapMemory(VAddr virtual_addr, size_t size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark region as free and attempt to coalesce it with neighbours.
|
// Mark region as free and attempt to coalesce it with neighbours.
|
||||||
auto& vma = it->second;
|
const auto new_it = CarveVMA(virtual_addr, size);
|
||||||
|
auto& vma = new_it->second;
|
||||||
vma.type = VMAType::Free;
|
vma.type = VMAType::Free;
|
||||||
vma.prot = MemoryProt::NoAccess;
|
vma.prot = MemoryProt::NoAccess;
|
||||||
vma.phys_base = 0;
|
vma.phys_base = 0;
|
||||||
MergeAdjacent(vma_map, it);
|
vma.disallow_merge = false;
|
||||||
|
MergeAdjacent(vma_map, new_it);
|
||||||
|
|
||||||
// Unmap the memory region.
|
// Unmap the memory region.
|
||||||
impl.Unmap(virtual_addr, size, has_backing);
|
impl.Unmap(virtual_addr, size, has_backing);
|
||||||
|
@ -288,10 +266,10 @@ int MemoryManager::VirtualQuery(VAddr addr, int flags,
|
||||||
std::scoped_lock lk{mutex};
|
std::scoped_lock lk{mutex};
|
||||||
|
|
||||||
auto it = FindVMA(addr);
|
auto it = FindVMA(addr);
|
||||||
if (it->second.type == VMAType::Free && flags == 1) {
|
if (!it->second.IsMapped() && flags == 1) {
|
||||||
it++;
|
it++;
|
||||||
}
|
}
|
||||||
if (it->second.type == VMAType::Free) {
|
if (!it->second.IsMapped()) {
|
||||||
LOG_WARNING(Kernel_Vmm, "VirtualQuery on free memory region");
|
LOG_WARNING(Kernel_Vmm, "VirtualQuery on free memory region");
|
||||||
return ORBIS_KERNEL_ERROR_EACCES;
|
return ORBIS_KERNEL_ERROR_EACCES;
|
||||||
}
|
}
|
||||||
|
@ -360,14 +338,38 @@ std::pair<vk::Buffer, size_t> MemoryManager::GetVulkanBuffer(VAddr addr) {
|
||||||
return std::make_pair(*it->second.buffer, addr - it->first);
|
return std::make_pair(*it->second.buffer, addr - it->first);
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualMemoryArea& MemoryManager::AddMapping(VAddr virtual_addr, size_t size) {
|
VAddr MemoryManager::SearchFree(VAddr virtual_addr, size_t size, u32 alignment) {
|
||||||
|
auto it = FindVMA(virtual_addr);
|
||||||
|
// If the VMA is free and contains the requested mapping we are done.
|
||||||
|
if (it->second.IsFree() && it->second.Contains(virtual_addr, size)) {
|
||||||
|
return virtual_addr;
|
||||||
|
}
|
||||||
|
// Search for the first free VMA that fits our mapping.
|
||||||
|
const auto is_suitable = [&] {
|
||||||
|
if (!it->second.IsFree()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto& vma = it->second;
|
||||||
|
virtual_addr = Common::AlignUp(vma.base, alignment);
|
||||||
|
// Sometimes the alignment itself might be larger than the VMA.
|
||||||
|
if (virtual_addr > vma.base + vma.size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const size_t remaining_size = vma.base + vma.size - virtual_addr;
|
||||||
|
return remaining_size >= size;
|
||||||
|
};
|
||||||
|
while (!is_suitable()) {
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
return virtual_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryManager::VMAHandle MemoryManager::CarveVMA(VAddr virtual_addr, size_t size) {
|
||||||
auto vma_handle = FindVMA(virtual_addr);
|
auto vma_handle = FindVMA(virtual_addr);
|
||||||
ASSERT_MSG(vma_handle != vma_map.end(), "Virtual address not in vm_map");
|
ASSERT_MSG(vma_handle != vma_map.end(), "Virtual address not in vm_map");
|
||||||
|
|
||||||
const VirtualMemoryArea& vma = vma_handle->second;
|
const VirtualMemoryArea& vma = vma_handle->second;
|
||||||
ASSERT_MSG((vma.type == VMAType::Free || vma.type == VMAType::Reserved) &&
|
ASSERT_MSG(vma.base <= virtual_addr, "Adding a mapping to already mapped region");
|
||||||
vma.base <= virtual_addr,
|
|
||||||
"Adding a mapping to already mapped region");
|
|
||||||
|
|
||||||
const VAddr start_in_vma = virtual_addr - vma.base;
|
const VAddr start_in_vma = virtual_addr - vma.base;
|
||||||
const VAddr end_in_vma = start_in_vma + size;
|
const VAddr end_in_vma = start_in_vma + size;
|
||||||
|
@ -382,10 +384,10 @@ VirtualMemoryArea& MemoryManager::AddMapping(VAddr virtual_addr, size_t size) {
|
||||||
vma_handle = Split(vma_handle, start_in_vma);
|
vma_handle = Split(vma_handle, start_in_vma);
|
||||||
}
|
}
|
||||||
|
|
||||||
return vma_handle->second;
|
return vma_handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
DirectMemoryArea& MemoryManager::AddDmemAllocation(PAddr addr, size_t size) {
|
DirectMemoryArea& MemoryManager::CarveDmemArea(PAddr addr, size_t size) {
|
||||||
auto dmem_handle = FindDmemArea(addr);
|
auto dmem_handle = FindDmemArea(addr);
|
||||||
ASSERT_MSG(dmem_handle != dmem_map.end(), "Physical address not in dmem_map");
|
ASSERT_MSG(dmem_handle != dmem_map.end(), "Physical address not in dmem_map");
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,15 @@ struct VirtualMemoryArea {
|
||||||
uintptr_t fd = 0;
|
uintptr_t fd = 0;
|
||||||
|
|
||||||
bool Contains(VAddr addr, size_t size) const {
|
bool Contains(VAddr addr, size_t size) const {
|
||||||
return addr >= base && (addr + size) < (base + this->size);
|
return addr >= base && (addr + size) <= (base + this->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsFree() const noexcept {
|
||||||
|
return type == VMAType::Free;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsMapped() const noexcept {
|
||||||
|
return type != VMAType::Free && type != VMAType::Reserved;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CanMergeWith(const VirtualMemoryArea& next) const {
|
bool CanMergeWith(const VirtualMemoryArea& next) const {
|
||||||
|
@ -198,9 +206,11 @@ private:
|
||||||
return iter;
|
return iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualMemoryArea& AddMapping(VAddr virtual_addr, size_t size);
|
VAddr SearchFree(VAddr virtual_addr, size_t size, u32 alignment = 0);
|
||||||
|
|
||||||
DirectMemoryArea& AddDmemAllocation(PAddr addr, size_t size);
|
VMAHandle CarveVMA(VAddr virtual_addr, size_t size);
|
||||||
|
|
||||||
|
DirectMemoryArea& CarveDmemArea(PAddr addr, size_t size);
|
||||||
|
|
||||||
VMAHandle Split(VMAHandle vma_handle, size_t offset_in_vma);
|
VMAHandle Split(VMAHandle vma_handle, size_t offset_in_vma);
|
||||||
|
|
||||||
|
|
|
@ -150,10 +150,12 @@ void Emulator::Run(const std::filesystem::path& file) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Emulator::LoadSystemModules(const std::filesystem::path& file) {
|
void Emulator::LoadSystemModules(const std::filesystem::path& file) {
|
||||||
constexpr std::array<SysModules, 8> ModulesToLoad{
|
constexpr std::array<SysModules, 10> ModulesToLoad{
|
||||||
{{"libSceNgs2.sprx", nullptr},
|
{{"libSceNgs2.sprx", nullptr},
|
||||||
{"libSceFiber.sprx", nullptr},
|
{"libSceFiber.sprx", nullptr},
|
||||||
{"libSceUlt.sprx", nullptr},
|
{"libSceUlt.sprx", nullptr},
|
||||||
|
{"libSceJson.sprx", nullptr},
|
||||||
|
{"libSceJson2.sprx", nullptr},
|
||||||
{"libSceLibcInternal.sprx", &Libraries::LibcInternal::RegisterlibSceLibcInternal},
|
{"libSceLibcInternal.sprx", &Libraries::LibcInternal::RegisterlibSceLibcInternal},
|
||||||
{"libSceDiscMap.sprx", &Libraries::DiscMap::RegisterlibSceDiscMap},
|
{"libSceDiscMap.sprx", &Libraries::DiscMap::RegisterlibSceDiscMap},
|
||||||
{"libSceRtc.sprx", &Libraries::Rtc::RegisterlibSceRtc},
|
{"libSceRtc.sprx", &Libraries::Rtc::RegisterlibSceRtc},
|
||||||
|
|
|
@ -388,6 +388,10 @@ spv::ImageFormat GetFormat(const AmdGpu::Image& image) {
|
||||||
image.GetNumberFmt() == AmdGpu::NumberFormat::Unorm) {
|
image.GetNumberFmt() == AmdGpu::NumberFormat::Unorm) {
|
||||||
return spv::ImageFormat::Rgba8;
|
return spv::ImageFormat::Rgba8;
|
||||||
}
|
}
|
||||||
|
if (image.GetDataFmt() == AmdGpu::DataFormat::Format8_8_8_8 &&
|
||||||
|
image.GetNumberFmt() == AmdGpu::NumberFormat::Uint) {
|
||||||
|
return spv::ImageFormat::Rgba8ui;
|
||||||
|
}
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue