diff --git a/kernel/include/kernel/MemoryResource.hpp b/kernel/include/kernel/MemoryResource.hpp index 676a56e3c..31afe59b9 100644 --- a/kernel/include/kernel/MemoryResource.hpp +++ b/kernel/include/kernel/MemoryResource.hpp @@ -64,6 +64,7 @@ struct ExternalResource { std::errc create(std::size_t) { return {}; } void serialize(rx::Serializer &) const {} void deserialize(rx::Deserializer &) {} + void destroy() {} }; template @@ -150,16 +151,22 @@ struct AllocableResource : Resource { iterator end() { return allocations.end(); } AllocationResult map(std::uint64_t addressHint, std::uint64_t size, - AllocationT &allocationInfo, - rx::EnumBitSet flags, - std::uint64_t alignment) { + AllocationT &allocationInfo, + rx::EnumBitSet flags, + std::uint64_t alignment) { if (flags & AllocationFlags::Stack) { addressHint = rx::alignDown(addressHint, alignment); } else { addressHint = rx::alignUp(addressHint, alignment); } - auto it = allocations.queryArea(addressHint); + iterator it; + if (flags & AllocationFlags::Fixed) { + it = allocations.queryArea(addressHint); + } else { + it = allocations.lowerBound(addressHint); + } + if (it == allocations.end()) { return {end(), std::errc::invalid_argument, {}}; } @@ -179,7 +186,7 @@ struct AllocableResource : Resource { if (flags & AllocationFlags::NoOverwrite) { if (it->isAllocated() || !it.range().contains(fixedRange)) { - return {end(), std::errc::invalid_argument, {}}; + return {end(), std::errc::file_exists, {}}; } } else if ((flags & AllocationFlags::Dry) != AllocationFlags::Dry) { if constexpr (requires { @@ -237,7 +244,17 @@ struct AllocableResource : Resource { } } } else { - auto hasEnoughSpace = [alignment, size](rx::AddressRange range) { + auto hasEnoughSpace = [=](rx::AddressRange range) { + if (range.contains(addressHint)) { + if (flags & AllocationFlags::Stack) { + range = rx::AddressRange::fromBeginEnd( + rx::alignDown(range.beginAddress(), alignment), addressHint); + } else { + range = + rx::AddressRange::fromBeginEnd(addressHint, range.endAddress()); + } + } + auto alignedAddress = rx::AddressRange::fromBeginEnd( rx::alignUp(range.beginAddress(), alignment), range.endAddress()); @@ -271,8 +288,29 @@ struct AllocableResource : Resource { } // now `it` points to region that meets requirements, create fixed range - fixedRange = rx::AddressRange::fromBeginEnd( - rx::alignUp(it->beginAddress(), alignment), it->endAddress()); + if (it.range().contains(addressHint)) { + if (flags & AllocationFlags::Stack) { + fixedRange = + rx::AddressRange::fromBeginSize(rx::alignDown(addressHint - size, alignment), size); + } else { + fixedRange = + rx::AddressRange::fromBeginEnd(addressHint, it.endAddress()); + } + } else { + fixedRange = rx::AddressRange::fromBeginEnd( + rx::alignUp(it.beginAddress(), alignment), it.endAddress()); + } + } + + if (fixedRange.size() > size) { + if ((flags & AllocationFlags::Stack) && + !it.range().contains(addressHint)) { + fixedRange = rx::AddressRange::fromBeginSize( + rx::alignDown(fixedRange.endAddress() - size, alignment), size); + } else { + fixedRange = + rx::AddressRange::fromBeginSize(fixedRange.beginAddress(), size); + } } if (flags & AllocationFlags::Dry) { @@ -294,9 +332,18 @@ struct AllocableResource : Resource { if (it != begin()) { // try to merge with previous node - iterator prevIt = std::prev(it); - if (prevIt->isAllocated() && - prevIt->isRelated(it.get(), prevIt.range(), it.range())) { + iterator prevIt = it; + --prevIt; + + bool isRelated = false; + if (prevIt->isAllocated()) { + isRelated = it->isAllocated() && + prevIt->isRelated(it.get(), prevIt.range(), it.range()); + } else { + isRelated = !it->isAllocated(); + } + + if (isRelated) { // previous block is allocated and related to current block, do merge auto mergedRange = rx::AddressRange::fromBeginEnd(prevIt.beginAddress(), it.endAddress()); @@ -305,9 +352,16 @@ struct AllocableResource : Resource { } } - if (iterator nextIt = std::next(it); nextIt != end()) { - if (nextIt->isAllocated() && - it->isRelated(nextIt.get(), it.range(), nextIt.range())) { + if (iterator nextIt = it; ++nextIt != end()) { + bool isRelated = false; + if (nextIt->isAllocated()) { + isRelated = it->isAllocated() && + it->isRelated(nextIt.get(), it.range(), nextIt.range()); + } else { + isRelated = !it->isAllocated(); + } + + if (isRelated) { // next block is allocated and related to current block, do merge auto mergedRange = rx::AddressRange::fromBeginEnd(it.beginAddress(), nextIt.endAddress()); diff --git a/kernel/orbis/CMakeLists.txt b/kernel/orbis/CMakeLists.txt index 013f21310..bcc80bb9c 100644 --- a/kernel/orbis/CMakeLists.txt +++ b/kernel/orbis/CMakeLists.txt @@ -1,16 +1,19 @@ set(CMAKE_POSITION_INDEPENDENT_CODE on) add_library(obj.orbis-kernel OBJECT - src/module.cpp - src/pipe.cpp - src/sysvec.cpp src/event.cpp src/evf.cpp + src/fmem.cpp src/IoDevice.cpp src/ipmi.cpp src/KernelAllocator.cpp src/KernelContext.cpp + src/module.cpp + src/pipe.cpp + src/pmem.cpp + src/sysvec.cpp src/umtx.cpp + src/vmem.cpp src/sys/sys_acct.cpp src/sys/sys_audit.cpp src/sys/sys_capability.cpp diff --git a/kernel/orbis/include/orbis/IoDevice.hpp b/kernel/orbis/include/orbis/IoDevice.hpp index 2049e3f47..10a4ebaa7 100644 --- a/kernel/orbis/include/orbis/IoDevice.hpp +++ b/kernel/orbis/include/orbis/IoDevice.hpp @@ -2,7 +2,10 @@ #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 #include @@ -27,7 +30,7 @@ enum OpenFlags { struct File; struct Thread; - +struct Process; struct IoDevice : rx::RcBase { virtual ErrorCode open(rx::Ref *file, const char *path, std::uint32_t flags, std::uint32_t mode, @@ -56,6 +59,12 @@ struct IoDevice : rx::RcBase { virtual ErrorCode ioctl(std::uint64_t request, orbis::ptr argp, Thread *thread); + + virtual ErrorCode map(rx::AddressRange range, std::int64_t offset, + rx::EnumBitSet protection, + Process *process) { + return ErrorCode::NOTSUP; + } }; namespace ioctl { diff --git a/kernel/orbis/include/orbis/error/SysResult.hpp b/kernel/orbis/include/orbis/error/SysResult.hpp index 8f5856be9..9ce52eddb 100644 --- a/kernel/orbis/include/orbis/error/SysResult.hpp +++ b/kernel/orbis/include/orbis/error/SysResult.hpp @@ -20,6 +20,12 @@ public: [[nodiscard]] int value() const { return mValue < 0 ? -mValue : mValue; } [[nodiscard]] bool isError() const { return mValue < 0; } + [[nodiscard]] ErrorCode errc() const { + return static_cast(value()); + } + + explicit operator bool() const { return mValue != 0; } + [[nodiscard]] auto operator<=>(ErrorCode ec) const { return static_cast(value()) <=> ec; } diff --git a/kernel/orbis/include/orbis/fmem.hpp b/kernel/orbis/include/orbis/fmem.hpp new file mode 100644 index 000000000..b6edf1206 --- /dev/null +++ b/kernel/orbis/include/orbis/fmem.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "error/ErrorCode.hpp" +#include "rx/AddressRange.hpp" +#include + +namespace orbis { +struct Process; +} +namespace orbis::fmem { +ErrorCode initialize(Process *process, std::uint64_t size); +void destroy(Process *process); +std::pair allocate(Process *process, + std::uint64_t size); +ErrorCode deallocate(Process *process, rx::AddressRange range); +} // namespace orbis::fmem diff --git a/kernel/orbis/include/orbis/pmem.hpp b/kernel/orbis/include/orbis/pmem.hpp new file mode 100644 index 000000000..516354116 --- /dev/null +++ b/kernel/orbis/include/orbis/pmem.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include "error/ErrorCode.hpp" +#include "kernel/MemoryResource.hpp" +#include "rx/AddressRange.hpp" +#include "rx/EnumBitSet.hpp" +#include + +namespace orbis { +using kernel::AllocationFlags; +struct IoDevice; +} // namespace orbis + +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); +void destroy(); +std::pair +allocate(std::uint64_t addressHint, std::uint64_t size, MemoryType memoryType, + rx::EnumBitSet flags, std::uint64_t alignment); +ErrorCode deallocate(rx::AddressRange range); +std::optional query(std::uint64_t address); +ErrorCode map(std::uint64_t virtualAddress, rx::AddressRange range, + rx::EnumBitSet protection); +std::size_t getSize(); +IoDevice *getDevice(); +} // namespace orbis::pmem diff --git a/kernel/orbis/include/orbis/sys/sysproto.hpp b/kernel/orbis/include/orbis/sys/sysproto.hpp index e15ac4a83..200d17abb 100644 --- a/kernel/orbis/include/orbis/sys/sysproto.hpp +++ b/kernel/orbis/include/orbis/sys/sysproto.hpp @@ -13,6 +13,8 @@ using cpusetid_t = sint; using cpuwhich_t = sint; using cpulevel_t = sint; using SceKernelModule = ModuleHandle; +using SockLen = uint32_t; +using id_t = uint32_t; struct Thread; struct AuthInfo; @@ -501,17 +503,15 @@ SysResult sys_rtprio_thread(Thread *thread, sint function, lwpid_t lwpid, ptr rtp); SysResult sys_sctp_peeloff(Thread *thread, sint sd, uint32_t name); SysResult sys_sctp_generic_sendmsg(Thread *thread, sint sd, caddr_t msg, - sint mlen, caddr_t to, __socklen_t tolen, + sint mlen, caddr_t to, SockLen tolen, ptr sinfo, sint flags); SysResult sys_sctp_generic_sendmsg_iov(Thread *thread, sint sd, ptr iov, - sint iovlen, caddr_t to, - __socklen_t tolen, + sint iovlen, caddr_t to, SockLen tolen, ptr sinfo, sint flags); SysResult sys_sctp_generic_recvmsg(Thread *thread, sint sd, ptr iov, - sint iovlen, caddr_t from, - __socklen_t fromlen, + sint iovlen, caddr_t from, SockLen fromlen, ptr sinfo, sint flags); SysResult sys_pread(Thread *thread, sint fd, ptr buf, size_t nbyte, @@ -587,9 +587,9 @@ SysResult sys_cap_getmode(Thread *thread, ptr modep); SysResult sys_pdfork(Thread *thread, ptr fdp, sint flags); SysResult sys_pdkill(Thread *thread, sint fd, sint signum); SysResult sys_pdgetpid(Thread *thread, sint fd, ptr pidp); -SysResult sys_pselect(Thread *thread, sint nd, ptr in, ptr ou, - ptr ex, ptr ts, - ptr sm); +SysResult sys_pselect(Thread *thread, sint nd, ptr in, + ptr ou, ptr ex, + ptr ts, ptr sm); SysResult sys_getloginclass(Thread *thread, ptr namebuf, size_t namelen); SysResult sys_setloginclass(Thread *thread, ptr namebuf); SysResult sys_rctl_get_racct(Thread *thread, ptr inbufp, diff --git a/kernel/orbis/include/orbis/vmem.hpp b/kernel/orbis/include/orbis/vmem.hpp new file mode 100644 index 000000000..3a9724467 --- /dev/null +++ b/kernel/orbis/include/orbis/vmem.hpp @@ -0,0 +1,93 @@ +#pragma once + +#include "kernel/MemoryResource.hpp" +#include "orbis-config.hpp" +#include "rx/AddressRange.hpp" +#include "rx/EnumBitSet.hpp" +#include "rx/StaticString.hpp" +#include + +namespace orbis { +using kernel::AllocationFlags; +} + +namespace orbis { +struct IoDevice; +struct Process; + +namespace vmem { +static constexpr auto kPageSize = 16 * 1024; + +enum class Protection { + CpuRead, + CpuWrite, + CpuExec, + GpuRead = 4, + GpuWrite, + + bitset_last = GpuWrite +}; + +enum class BlockFlags { + FlexibleMemory, + DirectMemory, + Stack, + PooledMemory, + Commited, + Allocated, + + bitset_last = Allocated +}; + +inline constexpr auto kProtCpuReadWrite = + Protection::CpuRead | Protection::CpuWrite; +inline constexpr auto kProtCpuAll = + Protection::CpuRead | Protection::CpuWrite | Protection::CpuExec; +inline constexpr auto kProtGpuAll = Protection::GpuRead | Protection::GpuWrite; + +#pragma pack(push, 1) +struct QueryResult { + uint64_t start; + uint64_t end; + uint64_t offset; + uint32_t protection; + uint32_t memoryType; + uint32_t flags; + rx::StaticCString<32> name; + uint32_t _padding; +}; + +static_assert(sizeof(QueryResult) == 72); + +struct MemoryProtection { + uint64_t startAddress; + uint64_t endAddress; + rx::EnumBitSet prot; + uint32_t _padding; +}; + +static_assert(sizeof(MemoryProtection) == 24); +#pragma pack(pop) + +void initialize(Process *process, bool force = false); +void fork(Process *process, Process *parentThread); + +std::pair +reserve(Process *process, std::uint64_t addressHint, std::uint64_t size, + rx::EnumBitSet allocFlags); + +std::pair +map(Process *process, std::uint64_t addressHint, std::uint64_t size, + rx::EnumBitSet allocFlags, + rx::EnumBitSet prot = {}, + rx::EnumBitSet blockFlags = {}, + std::uint64_t alignment = kPageSize, std::string_view name = {}, + IoDevice *device = nullptr, std::int64_t deviceOffset = 0); +ErrorCode unmap(Process *process, rx::AddressRange range); +ErrorCode setName(Process *process, rx::AddressRange range, + std::string_view name); +std::optional query(Process *process, std::uint64_t address); +std::optional queryProtection(Process *process, + std::uint64_t address); +} // namespace vmem +} // namespace orbis diff --git a/kernel/orbis/src/fmem.cpp b/kernel/orbis/src/fmem.cpp new file mode 100644 index 000000000..cd6125e8d --- /dev/null +++ b/kernel/orbis/src/fmem.cpp @@ -0,0 +1,88 @@ +#include "fmem.hpp" + +#include "KernelObject.hpp" +#include "error.hpp" +#include "kernel/KernelObject.hpp" +#include "kernel/MemoryResource.hpp" +#include "pmem.hpp" +#include "rx/AddressRange.hpp" +#include "thread/Process.hpp" +#include "vmem.hpp" +#include +#include + +struct FlexibleMemoryAllocation { + bool allocated = false; + + [[nodiscard]] bool isAllocated() const { return allocated; } + [[nodiscard]] bool isRelated(const FlexibleMemoryAllocation &, + rx::AddressRange, rx::AddressRange) const { + return true; + } + + [[nodiscard]] FlexibleMemoryAllocation + merge(const FlexibleMemoryAllocation &other, rx::AddressRange, + rx::AddressRange) const { + return other; + } + + bool operator==(const FlexibleMemoryAllocation &) const = default; +}; + +using FlexibleMemoryResource = + kernel::AllocableResource; + +static auto g_fmemInstance = orbis::createProcessLocalObject< + kernel::LockableKernelObject>(); + +orbis::ErrorCode orbis::fmem::initialize(Process *process, std::uint64_t size) { + auto [range, errc] = + pmem::allocate(pmem::getSize() - 1, size, pmem::MemoryType::WbOnion, + kernel::AllocationFlags::Stack, vmem::kPageSize); + if (errc != ErrorCode{}) { + return errc; + } + + auto fmem = process->get(g_fmemInstance); + std::lock_guard lock(*fmem); + return toErrorCode(fmem->create(range)); +} + +void orbis::fmem::destroy(Process *process) { + auto fmem = process->get(g_fmemInstance); + + std::lock_guard lock(*fmem); + + for (auto allocation : fmem->allocations) { + pmem::deallocate(allocation); + } + + fmem->destroy(); +} + +std::pair +orbis::fmem::allocate(Process *process, std::uint64_t size) { + auto fmem = process->get(g_fmemInstance); + + std::lock_guard lock(*fmem); + FlexibleMemoryAllocation allocation{.allocated = true}; + auto [it, errc, range] = fmem->map( + 0, size, allocation, kernel::AllocationFlags::NoMerge, vmem::kPageSize); + + if (errc != std::errc{}) { + return {{}, toErrorCode(errc)}; + } + + return {range, {}}; +} + +orbis::ErrorCode orbis::fmem::deallocate(Process *process, + rx::AddressRange range) { + FlexibleMemoryAllocation allocation{}; + auto fmem = process->get(g_fmemInstance); + std::lock_guard lock(*fmem); + auto [it, errc, _] = fmem->map(range.beginAddress(), range.size(), allocation, + AllocationFlags::Fixed, 1); + + return toErrorCode(errc); +} diff --git a/kernel/orbis/src/ipmi.cpp b/kernel/orbis/src/ipmi.cpp index 0eb824f25..3c3c77b0c 100644 --- a/kernel/orbis/src/ipmi.cpp +++ b/kernel/orbis/src/ipmi.cpp @@ -5,7 +5,6 @@ #include "utils/Logs.hpp" #include #include -#include orbis::ErrorCode orbis::ipmiCreateClient(Process *proc, void *clientImpl, const char *name, diff --git a/kernel/orbis/src/pmem.cpp b/kernel/orbis/src/pmem.cpp new file mode 100644 index 000000000..8a6b76e46 --- /dev/null +++ b/kernel/orbis/src/pmem.cpp @@ -0,0 +1,127 @@ +#include "pmem.hpp" +#include "IoDevice.hpp" +#include "KernelObject.hpp" +#include "error.hpp" +#include "error/ErrorCode.hpp" +#include "kernel/KernelObject.hpp" +#include "kernel/MemoryResource.hpp" +#include "rx/AddressRange.hpp" +#include "vmem.hpp" +#include +#include + +struct PhysicalMemoryAllocation { + orbis::pmem::MemoryType type = orbis::pmem::MemoryType::Invalid; + + [[nodiscard]] bool isAllocated() const { + return type != orbis::pmem::MemoryType::Invalid; + } + [[nodiscard]] bool isRelated(const PhysicalMemoryAllocation &left, + rx::AddressRange, rx::AddressRange) const { + return type == left.type; + } + + [[nodiscard]] PhysicalMemoryAllocation + merge(const PhysicalMemoryAllocation &other, rx::AddressRange, + rx::AddressRange) const { + assert(other.type == type); + return other; + } + + bool operator==(const PhysicalMemoryAllocation &) const = default; +}; + +using MappableMemoryResource = + kernel::MappableResource; + +using PhysicalMemoryResource = + kernel::AllocableResource; + +static auto g_pmemInstance = orbis::createGlobalObject< + kernel::LockableKernelObject>(); + +struct PhysicalMemory : orbis::IoDevice { + orbis::ErrorCode open(rx::Ref *file, const char *path, + std::uint32_t flags, std::uint32_t mode, + orbis::Thread *thread) override { + rx::die("open PhysicalMemory device"); + } + + orbis::ErrorCode map(rx::AddressRange range, std::int64_t offset, + rx::EnumBitSet protection, + orbis::Process *) override { + return orbis::pmem::map( + range.beginAddress(), + rx::AddressRange::fromBeginSize(offset, range.size()), protection); + } + + void serialize(rx::Serializer &s) const {} + void deserialize(rx::Deserializer &s) {} +}; + +static auto g_phyMemory = orbis::createGlobalObject(); + +orbis::ErrorCode orbis::pmem::initialize(std::uint64_t size) { + std::lock_guard lock(*g_pmemInstance); + return toErrorCode( + g_pmemInstance->create(rx::AddressRange::fromBeginSize(0, size))); +} + +void orbis::pmem::destroy() { + std::lock_guard lock(*g_pmemInstance); + g_pmemInstance->destroy(); +} + +std::pair orbis::pmem::allocate( + std::uint64_t addressHint, std::uint64_t size, MemoryType memoryType, + rx::EnumBitSet flags, std::uint64_t alignment) { + std::lock_guard lock(*g_pmemInstance); + PhysicalMemoryAllocation allocation{.type = memoryType}; + auto [it, errc, range] = + g_pmemInstance->map(addressHint, size, allocation, flags, alignment); + + if (errc != std::errc{}) { + return {{}, toErrorCode(errc)}; + } + + return {range, {}}; +} + +orbis::ErrorCode orbis::pmem::deallocate(rx::AddressRange range) { + std::lock_guard lock(*g_pmemInstance); + PhysicalMemoryAllocation allocation{}; + auto [it, errc, _] = + g_pmemInstance->map(range.beginAddress(), range.size(), allocation, + AllocationFlags::Fixed, 1); + + return toErrorCode(errc); +} + +std::optional +orbis::pmem::query(std::uint64_t address) { + std::lock_guard lock(*g_pmemInstance); + auto result = g_pmemInstance->query(address); + + if (result == g_pmemInstance->end()) { + return {}; + } + + return AllocatedMemory{.range = result.range(), .memoryType = result->type}; +} + +orbis::ErrorCode +orbis::pmem::map(std::uint64_t virtualAddress, rx::AddressRange range, + rx::EnumBitSet protection) { + auto virtualRange = + rx::AddressRange::fromBeginSize(virtualAddress, range.size()); + auto errc = g_pmemInstance->mappable.map(virtualRange, range.beginAddress(), + protection, orbis::vmem::kPageSize); + + return toErrorCode(errc); +} + +std::size_t orbis::pmem::getSize() { return g_pmemInstance->size; } +orbis::IoDevice *orbis::pmem::getDevice() { return g_phyMemory.get(); } + diff --git a/kernel/orbis/src/sys/sys_event.cpp b/kernel/orbis/src/sys/sys_event.cpp index 01828e146..eab4e0d7d 100644 --- a/kernel/orbis/src/sys/sys_event.cpp +++ b/kernel/orbis/src/sys/sys_event.cpp @@ -7,7 +7,10 @@ #include #include #include + +#ifdef __linux #include +#endif orbis::SysResult orbis::sys_kqueue(Thread *thread) { auto queue = knew(); @@ -38,17 +41,22 @@ orbis::SysResult orbis::sys_kqueueex(Thread *thread, ptr name, } static bool isReadEventTriggered(int hostFd) { +#ifdef __linux fd_set fds{}; FD_SET(hostFd, &fds); timeval timeout{}; if (::select(hostFd + 1, &fds, nullptr, nullptr, &timeout) < 0) { return false; } - return FD_ISSET(hostFd, &fds); +#else +#warning "Not implemented" + return false; +#endif } static bool isWriteEventTriggered(int hostFd) { +#ifdef __linux fd_set fds{}; FD_SET(hostFd, &fds); timeval timeout{}; @@ -57,6 +65,10 @@ static bool isWriteEventTriggered(int hostFd) { } return FD_ISSET(hostFd, &fds); +#else +#warning "Not implemented" + return false; +#endif } namespace orbis { diff --git a/kernel/orbis/src/sys/sys_generic.cpp b/kernel/orbis/src/sys/sys_generic.cpp index fc8b5a6b3..5a9224e8d 100644 --- a/kernel/orbis/src/sys/sys_generic.cpp +++ b/kernel/orbis/src/sys/sys_generic.cpp @@ -322,10 +322,10 @@ orbis::SysResult orbis::sys_ioctl(Thread *thread, sint fd, ulong com, std::lock_guard lock(file->mtx); return ioctl(file.get(), com, data, thread); } -orbis::SysResult orbis::sys_pselect(Thread *thread, sint nd, ptr in, - ptr ou, ptr ex, +orbis::SysResult orbis::sys_pselect(Thread *thread, sint nd, ptr in, + ptr ou, ptr ex, ptr ts, - ptr sm) { + ptr sm) { return ErrorCode::NOSYS; } orbis::SysResult orbis::sys_select(Thread *thread, sint nd, diff --git a/kernel/orbis/src/sys/sys_resource.cpp b/kernel/orbis/src/sys/sys_resource.cpp index dcef27342..582ae617d 100644 --- a/kernel/orbis/src/sys/sys_resource.cpp +++ b/kernel/orbis/src/sys/sys_resource.cpp @@ -2,7 +2,10 @@ #include "thread/Process.hpp" #include "thread/Thread.hpp" #include "utils/Logs.hpp" + +#ifdef __linux #include +#endif namespace orbis { struct rlimit { @@ -37,7 +40,7 @@ orbis::SysResult orbis::sys_rtprio_thread(Thread *thread, sint function, return orbis::uwrite(rtp, targetThread->prio); } else if (function == 1) { ORBIS_RET_ON_ERROR(orbis::uread(targetThread->prio, rtp)); - +#ifdef __linux int hostPolicy = SCHED_RR; auto prioMin = sched_get_priority_min(hostPolicy); auto prioMax = sched_get_priority_max(hostPolicy); @@ -71,6 +74,7 @@ orbis::SysResult orbis::sys_rtprio_thread(Thread *thread, sint function, targetThread->tid, targetThread->prio.prio, targetThread->prio.type, prioMin, prioMax); } +#endif } return {}; } diff --git a/kernel/orbis/src/sys/sys_sce.cpp b/kernel/orbis/src/sys/sys_sce.cpp index ba22f9aef..a25d3ce66 100644 --- a/kernel/orbis/src/sys/sys_sce.cpp +++ b/kernel/orbis/src/sys/sys_sce.cpp @@ -16,8 +16,6 @@ #include "utils/Logs.hpp" #include #include -#include -#include #include struct orbis::AppMountInfo { diff --git a/kernel/orbis/src/sys/sys_sig.cpp b/kernel/orbis/src/sys/sys_sig.cpp index 82147a175..e5dc057f4 100644 --- a/kernel/orbis/src/sys/sys_sig.cpp +++ b/kernel/orbis/src/sys/sys_sig.cpp @@ -121,11 +121,15 @@ orbis::SysResult orbis::sys_kill(Thread *thread, sint pid, sint signum) { hostPid = process->hostPid; } +#ifdef __linux // FIXME: invoke subscriber thread int result = ::sigqueue(hostPid, SIGUSR1, {.sival_int = signum}); if (result < 0) { return static_cast(errno); } +#else +#warning "Not implemented" +#endif return {}; } diff --git a/kernel/orbis/src/sys/sys_time.cpp b/kernel/orbis/src/sys/sys_time.cpp index 82c6f49f4..89c6472c7 100644 --- a/kernel/orbis/src/sys/sys_time.cpp +++ b/kernel/orbis/src/sys/sys_time.cpp @@ -1,3 +1,4 @@ +#include "rx/die.hpp" #include "sys/sysproto.hpp" #include "time.hpp" #include "utils/Logs.hpp" @@ -141,26 +142,37 @@ orbis::SysResult orbis::sys_nanosleep(Thread *thread, orbis::SysResult orbis::sys_gettimeofday(Thread *thread, ptr tp, ptr tzp) { ORBIS_LOG_TRACE(__FUNCTION__, tp, tzp); + struct ::timeval tv; - if (::gettimeofday(&tv, nullptr) != 0) - std::abort(); + if (::gettimeofday(&tv, nullptr) != 0) { + rx::die("gettimeofday failed, {}", errno); + } + if (tp) { orbis::timeval value; value.tv_sec = tv.tv_sec; value.tv_usec = tv.tv_usec; - if (auto e = uwrite(tp, value); e != ErrorCode{}) - return e; + ORBIS_RET_ON_ERROR(uwrite(tp, value)); } + if (tzp) { - struct ::tm tp; - if (localtime_r(&tv.tv_sec, &tp) != &tp) - std::abort(); orbis::timezone value; + +#ifdef __linux + struct ::tm tp; + + if (localtime_r(&tv.tv_sec, &tp) != &tp) { + rx::die("localtime_r failed, {}", errno); + } value.tz_dsttime = 0; // TODO value.tz_mineast = tp.tm_gmtoff / 60; - if (auto e = uwrite(tzp, value); e != ErrorCode{}) - return e; +#else + value = {}; +#endif + + ORBIS_RET_ON_ERROR(uwrite(tzp, value)); } + return {}; } orbis::SysResult orbis::sys_settimeofday(Thread *thread, ptr tp, diff --git a/kernel/orbis/src/sys/sys_uipc.cpp b/kernel/orbis/src/sys/sys_uipc.cpp index 5ab829efd..eccf0f51a 100644 --- a/kernel/orbis/src/sys/sys_uipc.cpp +++ b/kernel/orbis/src/sys/sys_uipc.cpp @@ -5,7 +5,6 @@ #include "thread/Thread.hpp" #include "uio.hpp" #include "utils/Logs.hpp" -#include orbis::SysResult orbis::sys_socket(Thread *thread, sint domain, sint type, sint protocol) { @@ -212,18 +211,18 @@ orbis::SysResult orbis::sys_sctp_peeloff(Thread *thread, sint sd, } orbis::SysResult orbis::sys_sctp_generic_sendmsg(Thread *thread, sint sd, caddr_t msg, sint mlen, - caddr_t to, __socklen_t tolen, + caddr_t to, SockLen tolen, ptr sinfo, sint flags) { return ErrorCode::NOSYS; } orbis::SysResult orbis::sys_sctp_generic_sendmsg_iov( Thread *thread, sint sd, ptr iov, sint iovlen, caddr_t to, - __socklen_t tolen, ptr sinfo, sint flags) { + SockLen tolen, ptr sinfo, sint flags) { return ErrorCode::NOSYS; } orbis::SysResult orbis::sys_sctp_generic_recvmsg(Thread *thread, sint sd, ptr iov, - sint iovlen, caddr_t from, __socklen_t fromlen, + sint iovlen, caddr_t from, SockLen fromlen, ptr sinfo, sint flags) { return ErrorCode::NOSYS; } diff --git a/kernel/orbis/src/utils/Logs.cpp b/kernel/orbis/src/utils/Logs.cpp index 9b38ba877..28feda1c9 100644 --- a/kernel/orbis/src/utils/Logs.cpp +++ b/kernel/orbis/src/utils/Logs.cpp @@ -117,17 +117,17 @@ void log_class_string::format(std::string &out, const void *arg) { } template <> -void log_class_string::format(std::string &out, const void *arg) { +void log_class_string::format(std::string &out, const void *arg) { append_hex(out, get_object(arg)); } template <> void log_class_string::format(std::string &out, const void *arg) { - append_hex(out, static_cast(get_object(arg))); + append_hex(out, static_cast(get_object(arg))); } template <> -void log_class_string::format(std::string &out, const void *arg) { +void log_class_string::format(std::string &out, const void *arg) { append_hex(out, get_object(arg)); } @@ -137,7 +137,7 @@ void log_class_string::format(std::string &out, const void *arg) { } template <> -void log_class_string::format(std::string &out, const void *arg) { +void log_class_string::format(std::string &out, const void *arg) { append_hex(out, get_object(arg)); } diff --git a/kernel/orbis/src/vmem.cpp b/kernel/orbis/src/vmem.cpp new file mode 100644 index 000000000..0109d1ead --- /dev/null +++ b/kernel/orbis/src/vmem.cpp @@ -0,0 +1,296 @@ +#include "vmem.hpp" +#include "IoDevice.hpp" +#include "KernelObject.hpp" +#include "error.hpp" +#include "pmem.hpp" +#include "rx/Mappable.hpp" +#include "rx/Rc.hpp" +#include "rx/StaticString.hpp" +#include "rx/mem.hpp" +#include "rx/print.hpp" +#include "thread/Process.hpp" +#include +#include +#include + +struct VirtualMemoryAllocation { + rx::EnumBitSet flags{}; + rx::EnumBitSet prot{}; + rx::Ref device; + std::uint64_t deviceOffset = 0; + rx::StaticString<31> name; + + [[nodiscard]] bool isAllocated() const { + return (flags & orbis::vmem::BlockFlags::Allocated) == + orbis::vmem::BlockFlags::Allocated; + } + + [[nodiscard]] bool + isRelated(const VirtualMemoryAllocation &other, rx::AddressRange selfRange, + [[maybe_unused]] rx::AddressRange rightRange) const { + if (flags != other.flags || prot != other.prot || device != other.device || + name != other.name) { + return false; + } + + return !isAllocated() || + deviceOffset + selfRange.size() == other.deviceOffset; + } + + [[nodiscard]] VirtualMemoryAllocation merge(const VirtualMemoryAllocation &, + rx::AddressRange, + rx::AddressRange) const { + return *this; + } + + std::pair + truncate(rx::AddressRange selfRange, rx::AddressRange, + rx::AddressRange rightRange) { + if (!isAllocated() || !rightRange.isValid() || device == nullptr) { + return {}; + } + + // adjust deviceOffset for new right node + auto result = *this; + + result.deviceOffset = + rightRange.beginAddress() - selfRange.beginAddress() + deviceOffset; + return {rightRange, std::move(result)}; + } + + bool operator==(const VirtualMemoryAllocation &) const = default; +}; + +using MappableMemoryResource = + kernel::MappableResource; + +struct VirtualMemoryResource + : kernel::AllocableResource {}; + +static auto g_vmInstance = orbis::createProcessLocalObject< + kernel::LockableKernelObject>(); + +void orbis::vmem::initialize(Process *process, bool force) { + auto vmem = process->get(g_vmInstance); + + std::lock_guard lock(*vmem); + + // FIXME: for PS5 should be extended range + auto range = rx::AddressRange::fromBeginEnd(0x400000, 0x10000000000); + vmem->create(range); + + std::size_t address = range.beginAddress(); + auto alignment = std::max(rx::mem::pageSize, kPageSize); + + auto reserveRangeImpl = [&](rx::AddressRange reserveRange) { + { + auto virtualReserveRange = rx::AddressRange::fromBeginEnd( + rx::alignDown(reserveRange.beginAddress(), kPageSize), + rx::alignUp(reserveRange.endAddress(), kPageSize)); + vmem->allocations.map(virtualReserveRange, {}); + } + + reserveRange = rx::AddressRange::fromBeginEnd( + rx::alignUp(reserveRange.beginAddress(), alignment), + rx::alignDown(reserveRange.endAddress(), alignment)); + + if (!reserveRange.isValid() || reserveRange.size() < alignment) { + return; + } + + if (force) { + rx::mem::release(reserveRange, kPageSize); + } + + if (auto reserveResult = rx::mem::reserve(reserveRange); + reserveResult != std::errc{} && !force) { + rx::die("failed to reserve memory {:x}-{:x}", reserveRange.beginAddress(), + reserveRange.endAddress()); + } + }; + + for (auto usedRange : rx::mem::query(range)) { + reserveRangeImpl( + rx::AddressRange::fromBeginEnd(address, usedRange.beginAddress())); + + address = usedRange.endAddress(); + } + + reserveRangeImpl(rx::AddressRange::fromBeginEnd(address, range.endAddress())); +} + +void orbis::vmem::fork(Process *process, Process *parentThread) { + // FIXME: implement +} + +std::pair orbis::vmem::map( + Process *process, std::uint64_t addressHint, std::uint64_t size, + rx::EnumBitSet allocFlags, rx::EnumBitSet prot, + rx::EnumBitSet blockFlags, std::uint64_t alignment, + std::string_view name, IoDevice *device, std::int64_t deviceOffset) { + auto vmem = process->get(g_vmInstance); + + std::lock_guard lock(*vmem); + + VirtualMemoryAllocation allocationInfo; + allocationInfo.flags = blockFlags | orbis::vmem::BlockFlags::Allocated; + allocationInfo.device = device; + allocationInfo.prot = prot; + allocationInfo.deviceOffset = deviceOffset; + allocationInfo.name = name; + + if (device == nullptr) { + if (deviceOffset != 0 || prot) { + return {{}, ErrorCode::INVAL}; + } + + auto [_, errc, range] = + vmem->map(addressHint, size, allocationInfo, allocFlags, alignment); + + if (errc != std::errc{}) { + return {{}, toErrorCode(errc)}; + } + + return {range, {}}; + } + + auto [_, errc, range] = + vmem->map(addressHint, size, allocationInfo, + allocFlags | AllocationFlags::Dry, alignment); + + if (errc != std::errc{}) { + if (errc == std::errc::not_enough_memory) { + // virtual memory shouldn't care about physical memory + return {{}, orbis::ErrorCode::INVAL}; + } + + return {{}, toErrorCode(errc)}; + } + + rx::EnumBitSet deviceProtection = {}; + + if (prot & Protection::CpuRead) { + deviceProtection |= rx::mem::Protection::R; + } + + if (prot & Protection::CpuWrite) { + deviceProtection |= rx::mem::Protection::W; + } + + if (prot & Protection::CpuExec) { + deviceProtection |= rx::mem::Protection::X; + } + + if (auto error = device->map(range, deviceOffset, deviceProtection, process); + error != ErrorCode{}) { + return {{}, error}; + } + + auto [it, _errc, _range] = + vmem->map(range.beginAddress(), range.size(), allocationInfo, + AllocationFlags::Fixed | AllocationFlags::NoMerge, alignment); + + if (name.empty()) { + it->name = rx::format("anon:{:012x}", it.beginAddress()); + } + + return {range, {}}; +} + +orbis::ErrorCode orbis::vmem::setName(Process *process, rx::AddressRange range, + std::string_view name) { + auto vmem = process->get(g_vmInstance); + + std::lock_guard lock(*vmem); + + auto it = vmem->query(range.beginAddress()); + + if (it == vmem->end()) { + rx::println(stderr, + "vmem: attempt to set name of invalid address range: " + "{:x}-{:x}, name: {}", + range.beginAddress(), range.endAddress(), name); + return orbis::ErrorCode::INVAL; + } + + if (!it->isAllocated()) { + rx::println(stderr, + "vmem: attempt to set name of unallocated range: request " + "{:x}-{:x}, node: {:x}-{:x}, name {}", + range.beginAddress(), range.endAddress(), it.beginAddress(), + it.endAddress(), name); + return orbis::ErrorCode::INVAL; + } + + if (it.range() != range) { + rx::println(stderr, + "vmem: set name range mismatch " + "{:x}-{:x}, node: {:x}-{:x}, name {}", + range.beginAddress(), range.endAddress(), it.beginAddress(), + it.endAddress(), name); + } + + it->name = name; + return {}; +} + +orbis::ErrorCode orbis::vmem::unmap(Process *process, rx::AddressRange range) { + auto vmem = process->get(g_vmInstance); + + std::lock_guard lock(*vmem); + VirtualMemoryAllocation allocationInfo{}; + auto [it, errc, _] = + vmem->map(range.beginAddress(), range.endAddress(), allocationInfo, + AllocationFlags::Fixed, kPageSize); + + rx::mem::release(range, kPageSize); + return toErrorCode(errc); +} + +std::optional +orbis::vmem::query(Process *process, std::uint64_t address) { + auto vmem = process->get(g_vmInstance); + + std::lock_guard lock(*vmem); + + auto it = vmem->query(address); + if (it == vmem->end()) { + return {}; + } + + orbis::vmem::QueryResult result{}; + result.start = it.beginAddress(); + result.end = it.endAddress(); + + if (it->isAllocated()) { + if (it->flags == BlockFlags::DirectMemory) { + result.offset = it->deviceOffset; + } + + result.protection = it->prot.toUnderlying(); + result.flags = it->flags.toUnderlying(); + result.name = it->name; + } + + return result; +} + +std::optional +orbis::vmem::queryProtection(Process *process, std::uint64_t address) { + auto vmem = process->get(g_vmInstance); + + std::lock_guard lock(*vmem); + + auto it = vmem->query(address); + if (it == vmem->end()) { + return {}; + } + + orbis::vmem::MemoryProtection result{}; + result.startAddress = it.beginAddress(); + result.endAddress = it.endAddress(); + result.prot = it->prot.toUnderlying(); + return result; +} diff --git a/rpcsx/orbis-kernel-config/orbis-config.hpp b/rpcsx/orbis-kernel-config/orbis-config.hpp index cdc8c99c3..939c75d16 100644 --- a/rpcsx/orbis-kernel-config/orbis-config.hpp +++ b/rpcsx/orbis-kernel-config/orbis-config.hpp @@ -6,9 +6,12 @@ #include #include #include -#include #include +#ifdef __linux +#include +#endif + namespace orbis { using int8_t = std::int8_t; using int16_t = std::int16_t; @@ -110,6 +113,7 @@ template } inline uint64_t readRegister(void *context, RegisterId id) { +#ifdef __linux auto c = &reinterpret_cast(context)->uc_mcontext; switch (id) { case RegisterId::r15: @@ -152,9 +156,13 @@ inline uint64_t readRegister(void *context, RegisterId id) { std::fprintf(stderr, "***ERROR*** Unhandled RegisterId %d\n", static_cast(id)); std::abort(); +#else + return 0; +#endif } inline void writeRegister(void *context, RegisterId id, uint64_t value) { +#ifdef __linux auto c = &reinterpret_cast(context)->uc_mcontext; switch (id) { case RegisterId::r15: @@ -212,6 +220,7 @@ inline void writeRegister(void *context, RegisterId id, uint64_t value) { c->gregs[REG_RIP] = value; return; } +#endif } } // namespace orbis diff --git a/rx/include/rx/MemoryTable.hpp b/rx/include/rx/MemoryTable.hpp index 17ec5af84..7fefbf137 100644 --- a/rx/include/rx/MemoryTable.hpp +++ b/rx/include/rx/MemoryTable.hpp @@ -505,6 +505,16 @@ public: return *this; } + iterator &operator--() { + --it; + + if (it->second.isClose()) { + --it; + } + + return *this; + } + bool operator==(iterator other) const { return it == other.it; } bool operator!=(iterator other) const { return it != other.it; } diff --git a/rx/include/rx/Serializer.hpp b/rx/include/rx/Serializer.hpp index 7e8ccec09..b9742162c 100644 --- a/rx/include/rx/Serializer.hpp +++ b/rx/include/rx/Serializer.hpp @@ -113,7 +113,8 @@ namespace detail { template struct SerializableFieldTest { template requires(std::is_default_constructible_v && - !std::is_same_v && detail::IsSerializable) + !std::is_same_v, ClassT> && + detail::IsSerializable) constexpr operator FieldT(); }; @@ -121,20 +122,6 @@ struct SerializableAnyFieldTest { template constexpr operator FieldT(); }; -template constexpr bool isSerializableField() { - auto impl = []( - std::index_sequence, - std::index_sequence) { - return requires { - T{(Before, SerializableAnyFieldTest{})..., SerializableFieldTest{}, - (After, SerializableAnyFieldTest{})...}; - }; - }; - - return impl(std::make_index_sequence{}, - std::make_index_sequence - I - 1>{}); -} - template constexpr bool isSerializableFields() { auto impl = [](std::index_sequence) { return requires { T{(I, SerializableFieldTest{})...}; };