orbis: initial physical memory emulation support (not used)

avoid unconditional linux specific types/api usage
This commit is contained in:
DH 2025-11-09 21:38:23 +03:00
parent 07b1f422ef
commit 9fc036d9a5
23 changed files with 831 additions and 67 deletions

View file

@ -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 <typename T>
@ -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<AllocationFlags> flags,
std::uint64_t alignment) {
AllocationT &allocationInfo,
rx::EnumBitSet<AllocationFlags> 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());

View file

@ -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

View file

@ -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 <bit>
#include <type_traits>
@ -27,7 +30,7 @@ enum OpenFlags {
struct File;
struct Thread;
struct Process;
struct IoDevice : rx::RcBase {
virtual ErrorCode open(rx::Ref<File> *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<void> argp,
Thread *thread);
virtual ErrorCode map(rx::AddressRange range, std::int64_t offset,
rx::EnumBitSet<rx::mem::Protection> protection,
Process *process) {
return ErrorCode::NOTSUP;
}
};
namespace ioctl {

View file

@ -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<ErrorCode>(value());
}
explicit operator bool() const { return mValue != 0; }
[[nodiscard]] auto operator<=>(ErrorCode ec) const {
return static_cast<ErrorCode>(value()) <=> ec;
}

View file

@ -0,0 +1,16 @@
#pragma once
#include "error/ErrorCode.hpp"
#include "rx/AddressRange.hpp"
#include <cstdint>
namespace orbis {
struct Process;
}
namespace orbis::fmem {
ErrorCode initialize(Process *process, std::uint64_t size);
void destroy(Process *process);
std::pair<rx::AddressRange, ErrorCode> allocate(Process *process,
std::uint64_t size);
ErrorCode deallocate(Process *process, rx::AddressRange range);
} // namespace orbis::fmem

View file

@ -0,0 +1,38 @@
#pragma once
#include "error/ErrorCode.hpp"
#include "kernel/MemoryResource.hpp"
#include "rx/AddressRange.hpp"
#include "rx/EnumBitSet.hpp"
#include <cstdint>
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<rx::AddressRange, ErrorCode>
allocate(std::uint64_t addressHint, std::uint64_t size, MemoryType memoryType,
rx::EnumBitSet<AllocationFlags> flags, std::uint64_t alignment);
ErrorCode deallocate(rx::AddressRange range);
std::optional<AllocatedMemory> query(std::uint64_t address);
ErrorCode map(std::uint64_t virtualAddress, rx::AddressRange range,
rx::EnumBitSet<rx::mem::Protection> protection);
std::size_t getSize();
IoDevice *getDevice();
} // namespace orbis::pmem

View file

@ -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<struct rtprio> 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<struct sctp_sndrcvinfo> sinfo,
sint flags);
SysResult sys_sctp_generic_sendmsg_iov(Thread *thread, sint sd, ptr<IoVec> iov,
sint iovlen, caddr_t to,
__socklen_t tolen,
sint iovlen, caddr_t to, SockLen tolen,
ptr<struct sctp_sndrcvinfo> sinfo,
sint flags);
SysResult sys_sctp_generic_recvmsg(Thread *thread, sint sd, ptr<IoVec> iov,
sint iovlen, caddr_t from,
__socklen_t fromlen,
sint iovlen, caddr_t from, SockLen fromlen,
ptr<struct sctp_sndrcvinfo> sinfo,
sint flags);
SysResult sys_pread(Thread *thread, sint fd, ptr<void> buf, size_t nbyte,
@ -587,9 +587,9 @@ SysResult sys_cap_getmode(Thread *thread, ptr<uint> modep);
SysResult sys_pdfork(Thread *thread, ptr<sint> fdp, sint flags);
SysResult sys_pdkill(Thread *thread, sint fd, sint signum);
SysResult sys_pdgetpid(Thread *thread, sint fd, ptr<pid_t> pidp);
SysResult sys_pselect(Thread *thread, sint nd, ptr<fd_set> in, ptr<fd_set> ou,
ptr<fd_set> ex, ptr<const timespec> ts,
ptr<const sigset_t> sm);
SysResult sys_pselect(Thread *thread, sint nd, ptr<fd_set_t> in,
ptr<fd_set_t> ou, ptr<fd_set_t> ex,
ptr<const timespec> ts, ptr<const SigSet> sm);
SysResult sys_getloginclass(Thread *thread, ptr<char> namebuf, size_t namelen);
SysResult sys_setloginclass(Thread *thread, ptr<char> namebuf);
SysResult sys_rctl_get_racct(Thread *thread, ptr<const void> inbufp,

View file

@ -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 <string_view>
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<Protection> 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<rx::AddressRange, ErrorCode>
reserve(Process *process, std::uint64_t addressHint, std::uint64_t size,
rx::EnumBitSet<AllocationFlags> allocFlags);
std::pair<rx::AddressRange, ErrorCode>
map(Process *process, std::uint64_t addressHint, std::uint64_t size,
rx::EnumBitSet<AllocationFlags> allocFlags,
rx::EnumBitSet<Protection> prot = {},
rx::EnumBitSet<BlockFlags> 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<QueryResult> query(Process *process, std::uint64_t address);
std::optional<MemoryProtection> queryProtection(Process *process,
std::uint64_t address);
} // namespace vmem
} // namespace orbis

88
kernel/orbis/src/fmem.cpp Normal file
View file

@ -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 <cassert>
#include <rx/Mappable.hpp>
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<FlexibleMemoryAllocation>;
static auto g_fmemInstance = orbis::createProcessLocalObject<
kernel::LockableKernelObject<FlexibleMemoryResource>>();
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<rx::AddressRange, orbis::ErrorCode>
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);
}

View file

@ -5,7 +5,6 @@
#include "utils/Logs.hpp"
#include <chrono>
#include <span>
#include <sys/mman.h>
orbis::ErrorCode orbis::ipmiCreateClient(Process *proc, void *clientImpl,
const char *name,

127
kernel/orbis/src/pmem.cpp Normal file
View file

@ -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 <cassert>
#include <rx/Mappable.hpp>
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<decltype([](std::size_t size) {
return rx::Mappable::CreateMemory(size);
})>;
using PhysicalMemoryResource =
kernel::AllocableResource<PhysicalMemoryAllocation, MappableMemoryResource>;
static auto g_pmemInstance = orbis::createGlobalObject<
kernel::LockableKernelObject<PhysicalMemoryResource>>();
struct PhysicalMemory : orbis::IoDevice {
orbis::ErrorCode open(rx::Ref<orbis::File> *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<rx::mem::Protection> 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<PhysicalMemory>();
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<rx::AddressRange, orbis::ErrorCode> orbis::pmem::allocate(
std::uint64_t addressHint, std::uint64_t size, MemoryType memoryType,
rx::EnumBitSet<AllocationFlags> 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::AllocatedMemory>
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<rx::mem::Protection> 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(); }

View file

@ -7,7 +7,10 @@
#include <chrono>
#include <list>
#include <span>
#ifdef __linux
#include <sys/select.h>
#endif
orbis::SysResult orbis::sys_kqueue(Thread *thread) {
auto queue = knew<KQueue>();
@ -38,17 +41,22 @@ orbis::SysResult orbis::sys_kqueueex(Thread *thread, ptr<char> 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 {

View file

@ -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<fd_set> in,
ptr<fd_set> ou, ptr<fd_set> ex,
orbis::SysResult orbis::sys_pselect(Thread *thread, sint nd, ptr<fd_set_t> in,
ptr<fd_set_t> ou, ptr<fd_set_t> ex,
ptr<const timespec> ts,
ptr<const sigset_t> sm) {
ptr<const SigSet> sm) {
return ErrorCode::NOSYS;
}
orbis::SysResult orbis::sys_select(Thread *thread, sint nd,

View file

@ -2,7 +2,10 @@
#include "thread/Process.hpp"
#include "thread/Thread.hpp"
#include "utils/Logs.hpp"
#ifdef __linux
#include <sched.h>
#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 {};
}

View file

@ -16,8 +16,6 @@
#include "utils/Logs.hpp"
#include <fcntl.h>
#include <ranges>
#include <sys/mman.h>
#include <sys/stat.h>
#include <utility>
struct orbis::AppMountInfo {

View file

@ -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<ErrorCode>(errno);
}
#else
#warning "Not implemented"
#endif
return {};
}

View file

@ -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<orbis::timeval> tp,
ptr<orbis::timezone> 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<struct timeval> tp,

View file

@ -5,7 +5,6 @@
#include "thread/Thread.hpp"
#include "uio.hpp"
#include "utils/Logs.hpp"
#include <sys/socket.h>
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<struct sctp_sndrcvinfo> sinfo, sint flags) {
return ErrorCode::NOSYS;
}
orbis::SysResult orbis::sys_sctp_generic_sendmsg_iov(
Thread *thread, sint sd, ptr<IoVec> iov, sint iovlen, caddr_t to,
__socklen_t tolen, ptr<struct sctp_sndrcvinfo> sinfo, sint flags) {
SockLen tolen, ptr<struct sctp_sndrcvinfo> sinfo, sint flags) {
return ErrorCode::NOSYS;
}
orbis::SysResult
orbis::sys_sctp_generic_recvmsg(Thread *thread, sint sd, ptr<IoVec> iov,
sint iovlen, caddr_t from, __socklen_t fromlen,
sint iovlen, caddr_t from, SockLen fromlen,
ptr<struct sctp_sndrcvinfo> sinfo, sint flags) {
return ErrorCode::NOSYS;
}

View file

@ -117,17 +117,17 @@ void log_class_string<short>::format(std::string &out, const void *arg) {
}
template <>
void log_class_string<ushort>::format(std::string &out, const void *arg) {
void log_class_string<unsigned short>::format(std::string &out, const void *arg) {
append_hex(out, get_object(arg));
}
template <>
void log_class_string<int>::format(std::string &out, const void *arg) {
append_hex(out, static_cast<uint>(get_object(arg)));
append_hex(out, static_cast<unsigned int>(get_object(arg)));
}
template <>
void log_class_string<uint>::format(std::string &out, const void *arg) {
void log_class_string<unsigned int>::format(std::string &out, const void *arg) {
append_hex(out, get_object(arg));
}
@ -137,7 +137,7 @@ void log_class_string<long>::format(std::string &out, const void *arg) {
}
template <>
void log_class_string<ulong>::format(std::string &out, const void *arg) {
void log_class_string<unsigned long>::format(std::string &out, const void *arg) {
append_hex(out, get_object(arg));
}

296
kernel/orbis/src/vmem.cpp Normal file
View file

@ -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 <algorithm>
#include <cstdio>
#include <mutex>
struct VirtualMemoryAllocation {
rx::EnumBitSet<orbis::vmem::BlockFlags> flags{};
rx::EnumBitSet<orbis::vmem::Protection> prot{};
rx::Ref<orbis::IoDevice> 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<rx::AddressRange, VirtualMemoryAllocation>
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<decltype([](std::size_t size) {
return rx::Mappable::CreateMemory(size);
})>;
struct VirtualMemoryResource
: kernel::AllocableResource<VirtualMemoryAllocation> {};
static auto g_vmInstance = orbis::createProcessLocalObject<
kernel::LockableKernelObject<VirtualMemoryResource>>();
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<std::size_t>(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<rx::AddressRange, orbis::ErrorCode> orbis::vmem::map(
Process *process, std::uint64_t addressHint, std::uint64_t size,
rx::EnumBitSet<AllocationFlags> allocFlags, rx::EnumBitSet<Protection> prot,
rx::EnumBitSet<BlockFlags> 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<rx::mem::Protection> 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::QueryResult>
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::MemoryProtection>
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;
}

View file

@ -6,9 +6,12 @@
#include <cstdio>
#include <cstring>
#include <immintrin.h>
#include <sys/ucontext.h>
#include <type_traits>
#ifdef __linux
#include <sys/ucontext.h>
#endif
namespace orbis {
using int8_t = std::int8_t;
using int16_t = std::int16_t;
@ -110,6 +113,7 @@ template <typename T>
}
inline uint64_t readRegister(void *context, RegisterId id) {
#ifdef __linux
auto c = &reinterpret_cast<ucontext_t *>(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<int>(id));
std::abort();
#else
return 0;
#endif
}
inline void writeRegister(void *context, RegisterId id, uint64_t value) {
#ifdef __linux
auto c = &reinterpret_cast<ucontext_t *>(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

View file

@ -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; }

View file

@ -113,7 +113,8 @@ namespace detail {
template <typename ClassT> struct SerializableFieldTest {
template <typename FieldT>
requires(std::is_default_constructible_v<FieldT> &&
!std::is_same_v<FieldT, ClassT> && detail::IsSerializable<FieldT>)
!std::is_same_v<std::remove_cvref_t<FieldT>, ClassT> &&
detail::IsSerializable<FieldT>)
constexpr operator FieldT();
};
@ -121,20 +122,6 @@ struct SerializableAnyFieldTest {
template <typename FieldT> constexpr operator FieldT();
};
template <typename T, std::size_t I> constexpr bool isSerializableField() {
auto impl = []<std::size_t... Before, std::size_t... After>(
std::index_sequence<Before...>,
std::index_sequence<After...>) {
return requires {
T{(Before, SerializableAnyFieldTest{})..., SerializableFieldTest<T>{},
(After, SerializableAnyFieldTest{})...};
};
};
return impl(std::make_index_sequence<I>{},
std::make_index_sequence<rx::fieldCount<T> - I - 1>{});
}
template <typename T> constexpr bool isSerializableFields() {
auto impl = []<std::size_t... I>(std::index_sequence<I...>) {
return requires { T{(I, SerializableFieldTest<T>{})...}; };