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:
◱ PixelyIon 2020-03-26 00:27:05 +05:30 committed by ◱ PixelyIon
parent 773ee25e5a
commit 9f0ad46903
9 changed files with 256 additions and 27 deletions

View File

@ -6,25 +6,30 @@ namespace skyline::kernel {
auto chunk = std::upper_bound(chunkList.begin(), chunkList.end(), address, [](const u64 address, const ChunkDescriptor &chunk) -> bool { auto chunk = std::upper_bound(chunkList.begin(), chunkList.end(), address, [](const u64 address, const ChunkDescriptor &chunk) -> bool {
return address < chunk.address; return address < chunk.address;
}); });
if (chunk-- != chunkList.begin()) { if (chunk-- != chunkList.begin()) {
if ((chunk->address + chunk->size) > address) if ((chunk->address + chunk->size) > address)
return chunk.base(); return chunk.base();
} }
return nullptr; return nullptr;
} }
BlockDescriptor *MemoryManager::GetBlock(u64 address, ChunkDescriptor *chunk) { BlockDescriptor *MemoryManager::GetBlock(u64 address, ChunkDescriptor *chunk) {
if (!chunk) if (!chunk)
chunk = GetChunk(address); chunk = GetChunk(address);
if (chunk) { if (chunk) {
auto block = std::upper_bound(chunk->blockList.begin(), chunk->blockList.end(), address, [](const u64 address, const BlockDescriptor &block) -> bool { auto block = std::upper_bound(chunk->blockList.begin(), chunk->blockList.end(), address, [](const u64 address, const BlockDescriptor &block) -> bool {
return address < block.address; return address < block.address;
}); });
if (block-- != chunk->blockList.begin()) { if (block-- != chunk->blockList.begin()) {
if ((block->address + block->size) > address) if ((block->address + block->size) > address)
return block.base(); return block.base();
} }
} }
return nullptr; 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 { auto upperChunk = std::upper_bound(chunkList.begin(), chunkList.end(), chunk.address, [](const u64 address, const ChunkDescriptor &chunk) -> bool {
return address < chunk.address; return address < chunk.address;
}); });
if (upperChunk != chunkList.begin()) { if (upperChunk != chunkList.begin()) {
auto lowerChunk = std::prev(upperChunk); auto lowerChunk = std::prev(upperChunk);
if (lowerChunk->address + lowerChunk->size > chunk.address) 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); 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); chunkList.insert(upperChunk, chunk);
} }
@ -55,24 +63,29 @@ namespace skyline::kernel {
} else if (size > chunk->size) { } else if (size > chunk->size) {
auto begin = chunk->blockList.begin(); auto begin = chunk->blockList.begin();
auto end = std::prev(chunk->blockList.end()); auto end = std::prev(chunk->blockList.end());
BlockDescriptor block{ BlockDescriptor block{
.address = (end->address + end->size), .address = (end->address + end->size),
.size = (chunk->address + size) - (end->address + end->size), .size = (chunk->address + size) - (end->address + end->size),
.permission = begin->permission, .permission = begin->permission,
.attributes = begin->attributes, .attributes = begin->attributes,
}; };
chunk->blockList.push_back(block); chunk->blockList.push_back(block);
} else if (size < chunk->size) { } else if (size < chunk->size) {
auto endAddress = chunk->address + size; auto endAddress = chunk->address + size;
for (auto block = chunk->blockList.begin(), end = chunk->blockList.end(); block != end;) { for (auto block = chunk->blockList.begin(), end = chunk->blockList.end(); block != end;) {
if (block->address > endAddress) if (block->address > endAddress)
block = chunk->blockList.erase(block); block = chunk->blockList.erase(block);
else else
++block; ++block;
} }
auto end = std::prev(chunk->blockList.end()); auto end = std::prev(chunk->blockList.end());
end->size = endAddress - end->address; end->size = endAddress - end->address;
} }
chunk->size = size; chunk->size = size;
} }
@ -87,16 +100,19 @@ namespace skyline::kernel {
auto endBlock = *iter; auto endBlock = *iter;
endBlock.address = (block.address + block.size); endBlock.address = (block.address + block.size);
endBlock.size = (iter->address + iter->size) - endBlock.address; endBlock.size = (iter->address + iter->size) - endBlock.address;
iter->size = iter->address - block.address; iter->size = iter->address - block.address;
chunk->blockList.insert(std::next(iter), {block, endBlock}); chunk->blockList.insert(std::next(iter), {block, endBlock});
} }
} else if (std::next(iter) != chunk->blockList.end()) { } else if (std::next(iter) != chunk->blockList.end()) {
auto nextIter = std::next(iter); auto nextIter = std::next(iter);
auto nextEnd = nextIter->address + nextIter->size; auto nextEnd = nextIter->address + nextIter->size;
if(nextEnd > block.address) { if(nextEnd > block.address) {
iter->size = block.address - iter->address; iter->size = block.address - iter->address;
nextIter->address = block.address + block.size; nextIter->address = block.address + block.size;
nextIter->size = nextEnd - nextIter->address; nextIter->size = nextEnd - nextIter->address;
chunk->blockList.insert(nextIter, block); chunk->blockList.insert(nextIter, block);
} else { } else {
throw exception("InsertBlock: Inserting block across more than one block is not allowed"); throw exception("InsertBlock: Inserting block across more than one block is not allowed");
@ -107,6 +123,7 @@ namespace skyline::kernel {
return; return;
} }
} }
throw exception("InsertBlock: Block offset not present within current block list"); throw exception("InsertBlock: Block offset not present within current block list");
} }
@ -114,6 +131,7 @@ namespace skyline::kernel {
switch (type) { switch (type) {
case memory::AddressSpaceType::AddressSpace32Bit: case memory::AddressSpaceType::AddressSpace32Bit:
throw exception("32-bit address spaces are not supported"); throw exception("32-bit address spaces are not supported");
case memory::AddressSpaceType::AddressSpace36Bit: { case memory::AddressSpaceType::AddressSpace36Bit: {
base.address = constant::BaseAddress; base.address = constant::BaseAddress;
base.size = 0xFF8000000; base.size = 0xFF8000000;
@ -131,6 +149,7 @@ namespace skyline::kernel {
tlsIo.size = 0; tlsIo.size = 0;
break; break;
} }
case memory::AddressSpaceType::AddressSpace39Bit: { case memory::AddressSpaceType::AddressSpace39Bit: {
base.address = constant::BaseAddress; base.address = constant::BaseAddress;
base.size = 0x7FF8000000; base.size = 0x7FF8000000;
@ -147,6 +166,7 @@ namespace skyline::kernel {
break; 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 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); .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) { std::optional<DescriptorPack> MemoryManager::Get(u64 address) {
auto chunk = GetChunk(address); auto chunk = GetChunk(address);
if (chunk) if (chunk)
return DescriptorPack{*GetBlock(address, chunk), *chunk}; return DescriptorPack{*GetBlock(address, chunk), *chunk};
return std::nullopt; return std::nullopt;
} }
@ -179,8 +201,10 @@ namespace skyline::kernel {
size_t MemoryManager::GetProgramSize() { size_t MemoryManager::GetProgramSize() {
size_t size = 0; size_t size = 0;
for (const auto &chunk : chunkList) for (const auto &chunk : chunkList)
size += chunk.size; size += chunk.size;
return size; return size;
} }
} }

View File

@ -4,7 +4,7 @@
namespace skyline::kernel::svc { namespace skyline::kernel::svc {
void SetHeapSize(DeviceState &state) { void SetHeapSize(DeviceState &state) {
constexpr auto heapSizeAlign = 0x200000; // The heap size has to be a multiple of this value 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) { if (size % heapSizeAlign != 0) {
state.ctx->registers.w0 = constant::status::InvSize; state.ctx->registers.w0 = constant::status::InvSize;
@ -24,26 +24,30 @@ namespace skyline::kernel::svc {
} }
void SetMemoryAttribute(DeviceState &state) { void SetMemoryAttribute(DeviceState &state) {
const u64 address = state.ctx->registers.x0; auto address = state.ctx->registers.x0;
if (!utils::PageAligned(address)) { if (!utils::PageAligned(address)) {
state.ctx->registers.w0 = constant::status::InvAddress; state.ctx->registers.w0 = constant::status::InvAddress;
state.logger->Warn("svcSetMemoryAttribute: 'address' not page aligned: 0x{:X}", address); state.logger->Warn("svcSetMemoryAttribute: 'address' not page aligned: 0x{:X}", address);
return; return;
} }
const u64 size = state.ctx->registers.x1;
auto size = state.ctx->registers.x1;
if (!utils::PageAligned(size)) { if (!utils::PageAligned(size)) {
state.ctx->registers.w0 = constant::status::InvSize; state.ctx->registers.w0 = constant::status::InvSize;
state.logger->Warn("svcSetMemoryAttribute: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size); state.logger->Warn("svcSetMemoryAttribute: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size);
return; return;
} }
memory::MemoryAttribute mask{.value = state.ctx->registers.w2}; memory::MemoryAttribute mask{.value = state.ctx->registers.w2};
memory::MemoryAttribute value{.value = state.ctx->registers.w3}; 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) { if (maskedValue != mask.value || !mask.isUncached || mask.isDeviceShared || mask.isBorrowed || mask.isIpcLocked) {
state.ctx->registers.w0 = constant::status::InvCombination; state.ctx->registers.w0 = constant::status::InvCombination;
state.logger->Warn("svcSetMemoryAttribute: 'mask' invalid: 0x{:X}, 0x{:X}", mask.value, value.value); state.logger->Warn("svcSetMemoryAttribute: 'mask' invalid: 0x{:X}, 0x{:X}", mask.value, value.value);
return; return;
} }
auto chunk = state.os->memory.GetChunk(address); auto chunk = state.os->memory.GetChunk(address);
auto block = state.os->memory.GetBlock(address); auto block = state.os->memory.GetBlock(address);
if (!chunk || !block) { if (!chunk || !block) {
@ -51,37 +55,44 @@ namespace skyline::kernel::svc {
state.logger->Warn("svcSetMemoryAttribute: Cannot find memory region: 0x{:X}", address); state.logger->Warn("svcSetMemoryAttribute: Cannot find memory region: 0x{:X}", address);
return; return;
} }
if (!chunk->state.attributeChangeAllowed) { if (!chunk->state.attributeChangeAllowed) {
state.ctx->registers.w0 = constant::status::InvState; state.ctx->registers.w0 = constant::status::InvState;
state.logger->Warn("svcSetMemoryAttribute: Attribute change not allowed for chunk: 0x{:X}", address); state.logger->Warn("svcSetMemoryAttribute: Attribute change not allowed for chunk: 0x{:X}", address);
return; return;
} }
block->attributes.isUncached = value.isUncached; block->attributes.isUncached = value.isUncached;
MemoryManager::InsertBlock(chunk, *block); MemoryManager::InsertBlock(chunk, *block);
state.logger->Debug("svcSetMemoryAttribute: Set caching to {} at 0x{:X} for 0x{:X} bytes", !block->attributes.isUncached, address, size); 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; state.ctx->registers.w0 = constant::status::Success;
} }
void MapMemory(DeviceState &state) { void MapMemory(DeviceState &state) {
const u64 destination = state.ctx->registers.x0; auto destination = state.ctx->registers.x0;
const u64 source = state.ctx->registers.x1; auto source = state.ctx->registers.x1;
const u64 size = state.ctx->registers.x2; auto size = state.ctx->registers.x2;
if (!utils::PageAligned(destination) || !utils::PageAligned(source)) { if (!utils::PageAligned(destination) || !utils::PageAligned(source)) {
state.ctx->registers.w0 = constant::status::InvAddress; 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); state.logger->Warn("svcMapMemory: Addresses not page aligned: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
return; return;
} }
if (!utils::PageAligned(size)) { if (!utils::PageAligned(size)) {
state.ctx->registers.w0 = constant::status::InvSize; state.ctx->registers.w0 = constant::status::InvSize;
state.logger->Warn("svcMapMemory: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size); state.logger->Warn("svcMapMemory: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size);
return; return;
} }
auto stack = state.os->memory.GetRegion(memory::Regions::Stack); auto stack = state.os->memory.GetRegion(memory::Regions::Stack);
if (!stack.IsInside(destination)) { if (!stack.IsInside(destination)) {
state.ctx->registers.w0 = constant::status::InvMemRange; 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); state.logger->Warn("svcMapMemory: Destination not within stack region: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
return; return;
} }
auto descriptor = state.os->memory.Get(source); auto descriptor = state.os->memory.Get(source);
if (!descriptor) { if (!descriptor) {
state.ctx->registers.w0 = constant::status::InvAddress; 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); 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; return;
} }
state.process->NewHandle<type::KPrivateMemory>(destination, size, descriptor->block.permission, memory::states::Stack); state.process->NewHandle<type::KPrivateMemory>(destination, size, descriptor->block.permission, memory::states::Stack);
state.process->CopyMemory(source, destination, size); state.process->CopyMemory(source, destination, size);
auto object = state.process->GetMemoryObject(source); auto object = state.process->GetMemoryObject(source);
if (!object) if (!object)
throw exception("svcMapMemory: Cannot find memory object in handle table for address 0x{:X}", source); throw exception("svcMapMemory: Cannot find memory object in handle table for address 0x{:X}", source);
object->item->UpdatePermission(source, size, {false, false, false}); 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.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; state.ctx->registers.w0 = constant::status::Success;
} }
void UnmapMemory(DeviceState &state) { void UnmapMemory(DeviceState &state) {
const u64 source = state.ctx->registers.x0; auto source = state.ctx->registers.x0;
const u64 destination = state.ctx->registers.x1; auto destination = state.ctx->registers.x1;
const u64 size = state.ctx->registers.x2; auto size = state.ctx->registers.x2;
if (!utils::PageAligned(destination) || !utils::PageAligned(source)) { if (!utils::PageAligned(destination) || !utils::PageAligned(source)) {
state.ctx->registers.w0 = constant::status::InvAddress; 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); state.logger->Warn("svcUnmapMemory: Addresses not page aligned: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
return; return;
} }
if (!utils::PageAligned(size)) { if (!utils::PageAligned(size)) {
state.ctx->registers.w0 = constant::status::InvSize; state.ctx->registers.w0 = constant::status::InvSize;
state.logger->Warn("svcUnmapMemory: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size); state.logger->Warn("svcUnmapMemory: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size);
return; return;
} }
auto stack = state.os->memory.GetRegion(memory::Regions::Stack); auto stack = state.os->memory.GetRegion(memory::Regions::Stack);
if (!stack.IsInside(source)) { if (!stack.IsInside(source)) {
state.ctx->registers.w0 = constant::status::InvMemRange; 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); state.logger->Warn("svcUnmapMemory: Source not within stack region: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
return; return;
} }
auto sourceDesc = state.os->memory.Get(source); auto sourceDesc = state.os->memory.Get(source);
auto destDesc = state.os->memory.Get(destination); auto destDesc = state.os->memory.Get(destination);
if (!sourceDesc || !destDesc) { 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); state.logger->Warn("svcUnmapMemory: Addresses have no descriptor: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
return; return;
} }
if (!destDesc->chunk.state.mapAllowed) { if (!destDesc->chunk.state.mapAllowed) {
state.ctx->registers.w0 = constant::status::InvState; 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); 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; return;
} }
auto destObject = state.process->GetMemoryObject(destination); auto destObject = state.process->GetMemoryObject(destination);
if (!destObject) if (!destObject)
throw exception("svcUnmapMemory: Cannot find destination memory object in handle table for address 0x{:X}", destination); throw exception("svcUnmapMemory: Cannot find destination memory object in handle table for address 0x{:X}", destination);
destObject->item->UpdatePermission(destination, size, sourceDesc->block.permission); destObject->item->UpdatePermission(destination, size, sourceDesc->block.permission);
state.process->CopyMemory(destination, source, size); state.process->CopyMemory(destination, source, size);
auto sourceObject = state.process->GetMemoryObject(destination); auto sourceObject = state.process->GetMemoryObject(destination);
if (!sourceObject) if (!sourceObject)
throw exception("svcUnmapMemory: Cannot find source memory object in handle table for address 0x{:X}", source); throw exception("svcUnmapMemory: Cannot find source memory object in handle table for address 0x{:X}", source);
state.process->DeleteHandle(sourceObject->handle); 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.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; state.ctx->registers.w0 = constant::status::Success;
} }
void QueryMemory(DeviceState &state) { void QueryMemory(DeviceState &state) {
u64 address = state.ctx->registers.x2;
memory::MemoryInfo memInfo{}; memory::MemoryInfo memInfo{};
auto address = state.ctx->registers.x2;
auto descriptor = state.os->memory.Get(address); auto descriptor = state.os->memory.Get(address);
if (descriptor) { if (descriptor) {
memInfo = { memInfo = {
.address = descriptor->block.address, .address = descriptor->block.address,
@ -162,18 +190,23 @@ namespace skyline::kernel::svc {
.deviceRefCount = 0, .deviceRefCount = 0,
.ipcRefCount = 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" : "-"); 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 { } else {
auto region = state.os->memory.GetRegion(memory::Regions::Base); auto region = state.os->memory.GetRegion(memory::Regions::Base);
auto baseEnd = region.address + region.size; auto baseEnd = region.address + region.size;
memInfo = { memInfo = {
.address = region.address, .address = region.address,
.size = ~baseEnd + 1, .size = ~baseEnd + 1,
.type = static_cast<u32>(memory::MemoryType::Unmapped), .type = static_cast<u32>(memory::MemoryType::Unmapped),
}; };
state.logger->Debug("svcQueryMemory: Cannot find block of address: 0x{:X}", address); state.logger->Debug("svcQueryMemory: Cannot find block of address: 0x{:X}", address);
} }
state.process->WriteMemory(memInfo, state.ctx->registers.x0); state.process->WriteMemory(memInfo, state.ctx->registers.x0);
state.ctx->registers.w0 = constant::status::Success; state.ctx->registers.w0 = constant::status::Success;
} }
@ -187,13 +220,16 @@ namespace skyline::kernel::svc {
u64 entryArgument = state.ctx->registers.x2; u64 entryArgument = state.ctx->registers.x2;
u64 stackTop = state.ctx->registers.x3; u64 stackTop = state.ctx->registers.x3;
u8 priority = static_cast<u8>(state.ctx->registers.w4); u8 priority = static_cast<u8>(state.ctx->registers.w4);
if ((priority < constant::SwitchPriority.first) || (priority > constant::SwitchPriority.second)) { if ((priority < constant::SwitchPriority.first) || (priority > constant::SwitchPriority.second)) {
state.ctx->registers.w0 = constant::status::InvAddress; state.ctx->registers.w0 = constant::status::InvAddress;
state.logger->Warn("svcCreateThread: 'priority' invalid: {}", priority); state.logger->Warn("svcCreateThread: 'priority' invalid: {}", priority);
return; return;
} }
auto thread = state.process->CreateThread(entryAddress, entryArgument, stackTop, priority); 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.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.w1 = thread->handle;
state.ctx->registers.w0 = constant::status::Success; state.ctx->registers.w0 = constant::status::Success;
} }
@ -218,6 +254,7 @@ namespace skyline::kernel::svc {
void SleepThread(DeviceState &state) { void SleepThread(DeviceState &state) {
auto in = state.ctx->registers.x0; auto in = state.ctx->registers.x0;
switch (in) { switch (in) {
case 0: case 0:
case 1: case 1:
@ -239,6 +276,7 @@ namespace skyline::kernel::svc {
try { try {
auto priority = state.process->GetHandle<type::KThread>(handle)->priority; auto priority = state.process->GetHandle<type::KThread>(handle)->priority;
state.logger->Debug("svcGetThreadPriority: Writing thread priority {}", priority); state.logger->Debug("svcGetThreadPriority: Writing thread priority {}", priority);
state.ctx->registers.w1 = priority; state.ctx->registers.w1 = priority;
state.ctx->registers.w0 = constant::status::Success; state.ctx->registers.w0 = constant::status::Success;
} catch (const std::exception &) { } catch (const std::exception &) {
@ -250,6 +288,7 @@ namespace skyline::kernel::svc {
void SetThreadPriority(DeviceState &state) { void SetThreadPriority(DeviceState &state) {
auto handle = state.ctx->registers.w0; auto handle = state.ctx->registers.w0;
auto priority = state.ctx->registers.w1; auto priority = state.ctx->registers.w1;
try { try {
state.logger->Debug("svcSetThreadPriority: Setting thread priority to {}", priority); state.logger->Debug("svcSetThreadPriority: Setting thread priority to {}", priority);
state.process->GetHandle<type::KThread>(handle)->UpdatePriority(static_cast<u8>(priority)); state.process->GetHandle<type::KThread>(handle)->UpdatePriority(static_cast<u8>(priority));
@ -264,17 +303,20 @@ namespace skyline::kernel::svc {
try { try {
auto object = state.process->GetHandle<type::KSharedMemory>(state.ctx->registers.w0); auto object = state.process->GetHandle<type::KSharedMemory>(state.ctx->registers.w0);
u64 address = state.ctx->registers.x1; u64 address = state.ctx->registers.x1;
if (!utils::PageAligned(address)) { if (!utils::PageAligned(address)) {
state.ctx->registers.w0 = constant::status::InvAddress; state.ctx->registers.w0 = constant::status::InvAddress;
state.logger->Warn("svcMapSharedMemory: 'address' not page aligned: 0x{:X}", address); state.logger->Warn("svcMapSharedMemory: 'address' not page aligned: 0x{:X}", address);
return; return;
} }
const u64 size = state.ctx->registers.x2;
auto size = state.ctx->registers.x2;
if (!utils::PageAligned(size)) { if (!utils::PageAligned(size)) {
state.ctx->registers.w0 = constant::status::InvSize; state.ctx->registers.w0 = constant::status::InvSize;
state.logger->Warn("svcMapSharedMemory: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size); state.logger->Warn("svcMapSharedMemory: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size);
return; return;
} }
u32 perm = state.ctx->registers.w3; u32 perm = state.ctx->registers.w3;
memory::Permission permission = *reinterpret_cast<memory::Permission *>(&perm); memory::Permission permission = *reinterpret_cast<memory::Permission *>(&perm);
if ((permission.w && !permission.r) || (permission.x && !permission.r)) { if ((permission.w && !permission.r) || (permission.x && !permission.r)) {
@ -282,8 +324,11 @@ namespace skyline::kernel::svc {
state.ctx->registers.w0 = constant::status::InvPermission; state.ctx->registers.w0 = constant::status::InvPermission;
return; return;
} }
state.logger->Debug("svcMapSharedMemory: Mapping shared memory at 0x{:X} for {} bytes ({}{}{})", address, size, permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-"); 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); object->Map(address, size, permission);
state.ctx->registers.w0 = constant::status::Success; state.ctx->registers.w0 = constant::status::Success;
} catch (const std::exception &) { } catch (const std::exception &) {
state.logger->Warn("svcMapSharedMemory: 'handle' invalid: 0x{:X}", state.ctx->registers.w0); 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); state.logger->Warn("svcCreateTransferMemory: 'address' not page aligned: 0x{:X}", address);
return; return;
} }
u64 size = state.ctx->registers.x2; u64 size = state.ctx->registers.x2;
if (!utils::PageAligned(size)) { if (!utils::PageAligned(size)) {
state.ctx->registers.w0 = constant::status::InvSize; state.ctx->registers.w0 = constant::status::InvSize;
state.logger->Warn("svcCreateTransferMemory: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size); state.logger->Warn("svcCreateTransferMemory: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size);
return; return;
} }
u32 perm = state.ctx->registers.w3; u32 perm = state.ctx->registers.w3;
memory::Permission permission = *reinterpret_cast<memory::Permission *>(&perm); memory::Permission permission = *reinterpret_cast<memory::Permission *>(&perm);
if ((permission.w && !permission.r) || (permission.x && !permission.r)) { if ((permission.w && !permission.r) || (permission.x && !permission.r)) {
@ -311,8 +358,11 @@ namespace skyline::kernel::svc {
state.ctx->registers.w0 = constant::status::InvPermission; state.ctx->registers.w0 = constant::status::InvPermission;
return; return;
} }
state.logger->Debug("svcCreateTransferMemory: Creating transfer memory at 0x{:X} for {} bytes ({}{}{})", address, size, permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-"); 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); auto shmem = state.process->NewHandle<type::KTransferMemory>(state.process->pid, address, size, permission);
state.ctx->registers.w0 = constant::status::Success; state.ctx->registers.w0 = constant::status::Success;
state.ctx->registers.w1 = shmem.handle; state.ctx->registers.w1 = shmem.handle;
} }
@ -337,15 +387,18 @@ namespace skyline::kernel::svc {
case (type::KType::KEvent): case (type::KType::KEvent):
std::static_pointer_cast<type::KEvent>(object)->ResetSignal(); std::static_pointer_cast<type::KEvent>(object)->ResetSignal();
break; break;
case (type::KType::KProcess): case (type::KType::KProcess):
std::static_pointer_cast<type::KProcess>(object)->ResetSignal(); std::static_pointer_cast<type::KProcess>(object)->ResetSignal();
break; break;
default: { default: {
state.logger->Warn("svcResetSignal: 'handle' type invalid: 0x{:X} ({})", handle, object->objectType); state.logger->Warn("svcResetSignal: 'handle' type invalid: 0x{:X} ({})", handle, object->objectType);
state.ctx->registers.w0 = constant::status::InvHandle; state.ctx->registers.w0 = constant::status::InvHandle;
return; return;
} }
} }
state.logger->Debug("svcResetSignal: Resetting signal: 0x{:X}", handle); state.logger->Debug("svcResetSignal: Resetting signal: 0x{:X}", handle);
state.ctx->registers.w0 = constant::status::Success; state.ctx->registers.w0 = constant::status::Success;
} catch (const std::out_of_range &) { } catch (const std::out_of_range &) {
@ -357,6 +410,7 @@ namespace skyline::kernel::svc {
void WaitSynchronization(DeviceState &state) { void WaitSynchronization(DeviceState &state) {
constexpr auto maxSyncHandles = 0x40; // The total amount of handles that can be passed to WaitSynchronization constexpr auto maxSyncHandles = 0x40; // The total amount of handles that can be passed to WaitSynchronization
auto numHandles = state.ctx->registers.w2; auto numHandles = state.ctx->registers.w2;
if (numHandles > maxSyncHandles) { if (numHandles > maxSyncHandles) {
state.ctx->registers.w0 = constant::status::MaxHandles; state.ctx->registers.w0 = constant::status::MaxHandles;
@ -373,7 +427,6 @@ namespace skyline::kernel::svc {
handleStr += fmt::format("* 0x{:X}\n", handle); handleStr += fmt::format("* 0x{:X}\n", handle);
auto object = state.process->handles.at(handle); auto object = state.process->handles.at(handle);
switch (object->objectType) { switch (object->objectType) {
case type::KType::KProcess: case type::KType::KProcess:
case type::KType::KThread: 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); state.logger->Debug("svcWaitSynchronization: Waiting on handles:\n{}Timeout: 0x{:X} ns", handleStr, timeout);
auto start = utils::GetTimeNs(); auto start = utils::GetTimeNs();
while (true) { while (true) {
if (state.thread->cancelSync) { if (state.thread->cancelSync) {
state.thread->cancelSync = false; state.thread->cancelSync = false;
@ -437,15 +489,19 @@ namespace skyline::kernel::svc {
state.ctx->registers.w0 = constant::status::InvAddress; state.ctx->registers.w0 = constant::status::InvAddress;
return; return;
} }
auto ownerHandle = state.ctx->registers.w0; auto ownerHandle = state.ctx->registers.w0;
auto requesterHandle = state.ctx->registers.w2; auto requesterHandle = state.ctx->registers.w2;
if (requesterHandle != state.thread->handle) if (requesterHandle != state.thread->handle)
throw exception("svcWaitProcessWideKeyAtomic: Handle doesn't match current thread: 0x{:X} for thread 0x{:X}", 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); state.logger->Debug("svcArbitrateLock: Locking mutex at 0x{:X}", address);
if (state.process->MutexLock(address, ownerHandle)) if (state.process->MutexLock(address, ownerHandle))
state.logger->Debug("svcArbitrateLock: Locked mutex at 0x{:X}", address); state.logger->Debug("svcArbitrateLock: Locked mutex at 0x{:X}", address);
else else
state.logger->Debug("svcArbitrateLock: Owner handle did not match current owner for mutex at 0x{:X}", address); state.logger->Debug("svcArbitrateLock: Owner handle did not match current owner for mutex at 0x{:X}", address);
state.ctx->registers.w0 = constant::status::Success; state.ctx->registers.w0 = constant::status::Success;
} }
@ -456,7 +512,9 @@ namespace skyline::kernel::svc {
state.ctx->registers.w0 = constant::status::InvAddress; state.ctx->registers.w0 = constant::status::InvAddress;
return; return;
} }
state.logger->Debug("svcArbitrateUnlock: Unlocking mutex at 0x{:X}", address); state.logger->Debug("svcArbitrateUnlock: Unlocking mutex at 0x{:X}", address);
if (state.process->MutexUnlock(address)) { if (state.process->MutexUnlock(address)) {
state.logger->Debug("svcArbitrateUnlock: Unlocked mutex at 0x{:X}", address); state.logger->Debug("svcArbitrateUnlock: Unlocked mutex at 0x{:X}", address);
state.ctx->registers.w0 = constant::status::Success; state.ctx->registers.w0 = constant::status::Success;
@ -473,17 +531,21 @@ namespace skyline::kernel::svc {
state.ctx->registers.w0 = constant::status::InvAddress; state.ctx->registers.w0 = constant::status::InvAddress;
return; return;
} }
auto condAddress = state.ctx->registers.x1; auto condAddress = state.ctx->registers.x1;
auto handle = state.ctx->registers.w2; auto handle = state.ctx->registers.w2;
if (handle != state.thread->handle) if (handle != state.thread->handle)
throw exception("svcWaitProcessWideKeyAtomic: Handle doesn't match current thread: 0x{:X} for thread 0x{:X}", 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)) { if (!state.process->MutexUnlock(mtxAddress)) {
state.logger->Debug("WaitProcessWideKeyAtomic: A non-owner thread tried to release a mutex at 0x{:X}", 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; state.ctx->registers.w0 = constant::status::InvAddress;
return; return;
} }
auto timeout = state.ctx->registers.x3; auto timeout = state.ctx->registers.x3;
state.logger->Debug("svcWaitProcessWideKeyAtomic: Mutex: 0x{:X}, Conditional-Variable: 0x{:X}, Timeout: {} ns", mtxAddress, condAddress, timeout); state.logger->Debug("svcWaitProcessWideKeyAtomic: Mutex: 0x{:X}, Conditional-Variable: 0x{:X}, Timeout: {} ns", mtxAddress, condAddress, timeout);
if (state.process->ConditionalVariableWait(condAddress, mtxAddress, timeout)) { if (state.process->ConditionalVariableWait(condAddress, mtxAddress, timeout)) {
state.logger->Debug("svcWaitProcessWideKeyAtomic: Waited for conditional variable and relocked mutex"); state.logger->Debug("svcWaitProcessWideKeyAtomic: Waited for conditional variable and relocked mutex");
state.ctx->registers.w0 = constant::status::Success; state.ctx->registers.w0 = constant::status::Success;
@ -496,6 +558,7 @@ namespace skyline::kernel::svc {
void SignalProcessWideKey(DeviceState &state) { void SignalProcessWideKey(DeviceState &state) {
auto address = state.ctx->registers.x0; auto address = state.ctx->registers.x0;
auto count = state.ctx->registers.w1; auto count = state.ctx->registers.w1;
state.logger->Debug("svcSignalProcessWideKey: Signalling Conditional-Variable at 0x{:X} for {}", address, count); state.logger->Debug("svcSignalProcessWideKey: Signalling Conditional-Variable at 0x{:X} for {}", address, count);
state.process->ConditionalVariableSignal(address, count); state.process->ConditionalVariableSignal(address, count);
state.ctx->registers.w0 = constant::status::Success; state.ctx->registers.w0 = constant::status::Success;
@ -556,8 +619,10 @@ namespace skyline::kernel::svc {
void OutputDebugString(DeviceState &state) { void OutputDebugString(DeviceState &state) {
auto debug = state.process->GetString(state.ctx->registers.x0, state.ctx->registers.x1); auto debug = state.process->GetString(state.ctx->registers.x0, state.ctx->registers.x1);
if (debug.back() == '\n') if (debug.back() == '\n')
debug.pop_back(); debug.pop_back();
state.logger->Info("Debug Output: {}", debug); state.logger->Info("Debug Output: {}", debug);
state.ctx->registers.w0 = constant::status::Success; state.ctx->registers.w0 = constant::status::Success;
} }
@ -567,10 +632,10 @@ namespace skyline::kernel::svc {
auto handle = state.ctx->registers.w2; auto handle = state.ctx->registers.w2;
auto id1 = state.ctx->registers.x3; auto id1 = state.ctx->registers.x3;
constexpr auto totalPhysicalMemory = 0xF8000000; // ~4 GB of RAM
u64 out{}; u64 out{};
constexpr auto totalPhysicalMemory = 0xF8000000; // ~4 GB of RAM
switch (id0) { switch (id0) {
case constant::infoState::AllowedCpuIdBitmask: case constant::infoState::AllowedCpuIdBitmask:
case constant::infoState::AllowedThreadPriorityMask: case constant::infoState::AllowedThreadPriorityMask:

View File

@ -4,6 +4,9 @@
#include "KObject.h" #include "KObject.h"
namespace skyline::kernel::type { namespace skyline::kernel::type {
/**
* @brief The base kernel memory object that other memory classes derieve from
*/
class KMemory : public KObject { class KMemory : public KObject {
public: public:
KMemory(const DeviceState &state, KType objectType) : KObject(state, objectType) {} KMemory(const DeviceState &state, KType objectType) : KObject(state, objectType) {}

View File

@ -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) { 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)) if (address && !utils::PageAligned(address))
throw exception("KPrivateMemory was created with non-page-aligned address: 0x{:X}", address); throw exception("KPrivateMemory was created with non-page-aligned address: 0x{:X}", address);
fd = ASharedMemory_create("KPrivateMemory", size); fd = ASharedMemory_create("KPrivateMemory", size);
if (fd < 0) if (fd < 0)
throw exception("An error occurred while creating shared memory: {}", fd); 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); auto host = mmap(nullptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, fd, 0);
if (host == MAP_FAILED) if (host == MAP_FAILED)
throw exception("An occurred while mapping shared memory: {}", strerror(errno)); throw exception("An occurred while mapping shared memory: {}", strerror(errno));
Registers fregs{ Registers fregs{
.x0 = address, .x0 = address,
.x1 = size, .x1 = size,
@ -23,10 +26,13 @@ namespace skyline::kernel::type {
.x4 = static_cast<u64>(fd), .x4 = static_cast<u64>(fd),
.x8 = __NR_mmap, .x8 = __NR_mmap,
}; };
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while mapping private memory in child process"); throw exception("An error occurred while mapping private memory in child process");
this->address = fregs.x0; this->address = fregs.x0;
BlockDescriptor block{ BlockDescriptor block{
.address = fregs.x0, .address = fregs.x0,
.size = size, .size = size,
@ -45,17 +51,21 @@ namespace skyline::kernel::type {
void KPrivateMemory::Resize(size_t nSize) { void KPrivateMemory::Resize(size_t nSize) {
if (close(fd) < 0) if (close(fd) < 0)
throw exception("An error occurred while trying to close shared memory FD: {}", strerror(errno)); throw exception("An error occurred while trying to close shared memory FD: {}", strerror(errno));
fd = ASharedMemory_create("KPrivateMemory", nSize); fd = ASharedMemory_create("KPrivateMemory", nSize);
if (fd < 0) if (fd < 0)
throw exception("An error occurred while creating shared memory: {}", fd); throw exception("An error occurred while creating shared memory: {}", fd);
Registers fregs{ Registers fregs{
.x0 = address, .x0 = address,
.x1 = size, .x1 = size,
.x8 = __NR_munmap .x8 = __NR_munmap
}; };
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while unmapping private memory in child process"); throw exception("An error occurred while unmapping private memory in child process");
fregs = { fregs = {
.x0 = address, .x0 = address,
.x1 = nSize, .x1 = nSize,
@ -64,11 +74,14 @@ namespace skyline::kernel::type {
.x4 = static_cast<u64>(fd), .x4 = static_cast<u64>(fd),
.x8 = __NR_mmap, .x8 = __NR_mmap,
}; };
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while remapping private memory in child process"); throw exception("An error occurred while remapping private memory in child process");
auto chunk = state.os->memory.GetChunk(address); auto chunk = state.os->memory.GetChunk(address);
state.process->WriteMemory(reinterpret_cast<void *>(chunk->host), address, std::min(nSize, size), true); state.process->WriteMemory(reinterpret_cast<void *>(chunk->host), address, std::min(nSize, size), true);
for (const auto &block : chunk->blockList) { for (const auto &block : chunk->blockList) {
if ((block.address - chunk->address) < size) { if ((block.address - chunk->address) < size) {
fregs = { fregs = {
@ -77,6 +90,7 @@ namespace skyline::kernel::type {
.x2 = static_cast<u64>(block.permission.Get()), .x2 = static_cast<u64>(block.permission.Get()),
.x8 = __NR_mprotect, .x8 = __NR_mprotect,
}; };
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while updating private memory's permissions in child process"); throw exception("An error occurred while updating private memory's permissions in child process");
@ -84,10 +98,13 @@ namespace skyline::kernel::type {
break; break;
} }
} }
munmap(reinterpret_cast<void *>(chunk->host), size); 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); auto host = mmap(reinterpret_cast<void *>(chunk->host), nSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, fd, 0);
if (host == MAP_FAILED) if (host == MAP_FAILED)
throw exception("An occurred while mapping shared memory: {}", strerror(errno)); throw exception("An occurred while mapping shared memory: {}", strerror(errno));
chunk->host = reinterpret_cast<u64>(host); chunk->host = reinterpret_cast<u64>(host);
MemoryManager::ResizeChunk(chunk, nSize); MemoryManager::ResizeChunk(chunk, nSize);
size = nSize; size = nSize;
@ -100,9 +117,11 @@ namespace skyline::kernel::type {
.x2 = static_cast<u64>(permission.Get()), .x2 = static_cast<u64>(permission.Get()),
.x8 = __NR_mprotect, .x8 = __NR_mprotect,
}; };
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while updating private memory's permissions in child process"); throw exception("An error occurred while updating private memory's permissions in child process");
auto chunk = state.os->memory.GetChunk(address); auto chunk = state.os->memory.GetChunk(address);
BlockDescriptor block{ BlockDescriptor block{
.address = address, .address = address,
@ -124,6 +143,7 @@ namespace skyline::kernel::type {
} }
} catch (const std::exception &) { } catch (const std::exception &) {
} }
auto chunk = state.os->memory.GetChunk(address); auto chunk = state.os->memory.GetChunk(address);
if (chunk) { if (chunk) {
munmap(reinterpret_cast<void *>(chunk->host), chunk->size); munmap(reinterpret_cast<void *>(chunk->host), chunk->size);

View File

@ -13,6 +13,7 @@ namespace skyline::kernel::type {
u64 KProcess::TlsPage::ReserveSlot() { u64 KProcess::TlsPage::ReserveSlot() {
if (Full()) if (Full())
throw exception("Trying to get TLS slot from full page"); throw exception("Trying to get TLS slot from full page");
slot[index] = true; slot[index] = true;
return Get(index++); // ++ on right will cause increment after evaluation of expression 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) { u64 KProcess::TlsPage::Get(u8 slotNo) {
if (slotNo >= constant::TlsSlots) if (slotNo >= constant::TlsSlots)
throw exception("TLS slot is out of range"); throw exception("TLS slot is out of range");
return address + (constant::TlsSlotSize * slotNo); return address + (constant::TlsSlotSize * slotNo);
} }
@ -31,6 +33,7 @@ namespace skyline::kernel::type {
for (auto &tlsPage: tlsPages) for (auto &tlsPage: tlsPages)
if (!tlsPage->Full()) if (!tlsPage->Full())
return tlsPage->ReserveSlot(); return tlsPage->ReserveSlot();
u64 address; u64 address;
if (tlsPages.empty()) { if (tlsPages.empty()) {
auto region = state.os->memory.GetRegion(memory::Regions::TlsIo); auto region = state.os->memory.GetRegion(memory::Regions::TlsIo);
@ -38,6 +41,7 @@ namespace skyline::kernel::type {
} else { } else {
address = (*(tlsPages.end() - 1))->address + PAGE_SIZE; address = (*(tlsPages.end() - 1))->address + PAGE_SIZE;
} }
auto tlsMem = NewHandle<KPrivateMemory>(address, PAGE_SIZE, memory::Permission(true, true, false), memory::states::ThreadLocal).item; 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)); 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) { 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 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); 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; Registers fregs{
fregs.x1 = stackTop; .x0 = CLONE_THREAD | CLONE_SIGHAND | CLONE_PTRACE | CLONE_FS | CLONE_VM | CLONE_FILES | CLONE_IO,
fregs.x3 = tlsMem->Map(0, size, memory::Permission{true, true, false}); .x1 = stackTop,
fregs.x8 = __NR_clone; .x3 = tlsMem->Map(0, size, memory::Permission{ true, true, false }),
fregs.x5 = reinterpret_cast<u64>(&guest::GuestEntry); .x8 = __NR_clone,
fregs.x6 = entryPoint; .x5 = reinterpret_cast<u64>(&guest::GuestEntry),
.x6 = entryPoint,
};
state.nce->ExecuteFunction(ThreadCall::Clone, fregs); state.nce->ExecuteFunction(ThreadCall::Clone, fregs);
if (static_cast<int>(fregs.x0) < 0) if (static_cast<int>(fregs.x0) < 0)
throw exception("Cannot create thread: Address: 0x{:X}, Stack Top: 0x{:X}", entryPoint, stackTop); throw exception("Cannot create thread: Address: 0x{:X}, Stack Top: 0x{:X}", entryPoint, stackTop);
auto pid = static_cast<pid_t>(fregs.x0); auto pid = static_cast<pid_t>(fregs.x0);
auto process = NewHandle<KThread>(pid, entryPoint, entryArg, stackTop, GetTlsSlot(), priority, this, tlsMem).item; auto process = NewHandle<KThread>(pid, entryPoint, entryArg, stackTop, GetTlsSlot(), priority, this, tlsMem).item;
threads[pid] = process; threads[pid] = process;
return 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 { void KProcess::ReadMemory(void *destination, const u64 offset, const size_t size, const bool forceGuest) const {
if (!forceGuest) { if (!forceGuest) {
auto source = GetHostAddress(offset); auto source = GetHostAddress(offset);
if (source) { if (source) {
memcpy(destination, reinterpret_cast<void *>(source), size); memcpy(destination, reinterpret_cast<void *>(source), size);
return; return;
} }
} }
struct iovec local{ struct iovec local{
.iov_base = destination, .iov_base = destination,
.iov_len = size, .iov_len = size,
}; };
struct iovec remote{ struct iovec remote{
.iov_base = reinterpret_cast<void *>(offset), .iov_base = reinterpret_cast<void *>(offset),
.iov_len = size, .iov_len = size,
}; };
if (process_vm_readv(pid, &local, 1, &remote, 1, 0) < 0) if (process_vm_readv(pid, &local, 1, &remote, 1, 0) < 0)
pread64(memFd, destination, size, offset); 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 { void KProcess::WriteMemory(void *source, const u64 offset, const size_t size, const bool forceGuest) const {
if (!forceGuest) { if (!forceGuest) {
auto destination = GetHostAddress(offset); auto destination = GetHostAddress(offset);
if (destination) { if (destination) {
memcpy(reinterpret_cast<void *>(destination), source, size); memcpy(reinterpret_cast<void *>(destination), source, size);
return; return;
} }
} }
struct iovec local{ struct iovec local{
.iov_base = source, .iov_base = source,
.iov_len = size, .iov_len = size,
}; };
struct iovec remote{ struct iovec remote{
.iov_base = reinterpret_cast<void *>(offset), .iov_base = reinterpret_cast<void *>(offset),
.iov_len = size, .iov_len = size,
}; };
if (process_vm_writev(pid, &local, 1, &remote, 1, 0) < 0) if (process_vm_writev(pid, &local, 1, &remote, 1, 0) < 0)
pwrite64(memFd, source, size, offset); pwrite64(memFd, source, size, offset);
} }
@ -138,18 +155,22 @@ namespace skyline::kernel::type {
void KProcess::CopyMemory(u64 source, u64 destination, size_t size) const { void KProcess::CopyMemory(u64 source, u64 destination, size_t size) const {
auto sourceHost = GetHostAddress(source); auto sourceHost = GetHostAddress(source);
auto destinationHost = GetHostAddress(destination); auto destinationHost = GetHostAddress(destination);
if (sourceHost && destinationHost) { if (sourceHost && destinationHost) {
memcpy(reinterpret_cast<void *>(destinationHost), reinterpret_cast<const void *>(sourceHost), size); memcpy(reinterpret_cast<void *>(destinationHost), reinterpret_cast<const void *>(sourceHost), size);
} else { } else {
if (size <= PAGE_SIZE) { if (size <= PAGE_SIZE) {
std::vector<u8> buffer(size); std::vector<u8> buffer(size);
state.process->ReadMemory(buffer.data(), source, size); state.process->ReadMemory(buffer.data(), source, size);
state.process->WriteMemory(buffer.data(), destination, size); state.process->WriteMemory(buffer.data(), destination, size);
} else { } else {
Registers fregs{}; Registers fregs{
fregs.x0 = source; .x0 = source,
fregs.x1 = destination; .x1 = destination,
fregs.x2 = size; .x2 = size,
};
state.nce->ExecuteFunction(ThreadCall::Memcopy, fregs); state.nce->ExecuteFunction(ThreadCall::Memcopy, fregs);
} }
} }
@ -169,52 +190,64 @@ namespace skyline::kernel::type {
break; break;
} }
} }
return std::nullopt; return std::nullopt;
} }
bool KProcess::MutexLock(u64 address, KHandle owner) { bool KProcess::MutexLock(u64 address, KHandle owner) {
std::unique_lock lock(mutexLock); std::unique_lock lock(mutexLock);
auto mtx = GetPointer<u32>(address); auto mtx = GetPointer<u32>(address);
auto &mtxWaiters = mutexes[address]; auto &mtxWaiters = mutexes[address];
u32 mtxExpected = 0; 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)) 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; return true;
if (owner && (__atomic_load_n(mtx, __ATOMIC_SEQ_CST) != (owner | ~constant::MtxOwnerMask))) if (owner && (__atomic_load_n(mtx, __ATOMIC_SEQ_CST) != (owner | ~constant::MtxOwnerMask)))
return false; return false;
std::shared_ptr<WaitStatus> status; std::shared_ptr<WaitStatus> status;
for (auto it = mtxWaiters.begin();; ++it) { for (auto it = mtxWaiters.begin();; ++it) {
if (it != mtxWaiters.end() && (*it)->priority >= state.thread->priority) if (it != mtxWaiters.end() && (*it)->priority >= state.thread->priority)
continue; continue;
status = std::make_shared<WaitStatus>(state.thread->priority, state.thread->handle); status = std::make_shared<WaitStatus>(state.thread->priority, state.thread->handle);
mtxWaiters.insert(it, status); mtxWaiters.insert(it, status);
break; break;
} }
lock.unlock(); lock.unlock();
while (!status->flag); while (!status->flag);
lock.lock(); lock.lock();
status->flag = false; status->flag = false;
for (auto it = mtxWaiters.begin(); it != mtxWaiters.end(); ++it) { for (auto it = mtxWaiters.begin(); it != mtxWaiters.end(); ++it) {
if ((*it)->handle == state.thread->handle) { if ((*it)->handle == state.thread->handle) {
mtxWaiters.erase(it); mtxWaiters.erase(it);
break; break;
} }
} }
return true; return true;
} }
bool KProcess::MutexUnlock(u64 address) { bool KProcess::MutexUnlock(u64 address) {
std::unique_lock lock(mutexLock); std::unique_lock lock(mutexLock);
auto mtx = GetPointer<u32>(address); auto mtx = GetPointer<u32>(address);
auto &mtxWaiters = mutexes[address]; auto &mtxWaiters = mutexes[address];
u32 mtxDesired{}; u32 mtxDesired{};
if (!mtxWaiters.empty()) if (!mtxWaiters.empty())
mtxDesired = (*mtxWaiters.begin())->handle | ((mtxWaiters.size() > 1) ? ~constant::MtxOwnerMask : 0); mtxDesired = (*mtxWaiters.begin())->handle | ((mtxWaiters.size() > 1) ? ~constant::MtxOwnerMask : 0);
u32 mtxExpected = (constant::MtxOwnerMask & state.thread->handle) | ~constant::MtxOwnerMask; u32 mtxExpected = (constant::MtxOwnerMask & state.thread->handle) | ~constant::MtxOwnerMask;
if (!__atomic_compare_exchange_n(mtx, &mtxExpected, mtxDesired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { if (!__atomic_compare_exchange_n(mtx, &mtxExpected, mtxDesired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
mtxExpected = constant::MtxOwnerMask & state.thread->handle; mtxExpected = constant::MtxOwnerMask & state.thread->handle;
if (!__atomic_compare_exchange_n(mtx, &mtxExpected, mtxDesired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) if (!__atomic_compare_exchange_n(mtx, &mtxExpected, mtxDesired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
return false; return false;
} }
if (mtxDesired) { if (mtxDesired) {
auto status = (*mtxWaiters.begin()); auto status = (*mtxWaiters.begin());
status->flag = true; status->flag = true;
@ -222,52 +255,65 @@ namespace skyline::kernel::type {
while (status->flag); while (status->flag);
lock.lock(); lock.lock();
} }
return true; return true;
} }
bool KProcess::ConditionalVariableWait(u64 conditionalAddress, u64 mutexAddress, u64 timeout) { bool KProcess::ConditionalVariableWait(u64 conditionalAddress, u64 mutexAddress, u64 timeout) {
std::unique_lock lock(conditionalLock); std::unique_lock lock(conditionalLock);
auto &condWaiters = conditionals[conditionalAddress]; auto &condWaiters = conditionals[conditionalAddress];
std::shared_ptr<WaitStatus> status; std::shared_ptr<WaitStatus> status;
for (auto it = condWaiters.begin();; ++it) { for (auto it = condWaiters.begin();; ++it) {
if (it != condWaiters.end() && (*it)->priority >= state.thread->priority) if (it != condWaiters.end() && (*it)->priority >= state.thread->priority)
continue; continue;
status = std::make_shared<WaitStatus>(state.thread->priority, state.thread->handle, mutexAddress); status = std::make_shared<WaitStatus>(state.thread->priority, state.thread->handle, mutexAddress);
condWaiters.insert(it, status); condWaiters.insert(it, status);
break; break;
} }
lock.unlock(); lock.unlock();
bool timedOut{}; bool timedOut{};
auto start = utils::GetTimeNs(); auto start = utils::GetTimeNs();
while (!status->flag) while (!status->flag)
if ((utils::GetTimeNs() - start) >= timeout) if ((utils::GetTimeNs() - start) >= timeout)
timedOut = true; timedOut = true;
lock.lock(); lock.lock();
if (!status->flag) if (!status->flag)
timedOut = false; timedOut = false;
else else
status->flag = false; status->flag = false;
for (auto it = condWaiters.begin(); it != condWaiters.end(); ++it) { for (auto it = condWaiters.begin(); it != condWaiters.end(); ++it) {
if ((*it)->handle == state.thread->handle) { if ((*it)->handle == state.thread->handle) {
condWaiters.erase(it); condWaiters.erase(it);
break; break;
} }
} }
lock.unlock(); lock.unlock();
return !timedOut; return !timedOut;
} }
void KProcess::ConditionalVariableSignal(u64 address, u64 amount) { void KProcess::ConditionalVariableSignal(u64 address, u64 amount) {
std::unique_lock condLock(conditionalLock); std::unique_lock condLock(conditionalLock);
auto &condWaiters = conditionals[address]; auto &condWaiters = conditionals[address];
u64 count{}; u64 count{};
auto iter = condWaiters.begin(); auto iter = condWaiters.begin();
while (iter != condWaiters.end() && count < amount) { while (iter != condWaiters.end() && count < amount) {
auto &thread = *iter; auto &thread = *iter;
auto mtx = GetPointer<u32>(thread->mutexAddress); auto mtx = GetPointer<u32>(thread->mutexAddress);
u32 mtxValue = __atomic_load_n(mtx, __ATOMIC_SEQ_CST); u32 mtxValue = __atomic_load_n(mtx, __ATOMIC_SEQ_CST);
while (true) { while (true) {
u32 mtxDesired{}; u32 mtxDesired{};
if (!mtxValue) if (!mtxValue)
mtxDesired = (constant::MtxOwnerMask & thread->handle); mtxDesired = (constant::MtxOwnerMask & thread->handle);
else if ((mtxValue & constant::MtxOwnerMask) == state.thread->handle) else if ((mtxValue & constant::MtxOwnerMask) == state.thread->handle)
@ -276,13 +322,16 @@ namespace skyline::kernel::type {
mtxDesired = mtxValue | ~constant::MtxOwnerMask; mtxDesired = mtxValue | ~constant::MtxOwnerMask;
else else
break; break;
if (__atomic_compare_exchange_n(mtx, &mtxValue, mtxDesired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) if (__atomic_compare_exchange_n(mtx, &mtxValue, mtxDesired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
break; break;
} }
if (mtxValue && ((mtxValue & constant::MtxOwnerMask) != state.thread->handle)) { if (mtxValue && ((mtxValue & constant::MtxOwnerMask) != state.thread->handle)) {
std::unique_lock mtxLock(mutexLock); std::unique_lock mtxLock(mutexLock);
auto &mtxWaiters = mutexes[thread->mutexAddress]; auto &mtxWaiters = mutexes[thread->mutexAddress];
std::shared_ptr<WaitStatus> status; std::shared_ptr<WaitStatus> status;
for (auto it = mtxWaiters.begin();; ++it) { for (auto it = mtxWaiters.begin();; ++it) {
if (it != mtxWaiters.end() && (*it)->priority >= thread->priority) if (it != mtxWaiters.end() && (*it)->priority >= thread->priority)
continue; continue;
@ -290,21 +339,26 @@ namespace skyline::kernel::type {
mtxWaiters.insert(it, status); mtxWaiters.insert(it, status);
break; break;
} }
mtxLock.unlock(); mtxLock.unlock();
while (!status->flag); while (!status->flag);
mtxLock.lock(); mtxLock.lock();
status->flag = false; status->flag = false;
for (auto it = mtxWaiters.begin(); it != mtxWaiters.end(); ++it) { for (auto it = mtxWaiters.begin(); it != mtxWaiters.end(); ++it) {
if ((*it)->handle == thread->handle) { if ((*it)->handle == thread->handle) {
mtxWaiters.erase(it); mtxWaiters.erase(it);
break; break;
} }
} }
mtxLock.unlock(); mtxLock.unlock();
} }
thread->flag = true; thread->flag = true;
iter++; iter++;
count++; count++;
condLock.unlock(); condLock.unlock();
while (thread->flag); while (thread->flag);
condLock.lock(); condLock.lock();

View File

@ -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) { 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)) if (address && !utils::PageAligned(address))
throw exception("KSharedMemory was created with non-page-aligned address: 0x{:X}", address); throw exception("KSharedMemory was created with non-page-aligned address: 0x{:X}", address);
fd = ASharedMemory_create("KSharedMemory", size); fd = ASharedMemory_create("KSharedMemory", size);
if (fd < 0) if (fd < 0)
throw exception("An error occurred while creating shared memory: {}", fd); 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)); 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)) if (address == reinterpret_cast<u64>(MAP_FAILED))
throw exception("An occurred while mapping shared memory: {}", strerror(errno)); throw exception("An occurred while mapping shared memory: {}", strerror(errno));
kernel = {.address = address, .size = size, .permission = permission}; kernel = {.address = address, .size = size, .permission = permission};
} }
u64 KSharedMemory::Map(const u64 address, const u64 size, memory::Permission permission) { u64 KSharedMemory::Map(const u64 address, const u64 size, memory::Permission permission) {
if (address && !utils::PageAligned(address)) if (address && !utils::PageAligned(address))
throw exception("KSharedMemory was mapped to a non-page-aligned address: 0x{:X}", address); throw exception("KSharedMemory was mapped to a non-page-aligned address: 0x{:X}", address);
Registers fregs{ Registers fregs{
.x0 = address, .x0 = address,
.x1 = size, .x1 = size,
@ -29,10 +33,13 @@ namespace skyline::kernel::type {
.x4 = static_cast<u64>(fd), .x4 = static_cast<u64>(fd),
.x8 = __NR_mmap, .x8 = __NR_mmap,
}; };
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while mapping shared memory in guest"); throw exception("An error occurred while mapping shared memory in guest");
guest = {.address = fregs.x0, .size = size, .permission = permission}; guest = {.address = fregs.x0, .size = size, .permission = permission};
BlockDescriptor block{ BlockDescriptor block{
.address = fregs.x0, .address = fregs.x0,
.size = size, .size = size,
@ -46,6 +53,7 @@ namespace skyline::kernel::type {
.blockList = {block}, .blockList = {block},
}; };
state.os->memory.InsertChunk(chunk); state.os->memory.InsertChunk(chunk);
return fregs.x0; return fregs.x0;
} }
@ -53,17 +61,21 @@ namespace skyline::kernel::type {
if (guest.Valid() && kernel.Valid()) { if (guest.Valid() && kernel.Valid()) {
if (close(fd) < 0) if (close(fd) < 0)
throw exception("An error occurred while trying to close shared memory FD: {}", strerror(errno)); throw exception("An error occurred while trying to close shared memory FD: {}", strerror(errno));
fd = ASharedMemory_create("KSharedMemory", size); fd = ASharedMemory_create("KSharedMemory", size);
if (fd < 0) if (fd < 0)
throw exception("An error occurred while creating shared memory: {}", fd); throw exception("An error occurred while creating shared memory: {}", fd);
Registers fregs{ Registers fregs{
.x0 = guest.address, .x0 = guest.address,
.x1 = guest.size, .x1 = guest.size,
.x8 = __NR_munmap .x8 = __NR_munmap
}; };
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while unmapping private memory in child process"); throw exception("An error occurred while unmapping private memory in child process");
fregs = { fregs = {
.x0 = guest.address, .x0 = guest.address,
.x1 = size, .x1 = size,
@ -72,10 +84,13 @@ namespace skyline::kernel::type {
.x4 = static_cast<u64>(fd), .x4 = static_cast<u64>(fd),
.x8 = __NR_mmap, .x8 = __NR_mmap,
}; };
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while remapping private memory in child process"); 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); state.process->WriteMemory(reinterpret_cast<void *>(kernel.address), guest.address, std::min(guest.size, size), true);
auto chunk = state.os->memory.GetChunk(guest.address); auto chunk = state.os->memory.GetChunk(guest.address);
for (const auto &block : chunk->blockList) { for (const auto &block : chunk->blockList) {
if ((block.address - chunk->address) < guest.size) { if ((block.address - chunk->address) < guest.size) {
@ -85,6 +100,7 @@ namespace skyline::kernel::type {
.x2 = static_cast<u64>(block.permission.Get()), .x2 = static_cast<u64>(block.permission.Get()),
.x8 = __NR_mprotect, .x8 = __NR_mprotect,
}; };
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while updating private memory's permissions in child process"); throw exception("An error occurred while updating private memory's permissions in child process");
@ -92,25 +108,34 @@ namespace skyline::kernel::type {
break; break;
} }
} }
munmap(reinterpret_cast<void *>(kernel.address), kernel.size); 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); auto host = mmap(reinterpret_cast<void *>(chunk->host), size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, fd, 0);
if (host == MAP_FAILED) if (host == MAP_FAILED)
throw exception("An occurred while mapping shared memory: {}", strerror(errno)); throw exception("An occurred while mapping shared memory: {}", strerror(errno));
guest.size = size; guest.size = size;
MemoryManager::ResizeChunk(chunk, size); MemoryManager::ResizeChunk(chunk, size);
} else if (kernel.Valid()) { } else if (kernel.Valid()) {
if (close(fd) < 0) if (close(fd) < 0)
throw exception("An error occurred while trying to close shared memory FD: {}", strerror(errno)); throw exception("An error occurred while trying to close shared memory FD: {}", strerror(errno));
fd = ASharedMemory_create("KSharedMemory", size); fd = ASharedMemory_create("KSharedMemory", size);
if (fd < 0) if (fd < 0)
throw exception("An error occurred while creating shared memory: {}", fd); throw exception("An error occurred while creating shared memory: {}", fd);
std::vector<u8> data(std::min(size, kernel.size)); std::vector<u8> data(std::min(size, kernel.size));
memcpy(data.data(), reinterpret_cast<const void *>(kernel.address), 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); munmap(reinterpret_cast<void *>(kernel.address), kernel.size);
auto address = mmap(reinterpret_cast<void *>(kernel.address), size, kernel.permission.Get(), MAP_SHARED, fd, 0); auto address = mmap(reinterpret_cast<void *>(kernel.address), size, kernel.permission.Get(), MAP_SHARED, fd, 0);
if (address == MAP_FAILED) if (address == MAP_FAILED)
throw exception("An occurred while mapping shared memory: {}", strerror(errno)); throw exception("An occurred while mapping shared memory: {}", strerror(errno));
memcpy(address, data.data(), std::min(size, kernel.size)); memcpy(address, data.data(), std::min(size, kernel.size));
kernel.address = reinterpret_cast<u64>(address); kernel.address = reinterpret_cast<u64>(address);
kernel.size = size; kernel.size = size;
} else { } else {
@ -126,9 +151,11 @@ namespace skyline::kernel::type {
.x2 = static_cast<u64>(permission.Get()), .x2 = static_cast<u64>(permission.Get()),
.x8 = __NR_mprotect, .x8 = __NR_mprotect,
}; };
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while updating shared memory's permissions in guest"); throw exception("An error occurred while updating shared memory's permissions in guest");
auto chunk = state.os->memory.GetChunk(address); auto chunk = state.os->memory.GetChunk(address);
BlockDescriptor block{ BlockDescriptor block{
.address = address, .address = address,
@ -152,6 +179,7 @@ namespace skyline::kernel::type {
.x1 = guest.size, .x1 = guest.size,
.x8 = __NR_munmap, .x8 = __NR_munmap,
}; };
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
} }
} catch (const std::exception &) { } catch (const std::exception &) {

View File

@ -17,6 +17,7 @@ namespace skyline::kernel::type {
if (pid == parent->pid) if (pid == parent->pid)
parent->status = KProcess::Status::Started; parent->status = KProcess::Status::Started;
status = Status::Running; status = Status::Running;
state.nce->StartThread(entryArg, handle, parent->threads.at(pid)); state.nce->StartThread(entryArg, handle, parent->threads.at(pid));
} }
} }

View File

@ -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) { 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)) if (address && !utils::PageAligned(address))
throw exception("KTransferMemory was created with non-page-aligned address: 0x{:X}", address); throw exception("KTransferMemory was created with non-page-aligned address: 0x{:X}", address);
BlockDescriptor block{ BlockDescriptor block{
.size = size, .size = size,
.permission = permission, .permission = permission,
@ -16,10 +17,12 @@ namespace skyline::kernel::type {
.state = memState, .state = memState,
.blockList = {block}, .blockList = {block},
}; };
if (host) { if (host) {
address = reinterpret_cast<u64>(mmap(reinterpret_cast<void *>(address), size, permission.Get(), MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0), -1, 0)); 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) if (reinterpret_cast<void *>(address) == MAP_FAILED)
throw exception("An error occurred while mapping transfer memory in host"); throw exception("An error occurred while mapping transfer memory in host");
this->address = address; this->address = address;
chunk.address = address; chunk.address = address;
chunk.blockList.front().address = address; chunk.blockList.front().address = address;
@ -33,12 +36,15 @@ namespace skyline::kernel::type {
.x4 = static_cast<u64>(-1), .x4 = static_cast<u64>(-1),
.x8 = __NR_mmap, .x8 = __NR_mmap,
}; };
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while mapping shared region in child process"); throw exception("An error occurred while mapping shared region in child process");
this->address = fregs.x0; this->address = fregs.x0;
chunk.address = fregs.x0; chunk.address = fregs.x0;
chunk.blockList.front().address = fregs.x0; chunk.blockList.front().address = fregs.x0;
state.os->memory.InsertChunk(chunk); state.os->memory.InsertChunk(chunk);
} }
} }
@ -46,13 +52,17 @@ namespace skyline::kernel::type {
u64 KTransferMemory::Transfer(bool mHost, u64 nAddress, u64 nSize) { u64 KTransferMemory::Transfer(bool mHost, u64 nAddress, u64 nSize) {
if (nAddress && !utils::PageAligned(nAddress)) if (nAddress && !utils::PageAligned(nAddress))
throw exception("KTransferMemory was transferred to a non-page-aligned address: 0x{:X}", nAddress); throw exception("KTransferMemory was transferred to a non-page-aligned address: 0x{:X}", nAddress);
nSize = nSize ? nSize : size; nSize = nSize ? nSize : size;
ChunkDescriptor chunk = host ? hostChunk : *state.os->memory.GetChunk(address); ChunkDescriptor chunk = host ? hostChunk : *state.os->memory.GetChunk(address);
chunk.address = nAddress; chunk.address = nAddress;
chunk.size = nSize; chunk.size = nSize;
MemoryManager::ResizeChunk(&chunk, nSize); MemoryManager::ResizeChunk(&chunk, nSize);
for (auto &block : chunk.blockList) { for (auto &block : chunk.blockList) {
block.address = nAddress + (block.address - address); block.address = nAddress + (block.address - address);
if ((mHost && !host) || (!mHost && !host)) { if ((mHost && !host) || (!mHost && !host)) {
Registers fregs{ Registers fregs{
.x0 = block.address, .x0 = block.address,
@ -62,15 +72,18 @@ namespace skyline::kernel::type {
.x4 = static_cast<u64>(-1), .x4 = static_cast<u64>(-1),
.x8 = __NR_mmap, .x8 = __NR_mmap,
}; };
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while mapping transfer memory in child process"); throw exception("An error occurred while mapping transfer memory in child process");
nAddress = fregs.x0; nAddress = fregs.x0;
} else if ((!mHost && host) || (mHost && host)) { } 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)); 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) if (reinterpret_cast<void *>(nAddress) == MAP_FAILED)
throw exception("An error occurred while mapping transfer memory in host"); throw exception("An error occurred while mapping transfer memory in host");
} }
if (block.permission.r) { if (block.permission.r) {
if (mHost && !host) if (mHost && !host)
state.process->ReadMemory(reinterpret_cast<void *>(nAddress), address, block.size); 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()), .x2 = static_cast<u64>(block.permission.Get()),
.x8 = __NR_mprotect, .x8 = __NR_mprotect,
}; };
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while updating transfer memory's permissions in guest"); throw exception("An error occurred while updating transfer memory's permissions in guest");
} }
} }
} }
if (mHost && !host) { if (mHost && !host) {
state.os->memory.DeleteChunk(address); state.os->memory.DeleteChunk(address);
hostChunk = chunk; hostChunk = chunk;
@ -109,12 +124,14 @@ namespace skyline::kernel::type {
state.os->memory.DeleteChunk(address); state.os->memory.DeleteChunk(address);
state.os->memory.InsertChunk(chunk); state.os->memory.InsertChunk(chunk);
} }
if ((mHost && !host) || (!mHost && !host)) { if ((mHost && !host) || (!mHost && !host)) {
Registers fregs{ Registers fregs{
.x0 = address, .x0 = address,
.x1 = size, .x1 = size,
.x8 = __NR_munmap, .x8 = __NR_munmap,
}; };
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while unmapping transfer memory in child process"); 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) if (reinterpret_cast<void *>(munmap(reinterpret_cast<void *>(address), size)) == MAP_FAILED)
throw exception("An error occurred while unmapping transfer memory in host: {}"); throw exception("An error occurred while unmapping transfer memory in host: {}");
} }
host = mHost; host = mHost;
address = nAddress; address = nAddress;
size = nSize; size = nSize;
@ -139,10 +157,13 @@ namespace skyline::kernel::type {
.x2 = nSize, .x2 = nSize,
.x8 = __NR_mremap, .x8 = __NR_mremap,
}; };
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while remapping transfer memory in guest"); throw exception("An error occurred while remapping transfer memory in guest");
size = nSize; size = nSize;
auto chunk = state.os->memory.GetChunk(address); auto chunk = state.os->memory.GetChunk(address);
MemoryManager::ResizeChunk(chunk, size); MemoryManager::ResizeChunk(chunk, size);
} }
@ -154,9 +175,11 @@ namespace skyline::kernel::type {
.size = size, .size = size,
.permission = permission, .permission = permission,
}; };
if (host) { if (host) {
if (mprotect(reinterpret_cast<void *>(address), size, permission.Get()) == reinterpret_cast<u64>(MAP_FAILED)) if (mprotect(reinterpret_cast<void *>(address), size, permission.Get()) == reinterpret_cast<u64>(MAP_FAILED))
throw exception("An occurred while remapping transfer memory: {}", strerror(errno)); throw exception("An occurred while remapping transfer memory: {}", strerror(errno));
MemoryManager::InsertBlock(&hostChunk, block); MemoryManager::InsertBlock(&hostChunk, block);
} else { } else {
Registers fregs{ Registers fregs{
@ -165,9 +188,11 @@ namespace skyline::kernel::type {
.x2 = static_cast<u64>(permission.Get()), .x2 = static_cast<u64>(permission.Get()),
.x8 = __NR_mprotect, .x8 = __NR_mprotect,
}; };
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while updating transfer memory's permissions in guest"); throw exception("An error occurred while updating transfer memory's permissions in guest");
auto chunk = state.os->memory.GetChunk(address); auto chunk = state.os->memory.GetChunk(address);
MemoryManager::InsertBlock(chunk, block); MemoryManager::InsertBlock(chunk, block);
} }
@ -183,7 +208,9 @@ namespace skyline::kernel::type {
.x1 = size, .x1 = size,
.x8 = __NR_munmap, .x8 = __NR_munmap,
}; };
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
state.os->memory.DeleteChunk(address); state.os->memory.DeleteChunk(address);
} catch (const std::exception &) { } catch (const std::exception &) {
} }

View File

@ -7,14 +7,17 @@ namespace skyline::kernel {
void OS::Execute(const int romFd, const TitleFormat romType) { void OS::Execute(const int romFd, const TitleFormat romType) {
std::shared_ptr<loader::Loader> loader; std::shared_ptr<loader::Loader> loader;
if (romType == TitleFormat::NRO) { if (romType == TitleFormat::NRO) {
loader = std::make_shared<loader::NroLoader>(romFd); loader = std::make_shared<loader::NroLoader>(romFd);
} else } else
throw exception("Unsupported ROM extension."); throw exception("Unsupported ROM extension.");
auto process = CreateProcess(constant::BaseAddress, 0, constant::DefStackSize); auto process = CreateProcess(constant::BaseAddress, 0, constant::DefStackSize);
loader->LoadProcessData(process, state); loader->LoadProcessData(process, state);
process->InitializeMemory(); process->InitializeMemory();
process->threads.at(process->pid)->Start(); // The kernel itself is responsible for starting the main thread process->threads.at(process->pid)->Start(); // The kernel itself is responsible for starting the main thread
state.nce->Execute(); state.nce->Execute();
} }
@ -23,13 +26,17 @@ namespace skyline::kernel {
stack->guest = stack->kernel; stack->guest = stack->kernel;
if (mprotect(reinterpret_cast<void *>(stack->guest.address), PAGE_SIZE, PROT_NONE)) if (mprotect(reinterpret_cast<void *>(stack->guest.address), PAGE_SIZE, PROT_NONE))
throw exception("Failed to create guard pages"); 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); 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; 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)); 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) if (pid == -1)
throw exception("Call to clone() has failed: {}", strerror(errno)); throw exception("Call to clone() has failed: {}", strerror(errno));
state.logger->Debug("Successfully created process with PID: {}", pid); state.logger->Debug("Successfully created process with PID: {}", pid);
process = std::make_shared<kernel::type::KProcess>(state, pid, argument, stack, tlsMem); process = std::make_shared<kernel::type::KProcess>(state, pid, argument, stack, tlsMem);
return process; return process;
} }