diff --git a/kernel/orbis/CMakeLists.txt b/kernel/orbis/CMakeLists.txt index 5f029144e..013f21310 100644 --- a/kernel/orbis/CMakeLists.txt +++ b/kernel/orbis/CMakeLists.txt @@ -6,6 +6,7 @@ add_library(obj.orbis-kernel OBJECT src/sysvec.cpp src/event.cpp src/evf.cpp + src/IoDevice.cpp src/ipmi.cpp src/KernelAllocator.cpp src/KernelContext.cpp diff --git a/kernel/orbis/include/orbis/IoDevice.hpp b/kernel/orbis/include/orbis/IoDevice.hpp index 02b7a6aeb..07db206a9 100644 --- a/kernel/orbis/include/orbis/IoDevice.hpp +++ b/kernel/orbis/include/orbis/IoDevice.hpp @@ -1,7 +1,10 @@ #pragma once #include "error/ErrorCode.hpp" +#include "orbis-config.hpp" #include "rx/Rc.hpp" +#include +#include namespace orbis { enum OpenFlags { @@ -50,5 +53,133 @@ struct IoDevice : rx::RcBase { virtual ErrorCode rename(const char *from, const char *to, Thread *thread) { return ErrorCode::NOTSUP; } + + virtual ErrorCode ioctl(std::uint64_t request, orbis::ptr 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 arg, + IoDevice *device, void (*impl)()); + +struct IoctlHandlerEntry { + IoctlHandler handler; + void (*impl)(); + std::uint32_t cmd; +}; +template struct IoDeviceWithIoctl : IoDevice { + IoctlHandlerEntry ioctlTable[256]{}; + + template + requires requires { + requires std::is_base_of_v; + requires ioctl::paramSize(Cmd) == sizeof(T); + requires ioctl::group(Cmd) == Group; + requires std::is_const_v || ioctl::isInOut(Cmd); + requires !std::is_const_v || 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 arg, IoDevice *device, + void (*opaqueImpl)()) -> ErrorCode { + auto impl = std::bit_cast(opaqueImpl); + + std::remove_cvref_t _arg; + ORBIS_RET_ON_ERROR(orbis::uread(_arg, orbis::ptr(arg))); + ORBIS_RET_ON_ERROR(impl(thread, static_cast(device), _arg)); + + if constexpr (ioctl::isInOut(Cmd)) { + return orbis::uwrite(orbis::ptr(arg), _arg); + } else { + return {}; + } + }; + entry.impl = std::bit_cast(handler); + entry.cmd = Cmd; + } + + template + requires requires { + requires std::is_base_of_v; + 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, IoDevice *device, + void (*opaqueImpl)()) -> ErrorCode { + auto impl = std::bit_cast(opaqueImpl); + return impl(thread, static_cast(device)); + }; + + entry.impl = std::bit_cast(handler); + entry.cmd = Cmd; + } + + template + requires requires { + requires std::is_base_of_v; + requires ioctl::paramSize(Cmd) == sizeof(T); + requires ioctl::group(Cmd) == Group; + requires ioctl::isOut(Cmd); + } + void addIoctl(std::pair (*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 arg, IoDevice *device, + void (*opaqueImpl)()) -> ErrorCode { + auto impl = std::bit_cast(opaqueImpl); + auto [errc, value] = impl(thread, static_cast(device)); + ORBIS_RET_ON_ERROR(errc); + return orbis::uwrite(orbis::ptr(arg), value); + }; + entry.impl = std::bit_cast(handler); + entry.cmd = Cmd; + } + + ErrorCode ioctl(std::uint64_t request, orbis::ptr 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 diff --git a/kernel/orbis/src/IoDevice.cpp b/kernel/orbis/src/IoDevice.cpp new file mode 100644 index 000000000..0840f2a2e --- /dev/null +++ b/kernel/orbis/src/IoDevice.cpp @@ -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 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; +} diff --git a/kernel/orbis/src/sys/sys_generic.cpp b/kernel/orbis/src/sys/sys_generic.cpp index 97c1d9f01..fc8b5a6b3 100644 --- a/kernel/orbis/src/sys/sys_generic.cpp +++ b/kernel/orbis/src/sys/sys_generic.cpp @@ -303,129 +303,8 @@ orbis::SysResult orbis::sys_freebsd6_ftruncate(Thread *thread, sint fd, sint, off_t 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, caddr_t data) { - auto str = ioctlToString(com); - // ORBIS_LOG_WARNING(__FUNCTION__, fd, com, str); rx::Ref file = thread->tproc->fileDescriptors.get(fd); if (file == nullptr) { return ErrorCode::BADF; @@ -433,6 +312,10 @@ orbis::SysResult orbis::sys_ioctl(Thread *thread, sint fd, ulong com, auto ioctl = file->ops->ioctl; if (ioctl == nullptr) { + if (auto device = file->device.get()) { + return device->ioctl(com, data, thread); + } + return ErrorCode::NOTSUP; }