mirror of
https://github.com/RPCSX/rpcsx.git
synced 2025-12-06 07:12:14 +01:00
Compare commits
11 commits
479b09b2df
...
b9d36bc0b8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9d36bc0b8 | ||
|
|
4685e4fecc | ||
|
|
c650ac482b | ||
|
|
eeb3de8f74 | ||
|
|
9cb9f4c21e | ||
|
|
659ad9d045 | ||
|
|
5f290a8fb3 | ||
|
|
b2dcc3f4f5 | ||
|
|
091349ca1e | ||
|
|
46f4de9bc4 | ||
|
|
d7ad77b406 |
|
|
@ -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); }
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
12
kernel/orbis/include/orbis/MemoryType.hpp
Normal file
12
kernel/orbis/include/orbis/MemoryType.hpp
Normal 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
|
||||||
|
};
|
||||||
|
}
|
||||||
32
kernel/orbis/include/orbis/blockpool.hpp
Normal file
32
kernel/orbis/include/orbis/blockpool.hpp
Normal 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
|
||||||
62
kernel/orbis/include/orbis/dmem.hpp
Normal file
62
kernel/orbis/include/orbis/dmem.hpp
Normal 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
|
||||||
|
|
@ -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; }
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 */);
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
370
kernel/orbis/src/blockpool.cpp
Normal file
370
kernel/orbis/src/blockpool.cpp
Normal 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
610
kernel/orbis/src/dmem.cpp
Normal 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, {}};
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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; }
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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 */) {
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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>();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
};
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
};
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
466
rpcsx/linker.cpp
466
rpcsx/linker.cpp
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
223
rpcsx/main.cpp
223
rpcsx/main.cpp
|
|
@ -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;
|
||||||
|
|
|
||||||
197
rpcsx/ops.cpp
197
rpcsx/ops.cpp
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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];
|
||||||
|
|
|
||||||
1221
rpcsx/vm.cpp
1221
rpcsx/vm.cpp
File diff suppressed because it is too large
Load diff
84
rpcsx/vm.hpp
84
rpcsx/vm.hpp
|
|
@ -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
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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>();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue