diff --git a/rpcsx-os/CMakeLists.txt b/rpcsx-os/CMakeLists.txt index 25c7c0301..e7ce4b45c 100644 --- a/rpcsx-os/CMakeLists.txt +++ b/rpcsx-os/CMakeLists.txt @@ -35,7 +35,7 @@ add_executable(rpcsx-os ) target_include_directories(rpcsx-os PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(rpcsx-os PUBLIC orbis::kernel amdgpu::bridge libcrypto unwind unwind-x86_64 xbyak) +target_link_libraries(rpcsx-os PUBLIC orbis::kernel amdgpu::bridge rx libcrypto unwind unwind-x86_64 xbyak) target_link_options(rpcsx-os PUBLIC "LINKER:-Ttext-segment,0x0000010000000000") target_compile_options(rpcsx-os PRIVATE "-march=native") diff --git a/rpcsx-os/iodev/blockpool.cpp b/rpcsx-os/iodev/blockpool.cpp index 80b5067e9..a620204c7 100644 --- a/rpcsx-os/iodev/blockpool.cpp +++ b/rpcsx-os/iodev/blockpool.cpp @@ -20,9 +20,6 @@ static orbis::ErrorCode blockpool_ioctl(orbis::File *file, switch (request) { case 0xc020a801: { - auto dmem = static_cast(orbis::g_context.dmemDevice.get()); - std::lock_guard lock(dmem->mtx); - struct Args { std::uint64_t len; std::uint64_t searchStart; @@ -33,20 +30,22 @@ static orbis::ErrorCode blockpool_ioctl(orbis::File *file, ORBIS_LOG_TODO("blockpool expand", args->len, args->searchStart, args->searchEnd, args->flags); - std::uint64_t start = args->searchStart; - std::uint64_t len = std::min(args->searchEnd - start, args->len); - if (dmem->nextOffset > args->searchEnd) { - ORBIS_LOG_TODO("blockpool out of allocation", args->len, - args->searchStart, args->searchEnd, args->flags); - return orbis::ErrorCode::INVAL; - } + // auto dmem = static_cast(orbis::g_context.dmemDevice.get()); + // std::lock_guard lock(dmem->mtx); + // std::uint64_t start = args->searchStart; + // std::uint64_t len = std::min(args->searchEnd - start, args->len); + // if (dmem->nextOffset > args->searchEnd) { + // ORBIS_LOG_TODO("blockpool out of allocation", args->len, + // args->searchStart, args->searchEnd, args->flags); + // return orbis::ErrorCode::INVAL; + // } - start = std::max(dmem->nextOffset, start); - auto end = std::min(start + len, args->searchEnd); - dmem->nextOffset = end; - args->searchStart = start; + // start = std::max(dmem->nextOffset, start); + // auto end = std::min(start + len, args->searchEnd); + // dmem->nextOffset = end; + // args->searchStart = start; - blockPool->len += end - start; + // blockPool->len += end - start; return {}; } } diff --git a/rpcsx-os/iodev/dmem.cpp b/rpcsx-os/iodev/dmem.cpp index e51dc2f85..1716a9b0e 100644 --- a/rpcsx-os/iodev/dmem.cpp +++ b/rpcsx-os/iodev/dmem.cpp @@ -17,19 +17,17 @@ struct AllocateDirectMemoryArgs { std::uint32_t memoryType; }; -static constexpr auto dmemSize = 8ul * 1024 * 1024 * 1024; +static constexpr auto dmemSize = 8ull * 1024 * 1024 * 1024; // static const std::uint64_t nextOffset = 0; // static const std::uint64_t memBeginAddress = 0xfe0000000; orbis::ErrorCode DmemDevice::mmap(void **address, std::uint64_t len, - std::int32_t memoryType, std::int32_t prot, - std::int32_t flags, + std::int32_t prot, std::int32_t flags, std::int64_t directMemoryStart) { + auto result = + rx::vm::map(*address, len, prot, flags, 0, this, directMemoryStart); - auto result = rx::vm::map(*address, len, prot, flags); - - ORBIS_LOG_WARNING("dmem mmap", index, directMemoryStart, prot, flags, - memoryType, result); + ORBIS_LOG_WARNING("dmem mmap", index, directMemoryStart, prot, flags, result); if (result == (void *)-1) { return orbis::ErrorCode::NOMEM; // TODO } @@ -45,8 +43,8 @@ static orbis::ErrorCode dmem_ioctl(orbis::File *file, std::uint64_t request, std::lock_guard lock(device->mtx); switch (request) { case 0x4008800a: // get size - ORBIS_LOG_ERROR("dmem getTotalSize", device->index, argp); - *(std::uint64_t *)argp = dmemSize; + ORBIS_LOG_WARNING("dmem getTotalSize", device->index, argp); + *(std::uint64_t *)argp = device->dmemTotalSize; return {}; case 0xc0208016: { // get available size @@ -59,32 +57,21 @@ static orbis::ErrorCode dmem_ioctl(orbis::File *file, std::uint64_t request, auto args = reinterpret_cast(argp); - ORBIS_LOG_ERROR("dmem getAvaiableSize", device->index, argp, dmemSize, - device->nextOffset, dmemSize - device->nextOffset); - args->searchStart = device->nextOffset; - args->size = dmemSize - device->nextOffset; + return device->queryMaxFreeChunkSize(&args->searchStart, args->searchEnd, args->alignment, &args->size); + + ORBIS_LOG_WARNING("dmem getAvailableSize", device->index, argp, dmemSize); + // args->searchStart = device->nextOffset; + // args->size = dmemSize - device->nextOffset; + return {}; } + case 0xc0288011: case 0xc0288001: { // sceKernelAllocateDirectMemory auto args = reinterpret_cast(argp); - auto alignedOffset = - (device->nextOffset + args->alignment - 1) & ~(args->alignment - 1); - ORBIS_LOG_ERROR("dmem allocateDirectMemory", device->index, - args->searchStart, args->searchEnd, args->len, - args->alignment, args->memoryType, alignedOffset); - - if (alignedOffset + args->len > dmemSize) { - ORBIS_LOG_ERROR("dmem allocateDirectMemory: out of memory", alignedOffset, - args->len, alignedOffset + args->len); - - return orbis::ErrorCode::NOMEM; - } - - args->searchStart = alignedOffset; - device->nextOffset = alignedOffset + args->len; - return {}; + return device->allocate(&args->searchStart, args->searchEnd, args->len, + args->alignment, args->memoryType); } case 0x80108002: { // sceKernelReleaseDirectMemory @@ -95,29 +82,11 @@ static orbis::ErrorCode dmem_ioctl(orbis::File *file, std::uint64_t request, auto args = reinterpret_cast(argp); - ORBIS_LOG_TODO("dmem releaseDirectMemory", device->index, args->address, - args->size); - // std::fflush(stdout); - //__builtin_trap(); - return {}; - } + ORBIS_LOG_WARNING("dmem releaseDirectMemory", device->index, args->address, + args->size); - case 0xc0288011: { - auto args = reinterpret_cast(argp); - // TODO - auto alignedOffset = - (device->nextOffset + args->alignment - 1) & ~(args->alignment - 1); - - ORBIS_LOG_ERROR("dmem allocateMainDirectMemory", device->index, - args->searchStart, args->searchEnd, args->len, - args->alignment, args->memoryType, alignedOffset); - - if (alignedOffset + args->len > dmemSize) { - return orbis::ErrorCode::NOMEM; - } - - args->searchStart = alignedOffset; - device->nextOffset = alignedOffset + args->len; + device->allocations.map(args->address, args->address + args->size, + {.memoryType = 0}); return {}; } } @@ -132,18 +101,7 @@ static orbis::ErrorCode dmem_mmap(orbis::File *file, void **address, std::int32_t flags, std::int64_t offset, orbis::Thread *thread) { auto device = static_cast(file->device.get()); - auto target = device->memBeginAddress + offset; - ORBIS_LOG_WARNING("dmem mmap", device->index, offset, target); - - auto result = - rx::vm::map(reinterpret_cast(target), size, prot, flags); - - if (result == (void *)-1) { - return orbis::ErrorCode::INVAL; // TODO - } - - *address = result; - return {}; + return device->mmap(address, size, prot, flags, offset); } static const orbis::FileOps ops = { @@ -151,6 +109,113 @@ static const orbis::FileOps ops = { .mmap = dmem_mmap, }; +orbis::ErrorCode DmemDevice::allocate(std::uint64_t *start, + std::uint64_t searchEnd, + std::uint64_t len, + std::uint64_t alignment, + std::uint32_t memoryType) { + std::size_t offset = *start; + while (offset < searchEnd) { + offset += alignment - 1; + offset &= ~(alignment - 1); + + if (offset + len > dmemTotalSize) { + ORBIS_LOG_ERROR("dmem: failed to allocate direct memory: out of memory", + *start, searchEnd, len, alignment, memoryType, offset); + return orbis::ErrorCode::AGAIN; + } + + auto it = allocations.lowerBound(offset); + + if (it != allocations.end()) { + auto allocation = *it; + if (allocation.payload.memoryType == 0) { + if (offset < allocation.beginAddress) { + offset = allocation.beginAddress + alignment - 1; + offset &= ~(alignment - 1); + } + + if (offset + len >= allocation.endAddress) { + offset = allocation.endAddress; + continue; + } + } else { + if (offset + len > allocation.beginAddress) { + offset = allocation.endAddress; + continue; + } + } + } + + allocations.map(offset, offset + len, + { + .memoryType = memoryType, + }); + ORBIS_LOG_WARNING("dmem: allocated direct memory", *start, searchEnd, len, + alignment, memoryType, offset); + *start = offset; + return {}; + } + + ORBIS_LOG_ERROR("dmem: failed to allocate direct memory", *start, searchEnd, + len, alignment, memoryType, offset); + return orbis::ErrorCode::AGAIN; +} + +orbis::ErrorCode DmemDevice::queryMaxFreeChunkSize(std::uint64_t *start, + std::uint64_t searchEnd, + std::uint64_t alignment, + std::uint64_t *size) { + std::size_t offset = *start; + std::size_t resultSize = 0; + std::size_t resultOffset = 0; + while (offset < searchEnd) { + offset += alignment - 1; + offset &= ~(alignment - 1); + + if (offset >= dmemTotalSize) { + break; + } + + auto it = allocations.lowerBound(offset); + + if (it == allocations.end()) { + if (resultSize < dmemTotalSize - offset) { + resultSize = dmemTotalSize - offset; + resultOffset = offset; + } + + break; + } + + auto allocation = *it; + if (allocation.payload.memoryType == 0) { + if (offset < allocation.beginAddress) { + offset = allocation.beginAddress + alignment - 1; + offset &= ~(alignment - 1); + } + + if (allocation.endAddress > offset && + resultSize < allocation.endAddress - offset) { + resultSize = allocation.endAddress - offset; + resultOffset = offset; + } + } else if (offset > allocation.beginAddress && + resultSize < offset - allocation.beginAddress) { + resultSize = offset - allocation.beginAddress; + resultOffset = offset; + } + + offset = allocation.endAddress; + } + + *start = resultOffset; + *size = resultSize; + + ORBIS_LOG_WARNING("dmem queryMaxFreeChunkSize", resultOffset, resultSize); + return{}; +} + orbis::ErrorCode DmemDevice::open(orbis::Ref *file, const char *path, std::uint32_t flags, std::uint32_t mode, orbis::Thread *thread) { @@ -164,7 +229,6 @@ orbis::ErrorCode DmemDevice::open(orbis::Ref *file, IoDevice *createDmemCharacterDevice(int index) { auto *newDevice = orbis::knew(); newDevice->index = index; - newDevice->nextOffset = 0; - newDevice->memBeginAddress = 0xf'e000'0000 + dmemSize * index; + newDevice->dmemTotalSize = dmemSize; return newDevice; } diff --git a/rpcsx-os/iodev/dmem.hpp b/rpcsx-os/iodev/dmem.hpp index a129573b5..bff4efd05 100644 --- a/rpcsx-os/iodev/dmem.hpp +++ b/rpcsx-os/iodev/dmem.hpp @@ -6,19 +6,38 @@ #include "orbis/utils/Rc.hpp" #include "orbis/utils/SharedMutex.hpp" #include +#include #include struct DmemDevice : public IoDevice { orbis::shared_mutex mtx; int index; - std::uint64_t nextOffset; - std::uint64_t memBeginAddress; + std::size_t dmemTotalSize; + + struct AllocationInfo { + std::uint32_t memoryType; + + bool operator==(const AllocationInfo &) const = default; + auto operator<=>(const AllocationInfo &) const = default; + }; + + rx::MemoryTableWithPayload allocations; + + orbis::ErrorCode allocate(std::uint64_t *start, std::uint64_t searchEnd, + std::uint64_t len, std::uint64_t alignment, + std::uint32_t memoryType); + + orbis::ErrorCode queryMaxFreeChunkSize(std::uint64_t *start, + std::uint64_t searchEnd, + std::uint64_t alignment, + std::uint64_t *size); + + orbis::ErrorCode release(std::uint64_t start, std::uint64_t size); orbis::ErrorCode open(orbis::Ref *file, const char *path, std::uint32_t flags, std::uint32_t mode, orbis::Thread *thread) override; - orbis::ErrorCode mmap(void **address, std::uint64_t len, - std::int32_t memoryType, std::int32_t prot, + orbis::ErrorCode mmap(void **address, std::uint64_t len, std::int32_t prot, std::int32_t flags, std::int64_t directMemoryStart); }; diff --git a/rpcsx-os/ops.cpp b/rpcsx-os/ops.cpp index fdd70959a..77b60aa00 100644 --- a/rpcsx-os/ops.cpp +++ b/rpcsx-os/ops.cpp @@ -183,7 +183,7 @@ orbis::SysResult dmem_mmap(orbis::Thread *thread, orbis::caddr_t addr, auto dmem = static_cast(orbis::g_context.dmemDevice.get()); void *address = addr; auto result = - dmem->mmap(&address, len, memoryType, prot, flags, directMemoryStart); + dmem->mmap(&address, len, prot, flags, directMemoryStart); if (result != ErrorCode{}) { return result; } diff --git a/rpcsx-os/vm.cpp b/rpcsx-os/vm.cpp index 08be460fa..41389b9e0 100644 --- a/rpcsx-os/vm.cpp +++ b/rpcsx-os/vm.cpp @@ -1,6 +1,10 @@ #include "vm.hpp" #include "align.hpp" #include "bridge.hpp" +#include "io-device.hpp" +#include "iodev/dmem.hpp" +#include "orbis/utils/Logs.hpp" +#include "orbis/utils/Rc.hpp" #include #include #include @@ -12,6 +16,8 @@ #include #include +#include + namespace utils { namespace { void *map(void *address, std::size_t size, int prot, int flags, int fd = -1, @@ -604,8 +610,16 @@ struct Block { static Block gBlocks[kBlockCount]; -static std::map> - gVirtualAllocations; +struct MapInfo { + orbis::Ref device; + std::uint64_t offset; + std::uint32_t flags; + char name[32]; + + bool operator==(const MapInfo &) const = default; +}; + +static rx::MemoryTableWithPayload gMapInfo; static void reserve(std::uint64_t startAddress, std::uint64_t endAddress) { auto blockIndex = startAddress >> kBlockShift; @@ -660,23 +674,9 @@ constexpr auto kFlexibleMemorySize = 448ull * 1024 * 1024; constexpr auto kMainDirectMemorySize = kPhysicalMemorySize - kFlexibleMemorySize; -/* -std::uint64_t allocate(std::uint64_t phyAddress, std::uint64_t size, - std::uint64_t align, std::int32_t memType, - std::uint32_t blockFlags) { - // TODO - return 0; -} - -bool setMemoryRangeName(std::uint64_t phyAddress, std::uint64_t size, - const char *name) { - // TODO - return false; -} -*/ - void *rx::vm::map(void *addr, std::uint64_t len, std::int32_t prot, - std::int32_t flags, std::int32_t internalFlags) { + std::int32_t flags, std::int32_t internalFlags, + IoDevice *device, std::uint64_t offset) { std::fprintf(stderr, "rx::vm::map(addr = %p, len = %" PRIu64 ", prot = %s, flags = %s)\n", @@ -821,14 +821,17 @@ void *rx::vm::map(void *addr, std::uint64_t len, std::int32_t prot, std::fprintf(stderr, " unhandled flags 0x%" PRIx32 "\n", flags); } - auto &allocInfo = gVirtualAllocations[address]; - allocInfo.start = address; - allocInfo.end = address + len; - // allocInfo.offset = offset; // TODO - allocInfo.protection = prot; - allocInfo.memoryType = 3; // TODO - allocInfo.flags = kBlockFlagDirectMemory; // TODO - allocInfo.name[0] = '\0'; // TODO + { + MapInfo info; + if (auto it = gMapInfo.queryArea(address); it != gMapInfo.end()) { + info = (*it).payload; + } + info.device = device; + info.flags = flags; + info.offset = offset; + + gMapInfo.map(address, address + len, info); + } if (internalFlags & kMapInternalReserveOnly) { return reinterpret_cast(address); @@ -988,26 +991,72 @@ bool rx::vm::virtualQuery(const void *addr, std::int32_t flags, std::lock_guard lock(g_mtx); auto address = reinterpret_cast(addr); - auto it = gVirtualAllocations.lower_bound(address); + auto it = gMapInfo.lowerBound(address); - if (it == gVirtualAllocations.end()) { + if (it == gMapInfo.end()) { return false; } + auto queryInfo = *it; + if ((flags & 1) == 0) { - if (it->second.end <= address) { + if (queryInfo.endAddress <= address) { return false; } } else { - if (it->second.start > address || it->second.end <= address) { + if (queryInfo.beginAddress > address || queryInfo.endAddress <= address) { return false; } } - *info = it->second; + if (queryInfo.payload.device == nullptr) { + return false; + } + + std::int32_t memoryType = 0; + std::uint32_t blockFlags = 0; + if (auto dmem = dynamic_cast(queryInfo.payload.device.get())) { + auto dmemIt = dmem->allocations.queryArea(queryInfo.payload.offset); + if (dmemIt == dmem->allocations.end()) { + return false; + } + auto alloc = *dmemIt; + memoryType = alloc.payload.memoryType; + blockFlags = kBlockFlagDirectMemory; + } + // TODO + + std::int32_t prot = getPageProtectionImpl(queryInfo.beginAddress); + + *info = { + .start = queryInfo.beginAddress, + .end = queryInfo.endAddress, + .protection = prot, + .memoryType = memoryType, + .flags = blockFlags, + }; + + ORBIS_LOG_ERROR("virtualQuery", addr, flags, info->start, info->end, info->protection, + info->memoryType, info->flags); + + std::memcpy(info->name, queryInfo.payload.name, sizeof(info->name)); return true; } +void rx::vm::setName(std::uint64_t start, std::uint64_t size, + const char *name) { + std::lock_guard lock(g_mtx); + + MapInfo info; + if (auto it = gMapInfo.queryArea(start); it != gMapInfo.end()) { + info = (*it).payload; + } + + std::strncpy(info.name, name, sizeof(info.name)); + + gMapInfo.map(start, size, info); +} + void rx::vm::printHostStats() { FILE *maps = fopen("/proc/self/maps", "r"); diff --git a/rpcsx-os/vm.hpp b/rpcsx-os/vm.hpp index 5d7accccd..04e4461fa 100644 --- a/rpcsx-os/vm.hpp +++ b/rpcsx-os/vm.hpp @@ -1,4 +1,5 @@ #pragma once +#include "io-device.hpp" #include #include #include @@ -50,12 +51,12 @@ enum MapInternalFlags { }; struct VirtualQueryInfo { - uint64_t start; - uint64_t end; - uint64_t offset; - int32_t protection; - int32_t memoryType; - uint32_t flags; + std::uint64_t start; + std::uint64_t end; + std::uint64_t offset; + std::int32_t protection; + std::int32_t memoryType; + std::uint32_t flags; char name[32]; }; @@ -69,10 +70,12 @@ void printHostStats(); void initialize(); void deinitialize(); void *map(void *addr, std::uint64_t len, std::int32_t prot, std::int32_t flags, - std::int32_t internalFlags = 0); + std::int32_t internalFlags = 0, IoDevice *device = nullptr, + std::uint64_t offset = 0); bool unmap(void *addr, std::uint64_t size); bool protect(void *addr, std::uint64_t size, std::int32_t prot); +void setName(std::uint64_t start, std::uint64_t size, const char *name); bool virtualQuery(const void *addr, std::int32_t flags, VirtualQueryInfo *info); bool queryProtection(const void *addr, std::uint64_t *startAddress, std::uint64_t *endAddress, std::int32_t *prot); diff --git a/rx/include/rx/MemoryTable.hpp b/rx/include/rx/MemoryTable.hpp index 9c93833ac..a85383071 100644 --- a/rx/include/rx/MemoryTable.hpp +++ b/rx/include/rx/MemoryTable.hpp @@ -245,6 +245,26 @@ public: void clear() { mAreas.clear(); } + iterator lowerBound(std::uint64_t address) { + auto it = mAreas.lower_bound(address); + + if (it == mAreas.end()) { + return it; + } + + if (it->first == address) { + if (it->second.first == Kind::X) { + ++it; + } + } else { + if (it->second.first != Kind::O) { + --it; + } + } + + return it; + } + iterator queryArea(std::uint64_t address) { auto it = mAreas.lower_bound(address);