Compare commits

...

11 commits

Author SHA1 Message Date
DH b9d36bc0b8 orbis: dmem::release: fix end address validation
Some checks failed
Formatting check / formatting-check (push) Has been cancelled
Build RPCSX / build-linux (push) Has been cancelled
Build RPCSX / build-android (arm64-v8a, armv8-a) (push) Has been cancelled
Build RPCSX / build-android (arm64-v8a, armv8.1-a) (push) Has been cancelled
Build RPCSX / build-android (arm64-v8a, armv8.2-a) (push) Has been cancelled
Build RPCSX / build-android (arm64-v8a, armv8.4-a) (push) Has been cancelled
Build RPCSX / build-android (arm64-v8a, armv8.5-a) (push) Has been cancelled
Build RPCSX / build-android (arm64-v8a, armv9-a) (push) Has been cancelled
Build RPCSX / build-android (arm64-v8a, armv9.1-a) (push) Has been cancelled
Build RPCSX / build-android (x86_64, x86-64) (push) Has been cancelled
2025-11-30 21:06:45 +03:00
DH 4685e4fecc orbis: do not touch budget for not commited memory 2025-11-30 20:50:52 +03:00
DH c650ac482b orbis: log out of fmem/dmem budget 2025-11-30 20:38:19 +03:00
DH eeb3de8f74 fmem: increase fmem budget 2025-11-30 20:15:22 +03:00
DH 9cb9f4c21e orbis: vmem: merge direct regions
merge anonymous regions
2025-11-30 19:33:42 +03:00
DH 659ad9d045 orbis: sysctl: fix mlock 2025-11-30 19:03:39 +03:00
DH 5f290a8fb3 orbis: vmem: silence debug dump 2025-11-30 18:27:00 +03:00
DH b2dcc3f4f5 orbis: fix sys_batch_map & extend virtual memory range
tweaks for flags validation
2025-11-30 17:53:53 +03:00
DH 091349ca1e missed AddressRange change 2025-11-30 15:54:47 +03:00
DH 46f4de9bc4 revert include cleaner change 2025-11-30 15:51:46 +03:00
DH d7ad77b406 orbis: implement physical memory emulation level & utils improvement
fix blockpool & dmem implementation
modernize blockpool & dmem io devices
use budgets per allocation
add serialization support for MemoryTableWithPayload and AddressRange utils
add format support for EnumBitSet util
implemented trace formatter per syscall
increased allowed reference count for Ref
2025-11-30 15:46:37 +03:00
72 changed files with 4364 additions and 3167 deletions

View file

@ -65,6 +65,7 @@ struct LockableKernelObject : KernelObjectBase, StateT {
void lock() const { mtx.lock(); } void lock() const { mtx.lock(); }
void unlock() const { mtx.unlock(); } void unlock() const { mtx.unlock(); }
bool try_lock() const { return mtx.try_lock(); }
}; };
namespace detail { namespace detail {
@ -175,6 +176,7 @@ private:
template <typename Namespace, typename Scope, rx::Serializable T> template <typename Namespace, typename Scope, rx::Serializable T>
struct StaticObjectRef { struct StaticObjectRef {
using type = T;
std::uint32_t offset; std::uint32_t offset;
T *get(std::byte *storage) { return reinterpret_cast<T *>(storage + offset); } T *get(std::byte *storage) { return reinterpret_cast<T *>(storage + offset); }

View file

@ -75,11 +75,13 @@ concept AllocationInfo = requires(T &t, rx::AddressRange range) {
requires rx::Serializable<T>; requires rx::Serializable<T>;
}; };
template <AllocationInfo AllocationT, template <AllocationInfo AllocationT, template <typename> typename Allocator,
rx::Serializable Resource = ExternalResource> rx::Serializable Resource = ExternalResource>
struct AllocableResource : Resource { struct AllocableResource : Resource {
mutable rx::MemoryTableWithPayload<AllocationT> allocations; mutable rx::MemoryTableWithPayload<AllocationT, Allocator> allocations;
using iterator = typename rx::MemoryTableWithPayload<AllocationT>::iterator; using BaseResource = AllocableResource;
using iterator =
typename rx::MemoryTableWithPayload<AllocationT, Allocator>::iterator;
struct AllocationResult { struct AllocationResult {
iterator it; iterator it;
@ -133,6 +135,10 @@ struct AllocableResource : Resource {
} }
} }
iterator lowerBound(std::uint64_t address) {
return allocations.lowerBound(address);
}
iterator query(std::uint64_t address) { iterator query(std::uint64_t address) {
return allocations.queryArea(address); return allocations.queryArea(address);
} }
@ -164,7 +170,14 @@ struct AllocableResource : Resource {
if (flags & AllocationFlags::Fixed) { if (flags & AllocationFlags::Fixed) {
it = allocations.queryArea(addressHint); it = allocations.queryArea(addressHint);
} else { } else {
it = allocations.lowerBound(addressHint); if (addressHint == 0 && (flags & AllocationFlags::Stack)) {
it = allocations.end();
if (it != allocations.begin()) {
--it;
}
} else {
it = allocations.lowerBound(addressHint);
}
} }
if (it == allocations.end()) { if (it == allocations.end()) {
@ -245,7 +258,7 @@ struct AllocableResource : Resource {
} }
} else { } else {
auto hasEnoughSpace = [=](rx::AddressRange range) { auto hasEnoughSpace = [=](rx::AddressRange range) {
if (range.contains(addressHint)) { if (addressHint != 0 && range.contains(addressHint)) {
if (flags & AllocationFlags::Stack) { if (flags & AllocationFlags::Stack) {
range = rx::AddressRange::fromBeginEnd( range = rx::AddressRange::fromBeginEnd(
rx::alignDown(range.beginAddress(), alignment), addressHint); rx::alignDown(range.beginAddress(), alignment), addressHint);
@ -253,6 +266,11 @@ struct AllocableResource : Resource {
range = range =
rx::AddressRange::fromBeginEnd(addressHint, range.endAddress()); rx::AddressRange::fromBeginEnd(addressHint, range.endAddress());
} }
} else if (flags & AllocationFlags::Stack) {
auto alignedStackRange = rx::AddressRange::fromBeginEnd(
rx::alignDown(range.endAddress() - size, alignment),
range.endAddress());
range = range.intersection(alignedStackRange);
} }
auto alignedAddress = rx::AddressRange::fromBeginEnd( auto alignedAddress = rx::AddressRange::fromBeginEnd(
@ -261,7 +279,7 @@ struct AllocableResource : Resource {
return alignedAddress.isValid() && alignedAddress.size() >= size; return alignedAddress.isValid() && alignedAddress.size() >= size;
}; };
if (addressHint != 0 && (flags & AllocationFlags::Stack)) { if (flags & AllocationFlags::Stack) {
while (it != begin()) { while (it != begin()) {
if (!it->isAllocated() && hasEnoughSpace(it.range())) { if (!it->isAllocated() && hasEnoughSpace(it.range())) {
break; break;
@ -288,23 +306,28 @@ struct AllocableResource : Resource {
} }
// now `it` points to region that meets requirements, create fixed range // now `it` points to region that meets requirements, create fixed range
if (it.range().contains(addressHint)) { if (addressHint != 0 && it.range().contains(addressHint)) {
if (flags & AllocationFlags::Stack) { if (flags & AllocationFlags::Stack) {
fixedRange = fixedRange = rx::AddressRange::fromBeginSize(
rx::AddressRange::fromBeginSize(rx::alignDown(addressHint - size, alignment), size); rx::alignDown(addressHint - size, alignment), size);
} else { } else {
fixedRange = fixedRange =
rx::AddressRange::fromBeginEnd(addressHint, it.endAddress()); rx::AddressRange::fromBeginEnd(addressHint, it.endAddress());
} }
} else { } else {
fixedRange = rx::AddressRange::fromBeginEnd( if (flags & AllocationFlags::Stack) {
rx::alignUp(it.beginAddress(), alignment), it.endAddress()); fixedRange = rx::AddressRange::fromBeginSize(
rx::alignDown(it.endAddress() - size, alignment),
size);
} else {
fixedRange = rx::AddressRange::fromBeginSize(
rx::alignUp(it.beginAddress(), alignment), size);
}
} }
} }
if (fixedRange.size() > size) { if (fixedRange.size() > size) {
if ((flags & AllocationFlags::Stack) && if (flags & AllocationFlags::Stack) {
!it.range().contains(addressHint)) {
fixedRange = rx::AddressRange::fromBeginSize( fixedRange = rx::AddressRange::fromBeginSize(
rx::alignDown(fixedRange.endAddress() - size, alignment), size); rx::alignDown(fixedRange.endAddress() - size, alignment), size);
} else { } else {

View file

@ -1,6 +1,8 @@
set(CMAKE_POSITION_INDEPENDENT_CODE on) set(CMAKE_POSITION_INDEPENDENT_CODE on)
add_library(obj.orbis-kernel OBJECT add_library(obj.orbis-kernel OBJECT
src/blockpool.cpp
src/dmem.cpp
src/event.cpp src/event.cpp
src/evf.cpp src/evf.cpp
src/fmem.cpp src/fmem.cpp

View file

@ -9,6 +9,7 @@
#include <mutex> #include <mutex>
#include <span> #include <span>
#include <string_view> #include <string_view>
#include <utility>
namespace orbis { namespace orbis {
enum class BudgetResource : uint32_t { enum class BudgetResource : uint32_t {
@ -139,4 +140,44 @@ private:
BudgetList mList; BudgetList mList;
char mName[32]{}; char mName[32]{};
}; };
class ScopedBudgetAcquire {
Budget *mBudget = nullptr;
std::uint64_t mSize = 0;
BudgetResource mResourceId = {};
public:
ScopedBudgetAcquire() = default;
ScopedBudgetAcquire(const ScopedBudgetAcquire &) = delete;
ScopedBudgetAcquire &operator=(const ScopedBudgetAcquire &) = delete;
ScopedBudgetAcquire(Budget *budget, BudgetResource resourceId,
std::uint64_t size)
: mBudget(budget), mSize(size), mResourceId(resourceId) {
if (!budget->acquire(resourceId, size)) {
mBudget = nullptr;
}
}
ScopedBudgetAcquire(ScopedBudgetAcquire &&other) noexcept {
*this = std::move(other);
}
ScopedBudgetAcquire &operator=(ScopedBudgetAcquire &&other) noexcept {
std::swap(mBudget, other.mBudget);
std::swap(mSize, other.mSize);
std::swap(mResourceId, other.mResourceId);
return *this;
}
~ScopedBudgetAcquire() {
if (mBudget) {
mBudget->release(mResourceId, mSize);
}
}
explicit operator bool() const { return mBudget != nullptr; }
void commit() { mBudget = nullptr; }
};
} // namespace orbis } // namespace orbis

View file

@ -6,6 +6,7 @@
#include "rx/EnumBitSet.hpp" #include "rx/EnumBitSet.hpp"
#include "rx/Rc.hpp" #include "rx/Rc.hpp"
#include "rx/mem.hpp" #include "rx/mem.hpp"
#include "vmem.hpp"
#include <bit> #include <bit>
#include <type_traits> #include <type_traits>
@ -34,6 +35,8 @@ struct Process;
struct Stat; struct Stat;
struct StatFs; struct StatFs;
struct IoDevice : rx::RcBase { struct IoDevice : rx::RcBase {
rx::EnumBitSet<vmem::BlockFlags> blockFlags{};
virtual ErrorCode open(rx::Ref<File> *file, const char *path, virtual ErrorCode open(rx::Ref<File> *file, const char *path,
std::uint32_t flags, std::uint32_t mode, std::uint32_t flags, std::uint32_t mode,
Thread *thread) = 0; Thread *thread) = 0;
@ -67,14 +70,12 @@ struct IoDevice : rx::RcBase {
return ErrorCode::NOTSUP; return ErrorCode::NOTSUP;
} }
virtual ErrorCode ioctl(std::uint64_t request, orbis::ptr<void> argp, virtual ErrorCode ioctl(std::uint64_t request, ptr<void> argp,
Thread *thread); Thread *thread);
virtual ErrorCode map(rx::AddressRange range, std::int64_t offset, virtual ErrorCode map(rx::AddressRange range, std::int64_t offset,
rx::EnumBitSet<rx::mem::Protection> protection, rx::EnumBitSet<vmem::Protection> protection, File *file,
Process *process) { Process *process);
return ErrorCode::NOTSUP;
}
}; };
namespace ioctl { namespace ioctl {

View file

@ -122,8 +122,8 @@ public:
rx::Ref<EventEmitter> deviceEventEmitter; rx::Ref<EventEmitter> deviceEventEmitter;
rx::Ref<IoDevice> shmDevice; rx::Ref<IoDevice> shmDevice;
rx::Ref<IoDevice> dmemDevice; rx::Ref<File> dmem;
rx::Ref<IoDevice> blockpoolDevice; rx::Ref<File> blockpool;
rx::Ref<rx::RcBase> gpuDevice; rx::Ref<rx::RcBase> gpuDevice;
rx::Ref<IoDevice> dceDevice; rx::Ref<IoDevice> dceDevice;
rx::shared_mutex gpuDeviceMtx; rx::shared_mutex gpuDeviceMtx;

View file

@ -0,0 +1,12 @@
#pragma once
#include <cstdint>
namespace orbis {
enum class MemoryType : std::uint32_t {
Invalid = -1u,
WbOnion = 0, // write back, CPU bus
WCGarlic = 3, // combining, GPU bus
WbGarlic = 10, // write back, GPU bus
};
}

View file

@ -0,0 +1,32 @@
#pragma once
#include "dmem.hpp"
#include "error/ErrorCode.hpp"
#include "orbis-config.hpp"
#include "rx/AddressRange.hpp"
namespace orbis {
struct Process;
}
namespace orbis::blockpool {
#pragma pack(push, 1)
struct BlockStats {
sint availFlushedBlocks;
sint availCachedBlocks;
sint commitFlushedBlocks;
sint commitCachedBlocks;
};
static_assert(sizeof(BlockStats) == 16);
#pragma pack(pop)
void clear();
ErrorCode expand(rx::AddressRange dmemRange);
ErrorCode allocateControlBlock();
ErrorCode releaseControlBlock();
ErrorCode commit(Process *process, rx::AddressRange vmemRange, MemoryType type,
rx::EnumBitSet<orbis::vmem::Protection> protection);
void decommit(Process *process, rx::AddressRange vmemRange);
std::optional<MemoryType> getType(std::uint64_t address);
BlockStats stats();
} // namespace orbis::blockpool

View file

@ -0,0 +1,62 @@
#pragma once
#include "MemoryType.hpp"
#include "error/ErrorCode.hpp"
#include "rx/AddressRange.hpp"
#include "rx/EnumBitSet.hpp"
#include "vmem.hpp"
#include <utility>
namespace orbis {
struct Process;
struct IoDevice;
} // namespace orbis
namespace orbis::dmem {
static constexpr auto kPageSize = 64 * 1024;
enum class QueryFlags {
LowerBound,
Pooled,
bitset_last = Pooled,
};
struct QueryResult {
rx::AddressRange range;
MemoryType memoryType;
};
ErrorCode initialize();
ErrorCode clear(unsigned dmemIndex);
std::pair<std::uint64_t, ErrorCode>
allocate(unsigned dmemIndex, rx::AddressRange searchRange, std::uint64_t len,
MemoryType memoryType, std::uint64_t alignment = kPageSize,
bool pooled = false);
ErrorCode release(unsigned dmemIndex, rx::AddressRange range,
bool pooled = false);
std::pair<std::uint64_t, ErrorCode> reserveSystem(unsigned dmemIndex,
std::uint64_t size);
std::pair<std::uint64_t, ErrorCode> reservePooled(unsigned dmemIndex,
rx::AddressRange searchRange,
std::uint64_t size,
std::uint64_t alignment);
ErrorCode setType(unsigned dmemIndex, rx::AddressRange range, MemoryType type,
rx::EnumBitSet<QueryFlags> flags = {});
std::optional<QueryResult> query(unsigned dmemIndex, std::uint64_t dmemOffset,
rx::EnumBitSet<QueryFlags> flags = {});
std::uint64_t getSize(unsigned dmemIndex);
std::pair<rx::AddressRange, ErrorCode>
getAvailSize(unsigned dmemIndex, rx::AddressRange searchRange,
std::uint64_t alignment);
ErrorCode map(unsigned dmemIndex, rx::AddressRange range, std::uint64_t offset,
rx::EnumBitSet<vmem::Protection> protection);
std::pair<std::uint64_t, ErrorCode> getPmemOffset(unsigned dmemIndex,
std::uint64_t dmemOffset);
} // namespace orbis::dmem

View file

@ -4,6 +4,7 @@
#include "KernelAllocator.hpp" #include "KernelAllocator.hpp"
#include "error/ErrorCode.hpp" #include "error/ErrorCode.hpp"
#include "note.hpp" #include "note.hpp"
#include "rx/Mappable.hpp"
#include "rx/Rc.hpp" #include "rx/Rc.hpp"
#include "rx/SharedMutex.hpp" #include "rx/SharedMutex.hpp"
#include "stat.hpp" #include "stat.hpp"
@ -42,12 +43,6 @@ struct FileOps {
// TODO: chown // TODO: chown
// TODO: chmod // TODO: chmod
ErrorCode (*mmap)(File *file, void **address, std::uint64_t size,
std::int32_t prot, std::int32_t flags, std::int64_t offset,
Thread *thread) = nullptr;
ErrorCode (*munmap)(File *file, void **address, std::uint64_t size,
Thread *thread) = nullptr;
ErrorCode (*bind)(orbis::File *file, SocketAddress *address, ErrorCode (*bind)(orbis::File *file, SocketAddress *address,
std::size_t addressLen, Thread *thread) = nullptr; std::size_t addressLen, Thread *thread) = nullptr;
ErrorCode (*listen)(orbis::File *file, int backlog, Thread *thread) = nullptr; ErrorCode (*listen)(orbis::File *file, int backlog, Thread *thread) = nullptr;
@ -84,7 +79,7 @@ struct File : rx::RcBase {
std::uint64_t nextOff = 0; std::uint64_t nextOff = 0;
int flags = 0; int flags = 0;
int mode = 0; int mode = 0;
int hostFd = -1; rx::Mappable hostFd;
kvector<Dirent> dirEntries; kvector<Dirent> dirEntries;
bool noBlock() const { return (flags & 4) != 0; } bool noBlock() const { return (flags & 4) != 0; }

View file

@ -4,13 +4,9 @@
#include "rx/AddressRange.hpp" #include "rx/AddressRange.hpp"
#include <cstdint> #include <cstdint>
namespace orbis {
struct Process;
}
namespace orbis::fmem { namespace orbis::fmem {
ErrorCode initialize(Process *process, std::uint64_t size); ErrorCode initialize(std::uint64_t size);
void destroy(Process *process); void destroy();
std::pair<rx::AddressRange, ErrorCode> allocate(Process *process, std::pair<rx::AddressRange, ErrorCode> allocate(std::uint64_t size);
std::uint64_t size); ErrorCode deallocate(rx::AddressRange range);
ErrorCode deallocate(Process *process, rx::AddressRange range);
} // namespace orbis::fmem } // namespace orbis::fmem

View file

@ -9,30 +9,20 @@
namespace orbis { namespace orbis {
using kernel::AllocationFlags; using kernel::AllocationFlags;
struct IoDevice; struct IoDevice;
struct File;
} // namespace orbis } // namespace orbis
namespace orbis::pmem { namespace orbis::pmem {
enum class MemoryType : std::uint32_t {
Invalid = -1u,
WbOnion = 0, // write back, CPU bus
WCGarlic = 3, // combining, GPU bus
WbGarlic = 10, // write back, GPU bus
};
struct AllocatedMemory {
rx::AddressRange range;
MemoryType memoryType;
};
ErrorCode initialize(std::uint64_t size); ErrorCode initialize(std::uint64_t size);
void destroy(); void destroy();
std::pair<rx::AddressRange, ErrorCode> std::pair<rx::AddressRange, ErrorCode>
allocate(std::uint64_t addressHint, std::uint64_t size, MemoryType memoryType, allocate(std::uint64_t addressHint, std::uint64_t size,
rx::EnumBitSet<AllocationFlags> flags, std::uint64_t alignment); rx::EnumBitSet<AllocationFlags> flags, std::uint64_t alignment);
ErrorCode deallocate(rx::AddressRange range); ErrorCode deallocate(rx::AddressRange range);
std::optional<AllocatedMemory> query(std::uint64_t address); std::optional<rx::AddressRange> query(std::uint64_t address);
ErrorCode map(std::uint64_t virtualAddress, rx::AddressRange range, ErrorCode map(std::uint64_t virtualAddress, rx::AddressRange range,
rx::EnumBitSet<rx::mem::Protection> protection); rx::EnumBitSet<rx::mem::Protection> protection);
std::size_t getSize(); std::size_t getSize();
IoDevice *getDevice(); IoDevice *getDevice();
File *getFile();
} // namespace orbis::pmem } // namespace orbis::pmem

View file

@ -1,4 +1,7 @@
#include "orbis-config.hpp" #include "orbis-config.hpp"
#include "orbis/dmem.hpp"
#include "orbis/vmem.hpp"
#include "rx/EnumBitSet.hpp"
#include <orbis/Budget.hpp> #include <orbis/Budget.hpp>
#include <orbis/error.hpp> #include <orbis/error.hpp>
#include <orbis/module/ModuleHandle.hpp> #include <orbis/module/ModuleHandle.hpp>
@ -18,7 +21,9 @@ using id_t = uint32_t;
struct Thread; struct Thread;
struct AuthInfo; struct AuthInfo;
namespace vmem {
struct MemoryProtection; struct MemoryProtection;
}
struct ModuleInfo; struct ModuleInfo;
struct ModuleInfoEx; struct ModuleInfoEx;
struct KEvent; struct KEvent;
@ -105,16 +110,16 @@ SysResult sys_execve(Thread *thread, ptr<char> fname, ptr<ptr<char>> argv,
ptr<ptr<char>> envv); ptr<ptr<char>> envv);
SysResult sys_umask(Thread *thread, sint newmask); SysResult sys_umask(Thread *thread, sint newmask);
SysResult sys_chroot(Thread *thread, ptr<char> path); SysResult sys_chroot(Thread *thread, ptr<char> path);
SysResult sys_msync(Thread *thread, ptr<void> addr, size_t len, sint flags); SysResult sys_msync(Thread *thread, uintptr_t addr, size_t len, sint flags);
SysResult sys_vfork(Thread *thread); SysResult sys_vfork(Thread *thread);
SysResult sys_sbrk(Thread *thread, sint incr); SysResult sys_sbrk(Thread *thread, sint incr);
SysResult sys_sstk(Thread *thread, sint incr); SysResult sys_sstk(Thread *thread, sint incr);
SysResult sys_ovadvise(Thread *thread, sint anom); SysResult sys_ovadvise(Thread *thread, sint anom);
SysResult sys_munmap(Thread *thread, ptr<void> addr, size_t len); SysResult sys_munmap(Thread *thread, uintptr_t addr, size_t len);
SysResult sys_mprotect(Thread *thread, ptr<const void> addr, size_t len, SysResult sys_mprotect(Thread *thread, uintptr_t addr, size_t len,
sint prot); rx::EnumBitSet<vmem::Protection> prot);
SysResult sys_madvise(Thread *thread, ptr<void> addr, size_t len, sint behav); SysResult sys_madvise(Thread *thread, uintptr_t addr, size_t len, sint behav);
SysResult sys_mincore(Thread *thread, ptr<const void> addr, size_t len, SysResult sys_mincore(Thread *thread, uintptr_t addr, size_t len,
ptr<char> vec); ptr<char> vec);
SysResult sys_getgroups(Thread *thread, uint gidsetsize, ptr<gid_t> gidset); SysResult sys_getgroups(Thread *thread, uint gidsetsize, ptr<gid_t> gidset);
SysResult sys_setgroups(Thread *thread, uint gidsetsize, ptr<gid_t> gidset); SysResult sys_setgroups(Thread *thread, uint gidsetsize, ptr<gid_t> gidset);
@ -198,8 +203,10 @@ SysResult sys_getrlimit(Thread *thread, uint which, ptr<struct rlimit> rlp);
SysResult sys_setrlimit(Thread *thread, uint which, ptr<struct rlimit> rlp); SysResult sys_setrlimit(Thread *thread, uint which, ptr<struct rlimit> rlp);
SysResult sys_getdirentries(Thread *thread, sint fd, ptr<char> buf, uint count, SysResult sys_getdirentries(Thread *thread, sint fd, ptr<char> buf, uint count,
ptr<slong> basep); ptr<slong> basep);
SysResult sys_freebsd6_mmap(Thread *thread, caddr_t addr, size_t len, sint prot, SysResult sys_freebsd6_mmap(Thread *thread, uintptr_t addr, size_t len,
sint flags, sint fd, sint pad, off_t pos); rx::EnumBitSet<vmem::Protection> prot,
rx::EnumBitSet<vmem::MapFlags> flags, sint fd,
sint pad, off_t pos);
SysResult sys_freebsd6_lseek(Thread *thread, sint fd, sint pad, off_t offset, SysResult sys_freebsd6_lseek(Thread *thread, sint fd, sint pad, off_t offset,
sint whence); sint whence);
SysResult sys_freebsd6_truncate(Thread *thread, ptr<char> path, sint pad, SysResult sys_freebsd6_truncate(Thread *thread, ptr<char> path, sint pad,
@ -209,8 +216,8 @@ SysResult sys_freebsd6_ftruncate(Thread *thread, sint fd, sint pad,
SysResult sys___sysctl(Thread *thread, ptr<sint> name, uint namelen, SysResult sys___sysctl(Thread *thread, ptr<sint> name, uint namelen,
ptr<void> old, ptr<size_t> oldenp, ptr<void> new_, ptr<void> old, ptr<size_t> oldenp, ptr<void> new_,
size_t newlen); size_t newlen);
SysResult sys_mlock(Thread *thread, ptr<const void> addr, size_t len); SysResult sys_mlock(Thread *thread, uintptr_t addr, size_t len);
SysResult sys_munlock(Thread *thread, ptr<const void> addr, size_t len); SysResult sys_munlock(Thread *thread, uintptr_t addr, size_t len);
SysResult sys_undelete(Thread *thread, ptr<char> path); SysResult sys_undelete(Thread *thread, ptr<char> path);
SysResult sys_futimes(Thread *thread, sint fd, ptr<struct timeval> tptr); SysResult sys_futimes(Thread *thread, sint fd, ptr<struct timeval> tptr);
SysResult sys_getpgid(Thread *thread, pid_t pid); SysResult sys_getpgid(Thread *thread, pid_t pid);
@ -246,7 +253,7 @@ SysResult sys_ktimer_getoverrun(Thread *thread, sint timerid);
SysResult sys_nanosleep(Thread *thread, cptr<timespec> rqtp, SysResult sys_nanosleep(Thread *thread, cptr<timespec> rqtp,
ptr<timespec> rmtp); ptr<timespec> rmtp);
SysResult sys_ntp_gettime(Thread *thread, ptr<struct ntptimeval> ntvp); SysResult sys_ntp_gettime(Thread *thread, ptr<struct ntptimeval> ntvp);
SysResult sys_minherit(Thread *thread, ptr<void> addr, size_t len, SysResult sys_minherit(Thread *thread, uintptr_t addr, size_t len,
sint inherit); sint inherit);
SysResult sys_rfork(Thread *thread, sint flags); SysResult sys_rfork(Thread *thread, sint flags);
SysResult sys_openbsd_poll(Thread *thread, ptr<struct pollfd> fds, uint nfds, SysResult sys_openbsd_poll(Thread *thread, ptr<struct pollfd> fds, uint nfds,
@ -519,8 +526,9 @@ SysResult sys_pread(Thread *thread, sint fd, ptr<void> buf, size_t nbyte,
off_t offset); off_t offset);
SysResult sys_pwrite(Thread *thread, sint fd, ptr<const void> buf, size_t nbyte, SysResult sys_pwrite(Thread *thread, sint fd, ptr<const void> buf, size_t nbyte,
off_t offset); off_t offset);
SysResult sys_mmap(Thread *thread, caddr_t addr, size_t len, sint prot, SysResult sys_mmap(Thread *thread, uintptr_t addr, size_t len,
sint flags, sint fd, off_t pos); rx::EnumBitSet<vmem::Protection> prot,
rx::EnumBitSet<vmem::MapFlags> flags, sint fd, off_t pos);
SysResult sys_lseek(Thread *thread, sint fd, off_t offset, sint whence); SysResult sys_lseek(Thread *thread, sint fd, off_t offset, sint whence);
SysResult sys_truncate(Thread *thread, ptr<char> path, off_t length); SysResult sys_truncate(Thread *thread, ptr<char> path, off_t length);
SysResult sys_ftruncate(Thread *thread, sint fd, off_t length); SysResult sys_ftruncate(Thread *thread, sint fd, off_t length);
@ -621,7 +629,9 @@ SysResult sys_socketex(Thread *thread, ptr<const char> name, sint domain,
SysResult sys_socketclose(Thread *thread, sint fd); SysResult sys_socketclose(Thread *thread, sint fd);
SysResult sys_netgetiflist(Thread *thread /* TODO */); SysResult sys_netgetiflist(Thread *thread /* TODO */);
SysResult sys_kqueueex(Thread *thread, ptr<char> name, sint flags); SysResult sys_kqueueex(Thread *thread, ptr<char> name, sint flags);
SysResult sys_mtypeprotect(Thread *thread /* TODO */); SysResult sys_mtypeprotect(Thread *thread, uintptr_t addr, size_t len,
MemoryType type,
rx::EnumBitSet<vmem::Protection> prot);
SysResult sys_regmgr_call(Thread *thread, uint32_t op, uint32_t id, SysResult sys_regmgr_call(Thread *thread, uint32_t op, uint32_t id,
ptr<void> result, ptr<void> value, uint64_t len); ptr<void> result, ptr<void> value, uint64_t len);
SysResult sys_jitshm_create(Thread *thread /* TODO */); SysResult sys_jitshm_create(Thread *thread /* TODO */);
@ -644,9 +654,10 @@ SysResult sys_evf_set(Thread *thread, sint id, uint64_t value);
SysResult sys_evf_clear(Thread *thread, sint id, uint64_t value); SysResult sys_evf_clear(Thread *thread, sint id, uint64_t value);
SysResult sys_evf_cancel(Thread *thread, sint id, uint64_t value, SysResult sys_evf_cancel(Thread *thread, sint id, uint64_t value,
ptr<sint> pNumWaitThreads); ptr<sint> pNumWaitThreads);
SysResult sys_query_memory_protection(Thread *thread, ptr<void> address, SysResult sys_query_memory_protection(Thread *thread, uintptr_t address,
ptr<MemoryProtection> protection); ptr<vmem::MemoryProtection> protection);
SysResult sys_batch_map(Thread *thread, sint unk, sint flags, SysResult sys_batch_map(Thread *thread, sint unk,
rx::EnumBitSet<vmem::MapFlags> flags,
ptr<BatchMapEntry> entries, sint entriesCount, ptr<BatchMapEntry> entries, sint entriesCount,
ptr<sint> processedCount); ptr<sint> processedCount);
SysResult sys_osem_create(Thread *thread, ptr<const char[32]> name, uint attrs, SysResult sys_osem_create(Thread *thread, ptr<const char[32]> name, uint attrs,
@ -679,7 +690,7 @@ SysResult sys_budget_delete(Thread *thread, sint budget);
SysResult sys_budget_get(Thread *thread, sint id, ptr<BudgetInfo> budgetInfo, SysResult sys_budget_get(Thread *thread, sint id, ptr<BudgetInfo> budgetInfo,
ptr<sint> count); ptr<sint> count);
SysResult sys_budget_set(Thread *thread, sint budget); SysResult sys_budget_set(Thread *thread, sint budget);
SysResult sys_virtual_query(Thread *thread, ptr<void> addr, uint64_t unk, SysResult sys_virtual_query(Thread *thread, uintptr_t addr, uint64_t unk,
ptr<void> info, size_t infosz); ptr<void> info, size_t infosz);
SysResult sys_mdbg_call(Thread *thread /* TODO */); SysResult sys_mdbg_call(Thread *thread /* TODO */);
SysResult sys_obs_sblock_create(Thread *thread /* TODO */); SysResult sys_obs_sblock_create(Thread *thread /* TODO */);
@ -752,8 +763,10 @@ SysResult sys_opmc_set_hw(Thread *thread /* TODO */);
SysResult sys_opmc_get_hw(Thread *thread /* TODO */); SysResult sys_opmc_get_hw(Thread *thread /* TODO */);
SysResult sys_get_cpu_usage_all(Thread *thread, uint32_t unk, SysResult sys_get_cpu_usage_all(Thread *thread, uint32_t unk,
ptr<uint32_t> result); ptr<uint32_t> result);
SysResult sys_mmap_dmem(Thread *thread, caddr_t addr, size_t len, SysResult sys_mmap_dmem(Thread *thread, uintptr_t addr, size_t len,
sint memoryType, sint prot, sint flags, MemoryType memoryType,
rx::EnumBitSet<vmem::Protection> prot,
rx::EnumBitSet<vmem::MapFlags> flags,
off_t directMemoryStart); off_t directMemoryStart);
SysResult sys_physhm_open(Thread *thread /* TODO */); SysResult sys_physhm_open(Thread *thread /* TODO */);
SysResult sys_physhm_unlink(Thread *thread /* TODO */); SysResult sys_physhm_unlink(Thread *thread /* TODO */);
@ -785,9 +798,11 @@ SysResult sys_budget_get_ptype_of_budget(Thread *thread, sint budgetId);
SysResult sys_prepare_to_resume_process(Thread *thread /* TODO */); SysResult sys_prepare_to_resume_process(Thread *thread /* TODO */);
SysResult sys_process_terminate(Thread *thread /* TODO */); SysResult sys_process_terminate(Thread *thread /* TODO */);
SysResult sys_blockpool_open(Thread *thread); SysResult sys_blockpool_open(Thread *thread);
SysResult sys_blockpool_map(Thread *thread, caddr_t addr, size_t len, sint prot, SysResult sys_blockpool_map(Thread *thread, uintptr_t addr, size_t len,
sint flags); MemoryType type,
SysResult sys_blockpool_unmap(Thread *thread, caddr_t addr, size_t len, rx::EnumBitSet<vmem::Protection> prot,
rx::EnumBitSet<vmem::MapFlags> flags);
SysResult sys_blockpool_unmap(Thread *thread, uintptr_t addr, size_t len,
sint flags); sint flags);
SysResult sys_dynlib_get_info_for_libdbg(Thread *thread /* TODO */); SysResult sys_dynlib_get_info_for_libdbg(Thread *thread /* TODO */);
SysResult sys_blockpool_batch(Thread *thread /* TODO */); SysResult sys_blockpool_batch(Thread *thread /* TODO */);

View file

@ -102,6 +102,8 @@ struct Process final {
rx::RcIdMap<Thread, lwpid_t> threadsMap; rx::RcIdMap<Thread, lwpid_t> threadsMap;
rx::RcIdMap<orbis::File, sint> fileDescriptors; rx::RcIdMap<orbis::File, sint> fileDescriptors;
rx::AddressRange libkernelRange;
// Named objects for debugging // Named objects for debugging
rx::shared_mutex namedObjMutex; rx::shared_mutex namedObjMutex;
kmap<void *, kstring> namedObjNames; kmap<void *, kstring> namedObjNames;
@ -109,10 +111,6 @@ struct Process final {
kmap<std::int32_t, SigAction> sigActions; kmap<std::int32_t, SigAction> sigActions;
// Named memory ranges for debugging
rx::shared_mutex namedMemMutex;
kmap<NamedMemoryRange, kstring> namedMem;
// FIXME: implement process destruction // FIXME: implement process destruction
void incRef() {} void incRef() {}
void decRef() {} void decRef() {}
@ -126,6 +124,8 @@ struct Process final {
ref) { ref) {
return ref.get(storage); return ref.get(storage);
} }
Budget *getBudget() const;
}; };
pid_t allocatePid(); pid_t allocatePid();

View file

@ -10,34 +10,13 @@ struct Thread;
struct Module; struct Module;
struct timespec; struct timespec;
struct File; struct File;
namespace vmem {
struct MemoryProtection; struct MemoryProtection;
}
struct IoVec; struct IoVec;
struct UContext; struct UContext;
struct ProcessOps { struct ProcessOps {
SysResult (*mmap)(Thread *thread, caddr_t addr, size_t len, sint prot,
sint flags, sint fd, off_t pos);
SysResult (*dmem_mmap)(Thread *thread, caddr_t addr, size_t len,
sint memoryType, sint prot, sint flags,
off_t directMemoryStart);
SysResult (*munmap)(Thread *thread, ptr<void> addr, size_t len);
SysResult (*msync)(Thread *thread, ptr<void> addr, size_t len, sint flags);
SysResult (*mprotect)(Thread *thread, ptr<const void> addr, size_t len,
sint prot);
SysResult (*minherit)(Thread *thread, ptr<void> addr, size_t len,
sint inherit);
SysResult (*madvise)(Thread *thread, ptr<void> addr, size_t len, sint behav);
SysResult (*mincore)(Thread *thread, ptr<const void> addr, size_t len,
ptr<char> vec);
SysResult (*mlock)(Thread *thread, ptr<const void> addr, size_t len);
SysResult (*mlockall)(Thread *thread, sint how);
SysResult (*munlockall)(Thread *thread);
SysResult (*munlock)(Thread *thread, ptr<const void> addr, size_t len);
SysResult (*virtual_query)(Thread *thread, ptr<const void> addr, sint flags,
ptr<void> info, ulong infoSize);
SysResult (*query_memory_protection)(Thread *thread, ptr<void> address,
ptr<MemoryProtection> protection);
SysResult (*open)(Thread *thread, ptr<const char> path, sint flags, sint mode, SysResult (*open)(Thread *thread, ptr<const char> path, sint flags, sint mode,
rx::Ref<File> *file); rx::Ref<File> *file);
SysResult (*shm_open)(Thread *thread, const char *path, sint flags, sint mode, SysResult (*shm_open)(Thread *thread, const char *path, sint flags, sint mode,
@ -46,10 +25,6 @@ struct ProcessOps {
SysResult (*mkdir)(Thread *thread, ptr<const char> path, sint mode); SysResult (*mkdir)(Thread *thread, ptr<const char> path, sint mode);
SysResult (*rmdir)(Thread *thread, ptr<const char> path); SysResult (*rmdir)(Thread *thread, ptr<const char> path);
SysResult (*rename)(Thread *thread, ptr<const char> from, ptr<const char> to); SysResult (*rename)(Thread *thread, ptr<const char> from, ptr<const char> to);
SysResult (*blockpool_open)(Thread *thread, rx::Ref<File> *file);
SysResult (*blockpool_map)(Thread *thread, caddr_t addr, size_t len,
sint prot, sint flags);
SysResult (*blockpool_unmap)(Thread *thread, caddr_t addr, size_t len);
SysResult (*socket)(Thread *thread, ptr<const char> name, sint domain, SysResult (*socket)(Thread *thread, ptr<const char> name, sint domain,
sint type, sint protocol, rx::Ref<File> *file); sint type, sint protocol, rx::Ref<File> *file);
SysResult (*socketpair)(Thread *thread, sint domain, sint type, sint protocol, SysResult (*socketpair)(Thread *thread, sint domain, sint type, sint protocol,

View file

@ -32,8 +32,8 @@ struct Thread final {
uint64_t retval[2]{}; uint64_t retval[2]{};
void *context{}; void *context{};
kvector<void *> altStack; kvector<void *> altStack;
ptr<void> stackStart; uint64_t stackStart;
ptr<void> stackEnd; uint64_t stackEnd;
uint64_t fsBase{}; uint64_t fsBase{};
uint64_t gsBase{}; uint64_t gsBase{};
rx::StaticString<31> name; rx::StaticString<31> name;
@ -97,6 +97,7 @@ struct Thread final {
}; };
Thread *createThread(Process *process, std::string_view name); Thread *createThread(Process *process, std::string_view name);
uintptr_t getCallerAddress(Thread *thread);
extern thread_local Thread *g_currentThread; extern thread_local Thread *g_currentThread;

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "orbis-config.hpp" #include "orbis-config.hpp"
#include <string>
namespace orbis { namespace orbis {
struct Thread; struct Thread;
@ -9,6 +10,7 @@ using sy_call_t = SysResult(Thread *, uint64_t *);
struct sysent { struct sysent {
sint narg; sint narg;
sy_call_t *call; sy_call_t *call;
std::string (*format)(uint64_t *);
}; };
struct sysentvec { struct sysentvec {

View file

@ -15,7 +15,7 @@ struct rtprio {
struct thr_param { struct thr_param {
ptr<void(void *)> start_func; ptr<void(void *)> start_func;
ptr<void> arg; ptr<void> arg;
ptr<char> stack_base; uint64_t stack_base;
size_t stack_size; size_t stack_size;
ptr<char> tls_base; ptr<char> tls_base;
size_t tls_size; size_t tls_size;

View file

@ -47,7 +47,7 @@ struct MContext {
}; };
struct Stack { struct Stack {
ptr<void> sp; uintptr_t sp;
size_t size; size_t size;
sint flags; sint flags;
sint align; sint align;

View file

@ -1,13 +0,0 @@
#pragma once
#include "orbis-config.hpp"
namespace orbis {
struct MemoryProtection {
uint64_t startAddress;
uint64_t endAddress;
int32_t prot;
};
static_assert(sizeof(MemoryProtection) == 24);
} // namespace orbis

View file

@ -1,21 +1,22 @@
#pragma once #pragma once
#include "MemoryType.hpp"
#include "kernel/MemoryResource.hpp" #include "kernel/MemoryResource.hpp"
#include "orbis-config.hpp" #include "orbis-config.hpp"
#include "rx/AddressRange.hpp" #include "rx/AddressRange.hpp"
#include "rx/EnumBitSet.hpp" #include "rx/EnumBitSet.hpp"
#include "rx/StaticString.hpp" #include "rx/StaticString.hpp"
#include "rx/mem.hpp"
#include <string_view> #include <string_view>
namespace orbis { namespace orbis {
using kernel::AllocationFlags; using kernel::AllocationFlags;
}
namespace orbis { struct File;
struct IoDevice;
struct Process; struct Process;
} // namespace orbis
namespace vmem { namespace orbis::vmem {
static constexpr auto kPageSize = 16 * 1024; static constexpr auto kPageSize = 16 * 1024;
enum class Protection { enum class Protection {
@ -28,33 +29,85 @@ enum class Protection {
bitset_last = GpuWrite bitset_last = GpuWrite
}; };
enum class BlockFlags { enum class BlockFlags : std::uint8_t {
FlexibleMemory, FlexibleMemory,
DirectMemory, DirectMemory,
Stack, Stack,
PooledMemory, PooledMemory,
Commited, Commited,
Allocated,
bitset_last = Allocated bitset_last = Commited
}; };
enum class BlockFlagsEx : std::uint8_t {
Allocated,
Private,
Shared,
PoolControl,
Reserved,
bitset_last = Reserved
};
enum class MapFlags {
Shared = 0,
Private = 1,
Fixed = 4,
Rename = 5,
NoReserve = 6,
NoOverwrite = 7,
Void = 8,
HasSemaphore = 9,
Stack = 10,
NoSync = 11,
Anon = 12,
System = 13,
AllAvailable = 14,
NoCore = 17,
PrefaultRead = 18,
Self = 19,
NoCoalesce = 22,
bitset_last = NoCoalesce
};
inline constexpr std::uint32_t kMapFlagsAlignShift = 24;
inline constexpr std::uint32_t kMapFlagsAlignMask = 0x1f << kMapFlagsAlignShift;
inline constexpr auto kProtCpuReadWrite = inline constexpr auto kProtCpuReadWrite =
Protection::CpuRead | Protection::CpuWrite; Protection::CpuRead | Protection::CpuWrite;
inline constexpr auto kProtCpuAll = inline constexpr auto kProtCpuAll =
Protection::CpuRead | Protection::CpuWrite | Protection::CpuExec; Protection::CpuRead | Protection::CpuWrite | Protection::CpuExec;
inline constexpr auto kProtGpuAll = Protection::GpuRead | Protection::GpuWrite; inline constexpr auto kProtGpuAll = Protection::GpuRead | Protection::GpuWrite;
inline std::pair<std::uint64_t, rx::EnumBitSet<MapFlags>>
unpackMapFlags(rx::EnumBitSet<MapFlags> flags, std::uint64_t minAlignment) {
std::uint64_t alignment = minAlignment;
if (auto align =
(flags.toUnderlying() & kMapFlagsAlignMask) >> kMapFlagsAlignShift) {
alignment = std::uint64_t(1) << align;
flags = rx::EnumBitSet<vmem::MapFlags>::fromUnderlying(
flags.toUnderlying() & ~kMapFlagsAlignMask);
if (alignment < minAlignment) {
alignment = minAlignment;
}
}
return {alignment, flags};
}
#pragma pack(push, 1) #pragma pack(push, 1)
struct QueryResult { struct QueryResult {
uint64_t start; uint64_t start;
uint64_t end; uint64_t end;
uint64_t offset; uint64_t offset;
uint32_t protection; rx::EnumBitSet<Protection> protection;
uint32_t memoryType; MemoryType memoryType;
uint32_t flags; rx::EnumBitSet<BlockFlags> flags;
rx::StaticCString<32> name; rx::StaticCString<32> name;
uint32_t _padding; char _padding[7];
}; };
static_assert(sizeof(QueryResult) == 72); static_assert(sizeof(QueryResult) == 72);
@ -69,25 +122,81 @@ struct MemoryProtection {
static_assert(sizeof(MemoryProtection) == 24); static_assert(sizeof(MemoryProtection) == 24);
#pragma pack(pop) #pragma pack(pop)
inline constexpr rx::EnumBitSet<rx::mem::Protection>
toCpuProtection(rx::EnumBitSet<Protection> prot) {
rx::EnumBitSet<rx::mem::Protection> result{};
if (prot & Protection::CpuRead) {
result |= rx::mem::Protection::R;
}
if (prot & Protection::CpuWrite) {
result |= rx::mem::Protection::W;
}
if (prot & Protection::CpuExec) {
result |= rx::mem::Protection::X;
}
return result;
}
inline constexpr rx::EnumBitSet<rx::mem::Protection>
toGpuProtection(rx::EnumBitSet<Protection> prot) {
rx::EnumBitSet<rx::mem::Protection> result{};
if (prot & Protection::GpuRead) {
result |= rx::mem::Protection::R;
}
if (prot & Protection::GpuWrite) {
result |= rx::mem::Protection::W;
}
return result;
}
void initialize(Process *process, bool force = false); void initialize(Process *process, bool force = false);
void fork(Process *process, Process *parentThread); void fork(Process *process, Process *parentThread);
std::pair<rx::AddressRange, ErrorCode> std::pair<rx::AddressRange, ErrorCode>
reserve(Process *process, std::uint64_t addressHint, std::uint64_t size, reserve(Process *process, std::uint64_t addressHint, std::uint64_t size,
rx::EnumBitSet<AllocationFlags> allocFlags); rx::EnumBitSet<AllocationFlags> allocFlags,
rx::EnumBitSet<BlockFlagsEx> blockFlagsEx = {},
std::uint64_t alignment = kPageSize);
std::pair<rx::AddressRange, ErrorCode> std::pair<rx::AddressRange, ErrorCode>
map(Process *process, std::uint64_t addressHint, std::uint64_t size, mapFile(Process *process, std::uint64_t addressHint, std::uint64_t size,
rx::EnumBitSet<AllocationFlags> allocFlags, rx::EnumBitSet<AllocationFlags> allocFlags,
rx::EnumBitSet<Protection> prot = {}, rx::EnumBitSet<Protection> prot, rx::EnumBitSet<BlockFlags> blockFlags,
rx::EnumBitSet<BlockFlags> blockFlags = {}, rx::EnumBitSet<BlockFlagsEx> blockFlagsEx, File *file,
std::uint64_t alignment = kPageSize, std::string_view name = {}, std::uint64_t fileOffset, std::string_view name = {},
IoDevice *device = nullptr, std::int64_t deviceOffset = 0); std::uint64_t alignment = kPageSize,
MemoryType type = MemoryType::Invalid);
std::pair<rx::AddressRange, ErrorCode>
mapDirect(Process *process, std::uint64_t addressHint,
rx::AddressRange directRange, rx::EnumBitSet<Protection> prot,
rx::EnumBitSet<AllocationFlags> allocFlags,
std::string_view name = {}, std::uint64_t alignment = kPageSize,
MemoryType type = MemoryType::Invalid);
std::pair<rx::AddressRange, ErrorCode>
mapFlex(Process *process, std::uint64_t size, rx::EnumBitSet<Protection> prot,
std::uint64_t addressHint = 0,
rx::EnumBitSet<AllocationFlags> allocFlags = {},
rx::EnumBitSet<BlockFlags> blockFlags = {}, std::string_view name = {},
std::uint64_t alignment = kPageSize);
std::pair<rx::AddressRange, ErrorCode>
commitPooled(Process *process, rx::AddressRange addressRange, MemoryType type,
rx::EnumBitSet<Protection> prot);
ErrorCode decommitPooled(Process *process, rx::AddressRange addressRange);
ErrorCode protect(Process *process, rx::AddressRange range,
rx::EnumBitSet<Protection> prot);
ErrorCode unmap(Process *process, rx::AddressRange range); ErrorCode unmap(Process *process, rx::AddressRange range);
ErrorCode setName(Process *process, rx::AddressRange range, ErrorCode setName(Process *process, rx::AddressRange range,
std::string_view name); std::string_view name);
std::optional<QueryResult> query(Process *process, std::uint64_t address); ErrorCode setType(Process *process, rx::AddressRange range, MemoryType type);
ErrorCode setTypeAndProtect(Process *process, rx::AddressRange range,
MemoryType type, rx::EnumBitSet<Protection> prot);
std::optional<QueryResult> query(Process *process, std::uint64_t address,
bool lowerBound = false);
std::optional<MemoryProtection> queryProtection(Process *process, std::optional<MemoryProtection> queryProtection(Process *process,
std::uint64_t address); std::uint64_t address,
} // namespace vmem bool lowerBound = false);
} // namespace orbis } // namespace orbis::vmem

View file

@ -1,6 +1,9 @@
#include "IoDevice.hpp" #include "IoDevice.hpp"
#include "file.hpp"
#include "rx/Mappable.hpp"
#include "thread/Thread.hpp" #include "thread/Thread.hpp"
#include "utils/Logs.hpp" #include "utils/Logs.hpp"
#include "vmem.hpp"
static std::string iocGroupToString(unsigned iocGroup) { static std::string iocGroupToString(unsigned iocGroup) {
if (iocGroup >= 128) { if (iocGroup >= 128) {
@ -64,6 +67,23 @@ static std::string iocGroupToString(unsigned iocGroup) {
return "'?'"; return "'?'";
} }
orbis::ErrorCode
orbis::IoDevice::map(rx::AddressRange range, std::int64_t offset,
rx::EnumBitSet<vmem::Protection> protection, File *file,
Process *) {
if (!file->dirEntries.empty()) {
return orbis::ErrorCode::ISDIR;
}
if (!file->hostFd) {
return ErrorCode::NOTSUP;
}
auto errc = file->hostFd.map(range, offset, vmem::toCpuProtection(protection),
orbis::vmem::kPageSize);
return orbis::toErrorCode(errc);
}
orbis::ErrorCode orbis::IoDevice::ioctl(std::uint64_t request, orbis::ErrorCode orbis::IoDevice::ioctl(std::uint64_t request,
orbis::ptr<void> argp, Thread *thread) { orbis::ptr<void> argp, Thread *thread) {
auto group = iocGroupToString(ioctl::group(request)); auto group = iocGroupToString(ioctl::group(request));

View file

@ -8,7 +8,7 @@
#include "rx/print.hpp" #include "rx/print.hpp"
static const std::uint64_t g_allocProtWord = 0xDEADBEAFBADCAFE1; static const std::uint64_t g_allocProtWord = 0xDEADBEAFBADCAFE1;
static constexpr std::uintptr_t kHeapBaseAddress = 0x00000600'0000'0000; static constexpr std::uintptr_t kHeapBaseAddress = 0x7100'0000'0000;
static constexpr auto kHeapSize = 0x1'0000'0000; static constexpr auto kHeapSize = 0x1'0000'0000;
static constexpr int kDebugHeap = 0; static constexpr int kDebugHeap = 0;

View file

@ -0,0 +1,370 @@
#include "blockpool.hpp"
#include "KernelAllocator.hpp"
#include "KernelObject.hpp"
#include "dmem.hpp"
#include "pmem.hpp"
#include "rx/AddressRange.hpp"
#include "rx/MemoryTable.hpp"
#include "rx/die.hpp"
#include "thread/Process.hpp"
#include "vmem.hpp"
#include <algorithm>
#include <iterator>
#include <mutex>
// FIXME: remove
namespace amdgpu {
void mapMemory(std::uint32_t pid, rx::AddressRange virtualRange,
orbis::MemoryType memoryType,
rx::EnumBitSet<orbis::vmem::Protection> prot,
std::uint64_t offset);
void unmapMemory(std::uint32_t pid, rx::AddressRange virtualRange);
void protectMemory(std::uint32_t pid, rx::AddressRange virtualRange,
rx::EnumBitSet<orbis::vmem::Protection> prot);
} // namespace amdgpu
struct PooledMemoryResource {
struct CommitedBlock {
std::uint64_t pmemAddress;
orbis::MemoryType type;
bool operator==(const CommitedBlock &) const = default;
};
std::uint64_t total = 0;
std::uint64_t used = 0;
orbis::kvector<rx::AddressRange> freeBlocks;
rx::MemoryTableWithPayload<CommitedBlock, orbis::kallocator> usedBlocks;
orbis::kvector<std::uint64_t> reservedPages;
void clear() {
total = 0;
used = 0;
freeBlocks.clear();
usedBlocks.clear();
reservedPages.clear();
}
void addFreeBlock(rx::AddressRange dmemRange) {
if (freeBlocks.empty()) {
freeBlocks.push_back(dmemRange);
return;
}
auto it = std::upper_bound(
freeBlocks.begin(), freeBlocks.end(), dmemRange.beginAddress(),
[](auto lhs, auto rhs) {
if constexpr (requires { lhs.beginAddress() < rhs; }) {
return lhs.beginAddress() < rhs;
} else {
return lhs < rhs.beginAddress();
}
});
if (it != freeBlocks.end() &&
dmemRange.endAddress() == it->beginAddress()) {
*it = rx::AddressRange::fromBeginEnd(it->beginAddress(),
dmemRange.endAddress());
return;
}
if (it != freeBlocks.begin()) {
auto prev = std::prev(it);
if (prev->endAddress() == dmemRange.beginAddress()) {
*prev = rx::AddressRange::fromBeginEnd(prev->beginAddress(),
dmemRange.endAddress());
return;
}
}
freeBlocks.insert(it, dmemRange);
return;
}
void expand(rx::AddressRange dmemRange) {
addFreeBlock(dmemRange);
total += dmemRange.size();
}
orbis::ErrorCode reserve(std::size_t size) {
if (size > total) {
return orbis::ErrorCode::INVAL;
}
used += size;
return {};
}
std::pair<std::uint64_t, orbis::ErrorCode> reservePage() {
if (total - used < orbis::dmem::kPageSize) {
return {{}, orbis::ErrorCode::INVAL};
}
used += orbis::dmem::kPageSize;
auto &block = freeBlocks.back();
auto allocatedPage = block.beginAddress();
if (block.endAddress() == allocatedPage + orbis::dmem::kPageSize) {
freeBlocks.pop_back();
} else {
block = rx::AddressRange::fromBeginEnd(
allocatedPage + orbis::dmem::kPageSize, block.endAddress());
}
reservedPages.push_back(allocatedPage);
return {allocatedPage, {}};
}
std::pair<std::uint64_t, orbis::ErrorCode> releasePage() {
if (reservedPages.empty()) {
return {{}, orbis::ErrorCode::INVAL};
}
used -= orbis::dmem::kPageSize;
total -= orbis::dmem::kPageSize;
auto address = reservedPages.back();
reservedPages.pop_back();
return {address, {}};
}
void moveTo(PooledMemoryResource &other, std::size_t size) {
while (size > 0 && total > 0) {
auto &block = freeBlocks.back();
if (block.size() > size) {
total -= size;
auto moveRange =
rx::AddressRange::fromBeginSize(block.beginAddress(), size);
other.expand(moveRange);
block = rx::AddressRange::fromBeginEnd(moveRange.endAddress(),
block.endAddress());
break;
}
total -= block.size();
size -= block.size();
other.expand(block);
freeBlocks.pop_back();
}
}
void commit(orbis::Process *process, rx::AddressRange virtualRange,
orbis::MemoryType type,
rx::EnumBitSet<orbis::vmem::Protection> protection) {
while (virtualRange.isValid()) {
auto &block = freeBlocks.back();
if (block.size() >= virtualRange.size()) [[likely]] {
auto mapPhysicalRange = rx::AddressRange::fromBeginSize(
block.beginAddress(), virtualRange.size());
auto errc =
orbis::pmem::map(virtualRange.beginAddress(), mapPhysicalRange,
orbis::vmem::toCpuProtection(protection));
rx::dieIf(errc != orbis::ErrorCode{},
"blockpool: failed to map physical memory");
amdgpu::mapMemory(process->pid, virtualRange, type, protection,
block.beginAddress());
if (mapPhysicalRange.endAddress() == block.endAddress()) {
freeBlocks.pop_back();
} else {
block = rx::AddressRange::fromBeginEnd(mapPhysicalRange.endAddress(),
block.endAddress());
}
usedBlocks.map(virtualRange,
{.pmemAddress = block.beginAddress(), .type = type},
false);
used += virtualRange.size();
break;
}
auto mapVirtualRange = rx::AddressRange::fromBeginSize(
virtualRange.beginAddress(), block.size());
virtualRange = rx::AddressRange::fromBeginEnd(
mapVirtualRange.endAddress(), virtualRange.endAddress());
auto errc = orbis::pmem::map(mapVirtualRange.beginAddress(), block,
orbis::vmem::toCpuProtection(protection));
rx::dieIf(errc != orbis::ErrorCode{},
"blockpool: failed to map physical memory");
amdgpu::mapMemory(process->pid, mapVirtualRange, type, protection,
block.beginAddress());
usedBlocks.map(mapVirtualRange,
{.pmemAddress = block.beginAddress(), .type = type},
false);
freeBlocks.pop_back();
}
}
void decommit(orbis::Process *process, rx::AddressRange virtualRange) {
auto it = usedBlocks.lowerBound(virtualRange.beginAddress());
if (it != usedBlocks.end() &&
it.beginAddress() < virtualRange.beginAddress()) {
auto itRange = it.range();
auto decommitRange = itRange.intersection(virtualRange);
used -= decommitRange.size();
auto decommitPmemRange = rx::AddressRange::fromBeginSize(
it->pmemAddress +
(virtualRange.beginAddress() - itRange.beginAddress()),
decommitRange.size());
addFreeBlock(decommitPmemRange);
usedBlocks.unmap(decommitRange);
amdgpu::unmapMemory(process->pid, decommitRange);
++it;
}
while (it != usedBlocks.end() &&
it.beginAddress() < virtualRange.endAddress()) {
auto itRange = it.range();
auto decommitRange = itRange.intersection(virtualRange);
used -= decommitRange.size();
addFreeBlock(rx::AddressRange::fromBeginSize(it->pmemAddress,
decommitRange.size()));
amdgpu::unmapMemory(process->pid, decommitRange);
if (itRange == decommitRange) {
it = usedBlocks.unmap(it);
} else {
usedBlocks.unmap(decommitRange);
break;
}
}
}
std::optional<orbis::MemoryType> getMemoryType(std::uint64_t address) {
auto it = usedBlocks.queryArea(address);
if (it == usedBlocks.end()) {
return {};
}
return it->type;
}
};
static auto g_blockpool = orbis::createGlobalObject<
kernel::LockableKernelObject<PooledMemoryResource>>();
static auto g_cachedBlockpool = orbis::createGlobalObject<
kernel::LockableKernelObject<PooledMemoryResource>>();
void orbis::blockpool::clear() {
std::scoped_lock lock(*g_blockpool, *g_cachedBlockpool);
g_blockpool->clear();
g_cachedBlockpool->clear();
}
orbis::ErrorCode orbis::blockpool::expand(rx::AddressRange dmemRange) {
std::scoped_lock lock(*g_blockpool);
g_blockpool->expand(dmemRange);
return {};
}
orbis::ErrorCode orbis::blockpool::allocateControlBlock() {
std::scoped_lock cachedLock(*g_cachedBlockpool);
if (g_cachedBlockpool->used < g_cachedBlockpool->total) {
return g_cachedBlockpool->reservePage().second;
}
std::scoped_lock lock(*g_blockpool);
if (g_blockpool->total - g_blockpool->used < dmem::kPageSize) {
return ErrorCode::INVAL;
}
g_blockpool->moveTo(*g_cachedBlockpool, dmem::kPageSize);
return g_cachedBlockpool->reservePage().second;
}
orbis::ErrorCode orbis::blockpool::releaseControlBlock() {
std::scoped_lock lock(*g_cachedBlockpool);
// control block is always cached
if (!g_cachedBlockpool->reservedPages.empty()) {
auto [page, errc] = g_cachedBlockpool->releasePage();
if (errc != ErrorCode{}) {
return errc;
}
g_cachedBlockpool->expand(
rx::AddressRange::fromBeginSize(page, dmem::kPageSize));
return {};
}
return ErrorCode::INVAL;
}
orbis::ErrorCode
orbis::blockpool::commit(Process *process, rx::AddressRange vmemRange,
MemoryType type,
rx::EnumBitSet<orbis::vmem::Protection> protection) {
auto pool =
type == MemoryType::WbOnion ? g_cachedBlockpool : g_blockpool;
auto otherPool =
type == MemoryType::WbOnion ? g_blockpool : g_cachedBlockpool;
std::scoped_lock lock(*pool);
if (auto avail = pool->total - pool->used; avail < vmemRange.size()) {
// try to steal free blocks from other pool
std::scoped_lock lock(*otherPool);
auto pullSize = vmemRange.size() - avail;
if (otherPool->total - otherPool->used < pullSize) {
return ErrorCode::NOMEM;
}
otherPool->moveTo(*pool, pullSize);
}
pool->commit(process, vmemRange, type, protection);
return {};
}
void orbis::blockpool::decommit(Process *process, rx::AddressRange vmemRange) {
std::scoped_lock lock(*g_cachedBlockpool, *g_blockpool);
g_cachedBlockpool->decommit(process, vmemRange);
g_blockpool->decommit(process, vmemRange);
}
std::optional<orbis::MemoryType>
orbis::blockpool::getType(std::uint64_t address) {
{
std::scoped_lock lock(*g_cachedBlockpool);
if (auto result = g_cachedBlockpool->getMemoryType(address)) {
return result;
}
}
std::scoped_lock lock(*g_blockpool);
return g_blockpool->getMemoryType(address);
}
orbis::blockpool::BlockStats orbis::blockpool::stats() {
BlockStats result{};
{
std::scoped_lock lock(*g_cachedBlockpool, *g_blockpool);
result.availFlushedBlocks =
(g_blockpool->total - g_blockpool->used) / dmem::kPageSize;
result.availCachedBlocks =
(g_cachedBlockpool->total - g_cachedBlockpool->used) / dmem::kPageSize;
result.commitFlushedBlocks = g_blockpool->used / dmem::kPageSize;
result.commitCachedBlocks = g_cachedBlockpool->used / dmem::kPageSize;
}
return result;
}

610
kernel/orbis/src/dmem.cpp Normal file
View file

@ -0,0 +1,610 @@
#include "dmem.hpp"
#include "KernelAllocator.hpp"
#include "KernelObject.hpp"
#include "error.hpp"
#include "kernel/KernelObject.hpp"
#include "pmem.hpp"
#include "rx/AddressRange.hpp"
#include "rx/FileLock.hpp"
#include "rx/Serializer.hpp"
#include "rx/die.hpp"
#include "rx/print.hpp"
#include "utils/Logs.hpp"
#include "vmem.hpp"
#include <array>
#include <rx/format.hpp>
#include <string_view>
#include <utility>
struct DirectMemoryAllocation {
static constexpr std::uint32_t kAllocatedBit = 1 << 31;
static constexpr std::uint32_t kPooledBit = 1 << 30;
std::uint32_t type = 0;
[[nodiscard]] bool isPooled() const { return type & kPooledBit; }
void markAsPooled() { type |= kPooledBit | kAllocatedBit; }
void setMemoryType(orbis::MemoryType memoryType) {
type = (std::to_underlying(memoryType) & ~(kAllocatedBit | kPooledBit)) |
(type & kPooledBit) | kAllocatedBit;
}
[[nodiscard]] orbis::MemoryType getMemoryType() const {
return static_cast<orbis::MemoryType>(type & ~(kAllocatedBit | kPooledBit));
}
[[nodiscard]] bool isAllocated() const { return (type & kAllocatedBit) != 0; }
[[nodiscard]] bool isRelated(const DirectMemoryAllocation &other,
rx::AddressRange, rx::AddressRange) const {
return type == other.type;
}
[[nodiscard]] DirectMemoryAllocation
merge(const DirectMemoryAllocation &other, rx::AddressRange,
rx::AddressRange) const {
return other;
}
bool operator==(const DirectMemoryAllocation &) const = default;
};
struct DirectMemoryResourceState {
std::uint64_t pmemOffset{};
std::uint64_t dmemTotalSize{};
std::uint64_t dmemReservedSize{};
};
struct DirectMemoryResource
: kernel::AllocableResource<DirectMemoryAllocation, orbis::kallocator>,
DirectMemoryResourceState {
using BaseResource =
kernel::AllocableResource<DirectMemoryAllocation, orbis::kallocator>;
void create(std::size_t size) {
auto [pmemRange, errc] = orbis::pmem::allocate(0, size, {}, 64 * 1024);
rx::dieIf(errc != orbis::ErrorCode{},
"failed to allocate direct memory: size {:x}, error {}", size,
static_cast<int>(errc));
dmemTotalSize = pmemRange.size();
pmemOffset = pmemRange.beginAddress();
auto dmemResourceRange =
rx::AddressRange::fromBeginSize(0, pmemRange.size());
if (auto errc = BaseResource::create(dmemResourceRange);
errc != std::errc{}) {
rx::die("failed to create direct memory resource {:x}, error {}",
pmemRange.size(), errc);
}
}
orbis::ErrorCode clear() {
auto result = BaseResource::create(
rx::AddressRange::fromBeginSize(0, dmemTotalSize + dmemReservedSize));
if (result != std::errc{}) {
return orbis::toErrorCode(result);
}
dmemReservedSize = 0;
return {};
}
void serialize(rx::Serializer &s) const {
s.serialize(static_cast<const DirectMemoryResourceState &>(*this));
BaseResource::serialize(s);
}
void deserialize(rx::Deserializer &d) {
d.deserialize(static_cast<DirectMemoryResourceState &>(*this));
BaseResource::deserialize(d);
}
};
static std::array g_dmemPools = {
orbis::createGlobalObject<
kernel::LockableKernelObject<DirectMemoryResource>>(),
orbis::createGlobalObject<
kernel::LockableKernelObject<DirectMemoryResource>>(),
orbis::createGlobalObject<
kernel::LockableKernelObject<DirectMemoryResource>>(),
};
orbis::ErrorCode orbis::dmem::initialize() {
g_dmemPools[0]->create(0x120000000);
g_dmemPools[1]->create(0x1000000);
g_dmemPools[2]->create(0x1000000);
return {};
}
orbis::ErrorCode orbis::dmem::clear(unsigned dmemIndex) {
if (dmemIndex >= std::size(g_dmemPools)) {
return ErrorCode::INVAL;
}
{
auto dmem = g_dmemPools[dmemIndex];
std::lock_guard lock(*dmem);
auto result = dmem->clear();
if (result != orbis::ErrorCode{}) {
return result;
}
}
return {};
}
static void dmemDump(unsigned dmemIndex, std::string_view message = {}) {
auto dmem = g_dmemPools[dmemIndex];
rx::ScopedFileLock lock(stderr);
rx::println(stderr, "dmem0 {}", message);
for (auto alloc : *dmem) {
rx::println(stderr, " {:012x}-{:012x}: {}{} {}", alloc.beginAddress(),
alloc.endAddress(), "_A"[alloc->isAllocated()],
"_P"[alloc->isPooled()], alloc->getMemoryType());
}
}
std::pair<std::uint64_t, orbis::ErrorCode>
orbis::dmem::allocate(unsigned dmemIndex, rx::AddressRange searchRange,
std::uint64_t len, MemoryType memoryType,
std::uint64_t alignment, bool pooled) {
if (dmemIndex >= std::size(g_dmemPools)) {
return {{}, ErrorCode::INVAL};
}
if (searchRange.endAddress() != 0 &&
(!searchRange.isValid() || searchRange.size() < len)) {
ORBIS_LOG_ERROR(__FUNCTION__, "invalid range", searchRange.beginAddress(),
searchRange.endAddress(), len);
return {{}, orbis::ErrorCode::INVAL};
}
auto dmem = g_dmemPools[dmemIndex];
std::lock_guard lock(*dmem);
alignment = alignment == 0 ? kPageSize : alignment;
len = rx::alignUp(len, dmem::kPageSize);
if (searchRange.endAddress() == 0) {
searchRange = rx::AddressRange::fromBeginEnd(searchRange.beginAddress(),
dmem->dmemTotalSize -
dmem->dmemReservedSize);
}
if (!searchRange.isValid() || searchRange.size() < len) {
ORBIS_LOG_ERROR(__FUNCTION__, "invalid range", searchRange.beginAddress(),
searchRange.endAddress(), len);
return {{}, orbis::ErrorCode::INVAL};
}
if (len == 0) {
ORBIS_LOG_ERROR(__FUNCTION__, "len is 0", searchRange.beginAddress(),
searchRange.endAddress(), len);
return {{}, ErrorCode::INVAL};
}
if (searchRange.endAddress() > dmem->dmemTotalSize - dmem->dmemReservedSize) {
ORBIS_LOG_ERROR(__FUNCTION__, "out of direct memory size",
searchRange.beginAddress(), searchRange.endAddress(),
dmem->dmemTotalSize);
return {{}, ErrorCode::INVAL};
}
DirectMemoryAllocation allocation;
allocation.setMemoryType(memoryType);
if (pooled) {
allocation.markAsPooled();
}
auto allocResult = dmem->map(searchRange.beginAddress(), len, allocation,
AllocationFlags::Dry, alignment);
if (allocResult.errc != std::errc{}) {
return {{}, ErrorCode::AGAIN};
}
auto result = allocResult.range.beginAddress();
auto commitResult =
dmem->map(result, len, allocation, AllocationFlags::Fixed, alignment);
rx::dieIf(commitResult.errc != std::errc{},
"dmem: failed to commit memory, error {}", commitResult.errc);
dmemDump(dmemIndex,
rx::format("allocated {:x}-{:x}", allocResult.range.beginAddress(),
allocResult.range.endAddress()));
return {result, {}};
}
orbis::ErrorCode orbis::dmem::release(unsigned dmemIndex,
rx::AddressRange range, bool pooled) {
if (dmemIndex >= std::size(g_dmemPools)) {
return ErrorCode::INVAL;
}
if (range.beginAddress() % kPageSize || range.size() % vmem::kPageSize ||
!range.isValid()) {
return ErrorCode::INVAL;
}
auto dmem = g_dmemPools[dmemIndex];
std::lock_guard lock(*dmem);
constexpr auto razorGpuMemory =
rx::AddressRange::fromBeginSize(0x3000000000, 0x20000000);
if (dmemIndex == 0 && razorGpuMemory.contains(range.beginAddress())) {
return ErrorCode::OPNOTSUPP;
}
auto it = dmem->query(range.beginAddress());
if (it == dmem->end() || !it->isAllocated()) {
return ErrorCode::NOENT;
}
if (it->isPooled() && !pooled) {
return ErrorCode::NOENT;
}
DirectMemoryAllocation allocation{};
auto result = dmem->map(range.beginAddress(), range.size(), allocation,
AllocationFlags::Fixed, vmem::kPageSize);
if (result.errc != std::errc{}) {
return toErrorCode(result.errc);
}
return {};
}
std::pair<std::uint64_t, orbis::ErrorCode>
orbis::dmem::reserveSystem(unsigned dmemIndex, std::uint64_t size) {
if (dmemIndex >= std::size(g_dmemPools)) {
return {{}, ErrorCode::INVAL};
}
auto dmem = g_dmemPools[dmemIndex];
std::lock_guard lock(*dmem);
if (size == 0 || size % vmem::kPageSize) {
return {{}, ErrorCode::INVAL};
}
if (dmem->dmemReservedSize + size > dmem->dmemTotalSize) {
return {{}, ErrorCode::NOMEM};
}
DirectMemoryAllocation alloc;
alloc.setMemoryType(MemoryType::WbOnion);
auto result = dmem->map(0, size, alloc, AllocationFlags::Stack, kPageSize);
if (result.errc != std::errc{}) {
return {{}, toErrorCode(result.errc)};
}
dmem->dmemReservedSize += size;
return {result.range.beginAddress() | 0x4000000000, {}};
}
std::pair<std::uint64_t, orbis::ErrorCode>
orbis::dmem::reservePooled(unsigned dmemIndex, rx::AddressRange searchRange,
std::uint64_t size, std::uint64_t alignment) {
if (dmemIndex >= std::size(g_dmemPools)) {
return {{}, ErrorCode::INVAL};
}
alignment = alignment == 0 ? kPageSize : alignment;
if (alignment % kPageSize) {
return {{}, ErrorCode::INVAL};
}
auto dmem = g_dmemPools[dmemIndex];
if (searchRange.endAddress() > dmem->dmemTotalSize) {
ORBIS_LOG_ERROR(__FUNCTION__, "out of direct memory size",
searchRange.endAddress(), dmem->dmemTotalSize);
return {{}, ErrorCode::INVAL};
}
if (!searchRange.isValid() &&
searchRange.beginAddress() != searchRange.endAddress()) {
ORBIS_LOG_ERROR(__FUNCTION__, "invalid range", searchRange.beginAddress(),
searchRange.endAddress());
return {{}, ErrorCode::INVAL};
}
std::lock_guard lock(*dmem);
if (searchRange.beginAddress() == searchRange.endAddress()) {
searchRange = rx::AddressRange::fromBeginEnd(searchRange.beginAddress(),
dmem->dmemTotalSize -
dmem->dmemReservedSize);
if (!searchRange.isValid()) {
ORBIS_LOG_ERROR(__FUNCTION__, "invalid range", searchRange.beginAddress(),
searchRange.endAddress());
return {{}, ErrorCode::INVAL};
}
}
if (searchRange.endAddress() > dmem->dmemTotalSize - dmem->dmemReservedSize) {
ORBIS_LOG_ERROR(__FUNCTION__, "out of direct memory size",
searchRange.endAddress(), dmem->dmemTotalSize,
dmem->dmemReservedSize);
return {{}, ErrorCode::INVAL};
}
auto it = dmem->lowerBound(searchRange.beginAddress());
rx::AddressRange result;
while (it != dmem->end() && it.beginAddress() < searchRange.endAddress()) {
if (!it->isAllocated()) {
auto viewRange = searchRange.intersection(it.range());
viewRange = rx::AddressRange::fromBeginEnd(
rx::alignUp(viewRange.beginAddress(), alignment),
viewRange.endAddress());
if (viewRange.isValid() && viewRange.size() >= size) {
result =
rx::AddressRange::fromBeginSize(viewRange.beginAddress(), size);
break;
}
}
++it;
}
if (!result.isValid()) {
return {{}, ErrorCode::NOMEM};
}
DirectMemoryAllocation allocation;
allocation.markAsPooled();
auto commitResult = dmem->map(result.beginAddress(), result.size(),
allocation, AllocationFlags::Fixed, alignment);
if (commitResult.errc != std::errc{}) {
return {{}, toErrorCode(commitResult.errc)};
}
return {result.beginAddress(), {}};
}
orbis::ErrorCode orbis::dmem::setType(unsigned dmemIndex,
rx::AddressRange range, MemoryType type,
rx::EnumBitSet<QueryFlags> flags) {
if (dmemIndex >= std::size(g_dmemPools)) {
return ErrorCode::INVAL;
}
bool expectedPooled = (flags & QueryFlags::Pooled) == QueryFlags::Pooled;
auto dmem = g_dmemPools[dmemIndex];
std::lock_guard lock(*dmem);
auto it = dmem->query(range.beginAddress());
if (it == dmem->end() || !it->isAllocated() ||
it->isPooled() != expectedPooled) {
return ErrorCode::ACCES;
}
if (it->getMemoryType() == type && it.range().contains(range)) {
return {};
}
DirectMemoryAllocation allocation;
allocation.setMemoryType(type);
auto [_it, errc, _range] =
dmem->map(range.beginAddress(), range.size(), allocation,
AllocationFlags::Fixed, vmem::kPageSize);
return toErrorCode(errc);
}
std::optional<orbis::dmem::QueryResult>
orbis::dmem::query(unsigned dmemIndex, std::uint64_t dmemOffset,
rx::EnumBitSet<QueryFlags> flags) {
if (dmemIndex >= std::size(g_dmemPools)) {
return {};
}
auto dmem = g_dmemPools[dmemIndex];
std::lock_guard lock(*dmem);
auto it = dmem->lowerBound(dmemOffset);
if (flags & QueryFlags::LowerBound) {
while (it != dmem->end() && !it->isAllocated()) {
++it;
}
if (it == dmem->end()) {
return {};
}
} else if (it == dmem->end() || !it->isAllocated()) {
return {};
}
if (!(flags & QueryFlags::Pooled)) {
if (it->isPooled()) {
return {};
}
}
return QueryResult{.range = it.range(), .memoryType = it->getMemoryType()};
}
std::uint64_t orbis::dmem::getSize(unsigned dmemIndex) {
if (dmemIndex >= std::size(g_dmemPools)) {
return 0;
}
auto dmem = g_dmemPools[dmemIndex];
std::lock_guard lock(*dmem);
return dmem->dmemTotalSize - dmem->dmemReservedSize;
}
std::pair<rx::AddressRange, orbis::ErrorCode>
orbis::dmem::getAvailSize(unsigned dmemIndex, rx::AddressRange searchRange,
std::uint64_t alignment) {
if (dmemIndex >= std::size(g_dmemPools)) {
return {{}, ErrorCode::INVAL};
}
alignment = alignment == 0 ? kPageSize : alignment;
if (alignment % kPageSize) {
return {{}, ErrorCode::INVAL};
}
auto dmem = g_dmemPools[dmemIndex];
if (searchRange.endAddress() > dmem->dmemTotalSize) {
ORBIS_LOG_ERROR(__FUNCTION__, "out of direct memory size",
searchRange.endAddress(), dmem->dmemTotalSize);
return {{}, orbis::ErrorCode::INVAL};
}
if (!searchRange.isValid() &&
searchRange.beginAddress() != searchRange.endAddress()) {
ORBIS_LOG_ERROR(__FUNCTION__, "invalid range", searchRange.beginAddress(),
searchRange.endAddress());
return {{}, orbis::ErrorCode::INVAL};
}
std::lock_guard lock(*dmem);
if (searchRange.beginAddress() == searchRange.endAddress()) {
searchRange = rx::AddressRange::fromBeginEnd(searchRange.beginAddress(),
dmem->dmemTotalSize -
dmem->dmemReservedSize);
if (!searchRange.isValid()) {
ORBIS_LOG_ERROR(__FUNCTION__, "invalid range", searchRange.beginAddress(),
searchRange.endAddress());
return {{}, orbis::ErrorCode::INVAL};
}
}
if (searchRange.endAddress() > dmem->dmemTotalSize - dmem->dmemReservedSize) {
ORBIS_LOG_ERROR(__FUNCTION__, "out of direct memory size",
searchRange.endAddress(), dmem->dmemTotalSize,
dmem->dmemReservedSize);
return {{}, orbis::ErrorCode::INVAL};
}
auto it = dmem->lowerBound(searchRange.beginAddress());
rx::AddressRange result = rx::AddressRange::fromBeginEnd(
searchRange.beginAddress(), searchRange.beginAddress());
while (it != dmem->end() && it.beginAddress() < searchRange.endAddress()) {
if (!it->isAllocated()) {
auto viewRange = searchRange.intersection(it.range());
viewRange = rx::AddressRange::fromBeginEnd(
rx::alignUp(viewRange.beginAddress(), alignment),
viewRange.endAddress());
if (viewRange.isValid() &&
(!result.isValid() || viewRange.size() > result.size())) {
result = viewRange;
}
}
++it;
}
return {result, {}};
}
orbis::ErrorCode orbis::dmem::map(unsigned dmemIndex, rx::AddressRange range,
std::uint64_t offset,
rx::EnumBitSet<vmem::Protection> protection) {
if (dmemIndex >= std::size(g_dmemPools)) {
return ErrorCode::INVAL;
}
auto dmem = g_dmemPools[dmemIndex];
std::lock_guard lock(*dmem);
if (offset < 0 || offset + range.size() < offset ||
offset + range.size() > dmem->dmemTotalSize) {
return orbis::ErrorCode::INVAL;
}
if (offset + range.size() > dmem->dmemTotalSize - dmem->dmemReservedSize) {
return orbis::ErrorCode::ACCES;
}
auto allocationInfoIt = dmem->query(offset);
if (allocationInfoIt == dmem->end() || !allocationInfoIt->isAllocated()) {
if (allocationInfoIt != dmem->end()) {
dmemDump(
dmemIndex,
rx::format("map unallocated {:x}-{:x}, requested range {:x}-{:x}",
allocationInfoIt.beginAddress(),
allocationInfoIt.endAddress(), range.beginAddress(),
range.endAddress()));
} else {
dmemDump(dmemIndex, rx::format("map out of memory {:x}-{:x}",
range.beginAddress(), range.endAddress()));
}
return orbis::ErrorCode::ACCES;
}
if (allocationInfoIt->isPooled()) {
return orbis::ErrorCode::ACCES;
}
auto directRange = rx::AddressRange::fromBeginSize(offset, range.size())
.intersection(allocationInfoIt.range());
if (range.size() > directRange.size()) {
return orbis::ErrorCode::INVAL;
}
auto physicalRange =
rx::AddressRange::fromBeginSize(dmem->pmemOffset + offset, range.size());
return orbis::pmem::map(range.beginAddress(), physicalRange,
vmem::toCpuProtection(protection));
}
std::pair<std::uint64_t, orbis::ErrorCode>
orbis::dmem::getPmemOffset(unsigned dmemIndex, std::uint64_t dmemOffset) {
if (dmemIndex > std::size(g_dmemPools)) {
return {{}, ErrorCode::INVAL};
}
auto dmem = g_dmemPools[dmemIndex];
std::lock_guard lock(*dmem);
if (dmemOffset >= dmem->dmemTotalSize) {
return {{}, orbis::ErrorCode::INVAL};
}
if (dmemOffset >= dmem->dmemTotalSize - dmem->dmemReservedSize) {
return {{}, orbis::ErrorCode::ACCES};
}
auto allocationInfoIt = dmem->query(dmemOffset);
if (allocationInfoIt == dmem->end() || !allocationInfoIt->isAllocated()) {
return {{}, orbis::ErrorCode::ACCES};
}
return {dmem->pmemOffset + dmemOffset, {}};
}

View file

@ -1,12 +1,14 @@
#include "fmem.hpp" #include "fmem.hpp"
#include "KernelAllocator.hpp"
#include "KernelObject.hpp" #include "KernelObject.hpp"
#include "error.hpp" #include "error.hpp"
#include "kernel/KernelObject.hpp" #include "kernel/KernelObject.hpp"
#include "kernel/MemoryResource.hpp" #include "kernel/MemoryResource.hpp"
#include "pmem.hpp" #include "pmem.hpp"
#include "rx/AddressRange.hpp" #include "rx/AddressRange.hpp"
#include "thread/Process.hpp" #include "rx/debug.hpp"
#include "rx/print.hpp"
#include "vmem.hpp" #include "vmem.hpp"
#include <cassert> #include <cassert>
#include <rx/Mappable.hpp> #include <rx/Mappable.hpp>
@ -30,59 +32,55 @@ struct FlexibleMemoryAllocation {
}; };
using FlexibleMemoryResource = using FlexibleMemoryResource =
kernel::AllocableResource<FlexibleMemoryAllocation>; kernel::AllocableResource<FlexibleMemoryAllocation, orbis::kallocator>;
static auto g_fmemInstance = orbis::createProcessLocalObject< static auto g_fmemInstance = orbis::createGlobalObject<
kernel::LockableKernelObject<FlexibleMemoryResource>>(); kernel::LockableKernelObject<FlexibleMemoryResource>>();
orbis::ErrorCode orbis::fmem::initialize(Process *process, std::uint64_t size) { orbis::ErrorCode orbis::fmem::initialize(std::uint64_t size) {
auto [range, errc] = auto [range, errc] =
pmem::allocate(pmem::getSize() - 1, size, pmem::MemoryType::WbOnion, pmem::allocate(0, size, kernel::AllocationFlags::Stack, vmem::kPageSize);
kernel::AllocationFlags::Stack, vmem::kPageSize);
if (errc != ErrorCode{}) { if (errc != ErrorCode{}) {
return errc; return errc;
} }
auto fmem = process->get(g_fmemInstance); rx::println("fmem: {:x}-{:x}", range.beginAddress(), range.endAddress());
std::lock_guard lock(*fmem);
return toErrorCode(fmem->create(range)); std::lock_guard lock(*g_fmemInstance);
return toErrorCode(g_fmemInstance->create(range));
} }
void orbis::fmem::destroy(Process *process) { void orbis::fmem::destroy() {
auto fmem = process->get(g_fmemInstance); std::lock_guard lock(*g_fmemInstance);
std::lock_guard lock(*fmem); for (auto allocation : g_fmemInstance->allocations) {
for (auto allocation : fmem->allocations) {
pmem::deallocate(allocation); pmem::deallocate(allocation);
} }
fmem->destroy(); g_fmemInstance->destroy();
} }
std::pair<rx::AddressRange, orbis::ErrorCode> std::pair<rx::AddressRange, orbis::ErrorCode>
orbis::fmem::allocate(Process *process, std::uint64_t size) { orbis::fmem::allocate(std::uint64_t size) {
auto fmem = process->get(g_fmemInstance); std::lock_guard lock(*g_fmemInstance);
std::lock_guard lock(*fmem);
FlexibleMemoryAllocation allocation{.allocated = true}; FlexibleMemoryAllocation allocation{.allocated = true};
auto [it, errc, range] = fmem->map( auto [it, errc, range] = g_fmemInstance->map(
0, size, allocation, kernel::AllocationFlags::NoMerge, vmem::kPageSize); 0, size, allocation, kernel::AllocationFlags::NoMerge, vmem::kPageSize);
if (errc != std::errc{}) { if (errc != std::errc{}) {
rx::println(stderr, "fmem: failed to allocate {:#x} bytes", size);
return {{}, toErrorCode(errc)}; return {{}, toErrorCode(errc)};
} }
return {range, {}}; return {range, {}};
} }
orbis::ErrorCode orbis::fmem::deallocate(Process *process, orbis::ErrorCode orbis::fmem::deallocate(rx::AddressRange range) {
rx::AddressRange range) {
FlexibleMemoryAllocation allocation{}; FlexibleMemoryAllocation allocation{};
auto fmem = process->get(g_fmemInstance); std::lock_guard lock(*g_fmemInstance);
std::lock_guard lock(*fmem); auto [it, errc, _] =
auto [it, errc, _] = fmem->map(range.beginAddress(), range.size(), allocation, g_fmemInstance->map(range.beginAddress(), range.size(), allocation,
AllocationFlags::Fixed, 1); AllocationFlags::Fixed, 1);
return toErrorCode(errc); return toErrorCode(errc);
} }

View file

@ -1,30 +1,32 @@
#include "pmem.hpp" #include "pmem.hpp"
#include "IoDevice.hpp" #include "IoDevice.hpp"
#include "KernelAllocator.hpp"
#include "KernelObject.hpp" #include "KernelObject.hpp"
#include "error.hpp" #include "error.hpp"
#include "error/ErrorCode.hpp" #include "error/ErrorCode.hpp"
#include "file.hpp"
#include "kernel/KernelObject.hpp" #include "kernel/KernelObject.hpp"
#include "kernel/MemoryResource.hpp" #include "kernel/MemoryResource.hpp"
#include "rx/AddressRange.hpp" #include "rx/AddressRange.hpp"
#include "rx/Rc.hpp"
#include "rx/print.hpp"
#include "vmem.hpp" #include "vmem.hpp"
#include <cassert> #include <cassert>
#include <rx/Mappable.hpp> #include <rx/Mappable.hpp>
struct PhysicalMemoryAllocation { struct PhysicalMemoryAllocation {
orbis::pmem::MemoryType type = orbis::pmem::MemoryType::Invalid; bool allocated = false;
[[nodiscard]] bool isAllocated() const { [[nodiscard]] bool isAllocated() const { return allocated; }
return type != orbis::pmem::MemoryType::Invalid;
}
[[nodiscard]] bool isRelated(const PhysicalMemoryAllocation &left, [[nodiscard]] bool isRelated(const PhysicalMemoryAllocation &left,
rx::AddressRange, rx::AddressRange) const { rx::AddressRange, rx::AddressRange) const {
return type == left.type; return allocated == left.allocated;
} }
[[nodiscard]] PhysicalMemoryAllocation [[nodiscard]] PhysicalMemoryAllocation
merge(const PhysicalMemoryAllocation &other, rx::AddressRange, merge(const PhysicalMemoryAllocation &other, rx::AddressRange,
rx::AddressRange) const { rx::AddressRange) const {
assert(other.type == type); assert(other.allocated == allocated);
return other; return other;
} }
@ -37,12 +39,22 @@ using MappableMemoryResource =
})>; })>;
using PhysicalMemoryResource = using PhysicalMemoryResource =
kernel::AllocableResource<PhysicalMemoryAllocation, MappableMemoryResource>; kernel::AllocableResource<PhysicalMemoryAllocation, orbis::kallocator,
MappableMemoryResource>;
static auto g_pmemInstance = orbis::createGlobalObject< static auto g_pmemInstance = orbis::createGlobalObject<
kernel::LockableKernelObject<PhysicalMemoryResource>>(); kernel::LockableKernelObject<PhysicalMemoryResource>>();
struct PhysicalMemory : orbis::IoDevice { struct PhysicalMemory : orbis::IoDevice {
orbis::File file;
PhysicalMemory() {
incRef(); // do not delete global object
file.device = this;
file.incRef(); // do not delete property
}
orbis::ErrorCode open(rx::Ref<orbis::File> *file, const char *path, orbis::ErrorCode open(rx::Ref<orbis::File> *file, const char *path,
std::uint32_t flags, std::uint32_t mode, std::uint32_t flags, std::uint32_t mode,
orbis::Thread *thread) override { orbis::Thread *thread) override {
@ -50,11 +62,12 @@ struct PhysicalMemory : orbis::IoDevice {
} }
orbis::ErrorCode map(rx::AddressRange range, std::int64_t offset, orbis::ErrorCode map(rx::AddressRange range, std::int64_t offset,
rx::EnumBitSet<rx::mem::Protection> protection, rx::EnumBitSet<orbis::vmem::Protection> protection,
orbis::Process *) override { orbis::File *, orbis::Process *) override {
return orbis::pmem::map( return orbis::pmem::map(
range.beginAddress(), range.beginAddress(),
rx::AddressRange::fromBeginSize(offset, range.size()), protection); rx::AddressRange::fromBeginSize(offset, range.size()),
orbis::vmem::toCpuProtection(protection));
} }
void serialize(rx::Serializer &s) const {} void serialize(rx::Serializer &s) const {}
@ -65,6 +78,8 @@ static auto g_phyMemory = orbis::createGlobalObject<PhysicalMemory>();
orbis::ErrorCode orbis::pmem::initialize(std::uint64_t size) { orbis::ErrorCode orbis::pmem::initialize(std::uint64_t size) {
std::lock_guard lock(*g_pmemInstance); std::lock_guard lock(*g_pmemInstance);
rx::println("pmem: {:x}", size);
return toErrorCode( return toErrorCode(
g_pmemInstance->create(rx::AddressRange::fromBeginSize(0, size))); g_pmemInstance->create(rx::AddressRange::fromBeginSize(0, size)));
} }
@ -74,11 +89,12 @@ void orbis::pmem::destroy() {
g_pmemInstance->destroy(); g_pmemInstance->destroy();
} }
std::pair<rx::AddressRange, orbis::ErrorCode> orbis::pmem::allocate( std::pair<rx::AddressRange, orbis::ErrorCode>
std::uint64_t addressHint, std::uint64_t size, MemoryType memoryType, orbis::pmem::allocate(std::uint64_t addressHint, std::uint64_t size,
rx::EnumBitSet<AllocationFlags> flags, std::uint64_t alignment) { rx::EnumBitSet<AllocationFlags> flags,
std::uint64_t alignment) {
std::lock_guard lock(*g_pmemInstance); std::lock_guard lock(*g_pmemInstance);
PhysicalMemoryAllocation allocation{.type = memoryType}; PhysicalMemoryAllocation allocation{.allocated = true};
auto [it, errc, range] = auto [it, errc, range] =
g_pmemInstance->map(addressHint, size, allocation, flags, alignment); g_pmemInstance->map(addressHint, size, allocation, flags, alignment);
@ -99,8 +115,7 @@ orbis::ErrorCode orbis::pmem::deallocate(rx::AddressRange range) {
return toErrorCode(errc); return toErrorCode(errc);
} }
std::optional<orbis::pmem::AllocatedMemory> std::optional<rx::AddressRange> orbis::pmem::query(std::uint64_t address) {
orbis::pmem::query(std::uint64_t address) {
std::lock_guard lock(*g_pmemInstance); std::lock_guard lock(*g_pmemInstance);
auto result = g_pmemInstance->query(address); auto result = g_pmemInstance->query(address);
@ -108,7 +123,7 @@ orbis::pmem::query(std::uint64_t address) {
return {}; return {};
} }
return AllocatedMemory{.range = result.range(), .memoryType = result->type}; return result.range();
} }
orbis::ErrorCode orbis::ErrorCode
@ -117,11 +132,11 @@ orbis::pmem::map(std::uint64_t virtualAddress, rx::AddressRange range,
auto virtualRange = auto virtualRange =
rx::AddressRange::fromBeginSize(virtualAddress, range.size()); rx::AddressRange::fromBeginSize(virtualAddress, range.size());
auto errc = g_pmemInstance->mappable.map(virtualRange, range.beginAddress(), auto errc = g_pmemInstance->mappable.map(virtualRange, range.beginAddress(),
protection, orbis::vmem::kPageSize); protection, vmem::kPageSize);
return toErrorCode(errc); return toErrorCode(errc);
} }
std::size_t orbis::pmem::getSize() { return g_pmemInstance->size; } std::size_t orbis::pmem::getSize() { return g_pmemInstance->size; }
orbis::IoDevice *orbis::pmem::getDevice() { return g_phyMemory.get(); } orbis::IoDevice *orbis::pmem::getDevice() { return g_phyMemory.get(); }
orbis::File *orbis::pmem::getFile() { return &g_phyMemory->file; }

View file

@ -40,31 +40,33 @@ orbis::SysResult orbis::sys_kqueueex(Thread *thread, ptr<char> name,
return {}; return {};
} }
static bool isReadEventTriggered(int hostFd) { static bool isReadEventTriggered(const rx::Mappable &hostFd) {
#ifdef __linux #ifdef __linux
fd_set fds{}; fd_set fds{};
FD_SET(hostFd, &fds); FD_SET(hostFd.native_handle(), &fds);
timeval timeout{}; timeval timeout{};
if (::select(hostFd + 1, &fds, nullptr, nullptr, &timeout) < 0) { if (::select(hostFd.native_handle() + 1, &fds, nullptr, nullptr, &timeout) <
0) {
return false; return false;
} }
return FD_ISSET(hostFd, &fds); return FD_ISSET(hostFd.native_handle(), &fds);
#else #else
#warning "Not implemented" #warning "Not implemented"
return false; return false;
#endif #endif
} }
static bool isWriteEventTriggered(int hostFd) { static bool isWriteEventTriggered(const rx::Mappable &hostFd) {
#ifdef __linux #ifdef __linux
fd_set fds{}; fd_set fds{};
FD_SET(hostFd, &fds); FD_SET(hostFd.native_handle(), &fds);
timeval timeout{}; timeval timeout{};
if (::select(hostFd + 1, nullptr, &fds, nullptr, &timeout) < 0) { if (::select(hostFd.native_handle() + 1, nullptr, &fds, nullptr, &timeout) <
0) {
return false; return false;
} }
return FD_ISSET(hostFd, &fds); return FD_ISSET(hostFd.native_handle(), &fds);
#else #else
#warning "Not implemented" #warning "Not implemented"
return false; return false;
@ -131,7 +133,7 @@ static SysResult keventChange(KQueue *kq, KEvent &change, Thread *thread) {
eventEmitter->subscribe(&*nodeIt); eventEmitter->subscribe(&*nodeIt);
nodeIt->triggered = true; nodeIt->triggered = true;
kq->cv.notify_all(kq->mtx); kq->cv.notify_all(kq->mtx);
} else if (note.file->hostFd < 0) { } else if (!note.file->hostFd) {
ORBIS_LOG_ERROR("Unimplemented event emitter", change.ident); ORBIS_LOG_ERROR("Unimplemented event emitter", change.ident);
} }
} else if (change.filter == kEvFiltGraphicsCore || } else if (change.filter == kEvFiltGraphicsCore ||
@ -303,7 +305,7 @@ orbis::SysResult orbis::sys_kevent(Thread *thread, sint fd,
if (!note.triggered) { if (!note.triggered) {
if (note.event.filter == kEvFiltRead) { if (note.event.filter == kEvFiltRead) {
if (note.file->hostFd >= 0) { if (note.file->hostFd) {
if (isReadEventTriggered(note.file->hostFd)) { if (isReadEventTriggered(note.file->hostFd)) {
note.triggered = true; note.triggered = true;
} else { } else {
@ -311,7 +313,7 @@ orbis::SysResult orbis::sys_kevent(Thread *thread, sint fd,
} }
} }
} else if (note.event.filter == kEvFiltWrite) { } else if (note.event.filter == kEvFiltWrite) {
if (note.file->hostFd >= 0) { if (note.file->hostFd) {
if (isWriteEventTriggered(note.file->hostFd)) { if (isWriteEventTriggered(note.file->hostFd)) {
note.triggered = true; note.triggered = true;
} else { } else {

View file

@ -7,6 +7,9 @@
#include "module/ModuleInfoEx.hpp" #include "module/ModuleInfoEx.hpp"
#include "orbis/time.hpp" #include "orbis/time.hpp"
#include "osem.hpp" #include "osem.hpp"
#include "pmem.hpp"
#include "rx/AddressRange.hpp"
#include "rx/EnumBitSet.hpp"
#include "sys/sysproto.hpp" #include "sys/sysproto.hpp"
#include "thread/Process.hpp" #include "thread/Process.hpp"
#include "thread/ProcessOps.hpp" #include "thread/ProcessOps.hpp"
@ -14,6 +17,7 @@
#include "ucontext.hpp" #include "ucontext.hpp"
#include "uio.hpp" #include "uio.hpp"
#include "utils/Logs.hpp" #include "utils/Logs.hpp"
#include "vmem.hpp"
#include <fcntl.h> #include <fcntl.h>
#include <ranges> #include <ranges>
#include <utility> #include <utility>
@ -69,8 +73,12 @@ orbis::SysResult orbis::sys_netgetiflist(Thread *thread /* TODO */) {
return {}; return {};
} }
orbis::SysResult orbis::sys_mtypeprotect(Thread *thread /* TODO */) { orbis::SysResult
return ErrorCode::NOSYS; orbis::sys_mtypeprotect(Thread *thread, uintptr_t addr, size_t len,
MemoryType type,
rx::EnumBitSet<vmem::Protection> prot) {
auto range = rx::AddressRange::fromBeginSize(addr, len);
return vmem::setTypeAndProtect(thread->tproc, range, type, prot);
} }
orbis::SysResult orbis::sys_regmgr_call(Thread *thread, uint32_t op, orbis::SysResult orbis::sys_regmgr_call(Thread *thread, uint32_t op,
uint32_t id, ptr<void> result, uint32_t id, ptr<void> result,
@ -393,19 +401,20 @@ orbis::SysResult orbis::sys_evf_cancel(Thread *thread, sint id, uint64_t value,
return {}; return {};
} }
orbis::SysResult orbis::SysResult
orbis::sys_query_memory_protection(Thread *thread, ptr<void> address, orbis::sys_query_memory_protection(Thread *thread, uintptr_t address,
ptr<MemoryProtection> protection) { ptr<vmem::MemoryProtection> protection) {
if (auto query_memory_protection = auto result = vmem::queryProtection(thread->tproc, address);
thread->tproc->ops->query_memory_protection) {
return query_memory_protection(thread, address, protection); if (!result) {
return ErrorCode::ACCES;
} }
return ErrorCode::NOSYS; return uwrite(protection, *result);
} }
namespace orbis { namespace orbis {
struct BatchMapEntry { struct BatchMapEntry {
ptr<char> start; uintptr_t start;
off_t offset; off_t offset;
size_t length; size_t length;
char protection; char protection;
@ -419,14 +428,14 @@ struct BatchMapEntry {
}; };
} // namespace orbis } // namespace orbis
orbis::SysResult orbis::sys_batch_map(Thread *thread, sint unk, sint flags, orbis::SysResult orbis::sys_batch_map(Thread *thread, sint unk,
rx::EnumBitSet<vmem::MapFlags> flags,
ptr<BatchMapEntry> entries, ptr<BatchMapEntry> entries,
sint entriesCount, sint entriesCount,
ptr<sint> processedCount) { ptr<sint> processedCount) {
auto ops = thread->tproc->ops;
SysResult result = ErrorCode{}; SysResult result = ErrorCode{};
ORBIS_LOG_ERROR(__FUNCTION__, unk, flags, entries, entriesCount, ORBIS_LOG_ERROR(__FUNCTION__, unk, flags.toUnderlying(), entries,
processedCount); entriesCount, processedCount);
int processed = 0; int processed = 0;
for (int i = 0; i < entriesCount; ++i) { for (int i = 0; i < entriesCount; ++i) {
@ -438,16 +447,30 @@ orbis::SysResult orbis::sys_batch_map(Thread *thread, sint unk, sint flags,
switch (_entry.operation) { switch (_entry.operation) {
case 0: case 0:
result = ops->dmem_mmap(thread, _entry.start, _entry.length, _entry.type, result = sys_mmap_dmem(
_entry.protection, flags, _entry.offset); thread, _entry.start, _entry.length,
static_cast<MemoryType>(_entry.type),
rx::EnumBitSet<vmem::Protection>::fromUnderlying(_entry.protection),
flags, _entry.offset);
break; break;
case 1: case 1:
result = ops->munmap(thread, _entry.start, _entry.length); result = sys_munmap(thread, _entry.start, _entry.length);
break; break;
case 2: case 2:
result = result = sys_mprotect(
ops->mprotect(thread, _entry.start, _entry.length, _entry.protection); thread, _entry.start, _entry.length,
rx::EnumBitSet<vmem::Protection>::fromUnderlying(_entry.protection));
break; break;
case 3:
result = sys_mmap(
thread, _entry.start, _entry.length,
rx::EnumBitSet<vmem::Protection>::fromUnderlying(_entry.protection),
vmem::MapFlags::Void | vmem::MapFlags::Anon | flags, -1, 0);
break;
case 4:
ORBIS_LOG_ERROR(__FUNCTION__, "unimplemented type protect");
break;
default: default:
result = ErrorCode::INVAL; result = ErrorCode::INVAL;
break; break;
@ -880,14 +903,22 @@ orbis::SysResult orbis::sys_budget_set(Thread *thread, sint budgetId) {
thread->tproc->budgetId = budgetId; thread->tproc->budgetId = budgetId;
return {}; return {};
} }
orbis::SysResult orbis::sys_virtual_query(Thread *thread, ptr<void> addr,
uint64_t unk, ptr<void> info, orbis::SysResult orbis::sys_virtual_query(Thread *thread, uintptr_t addr,
uint64_t flags, ptr<void> info,
size_t infosz) { size_t infosz) {
if (auto virtual_query = thread->tproc->ops->virtual_query) {
return virtual_query(thread, addr, unk, info, infosz); if (infosz != sizeof(vmem::QueryResult)) {
return ErrorCode::INVAL;
} }
return ErrorCode::NOSYS; auto result = vmem::query(thread->tproc, addr, flags & 1);
if (!result) {
return ErrorCode::ACCES;
}
return uwrite(ptr<vmem::QueryResult>(info), *result);
} }
orbis::SysResult orbis::sys_mdbg_call(Thread *thread /* TODO */) { return {}; } orbis::SysResult orbis::sys_mdbg_call(Thread *thread /* TODO */) { return {}; }
orbis::SysResult orbis::sys_obs_sblock_create(Thread *thread /* TODO */) { orbis::SysResult orbis::sys_obs_sblock_create(Thread *thread /* TODO */) {
@ -930,7 +961,7 @@ orbis::SysResult orbis::sys_is_in_sandbox(Thread *thread /* TODO */) {
} }
orbis::SysResult orbis::sys_dmem_container(Thread *thread, uint id) { orbis::SysResult orbis::sys_dmem_container(Thread *thread, uint id) {
ORBIS_LOG_NOTICE(__FUNCTION__, id); ORBIS_LOG_NOTICE(__FUNCTION__, id);
thread->retval[0] = 1; // returns default direct memory device thread->retval[0] = 0; // returns default direct memory device
if (id + 1) if (id + 1)
return ErrorCode::PERM; return ErrorCode::PERM;
return {}; return {};
@ -955,24 +986,11 @@ orbis::SysResult orbis::sys_mname(Thread *thread, uint64_t addr, uint64_t len,
return result; return result;
} }
NamedMemoryRange range; auto range = rx::AddressRange::fromBeginSize(addr, len);
range.begin = addr & ~0x3fffull; if (auto error = vmem::setName(thread->tproc, range, _name);
range.end = range.begin; error != ErrorCode{}) {
range.end += ((addr & 0x3fff) + 0x3fff + len) & ~0x3fffull; ORBIS_LOG_ERROR(__FUNCTION__, "failure:", static_cast<int>(error));
std::lock_guard lock(thread->tproc->namedMemMutex);
auto [it, end] = thread->tproc->namedMem.equal_range<NamedMemoryRange>(range);
while (it != end) {
auto [addr2, end2] = it->first;
auto len2 = end2 - addr2;
ORBIS_LOG_NOTICE("sys_mname: removed overlapped", it->second, addr2, len2);
it = thread->tproc->namedMem.erase(it);
} }
if (!thread->tproc->namedMem.try_emplace(range, _name).second)
std::abort();
if (!thread->tproc->namedMem.count(addr))
std::abort();
return {}; return {};
} }
orbis::SysResult orbis::sys_dynlib_dlopen(Thread *thread /* TODO */) { orbis::SysResult orbis::sys_dynlib_dlopen(Thread *thread /* TODO */) {
@ -1019,7 +1037,7 @@ orbis::SysResult orbis::sys_dynlib_get_info(Thread *thread,
ModuleInfo result = {}; ModuleInfo result = {};
result.size = sizeof(ModuleInfo); result.size = sizeof(ModuleInfo);
std::strncpy(result.name, module->moduleName, sizeof(result.name)); std::strncpy(result.name, module->soName, sizeof(result.name));
std::memcpy(result.segments, module->segments, std::memcpy(result.segments, module->segments,
sizeof(ModuleSegment) * module->segmentCount); sizeof(ModuleSegment) * module->segmentCount);
result.segmentCount = module->segmentCount; result.segmentCount = module->segmentCount;
@ -1485,14 +1503,59 @@ orbis::SysResult orbis::sys_get_cpu_usage_all(Thread *thread, uint32_t unk,
ORBIS_LOG_TODO(__FUNCTION__, unk, result); ORBIS_LOG_TODO(__FUNCTION__, unk, result);
return {}; return {};
} }
orbis::SysResult orbis::sys_mmap_dmem(Thread *thread, caddr_t addr, size_t len, orbis::SysResult orbis::sys_mmap_dmem(Thread *thread, uintptr_t addr,
sint memoryType, sint prot, sint flags, size_t len, MemoryType memoryType,
rx::EnumBitSet<vmem::Protection> prot,
rx::EnumBitSet<vmem::MapFlags> flags,
off_t directMemoryStart) { off_t directMemoryStart) {
if (auto dmem_mmap = thread->tproc->ops->dmem_mmap) {
return dmem_mmap(thread, addr, len, memoryType, prot, flags, auto callerAddress = getCallerAddress(thread);
directMemoryStart); auto alignment = dmem::kPageSize;
{
auto unpacked = unpackMapFlags(flags, dmem::kPageSize);
alignment = unpacked.first;
flags = unpacked.second;
} }
return ErrorCode::NOSYS;
len = rx::alignUp(len, dmem::kPageSize);
rx::EnumBitSet<AllocationFlags> allocFlags{};
if (!prot) {
// HACK
// FIXME: implement protect for pid
prot = vmem::Protection::CpuRead | vmem::Protection::CpuWrite |
vmem::Protection::GpuRead | vmem::Protection::GpuWrite;
}
if (prot & vmem::Protection::CpuExec) {
return ErrorCode::INVAL;
}
if (flags & vmem::MapFlags::Fixed) {
allocFlags = AllocationFlags::Fixed;
} else if (addr == 0) {
addr = 0xfe0000000;
}
if (flags & vmem::MapFlags::NoOverwrite) {
allocFlags |= AllocationFlags::NoOverwrite;
}
auto name = callerAddress ? rx::format("anon:{:012x}", callerAddress) : "";
auto [range, errc] =
vmem::mapDirect(thread->tproc, addr,
rx::AddressRange::fromBeginSize(directMemoryStart, len),
prot, allocFlags, name, alignment, memoryType);
if (errc != ErrorCode{}) {
return errc;
}
thread->retval[0] = range.beginAddress();
return {};
} }
orbis::SysResult orbis::sys_physhm_open(Thread *thread /* TODO */) { orbis::SysResult orbis::sys_physhm_open(Thread *thread /* TODO */) {
return ErrorCode::NOSYS; return ErrorCode::NOSYS;
@ -1694,31 +1757,53 @@ orbis::SysResult orbis::sys_process_terminate(Thread *thread /* TODO */) {
return ErrorCode::NOSYS; return ErrorCode::NOSYS;
} }
orbis::SysResult orbis::sys_blockpool_open(Thread *thread) { orbis::SysResult orbis::sys_blockpool_open(Thread *thread) {
if (auto blockpool_open = thread->tproc->ops->blockpool_open) { if (!g_context->blockpool) {
rx::Ref<File> file; return ErrorCode::NOSYS;
auto result = blockpool_open(thread, &file); }
if (result.isError()) {
return result;
}
thread->retval[0] = thread->tproc->fileDescriptors.insert(file); auto fd = thread->tproc->fileDescriptors.insert(g_context->blockpool);
return {};
if (fd == thread->tproc->fileDescriptors.npos) {
return ErrorCode::MFILE;
} }
return ErrorCode::NOSYS;
thread->retval[0] = fd;
return {};
} }
orbis::SysResult orbis::sys_blockpool_map(Thread *thread, caddr_t addr, orbis::SysResult
size_t len, sint prot, sint flags) { orbis::sys_blockpool_map(Thread *thread, uintptr_t addr, size_t len,
if (auto blockpool_map = thread->tproc->ops->blockpool_map) { MemoryType type, rx::EnumBitSet<vmem::Protection> prot,
return blockpool_map(thread, addr, len, prot, flags); rx::EnumBitSet<vmem::MapFlags> flags) {
rx::EnumBitSet<AllocationFlags> allocFlags{};
if (flags & vmem::MapFlags::Fixed) {
allocFlags |= AllocationFlags::Fixed;
} }
return ErrorCode::NOSYS; if (flags & vmem::MapFlags::NoOverwrite) {
allocFlags |= AllocationFlags::NoOverwrite;
}
if (flags & vmem::MapFlags::NoCoalesce) {
allocFlags |= AllocationFlags::NoMerge;
}
auto [range, errc] = vmem::commitPooled(
thread->tproc, rx::AddressRange::fromBeginSize(addr, len), type, prot);
if (errc != ErrorCode{}) {
return errc;
}
thread->retval[0] = range.beginAddress();
return {};
} }
orbis::SysResult orbis::sys_blockpool_unmap(Thread *thread, caddr_t addr, orbis::SysResult orbis::sys_blockpool_unmap(Thread *thread, uintptr_t addr,
size_t len, sint flags) { size_t len, sint flags) {
if (auto blockpool_unmap = thread->tproc->ops->blockpool_unmap) { if (flags != 0) {
return blockpool_unmap(thread, addr, len); return ErrorCode::INVAL;
} }
return ErrorCode::NOSYS;
return vmem::decommitPooled(thread->tproc,
rx::AddressRange::fromBeginSize(addr, len));
} }
orbis::SysResult orbis::SysResult
orbis::sys_dynlib_get_info_for_libdbg(Thread *thread /* TODO */) { orbis::sys_dynlib_get_info_for_libdbg(Thread *thread /* TODO */) {

View file

@ -1,4 +1,5 @@
#include "KernelContext.hpp" #include "KernelContext.hpp"
#include "rx/print.hpp"
#include "sys/sysproto.hpp" #include "sys/sysproto.hpp"
#include "thread/Process.hpp" #include "thread/Process.hpp"
#include "thread/Thread.hpp" #include "thread/Thread.hpp"
@ -77,7 +78,7 @@ SysResult kern_sysctl(Thread *thread, ptr<sint> name, uint namelen,
enum sysctl_vm_budgets_ { enum sysctl_vm_budgets_ {
mlock_total = 1000, mlock_total = 1000,
mlock_avail = 1000, mlock_avail,
}; };
struct ProcInfo { struct ProcInfo {
@ -749,9 +750,8 @@ SysResult kern_sysctl(Thread *thread, ptr<sint> name, uint namelen,
return ErrorCode::INVAL; return ErrorCode::INVAL;
} }
std::printf("Reporting stack at %p\n", thread->stackEnd); rx::println(stderr, "Reporting stack at {:x}", thread->stackEnd);
*(ptr<void> *)old = thread->stackEnd; return uwrite(ptr<uint64_t>(old), thread->stackEnd);
return {};
} }
case sysctl_kern::smp_cpus: case sysctl_kern::smp_cpus:

View file

@ -1,8 +1,15 @@
#include "KernelContext.hpp"
#include "error.hpp" #include "error.hpp"
#include "rx/AddressRange.hpp"
#include "rx/align.hpp"
#include "rx/format.hpp"
#include "rx/print.hpp"
#include "sys/sysproto.hpp" #include "sys/sysproto.hpp"
#include "thread/Process.hpp" #include "thread/Process.hpp"
#include "thread/ProcessOps.hpp" #include "thread/ProcessOps.hpp"
#include "thread/Thread.hpp" #include "thread/Thread.hpp"
#include "utils/Logs.hpp"
#include "vmem.hpp"
orbis::SysResult orbis::sys_sbrk(Thread *, sint) { orbis::SysResult orbis::sys_sbrk(Thread *, sint) {
return ErrorCode::OPNOTSUPP; return ErrorCode::OPNOTSUPP;
@ -11,94 +18,188 @@ orbis::SysResult orbis::sys_sstk(Thread *, sint) {
return ErrorCode::OPNOTSUPP; return ErrorCode::OPNOTSUPP;
} }
orbis::SysResult orbis::sys_mmap(Thread *thread, caddr_t addr, size_t len, orbis::SysResult orbis::sys_mmap(Thread *thread, uintptr_t addr, size_t len,
sint prot, sint flags, sint fd, off_t pos) { rx::EnumBitSet<vmem::Protection> prot,
if (auto impl = thread->tproc->ops->mmap) { rx::EnumBitSet<vmem::MapFlags> flags, sint fd,
return impl(thread, addr, len, prot, flags, fd, pos); off_t pos) {
std::uint64_t callerAddress = getCallerAddress(thread);
auto shift = addr & (vmem::kPageSize - 1);
if ((flags & vmem::MapFlags::Fixed) && shift != 0) {
return ErrorCode::INVAL;
} }
return ErrorCode::NOSYS; if (len == 0) {
return ErrorCode::INVAL;
}
addr = rx::alignUp(addr, vmem::kPageSize);
rx::EnumBitSet<AllocationFlags> allocFlags{};
rx::EnumBitSet<vmem::BlockFlags> blockFlags{};
rx::EnumBitSet<vmem::BlockFlagsEx> blockFlagsEx{};
std::uint64_t alignment = vmem::kPageSize;
{
auto unpacked = unpackMapFlags(flags, vmem::kPageSize);
alignment = unpacked.first;
flags = unpacked.second;
}
len = rx::alignUp(len, vmem::kPageSize);
if (flags & vmem::MapFlags::Stack) {
allocFlags |= AllocationFlags::Stack;
}
if (flags & vmem::MapFlags::Fixed) {
allocFlags |= AllocationFlags::Fixed;
}
if (flags & vmem::MapFlags::NoOverwrite) {
allocFlags |= AllocationFlags::NoOverwrite;
}
if (flags & vmem::MapFlags::NoCoalesce) {
allocFlags |= AllocationFlags::NoMerge;
}
if (flags & vmem::MapFlags::Shared) {
blockFlagsEx |= vmem::BlockFlagsEx::Shared;
if (flags & vmem::MapFlags::Private) {
return ErrorCode::INVAL;
}
}
if (flags & vmem::MapFlags::Private) {
blockFlagsEx |= vmem::BlockFlagsEx::Private;
}
if (addr == 0) {
addr = flags & vmem::MapFlags::System ? 0xfc0000000 : 0x200000000;
}
if (flags & vmem::MapFlags::Void) {
flags |= vmem::MapFlags::Anon;
if (fd != -1 || pos != 0) {
return ErrorCode::INVAL;
}
}
if (flags & vmem::MapFlags::Stack) {
flags |= vmem::MapFlags::Anon;
blockFlags |= vmem::BlockFlags::Stack;
if (fd != -1 || pos != 0) {
return ErrorCode::INVAL;
}
if ((prot & (vmem::Protection::CpuRead | vmem::Protection::CpuWrite)) !=
(vmem::Protection::CpuRead | vmem::Protection::CpuWrite)) {
return ErrorCode::INVAL;
}
} else {
shift = 0;
}
auto name = callerAddress ? rx::format("anon:{:012x}", callerAddress) : "";
if (flags & vmem::MapFlags::Anon) {
if (fd != -1 || pos != 0) {
return ErrorCode::INVAL;
}
auto [range, errc] = vmem::mapFlex(thread->tproc, len, prot, addr,
allocFlags, blockFlags, name, alignment);
if (errc != orbis::ErrorCode{}) {
return errc;
}
thread->retval[0] = range.beginAddress() + shift;
return {};
}
auto file = thread->tproc->fileDescriptors.get(fd);
if (file == nullptr) {
return ErrorCode::BADF;
}
if (!file->device->blockFlags) {
blockFlags |= vmem::BlockFlags::FlexibleMemory;
}
auto [range, errc] =
vmem::mapFile(thread->tproc, addr, len, allocFlags, prot, blockFlags,
blockFlagsEx, file.get(), pos, name, alignment);
if (errc != ErrorCode{}) {
return errc;
}
thread->retval[0] = range.beginAddress() + shift;
return {};
} }
orbis::SysResult orbis::sys_freebsd6_mmap(Thread *thread, caddr_t addr, orbis::SysResult orbis::sys_freebsd6_mmap(Thread *thread, uintptr_t addr,
size_t len, sint prot, sint flags, size_t len,
rx::EnumBitSet<vmem::Protection> prot,
rx::EnumBitSet<vmem::MapFlags> flags,
sint fd, sint, off_t pos) { sint fd, sint, off_t pos) {
return sys_mmap(thread, addr, len, prot, flags, fd, pos); return sys_mmap(thread, addr, len, prot, flags, fd, pos);
} }
orbis::SysResult orbis::sys_msync(Thread *thread, ptr<void> addr, size_t len, orbis::SysResult orbis::sys_msync(Thread *thread, uintptr_t addr, size_t len,
sint flags) { sint flags) {
if (auto impl = thread->tproc->ops->msync) { ORBIS_LOG_TODO(__FUNCTION__, addr, len, flags);
return impl(thread, addr, len, flags); return {};
}
orbis::SysResult orbis::sys_munmap(Thread *thread, uintptr_t addr, size_t len) {
if (len == 0) {
return ErrorCode::INVAL;
} }
return ErrorCode::NOSYS; auto range = rx::AddressRange::fromBeginSize(addr, len);
}
orbis::SysResult orbis::sys_munmap(Thread *thread, ptr<void> addr, size_t len) {
if (auto impl = thread->tproc->ops->munmap) {
return impl(thread, addr, len);
}
return ErrorCode::NOSYS; return vmem::unmap(thread->tproc, range);
} }
orbis::SysResult orbis::sys_mprotect(Thread *thread, ptr<const void> addr, orbis::SysResult orbis::sys_mprotect(Thread *thread, uintptr_t addr, size_t len,
size_t len, sint prot) { rx::EnumBitSet<vmem::Protection> prot) {
if (auto impl = thread->tproc->ops->mprotect) { auto range = rx::AddressRange::fromBeginSize(addr, len);
return impl(thread, addr, len, prot); return vmem::protect(thread->tproc, range, prot);
}
return ErrorCode::NOSYS;
} }
orbis::SysResult orbis::sys_minherit(Thread *thread, ptr<void> addr, size_t len, orbis::SysResult orbis::sys_minherit(Thread *thread, uintptr_t addr, size_t len,
sint inherit) { sint inherit) {
if (auto impl = thread->tproc->ops->minherit) { ORBIS_LOG_TODO(__FUNCTION__, addr, len, inherit);
return impl(thread, addr, len, inherit);
}
return ErrorCode::NOSYS; return ErrorCode::NOSYS;
} }
orbis::SysResult orbis::sys_madvise(Thread *thread, ptr<void> addr, size_t len, orbis::SysResult orbis::sys_madvise(Thread *thread, uintptr_t addr, size_t len,
sint behav) { sint behav) {
if (auto impl = thread->tproc->ops->madvise) { ORBIS_LOG_TODO(__FUNCTION__, addr, len, behav);
return impl(thread, addr, len, behav);
}
return ErrorCode::NOSYS; return ErrorCode::NOSYS;
} }
orbis::SysResult orbis::sys_mincore(Thread *thread, ptr<const void> addr, orbis::SysResult orbis::sys_mincore(Thread *thread, uintptr_t addr, size_t len,
size_t len, ptr<char> vec) { ptr<char> vec) {
if (auto impl = thread->tproc->ops->mincore) { ORBIS_LOG_TODO(__FUNCTION__, addr, len, vec);
return impl(thread, addr, len, vec);
}
return ErrorCode::NOSYS; return ErrorCode::NOSYS;
} }
orbis::SysResult orbis::sys_mlock(Thread *thread, ptr<const void> addr, orbis::SysResult orbis::sys_mlock(Thread *thread, uintptr_t addr, size_t len) {
size_t len) { ORBIS_LOG_TODO(__FUNCTION__, addr, len);
if (auto impl = thread->tproc->ops->mlock) { return {};
return impl(thread, addr, len);
}
return ErrorCode::NOSYS;
} }
orbis::SysResult orbis::sys_mlockall(Thread *thread, sint how) { orbis::SysResult orbis::sys_mlockall(Thread *thread, sint how) {
if (auto impl = thread->tproc->ops->mlockall) { ORBIS_LOG_TODO(__FUNCTION__, how);
return impl(thread, how); return {};
}
return ErrorCode::NOSYS;
} }
orbis::SysResult orbis::sys_munlockall(Thread *thread) { orbis::SysResult orbis::sys_munlockall(Thread *thread) {
if (auto impl = thread->tproc->ops->munlockall) { ORBIS_LOG_TODO(__FUNCTION__);
return impl(thread); return {};
}
return ErrorCode::NOSYS;
} }
orbis::SysResult orbis::sys_munlock(Thread *thread, ptr<const void> addr, orbis::SysResult orbis::sys_munlock(Thread *thread, uintptr_t addr,
size_t len) { size_t len) {
if (auto impl = thread->tproc->ops->munlock) { ORBIS_LOG_TODO(__FUNCTION__, addr, len);
return impl(thread, addr, len); return {};
}
return ErrorCode::NOSYS;
} }

View file

@ -1,10 +1,15 @@
#include "orbis-config.hpp"
#include "rx/EnumBitSet.hpp"
#include "rx/format.hpp"
#include "sys/syscall.hpp" #include "sys/syscall.hpp"
#include "sys/sysentry.hpp" #include "sys/sysentry.hpp"
#include "sys/sysproto.hpp" #include "sys/sysproto.hpp"
#include "thread/Process.hpp" #include "thread/Process.hpp"
#include "thread/Thread.hpp" #include "thread/Thread.hpp"
#include <algorithm> #include <algorithm>
#include <type_traits>
#include <unordered_map> #include <unordered_map>
#include <utility>
enum { PSL_C = 0x1 }; enum { PSL_C = 0x1 };
@ -101,6 +106,72 @@ struct WrapImpl<Fn> {
sysent result; sysent result;
result.narg = sizeof...(Args); result.narg = sizeof...(Args);
result.call = &WrapImpl::call; result.call = &WrapImpl::call;
result.format = [](uint64_t *values) -> std::string {
std::string result = getSysentName(&WrapImpl::call);
result += "(";
auto formatArg =
[&]<std::size_t I, typename T>(std::integral_constant<std::size_t, I>,
T value, std::uint64_t raw) {
if (I != 0) {
result += ", ";
}
using type = std::remove_cvref_t<T>;
if constexpr (std::is_same_v<type, bool>) {
if (value) {
result += "true";
} else {
result += "false";
}
} else if constexpr (std::is_integral_v<type>) {
if (value > 9) {
result += rx::format("{:#x}", value);
} else {
result += rx::format("{}", value);
}
} else if constexpr (std::is_pointer_v<type>) {
result += rx::format("{}", (void *)value);
// using pointee = std::remove_cvref_t<std::remove_pointer_t<type>>;
// if constexpr (requires(rx::format_parse_context &ctx) {
// rx::formatter<pointee>().parse(ctx);
// }) {
// if (value) {
// pointee kernelValue;
// auto errc = orbis::uread(kernelValue, value);
// if (errc == ErrorCode{}) {
// result += rx::format("={}", kernelValue);
// } else {
// result += rx::format("={}", errc);
// }
// }
// }
} else if constexpr (requires(rx::format_parse_context &ctx) {
rx::formatter<type>().parse(ctx);
}) {
result += rx::format("{}", value);
} else {
if (raw > 9) {
result += rx::format("{:#x}", raw);
} else {
result += rx::format("{}", raw);
}
}
};
auto formatArgs = [&]<std::size_t... I>(std::index_sequence<I...>,
uint64_t *values) {
(formatArg(std::integral_constant<std::size_t, I>{},
makeArg<Args>(values[I]), values[I]),
...);
};
formatArgs(std::make_index_sequence<sizeof...(Args)>{}, values);
result += ")";
return result;
};
return result; return result;
} }
@ -113,7 +184,28 @@ private:
template <std::size_t... I> template <std::size_t... I>
static SysResult callImpl(Thread *thread, uint64_t *args, static SysResult callImpl(Thread *thread, uint64_t *args,
std::index_sequence<I...>) { std::index_sequence<I...>) {
return Fn(thread, Args(args[I])...); return Fn(thread, makeArg<Args>(args[I])...);
}
template <typename T>
static T makeArg(uint64_t raw)
requires requires { std::bit_cast<T>(raw); }
{
return std::bit_cast<T>(raw);
}
template <typename T>
static T makeArg(uint64_t raw)
requires requires { T(raw); } && (!requires { std::bit_cast<T>(raw); })
{
return T(raw);
}
template <typename T>
static T makeArg(uint64_t raw)
requires requires { T(T::fromUnderlying(raw)); }
{
return T::fromUnderlying(raw);
} }
}; };
} // namespace detail } // namespace detail

View file

@ -177,3 +177,9 @@ orbis::Process *orbis::createProcess(Process *parentProcess, pid_t pid) {
g_processList->list = result; g_processList->list = result;
return &result->object; return &result->object;
} }
orbis::Budget *orbis::Process::getBudget() const {
auto result = g_context->budgets.get(budgetId);
return result != nullptr ? result.get() : nullptr;
}

View file

@ -36,3 +36,35 @@ orbis::Thread *orbis::createThread(Process *process, std::string_view name) {
return result; return result;
} }
uintptr_t orbis::getCallerAddress(Thread *thread) {
if (!thread->context) {
return 0;
}
auto rbp = readRegister(thread->context, RegisterId::rbp);
std::uint64_t result = 0;
while (rbp < 0x8000'0000'0000) {
auto framePtr = std::bit_cast<ptr<uint64_t>>(rbp);
std::uint64_t nextFrame;
std::uint64_t retAddress;
if (orbis::uread(retAddress, framePtr + 1) != orbis::ErrorCode{}) {
break;
}
if (orbis::uread(nextFrame, framePtr) != orbis::ErrorCode{}) {
break;
}
if (!thread->tproc->libkernelRange.contains(retAddress)) {
result = retAddress;
break;
}
rbp = nextFrame;
}
return result;
}

File diff suppressed because it is too large Load diff

View file

@ -72,7 +72,6 @@ if(LINUX AND WITH_PS4)
main.cpp main.cpp
AudioOut.cpp AudioOut.cpp
backtrace.cpp backtrace.cpp
vm.cpp
ops.cpp ops.cpp
linker.cpp linker.cpp
io-device.cpp io-device.cpp
@ -81,7 +80,7 @@ if(LINUX AND WITH_PS4)
ipmi.cpp ipmi.cpp
) )
target_base_address(rpcsx 0x0000070000000000) target_base_address(rpcsx 0x00000700000000000)
target_compile_options(rpcsx PRIVATE "-mfsgsbase") target_compile_options(rpcsx PRIVATE "-mfsgsbase")
set_target_properties(rpcsx PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set_target_properties(rpcsx PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
install(TARGETS rpcsx RUNTIME DESTINATION bin) install(TARGETS rpcsx RUNTIME DESTINATION bin)

View file

@ -7,6 +7,8 @@
#include "orbis-config.hpp" #include "orbis-config.hpp"
#include "orbis/KernelContext.hpp" #include "orbis/KernelContext.hpp"
#include "orbis/note.hpp" #include "orbis/note.hpp"
#include "orbis/pmem.hpp"
#include "orbis/vmem.hpp"
#include "rx/AddressRange.hpp" #include "rx/AddressRange.hpp"
#include "rx/Config.hpp" #include "rx/Config.hpp"
#include "rx/bits.hpp" #include "rx/bits.hpp"
@ -66,11 +68,11 @@ static vk::Context createVkContext(Device *device) {
bool enableValidation = rx::g_config.validateGpu; bool enableValidation = rx::g_config.validateGpu;
for (std::size_t process = 0; process < 6; ++process) { for (std::size_t process = 0; process < 6; ++process) {
if (auto errc = rx::mem::reserve(rx::AddressRange::fromBeginSize( auto range = rx::AddressRange::fromBeginSize(
orbis::kMinAddress + orbis::kMaxAddress * process, 0x40'0000 + 0x100'0000'0000 * process, 0x100'0000'0000 - 0x40'0000);
orbis::kMaxAddress - orbis::kMinAddress)); if (auto errc = rx::mem::reserve(range); errc != std::errc{}) {
errc != std::errc{}) { rx::die("failed to reserve userspace memory: {} {:x}-{:x}", (int)errc,
rx::die("failed to reserve userspace memory: {}", (int)errc); range.beginAddress(), range.endAddress());
} }
} }
@ -135,8 +137,8 @@ static vk::Context createVkContext(Device *device) {
&device->debugMessenger)); &device->debugMessenger));
} }
glfwCreateWindowSurface(vk::context->instance, device->window, nullptr, VK_VERIFY(glfwCreateWindowSurface(vk::context->instance, device->window,
&device->surface); nullptr, &device->surface));
result.createDevice(device->surface, rx::g_config.gpuIndex, result.createDevice(device->surface, rx::g_config.gpuIndex,
{ {
@ -293,18 +295,6 @@ Device::~Device() {
vk::context->allocator); vk::context->allocator);
} }
for (auto fd : dmemFd) {
if (fd >= 0) {
::close(fd);
}
}
for (auto &[pid, info] : processInfo) {
if (info.vmFd >= 0) {
::close(info.vmFd);
}
}
for (auto &cachePage : cachePages) { for (auto &cachePage : cachePages) {
orbis::kfree(cachePage, kCachePageSize); orbis::kfree(cachePage, kCachePageSize);
} }
@ -321,28 +311,6 @@ void Device::start() {
}); });
} }
for (std::size_t i = 0; i < std::size(dmemFd); ++i) {
if (dmemFd[i] != -1) {
continue;
}
auto path = rx::format("{}/dmem-{}", rx::getShmPath(), i);
if (!std::filesystem::exists(path)) {
std::println("Waiting for dmem {}", i);
while (!std::filesystem::exists(path)) {
std::this_thread::sleep_for(std::chrono::milliseconds(300));
}
}
dmemFd[i] = ::open(path.c_str(), O_RDWR, S_IRUSR | S_IWUSR);
if (dmemFd[i] < 0) {
std::println(stderr, "failed to open dmem {}", path);
std::abort();
}
}
std::jthread vblankThread([](const std::stop_token &stopToken) { std::jthread vblankThread([](const std::stop_token &stopToken) {
orbis::g_context->deviceEventEmitter->emit( orbis::g_context->deviceEventEmitter->emit(
orbis::kEvFiltDisplay, orbis::kEvFiltDisplay,
@ -573,45 +541,27 @@ void Device::mapProcess(std::uint32_t pid, int vmId) {
auto memory = amdgpu::RemoteMemory{vmId}; auto memory = amdgpu::RemoteMemory{vmId};
std::string pidVmName = rx::format("{}/memory-{}", rx::getShmPath(), pid);
int memoryFd = ::open(pidVmName.c_str(), O_RDWR, S_IRUSR | S_IWUSR);
process.vmFd = memoryFd;
if (memoryFd < 0) {
std::println("failed to open shared memory of process {}", (int)pid);
std::abort();
}
for (auto slot : process.vmTable) { for (auto slot : process.vmTable) {
auto gpuProt = slot->prot >> 4; auto gpuProt = orbis::vmem::toGpuProtection(slot->prot);
if (gpuProt == 0) { if (!gpuProt) {
continue; continue;
} }
auto devOffset = slot->offset + slot.beginAddress() - slot->baseAddress; auto devOffset = slot->offset + slot.beginAddress() - slot->baseAddress;
int mapFd = memoryFd;
if (slot->memoryType >= 0) { auto errc = orbis::pmem::map(
mapFd = dmemFd[slot->memoryType]; memory.getVirtualAddress(slot.beginAddress()),
} rx::AddressRange::fromBeginSize(devOffset, slot.size()), gpuProt);
rx::dieIf(
errc != orbis::ErrorCode{},
"failed to map process {} memory, address {}-{}, type {}, vmId {}",
(int)pid, memory.getPointer(slot.beginAddress()),
memory.getPointer(slot.endAddress()), slot->memoryType, vmId);
auto mmapResult = std::println(stderr, "map process {} memory, address {}-{}, vmId {}",
::mmap(memory.getPointer(slot.beginAddress()), slot.size(), gpuProt, (int)pid, memory.getVirtualAddress(slot.beginAddress()),
MAP_FIXED | MAP_SHARED, mapFd, devOffset); memory.getVirtualAddress(slot.beginAddress()) + slot.size(),
vmId);
if (mmapResult == MAP_FAILED) {
std::println(
stderr,
"failed to map process {} memory, address {}-{}, type {:x}, vmId {}",
(int)pid, memory.getPointer(slot.beginAddress()),
memory.getPointer(slot.endAddress()), slot->memoryType, vmId);
std::abort();
}
// std::println(stderr,
// "map process {} memory, address {}-{}, type {:x}, vmId {}",
// (int)pid, memory.getPointer(startAddress),
// memory.getPointer(endAddress), slot.memoryType, vmId);
} }
} }
@ -629,13 +579,16 @@ void Device::unmapProcess(std::uint32_t pid) {
rx::die("failed to release userspace memory: {}", (int)errc); rx::die("failed to release userspace memory: {}", (int)errc);
} }
::close(process.vmFd);
process.vmFd = -1;
process.vmId = -1; process.vmId = -1;
} }
void Device::protectMemory(std::uint32_t pid, std::uint64_t address, void Device::protectMemory(std::uint32_t pid, std::uint64_t address,
std::uint64_t size, int prot) { std::uint64_t size,
rx::EnumBitSet<orbis::vmem::Protection> prot) {
if (address + size > 0x100'0000'0000) {
return;
}
auto &process = processInfo[pid]; auto &process = processInfo[pid];
auto vmSlotIt = process.vmTable.queryArea(address); auto vmSlotIt = process.vmTable.queryArea(address);
@ -648,22 +601,20 @@ void Device::protectMemory(std::uint32_t pid, std::uint64_t address,
process.vmTable.map(rx::AddressRange::fromBeginSize(address, size), process.vmTable.map(rx::AddressRange::fromBeginSize(address, size),
VmMapSlot{ VmMapSlot{
.memoryType = vmSlot.memoryType, .memoryType = vmSlot.memoryType,
.prot = static_cast<int>(prot), .prot = prot,
.offset = vmSlot.offset, .offset = vmSlot.offset,
.baseAddress = vmSlot.baseAddress, .baseAddress = vmSlot.baseAddress,
}); });
if (process.vmId >= 0) { if (process.vmId >= 0) {
auto memory = amdgpu::RemoteMemory{process.vmId}; auto memory = amdgpu::RemoteMemory{process.vmId};
rx::mem::protect( rx::mem::protect(rx::AddressRange::fromBeginSize(
rx::AddressRange::fromBeginSize(memory.getVirtualAddress(address), memory.getVirtualAddress(address), size),
size), orbis::vmem::toGpuProtection(prot));
rx::EnumBitSet<rx::mem::Protection>::fromUnderlying(prot >> 4));
// std::println(stderr, "protect process {} memory, address {}-{}, prot std::println(stderr, "protect process {} memory, address {}-{}, prot {}",
// {:x}", (int)pid, memory.getPointer(address),
// (int)pid, memory.getPointer(address), memory.getPointer(address + size), prot);
// memory.getPointer(address + size), prot);
} }
} }
@ -1006,17 +957,22 @@ void Device::waitForIdle() {
} }
} }
void Device::mapMemory(std::uint32_t pid, std::uint64_t address, void Device::mapMemory(std::uint32_t pid, rx::AddressRange virtualRange,
std::uint64_t size, int memoryType, int dmemIndex, orbis::MemoryType memoryType,
int prot, std::int64_t offset) { rx::EnumBitSet<orbis::vmem::Protection> prot,
std::uint64_t physicalOffset) {
if (virtualRange.endAddress() > 0x100'0000'0000) {
return;
}
auto &process = processInfo[pid]; auto &process = processInfo[pid];
process.vmTable.map(rx::AddressRange::fromBeginSize(address, size), process.vmTable.map(virtualRange,
VmMapSlot{ VmMapSlot{
.memoryType = memoryType >= 0 ? dmemIndex : -1, .memoryType = memoryType,
.prot = prot, .prot = prot,
.offset = offset, .offset = physicalOffset,
.baseAddress = address, .baseAddress = virtualRange.beginAddress(),
}); });
if (process.vmId < 0) { if (process.vmId < 0) {
@ -1025,33 +981,29 @@ void Device::mapMemory(std::uint32_t pid, std::uint64_t address,
auto memory = amdgpu::RemoteMemory{process.vmId}; auto memory = amdgpu::RemoteMemory{process.vmId};
int mapFd = process.vmFd; auto vmemAddress = memory.getVirtualAddress(virtualRange.beginAddress());
auto errc = orbis::pmem::map(vmemAddress,
if (memoryType >= 0) { rx::AddressRange::fromBeginSize(
mapFd = dmemFd[dmemIndex]; physicalOffset, virtualRange.beginAddress()),
orbis::vmem::toGpuProtection(prot));
if (errc != orbis::ErrorCode{}) {
rx::die("failed to map process {} memory, address {:x}-{:x}, type {}, "
"offset {:x}, prot {}, error {}",
pid, vmemAddress, vmemAddress + virtualRange.size(), memoryType,
physicalOffset, prot, errc);
} }
auto mmapResult = ::mmap(memory.getPointer(address), size, prot >> 4, std::println(
MAP_FIXED | MAP_SHARED, mapFd, offset); stderr,
"map memory of process {}, address {:x}-{:x}, prot {}, phy memory {:x}",
if (mmapResult == MAP_FAILED) { (int)pid, vmemAddress, vmemAddress + virtualRange.size(), prot,
perror("::mmap"); physicalOffset);
rx::die("failed to map process {} memory, address {}-{}, type {:x}, offset "
"{:x}, prot {:x}",
pid, memory.getPointer(address), memory.getPointer(address + size),
memoryType, offset, prot);
}
// std::println(stderr, "map memory of process {}, address {}-{}, prot {:x}",
// (int)pid, memory.getPointer(address),
// memory.getPointer(address + size), prot);
} }
void Device::unmapMemory(std::uint32_t pid, std::uint64_t address, void Device::unmapMemory(std::uint32_t pid, std::uint64_t address,
std::uint64_t size) { std::uint64_t size) {
// TODO // TODO
protectMemory(pid, address, size, 0); protectMemory(pid, address, size, {});
} }
static void notifyPageChanges(Device *device, int vmId, std::uint32_t firstPage, static void notifyPageChanges(Device *device, int vmId, std::uint32_t firstPage,

View file

@ -5,6 +5,11 @@
#include "Pipe.hpp" #include "Pipe.hpp"
#include "amdgpu/tiler_vulkan.hpp" #include "amdgpu/tiler_vulkan.hpp"
#include "orbis/KernelAllocator.hpp" #include "orbis/KernelAllocator.hpp"
#include "orbis/dmem.hpp"
#include "orbis/thread/Process.hpp"
#include "orbis/vmem.hpp"
#include "rx/AddressRange.hpp"
#include "rx/EnumBitSet.hpp"
#include "rx/MemoryTable.hpp" #include "rx/MemoryTable.hpp"
#include "rx/Rc.hpp" #include "rx/Rc.hpp"
#include "rx/SharedMutex.hpp" #include "rx/SharedMutex.hpp"
@ -37,9 +42,9 @@ std::array<std::uint32_t, sizeof...(T) + 1> createPm4Packet(std::uint32_t op,
} }
struct VmMapSlot { struct VmMapSlot {
int memoryType; orbis::MemoryType memoryType;
int prot; rx::EnumBitSet<orbis::vmem::Protection> prot;
std::int64_t offset; std::uint64_t offset;
std::uint64_t baseAddress; std::uint64_t baseAddress;
auto operator<=>(const VmMapSlot &) const = default; auto operator<=>(const VmMapSlot &) const = default;
@ -47,7 +52,6 @@ struct VmMapSlot {
struct ProcessInfo { struct ProcessInfo {
int vmId = -1; int vmId = -1;
int vmFd = -1;
BufferAttribute bufferAttributes[10]; BufferAttribute bufferAttributes[10];
Buffer buffers[10]; Buffer buffers[10];
rx::MemoryTableWithPayload<VmMapSlot> vmTable; rx::MemoryTableWithPayload<VmMapSlot> vmTable;
@ -90,7 +94,6 @@ struct Device : rx::RcBase, DeviceContext {
std::jthread cacheUpdateThread; std::jthread cacheUpdateThread;
int dmemFd[3] = {-1, -1, -1};
orbis::kmap<std::int32_t, ProcessInfo> processInfo; orbis::kmap<std::int32_t, ProcessInfo> processInfo;
Cache caches[kMaxProcessCount]{ Cache caches[kMaxProcessCount]{
@ -123,7 +126,8 @@ struct Device : rx::RcBase, DeviceContext {
void mapProcess(std::uint32_t pid, int vmId); void mapProcess(std::uint32_t pid, int vmId);
void unmapProcess(std::uint32_t pid); void unmapProcess(std::uint32_t pid);
void protectMemory(std::uint32_t pid, std::uint64_t address, void protectMemory(std::uint32_t pid, std::uint64_t address,
std::uint64_t size, int prot); std::uint64_t size,
rx::EnumBitSet<orbis::vmem::Protection> prot);
void onCommandBuffer(std::uint32_t pid, int cmdHeader, std::uint64_t address, void onCommandBuffer(std::uint32_t pid, int cmdHeader, std::uint64_t address,
std::uint64_t size); std::uint64_t size);
bool processPipes(); bool processPipes();
@ -131,8 +135,10 @@ struct Device : rx::RcBase, DeviceContext {
VkImage swapchainImage, VkImageView swapchainImageView); VkImage swapchainImage, VkImageView swapchainImageView);
void flip(std::uint32_t pid, int bufferIndex, std::uint64_t arg); void flip(std::uint32_t pid, int bufferIndex, std::uint64_t arg);
void waitForIdle(); void waitForIdle();
void mapMemory(std::uint32_t pid, std::uint64_t address, std::uint64_t size, void mapMemory(std::uint32_t pid, rx::AddressRange virtualRange,
int memoryType, int dmemIndex, int prot, std::int64_t offset); orbis::MemoryType memoryType,
rx::EnumBitSet<orbis::vmem::Protection> prot,
std::uint64_t physicalOffset);
void unmapMemory(std::uint32_t pid, std::uint64_t address, void unmapMemory(std::uint32_t pid, std::uint64_t address,
std::uint64_t size); std::uint64_t size);
void watchWrites(int vmId, std::uint64_t address, std::uint64_t size); void watchWrites(int vmId, std::uint64_t address, std::uint64_t size);

View file

@ -1,6 +1,7 @@
#include "DeviceCtl.hpp" #include "DeviceCtl.hpp"
#include "Device.hpp" #include "Device.hpp"
#include "gnm/pm4.hpp" #include "gnm/pm4.hpp"
#include "orbis/KernelContext.hpp"
#include "orbis/error/ErrorCode.hpp" #include "orbis/error/ErrorCode.hpp"
#include "rx/bits.hpp" #include "rx/bits.hpp"
#include "rx/die.hpp" #include "rx/die.hpp"
@ -230,3 +231,26 @@ void DeviceCtl::submitComputeQueue(std::uint32_t meId, std::uint32_t pipeId,
void DeviceCtl::start() { mDevice->start(); } void DeviceCtl::start() { mDevice->start(); }
void DeviceCtl::waitForIdle() { mDevice->waitForIdle(); } void DeviceCtl::waitForIdle() { mDevice->waitForIdle(); }
void amdgpu::mapMemory(std::uint32_t pid, rx::AddressRange virtualRange,
orbis::MemoryType memoryType,
rx::EnumBitSet<orbis::vmem::Protection> prot,
std::uint64_t offset) {
if (auto gpu = DeviceCtl{orbis::g_context->gpuDevice.rawCast<Device>()}) {
gpu.submitMapMemory(pid, virtualRange.beginAddress(), virtualRange.size(),
(int)memoryType, 0, prot.toUnderlying(), offset);
}
}
void amdgpu::unmapMemory(std::uint32_t pid, rx::AddressRange virtualRange) {
if (auto gpu = DeviceCtl{orbis::g_context->gpuDevice.rawCast<Device>()}) {
gpu.submitUnmapMemory(pid, virtualRange.beginAddress(),
virtualRange.size());
}
}
void amdgpu::protectMemory(std::uint32_t pid, rx::AddressRange virtualRange,
rx::EnumBitSet<orbis::vmem::Protection> prot) {
if (auto gpu = DeviceCtl{orbis::g_context->gpuDevice.rawCast<Device>()}) {
gpu.submitProtectMemory(pid, virtualRange.beginAddress(),
virtualRange.size(), prot.toUnderlying());
}
}

View file

@ -2,6 +2,10 @@
#include "DeviceContext.hpp" #include "DeviceContext.hpp"
#include "orbis-config.hpp" #include "orbis-config.hpp"
#include "orbis/dmem.hpp"
#include "orbis/vmem.hpp"
#include "rx/AddressRange.hpp"
#include "rx/EnumBitSet.hpp"
#include "rx/Rc.hpp" #include "rx/Rc.hpp"
#include <cstdint> #include <cstdint>
#include <span> #include <span>
@ -58,4 +62,12 @@ public:
explicit operator bool() const { return mDevice != nullptr; } explicit operator bool() const { return mDevice != nullptr; }
}; };
void mapMemory(std::uint32_t pid, rx::AddressRange virtualRange,
orbis::MemoryType memoryType,
rx::EnumBitSet<orbis::vmem::Protection> prot,
std::uint64_t offset);
void unmapMemory(std::uint32_t pid, rx::AddressRange virtualRange);
void protectMemory(std::uint32_t pid, rx::AddressRange virtualRange,
rx::EnumBitSet<orbis::vmem::Protection> prot);
} // namespace amdgpu } // namespace amdgpu

View file

@ -5,6 +5,9 @@
#include "gnm/mmio.hpp" #include "gnm/mmio.hpp"
#include "gnm/pm4.hpp" #include "gnm/pm4.hpp"
#include "orbis/KernelContext.hpp" #include "orbis/KernelContext.hpp"
#include "orbis/vmem.hpp"
#include "rx/AddressRange.hpp"
#include "rx/EnumBitSet.hpp"
#include "rx/print.hpp" #include "rx/print.hpp"
#include "vk.hpp" #include "vk.hpp"
#include <bit> #include <bit>
@ -2221,9 +2224,10 @@ void CommandPipe::mapMemory(Ring &ring) {
auto addressHi = ring.rptr[3]; auto addressHi = ring.rptr[3];
auto sizeLo = ring.rptr[4]; auto sizeLo = ring.rptr[4];
auto sizeHi = ring.rptr[5]; auto sizeHi = ring.rptr[5];
auto memoryType = ring.rptr[6]; auto memoryType = orbis::MemoryType(ring.rptr[6]);
auto dmemIndex = ring.rptr[7]; // auto dmemIndex = ring.rptr[7];
auto prot = ring.rptr[8]; auto prot =
rx::EnumBitSet<orbis::vmem::Protection>::fromUnderlying(ring.rptr[8]);
auto offsetLo = ring.rptr[9]; auto offsetLo = ring.rptr[9];
auto offsetHi = ring.rptr[10]; auto offsetHi = ring.rptr[10];
@ -2231,7 +2235,8 @@ void CommandPipe::mapMemory(Ring &ring) {
auto size = sizeLo | (static_cast<std::uint64_t>(sizeHi) << 32); auto size = sizeLo | (static_cast<std::uint64_t>(sizeHi) << 32);
auto offset = offsetLo | (static_cast<std::uint64_t>(offsetHi) << 32); auto offset = offsetLo | (static_cast<std::uint64_t>(offsetHi) << 32);
device->mapMemory(pid, address, size, memoryType, dmemIndex, prot, offset); device->mapMemory(pid, rx::AddressRange::fromBeginSize(address, size),
memoryType, prot, offset);
} }
void CommandPipe::unmapMemory(Ring &ring) { void CommandPipe::unmapMemory(Ring &ring) {
auto pid = ring.rptr[1]; auto pid = ring.rptr[1];
@ -2250,7 +2255,8 @@ void CommandPipe::protectMemory(Ring &ring) {
auto addressHi = ring.rptr[3]; auto addressHi = ring.rptr[3];
auto sizeLo = ring.rptr[4]; auto sizeLo = ring.rptr[4];
auto sizeHi = ring.rptr[5]; auto sizeHi = ring.rptr[5];
auto prot = ring.rptr[6]; auto prot =
rx::EnumBitSet<orbis::vmem::Protection>::fromUnderlying(ring.rptr[6]);
auto address = addressLo | (static_cast<std::uint64_t>(addressHi) << 32); auto address = addressLo | (static_cast<std::uint64_t>(addressHi) << 32);
auto size = sizeLo | (static_cast<std::uint64_t>(sizeHi) << 32); auto size = sizeLo | (static_cast<std::uint64_t>(sizeHi) << 32);

View file

@ -1,14 +1,16 @@
#include "io-device.hpp" #include "io-device.hpp"
#include "orbis/KernelAllocator.hpp" #include "orbis/KernelAllocator.hpp"
#include "orbis/SocketAddress.hpp" #include "orbis/SocketAddress.hpp"
#include "orbis/error.hpp"
#include "orbis/file.hpp" #include "orbis/file.hpp"
#include "orbis/stat.hpp" #include "orbis/stat.hpp"
#include "orbis/thread/Process.hpp" #include "orbis/thread/Process.hpp"
#include "orbis/thread/Thread.hpp" #include "orbis/thread/Thread.hpp"
#include "orbis/uio.hpp" #include "orbis/uio.hpp"
#include "orbis/utils/Logs.hpp" #include "orbis/utils/Logs.hpp"
#include "orbis/vmem.hpp"
#include "rx/Mappable.hpp"
#include "vfs.hpp" #include "vfs.hpp"
#include "vm.hpp"
#include <cerrno> #include <cerrno>
#include <dirent.h> #include <dirent.h>
#include <fcntl.h> #include <fcntl.h>
@ -33,8 +35,8 @@ struct HostFile : orbis::File {
bool alignTruncate = false; bool alignTruncate = false;
~HostFile() { ~HostFile() {
if (hostFd > 0 && closeOnExit) { if (!closeOnExit && hostFd) {
::close(hostFd); static_cast<void>(hostFd.release());
} }
} }
}; };
@ -46,12 +48,6 @@ struct SocketFile : orbis::File {
int prot = -1; int prot = -1;
orbis::kmap<int, orbis::kvector<std::byte>> options; orbis::kmap<int, orbis::kvector<std::byte>> options;
~SocketFile() {
if (hostFd > 0) {
::close(hostFd);
}
}
}; };
static orbis::ErrorCode convertErrc(std::errc errc) { static orbis::ErrorCode convertErrc(std::errc errc) {
@ -319,7 +315,7 @@ static orbis::ErrorCode host_read(orbis::File *file, orbis::Uio *uio,
if (!hostFile->dirEntries.empty()) if (!hostFile->dirEntries.empty())
return orbis::ErrorCode::ISDIR; return orbis::ErrorCode::ISDIR;
return host_fd_read(hostFile->hostFd, uio); return host_fd_read(hostFile->hostFd.native_handle(), uio);
} }
static orbis::ErrorCode host_write(orbis::File *file, orbis::Uio *uio, static orbis::ErrorCode host_write(orbis::File *file, orbis::Uio *uio,
@ -328,73 +324,14 @@ static orbis::ErrorCode host_write(orbis::File *file, orbis::Uio *uio,
if (!hostFile->dirEntries.empty()) if (!hostFile->dirEntries.empty())
return orbis::ErrorCode::ISDIR; return orbis::ErrorCode::ISDIR;
return host_fd_write(hostFile->hostFd, uio); return host_fd_write(hostFile->hostFd.native_handle(), uio);
}
static orbis::ErrorCode host_mmap(orbis::File *file, void **address,
std::uint64_t size, std::int32_t prot,
std::int32_t flags, std::int64_t offset,
orbis::Thread *thread) {
auto hostFile = static_cast<HostFile *>(file);
if (!hostFile->dirEntries.empty())
return orbis::ErrorCode::ISDIR;
auto result = vm::map(*address, size, prot, flags,
vm::kMapInternalReserveOnly, hostFile->device.get(), offset);
if (result == (void *)-1) {
return orbis::ErrorCode::NOMEM;
}
size = rx::alignUp(size, vm::kPageSize);
result =
::mmap(result, size, prot & vm::kMapProtCpuAll,
((flags & vm::kMapFlagPrivate) != 0 ? MAP_PRIVATE : MAP_SHARED) |
MAP_FIXED,
hostFile->hostFd, offset);
if (result == (void *)-1) {
auto errc = convertErrno();
std::printf("Failed to map file at %p-%p\n", *address,
(char *)*address + size);
return errc;
}
std::printf("file mapped at %p-%p:%lx\n", result, (char *)result + size,
offset);
struct stat stat;
fstat(hostFile->hostFd, &stat);
if (stat.st_size < offset + size) {
std::size_t rest = std::min(offset + size - stat.st_size, vm::kPageSize);
if (rest > rx::mem::pageSize) {
auto fillSize = rx::alignUp(rest, rx::mem::pageSize) - rx::mem::pageSize;
std::printf("adding dummy mapping %p-%p, file ends at %p\n",
(char *)result + size - fillSize, (char *)result + size,
(char *)result + (stat.st_size - offset));
auto ptr = ::mmap((char *)result + size - fillSize, fillSize,
prot & vm::kMapProtCpuAll,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
if (ptr == (void *)-1) {
std::printf("failed to add dummy mapping %p-%p\n", result,
(char *)result + size);
}
}
}
*address = result;
return {};
} }
static orbis::ErrorCode host_stat(orbis::File *file, orbis::Stat *sb, static orbis::ErrorCode host_stat(orbis::File *file, orbis::Stat *sb,
orbis::Thread *thread) { orbis::Thread *thread) {
auto hostFile = static_cast<HostFile *>(file); auto hostFile = static_cast<HostFile *>(file);
struct stat hostStat; struct stat hostStat;
::fstat(hostFile->hostFd, &hostStat); ::fstat(hostFile->hostFd.native_handle(), &hostStat);
sb->dev = hostStat.st_dev; // TODO sb->dev = hostStat.st_dev; // TODO
sb->ino = hostStat.st_ino; sb->ino = hostStat.st_ino;
sb->mode = hostStat.st_mode; sb->mode = hostStat.st_mode;
@ -437,10 +374,10 @@ static orbis::ErrorCode host_truncate(orbis::File *file, std::uint64_t len,
} }
if (hostFile->alignTruncate) { if (hostFile->alignTruncate) {
len = rx::alignUp(len, vm::kPageSize); len = rx::alignUp(len, orbis::vmem::kPageSize);
} }
if (::ftruncate(hostFile->hostFd, len)) { if (::ftruncate(hostFile->hostFd.native_handle(), len)) {
return convertErrno(); return convertErrno();
} }
@ -458,7 +395,7 @@ static orbis::ErrorCode socket_read(orbis::File *file, orbis::Uio *uio,
orbis::Thread *) { orbis::Thread *) {
auto socket = static_cast<SocketFile *>(file); auto socket = static_cast<SocketFile *>(file);
if (socket->hostFd < 0) { if (!socket->hostFd) {
while (true) { while (true) {
std::this_thread::sleep_for(std::chrono::days(1)); std::this_thread::sleep_for(std::chrono::days(1));
} }
@ -468,14 +405,14 @@ static orbis::ErrorCode socket_read(orbis::File *file, orbis::Uio *uio,
if (uio->iov->len) { if (uio->iov->len) {
ORBIS_LOG_FATAL(__FUNCTION__, file, uio->iov->len); ORBIS_LOG_FATAL(__FUNCTION__, file, uio->iov->len);
} }
return host_fd_read(socket->hostFd, uio); return host_fd_read(socket->hostFd.native_handle(), uio);
} }
static orbis::ErrorCode socket_write(orbis::File *file, orbis::Uio *uio, static orbis::ErrorCode socket_write(orbis::File *file, orbis::Uio *uio,
orbis::Thread *) { orbis::Thread *) {
auto socket = static_cast<SocketFile *>(file); auto socket = static_cast<SocketFile *>(file);
if (socket->hostFd < 0) { if (!socket->hostFd) {
for (auto io : std::span(uio->iov, uio->iovcnt)) { for (auto io : std::span(uio->iov, uio->iovcnt)) {
uio->offset += io.len; uio->offset += io.len;
} }
@ -483,7 +420,7 @@ static orbis::ErrorCode socket_write(orbis::File *file, orbis::Uio *uio,
} }
ORBIS_LOG_FATAL(__FUNCTION__, file, uio->iov->len); ORBIS_LOG_FATAL(__FUNCTION__, file, uio->iov->len);
return host_fd_write(socket->hostFd, uio); return host_fd_write(socket->hostFd.native_handle(), uio);
} }
static orbis::ErrorCode socket_bind(orbis::File *file, static orbis::ErrorCode socket_bind(orbis::File *file,
@ -492,7 +429,7 @@ static orbis::ErrorCode socket_bind(orbis::File *file,
orbis::Thread *thread) { orbis::Thread *thread) {
auto socket = static_cast<SocketFile *>(file); auto socket = static_cast<SocketFile *>(file);
if (socket->hostFd < 0) { if (!socket->hostFd) {
return {}; return {};
} }
@ -512,8 +449,8 @@ static orbis::ErrorCode socket_bind(orbis::File *file,
sockaddr_un un{.sun_family = AF_UNIX}; sockaddr_un un{.sun_family = AF_UNIX};
std::strncpy(un.sun_path, socketPath.c_str(), sizeof(un.sun_path)); std::strncpy(un.sun_path, socketPath.c_str(), sizeof(un.sun_path));
if (::bind(socket->hostFd, reinterpret_cast<::sockaddr *>(&un), if (::bind(socket->hostFd.native_handle(),
sizeof(un)) < 0) { reinterpret_cast<::sockaddr *>(&un), sizeof(un)) < 0) {
return convertErrno(); return convertErrno();
} }
@ -528,11 +465,11 @@ static orbis::ErrorCode socket_listen(orbis::File *file, int backlog,
orbis::Thread *thread) { orbis::Thread *thread) {
auto socket = static_cast<SocketFile *>(file); auto socket = static_cast<SocketFile *>(file);
if (socket->hostFd < 0) { if (!socket->hostFd) {
return {}; return {};
} }
if (::listen(socket->hostFd, backlog) < 0) { if (::listen(socket->hostFd.native_handle(), backlog) < 0) {
return convertErrno(); return convertErrno();
} }
@ -545,7 +482,7 @@ static orbis::ErrorCode socket_accept(orbis::File *file,
orbis::Thread *thread) { orbis::Thread *thread) {
auto socket = static_cast<SocketFile *>(file); auto socket = static_cast<SocketFile *>(file);
if (socket->hostFd < 0) { if (!socket->hostFd) {
ORBIS_LOG_ERROR(__FUNCTION__, socket->name, "wait forever"); ORBIS_LOG_ERROR(__FUNCTION__, socket->name, "wait forever");
while (true) { while (true) {
@ -558,8 +495,8 @@ static orbis::ErrorCode socket_accept(orbis::File *file,
if (socket->dom == 1 && socket->type == 1 && socket->prot == 0) { if (socket->dom == 1 && socket->type == 1 && socket->prot == 0) {
sockaddr_un un{.sun_family = AF_UNIX}; sockaddr_un un{.sun_family = AF_UNIX};
socklen_t len = sizeof(un); socklen_t len = sizeof(un);
int result = int result = ::accept(socket->hostFd.native_handle(),
::accept(socket->hostFd, reinterpret_cast<sockaddr *>(&un), &len); reinterpret_cast<sockaddr *>(&un), &len);
if (result < 0) { if (result < 0) {
return convertErrno(); return convertErrno();
@ -585,7 +522,7 @@ static orbis::ErrorCode socket_connect(orbis::File *file,
orbis::Thread *thread) { orbis::Thread *thread) {
auto socket = static_cast<SocketFile *>(file); auto socket = static_cast<SocketFile *>(file);
if (socket->hostFd < 0) { if (socket->hostFd.native_handle()) {
return orbis::ErrorCode::CONNREFUSED; return orbis::ErrorCode::CONNREFUSED;
} }
@ -604,8 +541,8 @@ static orbis::ErrorCode socket_connect(orbis::File *file,
sockaddr_un un{.sun_family = AF_UNIX}; sockaddr_un un{.sun_family = AF_UNIX};
std::strncpy(un.sun_path, socketPath.c_str(), sizeof(un.sun_path)); std::strncpy(un.sun_path, socketPath.c_str(), sizeof(un.sun_path));
if (::connect(socket->hostFd, reinterpret_cast<::sockaddr *>(&un), if (::connect(socket->hostFd.native_handle(),
sizeof(un)) < 0) { reinterpret_cast<::sockaddr *>(&un), sizeof(un)) < 0) {
return convertErrno(); return convertErrno();
} }
@ -650,12 +587,13 @@ orbis::ErrorCode socket_recvfrom(orbis::File *file, void *buf,
orbis::Thread *thread) { orbis::Thread *thread) {
auto socket = static_cast<SocketFile *>(file); auto socket = static_cast<SocketFile *>(file);
if (socket->hostFd < 0) { if (!socket->hostFd) {
return orbis::ErrorCode::CONNREFUSED; return orbis::ErrorCode::CONNREFUSED;
} }
if (socket->dom == 1 && socket->type == 1 && socket->prot == 0) { if (socket->dom == 1 && socket->type == 1 && socket->prot == 0) {
auto count = ::recvfrom(socket->hostFd, buf, len, flags, nullptr, nullptr); auto count = ::recvfrom(socket->hostFd.native_handle(), buf, len, flags,
nullptr, nullptr);
if (count < 0) { if (count < 0) {
return convertErrno(); return convertErrno();
} }
@ -678,7 +616,6 @@ static const orbis::FileOps hostOps = {
.write = host_write, .write = host_write,
.truncate = host_truncate, .truncate = host_truncate,
.stat = host_stat, .stat = host_stat,
.mmap = host_mmap,
}; };
static const orbis::FileOps socketOps = { static const orbis::FileOps socketOps = {
@ -710,7 +647,7 @@ rx::Ref<orbis::File> wrapSocket(int hostFd, orbis::kstring name, int dom,
s->dom = dom; s->dom = dom;
s->type = type; s->type = type;
s->prot = prot; s->prot = prot;
s->hostFd = hostFd; s->hostFd = rx::Mappable::CreateFromNativeHandle(hostFd);
s->ops = &socketOps; s->ops = &socketOps;
return s; return s;
} }
@ -736,7 +673,7 @@ orbis::ErrorCode createSocket(rx::Ref<orbis::File> *file, orbis::kstring name,
static std::optional<std::string> static std::optional<std::string>
findFileInDir(const std::filesystem::path &dir, const char *name) { findFileInDir(const std::filesystem::path &dir, const char *name) {
for (auto entry : std::filesystem::directory_iterator(dir)) { for (auto &entry : std::filesystem::directory_iterator(dir)) {
auto entryName = entry.path().filename(); auto entryName = entry.path().filename();
if (strcasecmp(entryName.c_str(), name) == 0) { if (strcasecmp(entryName.c_str(), name) == 0) {
return entryName; return entryName;
@ -752,7 +689,7 @@ toRealPath(const std::filesystem::path &inp) {
} }
std::filesystem::path result; std::filesystem::path result;
for (auto elem : inp) { for (auto &elem : inp) {
if (result.empty() || std::filesystem::exists(result / elem)) { if (result.empty() || std::filesystem::exists(result / elem)) {
result /= elem; result /= elem;
continue; continue;
@ -887,7 +824,7 @@ orbis::ErrorCode HostFsDevice::open(rx::Ref<orbis::File> *file,
} }
auto newFile = orbis::knew<HostFile>(); auto newFile = orbis::knew<HostFile>();
newFile->hostFd = hostFd; newFile->hostFd = rx::Mappable::CreateFromNativeHandle(hostFd);
newFile->dirEntries = std::move(dirEntries); newFile->dirEntries = std::move(dirEntries);
newFile->ops = &hostOps; newFile->ops = &hostOps;
newFile->device = this; newFile->device = this;
@ -936,10 +873,10 @@ orbis::ErrorCode HostFsDevice::rename(const char *from, const char *to,
return convertErrorCode(ec); return convertErrorCode(ec);
} }
orbis::File *createHostFile(int hostFd, rx::Ref<orbis::IoDevice> device, orbis::File *createHostFile(int hostFd, orbis::IoDevice *device,
bool alignTruncate) { bool alignTruncate) {
auto newFile = orbis::knew<HostFile>(); auto newFile = orbis::knew<HostFile>();
newFile->hostFd = hostFd; newFile->hostFd = rx::Mappable::CreateFromNativeHandle(hostFd);
newFile->ops = &hostOps; newFile->ops = &hostOps;
newFile->device = device; newFile->device = device;
newFile->alignTruncate = alignTruncate; newFile->alignTruncate = alignTruncate;

View file

@ -35,6 +35,6 @@ rx::Ref<orbis::File> wrapSocket(int hostFd, orbis::kstring name, int dom,
int type, int prot); int type, int prot);
orbis::ErrorCode createSocket(rx::Ref<orbis::File> *file, orbis::kstring name, orbis::ErrorCode createSocket(rx::Ref<orbis::File> *file, orbis::kstring name,
int dom, int type, int prot); int dom, int type, int prot);
orbis::File *createHostFile(int hostFd, rx::Ref<orbis::IoDevice> device, orbis::File *createHostFile(int hostFd, orbis::IoDevice *device,
bool alignTruncate = false); bool alignTruncate = false);
orbis::IoDevice *createFdWrapDevice(int fd); orbis::IoDevice *createFdWrapDevice(int fd);

View file

@ -5,9 +5,10 @@
namespace orbis { namespace orbis {
struct IoDevice; struct IoDevice;
struct Process;
} }
orbis::IoDevice *createDceCharacterDevice(); orbis::IoDevice *createDceCharacterDevice(orbis::Process *process);
orbis::IoDevice *createDipswCharacterDevice(); orbis::IoDevice *createDipswCharacterDevice();
orbis::IoDevice *createDmemCharacterDevice(int index); orbis::IoDevice *createDmemCharacterDevice(int index);
orbis::IoDevice *createGcCharacterDevice(); orbis::IoDevice *createGcCharacterDevice();

View file

@ -68,7 +68,7 @@ struct AjmDevice
AjmDevice(); AjmDevice();
}; };
AVSampleFormat ajmToAvFormat(AJMFormat ajmFormat) { static AVSampleFormat ajmToAvFormat(AJMFormat ajmFormat) {
switch (ajmFormat) { switch (ajmFormat) {
case AJM_FORMAT_S16: case AJM_FORMAT_S16:
return AV_SAMPLE_FMT_S16; return AV_SAMPLE_FMT_S16;
@ -81,7 +81,7 @@ AVSampleFormat ajmToAvFormat(AJMFormat ajmFormat) {
} }
} }
void reset(Instance *instance) { static void reset(Instance *instance) {
instance->gapless.skipSamples = 0; instance->gapless.skipSamples = 0;
instance->gapless.totalSamples = 0; instance->gapless.totalSamples = 0;
instance->gapless.totalSkippedSamples = 0; instance->gapless.totalSkippedSamples = 0;
@ -90,7 +90,7 @@ void reset(Instance *instance) {
instance->aac.framesSkipped = 0; instance->aac.framesSkipped = 0;
} }
void resetAt9(Instance *instance) { static void resetAt9(Instance *instance) {
if (instance->at9.configData) { if (instance->at9.configData) {
Atrac9ReleaseHandle(instance->at9.handle); Atrac9ReleaseHandle(instance->at9.handle);
instance->at9.estimatedSizeUsed = 0; instance->at9.estimatedSizeUsed = 0;
@ -316,7 +316,8 @@ ajm_ioctl_start_batch_buffer(orbis::Thread *, AjmDevice *device,
args.result = 0; args.result = 0;
args.batchId = device->batchId++; args.batchId = device->batchId++;
// ORBIS_LOG_ERROR(__FUNCTION__, args.result, args.unk0, args.pBatch, // ORBIS_LOG_ERROR(__FUNCTION__, args.result, args.unk0, args.pBatch,
// args.batchSize, args.priority, args.batchError, args.batchId); // args.batchSize, args.priority, args.batchError,
// args.batchId);
// thread->where(); // thread->where();
auto ptr = args.pBatch; auto ptr = args.pBatch;

View file

@ -1,107 +1,114 @@
#include "blockpool.hpp" #include "orbis/blockpool.hpp"
#include "dmem.hpp"
#include "orbis/IoDevice.hpp" #include "orbis/IoDevice.hpp"
#include "orbis/KernelAllocator.hpp" #include "orbis/KernelAllocator.hpp"
#include "orbis/KernelContext.hpp" #include "orbis/KernelContext.hpp"
#include "orbis/dmem.hpp"
#include "orbis/file.hpp" #include "orbis/file.hpp"
#include "orbis/thread/Thread.hpp" #include "orbis/thread/Thread.hpp"
#include "orbis/utils/Logs.hpp" #include "orbis/utils/Logs.hpp"
#include "vm.hpp" #include "orbis/vmem.hpp"
#include <cstddef> #include "rx/AddressRange.hpp"
#include <mutex>
#include <sys/mman.h> enum {
BLOCKPOOL_IOCTL_EXPAND = 0xc020a801,
BLOCKPOOL_IOCTL_GET_BLOCK_STATS = 0x4010a802,
};
struct BlockPoolAllocation {
bool allocated = false;
[[nodiscard]] bool isAllocated() const { return allocated; }
[[nodiscard]] bool isRelated(const BlockPoolAllocation &, rx::AddressRange,
rx::AddressRange) const {
return true;
}
[[nodiscard]] BlockPoolAllocation merge(const BlockPoolAllocation &other,
rx::AddressRange,
rx::AddressRange) const {
return other;
}
bool operator==(const BlockPoolAllocation &) const = default;
};
using DirectMemoryResource =
kernel::AllocableResource<BlockPoolAllocation, orbis::kallocator>;
struct BlockPoolFile : public orbis::File {}; struct BlockPoolFile : public orbis::File {};
struct BlockPoolDevice
: orbis::IoDeviceWithIoctl<orbis::ioctl::group(BLOCKPOOL_IOCTL_EXPAND)> {
rx::shared_mutex mtx;
rx::MemoryAreaTable<> pool;
orbis::sint availBlocks{};
orbis::sint commitBlocks{};
static orbis::ErrorCode blockpool_ioctl(orbis::File *file, BlockPoolDevice();
std::uint64_t request, void *argp,
orbis::Thread *thread) {
auto blockPool = static_cast<BlockPoolDevice *>(file->device.get());
std::lock_guard lock(blockPool->mtx);
switch (request) { orbis::ErrorCode open(rx::Ref<orbis::File> *file, const char *path,
case 0xc020a801: { std::uint32_t flags, std::uint32_t mode,
struct Args { orbis::Thread *thread) override;
std::uint64_t len; orbis::ErrorCode map(rx::AddressRange range, std::int64_t offset,
std::uint64_t searchStart; rx::EnumBitSet<orbis::vmem::Protection> protection,
std::uint64_t searchEnd; orbis::File *file, orbis::Process *process) override;
std::uint32_t flags;
};
auto args = reinterpret_cast<Args *>(argp);
ORBIS_LOG_TODO("blockpool expand", args->len, args->searchStart,
args->searchEnd, args->flags);
auto dmem = orbis::g_context->dmemDevice.rawStaticCast<DmemDevice>();
std::lock_guard lock(dmem->mtx);
std::uint64_t start = args->searchStart;
ORBIS_RET_ON_ERROR(
dmem->allocate(&start, args->searchEnd, args->len, 1, args->flags));
blockPool->pool.map(rx::AddressRange::fromBeginSize(start, args->len));
return {};
}
}
ORBIS_LOG_FATAL("Unhandled blockpool ioctl", request);
thread->where();
return {};
}
static orbis::ErrorCode blockpool_mmap(orbis::File *file, void **address,
std::uint64_t size, std::int32_t prot,
std::int32_t flags, std::int64_t offset,
orbis::Thread *thread) {
auto blockPool = static_cast<BlockPoolDevice *>(file->device.get());
std::lock_guard lock(blockPool->mtx);
ORBIS_LOG_FATAL("blockpool mmap", *address, size, offset);
std::size_t totalBlockPoolSize = 0;
for (auto entry : blockPool->pool) {
totalBlockPoolSize += entry.endAddress - entry.beginAddress;
}
if (totalBlockPoolSize < size) {
return orbis::ErrorCode::NOMEM;
}
auto dmem = orbis::g_context->dmemDevice.rawStaticCast<DmemDevice>();
auto mapped = reinterpret_cast<std::byte *>(vm::map(
*address, size, prot, flags, vm::kMapInternalReserveOnly, blockPool));
if (mapped == MAP_FAILED) {
return orbis::ErrorCode::NOMEM;
}
auto result = mapped;
flags |= vm::kMapFlagFixed;
flags &= ~vm::kMapFlagNoOverwrite;
while (true) {
auto entry = *blockPool->pool.begin();
auto blockSize = std::min(entry.endAddress - entry.beginAddress, size);
void *mapAddress = mapped;
ORBIS_LOG_FATAL("blockpool mmap", mapAddress, blockSize, entry.beginAddress,
blockSize);
ORBIS_RET_ON_ERROR(
dmem->mmap(&mapAddress, blockSize, prot, flags, entry.beginAddress));
mapped += blockSize;
size -= blockSize;
blockPool->pool.unmap(entry.beginAddress, entry.beginAddress + blockSize);
if (size == 0) {
break;
}
}
*address = result;
return {};
}
static const orbis::FileOps ops = {
.ioctl = blockpool_ioctl,
.mmap = blockpool_mmap,
}; };
#pragma pack(push, 1)
struct BlockPoolIoctlExpand {
orbis::uint64_t len;
orbis::uint64_t searchStart;
orbis::uint64_t searchEnd;
orbis::uint32_t flags;
orbis::uint32_t _padding;
};
#pragma pack(pop)
static orbis::ErrorCode blockpool_ioctl_expand(orbis::Thread *thread,
BlockPoolDevice *device,
BlockPoolIoctlExpand &args) {
ORBIS_LOG_TODO(__FUNCTION__, args.len, args.searchStart, args.searchEnd,
args.flags);
if (args.len % orbis::dmem::kPageSize || args.len == 0) {
return orbis::ErrorCode::INVAL;
}
auto alignment = args.flags == 0 ? 0 : 1ull << ((args.flags >> 24) & 0x1f);
auto [dmemOffset, dmemErrc] = orbis::dmem::allocate(
0, rx::AddressRange::fromBeginEnd(args.searchStart, args.searchEnd),
args.len, orbis::MemoryType::WbOnion, alignment, true);
if (dmemErrc != orbis::ErrorCode{}) {
return dmemErrc;
}
auto [pmemOffset, pmemErrc] = orbis::dmem::getPmemOffset(0, dmemOffset);
if (pmemErrc != orbis::ErrorCode{}) {
return pmemErrc;
}
args.searchStart = dmemOffset;
return orbis::blockpool::expand(
rx::AddressRange::fromBeginSize(pmemOffset, args.len));
}
static std::pair<orbis::ErrorCode, orbis::blockpool::BlockStats>
blockpool_ioctl_get_block_stats(orbis::Thread *, BlockPoolDevice *) {
return {{}, orbis::blockpool::stats()};
}
static const orbis::FileOps ops = {};
BlockPoolDevice::BlockPoolDevice() {
blockFlags = orbis::vmem::BlockFlags::PooledMemory;
addIoctl<BLOCKPOOL_IOCTL_EXPAND>(blockpool_ioctl_expand);
addIoctl<BLOCKPOOL_IOCTL_GET_BLOCK_STATS>(blockpool_ioctl_get_block_stats);
}
orbis::ErrorCode BlockPoolDevice::open(rx::Ref<orbis::File> *file, orbis::ErrorCode BlockPoolDevice::open(rx::Ref<orbis::File> *file,
const char *path, std::uint32_t flags, const char *path, std::uint32_t flags,
std::uint32_t mode, std::uint32_t mode,
@ -113,32 +120,17 @@ orbis::ErrorCode BlockPoolDevice::open(rx::Ref<orbis::File> *file,
return {}; return {};
} }
orbis::ErrorCode BlockPoolDevice::map(void **address, std::uint64_t len, orbis::ErrorCode
std::int32_t prot, std::int32_t flags, BlockPoolDevice::map(rx::AddressRange range, std::int64_t offset,
orbis::Thread *thread) { rx::EnumBitSet<orbis::vmem::Protection> protection,
ORBIS_LOG_FATAL("blockpool device map", *address, len); orbis::File *file, orbis::Process *process) {
if (prot == 0) { if (protection || offset != 0) {
// FIXME: investigate it return orbis::ErrorCode::INVAL;
prot = 0x33;
} }
auto result = vm::map(*address, len, prot, flags);
if (result == (void *)-1) {
return orbis::ErrorCode::NOMEM;
}
*address = result;
return {}; return {};
} }
orbis::ErrorCode BlockPoolDevice::unmap(void *address, std::uint64_t len,
orbis::Thread *thread) {
ORBIS_LOG_FATAL("blockpool device unmap", address, len);
if (vm::unmap(address, len)) {
return {};
}
return orbis::ErrorCode::INVAL;
}
orbis::IoDevice *createBlockPoolDevice() { orbis::IoDevice *createBlockPoolDevice() {
return orbis::knew<BlockPoolDevice>(); return orbis::knew<BlockPoolDevice>();
} }

View file

@ -1,22 +0,0 @@
#pragma once
#include "orbis/IoDevice.hpp"
#include "orbis/error/ErrorCode.hpp"
#include "orbis/file.hpp"
#include "rx/MemoryTable.hpp"
#include "rx/Rc.hpp"
#include "rx/SharedMutex.hpp"
#include <cstdint>
struct BlockPoolDevice : public orbis::IoDevice {
rx::shared_mutex mtx;
rx::MemoryAreaTable<> pool;
orbis::ErrorCode open(rx::Ref<orbis::File> *file, const char *path,
std::uint32_t flags, std::uint32_t mode,
orbis::Thread *thread) override;
orbis::ErrorCode map(void **address, std::uint64_t len, std::int32_t prot,
std::int32_t flags, orbis::Thread *thread);
orbis::ErrorCode unmap(void *address, std::uint64_t len,
orbis::Thread *thread);
};

View file

@ -1,18 +1,22 @@
#include "dce.hpp" #include "dce.hpp"
#include "gpu/DeviceCtl.hpp" #include "gpu/DeviceCtl.hpp"
#include "io-device.hpp"
#include "iodev/dmem.hpp"
#include "orbis/KernelAllocator.hpp" #include "orbis/KernelAllocator.hpp"
#include "orbis/KernelContext.hpp" #include "orbis/KernelContext.hpp"
#include "orbis/dmem.hpp"
#include "orbis/error/ErrorCode.hpp" #include "orbis/error/ErrorCode.hpp"
#include "orbis/file.hpp" #include "orbis/file.hpp"
#include "orbis/pmem.hpp"
#include "orbis/thread/Process.hpp" #include "orbis/thread/Process.hpp"
#include "orbis/thread/Thread.hpp" #include "orbis/thread/Thread.hpp"
#include "orbis/utils/Logs.hpp" #include "orbis/utils/Logs.hpp"
#include "orbis/vmem.hpp"
#include "rx/AddressRange.hpp"
#include "rx/EnumBitSet.hpp"
#include "rx/die.hpp" #include "rx/die.hpp"
#include "rx/format.hpp"
#include "rx/mem.hpp" #include "rx/mem.hpp"
#include "rx/print.hpp"
#include "rx/watchdog.hpp" #include "rx/watchdog.hpp"
#include "vm.hpp"
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <mutex> #include <mutex>
@ -144,7 +148,7 @@ struct ResolutionStatus {
// refreshRate = 0x23, result.refreshHz = 0x42b3d1ec( 89.91) REFRESH_RATE_89_91HZ // refreshRate = 0x23, result.refreshHz = 0x42b3d1ec( 89.91) REFRESH_RATE_89_91HZ
// clang-format on // clang-format on
static void runBridge(int vmId) { static void runBridge(int vmId, orbis::Process *process) {
std::thread{[=] { std::thread{[=] {
pthread_setname_np(pthread_self(), "Bridge"); pthread_setname_np(pthread_self(), "Bridge");
@ -186,29 +190,23 @@ static void runBridge(int vmId) {
gpuCtx.cachePages[vmId][page].load(std::memory_order::relaxed); gpuCtx.cachePages[vmId][page].load(std::memory_order::relaxed);
auto address = static_cast<std::uint64_t>(page) * rx::mem::pageSize; auto address = static_cast<std::uint64_t>(page) * rx::mem::pageSize;
auto origVmProt = vm::getPageProtection(address); auto range =
int prot = 0; rx::AddressRange::fromBeginSize(address, rx::mem::pageSize * count);
auto origVmProt = orbis::vmem::queryProtection(process, address);
if (origVmProt && origVmProt->prot) {
auto prot = orbis::vmem::toCpuProtection(origVmProt->prot);
if (origVmProt & vm::kMapProtCpuRead) { if (pageFlags & amdgpu::kPageReadWriteLock) {
prot |= PROT_READ; prot = prot & ~(rx::mem::Protection::R | rx::mem::Protection::W);
} } else if (pageFlags & amdgpu::kPageWriteWatch) {
if (origVmProt & vm::kMapProtCpuWrite) { prot = prot & ~(rx::mem::Protection::W);
prot |= PROT_WRITE; }
}
if (origVmProt & vm::kMapProtCpuExec) {
prot |= PROT_EXEC;
}
if (pageFlags & amdgpu::kPageReadWriteLock) { if (auto errc = rx::mem::protect(range, prot); errc != std::errc{}) {
prot &= ~(PROT_READ | PROT_WRITE); rx::die("gpu cache: failed to protect memory {:x}-{:x}, error {}",
} else if (pageFlags & amdgpu::kPageWriteWatch) { range.beginAddress(), range.endAddress(),
prot &= ~PROT_WRITE; static_cast<int>(errc));
} }
if (::mprotect(reinterpret_cast<void *>(address),
rx::mem::pageSize * count, prot)) {
perror("protection failed");
std::abort();
} }
} }
@ -237,43 +235,6 @@ int DceDevice::allocateVmId() {
void DceDevice::deallocateVmId(int vmId) { freeVmIds |= (1 << vmId); } void DceDevice::deallocateVmId(int vmId) { freeVmIds |= (1 << vmId); }
static void initDceMemory(DceDevice *device) {
if (device->dmemOffset + 1) {
return;
}
std::lock_guard lock(device->mtx);
if (device->dmemOffset + 1) {
return;
}
auto dmem = orbis::g_context->dmemDevice.cast<DmemDevice>();
std::uint64_t start = 0;
if (dmem->allocate(&start, ~0ull, kDceControlMemorySize, 0x100000, 1) !=
orbis::ErrorCode{}) {
std::abort();
}
void *address = nullptr;
if (dmem->mmap(&address, kDceControlMemorySize, vm::kMapProtCpuWrite, 0,
start) != orbis::ErrorCode{}) {
std::abort();
}
auto dceControl = reinterpret_cast<std::byte *>(address);
*reinterpret_cast<orbis::uint64_t *>(dceControl + 0x130) = 0;
*reinterpret_cast<orbis::uint64_t *>(dceControl + 0x138) = 1;
*reinterpret_cast<orbis::uint16_t *>(dceControl + 0x140) =
orbis::kEvFiltDisplay;
vm::unmap(address, kDceControlMemorySize);
device->dmemOffset = start;
}
static orbis::ErrorCode dce_mmap(orbis::File *file, void **address,
std::uint64_t size, std::int32_t prot,
std::int32_t flags, std::int64_t offset,
orbis::Thread *thread);
static orbis::ErrorCode dce_ioctl(orbis::File *file, std::uint64_t request, static orbis::ErrorCode dce_ioctl(orbis::File *file, std::uint64_t request,
void *argp, orbis::Thread *thread) { void *argp, orbis::Thread *thread) {
auto device = static_cast<DceDevice *>(file->device.get()); auto device = static_cast<DceDevice *>(file->device.get());
@ -312,21 +273,30 @@ static orbis::ErrorCode dce_ioctl(orbis::File *file, std::uint64_t request,
} }
if (args->id == 9) { if (args->id == 9) {
ORBIS_LOG_NOTICE("dce: FlipControl allocate", args->id, args->padding, ORBIS_LOG_NOTICE("dce: FlipControl map", args->id, args->padding,
args->arg2, args->ptr, args->size, args->arg5, args->arg2, args->ptr, args->size, args->arg5,
args->arg6); args->arg6);
void *address; auto [range, errc] = orbis::vmem::mapDirect(
ORBIS_RET_ON_ERROR( thread->tproc, 0,
dce_mmap(file, &address, vm::kPageSize, rx::AddressRange::fromBeginSize(device->dmemRange.beginAddress(),
vm::kMapProtCpuReadWrite | vm::kMapProtGpuAll, orbis::vmem::kPageSize),
vm::kMapFlagShared, 0, thread)); orbis::vmem::Protection::CpuRead |
orbis::vmem::Protection::CpuWrite |
orbis::vmem::Protection::GpuRead |
orbis::vmem::Protection::GpuWrite,
{}, "DCE");
*(void **)args->ptr = address; if (errc != orbis::ErrorCode{}) {
*(std::uint64_t *)args->arg5 = vm::kPageSize; return errc;
}
*(std::uint64_t *)args->ptr = range.beginAddress();
*(std::uint64_t *)args->arg5 = range.size();
return {}; return {};
} }
if (args->id == 0x38) { if (args->id == 0x38) {
auto attrs = (RegisterBufferAttributeArgs *)args->ptr; auto attrs = (RegisterBufferAttributeArgs *)args->ptr;
@ -527,11 +497,11 @@ static orbis::ErrorCode dce_ioctl(orbis::File *file, std::uint64_t request,
if (request == 0xc0308207) { // SCE_SYS_DCE_IOCTL_REGISTER_BUFFER_ATTRIBUTE if (request == 0xc0308207) { // SCE_SYS_DCE_IOCTL_REGISTER_BUFFER_ATTRIBUTE
auto args = reinterpret_cast<RegisterBufferAttributeArgs *>(argp); auto args = reinterpret_cast<RegisterBufferAttributeArgs *>(argp);
ORBIS_LOG_ERROR("dce: RegisterBufferAttributes", args->canary, args->attrid, ORBIS_LOG_ERROR(
args->submit, args->unk3, args->pixelFormat, "dce: RegisterBufferAttributes", args->canary, (int)args->attrid,
args->tilingMode, args->pitch, args->width, args->height, (int)args->submit, args->unk3, args->pixelFormat, args->tilingMode,
args->unk4_zero, args->unk5_zero, args->options, args->pitch, args->width, args->height, (int)args->unk4_zero,
args->reserved1, args->reserved2); (int)args->unk5_zero, args->options, args->reserved1, args->reserved2);
gpu.registerBufferAttribute(thread->tproc->pid, gpu.registerBufferAttribute(thread->tproc->pid,
{ {
@ -596,20 +566,30 @@ static orbis::ErrorCode dce_ioctl(orbis::File *file, std::uint64_t request,
return {}; return {};
} }
static orbis::ErrorCode dce_mmap(orbis::File *file, void **address, orbis::ErrorCode
std::uint64_t size, std::int32_t prot, DceDevice::map(rx::AddressRange range, std::int64_t offset,
std::int32_t flags, std::int64_t offset, rx::EnumBitSet<orbis::vmem::Protection> protection,
orbis::Thread *thread) { orbis::File *, orbis::Process *process) {
ORBIS_LOG_FATAL("dce mmap", address, size, offset); if (offset + range.size() > dmemRange.size()) {
auto dce = file->device.cast<DceDevice>(); return orbis::ErrorCode::INVAL;
initDceMemory(dce.get()); }
auto dmem = orbis::g_context->dmemDevice.cast<DmemDevice>();
return dmem->mmap(address, size, prot, flags, dce->dmemOffset + offset); rx::println(stderr, "map dce {:x}-{:x} {:04x} {}", range.beginAddress(),
range.endAddress(), offset, protection);
auto result =
orbis::dmem::map(0, range, dmemRange.beginAddress() + offset, protection);
if (result == orbis::ErrorCode{}) {
amdgpu::mapMemory(process->pid, range, orbis::MemoryType::WbGarlic,
protection, dmemRange.beginAddress() + offset);
}
return result;
} }
static const orbis::FileOps ops = { static const orbis::FileOps ops = {
.ioctl = dce_ioctl, .ioctl = dce_ioctl,
.mmap = dce_mmap,
}; };
static void createGpu() { static void createGpu() {
@ -626,6 +606,8 @@ static void createGpu() {
} }
} }
DceDevice::~DceDevice() { orbis::dmem::release(0, dmemRange); }
orbis::ErrorCode DceDevice::open(rx::Ref<orbis::File> *file, const char *path, orbis::ErrorCode DceDevice::open(rx::Ref<orbis::File> *file, const char *path,
std::uint32_t flags, std::uint32_t mode, std::uint32_t flags, std::uint32_t mode,
orbis::Thread *thread) { orbis::Thread *thread) {
@ -649,8 +631,32 @@ void DceDevice::initializeProcess(orbis::Process *process) {
process->vmId = vmId; process->vmId = vmId;
} }
runBridge(vmId); runBridge(vmId, process);
} }
} }
orbis::IoDevice *createDceCharacterDevice() { return orbis::knew<DceDevice>(); } orbis::IoDevice *createDceCharacterDevice(orbis::Process *process) {
auto result = orbis::knew<DceDevice>();
auto dmemSize = orbis::dmem::getSize(0);
auto [dmemOffset, errc] = orbis::dmem::allocate(
0,
rx::AddressRange::fromBeginEnd(dmemSize - orbis::dmem::kPageSize * 2,
dmemSize),
orbis::dmem::kPageSize, orbis::MemoryType::WbGarlic);
rx::dieIf(errc != orbis::ErrorCode{},
"failed to allocate DCE memory, error {}", errc);
result->dmemRange =
rx::AddressRange::fromBeginSize(dmemOffset, orbis::dmem::kPageSize);
auto [vmem, mapErrc] = orbis::vmem::mapDirect(
process, 0, result->dmemRange, orbis::vmem::Protection::CpuWrite, {});
auto dceControl = reinterpret_cast<std::byte *>(vmem.beginAddress());
*reinterpret_cast<orbis::uint64_t *>(dceControl + 0x130) = 0;
*reinterpret_cast<orbis::uint64_t *>(dceControl + 0x138) = 1;
*reinterpret_cast<orbis::uint16_t *>(dceControl + 0x140) =
orbis::kEvFiltDisplay;
orbis::vmem::unmap(process, vmem);
return result;
}

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "io-device.hpp"
#include "orbis-config.hpp" #include "orbis-config.hpp"
#include "orbis/IoDevice.hpp"
#include "orbis/error/ErrorCode.hpp" #include "orbis/error/ErrorCode.hpp"
#include "orbis/file.hpp" #include "orbis/file.hpp"
#include "orbis/thread/Process.hpp" #include "orbis/thread/Process.hpp"
@ -12,14 +12,19 @@ static constexpr auto kVmIdCount = 6;
struct DceDevice : orbis::IoDevice { struct DceDevice : orbis::IoDevice {
rx::shared_mutex mtx; rx::shared_mutex mtx;
rx::AddressRange dmemRange;
std::uint32_t eopCount = 0; std::uint32_t eopCount = 0;
std::uint32_t freeVmIds = (1 << (kVmIdCount + 1)) - 1; std::uint32_t freeVmIds = (1 << (kVmIdCount + 1)) - 1;
orbis::uint64_t dmemOffset = ~static_cast<std::uint64_t>(0);
DceDevice() { blockFlags = orbis::vmem::BlockFlags::DirectMemory; }
~DceDevice();
orbis::ErrorCode open(rx::Ref<orbis::File> *file, const char *path, orbis::ErrorCode open(rx::Ref<orbis::File> *file, const char *path,
std::uint32_t flags, std::uint32_t mode, std::uint32_t flags, std::uint32_t mode,
orbis::Thread *thread) override; orbis::Thread *thread) override;
orbis::ErrorCode map(rx::AddressRange range, std::int64_t offset,
rx::EnumBitSet<orbis::vmem::Protection> protection,
orbis::File *file, orbis::Process *process) override;
int allocateVmId(); int allocateVmId();
void deallocateVmId(int vmId); void deallocateVmId(int vmId);
void initializeProcess(orbis::Process *process); void initializeProcess(orbis::Process *process);

View file

@ -1,390 +1,354 @@
#include "dmem.hpp" #include "orbis/dmem.hpp"
#include "gpu/DeviceCtl.hpp" #include "gpu/DeviceCtl.hpp"
#include "io-device.hpp"
#include "orbis/KernelAllocator.hpp" #include "orbis/KernelAllocator.hpp"
#include "orbis/KernelContext.hpp" #include "orbis/KernelContext.hpp"
#include "orbis/error.hpp"
#include "orbis/file.hpp" #include "orbis/file.hpp"
#include "orbis/pmem.hpp"
#include "orbis/thread/Process.hpp" #include "orbis/thread/Process.hpp"
#include "orbis/thread/Thread.hpp" #include "orbis/thread/Thread.hpp"
#include "orbis/utils/Logs.hpp" #include "orbis/utils/Logs.hpp"
#include "rx/align.hpp" #include "orbis/vmem.hpp"
#include "rx/format.hpp" #include "rx/format.hpp"
#include "rx/watchdog.hpp" #include "rx/AddressRange.hpp"
#include "vm.hpp" #include "rx/EnumBitSet.hpp"
#include <fcntl.h>
#include <filesystem> enum {
#include <mutex> DMEM_IOCTL_ALLOCATE = 0xc0288001,
#include <sys/mman.h> DMEM_IOCTL_RELEASE = 0x80108002,
#include <unistd.h> DMEM_IOCTL_SET_TYPE = 0x80188003,
DMEM_IOCTL_GET_TYPE = 0xc0208004,
DMEM_IOCTL_GET_TOTAL_SIZE = 0x4008800a,
DMEM_IOCTL_CLEAR = 0x2000800b,
DMEM_IOCTL_TRANSFER_BUDGET = 0xc018800d,
DMEM_IOCTL_CONTROL_RELEASE = 0xc018800e,
DMEM_IOCTL_SET_PID_AND_PROTECT = 0xc018800f,
DMEM_IOCTL_ALLOCATE_FOR_MINI_APP = 0xc0288010,
DMEM_IOCTL_ALLOCATE_MAIN = 0xc0288011,
DMEM_IOCTL_QUERY = 0x80288012,
DMEM_IOCTL_CHECKED_RELEASE = 0x80108015,
DMEM_IOCTL_GET_AVAIL_SIZE = 0xc0208016,
DMEM_IOCTL_RESERVE = 0xc010801a,
};
struct DmemDevice
: orbis::IoDeviceWithIoctl<orbis::ioctl::group(DMEM_IOCTL_ALLOCATE)> {
int index;
DmemDevice(int index);
orbis::ErrorCode open(rx::Ref<orbis::File> *file, const char *path,
std::uint32_t flags, std::uint32_t mode,
orbis::Thread *thread) override;
orbis::ErrorCode map(rx::AddressRange range, std::int64_t offset,
rx::EnumBitSet<orbis::vmem::Protection> protection,
orbis::File *file, orbis::Process *process) override;
};
struct DmemFile : public orbis::File {}; struct DmemFile : public orbis::File {};
struct AllocateDirectMemoryArgs { #pragma pack(push, 1)
std::uint64_t searchStart; struct DmemIoctlAllocate {
std::uint64_t searchEnd; orbis::uintptr_t searchStart;
std::uint64_t len; orbis::uintptr_t searchEnd;
std::uint64_t alignment; orbis::size_t len;
std::uint32_t memoryType; orbis::size_t alignment;
orbis::MemoryType memoryType;
orbis::uint32_t padding;
}; };
static constexpr auto dmemSize = 0x5000000000; struct DmemIoctlRelease {
// static const std::uint64_t nextOffset = 0; orbis::uintptr_t address;
// static const std::uint64_t memBeginAddress = 0xfe0000000; orbis::size_t size;
};
struct DmemIoctlSetType {
orbis::uintptr_t start;
orbis::uintptr_t end;
orbis::MemoryType memoryType;
orbis::uint32_t padding;
};
struct DmemIoctlGetType {
orbis::uintptr_t start;
orbis::uintptr_t regionStart;
orbis::uintptr_t regionEnd;
orbis::MemoryType memoryType;
orbis::uint32_t padding;
};
struct DmemIoctlTransferBudget {
orbis::uint64_t unk0;
orbis::uint64_t unk1;
orbis::uint64_t unk2;
};
struct DmemIoctlControlRelease {
orbis::uint64_t unk0;
orbis::uint64_t unk1;
orbis::uint64_t unk2;
};
struct DmemIoctlSetPidAndProtect {
orbis::uintptr_t address;
orbis::size_t size;
orbis::pid_t pid; // 0 if all
rx::EnumBitSet<orbis::vmem::Protection> prot;
};
DmemDevice::~DmemDevice() { struct DirectMemoryQueryInfo {
if (shmFd > 0) { orbis::uintptr_t start;
close(shmFd); orbis::uintptr_t end;
orbis::MemoryType memoryType;
orbis::uint32_t padding;
};
struct DmemIoctlQuery {
orbis::uint32_t devIndex;
orbis::uint32_t flags;
orbis::uint32_t unk;
orbis::uint32_t _padding;
orbis::uint64_t offset;
orbis::ptr<DirectMemoryQueryInfo> info;
orbis::uint64_t infoSize;
};
struct DmemIoctlGetAvailSize {
orbis::uintptr_t searchStart;
orbis::uintptr_t searchEnd;
orbis::size_t alignment;
orbis::size_t size;
};
struct DmemIoctlReserve {
orbis::size_t size;
orbis::uint32_t flags;
orbis::uint32_t padding;
};
#pragma pack(pop)
static orbis::ErrorCode dmem_ioctl_allocate(orbis::Thread *thread,
DmemDevice *device,
DmemIoctlAllocate &args) {
ORBIS_LOG_WARNING(__FUNCTION__, args.searchStart, args.searchEnd,
args.alignment, args.len, (int)args.memoryType);
auto [offset, errc] = orbis::dmem::allocate(
device->index,
rx::AddressRange::fromBeginEnd(args.searchStart, args.searchEnd),
args.len, args.memoryType);
if (errc != orbis::ErrorCode{}) {
return errc;
} }
std::filesystem::remove(rx::format("{}/dmem-{}", rx::getShmPath(), index)); args.searchStart = offset;
return {};
} }
orbis::ErrorCode DmemDevice::mmap(void **address, std::uint64_t len, static orbis::ErrorCode dmem_ioctl_release(orbis::Thread *thread,
std::int32_t prot, std::int32_t flags, DmemDevice *device,
std::int64_t directMemoryStart) { const DmemIoctlRelease &args) {
if (prot == 0) { ORBIS_LOG_WARNING(__FUNCTION__, args.address, args.size);
// hack
// fixme: implement protect for pid return orbis::dmem::release(
prot = vm::kMapProtCpuReadWrite | vm::kMapProtGpuAll; device->index, rx::AddressRange::fromBeginSize(args.address, args.size));
}
static orbis::ErrorCode dmem_ioctl_set_type(orbis::Thread *thread,
DmemDevice *device,
const DmemIoctlSetType &args) {
// removed ioctl
return orbis::ErrorCode::INVAL;
}
static orbis::ErrorCode dmem_ioctl_get_type(orbis::Thread *thread,
DmemDevice *device,
DmemIoctlGetType &args) {
ORBIS_LOG_WARNING(__FUNCTION__, args.start);
auto result = orbis::dmem::query(device->index, args.start);
if (!result) {
return orbis::ErrorCode::NOENT;
} }
if (*address == nullptr) { args.regionStart = result->range.beginAddress();
*address = std::bit_cast<void *>(0x80000000ull); args.regionEnd = result->range.endAddress();
flags &= ~vm::kMapFlagFixed; args.memoryType = result->memoryType;
return {};
}
static std::pair<orbis::ErrorCode, orbis::uint64_t>
dmem_ioctl_get_total_size(orbis::Thread *thread, DmemDevice *device) {
auto result = orbis::dmem::getSize(device->index);
ORBIS_LOG_WARNING(__FUNCTION__, result);
auto limit = thread->tproc->getBudget()->get(orbis::BudgetResource::Dmem);
return {{}, orbis::uint64_t(std::min(result, limit.total))};
}
static orbis::ErrorCode dmem_ioctl_clear(orbis::Thread *thread,
DmemDevice *device) {
ORBIS_LOG_WARNING(__FUNCTION__);
auto result = orbis::dmem::clear(device->index);
if (result == orbis::ErrorCode{}) {
thread->tproc->getBudget()->release(orbis::BudgetResource::Dmem, -1);
}
return result;
}
static orbis::ErrorCode
dmem_ioctl_transfer_budget(orbis::Thread *thread, DmemDevice *device,
DmemIoctlTransferBudget &args) {
ORBIS_LOG_WARNING(__FUNCTION__, args.unk0, args.unk1, args.unk2);
return {};
}
static orbis::ErrorCode
dmem_ioctl_control_release(orbis::Thread *thread, DmemDevice *device,
DmemIoctlControlRelease &args) {
ORBIS_LOG_WARNING(__FUNCTION__, args.unk0, args.unk1, args.unk2);
return {};
}
static orbis::ErrorCode
dmem_ioctl_set_pid_and_protect(orbis::Thread *thread, DmemDevice *device,
DmemIoctlSetPidAndProtect &args) {
ORBIS_LOG_WARNING(__FUNCTION__, args.pid, args.address, args.size,
args.prot.toUnderlying());
return {};
}
static orbis::ErrorCode
dmem_ioctl_allocate_for_mini_app(orbis::Thread *thread, DmemDevice *device,
DmemIoctlAllocate &args) {
// FIXME: implement
return dmem_ioctl_allocate(thread, device, args);
}
static orbis::ErrorCode dmem_ioctl_allocate_main(orbis::Thread *thread,
DmemDevice *device,
DmemIoctlAllocate &args) {
// FIXME: implement
return dmem_ioctl_allocate(thread, device, args);
}
static orbis::ErrorCode dmem_ioctl_query(orbis::Thread *thread,
DmemDevice *device,
const DmemIoctlQuery &args) {
ORBIS_LOG_WARNING(__FUNCTION__, device->index, args.devIndex, args.unk,
args.flags, args.offset, args.info, args.infoSize);
if (args.devIndex != device->index) {
ORBIS_LOG_WARNING(__FUNCTION__, "device mismatch", device->index,
args.devIndex, args.unk, args.flags, args.offset,
args.info, args.infoSize);
} }
int memoryType = 0; if (args.infoSize != sizeof(DirectMemoryQueryInfo) || args.devIndex >= 3) {
if (auto allocationInfoIt = allocations.queryArea(directMemoryStart);
allocationInfoIt != allocations.end()) {
memoryType = allocationInfoIt->memoryType;
}
auto result = vm::map(*address, len, prot, flags, vm::kMapInternalReserveOnly,
this, directMemoryStart);
ORBIS_LOG_WARNING("dmem mmap", index, directMemoryStart, len, prot, flags,
result, *address);
if (result == (void *)-1) {
return orbis::ErrorCode::NOMEM; // TODO
}
if (::mmap(result, len, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, shmFd,
directMemoryStart) == (void *)-1) {
return orbis::ErrorCode::INVAL; return orbis::ErrorCode::INVAL;
} }
if (auto gpu = amdgpu::DeviceCtl{orbis::g_context->gpuDevice}) { rx::EnumBitSet<orbis::dmem::QueryFlags> queryFlags = {};
gpu.submitMapMemory(orbis::g_currentThread->tproc->pid,
reinterpret_cast<std::uint64_t>(result), len, if (args.flags & 1) {
memoryType, index, prot, directMemoryStart); queryFlags |= orbis::dmem::QueryFlags::LowerBound;
} }
*address = result; auto result = orbis::dmem::query(args.devIndex, args.offset, queryFlags);
if (!result) {
return orbis::ErrorCode::ACCES;
}
DirectMemoryQueryInfo info{
.start = result->range.beginAddress(),
.end = result->range.endAddress(),
.memoryType = result->memoryType,
};
ORBIS_LOG_WARNING(__FUNCTION__, device->index, args.devIndex, args.unk,
args.flags, args.offset, args.info, args.infoSize,
info.start, info.end, (int)info.memoryType);
return orbis::uwrite(args.info, info);
}
static orbis::ErrorCode
dmem_ioctl_checked_release(orbis::Thread *thread, DmemDevice *device,
const DmemIoctlRelease &args) {
return dmem_ioctl_release(thread, device, args);
}
static orbis::ErrorCode dmem_ioctl_get_avail_size(orbis::Thread *thread,
DmemDevice *device,
DmemIoctlGetAvailSize &args) {
ORBIS_LOG_WARNING(__FUNCTION__, args.searchStart, args.searchEnd,
args.alignment, args.size);
auto [range, errc] = orbis::dmem::getAvailSize(
device->index,
rx::AddressRange::fromBeginEnd(args.searchStart, args.searchEnd),
args.alignment);
if (errc != orbis::ErrorCode{}) {
return errc;
}
ORBIS_LOG_WARNING(__FUNCTION__, args.searchStart, args.searchEnd,
args.alignment, args.size, range.beginAddress(),
range.size());
args.searchStart = range.beginAddress();
args.size = range.size();
return {}; return {};
} }
static orbis::ErrorCode dmem_ioctl(orbis::File *file, std::uint64_t request, static orbis::ErrorCode dmem_ioctl_reserve(orbis::Thread *thread,
void *argp, orbis::Thread *thread) { DmemDevice *device,
auto device = file->device.rawStaticCast<DmemDevice>(); DmemIoctlReserve &args) {
ORBIS_LOG_WARNING(__FUNCTION__, args.size, (int)args.flags);
auto [offset, errc] = orbis::dmem::reserveSystem(device->index, args.size);
std::lock_guard lock(device->mtx); if (errc == orbis::ErrorCode{}) {
switch (request) { args.size = offset | 0x4000000000;
case 0x4008800a: // get size
ORBIS_LOG_WARNING("dmem getTotalSize", device->index, argp);
*(std::uint64_t *)argp = device->dmemTotalSize / 0x10;
return {};
case 0xc0208016: { // get available size
struct Args {
std::uint64_t searchStart;
std::uint64_t searchEnd;
std::uint64_t alignment;
std::uint64_t size;
};
auto args = reinterpret_cast<Args *>(argp);
return device->queryMaxFreeChunkSize(&args->searchStart, args->searchEnd,
args->alignment, &args->size);
} }
return errc;
case 0xc0288010: // sceKernelAllocateDirectMemoryForMiniApp
case 0xc0288011:
case 0xc0288001: { // sceKernelAllocateDirectMemory
auto args = reinterpret_cast<AllocateDirectMemoryArgs *>(argp);
return device->allocate(&args->searchStart, args->searchEnd, args->len,
args->alignment, args->memoryType);
}
case 0xc018800d: { // transfer budget
return {};
}
case 0xc018800f: { // protect memory for pid
struct Args {
std::uint64_t address;
std::uint64_t size;
std::uint32_t pid; // 0 if all
std::uint32_t prot;
};
return {};
}
case 0x80108015: // sceKernelCheckedReleaseDirectMemory
case 0x80108002: { // sceKernelReleaseDirectMemory
struct Args {
std::uint64_t address;
std::uint64_t size;
};
auto args = reinterpret_cast<Args *>(argp);
ORBIS_LOG_WARNING("dmem releaseDirectMemory", device->index, args->address,
args->size);
device->allocations.map(
rx::AddressRange::fromBeginSize(args->address, args->size),
{.memoryType = -1u});
return {};
}
case 0xc0208004: { // get direct memory type
struct Args {
std::uint64_t start;
std::uint64_t regionStart;
std::uint64_t regionEnd;
std::uint32_t memoryType;
};
auto args = reinterpret_cast<Args *>(argp);
auto it = device->allocations.lowerBound(args->start);
if (it == device->allocations.end() || it->memoryType == -1u) {
return orbis::ErrorCode::SRCH;
}
args->regionStart = it.beginAddress();
args->regionEnd = it.endAddress();
args->memoryType = it->memoryType;
return {};
}
case 0x80288012: { // direct memory query
struct DirectMemoryQueryInfo {
std::uint64_t start;
std::uint64_t end;
std::uint32_t memoryType;
};
struct Args {
std::uint32_t devIndex;
std::uint32_t flags;
std::uint32_t unk;
std::uint64_t offset;
orbis::ptr<DirectMemoryQueryInfo> info;
std::uint64_t infoSize;
};
auto args = reinterpret_cast<Args *>(argp);
ORBIS_LOG_WARNING("dmem directMemoryQuery", device->index, args->devIndex,
args->unk, args->flags, args->offset, args->info,
args->infoSize);
if (args->devIndex != device->index) {
// TODO
ORBIS_LOG_ERROR("dmem directMemoryQuery: device mismatch", device->index,
args->devIndex, args->unk, args->flags, args->offset,
args->info, args->infoSize);
return orbis::ErrorCode::INVAL;
}
if (args->infoSize != sizeof(DirectMemoryQueryInfo)) {
return orbis::ErrorCode::INVAL;
}
auto it = device->allocations.lowerBound(args->offset);
if (it == device->allocations.end()) {
return orbis::ErrorCode::ACCES;
}
auto queryInfo = *it;
if (it->memoryType == -1u) {
return orbis::ErrorCode::ACCES;
}
if ((args->flags & 1) == 0) {
if (it.endAddress() <= args->offset) {
return orbis::ErrorCode::ACCES;
}
} else {
if (it.beginAddress() > args->offset || it.endAddress() <= args->offset) {
return orbis::ErrorCode::ACCES;
}
}
DirectMemoryQueryInfo info{
.start = it.beginAddress(),
.end = it.endAddress(),
.memoryType = it->memoryType,
};
ORBIS_LOG_WARNING("dmem directMemoryQuery", device->index, args->devIndex,
args->unk, args->flags, args->offset, args->info,
args->infoSize, info.start, info.end, info.memoryType);
return orbis::uwrite(args->info, info);
}
}
ORBIS_LOG_FATAL("Unhandled dmem ioctl", device->index, request);
thread->where();
return {};
} }
static orbis::ErrorCode dmem_mmap(orbis::File *file, void **address, DmemDevice::DmemDevice(int index) : index(index) {
std::uint64_t size, std::int32_t prot, blockFlags = orbis::vmem::BlockFlags::DirectMemory;
std::int32_t flags, std::int64_t offset,
orbis::Thread *thread) { addIoctl<DMEM_IOCTL_ALLOCATE>(dmem_ioctl_allocate);
auto device = file->device.rawStaticCast<DmemDevice>(); addIoctl<DMEM_IOCTL_RELEASE>(dmem_ioctl_release);
return device->mmap(address, size, prot, flags, offset); addIoctl<DMEM_IOCTL_SET_TYPE>(dmem_ioctl_set_type);
addIoctl<DMEM_IOCTL_GET_TYPE>(dmem_ioctl_get_type);
addIoctl<DMEM_IOCTL_GET_TOTAL_SIZE>(dmem_ioctl_get_total_size);
addIoctl<DMEM_IOCTL_CLEAR>(dmem_ioctl_clear);
addIoctl<DMEM_IOCTL_TRANSFER_BUDGET>(dmem_ioctl_transfer_budget);
addIoctl<DMEM_IOCTL_CONTROL_RELEASE>(dmem_ioctl_control_release);
addIoctl<DMEM_IOCTL_SET_PID_AND_PROTECT>(dmem_ioctl_set_pid_and_protect);
addIoctl<DMEM_IOCTL_ALLOCATE_FOR_MINI_APP>(dmem_ioctl_allocate_for_mini_app);
addIoctl<DMEM_IOCTL_ALLOCATE_MAIN>(dmem_ioctl_allocate_main);
addIoctl<DMEM_IOCTL_QUERY>(dmem_ioctl_query);
addIoctl<DMEM_IOCTL_CHECKED_RELEASE>(dmem_ioctl_checked_release);
addIoctl<DMEM_IOCTL_GET_AVAIL_SIZE>(dmem_ioctl_get_avail_size);
addIoctl<DMEM_IOCTL_RESERVE>(dmem_ioctl_reserve);
} }
static const orbis::FileOps ops = { orbis::ErrorCode
.ioctl = dmem_ioctl, DmemDevice::map(rx::AddressRange range, std::int64_t offset,
.mmap = dmem_mmap, rx::EnumBitSet<orbis::vmem::Protection> protection,
}; orbis::File *, orbis::Process *process) {
auto result = orbis::dmem::map(index, range, offset, protection);
orbis::ErrorCode DmemDevice::allocate(std::uint64_t *start, if (result == orbis::ErrorCode{}) {
std::uint64_t searchEnd, if (auto dmemType = orbis::dmem::query(0, offset)) {
std::uint64_t len, auto [pmemOffset, errc] = orbis::dmem::getPmemOffset(0, offset);
std::uint64_t alignment, rx::dieIf(errc != orbis::ErrorCode{}, "failed to query dmem type {}",
std::uint32_t memoryType) { errc);
std::size_t offset = *start;
if (alignment == 0) {
alignment = 1;
}
if (searchEnd == 0) {
searchEnd = dmemTotalSize;
}
while (offset < searchEnd) { amdgpu::mapMemory(process->pid, range, dmemType->memoryType, protection,
offset += alignment - 1; pmemOffset);
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()) {
if (it->memoryType == -1u) {
if (offset < it.beginAddress()) {
offset = it.beginAddress() + alignment - 1;
offset &= ~(alignment - 1);
}
if (offset + len >= it.endAddress()) {
offset = it.endAddress();
continue;
}
} else {
if (offset + len > it.beginAddress()) {
offset = it.endAddress();
continue;
}
}
}
allocations.map(rx::AddressRange::fromBeginSize(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, return result;
len, alignment, memoryType, offset);
return orbis::ErrorCode::AGAIN;
} }
orbis::ErrorCode DmemDevice::release(std::uint64_t start, std::uint64_t size) { static const orbis::FileOps ops = {};
allocations.unmap(rx::AddressRange::fromBeginSize(start, size));
return {};
}
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;
alignment = std::max(alignment, vm::kPageSize);
alignment = rx::alignUp(alignment, vm::kPageSize);
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;
}
if (it->memoryType == -1u) {
if (offset < it.beginAddress()) {
offset = it.beginAddress() + alignment - 1;
offset &= ~(alignment - 1);
}
if (it.endAddress() > offset && resultSize < it.endAddress() - offset) {
resultSize = it.endAddress() - offset;
resultOffset = offset;
}
} else if (offset > it.beginAddress() &&
resultSize < offset - it.beginAddress()) {
resultSize = offset - it.beginAddress();
resultOffset = offset;
}
offset = it.endAddress();
}
resultSize /= 0x20;
*start = resultOffset;
*size = resultSize;
ORBIS_LOG_WARNING("dmem queryMaxFreeChunkSize", resultOffset, resultSize);
if (resultSize == 0) {
return orbis::ErrorCode::NOMEM;
}
return {};
}
orbis::ErrorCode DmemDevice::open(rx::Ref<orbis::File> *file, const char *path, orbis::ErrorCode DmemDevice::open(rx::Ref<orbis::File> *file, const char *path,
std::uint32_t flags, std::uint32_t mode, std::uint32_t flags, std::uint32_t mode,
@ -397,18 +361,6 @@ orbis::ErrorCode DmemDevice::open(rx::Ref<orbis::File> *file, const char *path,
} }
orbis::IoDevice *createDmemCharacterDevice(int index) { orbis::IoDevice *createDmemCharacterDevice(int index) {
auto *newDevice = orbis::knew<DmemDevice>(); auto *newDevice = orbis::knew<DmemDevice>(index);
newDevice->index = index;
newDevice->dmemTotalSize = dmemSize;
auto path = rx::format("{}/dmem-{}", rx::getShmPath(), index);
auto shmFd = ::open(path.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
if (ftruncate(shmFd, dmemSize) < 0) {
::close(shmFd);
std::abort();
}
newDevice->shmFd = shmFd;
return newDevice; return newDevice;
} }

View file

@ -1,47 +0,0 @@
#pragma once
#include "orbis/IoDevice.hpp"
#include "orbis/KernelAllocator.hpp"
#include "orbis/error/ErrorCode.hpp"
#include "orbis/file.hpp"
#include "rx/Rc.hpp"
#include "rx/SharedMutex.hpp"
#include <cstdint>
#include <rx/MemoryTable.hpp>
#include <unistd.h>
struct DmemDevice : public orbis::IoDevice {
rx::shared_mutex mtx;
int index;
int shmFd = -1;
std::size_t dmemTotalSize;
~DmemDevice();
struct AllocationInfo {
std::uint32_t memoryType;
bool operator==(const AllocationInfo &) const = default;
auto operator<=>(const AllocationInfo &) const = default;
};
rx::MemoryTableWithPayload<AllocationInfo, orbis::kallocator> 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(rx::Ref<orbis::File> *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 prot,
std::int32_t flags, std::int64_t directMemoryStart);
};

View file

@ -1,17 +1,20 @@
#include "dce.hpp"
#include "gpu/DeviceCtl.hpp" #include "gpu/DeviceCtl.hpp"
#include "iodev/dce.hpp"
#include "iodev/dmem.hpp"
#include "orbis/IoDevice.hpp" #include "orbis/IoDevice.hpp"
#include "orbis/KernelAllocator.hpp" #include "orbis/KernelAllocator.hpp"
#include "orbis/KernelContext.hpp" #include "orbis/KernelContext.hpp"
#include "orbis/dmem.hpp"
#include "orbis/file.hpp" #include "orbis/file.hpp"
#include "orbis/pmem.hpp"
#include "orbis/thread/Process.hpp" #include "orbis/thread/Process.hpp"
#include "orbis/thread/Thread.hpp" #include "orbis/thread/Thread.hpp"
#include "orbis/utils/Logs.hpp" #include "orbis/utils/Logs.hpp"
#include "orbis/vmem.hpp"
#include "rx/AddressRange.hpp"
#include "rx/SharedMutex.hpp" #include "rx/SharedMutex.hpp"
#include "rx/die.hpp" #include "rx/die.hpp"
#include "rx/format.hpp"
#include "rx/print.hpp" #include "rx/print.hpp"
#include "vm.hpp"
#include <cstdio> #include <cstdio>
#include <mutex> #include <mutex>
#include <sys/mman.h> #include <sys/mman.h>
@ -26,15 +29,44 @@ struct ComputeQueue {
struct GcDevice : public orbis::IoDevice { struct GcDevice : public orbis::IoDevice {
rx::shared_mutex mtx; rx::shared_mutex mtx;
rx::AddressRange dmemRange;
orbis::kmap<orbis::pid_t, int> clients; orbis::kmap<orbis::pid_t, int> clients;
orbis::kmap<std::uint64_t, ComputeQueue> computeQueues; orbis::kmap<std::uint64_t, ComputeQueue> computeQueues;
void *submitArea = nullptr; orbis::uintptr_t submitArea = 0;
orbis::ErrorCode open(rx::Ref<orbis::File> *file, const char *path, orbis::ErrorCode open(rx::Ref<orbis::File> *file, const char *path,
std::uint32_t flags, std::uint32_t mode, std::uint32_t flags, std::uint32_t mode,
orbis::Thread *thread) override; orbis::Thread *thread) override;
void addClient(orbis::Process *process); void addClient(orbis::Process *process);
void removeClient(orbis::Process *process); void removeClient(orbis::Process *process);
GcDevice() { blockFlags = orbis::vmem::BlockFlags::DirectMemory; }
~GcDevice() { orbis::pmem::deallocate(dmemRange); }
orbis::ErrorCode map(rx::AddressRange range, std::int64_t offset,
rx::EnumBitSet<orbis::vmem::Protection> protection,
orbis::File *file, orbis::Process *process) override {
if (offset + range.size() > dmemRange.size()) {
return orbis::ErrorCode::INVAL;
}
rx::println(stderr, "map gc {:x}-{:x} {:04x} {}", range.beginAddress(),
range.endAddress(), offset, protection);
auto result =
orbis::pmem::map(range.beginAddress(),
rx::AddressRange::fromBeginSize(
dmemRange.beginAddress() + offset, range.size()),
orbis::vmem::toCpuProtection(protection));
if (result == orbis::ErrorCode{}) {
amdgpu::mapMemory(process->pid, range, orbis::MemoryType::WbOnion,
protection, dmemRange.beginAddress() + offset);
}
return result;
}
}; };
struct GcFile : public orbis::File { struct GcFile : public orbis::File {
@ -55,26 +87,35 @@ static orbis::ErrorCode gc_ioctl(orbis::File *file, std::uint64_t request,
switch (request) { switch (request) {
case 0xc008811b: // get submit done flag ptr? case 0xc008811b: // get submit done flag ptr?
if (device->submitArea == nullptr) { if (device->submitArea == 0) {
auto dmem = orbis::g_context->dmemDevice.staticCast<DmemDevice>(); auto [dmemOffset, dmemErrc] = orbis::dmem::allocate(
std::uint64_t start = 0; 0, rx::AddressRange::fromBeginEnd(0, 0), orbis::dmem::kPageSize,
auto err = dmem->allocate(&start, ~0, vm::kPageSize, 0, 0); orbis::MemoryType::WbGarlic);
if (err != orbis::ErrorCode{}) {
return err; if (dmemErrc != orbis::ErrorCode{}) {
return dmemErrc;
} }
auto address = reinterpret_cast<void *>(0xfe0100000);
err = dmem->mmap(&address, vm::kPageSize, auto directRange =
vm::kMapProtCpuReadWrite | vm::kMapProtGpuAll, rx::AddressRange::fromBeginSize(dmemOffset, orbis::dmem::kPageSize);
vm::kMapFlagShared, start);
if (err != orbis::ErrorCode{}) { auto [vmemRange, vmemErrc] = orbis::vmem::mapDirect(
dmem->release(start, vm::kPageSize); thread->tproc, 0xfe0100000, directRange,
return err; orbis::vmem::Protection::CpuRead | orbis::vmem::Protection::CpuWrite |
orbis::vmem::Protection::GpuRead |
orbis::vmem::Protection::GpuWrite,
{});
if (vmemErrc != orbis::ErrorCode{}) {
orbis::dmem::release(0, directRange);
return dmemErrc;
} }
device->submitArea = address;
device->submitArea = vmemRange.beginAddress();
} }
ORBIS_LOG_ERROR("gc ioctl 0xc008811b", *(std::uint64_t *)argp); ORBIS_LOG_ERROR("gc ioctl 0xc008811b", *(std::uint64_t *)argp);
*reinterpret_cast<void **>(argp) = device->submitArea; *reinterpret_cast<orbis::uintptr_t *>(argp) = device->submitArea;
break; break;
case 0xc004812e: { case 0xc004812e: {
@ -438,25 +479,7 @@ static orbis::ErrorCode gc_ioctl(orbis::File *file, std::uint64_t request,
return {}; return {};
} }
static orbis::ErrorCode gc_mmap(orbis::File *file, void **address, static const orbis::FileOps ops = {.ioctl = gc_ioctl};
std::uint64_t size, std::int32_t prot,
std::int32_t flags, std::int64_t offset,
orbis::Thread *thread) {
ORBIS_LOG_FATAL("gc mmap", address, size, offset);
auto result = vm::map(*address, size, prot, flags);
if (result == (void *)-1) {
return orbis::ErrorCode::INVAL; // TODO
}
*address = result;
return {};
}
static const orbis::FileOps ops = {
.ioctl = gc_ioctl,
.mmap = gc_mmap,
};
orbis::ErrorCode GcDevice::open(rx::Ref<orbis::File> *file, const char *path, orbis::ErrorCode GcDevice::open(rx::Ref<orbis::File> *file, const char *path,
std::uint32_t flags, std::uint32_t mode, std::uint32_t flags, std::uint32_t mode,
@ -490,4 +513,18 @@ void GcDevice::removeClient(orbis::Process *process) {
} }
} }
orbis::IoDevice *createGcCharacterDevice() { return orbis::knew<GcDevice>(); } orbis::IoDevice *createGcCharacterDevice() {
auto result = orbis::knew<GcDevice>();
auto dmemSize = orbis::dmem::getSize(0);
auto [dmemOffset, errc] = orbis::dmem::allocate(
0,
rx::AddressRange::fromBeginEnd(dmemSize - orbis::dmem::kPageSize * 2,
dmemSize),
orbis::dmem::kPageSize, orbis::MemoryType::WbGarlic);
rx::dieIf(errc != orbis::ErrorCode{},
"failed to allocate GC memory, error {}", errc);
result->dmemRange =
rx::AddressRange::fromBeginSize(dmemOffset, orbis::dmem::kPageSize);
return result;
}

View file

@ -2,7 +2,6 @@
#include "orbis/KernelAllocator.hpp" #include "orbis/KernelAllocator.hpp"
#include "orbis/file.hpp" #include "orbis/file.hpp"
#include "orbis/utils/Logs.hpp" #include "orbis/utils/Logs.hpp"
#include "vm.hpp"
struct HmdMmapDevice : public orbis::IoDevice { struct HmdMmapDevice : public orbis::IoDevice {
orbis::ErrorCode open(rx::Ref<orbis::File> *file, const char *path, orbis::ErrorCode open(rx::Ref<orbis::File> *file, const char *path,
@ -19,24 +18,8 @@ static orbis::ErrorCode hmd_mmap_ioctl(orbis::File *file, std::uint64_t request,
return {}; return {};
} }
static orbis::ErrorCode hmd_mmap_mmap(orbis::File *file, void **address,
std::uint64_t size, std::int32_t prot,
std::int32_t flags, std::int64_t offset,
orbis::Thread *thread) {
ORBIS_LOG_FATAL("hmd_mmap mmap", address, size, offset);
auto result = vm::map(*address, size, prot, flags);
if (result == (void *)-1) {
return orbis::ErrorCode::INVAL; // TODO
}
*address = result;
return {};
}
static const orbis::FileOps ops = { static const orbis::FileOps ops = {
.ioctl = hmd_mmap_ioctl, .ioctl = hmd_mmap_ioctl,
.mmap = hmd_mmap_mmap,
}; };
orbis::ErrorCode HmdMmapDevice::open(rx::Ref<orbis::File> *file, orbis::ErrorCode HmdMmapDevice::open(rx::Ref<orbis::File> *file,

View file

@ -2,7 +2,6 @@
#include "orbis/KernelAllocator.hpp" #include "orbis/KernelAllocator.hpp"
#include "orbis/file.hpp" #include "orbis/file.hpp"
#include "orbis/utils/Logs.hpp" #include "orbis/utils/Logs.hpp"
#include "vm.hpp"
struct RngDevice : public orbis::IoDevice { struct RngDevice : public orbis::IoDevice {
orbis::ErrorCode open(rx::Ref<orbis::File> *file, const char *path, orbis::ErrorCode open(rx::Ref<orbis::File> *file, const char *path,
@ -19,24 +18,8 @@ static orbis::ErrorCode rng_ioctl(orbis::File *file, std::uint64_t request,
return {}; return {};
} }
static orbis::ErrorCode rng_mmap(orbis::File *file, void **address,
std::uint64_t size, std::int32_t prot,
std::int32_t flags, std::int64_t offset,
orbis::Thread *thread) {
ORBIS_LOG_FATAL("rng mmap", address, size, offset);
auto result = vm::map(*address, size, prot, flags);
if (result == (void *)-1) {
return orbis::ErrorCode::INVAL; // TODO
}
*address = result;
return {};
}
static const orbis::FileOps ops = { static const orbis::FileOps ops = {
.ioctl = rng_ioctl, .ioctl = rng_ioctl
.mmap = rng_mmap,
}; };
orbis::ErrorCode RngDevice::open(rx::Ref<orbis::File> *file, const char *path, orbis::ErrorCode RngDevice::open(rx::Ref<orbis::File> *file, const char *path,

View file

@ -5,7 +5,6 @@
#include "orbis/thread/Thread.hpp" #include "orbis/thread/Thread.hpp"
#include "orbis/utils/Logs.hpp" #include "orbis/utils/Logs.hpp"
#include "rx/SharedMutex.hpp" #include "rx/SharedMutex.hpp"
#include "vm.hpp"
struct SblSrvFile : public orbis::File {}; struct SblSrvFile : public orbis::File {};
@ -23,24 +22,8 @@ static orbis::ErrorCode sbl_srv_ioctl(orbis::File *file, std::uint64_t request,
return {}; return {};
} }
static orbis::ErrorCode sbl_srv_mmap(orbis::File *file, void **address,
std::uint64_t size, std::int32_t prot,
std::int32_t flags, std::int64_t offset,
orbis::Thread *thread) {
ORBIS_LOG_FATAL("sbl_srv mmap", address, size, offset);
auto result = vm::map(*address, size, prot, flags);
if (result == (void *)-1) {
return orbis::ErrorCode::INVAL; // TODO
}
*address = result;
return {};
}
static const orbis::FileOps ops = { static const orbis::FileOps ops = {
.ioctl = sbl_srv_ioctl, .ioctl = sbl_srv_ioctl,
.mmap = sbl_srv_mmap,
}; };
orbis::ErrorCode SblSrvDevice::open(rx::Ref<orbis::File> *file, orbis::ErrorCode SblSrvDevice::open(rx::Ref<orbis::File> *file,

View file

@ -4,15 +4,20 @@
#include "io-device.hpp" #include "io-device.hpp"
#include "orbis/KernelContext.hpp" #include "orbis/KernelContext.hpp"
#include "orbis/osem.hpp" #include "orbis/osem.hpp"
#include "orbis/pmem.hpp"
#include "orbis/thread/Process.hpp" #include "orbis/thread/Process.hpp"
#include "orbis/thread/Thread.hpp"
#include "orbis/utils/Logs.hpp" #include "orbis/utils/Logs.hpp"
#include "orbis/vmem.hpp"
#include "rx/AddressRange.hpp"
#include "rx/align.hpp"
#include "rx/die.hpp"
#include "rx/format.hpp" #include "rx/format.hpp"
#include "rx/hexdump.hpp" #include "rx/hexdump.hpp"
#include "rx/mem.hpp"
#include "rx/print.hpp" #include "rx/print.hpp"
#include "rx/watchdog.hpp" #include "rx/watchdog.hpp"
#include "vfs.hpp" #include "vfs.hpp"
#include "vm.hpp" #include <bit>
#include <cstdint> #include <cstdint>
#include <fcntl.h> #include <fcntl.h>
#include <filesystem> #include <filesystem>
@ -21,45 +26,65 @@
ipmi::IpmiClient ipmi::audioIpmiClient; ipmi::IpmiClient ipmi::audioIpmiClient;
orbis::Process *g_workerProcess;
template <typename T = std::byte> struct GuestAlloc { template <typename T = std::byte> struct GuestAlloc {
orbis::ptr<T> guestAddress; orbis::ptr<T> guestAddress;
std::size_t size;
GuestAlloc(std::size_t size) { GuestAlloc(std::size_t size) : size(size) {
if (size == 0) { if (size == 0) {
guestAddress = nullptr; guestAddress = nullptr;
} else { } else {
guestAddress = orbis::ptr<T>( size = rx::alignUp(size, orbis::vmem::kPageSize);
vm::map(nullptr, size, vm::kMapProtCpuRead | vm::kMapProtCpuWrite,
vm::kMapFlagPrivate | vm::kMapFlagAnonymous)); auto [range, vmemErrc] = orbis::vmem::mapFlex(
g_workerProcess ? g_workerProcess : orbis::g_currentThread->tproc,
size,
orbis::vmem::Protection::CpuRead | orbis::vmem::Protection::CpuWrite);
rx::dieIf(vmemErrc != orbis::ErrorCode{},
"impi: failed to map memory, error {}",
static_cast<int>(vmemErrc));
guestAddress = std::bit_cast<orbis::ptr<T>>(range.beginAddress());
} }
} }
GuestAlloc() : GuestAlloc(sizeof(T)) {} GuestAlloc() : GuestAlloc(sizeof(T)) {}
GuestAlloc(const T &data) : GuestAlloc() { GuestAlloc(const T &data) : GuestAlloc() {
if (orbis::uwrite(guestAddress, data) != orbis::ErrorCode{}) { if (auto errc = orbis::uwrite(guestAddress, data);
std::abort(); errc != orbis::ErrorCode{}) {
rx::die("ipmi: failed to write data to allocated page {}, error {}",
(void *)guestAddress, errc);
} }
} }
GuestAlloc(const void *data, std::size_t size) : GuestAlloc(size) { GuestAlloc(const void *data, std::size_t size) : GuestAlloc(size) {
if (orbis::uwriteRaw(guestAddress, data, size) != orbis::ErrorCode{}) { if (auto errc = orbis::uwriteRaw(guestAddress, data, size);
std::abort(); errc != orbis::ErrorCode{}) {
rx::die("ipmi: failed to write data to allocated page {}, error {}, data "
"{}, size {}",
(void *)guestAddress, errc, data, size);
} }
} }
GuestAlloc(const GuestAlloc &) = delete; GuestAlloc(const GuestAlloc &) = delete;
GuestAlloc(GuestAlloc &&other) noexcept : guestAddress(other.guestAddress) { GuestAlloc(GuestAlloc &&other) noexcept : guestAddress(other.guestAddress) {
other.guestAddress = 0; other.guestAddress = nullptr;
} }
GuestAlloc &operator=(GuestAlloc &&other) noexcept { GuestAlloc &operator=(GuestAlloc &&other) noexcept {
std::swap(guestAddress, other.guestAddress); std::swap(guestAddress, other.guestAddress);
} }
~GuestAlloc() { ~GuestAlloc() {
if (guestAddress != 0) { if (guestAddress != nullptr) {
vm::unmap(guestAddress, sizeof(T)); orbis::vmem::unmap(
g_workerProcess ? g_workerProcess : orbis::g_currentThread->tproc,
rx::AddressRange::fromBeginSize(
std::bit_cast<orbis::uintptr_t>(guestAddress), size));
} }
} }
@ -117,6 +142,10 @@ orbis::sint ipmi::IpmiClient::sendSyncMessageRaw(
return serverResult; return serverResult;
} }
void ipmi::setWorkerProcess(orbis::Process *process) {
g_workerProcess = process;
}
ipmi::IpmiClient ipmi::createIpmiClient(orbis::Thread *thread, ipmi::IpmiClient ipmi::createIpmiClient(orbis::Thread *thread,
const char *name) { const char *name) {
rx::Ref<orbis::IpmiClient> client; rx::Ref<orbis::IpmiClient> client;

View file

@ -258,6 +258,8 @@ struct IpmiServer {
extern ipmi::IpmiClient audioIpmiClient; extern ipmi::IpmiClient audioIpmiClient;
void setWorkerProcess(orbis::Process *process);
IpmiClient createIpmiClient(orbis::Thread *thread, const char *name); IpmiClient createIpmiClient(orbis::Thread *thread, const char *name);
IpmiServer &createIpmiServer(orbis::Process *process, const char *name); IpmiServer &createIpmiServer(orbis::Process *process, const char *name);
orbis::EventFlag *createEventFlag(std::string_view name, uint32_t attrs, orbis::EventFlag *createEventFlag(std::string_view name, uint32_t attrs,

View file

@ -1,11 +1,23 @@
#include "linker.hpp" #include "linker.hpp"
#include "io-device.hpp" #include "kernel/KernelObject.hpp"
#include "orbis/IoDevice.hpp"
#include "orbis/KernelAllocator.hpp" #include "orbis/KernelAllocator.hpp"
#include "orbis/KernelObject.hpp"
#include "orbis/fmem.hpp"
#include "orbis/module/Module.hpp" #include "orbis/module/Module.hpp"
#include "orbis/pmem.hpp"
#include "orbis/stat.hpp" #include "orbis/stat.hpp"
#include "orbis/uio.hpp" #include "orbis/uio.hpp"
#include "orbis/vmem.hpp"
#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"
#include "rx/print.hpp"
#include "vfs.hpp" #include "vfs.hpp"
#include "vm.hpp"
#include <bit> #include <bit>
#include <crypto/sha1.h> #include <crypto/sha1.h>
#include <elf.h> #include <elf.h>
@ -16,8 +28,6 @@
#include <sys/mman.h> #include <sys/mman.h>
#include <unordered_map> #include <unordered_map>
std::uint64_t monoPimpAddress;
static std::vector<std::byte> unself(const std::byte *image, std::size_t size) { static std::vector<std::byte> unself(const std::byte *image, std::size_t size) {
struct [[gnu::packed]] Header { struct [[gnu::packed]] Header {
std::uint32_t magic; std::uint32_t magic;
@ -70,8 +80,7 @@ static std::vector<std::byte> unself(const std::byte *image, std::size_t size) {
auto &segment = segments[i]; auto &segment = segments[i];
if ((segment.flags & 0x7fb) != 0 || if ((segment.flags & 0x7fb) != 0 ||
segment.decryptedSize != segment.encryptedSize) { segment.decryptedSize != segment.encryptedSize) {
std::fprintf(stderr, "Unsupported self segment (%lx)\n", segment.flags); rx::die("Unsupported self segment ({:x})", segment.flags);
std::abort();
} }
if (~segment.flags & 0x800) { if (~segment.flags & 0x800) {
@ -92,6 +101,8 @@ static std::vector<std::byte> unself(const std::byte *image, std::size_t size) {
return result; return result;
} }
std::uint64_t monoPimpAddress;
std::uint64_t rx::linker::encodeFid(std::string_view fid) { std::uint64_t rx::linker::encodeFid(std::string_view fid) {
static const char suffix[] = static const char suffix[] =
"\x51\x8D\x64\xA6\x35\xDE\xD8\xC1\xE6\xB0\x39\xB1\xC3\xE5\x52\x30"; "\x51\x8D\x64\xA6\x35\xDE\xD8\xC1\xE6\xB0\x39\xB1\xC3\xE5\x52\x30";
@ -359,12 +370,159 @@ void rx::linker::override(std::string originalModuleName,
std::move(replacedModulePath); std::move(replacedModulePath);
} }
rx::Ref<orbis::Module> rx::linker::loadModule(std::span<std::byte> image, struct ElfFile : orbis::File {
orbis::Process *process) { rx::AddressRange physicalMemory;
orbis::kvector<std::byte> image;
bool initialized = false;
};
struct ElfDevice : orbis::IoDevice {
rx::shared_mutex mtx;
orbis::kmap<orbis::kstring, rx::Ref<ElfFile>, rx::StringLess> files;
orbis::ErrorCode open(rx::Ref<orbis::File> *file, const char *path,
std::uint32_t flags, std::uint32_t mode,
orbis::Thread *thread) override {
std::lock_guard lock(mtx);
auto it = files.lower_bound(path);
if (it != files.end() && it->first == path) {
*file = it->second;
return {};
}
rx::Ref<orbis::File> rawFile;
if (auto result =
vfs::open(path, orbis::kOpenFlagReadOnly, 0, &rawFile, thread)) {
return result.errc();
}
orbis::Stat fileStat;
if (auto error = rawFile->ops->stat(rawFile.get(), &fileStat, nullptr);
error != orbis::ErrorCode{}) {
return error;
}
auto len = fileStat.size;
std::vector<std::byte> image(len);
auto ptr = image.data();
orbis::IoVec ioVec{
.base = ptr,
.len = static_cast<std::uint64_t>(len),
};
orbis::Uio io{
.offset = 0,
.iov = &ioVec,
.iovcnt = 1,
.resid = 0,
.segflg = orbis::UioSeg::SysSpace,
.rw = orbis::UioRw::Read,
.td = thread,
};
while (io.offset < image.size()) {
ioVec = {
.base = ptr + io.offset,
.len = image.size() - io.offset,
};
auto result = rawFile->ops->read(rawFile.get(), &io, thread);
rx::dieIf(result != orbis::ErrorCode{}, "elf: '{}' read failed: {}", path,
static_cast<int>(result));
}
rawFile = {};
if (image[0] != std::byte{'\x7f'} || image[1] != std::byte{'E'} ||
image[2] != std::byte{'L'} || image[3] != std::byte{'F'}) {
image = unself(image.data(), image.size());
}
Elf64_Ehdr header;
std::memcpy(&header, image.data(), sizeof(Elf64_Ehdr));
Elf64_Phdr phdrsStorage[16];
if (header.e_phnum > std::size(phdrsStorage)) {
rx::die("elf: unexpected count of segments {}, {}", header.e_phnum, path);
}
std::memcpy(phdrsStorage, image.data() + header.e_phoff,
header.e_phnum * sizeof(Elf64_Phdr));
auto phdrs = std::span(phdrsStorage, header.e_phnum);
std::uint64_t endAddress = 0;
std::uint64_t baseAddress = ~static_cast<std::uint64_t>(0);
for (auto &phdr : phdrs) {
switch (phdr.p_type) {
case kElfProgramTypeLoad:
baseAddress =
std::min(baseAddress, rx::alignDown(phdr.p_vaddr, phdr.p_align));
if ((phdr.p_flags & PF_W) == 0) {
endAddress =
std::max(endAddress, rx::alignUp(phdr.p_vaddr + phdr.p_memsz,
orbis::vmem::kPageSize));
}
break;
}
}
auto imageSize = endAddress - baseAddress;
auto alignedImageSize = rx::alignUp(imageSize, orbis::vmem::kPageSize);
auto [allocationRange, errc] = orbis::pmem::allocate(
orbis::pmem::getSize() - 1, alignedImageSize,
orbis::AllocationFlags::Stack, orbis::vmem::kPageSize);
if (errc != orbis::ErrorCode{}) {
return errc;
}
auto newFile = orbis::knew<ElfFile>();
newFile->physicalMemory = allocationRange;
newFile->image = orbis::kvector<std::byte>(image.begin(), image.end());
newFile->device = this;
*file = newFile;
files.emplace_hint(it, orbis::kstring(path), newFile);
return {};
}
orbis::ErrorCode map(rx::AddressRange range, std::int64_t offset,
rx::EnumBitSet<orbis::vmem::Protection> protection,
orbis::File *file, orbis::Process *) override {
auto elf = static_cast<ElfFile *>(file);
auto physicalRange = rx::AddressRange::fromBeginSize(
elf->physicalMemory.beginAddress() + offset, range.size());
if (!physicalRange.isValid() ||
physicalRange.endAddress() > elf->physicalMemory.endAddress()) {
return orbis::ErrorCode::INVAL;
}
return orbis::pmem::map(range.beginAddress(), physicalRange,
orbis::vmem::toCpuProtection(protection));
}
void serialize(rx::Serializer &s) const {
// FIXME: implement
}
void deserialize(rx::Deserializer &d) {
// FIXME: implement
}
};
static auto g_elfDevice = orbis::createGlobalObject<ElfDevice>();
static rx::Ref<orbis::Module> loadModule(ElfFile *elf, orbis::Process *process,
std::string_view name) {
rx::Ref<orbis::Module> result{orbis::knew<orbis::Module>()}; rx::Ref<orbis::Module> result{orbis::knew<orbis::Module>()};
Elf64_Ehdr header; Elf64_Ehdr header;
std::memcpy(&header, image.data(), sizeof(Elf64_Ehdr)); std::memcpy(&header, elf->image.data(), sizeof(Elf64_Ehdr));
result->type = header.e_type; result->type = header.e_type;
Elf64_Phdr phdrsStorage[16]; Elf64_Phdr phdrsStorage[16];
@ -372,7 +530,7 @@ rx::Ref<orbis::Module> rx::linker::loadModule(std::span<std::byte> image,
std::abort(); std::abort();
} }
std::memcpy(phdrsStorage, image.data() + header.e_phoff, std::memcpy(phdrsStorage, elf->image.data() + header.e_phoff,
header.e_phnum * sizeof(Elf64_Phdr)); header.e_phnum * sizeof(Elf64_Phdr));
auto phdrs = std::span(phdrsStorage, header.e_phnum); auto phdrs = std::span(phdrsStorage, header.e_phnum);
@ -402,8 +560,8 @@ rx::Ref<orbis::Module> rx::linker::loadModule(std::span<std::byte> image,
case kElfProgramTypeLoad: case kElfProgramTypeLoad:
baseAddress = baseAddress =
std::min(baseAddress, rx::alignDown(phdr.p_vaddr, phdr.p_align)); std::min(baseAddress, rx::alignDown(phdr.p_vaddr, phdr.p_align));
endAddress = std::max( endAddress = std::max(endAddress, rx::alignUp(phdr.p_vaddr + phdr.p_memsz,
endAddress, rx::alignUp(phdr.p_vaddr + phdr.p_memsz, vm::kPageSize)); orbis::vmem::kPageSize));
break; break;
case kElfProgramTypeDynamic: case kElfProgramTypeDynamic:
dynamicPhdrIndex = index; dynamicPhdrIndex = index;
@ -436,8 +594,8 @@ rx::Ref<orbis::Module> rx::linker::loadModule(std::span<std::byte> image,
sceRelRoPhdrIndex = index; sceRelRoPhdrIndex = index;
baseAddress = baseAddress =
std::min(baseAddress, rx::alignDown(phdr.p_vaddr, phdr.p_align)); std::min(baseAddress, rx::alignDown(phdr.p_vaddr, phdr.p_align));
endAddress = std::max( endAddress = std::max(endAddress, rx::alignUp(phdr.p_vaddr + phdr.p_memsz,
endAddress, rx::alignUp(phdr.p_vaddr + phdr.p_memsz, vm::kPageSize)); orbis::vmem::kPageSize));
break; break;
case kElfProgramTypeGnuEhFrame: case kElfProgramTypeGnuEhFrame:
gnuEhFramePhdrIndex = index; gnuEhFramePhdrIndex = index;
@ -455,17 +613,27 @@ rx::Ref<orbis::Module> rx::linker::loadModule(std::span<std::byte> image,
} }
auto imageSize = endAddress - baseAddress; auto imageSize = endAddress - baseAddress;
auto alignedImageSize = rx::alignUp(imageSize, orbis::vmem::kPageSize);
auto imageBase = reinterpret_cast<std::byte *>( rx::EnumBitSet<orbis::AllocationFlags> allocationFlags = {};
vm::map(reinterpret_cast<void *>(baseAddress),
rx::alignUp(imageSize, vm::kPageSize), 0,
vm::kMapFlagPrivate | vm::kMapFlagAnonymous |
(baseAddress ? vm::kMapFlagFixed : 0)));
if (imageBase == MAP_FAILED) { auto mapHitAddress = baseAddress;
std::abort(); if (baseAddress != 0) {
allocationFlags |= orbis::AllocationFlags::Fixed;
} else if (header.e_type == rx::linker::kElfTypeDyn ||
header.e_type == rx::linker::kElfTypeSceDynamic) {
mapHitAddress = 0x800000000;
} }
auto [imageRange, errc] = orbis::vmem::reserve(
process, mapHitAddress, alignedImageSize, allocationFlags);
rx::dieIf(errc != orbis::ErrorCode{},
"failed to map image memory {}, errno {}", imageSize,
static_cast<int>(errc));
auto imageBase = reinterpret_cast<std::byte *>(imageRange.beginAddress());
result->entryPoint = header.e_entry result->entryPoint = header.e_entry
? reinterpret_cast<std::uintptr_t>( ? reinterpret_cast<std::uintptr_t>(
imageBase - baseAddress + header.e_entry) imageBase - baseAddress + header.e_entry)
@ -473,7 +641,7 @@ rx::Ref<orbis::Module> rx::linker::loadModule(std::span<std::byte> image,
if (interpPhdrIndex >= 0) { if (interpPhdrIndex >= 0) {
result->interp = reinterpret_cast<const char *>( result->interp = reinterpret_cast<const char *>(
image.data() + phdrs[interpPhdrIndex].p_offset); elf->image.data() + phdrs[interpPhdrIndex].p_offset);
} }
if (sceProcParamIndex >= 0) { if (sceProcParamIndex >= 0) {
@ -518,19 +686,12 @@ rx::Ref<orbis::Module> rx::linker::loadModule(std::span<std::byte> image,
}; };
auto *exinfo = reinterpret_cast<GnuExceptionInfo *>( auto *exinfo = reinterpret_cast<GnuExceptionInfo *>(
image.data() + phdrs[gnuEhFramePhdrIndex].p_offset); elf->image.data() + phdrs[gnuEhFramePhdrIndex].p_offset);
if (exinfo->version != 1) { rx::dieIf(exinfo->version != 1, "Unexpected gnu ehframe version");
std::abort(); rx::dieIf(exinfo->fdeCount != 0x03, "Unexpected gnu ehframe fde count");
} rx::dieIf(exinfo->encodingTable != 0x3b,
"Unexpected gnu ehframe encoding table");
if (exinfo->fdeCount != 0x03) {
std::abort();
}
if (exinfo->encodingTable != 0x3b) {
std::abort();
}
std::byte *dataBuffer = nullptr; std::byte *dataBuffer = nullptr;
@ -541,7 +702,7 @@ rx::Ref<orbis::Module> rx::linker::loadModule(std::span<std::byte> image,
auto offset = *reinterpret_cast<std::int32_t *>(&exinfo->first); auto offset = *reinterpret_cast<std::int32_t *>(&exinfo->first);
dataBuffer = &exinfo->first + sizeof(std::int32_t) + offset; dataBuffer = &exinfo->first + sizeof(std::int32_t) + offset;
} else { } else {
std::abort(); rx::die("unexpected gnu ehframe encoding {:x}", exinfo->encoding);
} }
auto *dataBufferIt = dataBuffer; auto *dataBufferIt = dataBuffer;
@ -563,7 +724,7 @@ rx::Ref<orbis::Module> rx::linker::loadModule(std::span<std::byte> image,
result->ehFrame = result->ehFrame =
imageBase - baseAddress + phdrs[gnuEhFramePhdrIndex].p_vaddr + imageBase - baseAddress + phdrs[gnuEhFramePhdrIndex].p_vaddr +
(dataBuffer - image.data() - phdrs[gnuEhFramePhdrIndex].p_offset); (dataBuffer - elf->image.data() - phdrs[gnuEhFramePhdrIndex].p_offset);
result->ehFrameSize = dataBufferIt - dataBuffer; result->ehFrameSize = dataBufferIt - dataBuffer;
} }
@ -575,7 +736,7 @@ rx::Ref<orbis::Module> rx::linker::loadModule(std::span<std::byte> image,
if (dynamicPhdrIndex >= 0 && phdrs[dynamicPhdrIndex].p_filesz > 0) { if (dynamicPhdrIndex >= 0 && phdrs[dynamicPhdrIndex].p_filesz > 0) {
auto &dynPhdr = phdrs[dynamicPhdrIndex]; auto &dynPhdr = phdrs[dynamicPhdrIndex];
std::vector<Elf64_Dyn> dyns(dynPhdr.p_filesz / sizeof(Elf64_Dyn)); std::vector<Elf64_Dyn> dyns(dynPhdr.p_filesz / sizeof(Elf64_Dyn));
std::memcpy(dyns.data(), image.data() + dynPhdr.p_offset, std::memcpy(dyns.data(), elf->image.data() + dynPhdr.p_offset,
dyns.size() * sizeof(Elf64_Dyn)); dyns.size() * sizeof(Elf64_Dyn));
int sceStrtabIndex = -1; int sceStrtabIndex = -1;
@ -616,7 +777,7 @@ rx::Ref<orbis::Module> rx::linker::loadModule(std::span<std::byte> image,
auto sceStrtab = auto sceStrtab =
sceStrtabIndex >= 0 && sceDynlibDataPhdrIndex >= 0 sceStrtabIndex >= 0 && sceDynlibDataPhdrIndex >= 0
? reinterpret_cast<const char *>( ? reinterpret_cast<const char *>(
image.data() + elf->image.data() +
dynTabOffsetGet(dyns[sceStrtabIndex].d_un.d_val) + dynTabOffsetGet(dyns[sceStrtabIndex].d_un.d_val) +
phdrs[sceDynlibDataPhdrIndex].p_offset) phdrs[sceDynlibDataPhdrIndex].p_offset)
: nullptr; : nullptr;
@ -625,12 +786,12 @@ rx::Ref<orbis::Module> rx::linker::loadModule(std::span<std::byte> image,
if (strtab == nullptr && strtabIndex >= 0) { if (strtab == nullptr && strtabIndex >= 0) {
strtab = reinterpret_cast<const char *>( strtab = reinterpret_cast<const char *>(
image.data() + dynTabOffsetGet(dyns[strtabIndex].d_un.d_val)); elf->image.data() + dynTabOffsetGet(dyns[strtabIndex].d_un.d_val));
} }
auto sceDynlibData = auto sceDynlibData =
sceDynlibDataPhdrIndex >= 0 sceDynlibDataPhdrIndex >= 0
? image.data() + phdrs[sceDynlibDataPhdrIndex].p_offset ? elf->image.data() + phdrs[sceDynlibDataPhdrIndex].p_offset
: nullptr; : nullptr;
auto sceSymtabData = auto sceSymtabData =
@ -646,15 +807,13 @@ rx::Ref<orbis::Module> rx::linker::loadModule(std::span<std::byte> image,
: 0; : 0;
if (symtab == nullptr && symtabIndex >= 0) { if (symtab == nullptr && symtabIndex >= 0) {
if (hashIndex < 0) { rx::dieIf(hashIndex < 0, "elf: SYMTAB without HASH! {}",
std::fprintf(stderr, "SYMTAB without HASH!\n"); result->moduleName);
std::abort();
}
symtab = reinterpret_cast<const Elf64_Sym *>( symtab = reinterpret_cast<const Elf64_Sym *>(
image.data() + dynTabOffsetGet(dyns[symtabIndex].d_un.d_val)); elf->image.data() + dynTabOffsetGet(dyns[symtabIndex].d_un.d_val));
symtabSize = *reinterpret_cast<const std::uint32_t *>( symtabSize = *reinterpret_cast<const std::uint32_t *>(
image.data() + dynTabOffsetGet(dyns[hashIndex].d_un.d_val) + elf->image.data() + dynTabOffsetGet(dyns[hashIndex].d_un.d_val) +
sizeof(std::uint32_t)); sizeof(std::uint32_t));
} }
@ -760,7 +919,7 @@ rx::Ref<orbis::Module> rx::linker::loadModule(std::span<std::byte> image,
sceDynlibData + dyn.d_un.d_ptr); sceDynlibData + dyn.d_un.d_ptr);
} else { } else {
pltRelocations = reinterpret_cast<orbis::Relocation *>( pltRelocations = reinterpret_cast<orbis::Relocation *>(
image.data() + dyn.d_un.d_ptr); elf->image.data() + dyn.d_un.d_ptr);
} }
break; break;
case kElfDynamicTypeScePltRel: case kElfDynamicTypeScePltRel:
@ -781,7 +940,7 @@ rx::Ref<orbis::Module> rx::linker::loadModule(std::span<std::byte> image,
sceDynlibData + dyn.d_un.d_ptr); sceDynlibData + dyn.d_un.d_ptr);
} else { } else {
nonPltRelocations = reinterpret_cast<orbis::Relocation *>( nonPltRelocations = reinterpret_cast<orbis::Relocation *>(
image.data() + dyn.d_un.d_ptr); elf->image.data() + dyn.d_un.d_ptr);
} }
break; break;
case kElfDynamicTypeRelaSize: case kElfDynamicTypeRelaSize:
@ -800,10 +959,7 @@ rx::Ref<orbis::Module> rx::linker::loadModule(std::span<std::byte> image,
} }
} }
if (hasPs4Dyn && hasPs5Dyn) { rx::dieIf(hasPs4Dyn && hasPs5Dyn, "unexpected import type");
std::fprintf(stderr, "unexpected import type\n");
std::abort();
}
if (!hasPs4Dyn && !hasPs5Dyn && interpPhdrIndex >= 0) { if (!hasPs4Dyn && !hasPs5Dyn && interpPhdrIndex >= 0) {
result->dynType = orbis::DynType::FreeBsd; result->dynType = orbis::DynType::FreeBsd;
@ -854,24 +1010,23 @@ rx::Ref<orbis::Module> rx::linker::loadModule(std::span<std::byte> image,
library = moduleLibary.substr(0, hashPos); library = moduleLibary.substr(0, hashPos);
module = moduleLibary.substr(hashPos + 1); module = moduleLibary.substr(hashPos + 1);
auto libaryNid = *decodeNid(library); auto libaryNid = *rx::linker::decodeNid(library);
auto moduleNid = *decodeNid(module); auto moduleNid = *rx::linker::decodeNid(module);
symbol.libraryIndex = idToLibraryIndex.at(libaryNid); symbol.libraryIndex = idToLibraryIndex.at(libaryNid);
symbol.moduleIndex = idToModuleIndex.at(moduleNid); symbol.moduleIndex = idToModuleIndex.at(moduleNid);
symbol.id = *decodeNid(name); symbol.id = *rx::linker::decodeNid(name);
if (name == "5JrIq4tzVIo") { if (name == "5JrIq4tzVIo") {
monoPimpAddress = symbol.address + (std::uint64_t)imageBase; monoPimpAddress = symbol.address + (std::uint64_t)imageBase;
std::fprintf(stderr, "mono_pimp address = %lx\n", rx::println(stderr, "mono_pimp address = {:x}", monoPimpAddress);
monoPimpAddress);
} }
} else if (auto nid = decodeNid(fullName)) { } else if (auto nid = rx::linker::decodeNid(fullName)) {
symbol.id = *nid; symbol.id = *nid;
symbol.libraryIndex = -1; symbol.libraryIndex = -1;
symbol.moduleIndex = -1; symbol.moduleIndex = -1;
} else { } else {
symbol.id = symbol.id = rx::linker::encodeFid(
encodeFid(strtab + static_cast<std::uint32_t>(sym.st_name)); strtab + static_cast<std::uint32_t>(sym.st_name));
symbol.libraryIndex = -1; symbol.libraryIndex = -1;
symbol.moduleIndex = -1; symbol.moduleIndex = -1;
} }
@ -882,50 +1037,126 @@ rx::Ref<orbis::Module> rx::linker::loadModule(std::span<std::byte> image,
} }
} }
std::string_view mapName = result->moduleName;
if (header.e_type == rx::linker::kElfTypeExec ||
header.e_type == rx::linker::kElfTypeSceExec ||
header.e_type == rx::linker::kElfTypeSceDynExec) {
mapName = "executable";
}
for (auto phdr : phdrs) { for (auto phdr : phdrs) {
if (phdr.p_type == kElfProgramTypeLoad || if (phdr.p_type == kElfProgramTypeLoad ||
phdr.p_type == kElfProgramTypeSceRelRo || phdr.p_type == kElfProgramTypeSceRelRo ||
phdr.p_type == kElfProgramTypeGnuRelRo) { phdr.p_type == kElfProgramTypeGnuRelRo) {
auto segmentEnd = rx::alignUp(phdr.p_vaddr + phdr.p_memsz, vm::kPageSize); rx::EnumBitSet<orbis::vmem::Protection> protFlags = {};
auto segmentBegin =
rx::alignDown(phdr.p_vaddr - baseAddress, phdr.p_align);
auto segmentSize = segmentEnd - segmentBegin;
::mprotect(imageBase + segmentBegin, segmentSize, PROT_WRITE);
std::memcpy(imageBase + phdr.p_vaddr - baseAddress,
image.data() + phdr.p_offset, phdr.p_filesz);
if (phdr.p_type == kElfProgramTypeSceRelRo ||
phdr.p_type == kElfProgramTypeGnuRelRo) {
phdr.p_flags |= vm::kMapProtCpuWrite; // TODO: reprotect on relocations
}
int mapFlags = 0;
if (phdr.p_flags & PF_X) { if (phdr.p_flags & PF_X) {
mapFlags |= vm::kMapProtCpuExec; protFlags |= orbis::vmem::Protection::CpuExec;
} }
if (phdr.p_flags & PF_W) { if (phdr.p_flags & PF_W) {
mapFlags |= vm::kMapProtCpuWrite; protFlags |= orbis::vmem::Protection::CpuWrite;
} }
if (phdr.p_flags & PF_R) { if (phdr.p_flags & PF_R) {
mapFlags |= vm::kMapProtCpuRead; protFlags |= orbis::vmem::Protection::CpuRead;
} }
if (mapFlags == 0) { if (!protFlags) {
mapFlags = vm::kMapProtCpuWrite; protFlags = orbis::vmem::Protection::CpuRead |
orbis::vmem::Protection::CpuWrite;
} }
vm::protect(imageBase + segmentBegin, segmentSize, mapFlags); phdr.p_memsz = rx::alignUp(phdr.p_memsz, orbis::vmem::kPageSize);
if (phdr.p_type == kElfProgramTypeLoad) { auto segmentEnd =
if (result->segmentCount >= std::size(result->segments)) { rx::alignUp(phdr.p_vaddr + phdr.p_memsz, orbis::vmem::kPageSize);
std::abort(); auto segmentBegin = rx::alignDown(phdr.p_vaddr, phdr.p_align);
auto segmentRange =
rx::AddressRange::fromBeginEnd(segmentBegin, segmentEnd);
if ((phdr.p_flags & PF_W) || phdr.p_type == kElfProgramTypeSceRelRo ||
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);
rx::dieIf(vmemErrc != orbis::ErrorCode{},
"elf: failed to map flexible to virtual memory {}",
(int)vmemErrc);
if ((phdr.p_flags & PF_W) == 0) {
rx::mem::protect(vmem,
rx::mem::Protection::R | rx::mem::Protection::W);
} }
auto &segment = result->segments[result->segmentCount++]; std::memcpy(imageBase + phdr.p_vaddr, elf->image.data() + phdr.p_offset,
phdr.p_filesz);
rx::println(stderr, "{}: RW segment {:x}-{:x}, {}", result->moduleName,
segmentRange.beginAddress(), segmentRange.endAddress(),
protFlags.raw());
} else {
// map elf device directly
rx::dieIf(rx::alignUp(phdr.p_filesz, orbis::vmem::kPageSize) !=
segmentRange.size(),
"unexpected read only segment size, {:x} vs {:x}",
phdr.p_filesz, segmentRange.size());
rx::println(stderr, "{}: RX segment {:x}-{:x}, {}", result->moduleName,
segmentRange.beginAddress(), segmentRange.endAddress(),
protFlags.raw());
{
std::lock_guard lock(elf->mtx);
if (!elf->initialized) {
auto [vmem, vmemErrc] = orbis::vmem::mapFile(
process, imageRange.beginAddress() + segmentBegin,
segmentRange.size(), orbis::AllocationFlags::Fixed,
protFlags | orbis::vmem::Protection::CpuWrite, {}, {}, elf,
segmentBegin, 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,
elf->image.data() + phdr.p_offset, phdr.p_filesz);
elf->initialized = true;
}
}
auto [vmem, vmemErrc] = orbis::vmem::mapFile(
process, imageRange.beginAddress() + segmentBegin,
segmentRange.size(), orbis::AllocationFlags::Fixed, protFlags,
orbis::vmem::BlockFlags::FlexibleMemory |
orbis::vmem::BlockFlags::Commited,
orbis::vmem::BlockFlagsEx::Shared, elf, segmentBegin, mapName);
rx::dieIf(vmemErrc != orbis::ErrorCode{},
"elf: failed to map elf to virtual memory {}", (int)vmemErrc);
}
std::uint32_t segmentIndex;
if (phdr.p_type == kElfProgramTypeLoad) {
segmentIndex = protFlags & orbis::vmem::Protection::CpuExec ? 0 : 1;
} else {
segmentIndex = 2;
}
auto &segment = result->segments[segmentIndex];
if (segment.addr != nullptr) {
rx::println(stderr, "elf: corrupted, segment {} overriding. {}",
segmentIndex, name);
} else {
segment.addr = imageBase + segmentBegin; segment.addr = imageBase + segmentBegin;
segment.size = phdr.p_memsz; segment.size = phdr.p_memsz;
segment.prot = phdr.p_flags; segment.prot = protFlags.toUnderlying();
result->segmentCount = std::max(segmentIndex + 1, result->segmentCount);
} }
} }
} }
@ -951,16 +1182,19 @@ rx::Ref<orbis::Module> rx::linker::loadModule(std::span<std::byte> image,
result->phNum = header.e_phnum; result->phNum = header.e_phnum;
result->proc = process; result->proc = process;
std::printf("Loaded module '%s' (%lx) from object '%s', address: %p - %p\n", std::strncpy(result->soName, name.data(), sizeof(result->soName));
rx::println("Loaded module '{}' ({:x}) from object '{}', "
"address: {} - {}",
result->moduleName, (unsigned long)result->attributes, result->moduleName, (unsigned long)result->attributes,
result->soName, imageBase, (char *)imageBase + result->size); result->soName, (void *)imageBase,
(void *)((char *)imageBase + result->size));
for (const auto &mod : result->neededModules) { for (const auto &mod : result->neededModules) {
std::printf(" needed module '%s' (%lx)\n", mod.name.c_str(), rx::println(" needed module '{}' ({:x})", mod.name.c_str(),
(unsigned long)mod.attr); (unsigned long)mod.attr);
} }
for (const auto &lib : result->neededLibraries) { for (const auto &lib : result->neededLibraries) {
std::printf(" needed library '%s' (%lx), kind %s\n", lib.name.c_str(), rx::println(" needed library '{}' ({:x}), kind {}", lib.name.c_str(),
(unsigned long)lib.attr, lib.isExport ? "export" : "import"); (unsigned long)lib.attr, lib.isExport ? "export" : "import");
} }
@ -973,59 +1207,17 @@ rx::Ref<orbis::Module> rx::linker::loadModule(std::span<std::byte> image,
static rx::Ref<orbis::Module> loadModuleFileImpl(std::string_view path, static rx::Ref<orbis::Module> loadModuleFileImpl(std::string_view path,
orbis::Thread *thread) { orbis::Thread *thread) {
rx::Ref<orbis::File> instance; rx::Ref<orbis::File> elf;
if (vfs::open(path, orbis::kOpenFlagReadOnly, 0, &instance, thread) if (auto errc = g_elfDevice->open(&elf, path.data(), 0, 0, thread);
.isError()) { errc != orbis::ErrorCode{}) {
return {}; return {};
} }
orbis::Stat fileStat; if (auto sepPos = path.rfind('/'); sepPos != std::string_view::npos) {
if (instance->ops->stat(instance.get(), &fileStat, nullptr) != path.remove_prefix(sepPos + 1);
orbis::ErrorCode{}) {
return {};
} }
auto len = fileStat.size; return loadModule(elf.rawStaticCast<ElfFile>(), thread->tproc, path);
std::vector<std::byte> image(len);
auto ptr = image.data();
orbis::IoVec ioVec{
.base = ptr,
.len = static_cast<std::uint64_t>(len),
};
orbis::Uio io{
.offset = 0,
.iov = &ioVec,
.iovcnt = 1,
.resid = 0,
.segflg = orbis::UioSeg::SysSpace,
.rw = orbis::UioRw::Read,
.td = thread,
};
while (io.offset < image.size()) {
ioVec = {
.base = ptr + io.offset,
.len = image.size() - io.offset,
};
auto result = instance->ops->read(instance.get(), &io, thread);
if (result != orbis::ErrorCode{}) {
std::fprintf(stderr, "Module file reading error\n");
std::abort();
}
}
if (image[0] != std::byte{'\x7f'} || image[1] != std::byte{'E'} ||
image[2] != std::byte{'L'} || image[3] != std::byte{'F'}) {
image = unself(image.data(), image.size());
// std::ofstream(
// std::filesystem::path(path).filename().replace_extension("elf"),
// std::ios::binary)
// .write((const char *)image.data(), image.size());
}
return rx::linker::loadModule(image, thread->tproc);
} }
rx::Ref<orbis::Module> rx::linker::loadModuleFile(std::string_view path, rx::Ref<orbis::Module> rx::linker::loadModuleFile(std::string_view path,

View file

@ -75,8 +75,6 @@ enum OrbisElfType_t {
void override(std::string originalModuleName, void override(std::string originalModuleName,
std::filesystem::path replacedModulePath); std::filesystem::path replacedModulePath);
rx::Ref<orbis::Module> loadModule(std::span<std::byte> image,
orbis::Process *process);
rx::Ref<orbis::Module> loadModuleFile(std::string_view path, rx::Ref<orbis::Module> loadModuleFile(std::string_view path,
orbis::Thread *thread); orbis::Thread *thread);
} // namespace rx::linker } // namespace rx::linker

View file

@ -8,16 +8,22 @@
#include "ipmi.hpp" #include "ipmi.hpp"
#include "linker.hpp" #include "linker.hpp"
#include "ops.hpp" #include "ops.hpp"
#include "orbis/dmem.hpp"
#include "orbis/fmem.hpp"
#include "orbis/pmem.hpp"
#include "orbis/ucontext.hpp" #include "orbis/ucontext.hpp"
#include "orbis/utils/Logs.hpp" #include "orbis/utils/Logs.hpp"
#include "orbis/vmem.hpp"
#include "rx/Config.hpp" #include "rx/Config.hpp"
#include "rx/die.hpp"
#include "rx/format.hpp"
#include "rx/mem.hpp" #include "rx/mem.hpp"
#include "rx/print.hpp" #include "rx/print.hpp"
#include "rx/watchdog.hpp" #include "rx/watchdog.hpp"
#include "thread.hpp" #include "thread.hpp"
#include "vfs.hpp" #include "vfs.hpp"
#include "vm.hpp"
#include "xbyak/xbyak.h" #include "xbyak/xbyak.h"
#include <bit>
#include <optional> #include <optional>
#include <rx/Rc.hpp> #include <rx/Rc.hpp>
#include <rx/Version.hpp> #include <rx/Version.hpp>
@ -42,6 +48,7 @@
#include <pthread.h> #include <pthread.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/prctl.h> #include <sys/prctl.h>
#include <thread>
#include <ucontext.h> #include <ucontext.h>
#include <atomic> #include <atomic>
@ -65,26 +72,19 @@ handle_signal(int sig, siginfo_t *info, void *ucontext) {
orbis::g_currentThread->tproc->vmId >= 0 && sig == SIGSEGV && orbis::g_currentThread->tproc->vmId >= 0 && sig == SIGSEGV &&
signalAddress >= orbis::kMinAddress && signalAddress >= orbis::kMinAddress &&
signalAddress < orbis::kMaxAddress) { signalAddress < orbis::kMaxAddress) {
auto vmid = orbis::g_currentThread->tproc->vmId; auto process = orbis::g_currentThread->tproc;
auto vmid = process->vmId;
auto ctx = reinterpret_cast<ucontext_t *>(ucontext); auto ctx = reinterpret_cast<ucontext_t *>(ucontext);
bool isWrite = (ctx->uc_mcontext.gregs[REG_ERR] & 0x2) != 0; bool isWrite = (ctx->uc_mcontext.gregs[REG_ERR] & 0x2) != 0;
auto origVmProt = vm::getPageProtection(signalAddress); auto origVmProt = orbis::vmem::queryProtection(process, signalAddress);
int prot = 0;
auto page = signalAddress / rx::mem::pageSize; auto page = signalAddress / rx::mem::pageSize;
if (origVmProt & vm::kMapProtCpuRead) {
prot |= PROT_READ;
}
if (origVmProt & vm::kMapProtCpuWrite) {
prot |= PROT_WRITE;
}
if (origVmProt & vm::kMapProtCpuExec) {
prot |= PROT_EXEC;
}
auto gpuDevice = amdgpu::DeviceCtl{orbis::g_context->gpuDevice}; auto gpuDevice = amdgpu::DeviceCtl{orbis::g_context->gpuDevice};
if (gpuDevice && (prot & (isWrite ? PROT_WRITE : PROT_READ)) != 0) { if (gpuDevice && origVmProt &&
(origVmProt->prot & (isWrite ? orbis::vmem::Protection::CpuWrite
: orbis::vmem::Protection::CpuRead))) {
auto prot = toCpuProtection(origVmProt->prot);
auto &gpuContext = gpuDevice.getContext(); auto &gpuContext = gpuDevice.getContext();
while (true) { while (true) {
auto flags = auto flags =
@ -115,7 +115,7 @@ handle_signal(int sig, siginfo_t *info, void *ucontext) {
} }
if (!isWrite) { if (!isWrite) {
prot &= ~PROT_WRITE; prot = prot & ~rx::mem::Protection::W;
break; break;
} }
@ -125,19 +125,20 @@ handle_signal(int sig, siginfo_t *info, void *ucontext) {
} }
} }
if (::mprotect((void *)(page * rx::mem::pageSize), rx::mem::pageSize, auto range = rx::AddressRange::fromBeginSize(page * rx::mem::pageSize,
prot)) { rx::mem::pageSize);
std::perror("cache reprotection error");
std::abort();
}
auto errc = rx::mem::protect(range, prot);
rx::dieIf(errc != std::errc{},
"cache: virtual memory protection failed, address {}, error {}",
range.beginAddress(), static_cast<int>(errc));
_writefsbase_u64(orbis::g_currentThread->fsBase); _writefsbase_u64(orbis::g_currentThread->fsBase);
return; return;
} }
std::fprintf(stderr, "SIGSEGV, address %lx, access %s, prot %s\n", std::fprintf(stderr, "SIGSEGV, address %lx, access %s, prot %u\n",
signalAddress, isWrite ? "write" : "read", signalAddress, isWrite ? "write" : "read",
vm::mapProtToString(origVmProt).c_str()); origVmProt ? origVmProt->prot.toUnderlying() : -1);
} }
if (orbis::g_currentThread != nullptr) { if (orbis::g_currentThread != nullptr) {
@ -290,14 +291,14 @@ struct StackWriter {
}; };
static bool g_traceSyscalls = false; static bool g_traceSyscalls = false;
static const char *getSyscallName(orbis::Thread *thread, int sysno) { static const orbis::sysent *getSyscallEnt(orbis::Thread *thread, int sysno) {
auto sysvec = thread->tproc->sysent; auto sysvec = thread->tproc->sysent;
if (sysno >= sysvec->size) { if (sysno >= sysvec->size) {
return nullptr; return nullptr;
} }
return orbis::getSysentName(sysvec->table[sysno].call); return sysvec->table + sysno;
} }
static void onSysEnter(orbis::Thread *thread, int id, uint64_t *args, static void onSysEnter(orbis::Thread *thread, int id, uint64_t *args,
int argsCount) { int argsCount) {
@ -307,12 +308,13 @@ static void onSysEnter(orbis::Thread *thread, int id, uint64_t *args,
flockfile(stderr); flockfile(stderr);
std::fprintf(stderr, " [%u] ", thread->tid); std::fprintf(stderr, " [%u] ", thread->tid);
if (auto name = getSyscallName(thread, id)) { if (auto ent = getSyscallEnt(thread, id)) {
std::fprintf(stderr, "%s(", name); std::fprintf(stderr, "%s\n", ent->format(args).c_str());
} else { return;
std::fprintf(stderr, "sys_%u(", id);
} }
std::fprintf(stderr, "sys_%u(", id);
for (int i = 0; i < argsCount; ++i) { for (int i = 0; i < argsCount; ++i) {
if (i != 0) { if (i != 0) {
std::fprintf(stderr, ", "); std::fprintf(stderr, ", ");
@ -332,24 +334,26 @@ static void onSysExit(orbis::Thread *thread, int id, uint64_t *args,
} }
flockfile(stderr); flockfile(stderr);
std::fprintf(stderr, "%c: [%u] ", result.isError() ? 'E' : 'S', thread->tid); rx::print(stderr, "{}: [{}] ", result.isError() ? 'E' : 'S', thread->tid);
if (auto name = getSyscallName(thread, id)) { if (auto ent = getSyscallEnt(thread, id)) {
std::fprintf(stderr, "%s(", name); rx::print(stderr, "{}", ent->format(args));
} else { } else {
std::fprintf(stderr, "sys_%u(", id); rx::print(stderr, "sys_{}(", id);
}
for (int i = 0; i < argsCount; ++i) { for (int i = 0; i < argsCount; ++i) {
if (i != 0) { if (i != 0) {
std::fprintf(stderr, ", "); rx::print(stderr, ", ");
}
rx::print(stderr, "{:#x}", args[i]);
} }
std::fprintf(stderr, "%#lx", args[i]); rx::print(stderr, ")");
} }
std::fprintf(stderr, ") -> Status %d, Value %lx:%lx\n", result.value(), rx::println(stderr, " -> {}, Value {:x}:{:x}", result.errc(),
thread->retval[0], thread->retval[1]); thread->retval[0], thread->retval[1]);
if (result.isError()) { if (result.isError()) {
thread->where(); thread->where();
@ -357,11 +361,11 @@ static void onSysExit(orbis::Thread *thread, int id, uint64_t *args,
funlockfile(stderr); funlockfile(stderr);
} }
static void guestInitDev() { static void guestInitDev(orbis::Thread *thread) {
auto dmem1 = createDmemCharacterDevice(1); auto dmem0 = createDmemCharacterDevice(0);
orbis::g_context->dmemDevice = dmem1; dmem0->open(&orbis::g_context->dmem, "", 0, 0, thread);
auto dce = createDceCharacterDevice(); auto dce = createDceCharacterDevice(thread->tproc);
orbis::g_context->dceDevice = dce; orbis::g_context->dceDevice = dce;
auto ttyFd = ::open("tty.txt", O_CREAT | O_TRUNC | O_WRONLY, 0666); auto ttyFd = ::open("tty.txt", O_CREAT | O_TRUNC | O_WRONLY, 0666);
@ -377,12 +381,12 @@ static void guestInitDev() {
auto analogAudioDevice = nullAudioDevice; auto analogAudioDevice = nullAudioDevice;
auto spdifAudioDevice = nullAudioDevice; auto spdifAudioDevice = nullAudioDevice;
vfs::addDevice("dmem0", createDmemCharacterDevice(0));
vfs::addDevice("npdrm", createNpdrmCharacterDevice()); vfs::addDevice("npdrm", createNpdrmCharacterDevice());
vfs::addDevice("icc_configuration", createIccConfigurationCharacterDevice()); vfs::addDevice("icc_configuration", createIccConfigurationCharacterDevice());
vfs::addDevice("console", consoleDev); vfs::addDevice("console", consoleDev);
vfs::addDevice("camera", createCameraCharacterDevice()); vfs::addDevice("camera", createCameraCharacterDevice());
vfs::addDevice("dmem1", dmem1); vfs::addDevice("dmem0", dmem0);
vfs::addDevice("dmem1", createDmemCharacterDevice(1));
vfs::addDevice("dmem2", createDmemCharacterDevice(2)); vfs::addDevice("dmem2", createDmemCharacterDevice(2));
vfs::addDevice("stdout", consoleDev); vfs::addDevice("stdout", consoleDev);
vfs::addDevice("stderr", consoleDev); vfs::addDevice("stderr", consoleDev);
@ -518,7 +522,7 @@ static void guestInitDev() {
auto shm = createShmDevice(); auto shm = createShmDevice();
orbis::g_context->shmDevice = shm; orbis::g_context->shmDevice = shm;
orbis::g_context->blockpoolDevice = createBlockPoolDevice(); createBlockPoolDevice()->open(&orbis::g_context->blockpool, "", 0, 0, thread);
} }
static void guestInitFd(orbis::Thread *mainThread) { static void guestInitFd(orbis::Thread *mainThread) {
@ -542,22 +546,29 @@ struct ExecEnv {
int guestExec(orbis::Thread *mainThread, ExecEnv execEnv, int guestExec(orbis::Thread *mainThread, ExecEnv execEnv,
rx::Ref<orbis::Module> executableModule, rx::Ref<orbis::Module> executableModule,
std::span<std::string> argv, std::span<std::string> envp) { std::span<std::string> argv, std::span<std::string> envp) {
const auto stackEndAddress = 0x7'ffff'c000ull; const auto stackEndAddress = 0x7'eeff'c000ull;
const auto stackSize = 0x40000 * 32; const auto stackSize = 0x200000;
auto stackStartAddress = stackEndAddress - stackSize;
mainThread->stackStart =
vm::map(reinterpret_cast<void *>(stackStartAddress), stackSize,
vm::kMapProtCpuWrite | vm::kMapProtCpuRead,
vm::kMapFlagAnonymous | vm::kMapFlagFixed | vm::kMapFlagPrivate |
vm::kMapFlagStack);
mainThread->stackEnd = auto stackStartAddress = stackEndAddress - stackSize;
reinterpret_cast<std::byte *>(mainThread->stackStart) + stackSize;
auto [stackVmRange, vmErrc] = orbis::vmem::mapFlex(
mainThread->tproc, stackSize,
orbis::vmem::Protection::CpuRead | orbis::vmem::Protection::CpuWrite,
stackStartAddress,
orbis::AllocationFlags::Stack | orbis::AllocationFlags::Fixed,
orbis::vmem::BlockFlags::Stack, "main stack");
rx::dieIf(vmErrc != orbis::ErrorCode{},
"failed to map main thread stack, error {}",
static_cast<int>(vmErrc));
mainThread->stackStart = stackVmRange.beginAddress();
mainThread->stackEnd = stackVmRange.endAddress();
std::vector<std::uint64_t> argvOffsets; std::vector<std::uint64_t> argvOffsets;
std::vector<std::uint64_t> envpOffsets; std::vector<std::uint64_t> envpOffsets;
StackWriter stack{reinterpret_cast<std::uint64_t>(mainThread->stackEnd)}; StackWriter stack{mainThread->stackEnd};
for (auto &elem : argv) { for (auto &elem : argv) {
argvOffsets.push_back(stack.pushString(elem.data())); argvOffsets.push_back(stack.pushString(elem.data()));
@ -680,26 +691,24 @@ ExecEnv guestCreateExecEnv(orbis::Thread *mainThread,
return {.entryPoint = entryPoint, .interpBase = interpBase}; return {.entryPoint = entryPoint, .interpBase = interpBase};
} }
auto libSceLibcInternal = rx::linker::loadModuleFile(
"/system/common/lib/libSceLibcInternal.sprx", mainThread);
if (libSceLibcInternal == nullptr) {
std::println(stderr, "libSceLibcInternal not found");
std::abort();
}
libSceLibcInternal->id =
mainThread->tproc->modulesMap.insert(libSceLibcInternal);
auto libkernel = rx::linker::loadModuleFile( auto libkernel = rx::linker::loadModuleFile(
(isSystem ? "/system/common/lib/libkernel_sys.sprx" (isSystem ? "/system/common/lib/libkernel_sys.sprx"
: "/system/common/lib/libkernel.sprx"), : "/system/common/lib/libkernel.sprx"),
mainThread); mainThread);
if (libkernel == nullptr) { rx::dieIf(libkernel == nullptr, "libkernel not found");
rx::println(stderr, "libkernel not found"); libkernel->id = mainThread->tproc->modulesMap.insert(libkernel);
std::abort();
} mainThread->tproc->libkernelRange = rx::AddressRange::fromBeginSize(
std::bit_cast<orbis::uintptr_t>(libkernel->base), libkernel->size);
auto libSceLibcInternal = rx::linker::loadModuleFile(
"/system/common/lib/libSceLibcInternal.sprx", mainThread);
rx::dieIf(libSceLibcInternal == nullptr, "libSceLibcInternal not found");
libSceLibcInternal->id =
mainThread->tproc->modulesMap.insert(libSceLibcInternal);
if (orbis::g_context->fwType == orbis::FwType::Ps4) { if (orbis::g_context->fwType == orbis::FwType::Ps4) {
for (auto sym : libkernel->symbols) { for (auto sym : libkernel->symbols) {
@ -707,7 +716,7 @@ ExecEnv guestCreateExecEnv(orbis::Thread *mainThread,
auto address = (uint64_t)libkernel->base + sym.address; auto address = (uint64_t)libkernel->base + sym.address;
::mprotect((void *)rx::alignDown(address, 0x1000), ::mprotect((void *)rx::alignDown(address, 0x1000),
rx::alignUp(sym.size + sym.address, 0x1000), PROT_WRITE); rx::alignUp(sym.size + sym.address, 0x1000), PROT_WRITE);
std::println("patching sceKernelGetMainSocId"); rx::println("patching sceKernelGetMainSocId");
struct GetMainSocId : Xbyak::CodeGenerator { struct GetMainSocId : Xbyak::CodeGenerator {
GetMainSocId(std::uint64_t address, std::uint64_t size) GetMainSocId(std::uint64_t address, std::uint64_t size)
: Xbyak::CodeGenerator(size, (void *)address) { : Xbyak::CodeGenerator(size, (void *)address) {
@ -741,7 +750,6 @@ ExecEnv guestCreateExecEnv(orbis::Thread *mainThread,
} }
} }
libkernel->id = mainThread->tproc->modulesMap.insert(libkernel);
interpBase = reinterpret_cast<std::uint64_t>(libkernel->base); interpBase = reinterpret_cast<std::uint64_t>(libkernel->base);
entryPoint = libkernel->entryPoint; entryPoint = libkernel->entryPoint;
@ -757,18 +765,18 @@ int guestExec(orbis::Thread *mainThread,
} }
static void usage(const char *argv0) { static void usage(const char *argv0) {
std::println("{} [<options>...] <virtual path to elf> [args...]", argv0); rx::println("{} [<options>...] <virtual path to elf> [args...]", argv0);
std::println(" options:"); rx::println(" options:");
std::println(" --version, -v - print version"); rx::println(" --version, -v - print version");
std::println(" -m, --mount <host path> <virtual path>"); rx::println(" -m, --mount <host path> <virtual path>");
std::println(" -o, --override <original module name> <virtual path to " rx::println(" -o, --override <original module name> <virtual path to "
"overriden module>"); "overriden module>");
std::println(" --fw <path to firmware root>"); rx::println(" --fw <path to firmware root>");
std::println( rx::println(
" --gpu <index> - specify physical gpu index to use, default is 0"); " --gpu <index> - specify physical gpu index to use, default is 0");
std::println(" --disable-cache - disable cache of gpu resources"); rx::println(" --disable-cache - disable cache of gpu resources");
// std::println(" --presenter <window>"); // rx::println(" --presenter <window>");
std::println(" --trace"); rx::println(" --trace");
} }
static orbis::SysResult launchDaemon(orbis::Thread *thread, std::string path, static orbis::SysResult launchDaemon(orbis::Thread *thread, std::string path,
@ -792,6 +800,7 @@ static orbis::SysResult launchDaemon(orbis::Thread *thread, std::string path,
} }
auto process = orbis::createProcess(thread->tproc, childPid); auto process = orbis::createProcess(thread->tproc, childPid);
orbis::vmem::initialize(process, true); // override init process mappings
auto logFd = ::open(("log-" + std::to_string(childPid) + ".txt").c_str(), auto logFd = ::open(("log-" + std::to_string(childPid) + ".txt").c_str(),
O_CREAT | O_TRUNC | O_WRONLY, 0666); O_CREAT | O_TRUNC | O_WRONLY, 0666);
@ -822,10 +831,9 @@ static orbis::SysResult launchDaemon(orbis::Thread *thread, std::string path,
0xF0000000FFFF4000, 0xF0000000FFFF4000,
}, },
}; };
process->budgetId = 0; process->budgetId = thread->tproc->budgetId;
process->isInSandbox = false; process->isInSandbox = false;
vm::fork(childPid);
vfs::fork(); vfs::fork();
*flag = true; *flag = true;
@ -859,8 +867,6 @@ static orbis::SysResult launchDaemon(orbis::Thread *thread, std::string path,
} }
} }
vm::reset();
thread->tproc->nextTlsSlot = 1; thread->tproc->nextTlsSlot = 1;
auto executableModule = rx::linker::loadModuleFile(path, thread); auto executableModule = rx::linker::loadModuleFile(path, thread);
@ -920,10 +926,10 @@ int main(int argc, const char *argv[]) {
return 1; return 1;
} }
std::println("mounting '{}' to virtual '{}'", argv[argIndex + 1], rx::println("mounting '{}' to virtual '{}'", argv[argIndex + 1],
argv[argIndex + 2]); argv[argIndex + 2]);
if (!std::filesystem::is_directory(argv[argIndex + 1])) { if (!std::filesystem::is_directory(argv[argIndex + 1])) {
std::println(stderr, "Directory '{}' not exists", argv[argIndex + 1]); rx::println(stderr, "Directory '{}' not exists", argv[argIndex + 1]);
return 1; return 1;
} }
@ -939,7 +945,7 @@ int main(int argc, const char *argv[]) {
return 1; return 1;
} }
std::println("mounting firmware '{}'", argv[argIndex + 1]); rx::println("mounting firmware '{}'", argv[argIndex + 1]);
vfs::mount("/", createHostIoDevice(argv[argIndex + 1], "/")); vfs::mount("/", createHostIoDevice(argv[argIndex + 1], "/"));
@ -1030,10 +1036,19 @@ int main(int argc, const char *argv[]) {
orbis::constructAllGlobals(); orbis::constructAllGlobals();
orbis::g_context->deviceEventEmitter = orbis::knew<orbis::EventEmitter>(); orbis::g_context->deviceEventEmitter = orbis::knew<orbis::EventEmitter>();
// FIXME: determine mode by reading elf file
orbis::pmem::initialize(10ull * 1024 * 1024 * 1024);
orbis::dmem::initialize();
orbis::fmem::initialize(2ull * 1024 * 1024 * 1024);
rx::startWatchdog(); rx::startWatchdog();
rx::createGpuDevice(); rx::createGpuDevice();
vfs::initialize(); vfs::initialize();
while (orbis::g_context->gpuDevice == nullptr) {
std::this_thread::yield();
}
std::vector<std::string> guestArgv(argv + argIndex, argv + argc); std::vector<std::string> guestArgv(argv + argIndex, argv + argc);
if (guestArgv.empty()) { if (guestArgv.empty()) {
guestArgv.emplace_back("/mini-syscore.elf"); guestArgv.emplace_back("/mini-syscore.elf");
@ -1046,6 +1061,8 @@ int main(int argc, const char *argv[]) {
// vm::printHostStats(); // vm::printHostStats();
orbis::allocatePid(); orbis::allocatePid();
auto initProcess = orbis::createProcess(nullptr, asRoot ? 1 : 10); auto initProcess = orbis::createProcess(nullptr, asRoot ? 1 : 10);
orbis::vmem::initialize(initProcess);
// pthread_setname_np(pthread_self(), "10.MAINTHREAD"); // pthread_setname_np(pthread_self(), "10.MAINTHREAD");
int status = 0; int status = 0;
@ -1080,8 +1097,7 @@ int main(int argc, const char *argv[]) {
.flags = 0, .flags = 0,
.item = .item =
{ {
// vmem - reserved space for stack .total = 2ul * 1024 * 1024 * 1024,
.total = 2ul * 1024 * 1024 * 1024 - (64 * 1024 * 1024),
}, },
}, },
{ {
@ -1134,8 +1150,6 @@ int main(int argc, const char *argv[]) {
}, },
}; };
vm::initialize(initProcess->pid);
auto bigAppBudget = orbis::g_context->createProcessTypeBudget( auto bigAppBudget = orbis::g_context->createProcessTypeBudget(
orbis::Budget::ProcessType::BigApp, "big app budget", bigAppBudgetInfo); orbis::Budget::ProcessType::BigApp, "big app budget", bigAppBudgetInfo);
@ -1219,10 +1233,7 @@ int main(int argc, const char *argv[]) {
auto executableModule = rx::linker::loadModuleFile(guestArgv[0], mainThread); auto executableModule = rx::linker::loadModuleFile(guestArgv[0], mainThread);
if (executableModule == nullptr) { rx::dieIf(executableModule == nullptr, "Failed to open '{}'", guestArgv[0]);
std::println(stderr, "Failed to open '{}'", guestArgv[0]);
std::abort();
}
executableModule->id = initProcess->modulesMap.insert(executableModule); executableModule->id = initProcess->modulesMap.insert(executableModule);
initProcess->processParam = executableModule->processParam; initProcess->processParam = executableModule->processParam;
@ -1249,7 +1260,7 @@ int main(int argc, const char *argv[]) {
executableModule->dynType = orbis::DynType::Ps5; executableModule->dynType = orbis::DynType::Ps5;
} }
guestInitDev(); guestInitDev(mainThread);
guestInitFd(mainThread); guestInitFd(mainThread);
// data transfer mode // data transfer mode
@ -1294,6 +1305,9 @@ int main(int argc, const char *argv[]) {
orbis::g_context->regMgrInt[0x9010000] = 0; // video out color effect orbis::g_context->regMgrInt[0x9010000] = 0; // video out color effect
if (!isSystem) { if (!isSystem) {
// do IPMI allocations in init process
ipmi::setWorkerProcess(initProcess);
ipmi::createMiniSysCoreObjects(initProcess); ipmi::createMiniSysCoreObjects(initProcess);
ipmi::createSysAvControlObjects(initProcess); ipmi::createSysAvControlObjects(initProcess);
ipmi::createSysCoreObjects(initProcess); ipmi::createSysCoreObjects(initProcess);
@ -1380,7 +1394,6 @@ int main(int argc, const char *argv[]) {
status = guestExec(mainThread, execEnv, std::move(executableModule), status = guestExec(mainThread, execEnv, std::move(executableModule),
guestArgv, {}); guestArgv, {});
vm::deinitialize();
rx::thread::deinitialize(); rx::thread::deinitialize();
return status; return status;

View file

@ -2,8 +2,6 @@
#include "backtrace.hpp" #include "backtrace.hpp"
#include "io-device.hpp" #include "io-device.hpp"
#include "io-devices.hpp" #include "io-devices.hpp"
#include "iodev/blockpool.hpp"
#include "iodev/dmem.hpp"
#include "linker.hpp" #include "linker.hpp"
#include "orbis-config.hpp" #include "orbis-config.hpp"
#include "orbis/KernelContext.hpp" #include "orbis/KernelContext.hpp"
@ -14,14 +12,12 @@
#include "orbis/uio.hpp" #include "orbis/uio.hpp"
#include "orbis/umtx.hpp" #include "orbis/umtx.hpp"
#include "orbis/utils/Logs.hpp" #include "orbis/utils/Logs.hpp"
#include "orbis/vm.hpp" #include "orbis/vmem.hpp"
#include "rx/Rc.hpp" #include "rx/Rc.hpp"
#include "rx/watchdog.hpp" #include "rx/watchdog.hpp"
#include "thread.hpp" #include "thread.hpp"
#include "vfs.hpp" #include "vfs.hpp"
#include "vm.hpp"
#include <chrono> #include <chrono>
#include <csignal>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <fcntl.h> #include <fcntl.h>
@ -31,10 +27,15 @@
#include <set> #include <set>
#include <string> #include <string>
#include <string_view> #include <string_view>
#ifdef __linux
#include <sys/prctl.h> #include <sys/prctl.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <thread>
#include <unistd.h> #include <unistd.h>
#include <csignal>
#endif
#include <thread>
using namespace orbis; using namespace orbis;
@ -89,7 +90,7 @@ loadPrx(orbis::Thread *thread, std::string_view name, bool relocate,
loadedObjects[module->soName] = module.get(); loadedObjects[module->soName] = module.get();
if (loadedModules.try_emplace(module->moduleName, module.get()).second) { if (loadedModules.try_emplace(module->moduleName, module.get()).second) {
std::printf("Setting '%s' as '%s' module\n", module->soName, std::printf("Registering '%s' as '%s' module\n", module->soName,
module->moduleName); module->moduleName);
} }
@ -142,17 +143,7 @@ loadPrx(orbis::Thread *thread, std::string_view path, bool relocate) {
std::string expectedName; std::string expectedName;
if (auto sep = path.rfind('/'); sep != std::string_view::npos) { if (auto sep = path.rfind('/'); sep != std::string_view::npos) {
auto tmpExpectedName = path.substr(sep + 1); expectedName = path.substr(sep + 1);
if (tmpExpectedName.ends_with(".sprx")) {
tmpExpectedName.remove_suffix(5);
}
expectedName += tmpExpectedName;
if (!expectedName.ends_with(".prx")) {
expectedName += ".prx";
}
} }
return loadPrx(thread, path, relocate, loadedObjects, loadedModules, return loadPrx(thread, path, relocate, loadedObjects, loadedModules,
@ -174,126 +165,6 @@ std::string getAbsolutePath(std::string path, Thread *thread) {
return std::filesystem::path(path).lexically_normal().string(); return std::filesystem::path(path).lexically_normal().string();
} }
orbis::SysResult mmap(orbis::Thread *thread, orbis::caddr_t addr,
orbis::size_t len, orbis::sint prot, orbis::sint flags,
orbis::sint fd, orbis::off_t pos) {
if (fd == -1) {
auto result = vm::map(addr, len, prot, flags);
if (result == (void *)-1) {
return ErrorCode::NOMEM;
}
thread->retval[0] = reinterpret_cast<std::uint64_t>(result);
return {};
}
auto file = thread->tproc->fileDescriptors.get(fd);
if (file == nullptr) {
return ErrorCode::BADF;
}
if (file->ops->mmap == nullptr) {
ORBIS_LOG_FATAL("unimplemented mmap", fd, (void *)addr, len, prot, flags,
pos);
return mmap(thread, addr, len, prot, flags, -1, 0);
}
void *maddr = addr;
auto result =
file->ops->mmap(file.get(), &maddr, len, prot, flags, pos, thread);
if (result != ErrorCode{}) {
return result;
}
thread->retval[0] = reinterpret_cast<std::uint64_t>(maddr);
return {};
}
orbis::SysResult dmem_mmap(orbis::Thread *thread, orbis::caddr_t addr,
orbis::size_t len, orbis::sint memoryType,
orbis::sint prot, sint flags,
orbis::off_t directMemoryStart) {
auto dmem = static_cast<DmemDevice *>(orbis::g_context->dmemDevice.get());
void *address = addr;
auto result = dmem->mmap(&address, len, prot, flags, directMemoryStart);
if (result != ErrorCode{}) {
return result;
}
thread->retval[0] = reinterpret_cast<std::uint64_t>(address);
return {};
}
orbis::SysResult munmap(orbis::Thread *, orbis::ptr<void> addr,
orbis::size_t len) {
if (vm::unmap(addr, len)) {
return {};
}
return ErrorCode::INVAL;
}
orbis::SysResult msync(orbis::Thread *thread, orbis::ptr<void> addr,
orbis::size_t len, orbis::sint flags) {
return {};
}
orbis::SysResult mprotect(orbis::Thread *thread, orbis::ptr<const void> addr,
orbis::size_t len, orbis::sint prot) {
if (!vm::protect((void *)addr, len, prot)) {
return ErrorCode::INVAL;
}
return {};
}
orbis::SysResult minherit(orbis::Thread *thread, orbis::ptr<void> addr,
orbis::size_t len, orbis::sint inherit) {
return ErrorCode::INVAL;
}
orbis::SysResult madvise(orbis::Thread *thread, orbis::ptr<void> addr,
orbis::size_t len, orbis::sint behav) {
return {};
}
orbis::SysResult mincore(orbis::Thread *thread, orbis::ptr<const void> addr,
orbis::size_t len, orbis::ptr<char> vec) {
return ErrorCode::INVAL;
}
orbis::SysResult mlock(orbis::Thread *thread, orbis::ptr<const void> addr,
orbis::size_t len) {
return {};
}
orbis::SysResult mlockall(orbis::Thread *thread, orbis::sint how) { return {}; }
orbis::SysResult munlockall(orbis::Thread *thread) { return {}; }
orbis::SysResult munlock(orbis::Thread *thread, orbis::ptr<const void> addr,
orbis::size_t len) {
return {};
}
orbis::SysResult virtual_query(orbis::Thread *thread,
orbis::ptr<const void> addr, orbis::sint flags,
orbis::ptr<void> info, orbis::ulong infoSize) {
if (infoSize != sizeof(vm::VirtualQueryInfo)) {
return ErrorCode::INVAL;
}
if (!vm::virtualQuery(addr, flags, (vm::VirtualQueryInfo *)info)) {
return ErrorCode::ACCES;
}
return {};
}
orbis::SysResult
query_memory_protection(orbis::Thread *thread, orbis::ptr<void> address,
orbis::ptr<MemoryProtection> protection) {
if (vm::queryProtection(address, &protection->startAddress,
&protection->endAddress, &protection->prot)) {
return {};
}
return ErrorCode::INVAL;
}
orbis::SysResult open(orbis::Thread *thread, orbis::ptr<const char> path, orbis::SysResult open(orbis::Thread *thread, orbis::ptr<const char> path,
orbis::sint flags, orbis::sint mode, orbis::sint flags, orbis::sint mode,
rx::Ref<orbis::File> *file) { rx::Ref<orbis::File> *file) {
@ -324,33 +195,6 @@ orbis::SysResult rename(Thread *thread, ptr<const char> from,
thread); thread);
} }
orbis::SysResult blockpool_open(orbis::Thread *thread,
rx::Ref<orbis::File> *file) {
auto dev = static_cast<IoDevice *>(orbis::g_context->blockpoolDevice.get());
return dev->open(file, nullptr, 0, 0, thread);
}
orbis::SysResult blockpool_map(orbis::Thread *thread, orbis::caddr_t addr,
orbis::size_t len, orbis::sint prot,
orbis::sint flags) {
auto blockpool =
static_cast<BlockPoolDevice *>(orbis::g_context->blockpoolDevice.get());
void *address = addr;
auto result = blockpool->map(&address, len, prot, flags, thread);
if (result != ErrorCode{}) {
return result;
}
thread->retval[0] = reinterpret_cast<std::uint64_t>(address);
return {};
}
orbis::SysResult blockpool_unmap(orbis::Thread *thread, orbis::caddr_t addr,
orbis::size_t len) {
auto blockpool =
static_cast<BlockPoolDevice *>(orbis::g_context->blockpoolDevice.get());
return blockpool->unmap(addr, len, thread);
}
orbis::SysResult socket(orbis::Thread *thread, orbis::ptr<const char> name, orbis::SysResult socket(orbis::Thread *thread, orbis::ptr<const char> name,
orbis::sint domain, orbis::sint type, orbis::sint domain, orbis::sint type,
orbis::sint protocol, rx::Ref<File> *file) { orbis::sint protocol, rx::Ref<File> *file) {
@ -780,6 +624,7 @@ SysResult fork(Thread *thread, slong flags) {
} }
auto process = orbis::createProcess(thread->tproc, childPid); auto process = orbis::createProcess(thread->tproc, childPid);
orbis::vmem::fork(process, thread->tproc);
process->hostPid = ::getpid(); process->hostPid = ::getpid();
process->sysent = thread->tproc->sysent; process->sysent = thread->tproc->sysent;
process->onSysEnter = thread->tproc->onSysEnter; process->onSysEnter = thread->tproc->onSysEnter;
@ -803,7 +648,6 @@ SysResult fork(Thread *thread, slong flags) {
} }
} }
vm::fork(childPid);
vfs::fork(); vfs::fork();
*flag = true; *flag = true;
@ -892,7 +736,7 @@ SysResult execve(Thread *thread, ptr<char> fname, ptr<ptr<char>> argv,
} }
} }
vm::reset(); orbis::vmem::initialize(thread->tproc, true);
thread->tproc->nextTlsSlot = 1; thread->tproc->nextTlsSlot = 1;
for (auto [id, mod] : thread->tproc->modulesMap) { for (auto [id, mod] : thread->tproc->modulesMap) {
@ -940,6 +784,7 @@ void block(Thread *thread) {
if (--thread->unblocked != 0) { if (--thread->unblocked != 0) {
return; return;
} }
#ifdef __linux
sigset_t set; sigset_t set;
sigemptyset(&set); sigemptyset(&set);
sigaddset(&set, SIGUSR1); sigaddset(&set, SIGUSR1);
@ -954,6 +799,7 @@ void block(Thread *thread) {
std::free(thread->altStack.back()); std::free(thread->altStack.back());
thread->altStack.pop_back(); thread->altStack.pop_back();
thread->sigReturns.pop_back(); thread->sigReturns.pop_back();
#endif
} }
void unblock(Thread *thread) { void unblock(Thread *thread) {
@ -982,29 +828,12 @@ void unblock(Thread *thread) {
} // namespace } // namespace
ProcessOps rx::procOpsTable = { ProcessOps rx::procOpsTable = {
.mmap = mmap,
.dmem_mmap = dmem_mmap,
.munmap = munmap,
.msync = msync,
.mprotect = mprotect,
.minherit = minherit,
.madvise = madvise,
.mincore = mincore,
.mlock = mlock,
.mlockall = mlockall,
.munlockall = munlockall,
.munlock = munlock,
.virtual_query = virtual_query,
.query_memory_protection = query_memory_protection,
.open = open, .open = open,
.shm_open = shm_open, .shm_open = shm_open,
.unlink = unlink, .unlink = unlink,
.mkdir = mkdir, .mkdir = mkdir,
.rmdir = rmdir, .rmdir = rmdir,
.rename = rename, .rename = rename,
.blockpool_open = blockpool_open,
.blockpool_map = blockpool_map,
.blockpool_unmap = blockpool_unmap,
.socket = socket, .socket = socket,
.socketpair = socketPair, .socketpair = socketPair,
.shm_unlink = shm_unlink, .shm_unlink = shm_unlink,

View file

@ -276,7 +276,7 @@ void rx::thread::copyContext(orbis::Thread *thread, orbis::UContext &dst,
void rx::thread::setContext(orbis::Thread *thread, const orbis::UContext &src) { void rx::thread::setContext(orbis::Thread *thread, const orbis::UContext &src) {
auto &context = *std::bit_cast<ucontext_t *>(thread->context); auto &context = *std::bit_cast<ucontext_t *>(thread->context);
thread->stackStart = src.stack.sp; thread->stackStart = src.stack.sp;
thread->stackEnd = (char *)thread->stackStart + src.stack.size; thread->stackEnd = thread->stackStart + src.stack.size;
thread->setSigMask(src.sigmask); thread->setSigMask(src.sigmask);
// dst.onstack = src.gregs[REG_ONSTACK]; // dst.onstack = src.gregs[REG_ONSTACK];

File diff suppressed because it is too large Load diff

View file

@ -1,84 +0,0 @@
#pragma once
#include "orbis/IoDevice.hpp"
#include <cstdint>
#include <string>
namespace vm {
static constexpr std::uint64_t kPageShift = 14;
static constexpr std::uint64_t kPageSize = static_cast<std::uint64_t>(1)
<< kPageShift;
enum BlockFlags {
kBlockFlagFlexibleMemory = 1 << 0,
kBlockFlagDirectMemory = 1 << 1,
kBlockFlagStack = 1 << 2,
kBlockFlagPooledMemory = 1 << 3,
kBlockFlagCommited = 1 << 4,
};
enum MapFlags {
kMapFlagShared = 0x1,
kMapFlagPrivate = 0x2,
kMapFlagFixed = 0x10,
kMapFlagRename = 0x20,
kMapFlagNoReserve = 0x40,
kMapFlagNoOverwrite = 0x80,
kMapFlagVoid = 0x100,
kMapFlagHasSemaphore = 0x200,
kMapFlagStack = 0x400,
kMapFlagNoSync = 0x800,
kMapFlagAnonymous = 0x1000,
kMapFlagSystem = 0x2000,
kMapFlagAllAvailable = 0x4000,
kMapFlagNoCore = 0x20000,
kMapFlagPrefaultRead = 0x40000,
kMapFlagSelf = 0x80000,
};
enum MapProt {
kMapProtCpuRead = 1,
kMapProtCpuWrite = 2,
kMapProtCpuReadWrite = kMapProtCpuRead | kMapProtCpuWrite,
kMapProtCpuExec = 4,
kMapProtCpuAll = 0x7,
kMapProtGpuRead = 0x10,
kMapProtGpuWrite = 0x20,
kMapProtGpuAll = 0x30,
};
enum MapInternalFlags {
kMapInternalReserveOnly = 1 << 0,
};
struct VirtualQueryInfo {
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];
};
static constexpr std::uint32_t kMapFlagsAlignShift = 24;
static constexpr std::uint32_t kMapFlagsAlignMask = 0x1f << kMapFlagsAlignShift;
std::string mapFlagsToString(std::int32_t flags);
std::string mapProtToString(std::int32_t prot);
void fork(std::uint64_t pid);
void reset();
void initialize(std::uint64_t pid);
void deinitialize();
void *map(void *addr, std::uint64_t len, std::int32_t prot, std::int32_t flags,
std::int32_t internalFlags = 0, orbis::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);
unsigned getPageProtection(std::uint64_t address);
} // namespace vm

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "Serializer.hpp"
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <cstddef> #include <cstddef>
@ -64,5 +65,15 @@ public:
[[nodiscard]] constexpr std::size_t endAddress() const { return mEndAddress; } [[nodiscard]] constexpr std::size_t endAddress() const { return mEndAddress; }
constexpr bool operator==(const AddressRange &) const = default; constexpr bool operator==(const AddressRange &) const = default;
void serialize(rx::Serializer &s) const {
s.serialize(mBeginAddress);
s.serialize(mEndAddress);
}
void deserialize(rx::Deserializer &d) {
mBeginAddress = d.deserialize<std::uint64_t>();
mEndAddress = d.deserialize<std::uint64_t>();
}
}; };
} // namespace rx } // namespace rx

View file

@ -20,7 +20,9 @@ Examples:
Intersection (&) and symmetric difference (^) is also available. Intersection (&) and symmetric difference (^) is also available.
*/ */
#include "types.hpp" #include "format-base.hpp"
#include "rx/refl.hpp"
#include <type_traits>
namespace rx { namespace rx {
template <typename T> template <typename T>
@ -52,8 +54,8 @@ private:
: m_data(data) {} : m_data(data) {}
public: public:
static constexpr usz bitmax = sizeof(T) * 8; static constexpr std::size_t bitmax = sizeof(T) * 8;
static constexpr usz bitsize = static constexpr std::size_t bitsize =
static_cast<underlying_type>(T::bitset_last) + 1; static_cast<underlying_type>(T::bitset_last) + 1;
static_assert(std::is_enum_v<T>, static_assert(std::is_enum_v<T>,
@ -69,7 +71,7 @@ public:
<< static_cast<underlying_type>(value); << static_cast<underlying_type>(value);
} }
EnumBitSet() = default; constexpr EnumBitSet() = default;
// Construct from a single bit // Construct from a single bit
constexpr EnumBitSet(T bit) noexcept : m_data(shift(bit)) {} constexpr EnumBitSet(T bit) noexcept : m_data(shift(bit)) {}
@ -91,7 +93,9 @@ public:
return m_data; return m_data;
} }
constexpr detail::InvertedEnumBitSet<T> operator~() const { return {m_data}; } constexpr detail::InvertedEnumBitSet<T> operator~() const {
return static_cast<underlying_type>(~m_data);
}
[[deprecated("Use operator|=")]] constexpr EnumBitSet & [[deprecated("Use operator|=")]] constexpr EnumBitSet &
operator+=(EnumBitSet rhs) { operator+=(EnumBitSet rhs) {
@ -126,6 +130,11 @@ public:
return *this; return *this;
} }
constexpr EnumBitSet &operator&=(detail::InvertedEnumBitSet<T> rhs) {
m_data &= rhs.m_data;
return *this;
}
constexpr EnumBitSet &operator^=(EnumBitSet rhs) { constexpr EnumBitSet &operator^=(EnumBitSet rhs) {
m_data ^= static_cast<underlying_type>(rhs); m_data ^= static_cast<underlying_type>(rhs);
return *this; return *this;
@ -191,7 +200,9 @@ public:
return (m_data & arg.m_data) == 0; return (m_data & arg.m_data) == 0;
} }
underlying_type &raw() { return m_data; } constexpr underlying_type &raw() { return m_data; }
constexpr auto operator<=>(const EnumBitSet &) const = default;
}; };
template <BitSetEnum T> constexpr EnumBitSet<T> toBitSet(T bit) { template <BitSetEnum T> constexpr EnumBitSet<T> toBitSet(T bit) {
@ -288,4 +299,50 @@ constexpr EnumBitSet<T> operator^(const U &lhs, T rhs) {
} // namespace bitset } // namespace bitset
} // namespace rx } // namespace rx
template <typename T>
requires requires(rx::format_parse_context &ctx) {
rx::formatter<T>().parse(ctx);
}
struct rx::formatter<rx::EnumBitSet<T>> {
constexpr rx::format_parse_context::iterator
parse(rx::format_parse_context &ctx) {
return ctx.begin();
}
rx::format_context::iterator format(rx::EnumBitSet<T> bitSet,
rx::format_context &ctx) const {
auto raw = bitSet.toUnderlying();
if (raw != 0) {
bool first = true;
for (std::size_t i = 0; i <= static_cast<std::size_t>(T::bitset_last);
++i) {
auto mask = 1ull << i;
if (!(raw & mask)) {
continue;
}
if (first) {
first = false;
} else {
rx::format_to(ctx.out(), " | ");
}
rx::format_to(ctx.out(), "{}", T(i));
raw &= ~mask;
}
if (raw) {
if (!first) {
rx::format_to(ctx.out(), " | ");
}
rx::format_to(ctx.out(), "{:#x}", raw);
}
} else {
rx::format_to(ctx.out(), "{}::None", rx::getNameOf<T>());
}
return ctx.out();
}
};
using namespace rx::bitset; using namespace rx::bitset;

View file

@ -20,7 +20,7 @@ public:
: context( : context(
const_cast<std::remove_const_t<std::remove_cvref_t<T>> *>(&object)), const_cast<std::remove_const_t<std::remove_cvref_t<T>> *>(&object)),
invoke(+[](void *context, ArgsT... args) -> RT { invoke(+[](void *context, ArgsT... args) -> RT {
return (*reinterpret_cast<T *>(context))(std::move(args)...); return (*reinterpret_cast<T *>(context))(args...);
}) {} }) {}
template <typename... InvokeArgsT> template <typename... InvokeArgsT>

View file

@ -8,6 +8,7 @@
namespace rx { namespace rx {
class Mappable { class Mappable {
public:
#ifdef _WIN32 #ifdef _WIN32
using NativeHandle = void *; using NativeHandle = void *;
static constexpr NativeHandle kInvalidHandle = nullptr; static constexpr NativeHandle kInvalidHandle = nullptr;
@ -16,9 +17,6 @@ class Mappable {
static constexpr auto kInvalidHandle = NativeHandle(-1); static constexpr auto kInvalidHandle = NativeHandle(-1);
#endif #endif
NativeHandle m_handle = kInvalidHandle;
public:
Mappable() = default; Mappable() = default;
Mappable(Mappable &&other) noexcept { *this = std::move(other); } Mappable(Mappable &&other) noexcept { *this = std::move(other); }
Mappable(const Mappable &) = delete; Mappable(const Mappable &) = delete;
@ -33,6 +31,12 @@ public:
} }
} }
static Mappable CreateFromNativeHandle(NativeHandle handle) {
Mappable result;
result.m_handle = handle;
return result;
}
static std::pair<Mappable, std::errc> CreateMemory(std::size_t size); static std::pair<Mappable, std::errc> CreateMemory(std::size_t size);
static std::pair<Mappable, std::errc> CreateSwap(std::size_t size); static std::pair<Mappable, std::errc> CreateSwap(std::size_t size);
std::errc map(rx::AddressRange virtualRange, std::size_t offset, std::errc map(rx::AddressRange virtualRange, std::size_t offset,
@ -47,5 +51,7 @@ public:
private: private:
void destroy(); void destroy();
NativeHandle m_handle = kInvalidHandle;
}; };
} // namespace rx } // namespace rx

View file

@ -1,7 +1,8 @@
#pragma once #pragma once
#include "rx/AddressRange.hpp" #include "AddressRange.hpp"
#include "rx/Rc.hpp" #include "Rc.hpp"
#include "Serializer.hpp"
#include <bit> #include <bit>
#include <cassert> #include <cassert>
#include <concepts> #include <concepts>
@ -84,7 +85,7 @@ public:
return {startAddress, endAddress}; return {startAddress, endAddress};
} }
void map(rx::AddressRange range) { void map(AddressRange range) {
auto [beginIt, beginInserted] = auto [beginIt, beginInserted] =
mAreas.emplace(range.beginAddress(), Kind::O); mAreas.emplace(range.beginAddress(), Kind::O);
auto [endIt, endInserted] = mAreas.emplace(range.endAddress(), Kind::X); auto [endIt, endInserted] = mAreas.emplace(range.endAddress(), Kind::X);
@ -311,6 +312,23 @@ public:
assert(kind != Kind::X); assert(kind != Kind::X);
kind = Kind::O; kind = Kind::O;
} }
void serialize(Serializer &s) const
requires Serializable<T>
{
s.serialize(kind);
if (kind != Kind::X) {
s.serialize(storage.data);
}
}
void deserialize(Deserializer &d)
requires Serializable<T>
{
d.deserialize(kind);
if (kind != Kind::X && !d.failure()) {
d.deserialize(storage.data);
}
}
}; };
template <typename T> class Payload<T *> { template <typename T> class Payload<T *> {
@ -365,6 +383,9 @@ public:
assert(!isClose()); assert(!isClose());
value &= ~kCloseOpenBit; value &= ~kCloseOpenBit;
} }
void serialize(Serializer &s) const { s.serialize(value); }
void deserialize(Deserializer &d) { d.deserialize(value); }
}; };
template <typename T> class Payload<Ref<T>> { template <typename T> class Payload<Ref<T>> {
@ -452,6 +473,9 @@ public:
assert(!isClose()); assert(!isClose());
value &= ~kCloseOpenBit; value &= ~kCloseOpenBit;
} }
void serialize(Serializer &s) const { s.serialize(value); }
void deserialize(Deserializer &d) { d.deserialize(value); }
}; };
template <typename PayloadT, template <typename PayloadT,
@ -463,30 +487,28 @@ class MemoryTableWithPayload {
mAreas; mAreas;
public: public:
class AreaInfo : public rx::AddressRange { template <typename T> class AreaInfo : public AddressRange {
Payload<PayloadT> &payload; T &payload;
public: public:
AreaInfo(Payload<PayloadT> &payload, rx::AddressRange range) AreaInfo(T &payload, AddressRange range)
: payload(payload), AddressRange(range) {} : payload(payload), AddressRange(range) {}
decltype(auto) operator->() { return &payload.get(); } decltype(auto) operator->() { return &payload.get(); }
decltype(auto) get() { return payload.get(); } decltype(auto) get() { return payload.get(); }
}; };
class iterator { template <typename MapIterator, typename AreaInfo> class Iterator {
using map_iterator = MapIterator it;
typename std::map<std::uint64_t, payload_type>::iterator;
map_iterator it;
public: public:
iterator() = default; Iterator() = default;
iterator(map_iterator it) : it(it) {} Iterator(MapIterator it) : it(it) {}
AreaInfo operator*() const { return {it->second, range()}; } AreaInfo operator*() const { return {it->second, range()}; }
rx::AddressRange range() const { AddressRange range() const {
return rx::AddressRange::fromBeginEnd(beginAddress(), endAddress()); return AddressRange::fromBeginEnd(beginAddress(), endAddress());
} }
std::uint64_t beginAddress() const { return it->first; } std::uint64_t beginAddress() const { return it->first; }
@ -495,7 +517,8 @@ public:
decltype(auto) get() const { return it->second.get(); } decltype(auto) get() const { return it->second.get(); }
decltype(auto) operator->() const { return &it->second.get(); } decltype(auto) operator->() const { return &it->second.get(); }
iterator &operator++() {
Iterator &operator++() {
++it; ++it;
if (!it->second.isCloseOpen()) { if (!it->second.isCloseOpen()) {
@ -505,7 +528,7 @@ public:
return *this; return *this;
} }
iterator &operator--() { Iterator &operator--() {
--it; --it;
if (it->second.isClose()) { if (it->second.isClose()) {
@ -515,18 +538,28 @@ public:
return *this; return *this;
} }
bool operator==(iterator other) const { return it == other.it; } bool operator==(Iterator other) const { return it == other.it; }
bool operator!=(iterator other) const { return it != other.it; }
friend MemoryTableWithPayload; friend MemoryTableWithPayload;
}; };
using iterator =
Iterator<typename std::map<std::uint64_t, payload_type>::iterator,
AreaInfo<payload_type>>;
using const_iterator =
Iterator<typename std::map<std::uint64_t, payload_type>::const_iterator,
AreaInfo<const payload_type>>;
MemoryTableWithPayload() = default; MemoryTableWithPayload() = default;
MemoryTableWithPayload(MemoryTableWithPayload &&) = default; MemoryTableWithPayload(MemoryTableWithPayload &&) = default;
MemoryTableWithPayload &operator=(MemoryTableWithPayload &&) = default; MemoryTableWithPayload &operator=(MemoryTableWithPayload &&) = default;
MemoryTableWithPayload(const MemoryTableWithPayload &) = delete; MemoryTableWithPayload(const MemoryTableWithPayload &) = delete;
MemoryTableWithPayload &operator=(const MemoryTableWithPayload &) = delete; MemoryTableWithPayload &operator=(const MemoryTableWithPayload &) = delete;
const_iterator cbegin() const { return const_iterator(mAreas.begin()); }
const_iterator cend() const { return const_iterator(mAreas.end()); }
const_iterator begin() const { return const_iterator(mAreas.cbegin()); }
const_iterator end() const { return const_iterator(mAreas.cend()); }
iterator begin() { return iterator(mAreas.begin()); } iterator begin() { return iterator(mAreas.begin()); }
iterator end() { return iterator(mAreas.end()); } iterator end() { return iterator(mAreas.end()); }
@ -579,9 +612,9 @@ public:
return endAddress < address ? mAreas.end() : it; return endAddress < address ? mAreas.end() : it;
} }
iterator map(rx::AddressRange range, PayloadT payload, bool merge = true, iterator map(AddressRange range, PayloadT payload, bool merge = true,
bool noOverride = false) { bool noOverride = false) {
assert(range.beginAddress() < range.endAddress()); assert(range.isValid());
auto [beginIt, beginInserted] = auto [beginIt, beginInserted] =
mAreas.emplace(range.beginAddress(), payload_type::createOpen(payload)); mAreas.emplace(range.beginAddress(), payload_type::createOpen(payload));
auto [endIt, endInserted] = auto [endIt, endInserted] =
@ -676,7 +709,7 @@ public:
return origBegin; return origBegin;
} }
void unmap(iterator it) { iterator unmap(iterator it) {
auto openIt = it.it; auto openIt = it.it;
auto closeIt = openIt; auto closeIt = openIt;
++closeIt; ++closeIt;
@ -690,13 +723,49 @@ public:
if (closeIt->second.isCloseOpen()) { if (closeIt->second.isCloseOpen()) {
closeIt->second.setOpen(); closeIt->second.setOpen();
} else { } else {
mAreas.erase(closeIt); closeIt = mAreas.erase(closeIt);
} }
return iterator(closeIt);
} }
void unmap(rx::AddressRange range) { iterator unmap(AddressRange range) {
// FIXME: can be optimized // FIXME: can be optimized
unmap(map(range, PayloadT{}, false)); return unmap(map(range, PayloadT{}, false));
}
void serialize(Serializer &s) const
requires Serializable<payload_type>
{
for (auto block : *this) {
s.serialize(block.beginAddress());
s.serialize(block.endAddress());
s.serialize(block.get());
}
s.serialize<std::uint64_t>(-1);
s.serialize<std::uint64_t>(-1);
}
void deserialize(Deserializer &d)
requires Serializable<payload_type>
{
clear();
while (!d.failure()) {
auto beginAddress = d.deserialize<std::uint64_t>();
auto endAddress = d.deserialize<std::uint64_t>();
if (beginAddress == static_cast<std::uint64_t>(-1) &&
endAddress == static_cast<std::uint64_t>(-1)) {
break;
}
auto value = d.deserialize<PayloadT>();
map(AddressRange::fromBeginEnd(beginAddress, endAddress),
std::move(value), false);
}
} }
}; };
} // namespace rx } // namespace rx

View file

@ -12,7 +12,7 @@ struct RcBase {
virtual ~RcBase() = default; virtual ~RcBase() = default;
void incRef() { void incRef() {
if (references.fetch_add(1, std::memory_order::relaxed) > 4096) { if (references.fetch_add(1, std::memory_order::relaxed) > 100 * 1024 * 1024) {
assert(!"too many references"); assert(!"too many references");
} }
} }

View file

@ -169,19 +169,17 @@ struct Deserializer {
if constexpr (requires { if constexpr (requires {
{ type::deserialize(*this) } -> std::convertible_to<type>; { type::deserialize(*this) } -> std::convertible_to<type>;
}) { }) {
return T::deserialize(*this); return type::deserialize(*this);
} else if constexpr (requires(type &result) { } else if constexpr (requires(type &result) {
type::deserialize(*this, result); type::deserialize(*this, result);
}) { }) {
type result; type result;
T::deserialize(*this, result); type::deserialize(*this, result);
return result; return result;
} else if constexpr (requires(type &result) { } else if constexpr (requires(type &result) {
{ result.deserialize(*this);
result.deserialize(*this)
} -> std::convertible_to<type>;
}) { }) {
T result; type result;
result.deserialize(*this); result.deserialize(*this);
return result; return result;
} else if constexpr (requires(type &result) { } else if constexpr (requires(type &result) {

View file

@ -174,13 +174,29 @@ constexpr auto calcFieldCount() {
} else if constexpr (requires { EnumT::count; }) { } else if constexpr (requires { EnumT::count; }) {
return static_cast<std::size_t>(EnumT::count); return static_cast<std::size_t>(EnumT::count);
} else if constexpr (!requires { getNameOf<EnumT(N)>()[0]; }) { } else if constexpr (!requires { getNameOf<EnumT(N)>()[0]; }) {
return N; if constexpr (requires { getNameOf<EnumT(N + 1)>()[0]; }) {
if constexpr (constexpr auto c = getNameOf<EnumT(N + 1)>()[0];
c >= '0' && c <= '9') {
return N;
} else {
return calcFieldCount<EnumT, N + 2>();
}
} else {
return N;
}
} else { } else {
constexpr auto c = getNameOf<EnumT(N)>()[0]; if constexpr (constexpr auto c = getNameOf<EnumT(N)>()[0];
if constexpr (!requires { getNameOf<EnumT(N)>()[0]; }) { c >= '0' && c <= '9') {
return N; if constexpr (requires { getNameOf<EnumT(N + 1)>()[0]; }) {
} else if constexpr (c >= '0' && c <= '9') { if constexpr (constexpr auto c = getNameOf<EnumT(N + 1)>()[0];
return N; c >= '0' && c <= '9') {
return N;
} else {
return calcFieldCount<EnumT, N + 2>();
}
} else {
return N;
}
} else { } else {
return calcFieldCount<EnumT, N + 1>(); return calcFieldCount<EnumT, N + 1>();
} }