mirror of
https://github.com/RPCSX/rpcsx.git
synced 2025-12-06 07:12:14 +01:00
orbis: add type-safe IoDeviceWithIoctl
This commit is contained in:
parent
014012c219
commit
8cfb4e8d16
|
|
@ -6,6 +6,7 @@ add_library(obj.orbis-kernel OBJECT
|
||||||
src/sysvec.cpp
|
src/sysvec.cpp
|
||||||
src/event.cpp
|
src/event.cpp
|
||||||
src/evf.cpp
|
src/evf.cpp
|
||||||
|
src/IoDevice.cpp
|
||||||
src/ipmi.cpp
|
src/ipmi.cpp
|
||||||
src/KernelAllocator.cpp
|
src/KernelAllocator.cpp
|
||||||
src/KernelContext.cpp
|
src/KernelContext.cpp
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "error/ErrorCode.hpp"
|
#include "error/ErrorCode.hpp"
|
||||||
|
#include "orbis-config.hpp"
|
||||||
#include "rx/Rc.hpp"
|
#include "rx/Rc.hpp"
|
||||||
|
#include <bit>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
namespace orbis {
|
namespace orbis {
|
||||||
enum OpenFlags {
|
enum OpenFlags {
|
||||||
|
|
@ -50,5 +53,133 @@ struct IoDevice : rx::RcBase {
|
||||||
virtual ErrorCode rename(const char *from, const char *to, Thread *thread) {
|
virtual ErrorCode rename(const char *from, const char *to, Thread *thread) {
|
||||||
return ErrorCode::NOTSUP;
|
return ErrorCode::NOTSUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual ErrorCode ioctl(std::uint64_t request, orbis::ptr<void> argp,
|
||||||
|
Thread *thread);
|
||||||
|
};
|
||||||
|
|
||||||
|
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 == unhandledIoctl);
|
||||||
|
|
||||||
|
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 == unhandledIoctl);
|
||||||
|
|
||||||
|
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 == unhandledIoctl);
|
||||||
|
|
||||||
|
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
|
} // namespace orbis
|
||||||
|
|
|
||||||
75
kernel/orbis/src/IoDevice.cpp
Normal file
75
kernel/orbis/src/IoDevice.cpp
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
#include "IoDevice.hpp"
|
||||||
|
#include "thread/Thread.hpp"
|
||||||
|
#include "utils/Logs.hpp"
|
||||||
|
|
||||||
|
static std::string iocGroupToString(unsigned iocGroup) {
|
||||||
|
if (iocGroup >= 128) {
|
||||||
|
const char *sceGroups[] = {
|
||||||
|
"DEV",
|
||||||
|
"DMEM",
|
||||||
|
"GC",
|
||||||
|
"DCE",
|
||||||
|
"UVD",
|
||||||
|
"VCE",
|
||||||
|
"DBGGC",
|
||||||
|
"TWSI",
|
||||||
|
"MDBG",
|
||||||
|
"DEVENV",
|
||||||
|
"AJM",
|
||||||
|
"TRACE",
|
||||||
|
"IBS",
|
||||||
|
"MBUS",
|
||||||
|
"HDMI",
|
||||||
|
"CAMERA",
|
||||||
|
"FAN",
|
||||||
|
"THERMAL",
|
||||||
|
"PFS",
|
||||||
|
"ICC_CONFIG",
|
||||||
|
"IPC",
|
||||||
|
"IOSCHED",
|
||||||
|
"ICC_INDICATOR",
|
||||||
|
"EXFATFS",
|
||||||
|
"ICC_NVS",
|
||||||
|
"DVE",
|
||||||
|
"ICC_POWER",
|
||||||
|
"AV_CONTROL",
|
||||||
|
"ICC_SC_CONFIGURATION",
|
||||||
|
"ICC_DEVICE_POWER",
|
||||||
|
"SSHOT",
|
||||||
|
"DCE_SCANIN",
|
||||||
|
"FSCTRL",
|
||||||
|
"HMD",
|
||||||
|
"SHM",
|
||||||
|
"PHYSHM",
|
||||||
|
"HMDDFU",
|
||||||
|
"BLUETOOTH_HID",
|
||||||
|
"SBI",
|
||||||
|
"S3DA",
|
||||||
|
"SPM",
|
||||||
|
"BLOCKPOOL",
|
||||||
|
"SDK_EVENTLOG",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (iocGroup - 127 >= std::size(sceGroups)) {
|
||||||
|
return "'?'";
|
||||||
|
}
|
||||||
|
|
||||||
|
return sceGroups[iocGroup - 127];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isprint(iocGroup)) {
|
||||||
|
return "'" + std::string(1, (char)iocGroup) + "'";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "'?'";
|
||||||
|
}
|
||||||
|
|
||||||
|
orbis::ErrorCode orbis::IoDevice::ioctl(std::uint64_t request,
|
||||||
|
orbis::ptr<void> argp, Thread *thread) {
|
||||||
|
auto group = iocGroupToString(ioctl::group(request));
|
||||||
|
auto paramSize = ioctl::paramSize(request);
|
||||||
|
ORBIS_LOG_ERROR("unhandled ioctl", request, group, paramSize, argp,
|
||||||
|
thread->tid);
|
||||||
|
thread->where();
|
||||||
|
return ErrorCode::NOTSUP;
|
||||||
|
}
|
||||||
|
|
@ -303,129 +303,8 @@ orbis::SysResult orbis::sys_freebsd6_ftruncate(Thread *thread, sint fd, sint,
|
||||||
off_t length) {
|
off_t length) {
|
||||||
return sys_ftruncate(thread, fd, length);
|
return sys_ftruncate(thread, fd, length);
|
||||||
}
|
}
|
||||||
// clang-format off
|
|
||||||
#define IOCPARM_SHIFT 13 /* number of bits for ioctl size */
|
|
||||||
#define IOCPARM_MASK ((1 << IOCPARM_SHIFT) - 1) /* parameter length mask */
|
|
||||||
#define IOCPARM_LEN(x) (((x) >> 16) & IOCPARM_MASK)
|
|
||||||
#define IOCBASECMD(x) ((x) & ~(IOCPARM_MASK << 16))
|
|
||||||
#define IOCGROUP(x) (((x) >> 8) & 0xff)
|
|
||||||
|
|
||||||
#define IOCPARM_MAX (1 << IOCPARM_SHIFT) /* max size of ioctl */
|
|
||||||
#define IOC_VOID 0x20000000 /* no parameters */
|
|
||||||
#define IOC_OUT 0x40000000 /* copy out parameters */
|
|
||||||
#define IOC_IN 0x80000000 /* copy in parameters */
|
|
||||||
#define IOC_INOUT (IOC_IN | IOC_OUT)
|
|
||||||
#define IOC_DIRMASK (IOC_VOID | IOC_OUT | IOC_IN)
|
|
||||||
|
|
||||||
#define _IOC(inout, group, num, len) \
|
|
||||||
((unsigned long)((inout) | (((len) & IOCPARM_MASK) << 16) | ((group) << 8) | \
|
|
||||||
(num)))
|
|
||||||
#define _IO(g, n) _IOC(IOC_VOID, (g), (n), 0)
|
|
||||||
#define _IOWINT(g, n) _IOC(IOC_VOID, (g), (n), sizeof(int))
|
|
||||||
#define _IOR(g, n, t) _IOC(IOC_OUT, (g), (n), sizeof(t))
|
|
||||||
#define _IOW(g, n, t) _IOC(IOC_IN, (g), (n), sizeof(t))
|
|
||||||
/* this should be _IORW, but stdio got there first */
|
|
||||||
#define _IOWR(g, n, t) _IOC(IOC_INOUT, (g), (n), sizeof(t))
|
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
static std::string iocGroupToString(unsigned iocGroup) {
|
|
||||||
if (iocGroup >= 128) {
|
|
||||||
const char *sceGroups[] = {
|
|
||||||
"DEV",
|
|
||||||
"DMEM",
|
|
||||||
"GC",
|
|
||||||
"DCE",
|
|
||||||
"UVD",
|
|
||||||
"VCE",
|
|
||||||
"DBGGC",
|
|
||||||
"TWSI",
|
|
||||||
"MDBG",
|
|
||||||
"DEVENV",
|
|
||||||
"AJM",
|
|
||||||
"TRACE",
|
|
||||||
"IBS",
|
|
||||||
"MBUS",
|
|
||||||
"HDMI",
|
|
||||||
"CAMERA",
|
|
||||||
"FAN",
|
|
||||||
"THERMAL",
|
|
||||||
"PFS",
|
|
||||||
"ICC_CONFIG",
|
|
||||||
"IPC",
|
|
||||||
"IOSCHED",
|
|
||||||
"ICC_INDICATOR",
|
|
||||||
"EXFATFS",
|
|
||||||
"ICC_NVS",
|
|
||||||
"DVE",
|
|
||||||
"ICC_POWER",
|
|
||||||
"AV_CONTROL",
|
|
||||||
"ICC_SC_CONFIGURATION",
|
|
||||||
"ICC_DEVICE_POWER",
|
|
||||||
"SSHOT",
|
|
||||||
"DCE_SCANIN",
|
|
||||||
"FSCTRL",
|
|
||||||
"HMD",
|
|
||||||
"SHM",
|
|
||||||
"PHYSHM",
|
|
||||||
"HMDDFU",
|
|
||||||
"BLUETOOTH_HID",
|
|
||||||
"SBI",
|
|
||||||
"S3DA",
|
|
||||||
"SPM",
|
|
||||||
"BLOCKPOOL",
|
|
||||||
"SDK_EVENTLOG",
|
|
||||||
};
|
|
||||||
|
|
||||||
if (iocGroup - 127 >= std::size(sceGroups)) {
|
|
||||||
return "'?'";
|
|
||||||
}
|
|
||||||
|
|
||||||
return sceGroups[iocGroup - 127];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isprint(iocGroup)) {
|
|
||||||
return "'" + std::string(1, (char)iocGroup) + "'";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "'?'";
|
|
||||||
}
|
|
||||||
|
|
||||||
static void printIoctl(unsigned long arg) {
|
|
||||||
std::printf("0x%lx { IO%s%s %lu(%s), %lu, %lu }\n", arg,
|
|
||||||
arg & IOC_OUT ? "R" : "", arg & IOC_IN ? "W" : "", IOCGROUP(arg),
|
|
||||||
iocGroupToString(IOCGROUP(arg)).c_str(), arg & 0xFF,
|
|
||||||
IOCPARM_LEN(arg));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ioctlToStream(std::ostream &stream, unsigned long arg) {
|
|
||||||
stream << "0x" << std::hex << arg << " { IO";
|
|
||||||
|
|
||||||
if ((arg & IOC_OUT) != 0) {
|
|
||||||
stream << 'R';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((arg & IOC_IN) != 0) {
|
|
||||||
stream << 'W';
|
|
||||||
}
|
|
||||||
if ((arg & IOC_VOID) != 0) {
|
|
||||||
stream << 'i';
|
|
||||||
}
|
|
||||||
|
|
||||||
stream << " 0x" << IOCGROUP(arg);
|
|
||||||
stream << "('" << iocGroupToString(IOCGROUP(arg)) << "'), ";
|
|
||||||
stream << std::dec << (arg & 0xFF) << ", " << IOCPARM_LEN(arg) << " }";
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string ioctlToString(unsigned long arg) {
|
|
||||||
std::ostringstream stream;
|
|
||||||
ioctlToStream(stream, arg);
|
|
||||||
return std::move(stream).str();
|
|
||||||
}
|
|
||||||
|
|
||||||
orbis::SysResult orbis::sys_ioctl(Thread *thread, sint fd, ulong com,
|
orbis::SysResult orbis::sys_ioctl(Thread *thread, sint fd, ulong com,
|
||||||
caddr_t data) {
|
caddr_t data) {
|
||||||
auto str = ioctlToString(com);
|
|
||||||
// ORBIS_LOG_WARNING(__FUNCTION__, fd, com, str);
|
|
||||||
rx::Ref<File> file = thread->tproc->fileDescriptors.get(fd);
|
rx::Ref<File> file = thread->tproc->fileDescriptors.get(fd);
|
||||||
if (file == nullptr) {
|
if (file == nullptr) {
|
||||||
return ErrorCode::BADF;
|
return ErrorCode::BADF;
|
||||||
|
|
@ -433,6 +312,10 @@ orbis::SysResult orbis::sys_ioctl(Thread *thread, sint fd, ulong com,
|
||||||
|
|
||||||
auto ioctl = file->ops->ioctl;
|
auto ioctl = file->ops->ioctl;
|
||||||
if (ioctl == nullptr) {
|
if (ioctl == nullptr) {
|
||||||
|
if (auto device = file->device.get()) {
|
||||||
|
return device->ioctl(com, data, thread);
|
||||||
|
}
|
||||||
|
|
||||||
return ErrorCode::NOTSUP;
|
return ErrorCode::NOTSUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue