orbis: fix fork, fixed base elf support & dmem::release

This commit is contained in:
DH 2026-01-05 19:42:45 +03:00
parent e661de9ad7
commit d8b7826c9c
6 changed files with 214 additions and 30 deletions

View file

@ -167,7 +167,8 @@ inline bool validateProtection(rx::EnumBitSet<Protection> &prot) {
inline bool validateMemoryType(MemoryType type,
rx::EnumBitSet<Protection> prot) {
if (type == MemoryType::WbGarlic) {
if (prot & (Protection::CpuWrite | Protection::GpuWrite)) {
if ((prot & (Protection::CpuWrite | Protection::GpuWrite)) ==
(Protection::CpuWrite | Protection::GpuWrite)) {
return false;
}
}

View file

@ -68,7 +68,7 @@ struct DirectMemoryAllocation {
[[nodiscard]] bool isAllocated() const { return (type & kAllocatedBit) != 0; }
[[nodiscard]] bool isRelated(const DirectMemoryAllocation &other,
rx::AddressRange, rx::AddressRange) const {
return type == other.type;
return false;
}
[[nodiscard]] DirectMemoryAllocation
@ -249,11 +249,20 @@ static void dmemDump(unsigned dmemIndex, std::string_view message = {}) {
rx::ScopedFileLock lock(stderr);
rx::println(stderr, "dmem0 {}", message);
rx::println(stderr, "dmem{} {}", dmemIndex, message);
for (auto alloc : *dmem) {
rx::println(stderr, " {:012x}-{:012x}: {}{} {}", alloc.beginAddress(),
alloc.endAddress(), "_A"[alloc->isAllocated()],
"_P"[alloc->isPooled()], alloc->getMemoryType());
for (auto &map : alloc->mappings) {
auto dmemRange =
rx::AddressRange::fromBeginSize(map.dmemOffset, map.vmRange.size());
rx::println(stderr, " {:#x}-{:#x} -> {:#x}-{:#x} pid {}",
dmemRange.beginAddress(), dmemRange.endAddress(),
map.vmRange.beginAddress(), map.vmRange.endAddress(),
map.process->pid);
}
}
}
@ -278,7 +287,8 @@ orbis::dmem::allocate(unsigned dmemIndex, rx::AddressRange searchRange,
alignment = alignment == 0 ? kPageSize : alignment;
len = rx::alignUp(len, dmem::kPageSize);
if (searchRange.endAddress() == 0) {
if (searchRange.endAddress() == 0 ||
searchRange.endAddress() > dmem->dmemTotalSize - dmem->dmemReservedSize) {
searchRange = rx::AddressRange::fromBeginEnd(searchRange.beginAddress(),
dmem->dmemTotalSize -
dmem->dmemReservedSize);
@ -732,6 +742,10 @@ orbis::ErrorCode orbis::dmem::map(orbis::Process *process, unsigned dmemIndex,
}
if (offset + range.size() > dmem->dmemTotalSize - dmem->dmemReservedSize) {
dmemDump(dmemIndex,
rx::format("map out of memory {:x}-{:x}, dmem size {:x}",
range.beginAddress(), range.endAddress(),
dmem->dmemTotalSize - dmem->dmemReservedSize));
return orbis::ErrorCode::ACCES;
}
@ -746,10 +760,17 @@ orbis::ErrorCode orbis::dmem::map(orbis::Process *process, unsigned dmemIndex,
auto endIt = beginIt;
while (endIt != dmem->end() && endIt.beginAddress() < offset + range.size()) {
if (!endIt->isAllocated() || endIt->isPooled()) {
dmemDump(dmemIndex,
rx::format("attempt to map unallocated or pulled memory "
"{:x}-{:x}, dmem size {:x}",
range.beginAddress(), range.endAddress(),
dmem->dmemTotalSize - dmem->dmemReservedSize));
return orbis::ErrorCode::ACCES;
}
if (!vmem::validateMemoryType(endIt->getMemoryType(), protection)) {
dmemDump(dmemIndex, rx::format("invalid protection {} for memory type {}",
protection, endIt->getMemoryType()));
return ErrorCode::ACCES;
}
@ -761,20 +782,33 @@ orbis::ErrorCode orbis::dmem::map(orbis::Process *process, unsigned dmemIndex,
}
if (auto last = endIt; (--last).endAddress() < offset + range.size()) {
dmemDump(dmemIndex,
rx::format("map out of memory {:x}-{:x}, dmem size {:x}",
range.beginAddress(), range.endAddress(),
dmem->dmemTotalSize - dmem->dmemReservedSize));
return orbis::ErrorCode::ACCES;
}
auto dmemRange = rx::AddressRange::fromBeginSize(offset, range.size());
for (auto it = beginIt; it != endIt; ++it) {
auto itRange = it.range();
auto mappingRange = range.intersection(itRange);
auto mappingDmemRange = dmemRange.intersection(itRange);
auto mappingVmemRange = rx::AddressRange::fromBeginSize(
range.beginAddress() + (mappingDmemRange.beginAddress() - offset),
mappingDmemRange.size());
it->mappings.push_back({
.process = process,
.vmRange = mappingRange,
.dmemOffset =
offset + (mappingRange.beginAddress() - itRange.beginAddress()),
.vmRange = mappingVmemRange,
.dmemOffset = mappingDmemRange.beginAddress(),
});
}
dmemDump(dmemIndex,
rx::format("map {:#x}-{:#x} -> {:#x}-{:#x} pid {}",
dmemRange.beginAddress(), dmemRange.endAddress(),
range.beginAddress(), range.endAddress(), process->pid));
auto physicalRange =
rx::AddressRange::fromBeginSize(dmem->pmemOffset + offset, range.size());
@ -805,6 +839,9 @@ orbis::ErrorCode orbis::dmem::notifyUnmap(orbis::Process *process,
++it;
}
dmemDump(dmemIndex, rx::format("unmap {:#x}-{:#x}", range.beginAddress(),
range.endAddress()));
return {};
}

View file

@ -118,6 +118,13 @@ struct VirtualMemoryAllocation {
orbis::vmem::BlockFlags::FlexibleMemory;
}
[[nodiscard]] bool isCommited() const {
return (flags & orbis::vmem::BlockFlags::Commited) ==
orbis::vmem::BlockFlags::Commited;
}
[[nodiscard]] bool isFlexCommited() const { return isFlex() && isCommited(); }
[[nodiscard]] bool isDirect() const {
return (flags & orbis::vmem::BlockFlags::DirectMemory) ==
orbis::vmem::BlockFlags::DirectMemory;
@ -400,7 +407,43 @@ void orbis::vmem::initialize(Process *process, bool force) {
}
void orbis::vmem::fork(Process *process, Process *parentThread) {
// FIXME: implement
auto vmem = process->get(g_vmInstance);
auto parentVmem = parentThread->get(g_vmInstance);
std::lock_guard lock(*parentVmem);
std::vector<std::byte> tmpData;
for (auto alloc : parentVmem->allocations) {
if (alloc->isAllocated() && alloc->isFlexCommited()) {
auto clonedAllocation = alloc.get();
auto [flexRange, errc] = fmem::allocate(alloc.size());
rx::dieIf(errc != ErrorCode{}, "fork: fmem allocation failed: {}", errc);
clonedAllocation.deviceOffset = flexRange.beginAddress();
auto cpuProt = toCpuProtection(alloc->prot);
tmpData.resize(alloc.size());
std::memcpy(tmpData.data(), std::bit_cast<void *>(alloc.beginAddress()),
alloc.size());
pmem::map(alloc.beginAddress(), flexRange, cpuProt);
vmem->allocations.map(alloc, clonedAllocation);
if (!(cpuProt & rx::mem::Protection::W)) {
rx::mem::protect(alloc, cpuProt | rx::mem::Protection::W);
}
std::memcpy(std::bit_cast<void *>(alloc.beginAddress()), tmpData.data(),
tmpData.size());
if (!(cpuProt & rx::mem::Protection::W)) {
rx::mem::protect(alloc, cpuProt);
}
} else {
vmem->allocations.map(alloc, alloc.get());
}
}
}
std::pair<rx::AddressRange, orbis::ErrorCode> orbis::vmem::reserve(
@ -826,6 +869,18 @@ std::pair<rx::AddressRange, orbis::ErrorCode> orbis::vmem::mapFlex(
alignment);
if (prot) {
auto cpuProt = toCpuProtection(prot);
if (!(cpuProt & rx::mem::Protection::W)) {
rx::mem::protect(vmemRange, cpuProt | rx::mem::Protection::W);
}
std::memset(std::bit_cast<void *>(vmemRange.beginAddress()), 0,
vmemRange.size());
if (!(cpuProt & rx::mem::Protection::W)) {
rx::mem::protect(vmemRange, cpuProt);
}
amdgpu::mapMemory(process->pid, vmemRange, MemoryType::WbOnion, prot,
allocationInfo.deviceOffset);
}
@ -1143,11 +1198,24 @@ orbis::ErrorCode orbis::vmem::protect(Process *process, rx::AddressRange range,
rx::dieIf(errc != ErrorCode{},
"failed to allocate flexible memory");
errc = pmem::map(range.beginAddress(), pmemRange,
toCpuProtection(blockProt));
alloc.deviceOffset = pmemRange.beginAddress();
auto cpuProt = toCpuProtection(blockProt);
errc = pmem::map(range.beginAddress(), pmemRange, cpuProt);
rx::dieIf(errc != ErrorCode{}, "failed to map flexible memory");
if (!(cpuProt & rx::mem::Protection::W)) {
rx::mem::protect(range, cpuProt | rx::mem::Protection::W);
}
std::memset(std::bit_cast<void *>(range.beginAddress()), 0,
range.size());
if (!(cpuProt & rx::mem::Protection::W)) {
rx::mem::protect(range, cpuProt);
}
amdgpu::mapMemory(process->pid, range, MemoryType::WbOnion,
prot, pmemRange.beginAddress());
} else if (!blockProt && alloc.prot) {

View file

@ -3,7 +3,6 @@
#include "orbis/IoDevice.hpp"
#include "orbis/KernelAllocator.hpp"
#include "orbis/KernelObject.hpp"
#include "orbis/fmem.hpp"
#include "orbis/module/Module.hpp"
#include "orbis/pmem.hpp"
#include "orbis/stat.hpp"
@ -12,7 +11,6 @@
#include "rx/AddressRange.hpp"
#include "rx/SharedMutex.hpp"
#include "rx/StrUtil.hpp"
#include "rx/debug.hpp"
#include "rx/die.hpp"
#include "rx/format.hpp"
#include "rx/mem.hpp"
@ -519,7 +517,7 @@ static auto g_elfDevice = orbis::createGlobalObject<ElfDevice>();
static rx::Ref<orbis::Module> loadModule(ElfFile *elf, orbis::Process *process,
std::string_view name) {
rx::Ref<orbis::Module> result{orbis::knew<orbis::Module>()};
rx::Ref result{orbis::knew<orbis::Module>()};
Elf64_Ehdr header;
std::memcpy(&header, elf->image.data(), sizeof(Elf64_Ehdr));
@ -1079,10 +1077,10 @@ static rx::Ref<orbis::Module> loadModule(ElfFile *elf, orbis::Process *process,
phdr.p_type == kElfProgramTypeGnuRelRo) {
// map anonymous memory, copy segment data
auto [vmem, vmemErrc] =
orbis::vmem::mapFlex(process, segmentRange.size(), protFlags,
imageRange.beginAddress() + segmentBegin,
orbis::AllocationFlags::Fixed, {}, mapName);
auto [vmem, vmemErrc] = orbis::vmem::mapFlex(
process, segmentRange.size(), protFlags,
imageRange.beginAddress() + segmentBegin - baseAddress,
orbis::AllocationFlags::Fixed, {}, mapName);
rx::dieIf(vmemErrc != orbis::ErrorCode{},
"elf: failed to map flexible to virtual memory {}",
@ -1093,8 +1091,8 @@ static rx::Ref<orbis::Module> loadModule(ElfFile *elf, orbis::Process *process,
rx::mem::Protection::R | rx::mem::Protection::W);
}
std::memcpy(imageBase + phdr.p_vaddr, elf->image.data() + phdr.p_offset,
phdr.p_filesz);
std::memcpy(imageBase + phdr.p_vaddr - baseAddress,
elf->image.data() + phdr.p_offset, phdr.p_filesz);
rx::println(stderr, "{}: RW segment {:x}-{:x}, {}", result->moduleName,
segmentRange.beginAddress(), segmentRange.endAddress(),
@ -1114,28 +1112,29 @@ static rx::Ref<orbis::Module> loadModule(ElfFile *elf, orbis::Process *process,
std::lock_guard lock(elf->mtx);
if (!elf->initialized) {
auto [vmem, vmemErrc] = orbis::vmem::mapFile(
process, imageRange.beginAddress() + segmentBegin,
process, imageRange.beginAddress() + segmentBegin - baseAddress,
segmentRange.size(), orbis::AllocationFlags::Fixed,
protFlags | orbis::vmem::Protection::CpuWrite, {}, {}, elf,
segmentBegin, mapName);
segmentBegin - baseAddress, mapName);
rx::dieIf(vmemErrc != orbis::ErrorCode{},
"elf: failed to map elf to virtual memory {}", vmemErrc);
std::memset(imageBase + phdr.p_vaddr + phdr.p_filesz, 0,
phdr.p_memsz - phdr.p_filesz);
std::memcpy(imageBase + phdr.p_vaddr,
std::memset(imageBase + phdr.p_vaddr + phdr.p_filesz - baseAddress,
0, phdr.p_memsz - phdr.p_filesz);
std::memcpy(imageBase + phdr.p_vaddr - baseAddress,
elf->image.data() + phdr.p_offset, phdr.p_filesz);
elf->initialized = true;
}
}
auto [vmem, vmemErrc] = orbis::vmem::mapFile(
process, imageRange.beginAddress() + segmentBegin,
process, imageRange.beginAddress() + segmentBegin - baseAddress,
segmentRange.size(), orbis::AllocationFlags::Fixed, protFlags,
orbis::vmem::BlockFlags::FlexibleMemory |
orbis::vmem::BlockFlags::Commited,
orbis::vmem::BlockFlagsEx::Shared, elf, segmentBegin, mapName);
orbis::vmem::BlockFlagsEx::Shared, elf, segmentBegin - baseAddress,
mapName);
rx::dieIf(vmemErrc != orbis::ErrorCode{},
"elf: failed to map elf to virtual memory {}", (int)vmemErrc);
@ -1153,7 +1152,7 @@ static rx::Ref<orbis::Module> loadModule(ElfFile *elf, orbis::Process *process,
rx::println(stderr, "elf: corrupted, segment {} overriding. {}",
segmentIndex, name);
} else {
segment.addr = imageBase + segmentBegin;
segment.addr = imageBase + segmentBegin - baseAddress;
segment.size = phdr.p_memsz;
segment.prot = protFlags.toUnderlying();
result->segmentCount = std::max(segmentIndex + 1, result->segmentCount);

View file

@ -1212,14 +1212,89 @@ int main(int argc, const char *argv[]) {
},
};
orbis::BudgetInfo systemBudgetInfo[]{
{
.resourceId = orbis::BudgetResource::Dmem,
.flags = 0,
.item =
{
.total = 0x1'8000'0000,
},
},
{
.resourceId = orbis::BudgetResource::Vmem,
.flags = 0,
.item =
{
.total = 2ul * 1024 * 1024 * 1024,
},
},
{
.resourceId = orbis::BudgetResource::Fmem,
.flags = 0,
.item =
{
.total = 2ul * 1024 * 1024 * 1024,
},
},
{
.resourceId = orbis::BudgetResource::CpuSet,
.flags = 0,
.item =
{
.total = 8,
},
},
{
.resourceId = orbis::BudgetResource::File,
.flags = 0,
.item =
{
.total = 4096,
},
},
{
.resourceId = orbis::BudgetResource::Socket,
.flags = 0,
.item =
{
.total = 4096,
},
},
{
.resourceId = orbis::BudgetResource::Equeue,
.flags = 0,
.item =
{
.total = 4096,
},
},
{
.resourceId = orbis::BudgetResource::Pipe,
.flags = 0,
.item =
{
.total = 4096,
},
},
{
.resourceId = orbis::BudgetResource::Device,
.flags = 0,
.item =
{
.total = 4096,
},
},
};
auto bigAppBudget = orbis::g_context->createProcessTypeBudget(
orbis::Budget::ProcessType::BigApp, "big app budget", bigAppBudgetInfo);
// FIXME: define following budgets
orbis::g_context->createProcessTypeBudget(
orbis::Budget::ProcessType::MiniApp, "mini-app budget", bigAppBudgetInfo);
orbis::g_context->createProcessTypeBudget(orbis::Budget::ProcessType::System,
"system budget", bigAppBudgetInfo);
auto systemBudget = orbis::g_context->createProcessTypeBudget(
orbis::Budget::ProcessType::System, "system budget", systemBudgetInfo);
orbis::g_context->createProcessTypeBudget(
orbis::Budget::ProcessType::NonGameMiniApp, "non-game mini-app budget",
bigAppBudgetInfo);
@ -1251,6 +1326,7 @@ int main(int argc, const char *argv[]) {
-1ul,
}};
initProcess->budgetProcessType = orbis::Budget::ProcessType::System;
initProcess->budgetId = orbis::g_context->budgets.insert(systemBudget);
initProcess->isInSandbox = false;
} else {
initProcess->authInfo = {

View file

@ -647,6 +647,9 @@ SysResult fork(Thread *thread, slong flags) {
process->authInfo = thread->tproc->authInfo;
process->sdkVersion = thread->tproc->sdkVersion;
process->type = thread->tproc->type;
process->budgetProcessType = thread->tproc->budgetProcessType;
process->budgetId = thread->tproc->budgetId;
for (auto [id, mod] : thread->tproc->modulesMap) {
if (!process->modulesMap.insert(id, mod)) {
std::abort();