diff --git a/include/memory.hpp b/include/memory.hpp index 472fe691..f2bc8383 100644 --- a/include/memory.hpp +++ b/include/memory.hpp @@ -102,6 +102,7 @@ namespace KernelMemoryTypes { }; } +struct FcramBlock; class KFcram; enum class FcramRegion; @@ -187,6 +188,7 @@ private: static constexpr std::array MACAddress = {0x40, 0xF4, 0x07, 0xFF, 0xFF, 0xEE}; void changeMemoryState(u32 vaddr, s32 pages, const Operation& op); + void queryPhysicalBlocks(std::list& outList, u32 vaddr, s32 pages); void mapPhysicalMemory(u32 vaddr, u32 paddr, s32 pages, bool r, bool w, bool x); void unmapPhysicalMemory(u32 vaddr, u32 paddr, s32 pages); @@ -256,6 +258,7 @@ private: bool mapVirtualMemory(u32 dstVaddr, u32 srcVaddr, s32 pages, bool r, bool w, bool x, KernelMemoryTypes::MemoryState oldDstState, KernelMemoryTypes::MemoryState oldSrcState, KernelMemoryTypes::MemoryState newDstState, KernelMemoryTypes::MemoryState newSrcState); + void changePermissions(u32 vaddr, s32 pages, bool r, bool w, bool x); Result::HorizonResult queryMemory(KernelMemoryTypes::MemoryInfo& out, u32 vaddr); Result::HorizonResult testMemoryState(u32 vaddr, s32 pages, KernelMemoryTypes::MemoryState desiredState); diff --git a/src/core/kernel/memory_management.cpp b/src/core/kernel/memory_management.cpp index 46d699a8..cc8b9313 100644 --- a/src/core/kernel/memory_management.cpp +++ b/src/core/kernel/memory_management.cpp @@ -94,7 +94,15 @@ void Kernel::controlMemory() { break; case Operation::Protect: - Helpers::warn("Ignoring mprotect! Hope nothing goes wrong but if the game accesses invalid memory or crashes then we prolly need to implement this\n"); + // Official kernel has an internal state bit to indicate that the region's permissions may be changed + // But this should account for all cases + if (!mem.testMemoryState(addr0, pages, MemoryState::Private) && + !mem.testMemoryState(addr0, pages, MemoryState::Alias) && + !mem.testMemoryState(addr0, pages, MemoryState::Aliased) && + !mem.testMemoryState(addr0, pages, MemoryState::AliasCode)) Helpers::panic("Tried to mprotect invalid region!"); + + mem.changePermissions(addr0, pages, r, w, false); + regs[1] = addr0; break; default: Helpers::warn("ControlMemory: unknown operation %X\n", operation); break; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 624780f2..991c5437 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -346,6 +346,28 @@ void Memory::changeMemoryState(u32 vaddr, s32 pages, const Operation& op) { } } +void Memory::queryPhysicalBlocks(FcramBlockList& outList, u32 vaddr, s32 pages) { + s32 srcPages = pages; + for (auto& alloc : memoryInfo) { + u32 blockStart = alloc.baseAddr; + u32 blockEnd = alloc.end(); + + if (!(vaddr >= blockStart && vaddr < blockEnd)) continue; + + s32 blockPaddr = paddrTable[vaddr >> 12]; + s32 blockPages = alloc.pages - ((vaddr - blockStart) >> 12); + blockPages = std::min(srcPages, blockPages); + FcramBlock physicalBlock(blockPaddr, blockPages); + outList.push_back(physicalBlock); + + vaddr += blockPages << 12; + srcPages -= blockPages; + if (srcPages == 0) break; + } + + if (srcPages != 0) Helpers::panic("Unable to find virtual pages to map!"); +} + void Memory::mapPhysicalMemory(u32 vaddr, u32 paddr, s32 pages, bool r, bool w, bool x) { assert(!(vaddr & 0xFFF)); assert(!(paddr & 0xFFF)); @@ -431,26 +453,7 @@ bool Memory::mapVirtualMemory(u32 dstVaddr, u32 srcVaddr, s32 pages, bool r, boo // Get a list of physical blocks in the source region FcramBlockList physicalList; - - s32 srcPages = pages; - for (auto& alloc : memoryInfo) { - u32 blockStart = alloc.baseAddr; - u32 blockEnd = alloc.end(); - - if (!(srcVaddr >= blockStart && srcVaddr < blockEnd)) continue; - - s32 blockPaddr = paddrTable[srcVaddr >> 12]; - s32 blockPages = alloc.pages - ((srcVaddr - blockStart) >> 12); - blockPages = std::min(srcPages, blockPages); - FcramBlock physicalBlock(blockPaddr, blockPages); - physicalList.push_back(physicalBlock); - - srcVaddr += blockPages << 12; - srcPages -= blockPages; - if (srcPages == 0) break; - } - - if (srcPages != 0) Helpers::panic("Unable to find virtual pages to map!"); + queryPhysicalBlocks(physicalList, srcVaddr, pages); // Map or unmap each physical block for (auto& block : physicalList) { @@ -462,6 +465,20 @@ bool Memory::mapVirtualMemory(u32 dstVaddr, u32 srcVaddr, s32 pages, bool r, boo return true; } +void Memory::changePermissions(u32 vaddr, s32 pages, bool r, bool w, bool x) { + Operation op{ .r = r, .w = w, .x = x, .changePerms = true }; + changeMemoryState(vaddr, pages, op); + + // Now that permissions have been changed, update the corresponding host tables + FcramBlockList physicalList; + queryPhysicalBlocks(physicalList, vaddr, pages); + + for (auto& block : physicalList) { + mapPhysicalMemory(vaddr, block.paddr, block.pages, r, w, x); + vaddr += block.pages; + } +} + Result::HorizonResult Memory::queryMemory(MemoryInfo& out, u32 vaddr) { // Check each allocation for (auto& alloc : memoryInfo) {