diff --git a/kernel/orbis/src/pipe.cpp b/kernel/orbis/src/pipe.cpp index 9509982fa..7e0dfdeae 100644 --- a/kernel/orbis/src/pipe.cpp +++ b/kernel/orbis/src/pipe.cpp @@ -32,8 +32,8 @@ static orbis::ErrorCode pipe_read(orbis::File *file, orbis::Uio *uio, uio->offset += size; std::memcpy(vec.base, pipe->data.data(), size); - ORBIS_LOG_ERROR(__FUNCTION__, thread->name.c_str(), thread->tid, file, - size, pipe->data.size(), uio->offset, file->nextOff); + ORBIS_LOG_ERROR(__FUNCTION__, thread->tid, file, size, + pipe->data.size(), uio->offset, file->nextOff); if (pipe->data.size() == size) { pipe->data.clear(); @@ -55,7 +55,7 @@ static orbis::ErrorCode pipe_read(orbis::File *file, orbis::Uio *uio, static orbis::ErrorCode pipe_write(orbis::File *file, orbis::Uio *uio, orbis::Thread *thread) { auto pipe = static_cast(file)->other; - ORBIS_LOG_ERROR(__FUNCTION__, thread->name.c_str(), thread->tid, file); + ORBIS_LOG_ERROR(__FUNCTION__, thread->tid, file); std::size_t cnt = 0; for (auto vec : std::span(uio->iov, uio->iovcnt)) { @@ -70,7 +70,7 @@ static orbis::ErrorCode pipe_write(orbis::File *file, orbis::Uio *uio, uio->resid -= cnt; uio->offset += cnt; - ORBIS_LOG_ERROR(__FUNCTION__, thread->name.c_str(), thread->tid, file, + ORBIS_LOG_ERROR(__FUNCTION__, thread->tid, file, uio->resid, uio->offset, file->nextOff, cnt); thread->where(); return {}; diff --git a/rpcsx/AudioOut.cpp b/rpcsx/AudioOut.cpp index 28a27d72c..b769d2053 100644 --- a/rpcsx/AudioOut.cpp +++ b/rpcsx/AudioOut.cpp @@ -60,8 +60,8 @@ void AudioOut::channelEntry(AudioOutChannelInfo info) { } auto controlPtr = reinterpret_cast( - rx::mem::map(nullptr, controlStat.st_size, PROT_READ | PROT_WRITE, - MAP_SHARED, controlFd)); + ::mmap(nullptr, controlStat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, + controlFd, 0)); if (controlPtr == MAP_FAILED) { perror("mmap"); std::abort(); diff --git a/rpcsx/gpu/Device.cpp b/rpcsx/gpu/Device.cpp index ace187399..5c2f764e1 100644 --- a/rpcsx/gpu/Device.cpp +++ b/rpcsx/gpu/Device.cpp @@ -66,11 +66,11 @@ static vk::Context createVkContext(Device *device) { bool enableValidation = rx::g_config.validateGpu; for (std::size_t process = 0; process < 6; ++process) { - if (!rx::mem::reserve( - reinterpret_cast(orbis::kMinAddress + - orbis::kMaxAddress * process), - orbis::kMaxAddress - orbis::kMinAddress)) { - rx::die("failed to reserve userspace memory"); + if (auto errc = rx::mem::reserve(rx::AddressRange::fromBeginSize( + orbis::kMinAddress + orbis::kMaxAddress * process, + orbis::kMaxAddress - orbis::kMinAddress)); + errc != std::errc{}) { + rx::die("failed to reserve userspace memory: {}", (int)errc); } } @@ -623,7 +623,11 @@ void Device::unmapProcess(std::uint32_t pid) { startAddress += orbis::kMinAddress; size -= orbis::kMinAddress; - rx::mem::reserve(reinterpret_cast(startAddress), size); + if (auto errc = rx::mem::release( + rx::AddressRange::fromBeginSize(startAddress, size), 1 << 14); + errc != std::errc{}) { + rx::die("failed to release userspace memory: {}", (int)errc); + } ::close(process.vmFd); process.vmFd = -1; @@ -651,7 +655,10 @@ void Device::protectMemory(std::uint32_t pid, std::uint64_t address, if (process.vmId >= 0) { auto memory = amdgpu::RemoteMemory{process.vmId}; - rx::mem::protect(memory.getPointer(address), size, prot >> 4); + rx::mem::protect( + rx::AddressRange::fromBeginSize(memory.getVirtualAddress(address), + size), + rx::EnumBitSet::fromUnderlying(prot >> 4)); // std::println(stderr, "protect process {} memory, address {}-{}, prot // {:x}", @@ -1030,7 +1037,6 @@ void Device::mapMemory(std::uint32_t pid, std::uint64_t address, if (mmapResult == MAP_FAILED) { perror("::mmap"); - rx::mem::printStats(); rx::die("failed to map process {} memory, address {}-{}, type {:x}, offset " "{:x}, prot {:x}", pid, memory.getPointer(address), memory.getPointer(address + size), diff --git a/rpcsx/gpu/Device.hpp b/rpcsx/gpu/Device.hpp index 86469cfe0..5a17f169b 100644 --- a/rpcsx/gpu/Device.hpp +++ b/rpcsx/gpu/Device.hpp @@ -56,10 +56,12 @@ struct ProcessInfo { struct RemoteMemory { int vmId; + std::uint64_t getVirtualAddress(std::uint64_t address) const { + return address ? static_cast(vmId) << 40 | address : 0; + } + template T *getPointer(std::uint64_t address) const { - return address ? reinterpret_cast( - static_cast(vmId) << 40 | address) - : nullptr; + return reinterpret_cast(getVirtualAddress(address)); } }; diff --git a/rpcsx/ipmi.cpp b/rpcsx/ipmi.cpp index 79887e3bf..0f981df68 100644 --- a/rpcsx/ipmi.cpp +++ b/rpcsx/ipmi.cpp @@ -668,8 +668,8 @@ void ipmi::createShellCoreObjects(orbis::Process *process) { } auto shmAddress = reinterpret_cast( - rx::mem::map(nullptr, controlStat.st_size, - PROT_READ | PROT_WRITE, MAP_SHARED, shmFd)); + ::mmap(nullptr, controlStat.st_size, PROT_READ | PROT_WRITE, + MAP_SHARED, shmFd, 0)); if (shmAddress == MAP_FAILED) { perror("mmap"); std::abort(); @@ -689,7 +689,7 @@ void ipmi::createShellCoreObjects(orbis::Process *process) { std::get<1>(orbis::g_context->dialogs.back()); ORBIS_LOG_TODO("Unmap shm after unlinking", currentDialogAddr, currentDialogSize); - rx::mem::unmap(currentDialogAddr, currentDialogSize); + ::munmap(currentDialogAddr, currentDialogSize); orbis::g_context->dialogs.pop_back(); } return 0; diff --git a/rpcsx/vm.cpp b/rpcsx/vm.cpp index 5defea907..2f6d8f28d 100644 --- a/rpcsx/vm.cpp +++ b/rpcsx/vm.cpp @@ -701,18 +701,19 @@ void vm::fork(std::uint64_t pid) { } if (prot & kMapProtCpuAll) { - auto mapping = rx::mem::map(nullptr, kPageSize, PROT_WRITE, MAP_SHARED, - gMemoryShm, address - kMinAddress); + auto mapping = ::mmap(nullptr, kPageSize, PROT_WRITE, MAP_SHARED, + gMemoryShm, address - kMinAddress); assert(mapping != MAP_FAILED); - rx::mem::protect(reinterpret_cast(address), kPageSize, PROT_READ); + rx::mem::protect(rx::AddressRange::fromBeginSize(address, kPageSize), + rx::mem::Protection::R); std::memcpy(mapping, reinterpret_cast(address), kPageSize); - rx::mem::unmap(mapping, kPageSize); - rx::mem::unmap(reinterpret_cast(address), kPageSize); + ::munmap(mapping, kPageSize); + ::munmap(reinterpret_cast(address), kPageSize); - mapping = rx::mem::map(reinterpret_cast(address), kPageSize, - prot & kMapProtCpuAll, MAP_FIXED | MAP_SHARED, - gMemoryShm, address - kMinAddress); + mapping = ::mmap(reinterpret_cast(address), kPageSize, + prot & kMapProtCpuAll, MAP_FIXED | MAP_SHARED, + gMemoryShm, address - kMinAddress); assert(mapping != MAP_FAILED); } @@ -723,8 +724,7 @@ void vm::fork(std::uint64_t pid) { void vm::reset() { std::memset(gBlocks, 0, sizeof(gBlocks)); - rx::mem::unmap(reinterpret_cast(kMinAddress), - kMaxAddress - kMinAddress); + ::munmap(reinterpret_cast(kMinAddress), kMaxAddress - kMinAddress); if (::ftruncate64(gMemoryShm, 0) < 0) { std::abort(); } @@ -733,8 +733,8 @@ void vm::reset() { } reserve(0, kMinAddress); - rx::mem::reserve(reinterpret_cast(kMinAddress), - kMaxAddress - kMinAddress); + rx::mem::reserve( + rx::AddressRange::fromBeginSize(kMinAddress, kMaxAddress - kMinAddress)); } void vm::initialize(std::uint64_t pid) { @@ -756,8 +756,7 @@ void vm::initialize(std::uint64_t pid) { reserve(0, kMinAddress); // unmapped area - rx::mem::reserve(reinterpret_cast(kMinAddress), - kMaxAddress - kMinAddress); + rx::mem::reserve(rx::AddressRange::fromBeginEnd(kMinAddress, kMaxAddress)); } void vm::deinitialize() { @@ -981,9 +980,9 @@ void *vm::map(void *addr, std::uint64_t len, std::int32_t prot, return reinterpret_cast(address); } - auto result = rx::mem::map(reinterpret_cast(address), len, - prot & kMapProtCpuAll, realFlags, gMemoryShm, - address - kMinAddress); + auto result = + ::mmap(reinterpret_cast(address), len, prot & kMapProtCpuAll, + realFlags, gMemoryShm, address - kMinAddress); if (result != MAP_FAILED && isAnon) { bool needReprotect = (prot & PROT_WRITE) == 0; @@ -1035,7 +1034,7 @@ bool vm::unmap(void *addr, std::uint64_t size) { address + size); } gMapInfo.unmap(rx::AddressRange::fromBeginSize(address, size)); - return rx::mem::unmap(addr, size); + return ::munmap(addr, size); } bool vm::protect(void *addr, std::uint64_t size, std::int32_t prot) { diff --git a/rx/include/rx/Mappable.hpp b/rx/include/rx/Mappable.hpp index 409357d7d..08d02b17d 100644 --- a/rx/include/rx/Mappable.hpp +++ b/rx/include/rx/Mappable.hpp @@ -2,21 +2,11 @@ #include "AddressRange.hpp" #include "EnumBitSet.hpp" +#include "mem.hpp" #include #include namespace rx { -enum class Protection { - R, - W, - X, - - bitset_last = X -}; - -std::errc reserveVirtualSpace(rx::AddressRange range); -std::errc releaseVirtualSpace(rx::AddressRange range, std::size_t alignment); - class Mappable { #ifdef _WIN32 using NativeHandle = void *; @@ -46,7 +36,8 @@ public: static std::pair CreateMemory(std::size_t size); static std::pair CreateSwap(std::size_t size); std::errc map(rx::AddressRange virtualRange, std::size_t offset, - rx::EnumBitSet protection, std::size_t alignment); + rx::EnumBitSet protection, + std::size_t alignment); [[nodiscard]] NativeHandle release() { return std::exchange(m_handle, kInvalidHandle); diff --git a/rx/include/rx/mem.hpp b/rx/include/rx/mem.hpp index 1e4de7476..fb4985b9b 100644 --- a/rx/include/rx/mem.hpp +++ b/rx/include/rx/mem.hpp @@ -1,14 +1,31 @@ #pragma once +#include "AddressRange.hpp" +#include "EnumBitSet.hpp" #include +#include +#include namespace rx::mem { +enum class Protection { + R, + W, + X, + + bitset_last = X +}; + +struct VirtualQueryEntry : rx::AddressRange { + rx::EnumBitSet flags{}; + + VirtualQueryEntry() = default; + VirtualQueryEntry(rx::AddressRange range, rx::EnumBitSet prot) + : AddressRange(range), flags(prot) {} +}; + extern const std::size_t pageSize; -void *map(void *address, std::size_t size, int prot, int flags, int fd = -1, - std::ptrdiff_t offset = 0); -void *reserve(std::size_t size); -bool reserve(void *address, std::size_t size); -bool protect(void *address, std::size_t size, int prot); -bool unmap(void *address, std::size_t size); -void printStats(); +std::errc reserve(rx::AddressRange range); +std::errc release(rx::AddressRange range, std::size_t alignment); +std::errc protect(rx::AddressRange range, rx::EnumBitSet prot); +std::vector query(rx::AddressRange range); } // namespace rx::mem diff --git a/rx/src/Mappable.cpp b/rx/src/Mappable.cpp index 8bce5954b..78a6c7856 100644 --- a/rx/src/Mappable.cpp +++ b/rx/src/Mappable.cpp @@ -1,4 +1,5 @@ #include "Mappable.hpp" +#include "mem.hpp" #include #ifndef _WIN32 @@ -13,59 +14,10 @@ #include #endif -std::errc rx::reserveVirtualSpace(rx::AddressRange range) { - auto pointer = std::bit_cast(range.beginAddress()); - -#ifdef _WIN32 - auto reservation = VirtualAlloc2(nullptr, pointer, range.size(), - MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, - PAGE_NOACCESS, nullptr, 0); - - if (reservation == nullptr) { - return std::errc::invalid_argument; - } -#else -#ifdef MAP_FIXED_NOREPLACE - static constexpr auto kMapFixedNoReplace = MAP_FIXED_NOREPLACE; -#else - static constexpr auto kMapFixedNoReplace = MAP_FIXED; +#ifdef ANDROID +#include "format-base.hpp" #endif - auto reservation = ::mmap(pointer, range.size(), PROT_NONE, - MAP_ANON | kMapFixedNoReplace | MAP_PRIVATE, -1, 0); - - if (reservation == MAP_FAILED) { - return std::errc{errno}; - } -#endif - return {}; -} - -std::errc rx::releaseVirtualSpace(rx::AddressRange range, - [[maybe_unused]] std::size_t alignment) { -#ifdef _WIN32 - // simple and stupid implementation - for (std::uintptr_t address = range.beginAddress(); - address < range.endAddress(); address += alignment) { - auto pointer = std::bit_cast(address); - if (!UnmapViewOfFileEx(pointer, MEM_PRESERVE_PLACEHOLDER)) { - VirtualFree(pointer, alignment, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); - } - } -#else - auto pointer = std::bit_cast(range.beginAddress()); - - auto reservation = ::mmap(pointer, range.size(), PROT_NONE, - MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); - - if (!reservation || reservation != pointer) { - return std::errc{errno}; - } -#endif - - return {}; -} - std::pair rx::Mappable::CreateMemory(std::size_t size) { rx::Mappable result; @@ -80,12 +32,22 @@ rx::Mappable::CreateMemory(std::size_t size) { } result.m_handle = handle; +#else +#ifdef ANDROID + auto name = rx::format("/{}-{:x}", (void *)&result, size); + auto fd = ::shm_open(name.c_str(), O_CREAT | O_TRUNC, 0666); #else auto fd = ::memfd_create("", 0); +#endif + if (fd < 0) { return {{}, std::errc{errno}}; } +#ifdef ANDROID + ::shm_unlink(name.c_str()); +#endif + result.m_handle = fd; if (::ftruncate(fd, size) < 0) { @@ -119,7 +81,7 @@ std::pair rx::Mappable::CreateSwap(std::size_t size) { } std::errc rx::Mappable::map(rx::AddressRange virtualRange, std::size_t offset, - rx::EnumBitSet protection, + rx::EnumBitSet protection, [[maybe_unused]] std::size_t alignment) { #ifdef _WIN32 static const DWORD protTable[] = { @@ -133,11 +95,11 @@ std::errc rx::Mappable::map(rx::AddressRange virtualRange, std::size_t offset, PAGE_EXECUTE_READWRITE, // XRW }; - auto prot = - protTable[(protection & (Protection::R | Protection::W | Protection::X)) - .toUnderlying()]; + auto prot = protTable[(protection & (mem::Protection::R | mem::Protection::W | + mem::Protection::X)) + .toUnderlying()]; - releaseVirtualSpace(virtualRange, alignment); + mem::release(virtualRange, alignment); for (std::uintptr_t address = virtualRange.beginAddress(); address < virtualRange.endAddress(); @@ -152,18 +114,16 @@ std::errc rx::Mappable::map(rx::AddressRange virtualRange, std::size_t offset, return std::errc::invalid_argument; } } - - return {}; #else int prot = 0; - if (protection & Protection::R) { + if (protection & mem::Protection::R) { prot |= PROT_READ; } - if (protection & Protection::W) { + if (protection & mem::Protection::W) { prot |= PROT_READ | PROT_WRITE; } - if (protection & Protection::X) { + if (protection & mem::Protection::X) { prot |= PROT_EXEC; } @@ -175,9 +135,9 @@ std::errc rx::Mappable::map(rx::AddressRange virtualRange, std::size_t offset, if (result == MAP_FAILED) { return std::errc{errno}; } +#endif return {}; -#endif } void rx::Mappable::destroy() { diff --git a/rx/src/mem.cpp b/rx/src/mem.cpp index 59baf5fd2..5c35db33e 100644 --- a/rx/src/mem.cpp +++ b/rx/src/mem.cpp @@ -1,50 +1,205 @@ #include "mem.hpp" -#include "print.hpp" - -#ifdef __linux__ +#include "die.hpp" +#ifdef _WIN32 +#define NTDDI_VERSION NTDDI_WIN10_NI +#define WIN32_LEAN_AND_MEAN +#include +#else #include #include #include - -extern const std::size_t rx::mem::pageSize = sysconf(_SC_PAGE_SIZE); - -void *rx::mem::map(void *address, std::size_t size, int prot, int flags, int fd, - std::ptrdiff_t offset) { - return ::mmap(address, size, prot, flags, fd, offset); -} - -void *rx::mem::reserve(std::size_t size) { - return map(nullptr, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS); -} - -bool rx::mem::reserve(void *address, std::size_t size) { - return map(address, size, PROT_NONE, - MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED) != MAP_FAILED; -} - -bool rx::mem::protect(void *address, std::size_t size, int prot) { - return ::mprotect(address, size, prot) == 0; -} - -bool rx::mem::unmap(void *address, std::size_t size) { - return ::munmap(address, size) == 0; -} - -void rx::mem::printStats() { - FILE *maps = fopen("/proc/self/maps", "r"); - - if (!maps) { - return; - } - - char *line = nullptr; - std::size_t size = 0; - while (getline(&line, &size, maps) > 0) { - rx::print("{}", line); - } - - free(line); - fclose(maps); -} #endif + +#ifdef _WIN32 +const std::size_t rx::mem::pageSize = [] { + SYSTEM_INFO info; + ::GetSystemInfo(&info); + return info.dwPageSize; +}(); +#else +const std::size_t rx::mem::pageSize = sysconf(_SC_PAGE_SIZE); +#endif + +std::errc rx::mem::reserve(rx::AddressRange range) { + auto pointer = std::bit_cast(range.beginAddress()); + +#ifdef _WIN32 + auto reservation = VirtualAlloc2(nullptr, pointer, range.size(), + MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, + PAGE_NOACCESS, nullptr, 0); + + if (reservation == nullptr) { + return std::errc::invalid_argument; + } +#else +#ifdef MAP_FIXED_NOREPLACE + static constexpr auto kMapFixedNoReplace = MAP_FIXED_NOREPLACE; +#else + static constexpr auto kMapFixedNoReplace = MAP_FIXED; +#endif + + auto reservation = ::mmap(pointer, range.size(), PROT_NONE, + MAP_ANON | kMapFixedNoReplace | MAP_PRIVATE, -1, 0); + + if (reservation == MAP_FAILED) { + return std::errc{errno}; + } +#endif + return {}; +} + +std::errc rx::mem::release(rx::AddressRange range, + [[maybe_unused]] std::size_t alignment) { +#ifdef _WIN32 + // simple and stupid implementation + for (std::uintptr_t address = range.beginAddress(); + address < range.endAddress(); address += alignment) { + auto pointer = std::bit_cast(address); + if (!UnmapViewOfFileEx(pointer, MEM_PRESERVE_PLACEHOLDER)) { + VirtualFree(pointer, alignment, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); + } + } +#else + auto pointer = std::bit_cast(range.beginAddress()); + + auto reservation = ::mmap(pointer, range.size(), PROT_NONE, + MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); + + if (!reservation || reservation != pointer) { + return std::errc{errno}; + } +#endif + + return {}; +} + +std::errc rx::mem::protect(rx::AddressRange range, + rx::EnumBitSet prot) { + auto pointer = std::bit_cast(range.beginAddress()); +#ifdef _WIN32 + static const DWORD protTable[] = { + PAGE_NOACCESS, // 0 + PAGE_READONLY, // R + PAGE_EXECUTE_READWRITE, // W + PAGE_EXECUTE_READWRITE, // RW + PAGE_EXECUTE, // X + PAGE_EXECUTE_READWRITE, // XR + PAGE_EXECUTE_READWRITE, // XW + PAGE_EXECUTE_READWRITE, // XRW + }; + + auto rawProt = + (prot & (mem::Protection::R | mem::Protection::W | mem::Protection::X)) + .toUnderlying(); + auto wProt = protTable[rawProt]; + + if (!VirtualProtect(pointer, range.size(), wProt, nullptr)) { + return std::errc::invalid_argument; + } +#else + if (::mprotect(pointer, range.size(), prot.toUnderlying())) { + return std::errc{errno}; + } +#endif + + return {}; +} + +std::vector rx::mem::query(rx::AddressRange range) { + std::vector result; + +#ifdef _WIN32 + std::uintptr_t address = range.beginAddress(); + while (address < range.endAddress()) { + MEMORY_BASIC_INFORMATION info; + if (!VirtualQuery((void *)address, &info, sizeof(info))) { + rx::die("VirtualQuery: failed, address = {:x}", address); + } + + auto region = rx::AddressRange::fromBeginSize( + (std::uintptr_t)info.BaseAddress, info.RegionSize) + .intersection(range); + + address = region.endAddress(); + + if (info.State == MEM_FREE || !region.isValid() || + !range.contains(region)) { + continue; + } + + rx::EnumBitSet flags = {}; + + switch (info.AllocationProtect & 0xff) { + case PAGE_NOACCESS: + break; + case PAGE_READONLY: + flags = Protection::R; + break; + case PAGE_READWRITE: + case PAGE_WRITECOPY: + flags = Protection::R | Protection::W; + break; + case PAGE_EXECUTE: + flags = Protection::X; + break; + case PAGE_EXECUTE_READ: + flags = Protection::X | Protection::R; + break; + + case PAGE_EXECUTE_READWRITE: + case PAGE_EXECUTE_WRITECOPY: + flags = Protection::X | Protection::W | Protection::R; + break; + } + + result.emplace_back(region, flags); + } +#elif defined(__linux) + char buf[1024]; + auto maps = std::fopen("/proc/self/maps", "r"); + + while (std::fgets(buf, sizeof(buf), maps)) { + std::uint64_t beginAddress; + std::uint64_t endAddress; + char flagChars[5]; + + std::sscanf(buf, "%lx-%lx %4s", &beginAddress, &endAddress, flagChars); + + auto region = rx::AddressRange::fromBeginEnd(beginAddress, endAddress) + .intersection(range); + + if (!region.isValid()) { + continue; + } + + if (region.beginAddress() >= range.beginAddress()) { + break; + } + + rx::EnumBitSet flags = {}; + + if (flagChars[0] == 'r') { + flags |= Protection::R; + } + + if (flagChars[1] == 'w') { + flags |= Protection::W; + } + + if (flagChars[2] == 'x') { + flags |= Protection::X; + } + + result.emplace_back(region, flags); + } + + std::fclose(maps); +#elif defined(__APPLE__) + // FIXME: use mach_vm_region_info? + // workaround: assume all pages are not used +#else +#error "Not implemented" +#endif + return result; +}