diff --git a/src/core/libraries/kernel/memory_management.cpp b/src/core/libraries/kernel/memory_management.cpp index 02978177..f544b8a4 100644 --- a/src/core/libraries/kernel/memory_management.cpp +++ b/src/core/libraries/kernel/memory_management.cpp @@ -216,6 +216,23 @@ int PS4_SYSV_ABI sceKernelMProtect(void* addr, size_t size, int prot) { return result; } +int PS4_SYSV_ABI sceKernelMTypeProtect(void* addr, size_t size, int mtype, int prot) { + LOG_INFO(Kernel_Vmm, "called addr = {}, size = {:#x}, mtype = {:#x}, prot = {:#x}", + fmt::ptr(addr), size, mtype, prot); + Core::MemoryManager* memory_manager = Core::Memory::Instance(); + if (!memory_manager) { + LOG_ERROR(Kernel_Vmm, "Failed to get MemoryManager instance"); + return ORBIS_KERNEL_ERROR_EINVAL; + } + + int result = memory_manager->MTypeProtect(std::bit_cast(addr), size, + static_cast(mtype), prot); + if (result != ORBIS_OK) { + LOG_ERROR(Kernel_Vmm, "MTypeProtect failed with result {}", result); + } + return result; +} + int PS4_SYSV_ABI sceKernelDirectMemoryQuery(u64 offset, int flags, OrbisQueryInfo* query_info, size_t infoSize) { LOG_WARNING(Kernel_Vmm, "called offset = {:#x}, flags = {:#x}", offset, flags); @@ -284,8 +301,8 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn processed++; } else if (entries[i].operation == MemoryOpTypes::ORBIS_KERNEL_MAP_OP_TYPE_PROTECT) { // MPROTECT - result = - sceKernelMProtect(entries[i].start, entries[i].length, entries[i].protection); + result = sceKernelMTypeProtect(entries[i].start, entries[i].length, entries[i].type, + entries[i].protection); LOG_INFO(Kernel_Vmm, "BatchMap: entry = {}, operation = {}, len = {:#x}, result = {}", i, entries[i].operation, entries[i].length, result); @@ -296,6 +313,19 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn if (result == 0) { processed++; } + } else if (entries[i].operation == + MemoryOpTypes::ORBIS_KERNEL_MAP_OP_PROTECT) { // MPROTECT + result = sceKernelMProtect(entries[i].start, entries[i].length, + entries[i].protection); + LOG_INFO(Kernel_Vmm, "BatchMap: entry = {}, operation = {}, len = {:#x}, result = {}", + i, entries[i].operation, entries[i].length, result); + if (result != ORBIS_OK) { + LOG_ERROR(Kernel_Vmm, "BatchMap: MProtect failed on entry {} with result {}", i, + result); + } + if (result == 0) { + processed++; + } } else if (entries[i].operation == MemoryOpTypes::ORBIS_KERNEL_MAP_OP_MAP_FLEXIBLE) { result = sceKernelMapNamedFlexibleMemory(&entries[i].start, entries[i].length, entries[i].protection, flags, ""); diff --git a/src/core/libraries/kernel/memory_management.h b/src/core/libraries/kernel/memory_management.h index 25a4a9f0..301a1ef5 100644 --- a/src/core/libraries/kernel/memory_management.h +++ b/src/core/libraries/kernel/memory_management.h @@ -95,6 +95,10 @@ s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, std::size_t len, int flags); int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void** end, u32* prot); +int PS4_SYSV_ABI sceKernelMProtect(void* addr, size_t size, int prot); + +int PS4_SYSV_ABI sceKernelMTypeProtect(void* addr, size_t size, int mtype, int prot); + int PS4_SYSV_ABI sceKernelDirectMemoryQuery(u64 offset, int flags, OrbisQueryInfo* query_info, size_t infoSize); s32 PS4_SYSV_ABI sceKernelAvailableFlexibleMemorySize(size_t* sizeOut); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 28a348d4..0d49ce98 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -315,6 +315,58 @@ int MemoryManager::MProtect(VAddr addr, size_t size, int prot) { return ORBIS_OK; } +int MemoryManager::MTypeProtect(VAddr addr, size_t size, VMAType mtype, int prot) { + std::scoped_lock lk{mutex}; + + // Find the virtual memory area that contains the specified address range. + auto it = FindVMA(addr); + if (it == vma_map.end() || !it->second.Contains(addr, size)) { + LOG_ERROR(Core, "Address range not mapped"); + return ORBIS_KERNEL_ERROR_EINVAL; + } + + VirtualMemoryArea& vma = it->second; + if (vma.type == VMAType::Free) { + LOG_ERROR(Core, "Cannot change protection on free memory region"); + return ORBIS_KERNEL_ERROR_EINVAL; + } + + // Check if the new protection flags are valid. + if ((static_cast(prot) & + ~(static_cast(MemoryProt::NoAccess) | static_cast(MemoryProt::CpuRead) | + static_cast(MemoryProt::CpuReadWrite) | static_cast(MemoryProt::GpuRead) | + static_cast(MemoryProt::GpuWrite) | static_cast(MemoryProt::GpuReadWrite))) != + 0) { + LOG_ERROR(Core, "Invalid protection flags, prot: {:#x}, GpuWrite: {:#x}", + static_cast(prot), static_cast(MemoryProt::GpuWrite)); + return ORBIS_KERNEL_ERROR_EINVAL; + } + + // Change the type and protection on the specified address range. + vma.type = mtype; + vma.prot = static_cast(prot); + + // Use the Protect function from the AddressSpace class. + Core::MemoryPermission perms; + if ((static_cast(prot) & static_cast(MemoryProt::CpuRead)) != 0) + perms |= Core::MemoryPermission::Read; + if ((static_cast(prot) & static_cast(MemoryProt::CpuReadWrite)) != 0) + perms |= Core::MemoryPermission::ReadWrite; + if ((static_cast(prot) & static_cast(MemoryProt::GpuRead)) != 0) + perms |= Core::MemoryPermission::Read; + if ((static_cast(prot) & static_cast(MemoryProt::GpuWrite)) != 0) + perms |= Core::MemoryPermission::Write; + if ((static_cast(prot) & static_cast(MemoryProt::GpuReadWrite)) != 0) + perms |= Core::MemoryPermission::ReadWrite; + impl.Protect(addr, size, perms); + + LOG_INFO(Core, "Changed type and protection on range {:#x}-{:#x} to {:#x} {:#x}", addr, + addr + size, mtype, prot); + return ORBIS_OK; +} + + + int MemoryManager::VirtualQuery(VAddr addr, int flags, Libraries::Kernel::OrbisVirtualQueryInfo* info) { diff --git a/src/core/memory.h b/src/core/memory.h index 0a9641d3..3e8b5021 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -167,6 +167,8 @@ public: int MProtect(VAddr addr, size_t size, int prot); + int MTypeProtect(VAddr addr, size_t size, VMAType mtype, int prot); + int VirtualQuery(VAddr addr, int flags, Libraries::Kernel::OrbisVirtualQueryInfo* info); int DirectMemoryQuery(PAddr addr, bool find_next, Libraries::Kernel::OrbisQueryInfo* out_info);