Core: Memory code cleanup and further direct memory fixes (#3655)

* Remove mapped dmem type

Since physical addresses can be mapped multiple times, tracking mapped pages is not necessary.
This also allows me to significantly simplify the MapMemory physical address validation logic.

* Proper implementation for sceKernelMtypeprotect

I've rewritten SetDirectMemoryType to use virtual addresses instead of physical addresses, allowing it to be used in sceKernelMtypeprotect.

To accommodate this change, I've also moved address and size alignment out of MemoryManager::Protect

* Apply memory type in sceKernelMemoryPoolCommit

* Organization

Some potentially important missing mutexes, removed some unnecessary mutexes, moved some mutexes after early error returns, and updated copyright dates

* Iterator logic cleanup

Missing end check in ClampRangeSize, and adjusted VirtualQuery and DirectMemoryQuery.

* Clang

* Adjustments

* Properly account for behavior differences in MapDirectMemory2

Undid the changes to direct memory areas, added more robust logic for changing dma types, and fixed DirectMemoryQuery to return hardware-accurate direct memory information in cases where dmas split here, but not on real hardware.

I've also changed MapMemory's is_exec flag to a validate_dmem flag, used to handle alternate behavior in MapDirectMemory2. is_exec is now determined by the use of MemoryProt::CpuExec instead.

* Clang

* Add execute permissions to physical backing

Needed for executable mappings to work properly on Windows, fixes regression in RE2 with prior commit.

* Minor variable cleanup

* Update memory.h

* Prohibit direct memory mappings with exec protections

Did a quick hardware test to confirm, only seems to be prohibited for dmem mappings though.

* Update memory.cpp
This commit is contained in:
Stephen Miller
2025-09-26 04:28:32 -05:00
committed by GitHub
parent 6e27842562
commit 528a060709
6 changed files with 250 additions and 109 deletions

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <bit>
@@ -159,19 +159,18 @@ s32 PS4_SYSV_ABI sceKernelReserveVirtualRange(void** addr, u64 len, s32 flags, u
}
s32 PS4_SYSV_ABI sceKernelMapNamedDirectMemory(void** addr, u64 len, s32 prot, s32 flags,
s64 directMemoryStart, u64 alignment,
const char* name) {
s64 phys_addr, u64 alignment, const char* name) {
LOG_INFO(Kernel_Vmm,
"in_addr = {}, len = {:#x}, prot = {:#x}, flags = {:#x}, "
"directMemoryStart = {:#x}, alignment = {:#x}, name = '{}'",
fmt::ptr(*addr), len, prot, flags, directMemoryStart, alignment, name);
"phys_addr = {:#x}, alignment = {:#x}, name = '{}'",
fmt::ptr(*addr), len, prot, flags, phys_addr, alignment, name);
if (len == 0 || !Common::Is16KBAligned(len)) {
LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 16KB aligned!");
return ORBIS_KERNEL_ERROR_EINVAL;
}
if (!Common::Is16KBAligned(directMemoryStart)) {
if (!Common::Is16KBAligned(phys_addr)) {
LOG_ERROR(Kernel_Vmm, "Start address is not 16KB aligned!");
return ORBIS_KERNEL_ERROR_EINVAL;
}
@@ -188,35 +187,72 @@ s32 PS4_SYSV_ABI sceKernelMapNamedDirectMemory(void** addr, u64 len, s32 prot, s
return ORBIS_KERNEL_ERROR_ENAMETOOLONG;
}
const VAddr in_addr = reinterpret_cast<VAddr>(*addr);
const auto mem_prot = static_cast<Core::MemoryProt>(prot);
if (True(mem_prot & Core::MemoryProt::CpuExec)) {
LOG_ERROR(Kernel_Vmm, "Executable permissions are not allowed.");
return ORBIS_KERNEL_ERROR_EACCES;
}
const auto map_flags = static_cast<Core::MemoryMapFlags>(flags);
const VAddr in_addr = reinterpret_cast<VAddr>(*addr);
auto* memory = Core::Memory::Instance();
const auto ret =
memory->MapMemory(addr, in_addr, len, mem_prot, map_flags, Core::VMAType::Direct, name,
false, directMemoryStart, alignment);
const auto ret = memory->MapMemory(addr, in_addr, len, mem_prot, map_flags,
Core::VMAType::Direct, name, false, phys_addr, alignment);
LOG_INFO(Kernel_Vmm, "out_addr = {}", fmt::ptr(*addr));
return ret;
}
s32 PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, s32 prot, s32 flags,
s64 directMemoryStart, u64 alignment) {
LOG_INFO(Kernel_Vmm, "called, redirected to sceKernelMapNamedDirectMemory");
return sceKernelMapNamedDirectMemory(addr, len, prot, flags, directMemoryStart, alignment,
"anon");
s32 PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, s32 prot, s32 flags, s64 phys_addr,
u64 alignment) {
LOG_TRACE(Kernel_Vmm, "called, redirected to sceKernelMapNamedDirectMemory");
return sceKernelMapNamedDirectMemory(addr, len, prot, flags, phys_addr, alignment, "anon");
}
s32 PS4_SYSV_ABI sceKernelMapDirectMemory2(void** addr, u64 len, s32 type, s32 prot, s32 flags,
s64 phys_addr, u64 alignment) {
LOG_INFO(Kernel_Vmm, "called, redirected to sceKernelMapNamedDirectMemory");
const s32 ret =
sceKernelMapNamedDirectMemory(addr, len, prot, flags, phys_addr, alignment, "anon");
LOG_INFO(Kernel_Vmm,
"in_addr = {}, len = {:#x}, prot = {:#x}, flags = {:#x}, "
"phys_addr = {:#x}, alignment = {:#x}",
fmt::ptr(*addr), len, prot, flags, phys_addr, alignment);
if (len == 0 || !Common::Is16KBAligned(len)) {
LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 16KB aligned!");
return ORBIS_KERNEL_ERROR_EINVAL;
}
if (!Common::Is16KBAligned(phys_addr)) {
LOG_ERROR(Kernel_Vmm, "Start address is not 16KB aligned!");
return ORBIS_KERNEL_ERROR_EINVAL;
}
if (alignment != 0) {
if ((!std::has_single_bit(alignment) && !Common::Is16KBAligned(alignment))) {
LOG_ERROR(Kernel_Vmm, "Alignment value is invalid!");
return ORBIS_KERNEL_ERROR_EINVAL;
}
}
const auto mem_prot = static_cast<Core::MemoryProt>(prot);
if (True(mem_prot & Core::MemoryProt::CpuExec)) {
LOG_ERROR(Kernel_Vmm, "Executable permissions are not allowed.");
return ORBIS_KERNEL_ERROR_EACCES;
}
const auto map_flags = static_cast<Core::MemoryMapFlags>(flags);
const VAddr in_addr = reinterpret_cast<VAddr>(*addr);
auto* memory = Core::Memory::Instance();
const auto ret = memory->MapMemory(addr, in_addr, len, mem_prot, map_flags,
Core::VMAType::Direct, "anon", true, phys_addr, alignment);
if (ret == 0) {
// If the map call succeeds, set the direct memory type using the output address.
auto* memory = Core::Memory::Instance();
memory->SetDirectMemoryType(phys_addr, type);
const auto out_addr = reinterpret_cast<VAddr>(*addr);
memory->SetDirectMemoryType(out_addr, len, type);
LOG_INFO(Kernel_Vmm, "out_addr = {:#x}", out_addr);
}
return ret;
}
@@ -262,9 +298,20 @@ s32 PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void**
s32 PS4_SYSV_ABI sceKernelMprotect(const void* addr, u64 size, s32 prot) {
LOG_INFO(Kernel_Vmm, "called addr = {}, size = {:#x}, prot = {:#x}", fmt::ptr(addr), size,
prot);
// Align addr and size to the nearest page boundary.
const VAddr in_addr = reinterpret_cast<VAddr>(addr);
auto aligned_addr = Common::AlignDown(in_addr, 16_KB);
auto aligned_size = Common::AlignUp(size + in_addr - aligned_addr, 16_KB);
if (aligned_size == 0) {
// Nothing to do.
return ORBIS_OK;
}
Core::MemoryManager* memory_manager = Core::Memory::Instance();
Core::MemoryProt protection_flags = static_cast<Core::MemoryProt>(prot);
return memory_manager->Protect(std::bit_cast<VAddr>(addr), size, protection_flags);
return memory_manager->Protect(aligned_addr, aligned_size, protection_flags);
}
s32 PS4_SYSV_ABI posix_mprotect(const void* addr, u64 size, s32 prot) {
@@ -279,9 +326,24 @@ s32 PS4_SYSV_ABI posix_mprotect(const void* addr, u64 size, s32 prot) {
s32 PS4_SYSV_ABI sceKernelMtypeprotect(const void* addr, u64 size, s32 mtype, s32 prot) {
LOG_INFO(Kernel_Vmm, "called addr = {}, size = {:#x}, prot = {:#x}", fmt::ptr(addr), size,
prot);
// Align addr and size to the nearest page boundary.
const VAddr in_addr = reinterpret_cast<VAddr>(addr);
auto aligned_addr = Common::AlignDown(in_addr, 16_KB);
auto aligned_size = Common::AlignUp(size + in_addr - aligned_addr, 16_KB);
if (aligned_size == 0) {
// Nothing to do.
return ORBIS_OK;
}
Core::MemoryManager* memory_manager = Core::Memory::Instance();
Core::MemoryProt protection_flags = static_cast<Core::MemoryProt>(prot);
return memory_manager->Protect(std::bit_cast<VAddr>(addr), size, protection_flags);
s32 result = memory_manager->Protect(aligned_addr, aligned_size, protection_flags);
if (result == ORBIS_OK) {
memory_manager->SetDirectMemoryType(aligned_addr, aligned_size, mtype);
}
return result;
}
s32 PS4_SYSV_ABI sceKernelDirectMemoryQuery(u64 offset, s32 flags, OrbisQueryInfo* query_info,
@@ -481,7 +543,7 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, u64 len, s32 type, s32 pr
const VAddr in_addr = reinterpret_cast<VAddr>(addr);
const auto mem_prot = static_cast<Core::MemoryProt>(prot);
auto* memory = Core::Memory::Instance();
return memory->PoolCommit(in_addr, len, mem_prot);
return memory->PoolCommit(in_addr, len, mem_prot, type);
}
s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, u64 len, s32 flags) {
@@ -588,12 +650,11 @@ void* PS4_SYSV_ABI posix_mmap(void* addr, u64 len, s32 prot, s32 flags, s32 fd,
auto* memory = Core::Memory::Instance();
const auto mem_prot = static_cast<Core::MemoryProt>(prot);
const auto mem_flags = static_cast<Core::MemoryMapFlags>(flags);
const auto is_exec = True(mem_prot & Core::MemoryProt::CpuExec);
s32 result = ORBIS_OK;
if (fd == -1) {
result = memory->MapMemory(&addr_out, std::bit_cast<VAddr>(addr), len, mem_prot, mem_flags,
Core::VMAType::Flexible, "anon", is_exec);
Core::VMAType::Flexible, "anon", false);
} else {
result = memory->MapFile(&addr_out, std::bit_cast<VAddr>(addr), len, mem_prot, mem_flags,
fd, phys_addr);

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -137,10 +137,9 @@ u64 PS4_SYSV_ABI sceKernelGetDirectMemorySize();
s32 PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u64 len,
u64 alignment, s32 memoryType, s64* physAddrOut);
s32 PS4_SYSV_ABI sceKernelMapNamedDirectMemory(void** addr, u64 len, s32 prot, s32 flags,
s64 directMemoryStart, u64 alignment,
const char* name);
s32 PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, s32 prot, s32 flags,
s64 directMemoryStart, u64 alignment);
s64 phys_addr, u64 alignment, const char* name);
s32 PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, s32 prot, s32 flags, s64 phys_addr,
u64 alignment);
s32 PS4_SYSV_ABI sceKernelAllocateMainDirectMemory(u64 len, u64 alignment, s32 memoryType,
s64* physAddrOut);
s32 PS4_SYSV_ABI sceKernelReleaseDirectMemory(u64 start, u64 len);