diff --git a/app/src/main/cpp/skyline/kernel/memory.cpp b/app/src/main/cpp/skyline/kernel/memory.cpp index 99c4ad1e..115cf8cf 100644 --- a/app/src/main/cpp/skyline/kernel/memory.cpp +++ b/app/src/main/cpp/skyline/kernel/memory.cpp @@ -6,25 +6,30 @@ namespace skyline::kernel { auto chunk = std::upper_bound(chunkList.begin(), chunkList.end(), address, [](const u64 address, const ChunkDescriptor &chunk) -> bool { return address < chunk.address; }); + if (chunk-- != chunkList.begin()) { if ((chunk->address + chunk->size) > address) return chunk.base(); } + return nullptr; } BlockDescriptor *MemoryManager::GetBlock(u64 address, ChunkDescriptor *chunk) { if (!chunk) chunk = GetChunk(address); + if (chunk) { auto block = std::upper_bound(chunk->blockList.begin(), chunk->blockList.end(), address, [](const u64 address, const BlockDescriptor &block) -> bool { return address < block.address; }); + if (block-- != chunk->blockList.begin()) { if ((block->address + block->size) > address) return block.base(); } } + return nullptr; } @@ -32,11 +37,14 @@ namespace skyline::kernel { auto upperChunk = std::upper_bound(chunkList.begin(), chunkList.end(), chunk.address, [](const u64 address, const ChunkDescriptor &chunk) -> bool { return address < chunk.address; }); + if (upperChunk != chunkList.begin()) { auto lowerChunk = std::prev(upperChunk); + if (lowerChunk->address + lowerChunk->size > chunk.address) throw exception("InsertChunk: Descriptors are colliding: 0x{:X} - 0x{:X} and 0x{:X} - 0x{:X}", lowerChunk->address, lowerChunk->address + lowerChunk->size, chunk.address, chunk.address + chunk.size); } + chunkList.insert(upperChunk, chunk); } @@ -55,24 +63,29 @@ namespace skyline::kernel { } else if (size > chunk->size) { auto begin = chunk->blockList.begin(); auto end = std::prev(chunk->blockList.end()); + BlockDescriptor block{ .address = (end->address + end->size), .size = (chunk->address + size) - (end->address + end->size), .permission = begin->permission, .attributes = begin->attributes, }; + chunk->blockList.push_back(block); } else if (size < chunk->size) { auto endAddress = chunk->address + size; + for (auto block = chunk->blockList.begin(), end = chunk->blockList.end(); block != end;) { if (block->address > endAddress) block = chunk->blockList.erase(block); else ++block; } + auto end = std::prev(chunk->blockList.end()); end->size = endAddress - end->address; } + chunk->size = size; } @@ -87,16 +100,19 @@ namespace skyline::kernel { auto endBlock = *iter; endBlock.address = (block.address + block.size); endBlock.size = (iter->address + iter->size) - endBlock.address; + iter->size = iter->address - block.address; chunk->blockList.insert(std::next(iter), {block, endBlock}); } } else if (std::next(iter) != chunk->blockList.end()) { auto nextIter = std::next(iter); auto nextEnd = nextIter->address + nextIter->size; + if(nextEnd > block.address) { iter->size = block.address - iter->address; nextIter->address = block.address + block.size; nextIter->size = nextEnd - nextIter->address; + chunk->blockList.insert(nextIter, block); } else { throw exception("InsertBlock: Inserting block across more than one block is not allowed"); @@ -107,6 +123,7 @@ namespace skyline::kernel { return; } } + throw exception("InsertBlock: Block offset not present within current block list"); } @@ -114,6 +131,7 @@ namespace skyline::kernel { switch (type) { case memory::AddressSpaceType::AddressSpace32Bit: throw exception("32-bit address spaces are not supported"); + case memory::AddressSpaceType::AddressSpace36Bit: { base.address = constant::BaseAddress; base.size = 0xFF8000000; @@ -131,6 +149,7 @@ namespace skyline::kernel { tlsIo.size = 0; break; } + case memory::AddressSpaceType::AddressSpace39Bit: { base.address = constant::BaseAddress; base.size = 0x7FF8000000; @@ -147,6 +166,7 @@ namespace skyline::kernel { break; } } + state.logger->Debug("Region Map:\nCode Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nAlias Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nHeap Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nStack Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nTLS/IO Region: 0x{:X} - 0x{:X} (Size: 0x{:X})", code.address, code.address + code.size, code.size, alias.address, alias.address + alias.size, alias.size, heap.address, heap .address + heap.size, heap.size, stack.address, stack.address + stack.size, stack.size, tlsIo.address, tlsIo.address + tlsIo.size, tlsIo.size); } @@ -155,8 +175,10 @@ namespace skyline::kernel { std::optional MemoryManager::Get(u64 address) { auto chunk = GetChunk(address); + if (chunk) return DescriptorPack{*GetBlock(address, chunk), *chunk}; + return std::nullopt; } @@ -179,8 +201,10 @@ namespace skyline::kernel { size_t MemoryManager::GetProgramSize() { size_t size = 0; + for (const auto &chunk : chunkList) size += chunk.size; + return size; } } diff --git a/app/src/main/cpp/skyline/kernel/svc.cpp b/app/src/main/cpp/skyline/kernel/svc.cpp index fbf2348d..6eb9ca59 100644 --- a/app/src/main/cpp/skyline/kernel/svc.cpp +++ b/app/src/main/cpp/skyline/kernel/svc.cpp @@ -4,7 +4,7 @@ namespace skyline::kernel::svc { void SetHeapSize(DeviceState &state) { constexpr auto heapSizeAlign = 0x200000; // The heap size has to be a multiple of this value - const u32 size = state.ctx->registers.w1; + auto size = state.ctx->registers.w1; if (size % heapSizeAlign != 0) { state.ctx->registers.w0 = constant::status::InvSize; @@ -24,26 +24,30 @@ namespace skyline::kernel::svc { } void SetMemoryAttribute(DeviceState &state) { - const u64 address = state.ctx->registers.x0; + auto address = state.ctx->registers.x0; if (!utils::PageAligned(address)) { state.ctx->registers.w0 = constant::status::InvAddress; state.logger->Warn("svcSetMemoryAttribute: 'address' not page aligned: 0x{:X}", address); return; } - const u64 size = state.ctx->registers.x1; + + auto size = state.ctx->registers.x1; if (!utils::PageAligned(size)) { state.ctx->registers.w0 = constant::status::InvSize; state.logger->Warn("svcSetMemoryAttribute: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size); return; } + memory::MemoryAttribute mask{.value = state.ctx->registers.w2}; memory::MemoryAttribute value{.value = state.ctx->registers.w3}; - u32 maskedValue = mask.value | value.value; + + auto maskedValue = mask.value | value.value; if (maskedValue != mask.value || !mask.isUncached || mask.isDeviceShared || mask.isBorrowed || mask.isIpcLocked) { state.ctx->registers.w0 = constant::status::InvCombination; state.logger->Warn("svcSetMemoryAttribute: 'mask' invalid: 0x{:X}, 0x{:X}", mask.value, value.value); return; } + auto chunk = state.os->memory.GetChunk(address); auto block = state.os->memory.GetBlock(address); if (!chunk || !block) { @@ -51,37 +55,44 @@ namespace skyline::kernel::svc { state.logger->Warn("svcSetMemoryAttribute: Cannot find memory region: 0x{:X}", address); return; } + if (!chunk->state.attributeChangeAllowed) { state.ctx->registers.w0 = constant::status::InvState; state.logger->Warn("svcSetMemoryAttribute: Attribute change not allowed for chunk: 0x{:X}", address); return; } + block->attributes.isUncached = value.isUncached; MemoryManager::InsertBlock(chunk, *block); + state.logger->Debug("svcSetMemoryAttribute: Set caching to {} at 0x{:X} for 0x{:X} bytes", !block->attributes.isUncached, address, size); state.ctx->registers.w0 = constant::status::Success; } void MapMemory(DeviceState &state) { - const u64 destination = state.ctx->registers.x0; - const u64 source = state.ctx->registers.x1; - const u64 size = state.ctx->registers.x2; + auto destination = state.ctx->registers.x0; + auto source = state.ctx->registers.x1; + auto size = state.ctx->registers.x2; + if (!utils::PageAligned(destination) || !utils::PageAligned(source)) { state.ctx->registers.w0 = constant::status::InvAddress; state.logger->Warn("svcMapMemory: Addresses not page aligned: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size); return; } + if (!utils::PageAligned(size)) { state.ctx->registers.w0 = constant::status::InvSize; state.logger->Warn("svcMapMemory: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size); return; } + auto stack = state.os->memory.GetRegion(memory::Regions::Stack); if (!stack.IsInside(destination)) { state.ctx->registers.w0 = constant::status::InvMemRange; state.logger->Warn("svcMapMemory: Destination not within stack region: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size); return; } + auto descriptor = state.os->memory.Get(source); if (!descriptor) { state.ctx->registers.w0 = constant::status::InvAddress; @@ -93,36 +104,44 @@ namespace skyline::kernel::svc { state.logger->Warn("svcMapMemory: Source doesn't allow usage of svcMapMemory: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes) 0x{:X}", source, destination, size, descriptor->chunk.state.value); return; } + state.process->NewHandle(destination, size, descriptor->block.permission, memory::states::Stack); state.process->CopyMemory(source, destination, size); + auto object = state.process->GetMemoryObject(source); if (!object) throw exception("svcMapMemory: Cannot find memory object in handle table for address 0x{:X}", source); + object->item->UpdatePermission(source, size, {false, false, false}); + state.logger->Debug("svcMapMemory: Mapped range 0x{:X} - 0x{:X} to 0x{:X} - 0x{:X} (Size: 0x{:X} bytes)", source, source + size, destination, destination + size, size); state.ctx->registers.w0 = constant::status::Success; } void UnmapMemory(DeviceState &state) { - const u64 source = state.ctx->registers.x0; - const u64 destination = state.ctx->registers.x1; - const u64 size = state.ctx->registers.x2; + auto source = state.ctx->registers.x0; + auto destination = state.ctx->registers.x1; + auto size = state.ctx->registers.x2; + if (!utils::PageAligned(destination) || !utils::PageAligned(source)) { state.ctx->registers.w0 = constant::status::InvAddress; state.logger->Warn("svcUnmapMemory: Addresses not page aligned: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size); return; } + if (!utils::PageAligned(size)) { state.ctx->registers.w0 = constant::status::InvSize; state.logger->Warn("svcUnmapMemory: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size); return; } + auto stack = state.os->memory.GetRegion(memory::Regions::Stack); if (!stack.IsInside(source)) { state.ctx->registers.w0 = constant::status::InvMemRange; state.logger->Warn("svcUnmapMemory: Source not within stack region: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size); return; } + auto sourceDesc = state.os->memory.Get(source); auto destDesc = state.os->memory.Get(destination); if (!sourceDesc || !destDesc) { @@ -130,28 +149,37 @@ namespace skyline::kernel::svc { state.logger->Warn("svcUnmapMemory: Addresses have no descriptor: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size); return; } + if (!destDesc->chunk.state.mapAllowed) { state.ctx->registers.w0 = constant::status::InvState; state.logger->Warn("svcUnmapMemory: Destination doesn't allow usage of svcMapMemory: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes) 0x{:X}", source, destination, size, destDesc->chunk.state.value); return; } + auto destObject = state.process->GetMemoryObject(destination); if (!destObject) throw exception("svcUnmapMemory: Cannot find destination memory object in handle table for address 0x{:X}", destination); + destObject->item->UpdatePermission(destination, size, sourceDesc->block.permission); + state.process->CopyMemory(destination, source, size); + auto sourceObject = state.process->GetMemoryObject(destination); if (!sourceObject) throw exception("svcUnmapMemory: Cannot find source memory object in handle table for address 0x{:X}", source); + state.process->DeleteHandle(sourceObject->handle); + state.logger->Debug("svcUnmapMemory: Unmapped range 0x{:X} - 0x{:X} to 0x{:X} - 0x{:X} (Size: 0x{:X} bytes)", source, source + size, destination, destination + size, size); state.ctx->registers.w0 = constant::status::Success; } void QueryMemory(DeviceState &state) { - u64 address = state.ctx->registers.x2; memory::MemoryInfo memInfo{}; + + auto address = state.ctx->registers.x2; auto descriptor = state.os->memory.Get(address); + if (descriptor) { memInfo = { .address = descriptor->block.address, @@ -162,18 +190,23 @@ namespace skyline::kernel::svc { .deviceRefCount = 0, .ipcRefCount = 0, }; + state.logger->Debug("svcQueryMemory: Address: 0x{:X}, Size: 0x{:X}, Type: 0x{:X}, Is Uncached: {}, Permissions: {}{}{}", memInfo.address, memInfo.size, memInfo.type, static_cast(descriptor->block.attributes.isUncached), descriptor->block.permission.r ? "R" : "-", descriptor->block.permission.w ? "W" : "-", descriptor->block.permission.x ? "X" : "-"); } else { auto region = state.os->memory.GetRegion(memory::Regions::Base); auto baseEnd = region.address + region.size; + memInfo = { .address = region.address, .size = ~baseEnd + 1, .type = static_cast(memory::MemoryType::Unmapped), }; + state.logger->Debug("svcQueryMemory: Cannot find block of address: 0x{:X}", address); } + state.process->WriteMemory(memInfo, state.ctx->registers.x0); + state.ctx->registers.w0 = constant::status::Success; } @@ -187,13 +220,16 @@ namespace skyline::kernel::svc { u64 entryArgument = state.ctx->registers.x2; u64 stackTop = state.ctx->registers.x3; u8 priority = static_cast(state.ctx->registers.w4); + if ((priority < constant::SwitchPriority.first) || (priority > constant::SwitchPriority.second)) { state.ctx->registers.w0 = constant::status::InvAddress; state.logger->Warn("svcCreateThread: 'priority' invalid: {}", priority); return; } + auto thread = state.process->CreateThread(entryAddress, entryArgument, stackTop, priority); state.logger->Debug("svcCreateThread: Created thread with handle 0x{:X} (Entry Point: 0x{:X}, Argument: 0x{:X}, Stack Pointer: 0x{:X}, Priority: {}, PID: {})", thread->handle, entryAddress, entryArgument, stackTop, priority, thread->pid); + state.ctx->registers.w1 = thread->handle; state.ctx->registers.w0 = constant::status::Success; } @@ -218,6 +254,7 @@ namespace skyline::kernel::svc { void SleepThread(DeviceState &state) { auto in = state.ctx->registers.x0; + switch (in) { case 0: case 1: @@ -239,6 +276,7 @@ namespace skyline::kernel::svc { try { auto priority = state.process->GetHandle(handle)->priority; state.logger->Debug("svcGetThreadPriority: Writing thread priority {}", priority); + state.ctx->registers.w1 = priority; state.ctx->registers.w0 = constant::status::Success; } catch (const std::exception &) { @@ -250,6 +288,7 @@ namespace skyline::kernel::svc { void SetThreadPriority(DeviceState &state) { auto handle = state.ctx->registers.w0; auto priority = state.ctx->registers.w1; + try { state.logger->Debug("svcSetThreadPriority: Setting thread priority to {}", priority); state.process->GetHandle(handle)->UpdatePriority(static_cast(priority)); @@ -264,17 +303,20 @@ namespace skyline::kernel::svc { try { auto object = state.process->GetHandle(state.ctx->registers.w0); u64 address = state.ctx->registers.x1; + if (!utils::PageAligned(address)) { state.ctx->registers.w0 = constant::status::InvAddress; state.logger->Warn("svcMapSharedMemory: 'address' not page aligned: 0x{:X}", address); return; } - const u64 size = state.ctx->registers.x2; + + auto size = state.ctx->registers.x2; if (!utils::PageAligned(size)) { state.ctx->registers.w0 = constant::status::InvSize; state.logger->Warn("svcMapSharedMemory: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size); return; } + u32 perm = state.ctx->registers.w3; memory::Permission permission = *reinterpret_cast(&perm); if ((permission.w && !permission.r) || (permission.x && !permission.r)) { @@ -282,8 +324,11 @@ namespace skyline::kernel::svc { state.ctx->registers.w0 = constant::status::InvPermission; return; } + state.logger->Debug("svcMapSharedMemory: Mapping shared memory at 0x{:X} for {} bytes ({}{}{})", address, size, permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-"); + object->Map(address, size, permission); + state.ctx->registers.w0 = constant::status::Success; } catch (const std::exception &) { state.logger->Warn("svcMapSharedMemory: 'handle' invalid: 0x{:X}", state.ctx->registers.w0); @@ -298,12 +343,14 @@ namespace skyline::kernel::svc { state.logger->Warn("svcCreateTransferMemory: 'address' not page aligned: 0x{:X}", address); return; } + u64 size = state.ctx->registers.x2; if (!utils::PageAligned(size)) { state.ctx->registers.w0 = constant::status::InvSize; state.logger->Warn("svcCreateTransferMemory: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size); return; } + u32 perm = state.ctx->registers.w3; memory::Permission permission = *reinterpret_cast(&perm); if ((permission.w && !permission.r) || (permission.x && !permission.r)) { @@ -311,8 +358,11 @@ namespace skyline::kernel::svc { state.ctx->registers.w0 = constant::status::InvPermission; return; } + state.logger->Debug("svcCreateTransferMemory: Creating transfer memory at 0x{:X} for {} bytes ({}{}{})", address, size, permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-"); + auto shmem = state.process->NewHandle(state.process->pid, address, size, permission); + state.ctx->registers.w0 = constant::status::Success; state.ctx->registers.w1 = shmem.handle; } @@ -337,15 +387,18 @@ namespace skyline::kernel::svc { case (type::KType::KEvent): std::static_pointer_cast(object)->ResetSignal(); break; + case (type::KType::KProcess): std::static_pointer_cast(object)->ResetSignal(); break; + default: { state.logger->Warn("svcResetSignal: 'handle' type invalid: 0x{:X} ({})", handle, object->objectType); state.ctx->registers.w0 = constant::status::InvHandle; return; } } + state.logger->Debug("svcResetSignal: Resetting signal: 0x{:X}", handle); state.ctx->registers.w0 = constant::status::Success; } catch (const std::out_of_range &) { @@ -357,6 +410,7 @@ namespace skyline::kernel::svc { void WaitSynchronization(DeviceState &state) { constexpr auto maxSyncHandles = 0x40; // The total amount of handles that can be passed to WaitSynchronization + auto numHandles = state.ctx->registers.w2; if (numHandles > maxSyncHandles) { state.ctx->registers.w0 = constant::status::MaxHandles; @@ -373,7 +427,6 @@ namespace skyline::kernel::svc { handleStr += fmt::format("* 0x{:X}\n", handle); auto object = state.process->handles.at(handle); - switch (object->objectType) { case type::KType::KProcess: case type::KType::KThread: @@ -394,7 +447,6 @@ namespace skyline::kernel::svc { state.logger->Debug("svcWaitSynchronization: Waiting on handles:\n{}Timeout: 0x{:X} ns", handleStr, timeout); auto start = utils::GetTimeNs(); - while (true) { if (state.thread->cancelSync) { state.thread->cancelSync = false; @@ -437,15 +489,19 @@ namespace skyline::kernel::svc { state.ctx->registers.w0 = constant::status::InvAddress; return; } + auto ownerHandle = state.ctx->registers.w0; auto requesterHandle = state.ctx->registers.w2; if (requesterHandle != state.thread->handle) throw exception("svcWaitProcessWideKeyAtomic: Handle doesn't match current thread: 0x{:X} for thread 0x{:X}", requesterHandle, state.thread->handle); + state.logger->Debug("svcArbitrateLock: Locking mutex at 0x{:X}", address); + if (state.process->MutexLock(address, ownerHandle)) state.logger->Debug("svcArbitrateLock: Locked mutex at 0x{:X}", address); else state.logger->Debug("svcArbitrateLock: Owner handle did not match current owner for mutex at 0x{:X}", address); + state.ctx->registers.w0 = constant::status::Success; } @@ -456,7 +512,9 @@ namespace skyline::kernel::svc { state.ctx->registers.w0 = constant::status::InvAddress; return; } + state.logger->Debug("svcArbitrateUnlock: Unlocking mutex at 0x{:X}", address); + if (state.process->MutexUnlock(address)) { state.logger->Debug("svcArbitrateUnlock: Unlocked mutex at 0x{:X}", address); state.ctx->registers.w0 = constant::status::Success; @@ -473,17 +531,21 @@ namespace skyline::kernel::svc { state.ctx->registers.w0 = constant::status::InvAddress; return; } + auto condAddress = state.ctx->registers.x1; auto handle = state.ctx->registers.w2; if (handle != state.thread->handle) throw exception("svcWaitProcessWideKeyAtomic: Handle doesn't match current thread: 0x{:X} for thread 0x{:X}", handle, state.thread->handle); + if (!state.process->MutexUnlock(mtxAddress)) { state.logger->Debug("WaitProcessWideKeyAtomic: A non-owner thread tried to release a mutex at 0x{:X}", mtxAddress); state.ctx->registers.w0 = constant::status::InvAddress; return; } + auto timeout = state.ctx->registers.x3; state.logger->Debug("svcWaitProcessWideKeyAtomic: Mutex: 0x{:X}, Conditional-Variable: 0x{:X}, Timeout: {} ns", mtxAddress, condAddress, timeout); + if (state.process->ConditionalVariableWait(condAddress, mtxAddress, timeout)) { state.logger->Debug("svcWaitProcessWideKeyAtomic: Waited for conditional variable and relocked mutex"); state.ctx->registers.w0 = constant::status::Success; @@ -496,6 +558,7 @@ namespace skyline::kernel::svc { void SignalProcessWideKey(DeviceState &state) { auto address = state.ctx->registers.x0; auto count = state.ctx->registers.w1; + state.logger->Debug("svcSignalProcessWideKey: Signalling Conditional-Variable at 0x{:X} for {}", address, count); state.process->ConditionalVariableSignal(address, count); state.ctx->registers.w0 = constant::status::Success; @@ -556,8 +619,10 @@ namespace skyline::kernel::svc { void OutputDebugString(DeviceState &state) { auto debug = state.process->GetString(state.ctx->registers.x0, state.ctx->registers.x1); + if (debug.back() == '\n') debug.pop_back(); + state.logger->Info("Debug Output: {}", debug); state.ctx->registers.w0 = constant::status::Success; } @@ -567,10 +632,10 @@ namespace skyline::kernel::svc { auto handle = state.ctx->registers.w2; auto id1 = state.ctx->registers.x3; - constexpr auto totalPhysicalMemory = 0xF8000000; // ~4 GB of RAM - u64 out{}; + constexpr auto totalPhysicalMemory = 0xF8000000; // ~4 GB of RAM + switch (id0) { case constant::infoState::AllowedCpuIdBitmask: case constant::infoState::AllowedThreadPriorityMask: diff --git a/app/src/main/cpp/skyline/kernel/types/KMemory.h b/app/src/main/cpp/skyline/kernel/types/KMemory.h index 65e518ce..5f1f3c04 100644 --- a/app/src/main/cpp/skyline/kernel/types/KMemory.h +++ b/app/src/main/cpp/skyline/kernel/types/KMemory.h @@ -4,6 +4,9 @@ #include "KObject.h" namespace skyline::kernel::type { + /** + * @brief The base kernel memory object that other memory classes derieve from + */ class KMemory : public KObject { public: KMemory(const DeviceState &state, KType objectType) : KObject(state, objectType) {} diff --git a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp index a9f423ec..61bb1b70 100644 --- a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp @@ -9,12 +9,15 @@ namespace skyline::kernel::type { KPrivateMemory::KPrivateMemory(const DeviceState &state, u64 address, size_t size, memory::Permission permission, const memory::MemoryState memState) : size(size), KMemory(state, KType::KPrivateMemory) { if (address && !utils::PageAligned(address)) throw exception("KPrivateMemory was created with non-page-aligned address: 0x{:X}", address); + fd = ASharedMemory_create("KPrivateMemory", size); if (fd < 0) throw exception("An error occurred while creating shared memory: {}", fd); + auto host = mmap(nullptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, fd, 0); if (host == MAP_FAILED) throw exception("An occurred while mapping shared memory: {}", strerror(errno)); + Registers fregs{ .x0 = address, .x1 = size, @@ -23,10 +26,13 @@ namespace skyline::kernel::type { .x4 = static_cast(fd), .x8 = __NR_mmap, }; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) throw exception("An error occurred while mapping private memory in child process"); + this->address = fregs.x0; + BlockDescriptor block{ .address = fregs.x0, .size = size, @@ -45,17 +51,21 @@ namespace skyline::kernel::type { void KPrivateMemory::Resize(size_t nSize) { if (close(fd) < 0) throw exception("An error occurred while trying to close shared memory FD: {}", strerror(errno)); + fd = ASharedMemory_create("KPrivateMemory", nSize); if (fd < 0) throw exception("An error occurred while creating shared memory: {}", fd); + Registers fregs{ .x0 = address, .x1 = size, .x8 = __NR_munmap }; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) throw exception("An error occurred while unmapping private memory in child process"); + fregs = { .x0 = address, .x1 = nSize, @@ -64,11 +74,14 @@ namespace skyline::kernel::type { .x4 = static_cast(fd), .x8 = __NR_mmap, }; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) throw exception("An error occurred while remapping private memory in child process"); + auto chunk = state.os->memory.GetChunk(address); state.process->WriteMemory(reinterpret_cast(chunk->host), address, std::min(nSize, size), true); + for (const auto &block : chunk->blockList) { if ((block.address - chunk->address) < size) { fregs = { @@ -77,6 +90,7 @@ namespace skyline::kernel::type { .x2 = static_cast(block.permission.Get()), .x8 = __NR_mprotect, }; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) throw exception("An error occurred while updating private memory's permissions in child process"); @@ -84,10 +98,13 @@ namespace skyline::kernel::type { break; } } + munmap(reinterpret_cast(chunk->host), size); + auto host = mmap(reinterpret_cast(chunk->host), nSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, fd, 0); if (host == MAP_FAILED) throw exception("An occurred while mapping shared memory: {}", strerror(errno)); + chunk->host = reinterpret_cast(host); MemoryManager::ResizeChunk(chunk, nSize); size = nSize; @@ -100,9 +117,11 @@ namespace skyline::kernel::type { .x2 = static_cast(permission.Get()), .x8 = __NR_mprotect, }; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) throw exception("An error occurred while updating private memory's permissions in child process"); + auto chunk = state.os->memory.GetChunk(address); BlockDescriptor block{ .address = address, @@ -124,6 +143,7 @@ namespace skyline::kernel::type { } } catch (const std::exception &) { } + auto chunk = state.os->memory.GetChunk(address); if (chunk) { munmap(reinterpret_cast(chunk->host), chunk->size); diff --git a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp index c27b1534..028febee 100644 --- a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp @@ -13,6 +13,7 @@ namespace skyline::kernel::type { u64 KProcess::TlsPage::ReserveSlot() { if (Full()) throw exception("Trying to get TLS slot from full page"); + slot[index] = true; return Get(index++); // ++ on right will cause increment after evaluation of expression } @@ -20,6 +21,7 @@ namespace skyline::kernel::type { u64 KProcess::TlsPage::Get(u8 slotNo) { if (slotNo >= constant::TlsSlots) throw exception("TLS slot is out of range"); + return address + (constant::TlsSlotSize * slotNo); } @@ -31,6 +33,7 @@ namespace skyline::kernel::type { for (auto &tlsPage: tlsPages) if (!tlsPage->Full()) return tlsPage->ReserveSlot(); + u64 address; if (tlsPages.empty()) { auto region = state.os->memory.GetRegion(memory::Regions::TlsIo); @@ -38,6 +41,7 @@ namespace skyline::kernel::type { } else { address = (*(tlsPages.end() - 1))->address + PAGE_SIZE; } + auto tlsMem = NewHandle(address, PAGE_SIZE, memory::Permission(true, true, false), memory::states::ThreadLocal).item; tlsPages.push_back(std::make_shared(tlsMem->address)); @@ -74,19 +78,24 @@ namespace skyline::kernel::type { std::shared_ptr KProcess::CreateThread(u64 entryPoint, u64 entryArg, u64 stackTop, u8 priority) { auto size = (sizeof(ThreadContext) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); auto tlsMem = std::make_shared(state, 0, size, memory::Permission{true, true, false}, memory::states::Reserved); - Registers fregs{}; - fregs.x0 = CLONE_THREAD | CLONE_SIGHAND | CLONE_PTRACE | CLONE_FS | CLONE_VM | CLONE_FILES | CLONE_IO; - fregs.x1 = stackTop; - fregs.x3 = tlsMem->Map(0, size, memory::Permission{true, true, false}); - fregs.x8 = __NR_clone; - fregs.x5 = reinterpret_cast(&guest::GuestEntry); - fregs.x6 = entryPoint; + + Registers fregs{ + .x0 = CLONE_THREAD | CLONE_SIGHAND | CLONE_PTRACE | CLONE_FS | CLONE_VM | CLONE_FILES | CLONE_IO, + .x1 = stackTop, + .x3 = tlsMem->Map(0, size, memory::Permission{ true, true, false }), + .x8 = __NR_clone, + .x5 = reinterpret_cast(&guest::GuestEntry), + .x6 = entryPoint, + }; + state.nce->ExecuteFunction(ThreadCall::Clone, fregs); if (static_cast(fregs.x0) < 0) throw exception("Cannot create thread: Address: 0x{:X}, Stack Top: 0x{:X}", entryPoint, stackTop); + auto pid = static_cast(fregs.x0); auto process = NewHandle(pid, entryPoint, entryArg, stackTop, GetTlsSlot(), priority, this, tlsMem).item; threads[pid] = process; + return process; } @@ -98,19 +107,23 @@ namespace skyline::kernel::type { void KProcess::ReadMemory(void *destination, const u64 offset, const size_t size, const bool forceGuest) const { if (!forceGuest) { auto source = GetHostAddress(offset); + if (source) { memcpy(destination, reinterpret_cast(source), size); return; } } + struct iovec local{ .iov_base = destination, .iov_len = size, }; + struct iovec remote{ .iov_base = reinterpret_cast(offset), .iov_len = size, }; + if (process_vm_readv(pid, &local, 1, &remote, 1, 0) < 0) pread64(memFd, destination, size, offset); } @@ -118,19 +131,23 @@ namespace skyline::kernel::type { void KProcess::WriteMemory(void *source, const u64 offset, const size_t size, const bool forceGuest) const { if (!forceGuest) { auto destination = GetHostAddress(offset); + if (destination) { memcpy(reinterpret_cast(destination), source, size); return; } } + struct iovec local{ .iov_base = source, .iov_len = size, }; + struct iovec remote{ .iov_base = reinterpret_cast(offset), .iov_len = size, }; + if (process_vm_writev(pid, &local, 1, &remote, 1, 0) < 0) pwrite64(memFd, source, size, offset); } @@ -138,18 +155,22 @@ namespace skyline::kernel::type { void KProcess::CopyMemory(u64 source, u64 destination, size_t size) const { auto sourceHost = GetHostAddress(source); auto destinationHost = GetHostAddress(destination); + if (sourceHost && destinationHost) { memcpy(reinterpret_cast(destinationHost), reinterpret_cast(sourceHost), size); } else { if (size <= PAGE_SIZE) { std::vector buffer(size); + state.process->ReadMemory(buffer.data(), source, size); state.process->WriteMemory(buffer.data(), destination, size); } else { - Registers fregs{}; - fregs.x0 = source; - fregs.x1 = destination; - fregs.x2 = size; + Registers fregs{ + .x0 = source, + .x1 = destination, + .x2 = size, + }; + state.nce->ExecuteFunction(ThreadCall::Memcopy, fregs); } } @@ -169,52 +190,64 @@ namespace skyline::kernel::type { break; } } + return std::nullopt; } bool KProcess::MutexLock(u64 address, KHandle owner) { std::unique_lock lock(mutexLock); + auto mtx = GetPointer(address); auto &mtxWaiters = mutexes[address]; + u32 mtxExpected = 0; if (__atomic_compare_exchange_n(mtx, &mtxExpected, (constant::MtxOwnerMask & state.thread->handle) | (mtxWaiters.empty() ? 0 : ~constant::MtxOwnerMask), false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) return true; if (owner && (__atomic_load_n(mtx, __ATOMIC_SEQ_CST) != (owner | ~constant::MtxOwnerMask))) return false; + std::shared_ptr status; for (auto it = mtxWaiters.begin();; ++it) { if (it != mtxWaiters.end() && (*it)->priority >= state.thread->priority) continue; + status = std::make_shared(state.thread->priority, state.thread->handle); mtxWaiters.insert(it, status); break; } + lock.unlock(); while (!status->flag); lock.lock(); status->flag = false; + for (auto it = mtxWaiters.begin(); it != mtxWaiters.end(); ++it) { if ((*it)->handle == state.thread->handle) { mtxWaiters.erase(it); break; } } + return true; } bool KProcess::MutexUnlock(u64 address) { std::unique_lock lock(mutexLock); + auto mtx = GetPointer(address); auto &mtxWaiters = mutexes[address]; u32 mtxDesired{}; if (!mtxWaiters.empty()) mtxDesired = (*mtxWaiters.begin())->handle | ((mtxWaiters.size() > 1) ? ~constant::MtxOwnerMask : 0); + u32 mtxExpected = (constant::MtxOwnerMask & state.thread->handle) | ~constant::MtxOwnerMask; if (!__atomic_compare_exchange_n(mtx, &mtxExpected, mtxDesired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { mtxExpected = constant::MtxOwnerMask & state.thread->handle; + if (!__atomic_compare_exchange_n(mtx, &mtxExpected, mtxDesired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) return false; } + if (mtxDesired) { auto status = (*mtxWaiters.begin()); status->flag = true; @@ -222,52 +255,65 @@ namespace skyline::kernel::type { while (status->flag); lock.lock(); } + return true; } bool KProcess::ConditionalVariableWait(u64 conditionalAddress, u64 mutexAddress, u64 timeout) { std::unique_lock lock(conditionalLock); auto &condWaiters = conditionals[conditionalAddress]; + std::shared_ptr status; for (auto it = condWaiters.begin();; ++it) { if (it != condWaiters.end() && (*it)->priority >= state.thread->priority) continue; + status = std::make_shared(state.thread->priority, state.thread->handle, mutexAddress); condWaiters.insert(it, status); break; } + lock.unlock(); bool timedOut{}; + auto start = utils::GetTimeNs(); while (!status->flag) if ((utils::GetTimeNs() - start) >= timeout) timedOut = true; + lock.lock(); if (!status->flag) timedOut = false; else status->flag = false; + for (auto it = condWaiters.begin(); it != condWaiters.end(); ++it) { if ((*it)->handle == state.thread->handle) { condWaiters.erase(it); break; } } + lock.unlock(); + return !timedOut; } void KProcess::ConditionalVariableSignal(u64 address, u64 amount) { std::unique_lock condLock(conditionalLock); + auto &condWaiters = conditionals[address]; u64 count{}; + auto iter = condWaiters.begin(); while (iter != condWaiters.end() && count < amount) { auto &thread = *iter; auto mtx = GetPointer(thread->mutexAddress); u32 mtxValue = __atomic_load_n(mtx, __ATOMIC_SEQ_CST); + while (true) { u32 mtxDesired{}; + if (!mtxValue) mtxDesired = (constant::MtxOwnerMask & thread->handle); else if ((mtxValue & constant::MtxOwnerMask) == state.thread->handle) @@ -276,13 +322,16 @@ namespace skyline::kernel::type { mtxDesired = mtxValue | ~constant::MtxOwnerMask; else break; + if (__atomic_compare_exchange_n(mtx, &mtxValue, mtxDesired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) break; } if (mtxValue && ((mtxValue & constant::MtxOwnerMask) != state.thread->handle)) { std::unique_lock mtxLock(mutexLock); + auto &mtxWaiters = mutexes[thread->mutexAddress]; std::shared_ptr status; + for (auto it = mtxWaiters.begin();; ++it) { if (it != mtxWaiters.end() && (*it)->priority >= thread->priority) continue; @@ -290,21 +339,26 @@ namespace skyline::kernel::type { mtxWaiters.insert(it, status); break; } + mtxLock.unlock(); while (!status->flag); mtxLock.lock(); status->flag = false; + for (auto it = mtxWaiters.begin(); it != mtxWaiters.end(); ++it) { if ((*it)->handle == thread->handle) { mtxWaiters.erase(it); break; } } + mtxLock.unlock(); } + thread->flag = true; iter++; count++; + condLock.unlock(); while (thread->flag); condLock.lock(); diff --git a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp index 888ec1fe..93606834 100644 --- a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp @@ -9,18 +9,22 @@ namespace skyline::kernel::type { KSharedMemory::KSharedMemory(const DeviceState &state, u64 address, size_t size, const memory::Permission permission, memory::MemoryState memState, int mmapFlags) : initialState(memState), KMemory(state, KType::KSharedMemory) { if (address && !utils::PageAligned(address)) throw exception("KSharedMemory was created with non-page-aligned address: 0x{:X}", address); + fd = ASharedMemory_create("KSharedMemory", size); if (fd < 0) throw exception("An error occurred while creating shared memory: {}", fd); + address = reinterpret_cast(mmap(reinterpret_cast(address), size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | ((address) ? MAP_FIXED : 0) | mmapFlags, fd, 0)); if (address == reinterpret_cast(MAP_FAILED)) throw exception("An occurred while mapping shared memory: {}", strerror(errno)); + kernel = {.address = address, .size = size, .permission = permission}; } u64 KSharedMemory::Map(const u64 address, const u64 size, memory::Permission permission) { if (address && !utils::PageAligned(address)) throw exception("KSharedMemory was mapped to a non-page-aligned address: 0x{:X}", address); + Registers fregs{ .x0 = address, .x1 = size, @@ -29,10 +33,13 @@ namespace skyline::kernel::type { .x4 = static_cast(fd), .x8 = __NR_mmap, }; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) throw exception("An error occurred while mapping shared memory in guest"); + guest = {.address = fregs.x0, .size = size, .permission = permission}; + BlockDescriptor block{ .address = fregs.x0, .size = size, @@ -46,6 +53,7 @@ namespace skyline::kernel::type { .blockList = {block}, }; state.os->memory.InsertChunk(chunk); + return fregs.x0; } @@ -53,17 +61,21 @@ namespace skyline::kernel::type { if (guest.Valid() && kernel.Valid()) { if (close(fd) < 0) throw exception("An error occurred while trying to close shared memory FD: {}", strerror(errno)); + fd = ASharedMemory_create("KSharedMemory", size); if (fd < 0) throw exception("An error occurred while creating shared memory: {}", fd); + Registers fregs{ .x0 = guest.address, .x1 = guest.size, .x8 = __NR_munmap }; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) throw exception("An error occurred while unmapping private memory in child process"); + fregs = { .x0 = guest.address, .x1 = size, @@ -72,10 +84,13 @@ namespace skyline::kernel::type { .x4 = static_cast(fd), .x8 = __NR_mmap, }; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) throw exception("An error occurred while remapping private memory in child process"); + state.process->WriteMemory(reinterpret_cast(kernel.address), guest.address, std::min(guest.size, size), true); + auto chunk = state.os->memory.GetChunk(guest.address); for (const auto &block : chunk->blockList) { if ((block.address - chunk->address) < guest.size) { @@ -85,6 +100,7 @@ namespace skyline::kernel::type { .x2 = static_cast(block.permission.Get()), .x8 = __NR_mprotect, }; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) throw exception("An error occurred while updating private memory's permissions in child process"); @@ -92,25 +108,34 @@ namespace skyline::kernel::type { break; } } + munmap(reinterpret_cast(kernel.address), kernel.size); + auto host = mmap(reinterpret_cast(chunk->host), size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, fd, 0); if (host == MAP_FAILED) throw exception("An occurred while mapping shared memory: {}", strerror(errno)); + guest.size = size; MemoryManager::ResizeChunk(chunk, size); } else if (kernel.Valid()) { if (close(fd) < 0) throw exception("An error occurred while trying to close shared memory FD: {}", strerror(errno)); + fd = ASharedMemory_create("KSharedMemory", size); if (fd < 0) throw exception("An error occurred while creating shared memory: {}", fd); + std::vector data(std::min(size, kernel.size)); memcpy(data.data(), reinterpret_cast(kernel.address), std::min(size, kernel.size)); + munmap(reinterpret_cast(kernel.address), kernel.size); + auto address = mmap(reinterpret_cast(kernel.address), size, kernel.permission.Get(), MAP_SHARED, fd, 0); if (address == MAP_FAILED) throw exception("An occurred while mapping shared memory: {}", strerror(errno)); + memcpy(address, data.data(), std::min(size, kernel.size)); + kernel.address = reinterpret_cast(address); kernel.size = size; } else { @@ -126,9 +151,11 @@ namespace skyline::kernel::type { .x2 = static_cast(permission.Get()), .x8 = __NR_mprotect, }; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) throw exception("An error occurred while updating shared memory's permissions in guest"); + auto chunk = state.os->memory.GetChunk(address); BlockDescriptor block{ .address = address, @@ -152,6 +179,7 @@ namespace skyline::kernel::type { .x1 = guest.size, .x8 = __NR_munmap, }; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); } } catch (const std::exception &) { diff --git a/app/src/main/cpp/skyline/kernel/types/KThread.cpp b/app/src/main/cpp/skyline/kernel/types/KThread.cpp index ccac06c1..24680951 100644 --- a/app/src/main/cpp/skyline/kernel/types/KThread.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KThread.cpp @@ -17,6 +17,7 @@ namespace skyline::kernel::type { if (pid == parent->pid) parent->status = KProcess::Status::Started; status = Status::Running; + state.nce->StartThread(entryArg, handle, parent->threads.at(pid)); } } diff --git a/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp index 32abd61a..b625d13e 100644 --- a/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp @@ -7,6 +7,7 @@ namespace skyline::kernel::type { KTransferMemory::KTransferMemory(const DeviceState &state, bool host, u64 address, size_t size, const memory::Permission permission, memory::MemoryState memState) : host(host), size(size), KMemory(state, KType::KTransferMemory) { if (address && !utils::PageAligned(address)) throw exception("KTransferMemory was created with non-page-aligned address: 0x{:X}", address); + BlockDescriptor block{ .size = size, .permission = permission, @@ -16,10 +17,12 @@ namespace skyline::kernel::type { .state = memState, .blockList = {block}, }; + if (host) { address = reinterpret_cast(mmap(reinterpret_cast(address), size, permission.Get(), MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0), -1, 0)); if (reinterpret_cast(address) == MAP_FAILED) throw exception("An error occurred while mapping transfer memory in host"); + this->address = address; chunk.address = address; chunk.blockList.front().address = address; @@ -33,12 +36,15 @@ namespace skyline::kernel::type { .x4 = static_cast(-1), .x8 = __NR_mmap, }; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) throw exception("An error occurred while mapping shared region in child process"); + this->address = fregs.x0; chunk.address = fregs.x0; chunk.blockList.front().address = fregs.x0; + state.os->memory.InsertChunk(chunk); } } @@ -46,13 +52,17 @@ namespace skyline::kernel::type { u64 KTransferMemory::Transfer(bool mHost, u64 nAddress, u64 nSize) { if (nAddress && !utils::PageAligned(nAddress)) throw exception("KTransferMemory was transferred to a non-page-aligned address: 0x{:X}", nAddress); + nSize = nSize ? nSize : size; + ChunkDescriptor chunk = host ? hostChunk : *state.os->memory.GetChunk(address); chunk.address = nAddress; chunk.size = nSize; MemoryManager::ResizeChunk(&chunk, nSize); + for (auto &block : chunk.blockList) { block.address = nAddress + (block.address - address); + if ((mHost && !host) || (!mHost && !host)) { Registers fregs{ .x0 = block.address, @@ -62,15 +72,18 @@ namespace skyline::kernel::type { .x4 = static_cast(-1), .x8 = __NR_mmap, }; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) throw exception("An error occurred while mapping transfer memory in child process"); + nAddress = fregs.x0; } else if ((!mHost && host) || (mHost && host)) { nAddress = reinterpret_cast(mmap(reinterpret_cast(block.address), block.size, block.permission.Get(), MAP_ANONYMOUS | MAP_PRIVATE | ((nAddress) ? MAP_FIXED : 0), -1, 0)); if (reinterpret_cast(nAddress) == MAP_FAILED) throw exception("An error occurred while mapping transfer memory in host"); } + if (block.permission.r) { if (mHost && !host) state.process->ReadMemory(reinterpret_cast(nAddress), address, block.size); @@ -92,12 +105,14 @@ namespace skyline::kernel::type { .x2 = static_cast(block.permission.Get()), .x8 = __NR_mprotect, }; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) throw exception("An error occurred while updating transfer memory's permissions in guest"); } } } + if (mHost && !host) { state.os->memory.DeleteChunk(address); hostChunk = chunk; @@ -109,12 +124,14 @@ namespace skyline::kernel::type { state.os->memory.DeleteChunk(address); state.os->memory.InsertChunk(chunk); } + if ((mHost && !host) || (!mHost && !host)) { Registers fregs{ .x0 = address, .x1 = size, .x8 = __NR_munmap, }; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) throw exception("An error occurred while unmapping transfer memory in child process"); @@ -122,6 +139,7 @@ namespace skyline::kernel::type { if (reinterpret_cast(munmap(reinterpret_cast(address), size)) == MAP_FAILED) throw exception("An error occurred while unmapping transfer memory in host: {}"); } + host = mHost; address = nAddress; size = nSize; @@ -139,10 +157,13 @@ namespace skyline::kernel::type { .x2 = nSize, .x8 = __NR_mremap, }; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) throw exception("An error occurred while remapping transfer memory in guest"); + size = nSize; + auto chunk = state.os->memory.GetChunk(address); MemoryManager::ResizeChunk(chunk, size); } @@ -154,9 +175,11 @@ namespace skyline::kernel::type { .size = size, .permission = permission, }; + if (host) { if (mprotect(reinterpret_cast(address), size, permission.Get()) == reinterpret_cast(MAP_FAILED)) throw exception("An occurred while remapping transfer memory: {}", strerror(errno)); + MemoryManager::InsertBlock(&hostChunk, block); } else { Registers fregs{ @@ -165,9 +188,11 @@ namespace skyline::kernel::type { .x2 = static_cast(permission.Get()), .x8 = __NR_mprotect, }; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); if (fregs.x0 < 0) throw exception("An error occurred while updating transfer memory's permissions in guest"); + auto chunk = state.os->memory.GetChunk(address); MemoryManager::InsertBlock(chunk, block); } @@ -183,7 +208,9 @@ namespace skyline::kernel::type { .x1 = size, .x8 = __NR_munmap, }; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); + state.os->memory.DeleteChunk(address); } catch (const std::exception &) { } diff --git a/app/src/main/cpp/skyline/os.cpp b/app/src/main/cpp/skyline/os.cpp index 22fdf615..366800a9 100644 --- a/app/src/main/cpp/skyline/os.cpp +++ b/app/src/main/cpp/skyline/os.cpp @@ -7,14 +7,17 @@ namespace skyline::kernel { void OS::Execute(const int romFd, const TitleFormat romType) { std::shared_ptr loader; + if (romType == TitleFormat::NRO) { loader = std::make_shared(romFd); } else throw exception("Unsupported ROM extension."); + auto process = CreateProcess(constant::BaseAddress, 0, constant::DefStackSize); loader->LoadProcessData(process, state); process->InitializeMemory(); process->threads.at(process->pid)->Start(); // The kernel itself is responsible for starting the main thread + state.nce->Execute(); } @@ -23,13 +26,17 @@ namespace skyline::kernel { stack->guest = stack->kernel; if (mprotect(reinterpret_cast(stack->guest.address), PAGE_SIZE, PROT_NONE)) throw exception("Failed to create guard pages"); + auto tlsMem = std::make_shared(state, 0, (sizeof(ThreadContext) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1), memory::Permission{true, true, false}, memory::states::Reserved); tlsMem->guest = tlsMem->kernel; + pid_t pid = clone(reinterpret_cast(&guest::GuestEntry), reinterpret_cast(stack->guest.address + stackSize), CLONE_FILES | CLONE_FS | CLONE_SETTLS | SIGCHLD, reinterpret_cast(entry), nullptr, reinterpret_cast(tlsMem->guest.address)); if (pid == -1) throw exception("Call to clone() has failed: {}", strerror(errno)); + state.logger->Debug("Successfully created process with PID: {}", pid); process = std::make_shared(state, pid, argument, stack, tlsMem); + return process; }