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:
TheTurtle 2024-07-25 23:01:12 +03:00 committed by GitHub
parent bc28ed66e8
commit a2cd1669b6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 198 additions and 90 deletions

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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");
@ -141,4 +139,4 @@ void RegisterlibScePlayGo(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("MPe0EeBGM-E", "libScePlayGo", 1, "libScePlayGo", 1, 0, scePlayGoTerminate); LIB_FUNCTION("MPe0EeBGM-E", "libScePlayGo", 1, "libScePlayGo", 1, 0, scePlayGoTerminate);
}; };
} // namespace Libraries::PlayGo } // namespace Libraries::PlayGo

View File

@ -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(

View File

@ -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,91 +145,63 @@ 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);
new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
new_vma.prot = prot;
new_vma.name = name;
new_vma.type = type;
if (type == VMAType::Direct) {
new_vma.phys_base = phys_addr;
MapVulkanMemory(mapped_addr, size);
}
if (type == VMAType::Flexible) {
flexible_usage += size;
}
};
// Fixed mapping means the virtual address must exactly match the provided one. // Fixed mapping means the virtual address must exactly match the provided one.
if (True(flags & MemoryMapFlags::Fixed) && True(flags & MemoryMapFlags::NoOverwrite)) { if (True(flags & MemoryMapFlags::Fixed)) {
// This should return SCE_KERNEL_ERROR_ENOMEM but shouldn't normally happen. // This should return SCE_KERNEL_ERROR_ENOMEM but shouldn't normally happen.
const auto& vma = FindVMA(mapped_addr)->second; const auto& vma = FindVMA(mapped_addr)->second;
const size_t remaining_size = vma.base + vma.size - mapped_addr; const size_t remaining_size = vma.base + vma.size - mapped_addr;
ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size); ASSERT_MSG(!vma.IsMapped() && 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;
}
} }
// Perform the mapping. // Perform the mapping.
*out_addr = impl.Map(mapped_addr, size, alignment, phys_addr, is_exec); *out_addr = impl.Map(mapped_addr, size, alignment, phys_addr, is_exec);
TRACK_ALLOC(*out_addr, size, "VMEM"); TRACK_ALLOC(*out_addr, size, "VMEM");
auto& new_vma = CarveVMA(mapped_addr, size)->second;
new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
new_vma.prot = prot;
new_vma.name = name;
new_vma.type = type;
if (type == VMAType::Direct) {
new_vma.phys_base = phys_addr;
MapVulkanMemory(mapped_addr, size);
}
if (type == VMAType::Flexible) {
flexible_usage += size;
}
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");

View File

@ -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);

View File

@ -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},

View File

@ -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();
} }