mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-28 08:15:29 +03:00
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:
parent
26a67f70b7
commit
1956a3bbbb
@ -40,7 +40,8 @@ namespace skyline {
|
||||
constexpr u64 MapSize = 0x1000000000; //!< The size of the map region
|
||||
constexpr u64 TotalPhyMem = 0xF8000000; // ~4 GB of RAM
|
||||
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 u8 TlsSlots = PAGE_SIZE / TlsSlotSize; //!< The amount of TLS slots in a single page
|
||||
// Loader
|
||||
@ -76,14 +77,18 @@ namespace skyline {
|
||||
// Status codes
|
||||
namespace status {
|
||||
constexpr u32 Success = 0x0; //!< "Success"
|
||||
constexpr u32 NoMessages = 0x680; //!< "No message available"
|
||||
constexpr u32 ServiceInvName = 0xC15; //!< "Invalid name"
|
||||
constexpr u32 ServiceNotReg = 0xE15; //!< "Service not registered"
|
||||
constexpr u32 InvSize = 0xCA01; //!< "Invalid size"
|
||||
constexpr u32 InvAddress = 0xCC01; //!< "Invalid address"
|
||||
constexpr u32 InvPermission = 0xE001; //!< "Invalid Permission"
|
||||
constexpr u32 InvHandle = 0xE401; //!< "Invalid handle"
|
||||
constexpr u32 InvCombination = 0xE801; //!< "Invalid combination"
|
||||
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 NoMessages = 0x680; //!< "No message available"
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -21,7 +21,7 @@ namespace skyline::gpu::device {
|
||||
handleTable[handleIndex] = std::make_shared<NvMapObject>(idIndex++, data.size);
|
||||
data.handle = handleIndex++;
|
||||
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) {
|
||||
@ -41,7 +41,7 @@ namespace skyline::gpu::device {
|
||||
state.thisProcess->WriteMemory(data, buffer.output[0].address);
|
||||
else
|
||||
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) {
|
||||
@ -61,7 +61,7 @@ namespace skyline::gpu::device {
|
||||
object->kind = data.kind;
|
||||
object->address = data.address;
|
||||
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) {
|
||||
@ -125,7 +125,7 @@ namespace skyline::gpu::device {
|
||||
break;
|
||||
}
|
||||
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) {
|
||||
@ -135,6 +135,6 @@ namespace skyline::gpu::device {
|
||||
} data = state.thisProcess->ReadMemory<Data>(buffer.input[0].address);
|
||||
data.id = handleTable.at(data.handle)->id;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -1,46 +1,74 @@
|
||||
#include "svc.h"
|
||||
#include <os.h>
|
||||
#include <kernel/types/KTransferMemory.h>
|
||||
|
||||
namespace skyline::kernel::svc {
|
||||
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;
|
||||
try {
|
||||
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 &) {
|
||||
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);
|
||||
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(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) {
|
||||
u64 addr = state.nce->GetRegister(Xreg::X0);
|
||||
u64 size = state.nce->GetRegister(Xreg::X1);
|
||||
bool isUncached = (state.nce->GetRegister(Wreg::W2) == 8) && (state.nce->GetRegister(Wreg::W3) == 8);
|
||||
const u64 addr = state.nce->GetRegister(Xreg::X0);
|
||||
if((addr & (PAGE_SIZE - 1))) {
|
||||
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;
|
||||
for (const auto&[address, region] : state.thisProcess->memoryMap) {
|
||||
if (addr >= address && addr < (address + region->size)) {
|
||||
bool subFound = false;
|
||||
for (auto &subregion : region->regionInfoVec) {
|
||||
if ((address >= subregion.address) && (address < (subregion.address + subregion.size)))
|
||||
subregion.isUncached = isUncached;
|
||||
subregion.isUncached = attribute.isUncached;
|
||||
subFound = true;
|
||||
break;
|
||||
}
|
||||
if (!subFound)
|
||||
region->regionInfoVec.emplace_back(addr, size, isUncached);
|
||||
region->regionInfoVec.emplace_back(addr, size, static_cast<bool>(attribute.isUncached));
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
state.logger->Debug("svcSetMemoryAttribute set caching to {} at 0x{:X} for 0x{:X} bytes", !isUncached, addr, size);
|
||||
state.nce->SetRegister(Wreg::W0, found ? constant::status::Success : constant::status::InvAddress);
|
||||
if(!found) {
|
||||
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) {
|
||||
@ -81,34 +109,49 @@ namespace skyline::kernel::svc {
|
||||
.size = static_cast<u64>(-constant::BaseEnd + 1),
|
||||
.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.nce->SetRegister(Wreg::W0, constant::status::Success);
|
||||
}
|
||||
|
||||
void ExitProcess(DeviceState &state) {
|
||||
state.logger->Debug("svcExitProcess: Exiting current process: {}", state.thisProcess->mainThread);
|
||||
state.os->KillThread(state.thisProcess->mainThread);
|
||||
}
|
||||
|
||||
void CreateThread(DeviceState &state) {
|
||||
// TODO: Support Core Mask potentially
|
||||
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 entryAddr = state.nce->GetRegister(Xreg::X1);
|
||||
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::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) {
|
||||
auto &object = state.thisProcess->handleTable.at(static_cast<const unsigned int &>(state.nce->GetRegister(Wreg::W0)));
|
||||
if (object->objectType == type::KType::KThread)
|
||||
std::static_pointer_cast<type::KThread>(object)->Start();
|
||||
else
|
||||
throw exception("StartThread was called on a non-KThread object");
|
||||
auto handle = state.nce->GetRegister(Wreg::W0);
|
||||
try {
|
||||
auto thread = state.thisProcess->GetHandle<type::KThread>(handle);
|
||||
state.logger->Debug("svcStartThread: Starting thread: 0x{:X}, {}", handle, thread->pid);
|
||||
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) {
|
||||
state.logger->Debug("svcExitProcess: Exiting current thread: {}", state.thisThread->pid);
|
||||
state.os->KillThread(state.thisThread->pid);
|
||||
}
|
||||
|
||||
@ -118,62 +161,145 @@ namespace skyline::kernel::svc {
|
||||
case 0:
|
||||
case 1:
|
||||
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
|
||||
break;
|
||||
default:
|
||||
state.logger->Debug("svcSleepThread: Thread sleeping for {} ns", in);
|
||||
state.thisThread->timeout = GetCurrTimeNs() + in;
|
||||
state.thisThread->status = type::KThread::Status::Sleeping;
|
||||
}
|
||||
}
|
||||
|
||||
void GetThreadPriority(DeviceState &state) {
|
||||
state.nce->SetRegister(Wreg::W1, state.thisProcess->GetHandle<type::KThread>(static_cast<handle_t>(state.nce->GetRegister(Wreg::W0)))->priority);
|
||||
state.nce->SetRegister(Wreg::W0, constant::status::Success);
|
||||
auto handle = state.nce->GetRegister(Wreg::W0);
|
||||
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) {
|
||||
state.thisProcess->GetHandle<type::KThread>(static_cast<handle_t>(state.nce->GetRegister(Wreg::W0)))->Start();
|
||||
state.nce->SetRegister(Wreg::W0, constant::status::Success);
|
||||
auto handle = state.nce->GetRegister(Wreg::W0);
|
||||
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) {
|
||||
auto object = state.thisProcess->GetHandle<type::KSharedMemory>(static_cast<handle_t>(state.nce->GetRegister(Wreg::W0)));
|
||||
object->Map(state.nce->GetRegister(Xreg::X1), state.nce->GetRegister(Xreg::X2), state.thisProcess->mainThread);
|
||||
state.nce->SetRegister(Wreg::W0, constant::status::Success);
|
||||
}
|
||||
|
||||
void CloseHandle(DeviceState &state) {
|
||||
auto handle = static_cast<handle_t>(state.nce->GetRegister(Wreg::W0));
|
||||
state.logger->Debug("Closing handle: 0x{:X}", handle);
|
||||
auto &object = state.thisProcess->handleTable.at(handle);
|
||||
switch (object->objectType) {
|
||||
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);
|
||||
try {
|
||||
auto object = state.thisProcess->GetHandle<type::KSharedMemory>(state.nce->GetRegister(Wreg::W0));
|
||||
u64 addr = state.nce->GetRegister(Xreg::X1);
|
||||
if ((addr & (PAGE_SIZE - 1U))) {
|
||||
state.nce->SetRegister(Wreg::W0, constant::status::InvAddress);
|
||||
state.logger->Warn("svcMapSharedMemory: 'address' not page aligned: 0x{:X}", addr);
|
||||
return;
|
||||
}
|
||||
const u64 size = state.nce->GetRegister(Xreg::X2);
|
||||
if ((size & (PAGE_SIZE - 1U)) || !size) {
|
||||
state.nce->SetRegister(Wreg::W0, constant::status::InvSize);
|
||||
state.logger->Warn("svcMapSharedMemory: '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("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) {
|
||||
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);
|
||||
u32 perms = state.nce->GetRegister(Wreg::W3);
|
||||
auto shmem = state.thisProcess->NewHandle<type::KTransferMemory>(state.thisProcess->mainThread, address, size, *reinterpret_cast<memory::Permission *>(&perms));
|
||||
if ((size & (PAGE_SIZE - 1U)) || !size) {
|
||||
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::W1, shmem.handle);
|
||||
}
|
||||
|
||||
void ResetSignal(DeviceState &state) {
|
||||
void CloseHandle(DeviceState &state) {
|
||||
auto handle = static_cast<handle_t>(state.nce->GetRegister(Wreg::W0));
|
||||
try {
|
||||
state.thisProcess->GetHandle<type::KEvent>(state.nce->GetRegister(Wreg::W0))->ResetSignal();
|
||||
} catch (const exception &) {
|
||||
state.thisProcess->GetHandle<type::KProcess>(state.nce->GetRegister(Wreg::W0))->ResetSignal();
|
||||
auto &object = state.thisProcess->handleTable.at(handle);
|
||||
switch (object->objectType) {
|
||||
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) {
|
||||
@ -203,7 +329,7 @@ namespace skyline::kernel::svc {
|
||||
}
|
||||
auto syncObject = std::static_pointer_cast<type::KSyncObject>(object);
|
||||
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::W1, index);
|
||||
state.thisThread->ClearWaitObjects();
|
||||
@ -212,16 +338,91 @@ namespace skyline::kernel::svc {
|
||||
state.thisThread->waitObjects.push_back(syncObject);
|
||||
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())
|
||||
state.thisThread->timeout = GetCurrTimeNs() + state.nce->GetRegister(Xreg::X3);
|
||||
state.thisThread->timeout = GetCurrTimeNs() + timeout;
|
||||
else
|
||||
state.thisThread->timeout = 0;
|
||||
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) {
|
||||
u64 tick{};
|
||||
u64 tick;
|
||||
asm("STR X1, [SP, #-16]!\n\t"
|
||||
"MRS %0, CNTVCT_EL0\n\t"
|
||||
"MOV X1, #0xF800\n\t"
|
||||
@ -233,60 +434,19 @@ namespace skyline::kernel::svc {
|
||||
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) {
|
||||
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);
|
||||
handle_t handle{};
|
||||
if (std::strcmp(port, "sm:") == 0)
|
||||
state.nce->SetRegister(Wreg::W1, state.os->serviceManager.NewSession(service::Service::sm));
|
||||
else
|
||||
throw exception("svcConnectToNamedPort tried connecting to invalid port: \"{}\"", port);
|
||||
handle = state.os->serviceManager.NewSession(service::Service::sm);
|
||||
else {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -297,11 +457,12 @@ namespace skyline::kernel::svc {
|
||||
|
||||
void GetThreadId(DeviceState &state) {
|
||||
pid_t pid{};
|
||||
if (state.nce->GetRegister(Wreg::W1) != constant::ThreadSelf) {
|
||||
handle_t thread = state.nce->GetRegister(Wreg::W1);
|
||||
pid = state.thisProcess->GetHandle<type::KThread>(thread)->pid;
|
||||
auto handle = state.nce->GetRegister(Wreg::W1);
|
||||
if (handle != constant::ThreadSelf) {
|
||||
pid = state.thisProcess->GetHandle<type::KThread>(handle)->pid;
|
||||
} else
|
||||
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(Wreg::W0, constant::status::Success);
|
||||
}
|
||||
@ -314,65 +475,69 @@ namespace skyline::kernel::svc {
|
||||
}
|
||||
|
||||
void GetInfo(DeviceState &state) {
|
||||
state.logger->Debug("svcGetInfo called with ID0: {}, ID1: {}", state.nce->GetRegister(Wreg::W1), state.nce->GetRegister(Xreg::X3));
|
||||
switch (state.nce->GetRegister(Wreg::W1)) {
|
||||
auto id0 = 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::AllowedThreadPriorityMask:
|
||||
case constant::infoState::IsCurrentProcessBeingDebugged:
|
||||
case constant::infoState::TitleId:
|
||||
case constant::infoState::PrivilegedProcessId:
|
||||
state.nce->SetRegister(Xreg::X1, 0);
|
||||
break;
|
||||
case constant::infoState::AliasRegionBaseAddr:
|
||||
state.nce->SetRegister(Xreg::X1, constant::MapAddr);
|
||||
out = constant::MapAddr;
|
||||
break;
|
||||
case constant::infoState::AliasRegionSize:
|
||||
state.nce->SetRegister(Xreg::X1, constant::MapSize);
|
||||
out = constant::MapSize;
|
||||
break;
|
||||
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;
|
||||
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;
|
||||
case constant::infoState::TotalMemoryAvailable:
|
||||
state.nce->SetRegister(Xreg::X1, constant::TotalPhyMem);
|
||||
out = constant::TotalPhyMem;
|
||||
break;
|
||||
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;
|
||||
case constant::infoState::AddressSpaceBaseAddr:
|
||||
state.nce->SetRegister(Xreg::X1, constant::BaseAddr);
|
||||
out = constant::BaseAddr;
|
||||
break;
|
||||
case constant::infoState::AddressSpaceSize:
|
||||
state.nce->SetRegister(Xreg::X1, constant::BaseSize);
|
||||
out = constant::BaseSize;
|
||||
break;
|
||||
case constant::infoState::StackRegionBaseAddr:
|
||||
state.nce->SetRegister(Xreg::X1, state.thisThread->stackTop);
|
||||
out = state.thisThread->stackTop;
|
||||
break;
|
||||
case constant::infoState::StackRegionSize:
|
||||
state.nce->SetRegister(Xreg::X1, state.thisProcess->mainThreadStackSz);
|
||||
out = state.thisProcess->mainThreadStackSz;
|
||||
break;
|
||||
case constant::infoState::PersonalMmHeapSize:
|
||||
state.nce->SetRegister(Xreg::X1, constant::TotalPhyMem);
|
||||
out = constant::TotalPhyMem;
|
||||
break;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
case constant::infoState::UserExceptionContextAddr:
|
||||
state.nce->SetRegister(Xreg::X1, state.thisProcess->tlsPages[0]->Get(0));
|
||||
out = state.thisProcess->tlsPages[0]->Get(0);
|
||||
break;
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -91,16 +91,16 @@ namespace skyline {
|
||||
*/
|
||||
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)
|
||||
*/
|
||||
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)
|
||||
*/
|
||||
@ -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)
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@ -136,6 +130,11 @@ namespace skyline {
|
||||
*/
|
||||
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
|
||||
*/
|
||||
|
@ -24,8 +24,8 @@ namespace skyline::kernel::type {
|
||||
this->address = fregs.regs[0];
|
||||
}
|
||||
|
||||
u64 RemapPrivateFunc(u64 address, size_t oldSize, size_t size, u64 perms) {
|
||||
return reinterpret_cast<u64>(mremap(reinterpret_cast<void *>(address), oldSize, size, 0));
|
||||
u64 RemapPrivateFunc(u64 address, size_t oldSize, size_t size, u64 flags) {
|
||||
return reinterpret_cast<u64>(mremap(reinterpret_cast<void *>(address), oldSize, size, static_cast<int>(flags)));
|
||||
}
|
||||
|
||||
u64 KPrivateMemory::Resize(size_t newSize, bool canMove) {
|
||||
@ -33,7 +33,7 @@ namespace skyline::kernel::type {
|
||||
fregs.regs[0] = address;
|
||||
fregs.regs[1] = size;
|
||||
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);
|
||||
if (reinterpret_cast<void *>(fregs.regs[0]) == MAP_FAILED)
|
||||
throw exception("An error occurred while remapping private region in child process");
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
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) {
|
||||
fd = ASharedMemory_create("", ksize);
|
||||
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("", size);
|
||||
if (fd < 0)
|
||||
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));
|
||||
if (kaddress == reinterpret_cast<u64>(MAP_FAILED)) // NOLINT(hicpp-signed-bitwise)
|
||||
address = MapSharedFunc(address, size, static_cast<u64>(permission.Get()), static_cast<u64>(fd));
|
||||
if (address == reinterpret_cast<u64>(MAP_FAILED)) // NOLINT(hicpp-signed-bitwise)
|
||||
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};
|
||||
fregs.regs[0] = address;
|
||||
fregs.regs[1] = size;
|
||||
if (process == owner)
|
||||
fregs.regs[2] = static_cast<u64 >(localPermission.Get());
|
||||
else
|
||||
fregs.regs[2] = static_cast<u64>(remotePermission.Get());
|
||||
fregs.regs[2] = static_cast<u64>(permission.Get());
|
||||
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)
|
||||
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];
|
||||
}
|
||||
|
||||
@ -40,14 +38,15 @@ namespace skyline::kernel::type {
|
||||
KSharedMemory::~KSharedMemory() {
|
||||
for (auto[process, procInf] : procInfMap) {
|
||||
try {
|
||||
user_pt_regs fregs = {0};
|
||||
fregs.regs[0] = procInf.address;
|
||||
fregs.regs[1] = procInf.size;
|
||||
state.nce->ExecuteFunction(reinterpret_cast<void *>(UnmapSharedFunc), fregs, process);
|
||||
} catch (const std::exception &) {
|
||||
}
|
||||
if(process) {
|
||||
user_pt_regs fregs = {0};
|
||||
fregs.regs[0] = procInf.address;
|
||||
fregs.regs[1] = procInf.size;
|
||||
state.nce->ExecuteFunction(reinterpret_cast<void *>(UnmapSharedFunc), fregs, process);
|
||||
} else
|
||||
UnmapSharedFunc(procInf.address, procInf.size);
|
||||
} catch (const std::exception &) {}
|
||||
}
|
||||
UnmapSharedFunc(kaddress, ksize);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
@ -57,54 +56,53 @@ namespace skyline::kernel::type {
|
||||
|
||||
void KSharedMemory::Resize(size_t newSize) {
|
||||
for (auto&[process, procInf] : procInfMap) {
|
||||
user_pt_regs fregs = {0};
|
||||
fregs.regs[0] = procInf.address;
|
||||
fregs.regs[1] = procInf.size;
|
||||
fregs.regs[2] = newSize;
|
||||
state.nce->ExecuteFunction(reinterpret_cast<void *>(RemapSharedFunc), fregs, process);
|
||||
if (reinterpret_cast<void *>(fregs.regs[0]) == MAP_FAILED)
|
||||
throw exception("An error occurred while remapping shared region in child process");
|
||||
if(process) {
|
||||
user_pt_regs fregs = {0};
|
||||
fregs.regs[0] = procInf.address;
|
||||
fregs.regs[1] = procInf.size;
|
||||
fregs.regs[2] = newSize;
|
||||
state.nce->ExecuteFunction(reinterpret_cast<void *>(RemapSharedFunc), fregs, 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;
|
||||
}
|
||||
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) {
|
||||
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) {
|
||||
if ((local && process == owner) || (!local && process != owner)) {
|
||||
if(process) {
|
||||
user_pt_regs fregs = {0};
|
||||
fregs.regs[0] = procInf.address;
|
||||
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);
|
||||
if (static_cast<int>(fregs.regs[0]) == -1)
|
||||
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{};
|
||||
const auto &procInf = procInfMap.at(process);
|
||||
const auto &procInf = procInfMap.at(pid);
|
||||
info.baseAddress = procInf.address;
|
||||
info.size = procInf.size;
|
||||
info.type = static_cast<u64>(type);
|
||||
info.memoryAttribute.isIpcLocked = (info.ipcRefCount > 0);
|
||||
info.memoryAttribute.isDeviceShared = (info.deviceRefCount > 0);
|
||||
info.perms = (process == owner) ? localPermission : remotePermission;
|
||||
info.perms = procInf.permission;
|
||||
info.ipcRefCount = ipcRefCount;
|
||||
info.deviceRefCount = deviceRefCount;
|
||||
return info;
|
||||
|
@ -18,36 +18,31 @@ namespace skyline::kernel::type {
|
||||
struct ProcessInfo {
|
||||
u64 address;
|
||||
size_t size;
|
||||
memory::Permission permission;
|
||||
};
|
||||
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 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
|
||||
|
||||
/**
|
||||
* @param state The state of the device
|
||||
* @param pid The PID of the owner thread
|
||||
* @param kaddress The address of the allocation on the kernel (Arbitrary is 0)
|
||||
* @param ksize The size of the allocation on the kernel
|
||||
* @param localPermission The permission of the owner process
|
||||
* @param remotePermission The permission of any process except the owner process
|
||||
* @param address The address of the allocation on the kernel (Arbitrary is 0)
|
||||
* @param size The size of the allocation on the kernel
|
||||
* @param permission The permission of the kernel process
|
||||
* @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
|
||||
* @param address The address to map to (If NULL an arbitrary address is picked)
|
||||
* @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
|
||||
*/
|
||||
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
|
||||
@ -57,16 +52,16 @@ namespace skyline::kernel::type {
|
||||
|
||||
/**
|
||||
* Updates the permissions of a chunk of mapped memory
|
||||
* @param local If true change local permissions else change remote permissions
|
||||
* @param perms The new permissions to be set for the memory
|
||||
* @param pid The PID of the requesting process
|
||||
* @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
|
||||
*/
|
||||
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
|
||||
|
@ -13,10 +13,12 @@ namespace skyline::kernel::type {
|
||||
}
|
||||
|
||||
void KThread::Start() {
|
||||
if (pid == parent->mainThread)
|
||||
parent->status = KProcess::Status::Started;
|
||||
status = Status::Running;
|
||||
state.nce->StartProcess(entryPoint, entryArg, stackTop, handle, pid);
|
||||
if(status == Status::Created) {
|
||||
if (pid == parent->mainThread)
|
||||
parent->status = KProcess::Status::Started;
|
||||
status = Status::Running;
|
||||
state.nce->StartProcess(entryPoint, entryArg, stackTop, handle, pid);
|
||||
}
|
||||
}
|
||||
|
||||
void KThread::UpdatePriority(u8 priority) {
|
||||
|
@ -62,15 +62,15 @@ namespace skyline::kernel {
|
||||
}
|
||||
}
|
||||
|
||||
void OS::SvcHandler(u16 svc) {
|
||||
if (svc::SvcTable[svc]) {
|
||||
state.logger->Debug("SVC called 0x{:X}", svc);
|
||||
(*svc::SvcTable[svc])(state);
|
||||
} else
|
||||
throw exception("Unimplemented SVC 0x{:X}", svc);
|
||||
}
|
||||
|
||||
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) {
|
||||
return std::make_shared<kernel::type::KSharedMemory>(state, 0, address, size, kernelPermission, remotePermission, type);
|
||||
void OS::SvcHandler(const u16 svc) {
|
||||
try {
|
||||
if (svc::SvcTable[svc]) {
|
||||
state.logger->Debug("SVC called 0x{:X}", svc);
|
||||
(*svc::SvcTable[svc])(state);
|
||||
} else
|
||||
throw exception("Unimplemented SVC 0x{:X}", svc);
|
||||
} catch(const exception& e) {
|
||||
throw exception("{} (SVC: {})", e.what(), svc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,18 +55,6 @@ namespace skyline::kernel {
|
||||
* @brief Handles a particular SuperVisor Call
|
||||
* @param svc The ID of the SVC to be called
|
||||
*/
|
||||
void SvcHandler(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);
|
||||
void SvcHandler(const u16 svc);
|
||||
};
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ namespace skyline::service::am {
|
||||
|
||||
void ICommonStateGetter::GetEventHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -7,9 +7,9 @@ namespace skyline::service::hid {
|
||||
}) {}
|
||||
|
||||
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);
|
||||
state.logger->Info("Writing HID SHM: {}", handle);
|
||||
state.logger->Debug("HID Shared Memory Handle: {}", handle);
|
||||
response.copyHandles.push_back(handle);
|
||||
}
|
||||
|
||||
|
@ -42,9 +42,10 @@ namespace skyline::service::nvdrv {
|
||||
u32 fd;
|
||||
u32 eventId;
|
||||
} *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);
|
||||
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) {
|
||||
|
@ -108,7 +108,7 @@ namespace skyline::service::vi {
|
||||
|
||||
void IApplicationDisplayService::GetDisplayVsyncEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user