mirror of
https://github.com/RPCSX/rpcsx.git
synced 2025-12-06 07:12:14 +01:00
fix blockpool & dmem implementation modernize blockpool & dmem io devices use budgets per allocation add serialization support for MemoryTableWithPayload and AddressRange utils add format support for EnumBitSet util implemented trace formatter per syscall increased allowed reference count for Ref
206 lines
6.6 KiB
C++
206 lines
6.6 KiB
C++
#pragma once
|
|
|
|
#include "error/ErrorCode.hpp"
|
|
#include "orbis-config.hpp"
|
|
#include "rx/AddressRange.hpp"
|
|
#include "rx/EnumBitSet.hpp"
|
|
#include "rx/Rc.hpp"
|
|
#include "rx/mem.hpp"
|
|
#include "vmem.hpp"
|
|
#include <bit>
|
|
#include <type_traits>
|
|
|
|
namespace orbis {
|
|
enum OpenFlags {
|
|
kOpenFlagReadOnly = 0x0,
|
|
kOpenFlagWriteOnly = 0x1,
|
|
kOpenFlagReadWrite = 0x2,
|
|
kOpenFlagNonBlock = 0x4,
|
|
kOpenFlagAppend = 0x8,
|
|
kOpenFlagShLock = 0x10,
|
|
kOpenFlagExLock = 0x20,
|
|
kOpenFlagAsync = 0x40,
|
|
kOpenFlagFsync = 0x80,
|
|
kOpenFlagCreat = 0x200,
|
|
kOpenFlagTrunc = 0x400,
|
|
kOpenFlagExcl = 0x800,
|
|
kOpenFlagDSync = 0x1000,
|
|
kOpenFlagDirect = 0x10000,
|
|
kOpenFlagDirectory = 0x20000,
|
|
};
|
|
|
|
struct File;
|
|
struct Thread;
|
|
struct Process;
|
|
struct Stat;
|
|
struct StatFs;
|
|
struct IoDevice : rx::RcBase {
|
|
rx::EnumBitSet<vmem::BlockFlags> blockFlags{};
|
|
|
|
virtual ErrorCode open(rx::Ref<File> *file, const char *path,
|
|
std::uint32_t flags, std::uint32_t mode,
|
|
Thread *thread) = 0;
|
|
|
|
virtual ErrorCode statfs(const char *path, StatFs *sb, Thread *thread) {
|
|
return ErrorCode::NOTSUP;
|
|
}
|
|
|
|
virtual ErrorCode stat(const char *path, Stat *sb, Thread *thread) {
|
|
return ErrorCode::NOTSUP;
|
|
}
|
|
|
|
virtual ErrorCode unlink(const char *path, bool recursive, Thread *thread) {
|
|
return ErrorCode::NOTSUP;
|
|
}
|
|
|
|
virtual ErrorCode createSymlink(const char *target, const char *linkPath,
|
|
Thread *thread) {
|
|
return ErrorCode::NOTSUP;
|
|
}
|
|
|
|
virtual ErrorCode mkdir(const char *path, int mode, Thread *thread) {
|
|
return ErrorCode::NOTSUP;
|
|
}
|
|
|
|
virtual ErrorCode rmdir(const char *path, Thread *thread) {
|
|
return ErrorCode::NOTSUP;
|
|
}
|
|
|
|
virtual ErrorCode rename(const char *from, const char *to, Thread *thread) {
|
|
return ErrorCode::NOTSUP;
|
|
}
|
|
|
|
virtual ErrorCode ioctl(std::uint64_t request, ptr<void> argp,
|
|
Thread *thread);
|
|
|
|
virtual ErrorCode map(rx::AddressRange range, std::int64_t offset,
|
|
rx::EnumBitSet<vmem::Protection> protection, File *file,
|
|
Process *process);
|
|
};
|
|
|
|
namespace ioctl {
|
|
inline constexpr std::uint32_t VoidBit = 0x20000000;
|
|
inline constexpr std::uint32_t OutBit = 0x40000000;
|
|
inline constexpr std::uint32_t InBit = 0x80000000;
|
|
inline constexpr std::uint32_t DirMask = VoidBit | InBit | OutBit;
|
|
|
|
constexpr bool isVoid(std::uint32_t cmd) { return (cmd & DirMask) == VoidBit; }
|
|
constexpr bool isIn(std::uint32_t cmd) { return (cmd & DirMask) == InBit; }
|
|
constexpr bool isOut(std::uint32_t cmd) { return (cmd & DirMask) == OutBit; }
|
|
constexpr bool isInOut(std::uint32_t cmd) {
|
|
return (cmd & DirMask) == (OutBit | InBit);
|
|
}
|
|
constexpr std::uint32_t paramSize(std::uint32_t cmd) {
|
|
return (cmd >> 16) & ((1 << 13) - 1);
|
|
}
|
|
constexpr std::uint32_t group(std::uint32_t cmd) { return (cmd >> 8) & 0xff; }
|
|
constexpr std::uint32_t id(std::uint32_t cmd) { return cmd & 0xff; }
|
|
} // namespace ioctl
|
|
|
|
struct IoctlHandlerEntry;
|
|
|
|
using IoctlHandler = ErrorCode (*)(Thread *thread, orbis::ptr<void> arg,
|
|
IoDevice *device, void (*impl)());
|
|
|
|
struct IoctlHandlerEntry {
|
|
IoctlHandler handler;
|
|
void (*impl)();
|
|
std::uint32_t cmd;
|
|
};
|
|
template <int Group> struct IoDeviceWithIoctl : IoDevice {
|
|
IoctlHandlerEntry ioctlTable[256]{};
|
|
|
|
template <std::uint32_t Cmd, typename InstanceT, typename T>
|
|
requires requires {
|
|
requires std::is_base_of_v<IoDeviceWithIoctl, InstanceT>;
|
|
requires ioctl::paramSize(Cmd) == sizeof(T);
|
|
requires ioctl::group(Cmd) == Group;
|
|
requires std::is_const_v<T> || ioctl::isInOut(Cmd);
|
|
requires !std::is_const_v<T> || ioctl::isIn(Cmd);
|
|
}
|
|
void addIoctl(ErrorCode (*handler)(Thread *thread, InstanceT *device,
|
|
T &arg)) {
|
|
constexpr auto id = ioctl::id(Cmd);
|
|
assert(ioctlTable[id].handler == nullptr);
|
|
|
|
IoctlHandlerEntry &entry = ioctlTable[id];
|
|
|
|
entry.handler = [](Thread *thread, orbis::ptr<void> arg, IoDevice *device,
|
|
void (*opaqueImpl)()) -> ErrorCode {
|
|
auto impl = std::bit_cast<decltype(handler)>(opaqueImpl);
|
|
|
|
std::remove_cvref_t<T> _arg;
|
|
ORBIS_RET_ON_ERROR(orbis::uread(_arg, orbis::ptr<T>(arg)));
|
|
ORBIS_RET_ON_ERROR(impl(thread, static_cast<InstanceT *>(device), _arg));
|
|
|
|
if constexpr (ioctl::isInOut(Cmd)) {
|
|
return orbis::uwrite(orbis::ptr<T>(arg), _arg);
|
|
} else {
|
|
return {};
|
|
}
|
|
};
|
|
entry.impl = std::bit_cast<void (*)()>(handler);
|
|
entry.cmd = Cmd;
|
|
}
|
|
|
|
template <std::uint32_t Cmd, typename InstanceT>
|
|
requires requires {
|
|
requires std::is_base_of_v<IoDeviceWithIoctl, InstanceT>;
|
|
requires ioctl::group(Cmd) == Group;
|
|
requires ioctl::isVoid(Cmd);
|
|
}
|
|
|
|
void addIoctl(ErrorCode (*handler)(Thread *thread, InstanceT *device)) {
|
|
constexpr auto id = ioctl::id(Cmd);
|
|
assert(ioctlTable[id].handler == nullptr);
|
|
|
|
IoctlHandlerEntry &entry = ioctlTable[id];
|
|
|
|
entry.handler = [](Thread *thread, orbis::ptr<void>, IoDevice *device,
|
|
void (*opaqueImpl)()) -> ErrorCode {
|
|
auto impl = std::bit_cast<decltype(handler)>(opaqueImpl);
|
|
return impl(thread, static_cast<InstanceT *>(device));
|
|
};
|
|
|
|
entry.impl = std::bit_cast<void (*)()>(handler);
|
|
entry.cmd = Cmd;
|
|
}
|
|
|
|
template <std::uint32_t Cmd, typename InstanceT, typename T>
|
|
requires requires {
|
|
requires std::is_base_of_v<IoDeviceWithIoctl, InstanceT>;
|
|
requires ioctl::paramSize(Cmd) == sizeof(T);
|
|
requires ioctl::group(Cmd) == Group;
|
|
requires ioctl::isOut(Cmd);
|
|
}
|
|
void addIoctl(std::pair<ErrorCode, T> (*handler)(Thread *thread,
|
|
InstanceT *device)) {
|
|
constexpr auto id = ioctl::id(Cmd);
|
|
assert(ioctlTable[id].handler == nullptr);
|
|
|
|
IoctlHandlerEntry &entry = ioctlTable[id];
|
|
|
|
entry.handler = [](Thread *thread, orbis::ptr<void> arg, IoDevice *device,
|
|
void (*opaqueImpl)()) -> ErrorCode {
|
|
auto impl = std::bit_cast<decltype(handler)>(opaqueImpl);
|
|
auto [errc, value] = impl(thread, static_cast<InstanceT *>(device));
|
|
ORBIS_RET_ON_ERROR(errc);
|
|
return orbis::uwrite(orbis::ptr<T>(arg), value);
|
|
};
|
|
entry.impl = std::bit_cast<void (*)()>(handler);
|
|
entry.cmd = Cmd;
|
|
}
|
|
|
|
ErrorCode ioctl(std::uint64_t request, orbis::ptr<void> argp,
|
|
Thread *thread) override {
|
|
auto id = request & 0xff;
|
|
|
|
if (ioctlTable[id].handler == nullptr || ioctlTable[id].cmd != request) {
|
|
return IoDevice::ioctl(request, argp, thread);
|
|
}
|
|
|
|
return ioctlTable[id].handler(thread, argp, this, ioctlTable[id].impl);
|
|
}
|
|
};
|
|
} // namespace orbis
|