Make SVCs more accurate & Improve KSharedMemory

This commit adds logging to almost all SVCs with the exception of svcGetSystemTick and adds accurate error handling to them. It also improves how KSharedMemory is handled.
This commit is contained in:
◱ PixelyIon 2019-11-18 01:49:01 +05:30 committed by ◱ PixelyIon
parent 26a67f70b7
commit 1956a3bbbb
14 changed files with 396 additions and 243 deletions

View File

@ -40,7 +40,8 @@ namespace skyline {
constexpr u64 MapSize = 0x1000000000; //!< The size of the map region constexpr u64 MapSize = 0x1000000000; //!< The size of the map region
constexpr u64 TotalPhyMem = 0xF8000000; // ~4 GB of RAM constexpr u64 TotalPhyMem = 0xF8000000; // ~4 GB of RAM
constexpr size_t DefStackSize = 0x1E8480; //!< The default amount of stack: 2 MB constexpr size_t DefStackSize = 0x1E8480; //!< The default amount of stack: 2 MB
constexpr size_t DefHeapSize = PAGE_SIZE; //!< The default amount of heap constexpr size_t HeapSizeDiv = 0x200000; //!< The amount heap size has to be divisible by
constexpr size_t DefHeapSize = HeapSizeDiv; //!< The default amount of heap
constexpr size_t TlsSlotSize = 0x200; //!< The size of a single TLS slot constexpr size_t TlsSlotSize = 0x200; //!< The size of a single TLS slot
constexpr u8 TlsSlots = PAGE_SIZE / TlsSlotSize; //!< The amount of TLS slots in a single page constexpr u8 TlsSlots = PAGE_SIZE / TlsSlotSize; //!< The amount of TLS slots in a single page
// Loader // Loader
@ -76,14 +77,18 @@ namespace skyline {
// Status codes // Status codes
namespace status { namespace status {
constexpr u32 Success = 0x0; //!< "Success" constexpr u32 Success = 0x0; //!< "Success"
constexpr u32 NoMessages = 0x680; //!< "No message available"
constexpr u32 ServiceInvName = 0xC15; //!< "Invalid name" constexpr u32 ServiceInvName = 0xC15; //!< "Invalid name"
constexpr u32 ServiceNotReg = 0xE15; //!< "Service not registered" constexpr u32 ServiceNotReg = 0xE15; //!< "Service not registered"
constexpr u32 InvSize = 0xCA01; //!< "Invalid size"
constexpr u32 InvAddress = 0xCC01; //!< "Invalid address" constexpr u32 InvAddress = 0xCC01; //!< "Invalid address"
constexpr u32 InvPermission = 0xE001; //!< "Invalid Permission"
constexpr u32 InvHandle = 0xE401; //!< "Invalid handle" constexpr u32 InvHandle = 0xE401; //!< "Invalid handle"
constexpr u32 InvCombination = 0xE801; //!< "Invalid combination"
constexpr u32 MaxHandles = 0xEE01; //!< "Too many handles" constexpr u32 MaxHandles = 0xEE01; //!< "Too many handles"
constexpr u32 Timeout = 0xEA01; //!< "Timeout while svcWaitSynchronization" constexpr u32 Timeout = 0xEA01; //!< "Timeout"
constexpr u32 NotFound = 0xF201; //!< "Not found"
constexpr u32 Unimpl = 0x177202; //!< "Unimplemented behaviour" constexpr u32 Unimpl = 0x177202; //!< "Unimplemented behaviour"
constexpr u32 NoMessages = 0x680; //!< "No message available"
} }
}; };

View File

@ -21,7 +21,7 @@ namespace skyline::gpu::device {
handleTable[handleIndex] = std::make_shared<NvMapObject>(idIndex++, data.size); handleTable[handleIndex] = std::make_shared<NvMapObject>(idIndex++, data.size);
data.handle = handleIndex++; data.handle = handleIndex++;
state.thisProcess->WriteMemory(data, buffer.output[0].address); state.thisProcess->WriteMemory(data, buffer.output[0].address);
state.logger->Info("Create: Input: Size: 0x{:X}, Output: Handle: 0x{:X}, Status: {}", data.size, data.handle, buffer.status); state.logger->Debug("Create: Input: Size: 0x{:X}, Output: Handle: 0x{:X}, Status: {}", data.size, data.handle, buffer.status);
} }
void NvMap::FromId(skyline::gpu::device::IoctlBuffers &buffer) { void NvMap::FromId(skyline::gpu::device::IoctlBuffers &buffer) {
@ -41,7 +41,7 @@ namespace skyline::gpu::device {
state.thisProcess->WriteMemory(data, buffer.output[0].address); state.thisProcess->WriteMemory(data, buffer.output[0].address);
else else
buffer.status = NvStatus::BadValue; buffer.status = NvStatus::BadValue;
state.logger->Info("FromId: Input: Handle: 0x{:X}, Output: ID: 0x{:X}, Status: {}", data.handle, data.id, buffer.status); state.logger->Debug("FromId: Input: Handle: 0x{:X}, Output: ID: 0x{:X}, Status: {}", data.handle, data.id, buffer.status);
} }
void NvMap::Alloc(IoctlBuffers &buffer) { void NvMap::Alloc(IoctlBuffers &buffer) {
@ -61,7 +61,7 @@ namespace skyline::gpu::device {
object->kind = data.kind; object->kind = data.kind;
object->address = data.address; object->address = data.address;
object->status = NvMapObject::Status::Allocated; object->status = NvMapObject::Status::Allocated;
state.logger->Info("Alloc: Input: Handle: 0x{:X}, HeapMask: 0x{:X}, Flags: {}, Align: 0x{:X}, Kind: {}, Address: 0x{:X}, Output: Status: {}", data.handle, data.heapMask, data.flags, data.align, data.kind, data.address, buffer.status); state.logger->Debug("Alloc: Input: Handle: 0x{:X}, HeapMask: 0x{:X}, Flags: {}, Align: 0x{:X}, Kind: {}, Address: 0x{:X}, Output: Status: {}", data.handle, data.heapMask, data.flags, data.align, data.kind, data.address, buffer.status);
} }
void NvMap::Free(skyline::gpu::device::IoctlBuffers &buffer) { void NvMap::Free(skyline::gpu::device::IoctlBuffers &buffer) {
@ -125,7 +125,7 @@ namespace skyline::gpu::device {
break; break;
} }
state.thisProcess->WriteMemory(data, buffer.output[0].address); state.thisProcess->WriteMemory(data, buffer.output[0].address);
state.logger->Info("Param: Input: Handle: 0x{:X}, Parameter: {}, Output: Result: 0x{:X}, Status: {}", data.handle, data.parameter, data.result, buffer.status); state.logger->Debug("Param: Input: Handle: 0x{:X}, Parameter: {}, Output: Result: 0x{:X}, Status: {}", data.handle, data.parameter, data.result, buffer.status);
} }
void NvMap::GetId(skyline::gpu::device::IoctlBuffers &buffer) { void NvMap::GetId(skyline::gpu::device::IoctlBuffers &buffer) {
@ -135,6 +135,6 @@ namespace skyline::gpu::device {
} data = state.thisProcess->ReadMemory<Data>(buffer.input[0].address); } data = state.thisProcess->ReadMemory<Data>(buffer.input[0].address);
data.id = handleTable.at(data.handle)->id; data.id = handleTable.at(data.handle)->id;
state.thisProcess->WriteMemory(data, buffer.output[0].address); state.thisProcess->WriteMemory(data, buffer.output[0].address);
state.logger->Info("GetId: Input: Handle: 0x{:X}, Output: ID: 0x{:X}, Status: {}", data.handle, data.id, buffer.status); state.logger->Debug("GetId: Input: Handle: 0x{:X}, Output: ID: 0x{:X}, Status: {}", data.handle, data.id, buffer.status);
} }
} }

View File

@ -1,46 +1,74 @@
#include "svc.h" #include "svc.h"
#include <os.h> #include <os.h>
#include <kernel/types/KTransferMemory.h>
namespace skyline::kernel::svc { namespace skyline::kernel::svc {
void SetHeapSize(DeviceState &state) { void SetHeapSize(DeviceState &state) {
u32 size = state.nce->GetRegister(Wreg::W1); const u32 size = state.nce->GetRegister(Wreg::W1);
if(size%constant::HeapSizeDiv != 0) {
state.nce->SetRegister(Xreg::X1, 0);
state.nce->SetRegister(Wreg::W0, constant::status::InvSize);
state.logger->Warn("svcSetHeapSize: 'size' not divisible by 2MB: {}", size);
return;
}
std::shared_ptr<type::KPrivateMemory> heap; std::shared_ptr<type::KPrivateMemory> heap;
try { try {
heap = state.thisProcess->memoryRegionMap.at(memory::Region::Heap); heap = state.thisProcess->memoryRegionMap.at(memory::Region::Heap);
heap->Resize(size, true); // This can fail due to not enough space to resize heap->Resize(size, true);
} catch (const exception &) { } catch (const exception &) {
state.logger->Warn("svcSetHeapSize is falling back to recreating memory"); state.logger->Warn("svcSetHeapSize: Falling back to recreating memory");
state.thisProcess->UnmapPrivateRegion(memory::Region::Heap); state.thisProcess->UnmapPrivateRegion(memory::Region::Heap);
heap = state.thisProcess->MapPrivateRegion(constant::HeapAddr, size, {true, true, false}, memory::Type::Heap, memory::Region::Heap).item; heap = state.thisProcess->MapPrivateRegion(constant::HeapAddr, size, {true, true, false}, memory::Type::Heap, memory::Region::Heap).item;
} }
state.nce->SetRegister(Wreg::W0, constant::status::Success); state.nce->SetRegister(Wreg::W0, constant::status::Success);
state.nce->SetRegister(Xreg::X1, heap->address); state.nce->SetRegister(Xreg::X1, heap->address);
state.logger->Debug("svcSetHeapSize allocated at 0x{:X} for 0x{:X} bytes", heap->address, heap->size); state.logger->Debug("svcSetHeapSize: Allocated at 0x{:X} for 0x{:X} bytes", heap->address, heap->size);
} }
void SetMemoryAttribute(DeviceState &state) { void SetMemoryAttribute(DeviceState &state) {
u64 addr = state.nce->GetRegister(Xreg::X0); const u64 addr = state.nce->GetRegister(Xreg::X0);
u64 size = state.nce->GetRegister(Xreg::X1); if((addr & (PAGE_SIZE - 1))) {
bool isUncached = (state.nce->GetRegister(Wreg::W2) == 8) && (state.nce->GetRegister(Wreg::W3) == 8); state.nce->SetRegister(Wreg::W0, constant::status::InvAddress);
state.logger->Warn("svcSetMemoryAttribute: 'address' not page aligned: {}", addr);
return;
}
const u64 size = state.nce->GetRegister(Xreg::X1);
if((size & (PAGE_SIZE - 1)) || !size) {
state.nce->SetRegister(Wreg::W0, constant::status::InvSize);
state.logger->Warn("svcSetMemoryAttribute: 'size' {}: {}", size ? "not page aligned" : "is zero", size);
return;
}
u32 mask = state.nce->GetRegister(Wreg::W2);
u32 value = state.nce->GetRegister(Wreg::W3);
u32 maskedValue = mask | value;
if(maskedValue != mask) {
state.nce->SetRegister(Wreg::W0, constant::status::InvCombination);
state.logger->Warn("svcSetMemoryAttribute: 'mask' invalid: 0x{:X}, 0x{:X}", mask, value);
return;
}
memory::MemoryAttribute attribute = *reinterpret_cast<memory::MemoryAttribute*>(&maskedValue);
bool found = false; bool found = false;
for (const auto&[address, region] : state.thisProcess->memoryMap) { for (const auto&[address, region] : state.thisProcess->memoryMap) {
if (addr >= address && addr < (address + region->size)) { if (addr >= address && addr < (address + region->size)) {
bool subFound = false; bool subFound = false;
for (auto &subregion : region->regionInfoVec) { for (auto &subregion : region->regionInfoVec) {
if ((address >= subregion.address) && (address < (subregion.address + subregion.size))) if ((address >= subregion.address) && (address < (subregion.address + subregion.size)))
subregion.isUncached = isUncached; subregion.isUncached = attribute.isUncached;
subFound = true; subFound = true;
break; break;
} }
if (!subFound) if (!subFound)
region->regionInfoVec.emplace_back(addr, size, isUncached); region->regionInfoVec.emplace_back(addr, size, static_cast<bool>(attribute.isUncached));
found = true; found = true;
break; break;
} }
} }
state.logger->Debug("svcSetMemoryAttribute set caching to {} at 0x{:X} for 0x{:X} bytes", !isUncached, addr, size); if(!found) {
state.nce->SetRegister(Wreg::W0, found ? constant::status::Success : constant::status::InvAddress); state.nce->SetRegister(Wreg::W0, constant::status::InvAddress);
state.logger->Warn("svcSetMemoryAttribute: Cannot find memory region: 0x{:X}", addr);
return;
}
state.logger->Debug("svcSetMemoryAttribute: Set caching to {} at 0x{:X} for 0x{:X} bytes", !attribute.isUncached, addr, size);
state.nce->SetRegister(Wreg::W0, constant::status::Success);
} }
void QueryMemory(DeviceState &state) { void QueryMemory(DeviceState &state) {
@ -81,34 +109,49 @@ namespace skyline::kernel::svc {
.size = static_cast<u64>(-constant::BaseEnd + 1), .size = static_cast<u64>(-constant::BaseEnd + 1),
.type = static_cast<u64>(memory::Type::Unmapped) .type = static_cast<u64>(memory::Type::Unmapped)
}; };
state.logger->Warn("Cannot find block of address: 0x{:X}", addr); state.logger->Debug("svcQueryMemory: Cannot find block of address: 0x{:X}", addr);
} }
} }
state.logger->Debug("svcQueryMemory: Address: 0x{:X}, Size: 0x{:X}, Type: {}, Is Uncached: {}, Permissions: {}{}{}", memInf.baseAddress, memInf.size, memInf.type, static_cast<bool>(memInf.memoryAttribute.isUncached), memInf.perms.r ? "R" : "-", memInf.perms.w ? "W" : "-", memInf.perms.x ? "X" : "-");
state.thisProcess->WriteMemory<memory::MemoryInfo>(memInf, state.nce->GetRegister(Xreg::X0)); state.thisProcess->WriteMemory<memory::MemoryInfo>(memInf, state.nce->GetRegister(Xreg::X0));
state.nce->SetRegister(Wreg::W0, constant::status::Success); state.nce->SetRegister(Wreg::W0, constant::status::Success);
} }
void ExitProcess(DeviceState &state) { void ExitProcess(DeviceState &state) {
state.logger->Debug("svcExitProcess: Exiting current process: {}", state.thisProcess->mainThread);
state.os->KillThread(state.thisProcess->mainThread); state.os->KillThread(state.thisProcess->mainThread);
} }
void CreateThread(DeviceState &state) { void CreateThread(DeviceState &state) {
// TODO: Support Core Mask potentially u64 entryAddr = state.nce->GetRegister(Xreg::X1);
auto thread = state.thisProcess->CreateThread(state.nce->GetRegister(Xreg::X1), state.nce->GetRegister(Xreg::X2), state.nce->GetRegister(Xreg::X3), static_cast<u8>(state.nce->GetRegister(Wreg::W4))); u64 entryArg = state.nce->GetRegister(Xreg::X2);
u64 stackTop = state.nce->GetRegister(Xreg::X3);
u8 priority = static_cast<u8>(state.nce->GetRegister(Wreg::W4));
if(priority >= constant::PriorityNin.first && priority <= constant::PriorityNin.second) {
state.nce->SetRegister(Wreg::W0, constant::status::InvAddress);
state.logger->Warn("svcSetHeapSize: 'priority' invalid: {}", priority);
return;
}
auto thread = state.thisProcess->CreateThread(entryAddr, entryArg, stackTop, priority);
state.nce->SetRegister(Wreg::W0, constant::status::Success); state.nce->SetRegister(Wreg::W0, constant::status::Success);
state.nce->SetRegister(Wreg::W1, thread->handle); state.nce->SetRegister(Wreg::W1, thread->handle);
state.logger->Info("Creating a thread: {}", thread->handle); state.logger->Info("svcCreateThread: Created thread with handle 0x{:X}", thread->handle);
} }
void StartThread(DeviceState &state) { void StartThread(DeviceState &state) {
auto &object = state.thisProcess->handleTable.at(static_cast<const unsigned int &>(state.nce->GetRegister(Wreg::W0))); auto handle = state.nce->GetRegister(Wreg::W0);
if (object->objectType == type::KType::KThread) try {
std::static_pointer_cast<type::KThread>(object)->Start(); auto thread = state.thisProcess->GetHandle<type::KThread>(handle);
else state.logger->Debug("svcStartThread: Starting thread: 0x{:X}, {}", handle, thread->pid);
throw exception("StartThread was called on a non-KThread object"); thread->Start();
} catch (const std::exception&) {
state.logger->Warn("svcStartThread: 'handle' invalid: 0x{:X}", handle);
state.nce->SetRegister(Wreg::W0, constant::status::InvHandle);
}
} }
void ExitThread(DeviceState &state) { void ExitThread(DeviceState &state) {
state.logger->Debug("svcExitProcess: Exiting current thread: {}", state.thisThread->pid);
state.os->KillThread(state.thisThread->pid); state.os->KillThread(state.thisThread->pid);
} }
@ -118,62 +161,145 @@ namespace skyline::kernel::svc {
case 0: case 0:
case 1: case 1:
case 2: case 2:
state.logger->Debug("svcSleepThread: Yielding thread: {}", in);
state.thisThread->status = type::KThread::Status::Runnable; // Will cause the application to awaken on the next iteration of the main loop state.thisThread->status = type::KThread::Status::Runnable; // Will cause the application to awaken on the next iteration of the main loop
break;
default: default:
state.logger->Debug("svcSleepThread: Thread sleeping for {} ns", in);
state.thisThread->timeout = GetCurrTimeNs() + in; state.thisThread->timeout = GetCurrTimeNs() + in;
state.thisThread->status = type::KThread::Status::Sleeping; state.thisThread->status = type::KThread::Status::Sleeping;
} }
} }
void GetThreadPriority(DeviceState &state) { void GetThreadPriority(DeviceState &state) {
state.nce->SetRegister(Wreg::W1, state.thisProcess->GetHandle<type::KThread>(static_cast<handle_t>(state.nce->GetRegister(Wreg::W0)))->priority); auto handle = state.nce->GetRegister(Wreg::W0);
state.nce->SetRegister(Wreg::W0, constant::status::Success); try {
auto priority = state.thisProcess->GetHandle<type::KThread>(handle)->priority;
state.nce->SetRegister(Wreg::W1, priority);
state.nce->SetRegister(Wreg::W0, constant::status::Success);
state.logger->Debug("svcGetThreadPriority: Writing thread priority {}", priority);
} catch (const std::exception&) {
state.logger->Warn("svcGetThreadPriority: 'handle' invalid: 0x{:X}", handle);
state.nce->SetRegister(Wreg::W0, constant::status::InvHandle);
}
} }
void SetThreadPriority(DeviceState &state) { void SetThreadPriority(DeviceState &state) {
state.thisProcess->GetHandle<type::KThread>(static_cast<handle_t>(state.nce->GetRegister(Wreg::W0)))->Start(); auto handle = state.nce->GetRegister(Wreg::W0);
state.nce->SetRegister(Wreg::W0, constant::status::Success); auto priority = state.nce->GetRegister(Wreg::W1);
try {
state.thisProcess->GetHandle<type::KThread>(handle)->UpdatePriority(static_cast<u8>(priority));
state.nce->SetRegister(Wreg::W0, constant::status::Success);
state.logger->Debug("svcSetThreadPriority: Setting thread priority to {}", priority);
} catch (const std::exception&) {
state.logger->Warn("svcSetThreadPriority: 'handle' invalid: 0x{:X}", handle);
state.nce->SetRegister(Wreg::W0, constant::status::InvHandle);
}
} }
void MapSharedMemory(DeviceState &state) { void MapSharedMemory(DeviceState &state) {
auto object = state.thisProcess->GetHandle<type::KSharedMemory>(static_cast<handle_t>(state.nce->GetRegister(Wreg::W0))); try {
object->Map(state.nce->GetRegister(Xreg::X1), state.nce->GetRegister(Xreg::X2), state.thisProcess->mainThread); auto object = state.thisProcess->GetHandle<type::KSharedMemory>(state.nce->GetRegister(Wreg::W0));
state.nce->SetRegister(Wreg::W0, constant::status::Success); u64 addr = state.nce->GetRegister(Xreg::X1);
} if ((addr & (PAGE_SIZE - 1U))) {
state.nce->SetRegister(Wreg::W0, constant::status::InvAddress);
void CloseHandle(DeviceState &state) { state.logger->Warn("svcMapSharedMemory: 'address' not page aligned: 0x{:X}", addr);
auto handle = static_cast<handle_t>(state.nce->GetRegister(Wreg::W0)); return;
state.logger->Debug("Closing handle: 0x{:X}", handle); }
auto &object = state.thisProcess->handleTable.at(handle); const u64 size = state.nce->GetRegister(Xreg::X2);
switch (object->objectType) { if ((size & (PAGE_SIZE - 1U)) || !size) {
case (type::KType::KThread): state.nce->SetRegister(Wreg::W0, constant::status::InvSize);
state.os->KillThread(std::static_pointer_cast<type::KThread>(object)->pid); state.logger->Warn("svcMapSharedMemory: 'size' {}: {}", size ? "not page aligned" : "is zero", size);
break; return;
case (type::KType::KProcess): }
state.os->KillThread(std::static_pointer_cast<type::KProcess>(object)->mainThread); u32 perm = state.nce->GetRegister(Wreg::W3);
break; memory::Permission permission = *reinterpret_cast<memory::Permission *>(&perm);
default: if ((permission.w && !permission.r) || (permission.x && !permission.r)) {
state.thisProcess->handleTable.erase(handle); state.logger->Warn("svcMapSharedMemory: 'permission' invalid: {}{}{}", permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-");
state.nce->SetRegister(Wreg::W0, constant::status::InvPermission);
return;
}
state.logger->Debug("svcMapSharedMemory: Mapping shared memory at 0x{:X} for {} bytes ({}{}{})", addr, size, permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-");
object->Map(addr, size, permission, state.thisProcess->mainThread);
state.nce->SetRegister(Wreg::W0, constant::status::Success);
} catch (const std::exception &) {
state.logger->Warn("svcMapSharedMemory: 'handle' invalid: 0x{:X}", state.nce->GetRegister(Wreg::W0));
state.nce->SetRegister(Wreg::W0, constant::status::InvHandle);
} }
state.nce->SetRegister(Wreg::W0, constant::status::Success);
} }
void CreateTransferMemory(DeviceState &state) { void CreateTransferMemory(DeviceState &state) {
u64 address = state.nce->GetRegister(Xreg::X1); u64 addr = state.nce->GetRegister(Xreg::X1);
if ((addr & (PAGE_SIZE - 1U))) {
state.nce->SetRegister(Wreg::W0, constant::status::InvAddress);
state.logger->Warn("svcCreateTransferMemory: 'address' not page aligned: {}", addr);
return;
}
u64 size = state.nce->GetRegister(Xreg::X2); u64 size = state.nce->GetRegister(Xreg::X2);
u32 perms = state.nce->GetRegister(Wreg::W3); if ((size & (PAGE_SIZE - 1U)) || !size) {
auto shmem = state.thisProcess->NewHandle<type::KTransferMemory>(state.thisProcess->mainThread, address, size, *reinterpret_cast<memory::Permission *>(&perms)); state.nce->SetRegister(Wreg::W0, constant::status::InvSize);
state.logger->Warn("svcCreateTransferMemory: 'size' {}: {}", size ? "not page aligned" : "is zero", size);
return;
}
u32 perm = state.nce->GetRegister(Wreg::W3);
memory::Permission permission = *reinterpret_cast<memory::Permission *>(&perm);
if ((permission.w && !permission.r) || (permission.x && !permission.r)) {
state.logger->Warn("svcCreateTransferMemory: 'permission' invalid: {}{}{}", permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-");
state.nce->SetRegister(Wreg::W0, constant::status::InvPermission);
return;
}
state.logger->Debug("svcCreateTransferMemory: Creating transfer memory at 0x{:X} for {} bytes ({}{}{})", addr, size, permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-");
auto shmem = state.thisProcess->NewHandle<type::KTransferMemory>(state.thisProcess->mainThread, addr, size, permission);
state.nce->SetRegister(Wreg::W0, constant::status::Success); state.nce->SetRegister(Wreg::W0, constant::status::Success);
state.nce->SetRegister(Wreg::W1, shmem.handle); state.nce->SetRegister(Wreg::W1, shmem.handle);
} }
void ResetSignal(DeviceState &state) { void CloseHandle(DeviceState &state) {
auto handle = static_cast<handle_t>(state.nce->GetRegister(Wreg::W0));
try { try {
state.thisProcess->GetHandle<type::KEvent>(state.nce->GetRegister(Wreg::W0))->ResetSignal(); auto &object = state.thisProcess->handleTable.at(handle);
} catch (const exception &) { switch (object->objectType) {
state.thisProcess->GetHandle<type::KProcess>(state.nce->GetRegister(Wreg::W0))->ResetSignal(); case (type::KType::KThread):
state.os->KillThread(std::static_pointer_cast<type::KThread>(object)->pid);
break;
case (type::KType::KProcess):
state.os->KillThread(std::static_pointer_cast<type::KProcess>(object)->mainThread);
break;
default:
state.thisProcess->handleTable.erase(handle);
}
state.logger->Debug("svcCloseHandle: Closing handle: 0x{:X}", handle);
state.nce->SetRegister(Wreg::W0, constant::status::Success);
} catch(const std::exception&) {
state.logger->Warn("svcCloseHandle: 'handle' invalid: 0x{:X}", handle);
state.nce->SetRegister(Wreg::W0, constant::status::InvHandle);
}
}
void ResetSignal(DeviceState &state) {
auto handle = state.nce->GetRegister(Wreg::W0);
try {
auto &object = state.thisProcess->handleTable.at(handle);
switch (object->objectType) {
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.nce->SetRegister(Wreg::W0, constant::status::InvHandle);
return;
}
}
state.logger->Debug("svcResetSignal: Resetting signal: 0x{:X}", handle);
state.nce->SetRegister(Wreg::W0, constant::status::Success);
} catch(const std::out_of_range&) {
state.logger->Warn("svcResetSignal: 'handle' invalid: 0x{:X}", handle);
state.nce->SetRegister(Wreg::W0, constant::status::InvHandle);
return;
} }
state.nce->SetRegister(Wreg::W0, constant::status::Success);
} }
void WaitSynchronization(DeviceState &state) { void WaitSynchronization(DeviceState &state) {
@ -203,7 +329,7 @@ namespace skyline::kernel::svc {
} }
auto syncObject = std::static_pointer_cast<type::KSyncObject>(object); auto syncObject = std::static_pointer_cast<type::KSyncObject>(object);
if (syncObject->signalled) { if (syncObject->signalled) {
state.logger->Debug("Found signalled handle: 0x{:X}", handle); state.logger->Debug("svcWaitSynchronization: Signalled handle: 0x{:X}", handle);
state.nce->SetRegister(Wreg::W0, constant::status::Success); state.nce->SetRegister(Wreg::W0, constant::status::Success);
state.nce->SetRegister(Wreg::W1, index); state.nce->SetRegister(Wreg::W1, index);
state.thisThread->ClearWaitObjects(); state.thisThread->ClearWaitObjects();
@ -212,16 +338,91 @@ namespace skyline::kernel::svc {
state.thisThread->waitObjects.push_back(syncObject); state.thisThread->waitObjects.push_back(syncObject);
syncObject->waitThreads.emplace_back(state.thisThread->pid, index); syncObject->waitThreads.emplace_back(state.thisThread->pid, index);
} }
state.logger->Debug("Waiting on handles:\n{}Timeout: 0x{:X} ns", handleStr, state.nce->GetRegister(Xreg::X3)); auto timeout = state.nce->GetRegister(Xreg::X3);
state.logger->Debug("svcWaitSynchronization: Waiting on handles:\n{}Timeout: 0x{:X} ns", handleStr, timeout);
if (state.nce->GetRegister(Xreg::X3) != std::numeric_limits<u64>::max()) if (state.nce->GetRegister(Xreg::X3) != std::numeric_limits<u64>::max())
state.thisThread->timeout = GetCurrTimeNs() + state.nce->GetRegister(Xreg::X3); state.thisThread->timeout = GetCurrTimeNs() + timeout;
else else
state.thisThread->timeout = 0; state.thisThread->timeout = 0;
state.thisThread->status = type::KThread::Status::WaitSync; state.thisThread->status = type::KThread::Status::WaitSync;
} }
void ArbitrateLock(DeviceState &state) {
auto addr = state.nce->GetRegister(Xreg::X1);
if((addr & ((1UL << WORD_BIT) - 1U))) {
state.nce->SetRegister(Wreg::W0, constant::status::InvAddress);
state.logger->Warn("svcArbitrateLock: 'address' not word aligned: {}", addr);
return;
}
auto handle = state.nce->GetRegister(Wreg::W2);
if (handle != state.thisThread->handle)
throw exception("svcArbitrateLock: Called from another thread");
state.logger->Debug("svcArbitrateLock: Locking mutex at 0x{:X} for thread 0x{:X}", addr, handle);
state.thisProcess->MutexLock(addr);
state.nce->SetRegister(Wreg::W0, constant::status::Success);
}
void ArbitrateUnlock(DeviceState &state) {
auto addr = state.nce->GetRegister(Xreg::X0);
if((addr & ((1UL << WORD_BIT) - 1U))) {
state.nce->SetRegister(Wreg::W0, constant::status::InvAddress);
state.logger->Warn("svcArbitrateUnlock: 'address' not word aligned: {}", addr);
return;
}
state.logger->Debug("svcArbitrateUnlock: Unlocking mutex at 0x{:X}", addr);
state.thisProcess->MutexUnlock(addr);
state.nce->SetRegister(Wreg::W0, constant::status::Success);
}
void WaitProcessWideKeyAtomic(DeviceState &state) {
auto mtxAddr = state.nce->GetRegister(Xreg::X0);
if((mtxAddr & ((1UL << WORD_BIT) - 1U))) {
state.nce->SetRegister(Wreg::W0, constant::status::InvAddress);
state.logger->Warn("svcWaitProcessWideKeyAtomic: mutex address not word aligned: {}", mtxAddr);
return;
}
auto handle = state.nce->GetRegister(Wreg::W2);
if (handle != state.thisThread->handle)
throw exception("svcWaitProcessWideKeyAtomic: Called from another thread");
state.thisProcess->MutexUnlock(mtxAddr);
auto condAddr = state.nce->GetRegister(Xreg::X1);
auto &cvarVec = state.thisProcess->condVarMap[condAddr];
for (auto thread = cvarVec.begin();; thread++) {
if ((*thread)->priority < state.thisThread->priority) {
cvarVec.insert(thread, state.thisThread);
break;
} else if (thread + 1 == cvarVec.end()) {
cvarVec.push_back(state.thisThread);
break;
}
}
auto timeout = state.nce->GetRegister(Xreg::X3);
state.logger->Debug("svcWaitProcessWideKeyAtomic: Mutex: 0x{:X}, Conditional-Variable: 0x:{:X}, Timeout: {} ns", mtxAddr, condAddr, timeout);
state.thisThread->status = type::KThread::Status::WaitCondVar;
state.thisThread->timeout = GetCurrTimeNs() + timeout;
state.nce->SetRegister(Wreg::W0, constant::status::Success);
}
void SignalProcessWideKey(DeviceState &state) {
auto address = state.nce->GetRegister(Xreg::X0);
auto count = state.nce->GetRegister(Wreg::W1);
state.nce->SetRegister(Wreg::W0, constant::status::Success);
if (!state.thisProcess->condVarMap.count(address)) {
state.logger->Warn("svcSignalProcessWideKey: No Conditional-Variable at 0x{:X}", address);
return;
}
auto &cvarVec = state.thisProcess->condVarMap.at(address);
count = std::min(count, static_cast<u32>(cvarVec.size()));
for (uint index = 0; index < count; index++)
cvarVec[index]->status = type::KThread::Status::Runnable;
cvarVec.erase(cvarVec.begin(), cvarVec.begin() + count);
if (cvarVec.empty())
state.thisProcess->condVarMap.erase(address);
state.logger->Debug("svcSignalProcessWideKey: Signalling Conditional-Variable at 0x{:X} for {}", address, count);
}
void GetSystemTick(DeviceState &state) { void GetSystemTick(DeviceState &state) {
u64 tick{}; u64 tick;
asm("STR X1, [SP, #-16]!\n\t" asm("STR X1, [SP, #-16]!\n\t"
"MRS %0, CNTVCT_EL0\n\t" "MRS %0, CNTVCT_EL0\n\t"
"MOV X1, #0xF800\n\t" "MOV X1, #0xF800\n\t"
@ -233,60 +434,19 @@ namespace skyline::kernel::svc {
state.nce->SetRegister(Xreg::X0, tick); state.nce->SetRegister(Xreg::X0, tick);
} }
void ArbitrateLock(DeviceState &state) {
if (state.nce->GetRegister(Wreg::W2) != state.thisThread->handle)
throw exception("A process requested locking a thread on behalf of another process");
state.thisProcess->MutexLock(state.nce->GetRegister(Xreg::X1));
state.nce->SetRegister(Wreg::W0, constant::status::Success);
}
void ArbitrateUnlock(DeviceState &state) {
state.thisProcess->MutexUnlock(state.nce->GetRegister(Xreg::X0));
state.nce->SetRegister(Wreg::W0, constant::status::Success);
}
void WaitProcessWideKeyAtomic(DeviceState &state) {
auto mtxAddr = state.nce->GetRegister(Xreg::X0);
if (state.nce->GetRegister(Wreg::W2) != state.thisThread->handle)
throw exception("svcWaitProcessWideKeyAtomic was called on behalf of another thread");
state.thisProcess->MutexUnlock(mtxAddr);
auto &cvarVec = state.thisProcess->condVarMap[state.nce->GetRegister(Xreg::X1)];
for (auto thread = cvarVec.begin();; thread++) {
if ((*thread)->priority < state.thisThread->priority) {
cvarVec.insert(thread, state.thisThread);
break;
} else if (thread + 1 == cvarVec.end()) {
cvarVec.push_back(state.thisThread);
break;
}
}
state.thisThread->status = type::KThread::Status::WaitCondVar;
state.thisThread->timeout = GetCurrTimeNs() + state.nce->GetRegister(Xreg::X3);
state.nce->SetRegister(Wreg::W0, constant::status::Success);
}
void SignalProcessWideKey(DeviceState &state) {
auto address = state.nce->GetRegister(Xreg::X0);
auto count = state.nce->GetRegister(Wreg::W1);
state.nce->SetRegister(Wreg::W0, constant::status::Success);
if (!state.thisProcess->condVarMap.count(address))
return; // No threads to awaken
auto &cvarVec = state.thisProcess->condVarMap[address];
count = std::min(count, static_cast<u32>(cvarVec.size()));
for (uint index = 0; index < count; index++)
cvarVec[index]->status = type::KThread::Status::Runnable;
cvarVec.erase(cvarVec.begin(), cvarVec.begin() + count);
if (cvarVec.empty())
state.thisProcess->condVarMap.erase(address);
}
void ConnectToNamedPort(DeviceState &state) { void ConnectToNamedPort(DeviceState &state) {
char port[constant::PortSize + 1]{0}; // +1 so string will always be null terminated char port[constant::PortSize + 1]{0};
state.os->thisProcess->ReadMemory(port, state.nce->GetRegister(Xreg::X1), constant::PortSize); state.os->thisProcess->ReadMemory(port, state.nce->GetRegister(Xreg::X1), constant::PortSize);
handle_t handle{};
if (std::strcmp(port, "sm:") == 0) if (std::strcmp(port, "sm:") == 0)
state.nce->SetRegister(Wreg::W1, state.os->serviceManager.NewSession(service::Service::sm)); handle = state.os->serviceManager.NewSession(service::Service::sm);
else else {
throw exception("svcConnectToNamedPort tried connecting to invalid port: \"{}\"", port); state.logger->Warn("svcConnectToNamedPort: Connecting to invalid port: '{}'", port);
state.nce->SetRegister(Wreg::W0, constant::status::NotFound);
return;
}
state.logger->Debug("svcConnectToNamedPort: Connecting to port '{}' at 0x{:X}", port, handle);
state.nce->SetRegister(Wreg::W1, handle);
state.nce->SetRegister(Wreg::W0, constant::status::Success); state.nce->SetRegister(Wreg::W0, constant::status::Success);
} }
@ -297,11 +457,12 @@ namespace skyline::kernel::svc {
void GetThreadId(DeviceState &state) { void GetThreadId(DeviceState &state) {
pid_t pid{}; pid_t pid{};
if (state.nce->GetRegister(Wreg::W1) != constant::ThreadSelf) { auto handle = state.nce->GetRegister(Wreg::W1);
handle_t thread = state.nce->GetRegister(Wreg::W1); if (handle != constant::ThreadSelf) {
pid = state.thisProcess->GetHandle<type::KThread>(thread)->pid; pid = state.thisProcess->GetHandle<type::KThread>(handle)->pid;
} else } else
pid = state.thisThread->pid; pid = state.thisThread->pid;
state.logger->Debug("svcGetThreadId: Handle: 0x{:X}, PID: {}", handle, pid);
state.nce->SetRegister(Xreg::X1, static_cast<u64>(pid)); state.nce->SetRegister(Xreg::X1, static_cast<u64>(pid));
state.nce->SetRegister(Wreg::W0, constant::status::Success); state.nce->SetRegister(Wreg::W0, constant::status::Success);
} }
@ -314,65 +475,69 @@ namespace skyline::kernel::svc {
} }
void GetInfo(DeviceState &state) { void GetInfo(DeviceState &state) {
state.logger->Debug("svcGetInfo called with ID0: {}, ID1: {}", state.nce->GetRegister(Wreg::W1), state.nce->GetRegister(Xreg::X3)); auto id0 = state.nce->GetRegister(Wreg::W1);
switch (state.nce->GetRegister(Wreg::W1)) { auto handle = state.nce->GetRegister(Wreg::W2);
auto id1 = state.nce->GetRegister(Xreg::X3);
u64 out{};
switch (id0) {
case constant::infoState::AllowedCpuIdBitmask: case constant::infoState::AllowedCpuIdBitmask:
case constant::infoState::AllowedThreadPriorityMask: case constant::infoState::AllowedThreadPriorityMask:
case constant::infoState::IsCurrentProcessBeingDebugged: case constant::infoState::IsCurrentProcessBeingDebugged:
case constant::infoState::TitleId: case constant::infoState::TitleId:
case constant::infoState::PrivilegedProcessId: case constant::infoState::PrivilegedProcessId:
state.nce->SetRegister(Xreg::X1, 0);
break; break;
case constant::infoState::AliasRegionBaseAddr: case constant::infoState::AliasRegionBaseAddr:
state.nce->SetRegister(Xreg::X1, constant::MapAddr); out = constant::MapAddr;
break; break;
case constant::infoState::AliasRegionSize: case constant::infoState::AliasRegionSize:
state.nce->SetRegister(Xreg::X1, constant::MapSize); out = constant::MapSize;
break; break;
case constant::infoState::HeapRegionBaseAddr: case constant::infoState::HeapRegionBaseAddr:
state.nce->SetRegister(Xreg::X1, state.os->thisProcess->memoryRegionMap.at(memory::Region::Heap)->address); out = state.os->thisProcess->memoryRegionMap.at(memory::Region::Heap)->address;
break; break;
case constant::infoState::HeapRegionSize: case constant::infoState::HeapRegionSize:
state.nce->SetRegister(Xreg::X1, state.os->thisProcess->memoryRegionMap.at(memory::Region::Heap)->size); out = state.os->thisProcess->memoryRegionMap.at(memory::Region::Heap)->size;
break; break;
case constant::infoState::TotalMemoryAvailable: case constant::infoState::TotalMemoryAvailable:
state.nce->SetRegister(Xreg::X1, constant::TotalPhyMem); out = constant::TotalPhyMem;
break; break;
case constant::infoState::TotalMemoryUsage: case constant::infoState::TotalMemoryUsage:
state.nce->SetRegister(Xreg::X1, state.os->thisProcess->memoryRegionMap.at(memory::Region::Heap)->address + state.thisProcess->mainThreadStackSz + state.thisProcess->GetProgramSize()); out = state.os->thisProcess->memoryRegionMap.at(memory::Region::Heap)->address + state.thisProcess->mainThreadStackSz + state.thisProcess->GetProgramSize();
break; break;
case constant::infoState::AddressSpaceBaseAddr: case constant::infoState::AddressSpaceBaseAddr:
state.nce->SetRegister(Xreg::X1, constant::BaseAddr); out = constant::BaseAddr;
break; break;
case constant::infoState::AddressSpaceSize: case constant::infoState::AddressSpaceSize:
state.nce->SetRegister(Xreg::X1, constant::BaseSize); out = constant::BaseSize;
break; break;
case constant::infoState::StackRegionBaseAddr: case constant::infoState::StackRegionBaseAddr:
state.nce->SetRegister(Xreg::X1, state.thisThread->stackTop); out = state.thisThread->stackTop;
break; break;
case constant::infoState::StackRegionSize: case constant::infoState::StackRegionSize:
state.nce->SetRegister(Xreg::X1, state.thisProcess->mainThreadStackSz); out = state.thisProcess->mainThreadStackSz;
break; break;
case constant::infoState::PersonalMmHeapSize: case constant::infoState::PersonalMmHeapSize:
state.nce->SetRegister(Xreg::X1, constant::TotalPhyMem); out = constant::TotalPhyMem;
break; break;
case constant::infoState::PersonalMmHeapUsage: case constant::infoState::PersonalMmHeapUsage:
state.nce->SetRegister(Xreg::X1, state.os->thisProcess->memoryRegionMap.at(memory::Region::Heap)->address + state.thisProcess->mainThreadStackSz); out = state.os->thisProcess->memoryRegionMap.at(memory::Region::Heap)->address + state.thisProcess->mainThreadStackSz;
break; break;
case constant::infoState::TotalMemoryAvailableWithoutMmHeap: case constant::infoState::TotalMemoryAvailableWithoutMmHeap:
state.nce->SetRegister(Xreg::X1, constant::TotalPhyMem); // TODO: NPDM specifies SystemResourceSize, subtract that from this out = constant::TotalPhyMem; // TODO: NPDM specifies SystemResourceSize, subtract that from this
break; break;
case constant::infoState::TotalMemoryUsedWithoutMmHeap: case constant::infoState::TotalMemoryUsedWithoutMmHeap:
state.nce->SetRegister(Xreg::X1, state.os->thisProcess->memoryRegionMap.at(memory::Region::Heap)->address + state.thisProcess->mainThreadStackSz); // TODO: Same as above out = state.os->thisProcess->memoryRegionMap.at(memory::Region::Heap)->address + state.thisProcess->mainThreadStackSz; // TODO: Same as above
break; break;
case constant::infoState::UserExceptionContextAddr: case constant::infoState::UserExceptionContextAddr:
state.nce->SetRegister(Xreg::X1, state.thisProcess->tlsPages[0]->Get(0)); out = state.thisProcess->tlsPages[0]->Get(0);
break; break;
default: default:
state.logger->Warn("Unimplemented svcGetInfo with ID0: {}, ID1: {}", state.nce->GetRegister(Wreg::W1), state.nce->GetRegister(Xreg::X3)); state.logger->Warn("svcGetInfo: Unimplemented case ID0: {}, ID1: {}", id0, id1);
state.nce->SetRegister(Wreg::W0, constant::status::Unimpl); state.nce->SetRegister(Wreg::W0, constant::status::Unimpl);
return; return;
} }
state.logger->Debug("svcGetInfo: ID0: {}, ID1: {}, Out: 0x{:X}", id0, id1, out);
state.nce->SetRegister(Xreg::X1, out);
state.nce->SetRegister(Wreg::W0, constant::status::Success); state.nce->SetRegister(Wreg::W0, constant::status::Success);
} }
} }

View File

@ -91,16 +91,16 @@ namespace skyline {
*/ */
void MapSharedMemory(DeviceState &state); void MapSharedMemory(DeviceState &state);
/**
* @brief Closes the specified handle
*/
void CloseHandle(DeviceState &state);
/** /**
* @brief Returns a handle to a KSharedMemory object (https://switchbrew.org/wiki/SVC#svcCreateTransferMemory) * @brief Returns a handle to a KSharedMemory object (https://switchbrew.org/wiki/SVC#svcCreateTransferMemory)
*/ */
void CreateTransferMemory(DeviceState &state); void CreateTransferMemory(DeviceState &state);
/**
* @brief Closes the specified handle
*/
void CloseHandle(DeviceState &state);
/** /**
* @brief This resets a particular KEvent or KProcess which is signalled (https://switchbrew.org/wiki/SVC#svcResetSignal) * @brief This resets a particular KEvent or KProcess which is signalled (https://switchbrew.org/wiki/SVC#svcResetSignal)
*/ */
@ -110,12 +110,6 @@ namespace skyline {
* @brief Stalls a thread till a KSyncObject signals or the timeout has ended (https://switchbrew.org/wiki/SVC#svcWaitSynchronization) * @brief Stalls a thread till a KSyncObject signals or the timeout has ended (https://switchbrew.org/wiki/SVC#svcWaitSynchronization)
*/ */
void WaitSynchronization(DeviceState &state); void WaitSynchronization(DeviceState &state);
/**
* @brief This returns the value of CNTPCT_EL0 on the Switch (https://switchbrew.org/wiki/SVC#svcGetSystemTick)
*/
void GetSystemTick(DeviceState &state);
/** /**
* @brief Locks a specified mutex * @brief Locks a specified mutex
*/ */
@ -136,6 +130,11 @@ namespace skyline {
*/ */
void SignalProcessWideKey(DeviceState &state); void SignalProcessWideKey(DeviceState &state);
/**
* @brief This returns the value of CNTPCT_EL0 on the Switch (https://switchbrew.org/wiki/SVC#svcGetSystemTick)
*/
void GetSystemTick(DeviceState &state);
/** /**
* @brief Connects to a named IPC port * @brief Connects to a named IPC port
*/ */

View File

@ -24,8 +24,8 @@ namespace skyline::kernel::type {
this->address = fregs.regs[0]; this->address = fregs.regs[0];
} }
u64 RemapPrivateFunc(u64 address, size_t oldSize, size_t size, u64 perms) { u64 RemapPrivateFunc(u64 address, size_t oldSize, size_t size, u64 flags) {
return reinterpret_cast<u64>(mremap(reinterpret_cast<void *>(address), oldSize, size, 0)); return reinterpret_cast<u64>(mremap(reinterpret_cast<void *>(address), oldSize, size, static_cast<int>(flags)));
} }
u64 KPrivateMemory::Resize(size_t newSize, bool canMove) { u64 KPrivateMemory::Resize(size_t newSize, bool canMove) {
@ -33,7 +33,7 @@ namespace skyline::kernel::type {
fregs.regs[0] = address; fregs.regs[0] = address;
fregs.regs[1] = size; fregs.regs[1] = size;
fregs.regs[2] = newSize; fregs.regs[2] = newSize;
fregs.regs[3] = static_cast<u64>(PROT_READ | PROT_WRITE); fregs.regs[3] = canMove ? MREMAP_MAYMOVE : 0;
state.nce->ExecuteFunction(reinterpret_cast<void *>(RemapPrivateFunc), fregs, owner); state.nce->ExecuteFunction(reinterpret_cast<void *>(RemapPrivateFunc), fregs, owner);
if (reinterpret_cast<void *>(fregs.regs[0]) == MAP_FAILED) if (reinterpret_cast<void *>(fregs.regs[0]) == MAP_FAILED)
throw exception("An error occurred while remapping private region in child process"); throw exception("An error occurred while remapping private region in child process");

View File

@ -8,28 +8,26 @@ namespace skyline::kernel::type {
return reinterpret_cast<u64>(mmap(reinterpret_cast<void *>(address), size, static_cast<int>(perms), MAP_SHARED | ((address) ? MAP_FIXED : 0), static_cast<int>(fd), 0)); // NOLINT(hicpp-signed-bitwise) return reinterpret_cast<u64>(mmap(reinterpret_cast<void *>(address), size, static_cast<int>(perms), MAP_SHARED | ((address) ? MAP_FIXED : 0), static_cast<int>(fd), 0)); // NOLINT(hicpp-signed-bitwise)
} }
KSharedMemory::KSharedMemory(const DeviceState &state, pid_t pid, u64 kaddress, size_t ksize, const memory::Permission localPermission, const memory::Permission remotePermission, memory::Type type) : kaddress(kaddress), ksize(ksize), localPermission(localPermission), remotePermission(remotePermission), type(type), owner(pid), KObject(state, KType::KSharedMemory) { KSharedMemory::KSharedMemory(const DeviceState &state, u64 address, size_t size, const memory::Permission permission, memory::Type type) : type(type), KObject(state, KType::KSharedMemory) {
fd = ASharedMemory_create("", ksize); fd = ASharedMemory_create("", 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);
kaddress = MapSharedFunc(kaddress, ksize, static_cast<u64>(pid ? remotePermission.Get() : localPermission.Get()), static_cast<u64>(fd)); address = MapSharedFunc(address, size, static_cast<u64>(permission.Get()), static_cast<u64>(fd));
if (kaddress == reinterpret_cast<u64>(MAP_FAILED)) // NOLINT(hicpp-signed-bitwise) if (address == reinterpret_cast<u64>(MAP_FAILED)) // NOLINT(hicpp-signed-bitwise)
throw exception("An occurred while mapping shared region: {}", strerror(errno)); throw exception("An occurred while mapping shared region: {}", strerror(errno));
procInfMap[0] = {address, size, permission};
} }
u64 KSharedMemory::Map(u64 address, u64 size, pid_t process) { u64 KSharedMemory::Map(u64 address, u64 size, memory::Permission permission, pid_t pid) {
user_pt_regs fregs = {0}; user_pt_regs fregs = {0};
fregs.regs[0] = address; fregs.regs[0] = address;
fregs.regs[1] = size; fregs.regs[1] = size;
if (process == owner) fregs.regs[2] = static_cast<u64>(permission.Get());
fregs.regs[2] = static_cast<u64 >(localPermission.Get());
else
fregs.regs[2] = static_cast<u64>(remotePermission.Get());
fregs.regs[3] = static_cast<u64>(fd); fregs.regs[3] = static_cast<u64>(fd);
state.nce->ExecuteFunction(reinterpret_cast<void *>(MapSharedFunc), fregs, process); state.nce->ExecuteFunction(reinterpret_cast<void *>(MapSharedFunc), fregs, pid);
if (reinterpret_cast<void *>(fregs.regs[0]) == MAP_FAILED) if (reinterpret_cast<void *>(fregs.regs[0]) == MAP_FAILED)
throw exception("An error occurred while mapping shared region in child process"); throw exception("An error occurred while mapping shared region in child process");
procInfMap[process] = {fregs.regs[0], size}; procInfMap[pid] = {fregs.regs[0], size, permission};
return fregs.regs[0]; return fregs.regs[0];
} }
@ -40,14 +38,15 @@ namespace skyline::kernel::type {
KSharedMemory::~KSharedMemory() { KSharedMemory::~KSharedMemory() {
for (auto[process, procInf] : procInfMap) { for (auto[process, procInf] : procInfMap) {
try { try {
user_pt_regs fregs = {0}; if(process) {
fregs.regs[0] = procInf.address; user_pt_regs fregs = {0};
fregs.regs[1] = procInf.size; fregs.regs[0] = procInf.address;
state.nce->ExecuteFunction(reinterpret_cast<void *>(UnmapSharedFunc), fregs, process); fregs.regs[1] = procInf.size;
} catch (const std::exception &) { state.nce->ExecuteFunction(reinterpret_cast<void *>(UnmapSharedFunc), fregs, process);
} } else
UnmapSharedFunc(procInf.address, procInf.size);
} catch (const std::exception &) {}
} }
UnmapSharedFunc(kaddress, ksize);
close(fd); close(fd);
} }
@ -57,54 +56,53 @@ namespace skyline::kernel::type {
void KSharedMemory::Resize(size_t newSize) { void KSharedMemory::Resize(size_t newSize) {
for (auto&[process, procInf] : procInfMap) { for (auto&[process, procInf] : procInfMap) {
user_pt_regs fregs = {0}; if(process) {
fregs.regs[0] = procInf.address; user_pt_regs fregs = {0};
fregs.regs[1] = procInf.size; fregs.regs[0] = procInf.address;
fregs.regs[2] = newSize; fregs.regs[1] = procInf.size;
state.nce->ExecuteFunction(reinterpret_cast<void *>(RemapSharedFunc), fregs, process); fregs.regs[2] = newSize;
if (reinterpret_cast<void *>(fregs.regs[0]) == MAP_FAILED) state.nce->ExecuteFunction(reinterpret_cast<void *>(RemapSharedFunc), fregs, process);
throw exception("An error occurred while remapping shared region in child process"); if (reinterpret_cast<void *>(fregs.regs[0]) == MAP_FAILED)
throw exception("An error occurred while remapping shared region in child process");
} else {
if (RemapSharedFunc(procInf.address, procInf.size, newSize) == reinterpret_cast<u64>(MAP_FAILED))
throw exception("An occurred while remapping shared region: {}", strerror(errno));
}
procInf.size = newSize; procInf.size = newSize;
} }
if (RemapSharedFunc(kaddress, ksize, newSize) == reinterpret_cast<u64>(MAP_FAILED))
throw exception("An occurred while remapping shared region: {}", strerror(errno));
ksize = newSize;
} }
u64 UpdatePermissionSharedFunc(u64 address, size_t size, u64 perms) { u64 UpdatePermissionSharedFunc(u64 address, size_t size, u64 perms) {
return static_cast<u64>(mprotect(reinterpret_cast<void *>(address), size, static_cast<int>(perms))); return static_cast<u64>(mprotect(reinterpret_cast<void *>(address), size, static_cast<int>(perms)));
} }
void KSharedMemory::UpdatePermission(bool local, memory::Permission newPerms) { void KSharedMemory::UpdatePermission(pid_t pid, memory::Permission permission) {
for (auto&[process, procInf] : procInfMap) { for (auto&[process, procInf] : procInfMap) {
if ((local && process == owner) || (!local && process != owner)) { if(process) {
user_pt_regs fregs = {0}; user_pt_regs fregs = {0};
fregs.regs[0] = procInf.address; fregs.regs[0] = procInf.address;
fregs.regs[1] = procInf.size; fregs.regs[1] = procInf.size;
fregs.regs[2] = static_cast<u64>(newPerms.Get()); fregs.regs[2] = static_cast<u64>(procInf.permission.Get());
state.nce->ExecuteFunction(reinterpret_cast<void *>(UpdatePermissionSharedFunc), fregs, process); state.nce->ExecuteFunction(reinterpret_cast<void *>(UpdatePermissionSharedFunc), fregs, process);
if (static_cast<int>(fregs.regs[0]) == -1) if (static_cast<int>(fregs.regs[0]) == -1)
throw exception("An error occurred while updating shared region's permissions in child process"); throw exception("An error occurred while updating shared region's permissions in child process");
} else {
if (UpdatePermissionSharedFunc(procInf.address, procInf.size, static_cast<u64>(permission.Get())) == reinterpret_cast<u64>(MAP_FAILED))
throw exception("An occurred while remapping shared region: {}", strerror(errno));
} }
procInf.permission = permission;
} }
if ((local && owner == 0) || (!local && owner != 0))
if (mprotect(reinterpret_cast<void *>(kaddress), ksize, newPerms.Get()) == -1)
throw exception("An occurred while updating shared region's permissions: {}", strerror(errno));
if (local)
localPermission = newPerms;
else
remotePermission = newPerms;
} }
memory::MemoryInfo KSharedMemory::GetInfo(pid_t process) { memory::MemoryInfo KSharedMemory::GetInfo(pid_t pid) {
memory::MemoryInfo info{}; memory::MemoryInfo info{};
const auto &procInf = procInfMap.at(process); const auto &procInf = procInfMap.at(pid);
info.baseAddress = procInf.address; info.baseAddress = procInf.address;
info.size = procInf.size; info.size = procInf.size;
info.type = static_cast<u64>(type); info.type = static_cast<u64>(type);
info.memoryAttribute.isIpcLocked = (info.ipcRefCount > 0); info.memoryAttribute.isIpcLocked = (info.ipcRefCount > 0);
info.memoryAttribute.isDeviceShared = (info.deviceRefCount > 0); info.memoryAttribute.isDeviceShared = (info.deviceRefCount > 0);
info.perms = (process == owner) ? localPermission : remotePermission; info.perms = procInf.permission;
info.ipcRefCount = ipcRefCount; info.ipcRefCount = ipcRefCount;
info.deviceRefCount = deviceRefCount; info.deviceRefCount = deviceRefCount;
return info; return info;

View File

@ -18,36 +18,31 @@ namespace skyline::kernel::type {
struct ProcessInfo { struct ProcessInfo {
u64 address; u64 address;
size_t size; size_t size;
memory::Permission permission;
}; };
std::unordered_map<pid_t, ProcessInfo> procInfMap; //!< Maps from a PID to where the memory was mapped to std::unordered_map<pid_t, ProcessInfo> procInfMap; //!< Maps from a PID to where the memory was mapped to
pid_t owner; //!< The PID of the process owning this shared memory
u64 kaddress; //!< The address of the allocated memory for the kernel
size_t ksize; //!< The size of the allocated memory
u16 ipcRefCount{}; //!< The amount of reference to this memory for IPC u16 ipcRefCount{}; //!< The amount of reference to this memory for IPC
u16 deviceRefCount{}; //!< The amount of reference to this memory for IPC u16 deviceRefCount{}; //!< The amount of reference to this memory for IPC
memory::Permission localPermission; //!< The permission for the owner process
memory::Permission remotePermission; //!< The permission of any process except the owner process
memory::Type type; //!< The type of this memory allocation memory::Type type; //!< The type of this memory allocation
/** /**
* @param state The state of the device * @param state The state of the device
* @param pid The PID of the owner thread * @param address The address of the allocation on the kernel (Arbitrary is 0)
* @param kaddress The address of the allocation on the kernel (Arbitrary is 0) * @param size The size of the allocation on the kernel
* @param ksize The size of the allocation on the kernel * @param permission The permission of the kernel process
* @param localPermission The permission of the owner process
* @param remotePermission The permission of any process except the owner process
* @param type The type of the memory * @param type The type of the memory
*/ */
KSharedMemory(const DeviceState &state, pid_t pid, u64 kaddress, size_t ksize, const memory::Permission localPermission, const memory::Permission remotePermission, memory::Type type); KSharedMemory(const DeviceState &state, u64 address, size_t size, const memory::Permission permission, memory::Type type);
/** /**
* @brief Maps the shared memory at an address * @brief Maps the shared memory at an address
* @param address The address to map to (If NULL an arbitrary address is picked) * @param address The address to map to (If NULL an arbitrary address is picked)
* @param size The amount of shared memory to map * @param size The amount of shared memory to map
* @param process The PID of the process * @param permission The permission of the kernel process
* @param pid The PID of the process
* @return The address of the allocation * @return The address of the allocation
*/ */
u64 Map(u64 address, u64 size, pid_t process); u64 Map(const u64 address, const u64 size, memory::Permission permission, pid_t pid);
/** /**
* @brief Resize a chunk of memory as to change the size occupied by it * @brief Resize a chunk of memory as to change the size occupied by it
@ -57,16 +52,16 @@ namespace skyline::kernel::type {
/** /**
* Updates the permissions of a chunk of mapped memory * Updates the permissions of a chunk of mapped memory
* @param local If true change local permissions else change remote permissions * @param pid The PID of the requesting process
* @param perms The new permissions to be set for the memory * @param permission The new permissions to be set for the memory
*/ */
void UpdatePermission(bool local, memory::Permission newPerms); void UpdatePermission(pid_t pid, memory::Permission permission);
/** /**
* @param process The PID of the requesting process * @param pid The PID of the requesting process
* @return A Memory::MemoryInfo struct based on attributes of the memory * @return A Memory::MemoryInfo struct based on attributes of the memory
*/ */
memory::MemoryInfo GetInfo(pid_t process); memory::MemoryInfo GetInfo(pid_t pid);
/** /**
* @brief The destructor of shared memory, it deallocates the memory from all processes * @brief The destructor of shared memory, it deallocates the memory from all processes

View File

@ -13,10 +13,12 @@ namespace skyline::kernel::type {
} }
void KThread::Start() { void KThread::Start() {
if (pid == parent->mainThread) if(status == Status::Created) {
parent->status = KProcess::Status::Started; if (pid == parent->mainThread)
status = Status::Running; parent->status = KProcess::Status::Started;
state.nce->StartProcess(entryPoint, entryArg, stackTop, handle, pid); status = Status::Running;
state.nce->StartProcess(entryPoint, entryArg, stackTop, handle, pid);
}
} }
void KThread::UpdatePriority(u8 priority) { void KThread::UpdatePriority(u8 priority) {

View File

@ -62,15 +62,15 @@ namespace skyline::kernel {
} }
} }
void OS::SvcHandler(u16 svc) { void OS::SvcHandler(const u16 svc) {
if (svc::SvcTable[svc]) { try {
state.logger->Debug("SVC called 0x{:X}", svc); if (svc::SvcTable[svc]) {
(*svc::SvcTable[svc])(state); state.logger->Debug("SVC called 0x{:X}", svc);
} else (*svc::SvcTable[svc])(state);
throw exception("Unimplemented SVC 0x{:X}", svc); } else
} throw exception("Unimplemented SVC 0x{:X}", svc);
} catch(const exception& e) {
std::shared_ptr<kernel::type::KSharedMemory> OS::MapSharedKernel(const u64 address, const size_t size, const memory::Permission kernelPermission, const memory::Permission remotePermission, const memory::Type type) { throw exception("{} (SVC: {})", e.what(), svc);
return std::make_shared<kernel::type::KSharedMemory>(state, 0, address, size, kernelPermission, remotePermission, type); }
} }
} }

View File

@ -55,18 +55,6 @@ namespace skyline::kernel {
* @brief Handles a particular SuperVisor Call * @brief Handles a particular SuperVisor Call
* @param svc The ID of the SVC to be called * @param svc The ID of the SVC to be called
*/ */
void SvcHandler(u16 svc); void SvcHandler(const u16 svc);
/**
* @brief Map a chunk of shared memory (Use only when kernel should be owner process else create KSharedMemory directly)
* @param address The address to map to (Can be 0 if address doesn't matter)
* @param size The size of the chunk of memory
* @param localPermission The permissions of the memory for the kernel
* @param remotePermission The permissions of the memory for the processes
* @param type The type of the memory
* @param region The specific region this memory is mapped for
* @return A shared pointer to the kernel::type::KSharedMemory object
*/
std::shared_ptr<kernel::type::KSharedMemory> MapSharedKernel(const u64 address, const size_t size, const memory::Permission kernelPermission, const memory::Permission remotePermission, const memory::Type type);
}; };
} }

View File

@ -21,7 +21,7 @@ namespace skyline::service::am {
void ICommonStateGetter::GetEventHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void ICommonStateGetter::GetEventHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto handle = state.thisProcess->InsertItem(messageEvent); auto handle = state.thisProcess->InsertItem(messageEvent);
state.logger->Info("Event Handle: 0x{:X}", handle); state.logger->Debug("Event Handle: 0x{:X}", handle);
response.copyHandles.push_back(handle); response.copyHandles.push_back(handle);
} }

View File

@ -7,9 +7,9 @@ namespace skyline::service::hid {
}) {} }) {}
void IAppletResource::GetSharedMemoryHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void IAppletResource::GetSharedMemoryHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
hidSharedMemory = state.os->MapSharedKernel(0, constant::hidSharedMemSize, memory::Permission(true, false, false), memory::Permission(true, true, false), memory::Type::SharedMemory); hidSharedMemory = std::make_shared<kernel::type::KSharedMemory>(state, NULL, constant::hidSharedMemSize, memory::Permission(true, false, false), memory::Type::SharedMemory);
auto handle = state.thisProcess->InsertItem<type::KSharedMemory>(hidSharedMemory); auto handle = state.thisProcess->InsertItem<type::KSharedMemory>(hidSharedMemory);
state.logger->Info("Writing HID SHM: {}", handle); state.logger->Debug("HID Shared Memory Handle: {}", handle);
response.copyHandles.push_back(handle); response.copyHandles.push_back(handle);
} }

View File

@ -42,9 +42,10 @@ namespace skyline::service::nvdrv {
u32 fd; u32 fd;
u32 eventId; u32 eventId;
} *input = reinterpret_cast<InputStruct *>(request.cmdArg); } *input = reinterpret_cast<InputStruct *>(request.cmdArg);
state.logger->Info("QueryEvent: FD: {}, Event ID: {}", input->fd, input->eventId);
auto event = std::make_shared<type::KEvent>(state); auto event = std::make_shared<type::KEvent>(state);
response.copyHandles.push_back(state.thisProcess->InsertItem<type::KEvent>(event)); auto handle = state.thisProcess->InsertItem<type::KEvent>(event);
state.logger->Debug("QueryEvent: FD: {}, Event ID: {}, Handle: {}", input->fd, input->eventId, handle);
response.copyHandles.push_back(handle);
} }
void nvdrv::SetAruidByPID(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void nvdrv::SetAruidByPID(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {

View File

@ -108,7 +108,7 @@ namespace skyline::service::vi {
void IApplicationDisplayService::GetDisplayVsyncEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void IApplicationDisplayService::GetDisplayVsyncEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
handle_t handle = state.thisProcess->InsertItem(state.gpu->vsyncEvent); handle_t handle = state.thisProcess->InsertItem(state.gpu->vsyncEvent);
state.logger->Info("VSyncEvent Handle: 0x{:X}", handle); state.logger->Debug("VSync Event Handle: 0x{:X}", handle);
response.copyHandles.push_back(handle); response.copyHandles.push_back(handle);
} }