mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-26 08:54:16 +01:00
Refactor OS/Kernel
This refactors the OS/Kernel by adding spacing and fixing/adding comments in some cases, in addition to some other minor fixes here and there.
This commit is contained in:
parent
773ee25e5a
commit
9f0ad46903
@ -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<DescriptorPack> 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;
|
||||
}
|
||||
}
|
||||
|
@ -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<type::KPrivateMemory>(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<bool>(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<u32>(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<u8>(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<type::KThread>(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<type::KThread>(handle)->UpdatePriority(static_cast<u8>(priority));
|
||||
@ -264,17 +303,20 @@ namespace skyline::kernel::svc {
|
||||
try {
|
||||
auto object = state.process->GetHandle<type::KSharedMemory>(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<memory::Permission *>(&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<memory::Permission *>(&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<type::KTransferMemory>(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<type::KEvent>(object)->ResetSignal();
|
||||
break;
|
||||
|
||||
case (type::KType::KProcess):
|
||||
std::static_pointer_cast<type::KProcess>(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:
|
||||
|
@ -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) {}
|
||||
|
@ -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<u64>(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<u64>(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<void *>(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<u64>(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<void *>(chunk->host), size);
|
||||
|
||||
auto host = mmap(reinterpret_cast<void *>(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<u64>(host);
|
||||
MemoryManager::ResizeChunk(chunk, nSize);
|
||||
size = nSize;
|
||||
@ -100,9 +117,11 @@ namespace skyline::kernel::type {
|
||||
.x2 = static_cast<u64>(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<void *>(chunk->host), chunk->size);
|
||||
|
@ -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<KPrivateMemory>(address, PAGE_SIZE, memory::Permission(true, true, false), memory::states::ThreadLocal).item;
|
||||
tlsPages.push_back(std::make_shared<TlsPage>(tlsMem->address));
|
||||
|
||||
@ -74,19 +78,24 @@ namespace skyline::kernel::type {
|
||||
std::shared_ptr<KThread> 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<type::KSharedMemory>(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<u64>(&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<u64>(&guest::GuestEntry),
|
||||
.x6 = entryPoint,
|
||||
};
|
||||
|
||||
state.nce->ExecuteFunction(ThreadCall::Clone, fregs);
|
||||
if (static_cast<int>(fregs.x0) < 0)
|
||||
throw exception("Cannot create thread: Address: 0x{:X}, Stack Top: 0x{:X}", entryPoint, stackTop);
|
||||
|
||||
auto pid = static_cast<pid_t>(fregs.x0);
|
||||
auto process = NewHandle<KThread>(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<void *>(source), size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
struct iovec local{
|
||||
.iov_base = destination,
|
||||
.iov_len = size,
|
||||
};
|
||||
|
||||
struct iovec remote{
|
||||
.iov_base = reinterpret_cast<void *>(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<void *>(destination), source, size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
struct iovec local{
|
||||
.iov_base = source,
|
||||
.iov_len = size,
|
||||
};
|
||||
|
||||
struct iovec remote{
|
||||
.iov_base = reinterpret_cast<void *>(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<void *>(destinationHost), reinterpret_cast<const void *>(sourceHost), size);
|
||||
} else {
|
||||
if (size <= PAGE_SIZE) {
|
||||
std::vector<u8> 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<u32>(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<WaitStatus> status;
|
||||
for (auto it = mtxWaiters.begin();; ++it) {
|
||||
if (it != mtxWaiters.end() && (*it)->priority >= state.thread->priority)
|
||||
continue;
|
||||
|
||||
status = std::make_shared<WaitStatus>(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<u32>(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<WaitStatus> status;
|
||||
for (auto it = condWaiters.begin();; ++it) {
|
||||
if (it != condWaiters.end() && (*it)->priority >= state.thread->priority)
|
||||
continue;
|
||||
|
||||
status = std::make_shared<WaitStatus>(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<u32>(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<WaitStatus> 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();
|
||||
|
@ -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<u64>(mmap(reinterpret_cast<void *>(address), size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | ((address) ? MAP_FIXED : 0) | mmapFlags, fd, 0));
|
||||
if (address == reinterpret_cast<u64>(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<u64>(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<u64>(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<void *>(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<u64>(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<void *>(kernel.address), kernel.size);
|
||||
|
||||
auto host = mmap(reinterpret_cast<void *>(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<u8> data(std::min(size, kernel.size));
|
||||
memcpy(data.data(), reinterpret_cast<const void *>(kernel.address), std::min(size, kernel.size));
|
||||
|
||||
munmap(reinterpret_cast<void *>(kernel.address), kernel.size);
|
||||
|
||||
auto address = mmap(reinterpret_cast<void *>(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<u64>(address);
|
||||
kernel.size = size;
|
||||
} else {
|
||||
@ -126,9 +151,11 @@ namespace skyline::kernel::type {
|
||||
.x2 = static_cast<u64>(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 &) {
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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<u64>(mmap(reinterpret_cast<void *>(address), size, permission.Get(), MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0), -1, 0));
|
||||
if (reinterpret_cast<void *>(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<u64>(-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<u64>(-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<u64>(mmap(reinterpret_cast<void *>(block.address), block.size, block.permission.Get(), MAP_ANONYMOUS | MAP_PRIVATE | ((nAddress) ? MAP_FIXED : 0), -1, 0));
|
||||
if (reinterpret_cast<void *>(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<void *>(nAddress), address, block.size);
|
||||
@ -92,12 +105,14 @@ namespace skyline::kernel::type {
|
||||
.x2 = static_cast<u64>(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<void *>(munmap(reinterpret_cast<void *>(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<void *>(address), size, permission.Get()) == reinterpret_cast<u64>(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<u64>(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 &) {
|
||||
}
|
||||
|
@ -7,14 +7,17 @@ namespace skyline::kernel {
|
||||
|
||||
void OS::Execute(const int romFd, const TitleFormat romType) {
|
||||
std::shared_ptr<loader::Loader> loader;
|
||||
|
||||
if (romType == TitleFormat::NRO) {
|
||||
loader = std::make_shared<loader::NroLoader>(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<void *>(stack->guest.address), PAGE_SIZE, PROT_NONE))
|
||||
throw exception("Failed to create guard pages");
|
||||
|
||||
auto tlsMem = std::make_shared<type::KSharedMemory>(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<int (*)(void *)>(&guest::GuestEntry), reinterpret_cast<void *>(stack->guest.address + stackSize), CLONE_FILES | CLONE_FS | CLONE_SETTLS | SIGCHLD, reinterpret_cast<void *>(entry), nullptr, reinterpret_cast<void *>(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<kernel::type::KProcess>(state, pid, argument, stack, tlsMem);
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user