diff --git a/kernel/orbis/include/orbis/vmem.hpp b/kernel/orbis/include/orbis/vmem.hpp index 6beeae0ac..000ff4bfb 100644 --- a/kernel/orbis/include/orbis/vmem.hpp +++ b/kernel/orbis/include/orbis/vmem.hpp @@ -167,7 +167,8 @@ inline bool validateProtection(rx::EnumBitSet &prot) { inline bool validateMemoryType(MemoryType type, rx::EnumBitSet prot) { if (type == MemoryType::WbGarlic) { - if (prot & (Protection::CpuWrite | Protection::GpuWrite)) { + if ((prot & (Protection::CpuWrite | Protection::GpuWrite)) == + (Protection::CpuWrite | Protection::GpuWrite)) { return false; } } diff --git a/kernel/orbis/src/dmem.cpp b/kernel/orbis/src/dmem.cpp index 424e43302..9fc22b865 100644 --- a/kernel/orbis/src/dmem.cpp +++ b/kernel/orbis/src/dmem.cpp @@ -68,7 +68,7 @@ struct DirectMemoryAllocation { [[nodiscard]] bool isAllocated() const { return (type & kAllocatedBit) != 0; } [[nodiscard]] bool isRelated(const DirectMemoryAllocation &other, rx::AddressRange, rx::AddressRange) const { - return type == other.type; + return false; } [[nodiscard]] DirectMemoryAllocation @@ -249,11 +249,20 @@ static void dmemDump(unsigned dmemIndex, std::string_view message = {}) { rx::ScopedFileLock lock(stderr); - rx::println(stderr, "dmem0 {}", message); + rx::println(stderr, "dmem{} {}", dmemIndex, message); for (auto alloc : *dmem) { rx::println(stderr, " {:012x}-{:012x}: {}{} {}", alloc.beginAddress(), alloc.endAddress(), "_A"[alloc->isAllocated()], "_P"[alloc->isPooled()], alloc->getMemoryType()); + for (auto &map : alloc->mappings) { + auto dmemRange = + rx::AddressRange::fromBeginSize(map.dmemOffset, map.vmRange.size()); + + rx::println(stderr, " {:#x}-{:#x} -> {:#x}-{:#x} pid {}", + dmemRange.beginAddress(), dmemRange.endAddress(), + map.vmRange.beginAddress(), map.vmRange.endAddress(), + map.process->pid); + } } } @@ -278,7 +287,8 @@ orbis::dmem::allocate(unsigned dmemIndex, rx::AddressRange searchRange, alignment = alignment == 0 ? kPageSize : alignment; len = rx::alignUp(len, dmem::kPageSize); - if (searchRange.endAddress() == 0) { + if (searchRange.endAddress() == 0 || + searchRange.endAddress() > dmem->dmemTotalSize - dmem->dmemReservedSize) { searchRange = rx::AddressRange::fromBeginEnd(searchRange.beginAddress(), dmem->dmemTotalSize - dmem->dmemReservedSize); @@ -732,6 +742,10 @@ orbis::ErrorCode orbis::dmem::map(orbis::Process *process, unsigned dmemIndex, } if (offset + range.size() > dmem->dmemTotalSize - dmem->dmemReservedSize) { + dmemDump(dmemIndex, + rx::format("map out of memory {:x}-{:x}, dmem size {:x}", + range.beginAddress(), range.endAddress(), + dmem->dmemTotalSize - dmem->dmemReservedSize)); return orbis::ErrorCode::ACCES; } @@ -746,10 +760,17 @@ orbis::ErrorCode orbis::dmem::map(orbis::Process *process, unsigned dmemIndex, auto endIt = beginIt; while (endIt != dmem->end() && endIt.beginAddress() < offset + range.size()) { if (!endIt->isAllocated() || endIt->isPooled()) { + dmemDump(dmemIndex, + rx::format("attempt to map unallocated or pulled memory " + "{:x}-{:x}, dmem size {:x}", + range.beginAddress(), range.endAddress(), + dmem->dmemTotalSize - dmem->dmemReservedSize)); return orbis::ErrorCode::ACCES; } if (!vmem::validateMemoryType(endIt->getMemoryType(), protection)) { + dmemDump(dmemIndex, rx::format("invalid protection {} for memory type {}", + protection, endIt->getMemoryType())); return ErrorCode::ACCES; } @@ -761,20 +782,33 @@ orbis::ErrorCode orbis::dmem::map(orbis::Process *process, unsigned dmemIndex, } if (auto last = endIt; (--last).endAddress() < offset + range.size()) { + dmemDump(dmemIndex, + rx::format("map out of memory {:x}-{:x}, dmem size {:x}", + range.beginAddress(), range.endAddress(), + dmem->dmemTotalSize - dmem->dmemReservedSize)); return orbis::ErrorCode::ACCES; } + auto dmemRange = rx::AddressRange::fromBeginSize(offset, range.size()); + for (auto it = beginIt; it != endIt; ++it) { auto itRange = it.range(); - auto mappingRange = range.intersection(itRange); + auto mappingDmemRange = dmemRange.intersection(itRange); + auto mappingVmemRange = rx::AddressRange::fromBeginSize( + range.beginAddress() + (mappingDmemRange.beginAddress() - offset), + mappingDmemRange.size()); + it->mappings.push_back({ .process = process, - .vmRange = mappingRange, - .dmemOffset = - offset + (mappingRange.beginAddress() - itRange.beginAddress()), + .vmRange = mappingVmemRange, + .dmemOffset = mappingDmemRange.beginAddress(), }); } + dmemDump(dmemIndex, + rx::format("map {:#x}-{:#x} -> {:#x}-{:#x} pid {}", + dmemRange.beginAddress(), dmemRange.endAddress(), + range.beginAddress(), range.endAddress(), process->pid)); auto physicalRange = rx::AddressRange::fromBeginSize(dmem->pmemOffset + offset, range.size()); @@ -805,6 +839,9 @@ orbis::ErrorCode orbis::dmem::notifyUnmap(orbis::Process *process, ++it; } + dmemDump(dmemIndex, rx::format("unmap {:#x}-{:#x}", range.beginAddress(), + range.endAddress())); + return {}; } diff --git a/kernel/orbis/src/vmem.cpp b/kernel/orbis/src/vmem.cpp index 105b0c973..0af0d1984 100644 --- a/kernel/orbis/src/vmem.cpp +++ b/kernel/orbis/src/vmem.cpp @@ -118,6 +118,13 @@ struct VirtualMemoryAllocation { orbis::vmem::BlockFlags::FlexibleMemory; } + [[nodiscard]] bool isCommited() const { + return (flags & orbis::vmem::BlockFlags::Commited) == + orbis::vmem::BlockFlags::Commited; + } + + [[nodiscard]] bool isFlexCommited() const { return isFlex() && isCommited(); } + [[nodiscard]] bool isDirect() const { return (flags & orbis::vmem::BlockFlags::DirectMemory) == orbis::vmem::BlockFlags::DirectMemory; @@ -400,7 +407,43 @@ void orbis::vmem::initialize(Process *process, bool force) { } void orbis::vmem::fork(Process *process, Process *parentThread) { - // FIXME: implement + auto vmem = process->get(g_vmInstance); + auto parentVmem = parentThread->get(g_vmInstance); + + std::lock_guard lock(*parentVmem); + + std::vector tmpData; + + for (auto alloc : parentVmem->allocations) { + if (alloc->isAllocated() && alloc->isFlexCommited()) { + auto clonedAllocation = alloc.get(); + auto [flexRange, errc] = fmem::allocate(alloc.size()); + + rx::dieIf(errc != ErrorCode{}, "fork: fmem allocation failed: {}", errc); + clonedAllocation.deviceOffset = flexRange.beginAddress(); + auto cpuProt = toCpuProtection(alloc->prot); + tmpData.resize(alloc.size()); + std::memcpy(tmpData.data(), std::bit_cast(alloc.beginAddress()), + alloc.size()); + + pmem::map(alloc.beginAddress(), flexRange, cpuProt); + vmem->allocations.map(alloc, clonedAllocation); + + if (!(cpuProt & rx::mem::Protection::W)) { + rx::mem::protect(alloc, cpuProt | rx::mem::Protection::W); + } + + std::memcpy(std::bit_cast(alloc.beginAddress()), tmpData.data(), + tmpData.size()); + + if (!(cpuProt & rx::mem::Protection::W)) { + rx::mem::protect(alloc, cpuProt); + } + + } else { + vmem->allocations.map(alloc, alloc.get()); + } + } } std::pair orbis::vmem::reserve( @@ -826,6 +869,18 @@ std::pair orbis::vmem::mapFlex( alignment); if (prot) { + auto cpuProt = toCpuProtection(prot); + if (!(cpuProt & rx::mem::Protection::W)) { + rx::mem::protect(vmemRange, cpuProt | rx::mem::Protection::W); + } + + std::memset(std::bit_cast(vmemRange.beginAddress()), 0, + vmemRange.size()); + + if (!(cpuProt & rx::mem::Protection::W)) { + rx::mem::protect(vmemRange, cpuProt); + } + amdgpu::mapMemory(process->pid, vmemRange, MemoryType::WbOnion, prot, allocationInfo.deviceOffset); } @@ -1143,11 +1198,24 @@ orbis::ErrorCode orbis::vmem::protect(Process *process, rx::AddressRange range, rx::dieIf(errc != ErrorCode{}, "failed to allocate flexible memory"); - errc = pmem::map(range.beginAddress(), pmemRange, - toCpuProtection(blockProt)); + alloc.deviceOffset = pmemRange.beginAddress(); + + auto cpuProt = toCpuProtection(blockProt); + errc = pmem::map(range.beginAddress(), pmemRange, cpuProt); rx::dieIf(errc != ErrorCode{}, "failed to map flexible memory"); + if (!(cpuProt & rx::mem::Protection::W)) { + rx::mem::protect(range, cpuProt | rx::mem::Protection::W); + } + + std::memset(std::bit_cast(range.beginAddress()), 0, + range.size()); + + if (!(cpuProt & rx::mem::Protection::W)) { + rx::mem::protect(range, cpuProt); + } + amdgpu::mapMemory(process->pid, range, MemoryType::WbOnion, prot, pmemRange.beginAddress()); } else if (!blockProt && alloc.prot) { diff --git a/rpcsx/linker.cpp b/rpcsx/linker.cpp index 5fe1e667a..72ae3264e 100644 --- a/rpcsx/linker.cpp +++ b/rpcsx/linker.cpp @@ -3,7 +3,6 @@ #include "orbis/IoDevice.hpp" #include "orbis/KernelAllocator.hpp" #include "orbis/KernelObject.hpp" -#include "orbis/fmem.hpp" #include "orbis/module/Module.hpp" #include "orbis/pmem.hpp" #include "orbis/stat.hpp" @@ -12,7 +11,6 @@ #include "rx/AddressRange.hpp" #include "rx/SharedMutex.hpp" #include "rx/StrUtil.hpp" -#include "rx/debug.hpp" #include "rx/die.hpp" #include "rx/format.hpp" #include "rx/mem.hpp" @@ -519,7 +517,7 @@ static auto g_elfDevice = orbis::createGlobalObject(); static rx::Ref loadModule(ElfFile *elf, orbis::Process *process, std::string_view name) { - rx::Ref result{orbis::knew()}; + rx::Ref result{orbis::knew()}; Elf64_Ehdr header; std::memcpy(&header, elf->image.data(), sizeof(Elf64_Ehdr)); @@ -1079,10 +1077,10 @@ static rx::Ref loadModule(ElfFile *elf, orbis::Process *process, phdr.p_type == kElfProgramTypeGnuRelRo) { // map anonymous memory, copy segment data - auto [vmem, vmemErrc] = - orbis::vmem::mapFlex(process, segmentRange.size(), protFlags, - imageRange.beginAddress() + segmentBegin, - orbis::AllocationFlags::Fixed, {}, mapName); + auto [vmem, vmemErrc] = orbis::vmem::mapFlex( + process, segmentRange.size(), protFlags, + imageRange.beginAddress() + segmentBegin - baseAddress, + orbis::AllocationFlags::Fixed, {}, mapName); rx::dieIf(vmemErrc != orbis::ErrorCode{}, "elf: failed to map flexible to virtual memory {}", @@ -1093,8 +1091,8 @@ static rx::Ref loadModule(ElfFile *elf, orbis::Process *process, rx::mem::Protection::R | rx::mem::Protection::W); } - std::memcpy(imageBase + phdr.p_vaddr, elf->image.data() + phdr.p_offset, - phdr.p_filesz); + std::memcpy(imageBase + phdr.p_vaddr - baseAddress, + elf->image.data() + phdr.p_offset, phdr.p_filesz); rx::println(stderr, "{}: RW segment {:x}-{:x}, {}", result->moduleName, segmentRange.beginAddress(), segmentRange.endAddress(), @@ -1114,28 +1112,29 @@ static rx::Ref loadModule(ElfFile *elf, orbis::Process *process, std::lock_guard lock(elf->mtx); if (!elf->initialized) { auto [vmem, vmemErrc] = orbis::vmem::mapFile( - process, imageRange.beginAddress() + segmentBegin, + process, imageRange.beginAddress() + segmentBegin - baseAddress, segmentRange.size(), orbis::AllocationFlags::Fixed, protFlags | orbis::vmem::Protection::CpuWrite, {}, {}, elf, - segmentBegin, mapName); + segmentBegin - baseAddress, mapName); rx::dieIf(vmemErrc != orbis::ErrorCode{}, "elf: failed to map elf to virtual memory {}", vmemErrc); - std::memset(imageBase + phdr.p_vaddr + phdr.p_filesz, 0, - phdr.p_memsz - phdr.p_filesz); - std::memcpy(imageBase + phdr.p_vaddr, + std::memset(imageBase + phdr.p_vaddr + phdr.p_filesz - baseAddress, + 0, phdr.p_memsz - phdr.p_filesz); + std::memcpy(imageBase + phdr.p_vaddr - baseAddress, elf->image.data() + phdr.p_offset, phdr.p_filesz); elf->initialized = true; } } auto [vmem, vmemErrc] = orbis::vmem::mapFile( - process, imageRange.beginAddress() + segmentBegin, + process, imageRange.beginAddress() + segmentBegin - baseAddress, segmentRange.size(), orbis::AllocationFlags::Fixed, protFlags, orbis::vmem::BlockFlags::FlexibleMemory | orbis::vmem::BlockFlags::Commited, - orbis::vmem::BlockFlagsEx::Shared, elf, segmentBegin, mapName); + orbis::vmem::BlockFlagsEx::Shared, elf, segmentBegin - baseAddress, + mapName); rx::dieIf(vmemErrc != orbis::ErrorCode{}, "elf: failed to map elf to virtual memory {}", (int)vmemErrc); @@ -1153,7 +1152,7 @@ static rx::Ref loadModule(ElfFile *elf, orbis::Process *process, rx::println(stderr, "elf: corrupted, segment {} overriding. {}", segmentIndex, name); } else { - segment.addr = imageBase + segmentBegin; + segment.addr = imageBase + segmentBegin - baseAddress; segment.size = phdr.p_memsz; segment.prot = protFlags.toUnderlying(); result->segmentCount = std::max(segmentIndex + 1, result->segmentCount); diff --git a/rpcsx/main.cpp b/rpcsx/main.cpp index dd44f590b..63d60e368 100644 --- a/rpcsx/main.cpp +++ b/rpcsx/main.cpp @@ -1212,14 +1212,89 @@ int main(int argc, const char *argv[]) { }, }; + orbis::BudgetInfo systemBudgetInfo[]{ + { + .resourceId = orbis::BudgetResource::Dmem, + .flags = 0, + .item = + { + .total = 0x1'8000'0000, + }, + }, + { + .resourceId = orbis::BudgetResource::Vmem, + .flags = 0, + .item = + { + .total = 2ul * 1024 * 1024 * 1024, + }, + }, + { + .resourceId = orbis::BudgetResource::Fmem, + .flags = 0, + .item = + { + .total = 2ul * 1024 * 1024 * 1024, + }, + }, + { + .resourceId = orbis::BudgetResource::CpuSet, + .flags = 0, + .item = + { + .total = 8, + }, + }, + { + .resourceId = orbis::BudgetResource::File, + .flags = 0, + .item = + { + .total = 4096, + }, + }, + { + .resourceId = orbis::BudgetResource::Socket, + .flags = 0, + .item = + { + .total = 4096, + }, + }, + { + .resourceId = orbis::BudgetResource::Equeue, + .flags = 0, + .item = + { + .total = 4096, + }, + }, + { + .resourceId = orbis::BudgetResource::Pipe, + .flags = 0, + .item = + { + .total = 4096, + }, + }, + { + .resourceId = orbis::BudgetResource::Device, + .flags = 0, + .item = + { + .total = 4096, + }, + }, + }; + auto bigAppBudget = orbis::g_context->createProcessTypeBudget( orbis::Budget::ProcessType::BigApp, "big app budget", bigAppBudgetInfo); // FIXME: define following budgets orbis::g_context->createProcessTypeBudget( orbis::Budget::ProcessType::MiniApp, "mini-app budget", bigAppBudgetInfo); - orbis::g_context->createProcessTypeBudget(orbis::Budget::ProcessType::System, - "system budget", bigAppBudgetInfo); + auto systemBudget = orbis::g_context->createProcessTypeBudget( + orbis::Budget::ProcessType::System, "system budget", systemBudgetInfo); orbis::g_context->createProcessTypeBudget( orbis::Budget::ProcessType::NonGameMiniApp, "non-game mini-app budget", bigAppBudgetInfo); @@ -1251,6 +1326,7 @@ int main(int argc, const char *argv[]) { -1ul, }}; initProcess->budgetProcessType = orbis::Budget::ProcessType::System; + initProcess->budgetId = orbis::g_context->budgets.insert(systemBudget); initProcess->isInSandbox = false; } else { initProcess->authInfo = { diff --git a/rpcsx/ops.cpp b/rpcsx/ops.cpp index 79b88aac8..5d44aad68 100644 --- a/rpcsx/ops.cpp +++ b/rpcsx/ops.cpp @@ -647,6 +647,9 @@ SysResult fork(Thread *thread, slong flags) { process->authInfo = thread->tproc->authInfo; process->sdkVersion = thread->tproc->sdkVersion; process->type = thread->tproc->type; + process->budgetProcessType = thread->tproc->budgetProcessType; + process->budgetId = thread->tproc->budgetId; + for (auto [id, mod] : thread->tproc->modulesMap) { if (!process->modulesMap.insert(id, mod)) { std::abort();