From de6d8d8f4803550e54d25ab5dc16a5679ade9f9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=97=B1=20PixelyIon?= Date: Thu, 9 Jan 2020 07:07:54 +0530 Subject: [PATCH] Work on making the kernel thread-safe #1 + add asynchronous ROM scanning This commit makes the kernel thread safe in some instances (but not fully) and showcases the significantly improved performance over the ptrace method used prior. In addition, scanning for ROMs is now done asynchronously. --- app/src/main/cpp/skyline/common.h | 2 + app/src/main/cpp/skyline/gpu.cpp | 1 - app/src/main/cpp/skyline/gpu/display.cpp | 22 +- app/src/main/cpp/skyline/gpu/display.h | 5 +- app/src/main/cpp/skyline/kernel/svc.cpp | 4 +- .../cpp/skyline/kernel/svc.cpp___jb_old___ | 534 ++++++++++++++++++ .../skyline/kernel/types/KPrivateMemory.cpp | 15 +- .../skyline/kernel/types/KSharedMemory.cpp | 4 +- .../skyline/kernel/types/KTransferMemory.cpp | 10 +- app/src/main/cpp/skyline/loader/loader.h | 2 +- app/src/main/cpp/skyline/nce.cpp | 165 +++--- app/src/main/cpp/skyline/nce.h | 5 + app/src/main/cpp/skyline/nce/guest.cpp | 71 ++- app/src/main/cpp/skyline/nce/guest_common.h | 1 + app/src/main/cpp/skyline/nce/instr.h | 41 ++ .../skyline/services/am/appletController.cpp | 2 +- app/src/main/cpp/skyline/services/hid/hid.cpp | 2 +- .../skyline/services/nvnflinger/dispdrv.cpp | 6 +- .../main/cpp/skyline/services/serviceman.cpp | 5 + .../main/cpp/skyline/services/serviceman.h | 1 + app/src/main/java/emu/skyline/MainActivity.kt | 63 ++- 21 files changed, 804 insertions(+), 157 deletions(-) create mode 100644 app/src/main/cpp/skyline/kernel/svc.cpp___jb_old___ diff --git a/app/src/main/cpp/skyline/common.h b/app/src/main/cpp/skyline/common.h index 8e5186e3..6d69222f 100644 --- a/app/src/main/cpp/skyline/common.h +++ b/app/src/main/cpp/skyline/common.h @@ -5,6 +5,7 @@ #include #include #include +#include #import #include #include @@ -52,6 +53,7 @@ namespace skyline { constexpr std::pair PriorityAn = {19, -8}; //!< The range of priority for Android, taken from https://medium.com/mindorks/exploring-android-thread-priority-5d0542eebbd1 constexpr std::pair PriorityNin = {0, 63}; //!< The range of priority for the Nintendo Switch constexpr u32 MtxOwnerMask = 0xBFFFFFFF; //!< The mask of values which contain the owner of a mutex + constexpr u32 CheckInterval = 10000000; //!< The amount of cycles to wait between checking if the guest thread is dead // IPC constexpr size_t TlsIpcSize = 0x100; //!< The size of the IPC command buffer in a TLS slot constexpr u8 PortSize = 0x8; //!< The size of a port name string diff --git a/app/src/main/cpp/skyline/gpu.cpp b/app/src/main/cpp/skyline/gpu.cpp index 066946fe..f063bed6 100644 --- a/app/src/main/cpp/skyline/gpu.cpp +++ b/app/src/main/cpp/skyline/gpu.cpp @@ -42,7 +42,6 @@ namespace skyline::gpu { resolution = buffer->resolution; format = buffer->gbpBuffer.format; } - buffer->UpdateBuffer(); u8 *inBuffer = buffer->dataBuffer.data(); madvise(inBuffer, buffer->gbpBuffer.size, MADV_SEQUENTIAL); ANativeWindow_Buffer windowBuffer; diff --git a/app/src/main/cpp/skyline/gpu/display.cpp b/app/src/main/cpp/skyline/gpu/display.cpp index 3a243138..de399240 100644 --- a/app/src/main/cpp/skyline/gpu/display.cpp +++ b/app/src/main/cpp/skyline/gpu/display.cpp @@ -49,25 +49,22 @@ namespace skyline::gpu { state.logger->Debug("RequestBuffer: Slot: {}", slot, sizeof(GbpBuffer)); } - bool BufferQueue::DequeueBuffer(Parcel &in, Parcel &out, kernel::ipc::OutputBuffer& buffer) { + void BufferQueue::DequeueBuffer(Parcel &in, Parcel &out) { auto *data = reinterpret_cast(in.data.data() + constant::TokenLength); i64 slot{-1}; - for (auto &buffer : queue) { - if (buffer.second->status == BufferStatus::Free && buffer.second->resolution.width == data->width && buffer.second->resolution.height == data->height && buffer.second->gbpBuffer.usage == data->usage) { - slot = buffer.first; - buffer.second->status = BufferStatus::Dequeued; + while(slot == -1) { + for (auto &buffer : queue) { + if (buffer.second->status == BufferStatus::Free && buffer.second->resolution.width == data->width && buffer.second->resolution.height == data->height && buffer.second->gbpBuffer.usage == data->usage) { + slot = buffer.first; + buffer.second->status = BufferStatus::Dequeued; + break; + } } - } - if (slot == -1) { - state.thread->Sleep(); - waitVec.emplace_back(state.thread, *data, buffer); - state.logger->Debug("DequeueBuffer: Width: {}, Height: {}, Format: {}, Usage: {}, Timestamps: {}, No Free Buffers", data->width, data->height, data->format, data->usage, data->timestamps); - return true; + sched_yield(); } DequeueOut output(static_cast(slot)); out.WriteData(output); state.logger->Debug("DequeueBuffer: Width: {}, Height: {}, Format: {}, Usage: {}, Timestamps: {}, Slot: {}", data->width, data->height, data->format, data->usage, data->timestamps, slot); - return false; } void BufferQueue::QueueBuffer(Parcel &in, Parcel &out) { @@ -85,6 +82,7 @@ namespace skyline::gpu { } *data = reinterpret_cast(in.data.data() + constant::TokenLength); auto buffer = queue.at(data->slot); buffer->status = BufferStatus::Queued; + buffer->UpdateBuffer(); displayQueue.emplace(buffer); state.gpu->bufferEvent->Signal(); struct { diff --git a/app/src/main/cpp/skyline/gpu/display.h b/app/src/main/cpp/skyline/gpu/display.h index d91abd20..f6cec24d 100644 --- a/app/src/main/cpp/skyline/gpu/display.h +++ b/app/src/main/cpp/skyline/gpu/display.h @@ -106,6 +106,7 @@ namespace skyline::gpu { GbpBuffer gbpBuffer; //!< The information about the underlying buffer BufferStatus status{BufferStatus::Free}; //!< The status of this buffer std::vector dataBuffer; //!< The vector holding the actual pixel data + std::vector swizzBuffer; //!< The vector holding the swizzled pixel data std::shared_ptr nvBuffer{}; //!< A shared pointer to the buffer's nvmap object /** @@ -187,10 +188,8 @@ namespace skyline::gpu { /** * @brief This returns the slot of a free buffer - * @param buffer The output parcel buffer - * @return If the process is waiting for a buffer or not */ - bool DequeueBuffer(Parcel &in, Parcel &out, kernel::ipc::OutputBuffer& buffer); + void DequeueBuffer(Parcel &in, Parcel &out); /** * @brief This queues a buffer to be displayed diff --git a/app/src/main/cpp/skyline/kernel/svc.cpp b/app/src/main/cpp/skyline/kernel/svc.cpp index 2ef62af9..17fb6397 100644 --- a/app/src/main/cpp/skyline/kernel/svc.cpp +++ b/app/src/main/cpp/skyline/kernel/svc.cpp @@ -73,7 +73,7 @@ namespace skyline::kernel::svc { void QueryMemory(DeviceState &state) { memory::MemoryInfo memInfo{}; - u64 addr = state.ctx->registers.x2; + u64 addr = (state.ctx->registers.x2 & ~(PAGE_SIZE - 1)); bool found = false; for (const auto&[address, region] : state.process->memoryMap) { if (addr >= address && addr < (address + region->size)) { @@ -111,7 +111,7 @@ namespace skyline::kernel::svc { 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: {}{}{}", memInfo.baseAddress, memInfo.size, memInfo.type, static_cast(memInfo.memoryAttribute.isUncached), memInfo.r ? "R" : "-", memInfo.w ? "W" : "-", memInfo.x ? "X" : "-"); + state.logger->Debug("svcQueryMemory: Address: 0x{:X}, Size: 0x{:X}, Type: 0x{:X}, Is Uncached: {}, Permissions: {}{}{}", memInfo.baseAddress, memInfo.size, memInfo.type, static_cast(memInfo.memoryAttribute.isUncached), memInfo.r ? "R" : "-", memInfo.w ? "W" : "-", memInfo.x ? "X" : "-"); state.process->WriteMemory(memInfo, state.ctx->registers.x0); state.ctx->registers.w0 = constant::status::Success; } diff --git a/app/src/main/cpp/skyline/kernel/svc.cpp___jb_old___ b/app/src/main/cpp/skyline/kernel/svc.cpp___jb_old___ new file mode 100644 index 00000000..a3107ceb --- /dev/null +++ b/app/src/main/cpp/skyline/kernel/svc.cpp___jb_old___ @@ -0,0 +1,534 @@ +#include "svc.h" +#include + +namespace skyline::kernel::svc { + void SetHeapSize(DeviceState &state) { + const u32 size = state.ctx->registers.w1; + if(size%constant::HeapSizeDiv != 0) { + state.ctx->registers.x1 = 0; + state.ctx->registers.w0 = constant::status::InvSize; + state.logger->Warn("svcSetHeapSize: 'size' not divisible by 2MB: {}", size); + return; + } + std::shared_ptr heap; + try { + heap = state.process->memoryRegionMap.at(memory::Region::Heap); + heap->Resize(size, true); + } catch (const exception &) { + state.logger->Warn("svcSetHeapSize: Falling back to recreating memory"); + state.process->UnmapPrivateRegion(memory::Region::Heap); + heap = state.process->MapPrivateRegion(constant::HeapAddr, size, {true, true, false}, memory::Type::Heap, memory::Region::Heap).item; + } + state.ctx->registers.w0 = constant::status::Success; + state.ctx->registers.x1 = heap->address; + state.logger->Debug("svcSetHeapSize: Allocated at 0x{:X} for 0x{:X} bytes", heap->address, heap->size); + } + + void SetMemoryAttribute(DeviceState &state) { + const u64 addr = state.ctx->registers.x0; + if((addr & (PAGE_SIZE - 1U))) { + state.ctx->registers.w0 = constant::status::InvAddress; + state.logger->Warn("svcSetMemoryAttribute: 'address' not page aligned: {}", addr); + return; + } + const u64 size = state.ctx->registers.x1; + if((size & (PAGE_SIZE - 1U)) || !size) { + state.ctx->registers.w0 = constant::status::InvSize; + state.logger->Warn("svcSetMemoryAttribute: 'size' {}: {}", size ? "not page aligned" : "is zero", size); + return; + } + u32 mask = state.ctx->registers.w2; + u32 value = state.ctx->registers.w3; + u32 maskedValue = mask | value; + if(maskedValue != mask) { + state.ctx->registers.w0 = constant::status::InvCombination; + state.logger->Warn("svcSetMemoryAttribute: 'mask' invalid: 0x{:X}, 0x{:X}", mask, value); + return; + } + memory::MemoryAttribute attribute = *reinterpret_cast(&maskedValue); + bool found = false; + for (const auto&[address, region] : state.process->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 = attribute.isUncached; + subFound = true; + break; + } + if (!subFound) + region->regionInfoVec.emplace_back(addr, size, static_cast(attribute.isUncached)); + found = true; + break; + } + } + if(!found) { + state.ctx->registers.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.ctx->registers.w0 = constant::status::Success; + } + + void QueryMemory(DeviceState &state) { + memory::MemoryInfo memInfo{}; + u64 addr = (state.ctx->registers.x2 & ~(PAGE_SIZE-1)); + bool found = false; + for (const auto&[address, region] : state.process->memoryMap) { + if (addr >= address && addr < (address + region->size)) { + memInfo = region->GetInfo(addr); + found = true; + break; + } + } + if (!found) { + for (const auto &object : state.process->handleTable) { + if (object.second->objectType == type::KType::KSharedMemory) { + const auto &mem = state.process->GetHandle(object.first); + if (mem->guest.valid()) { + if (addr >= mem->guest.address && addr < (mem->guest.address + mem->guest.size)) { + memInfo = mem->GetInfo(); + found = true; + break; + } + } + } else if (object.second->objectType == type::KType::KTransferMemory) { + const auto &mem = state.process->GetHandle(object.first); + if (addr >= mem->cAddress && addr < (mem->cAddress + mem->cSize)) { + memInfo = mem->GetInfo(); + found = true; + break; + } + } + } + if (!found) { + memInfo = { + .baseAddress = constant::BaseAddr, + .size = static_cast(constant::BaseEnd), + .type = static_cast(memory::Type::Unmapped) + }; + state.logger->Debug("svcQueryMemory: Cannot find block of address: 0x{:X}", addr); + } + } + state.logger->Debug("svcQueryMemory: Address: 0x{:X}, Size: 0x{:X}, Type: 0x{:X}, Is Uncached: {}, Permissions: {}{}{}", memInfo.baseAddress, memInfo.size, memInfo.type, static_cast(memInfo.memoryAttribute.isUncached), memInfo.r ? "R" : "-", memInfo.w ? "W" : "-", memInfo.x ? "X" : "-"); + state.process->WriteMemory(memInfo, state.ctx->registers.x0); + state.ctx->registers.w0 = constant::status::Success; + } + + void ExitProcess(DeviceState &state) { + state.logger->Debug("svcExitProcess: Exiting current process: {}", state.process->pid); + state.os->KillThread(state.process->pid); + } + + void CreateThread(DeviceState &state) { + u64 entryAddr = state.ctx->registers.x1; + u64 entryArg = state.ctx->registers.x2; + u64 stackTop = state.ctx->registers.x3; + u8 priority = static_cast(state.ctx->registers.w4); + if((priority < constant::PriorityNin.first) && (priority > constant::PriorityNin.second)) { // NOLINT(misc-redundant-expression) + state.ctx->registers.w0 = constant::status::InvAddress; + state.logger->Warn("svcCreateThread: 'priority' invalid: {}", priority); + return; + } + auto thread = state.process->CreateThread(entryAddr, entryArg, stackTop, priority); + state.logger->Debug("svcCreateThread: Created thread with handle 0x{:X} (Entry Point: 0x{:X}, Argument: 0x{:X}, Stack Pointer: 0x{:X}, Priority: {}, PID: {})", thread->handle, entryAddr, entryArg, stackTop, priority, thread->pid); + state.ctx->registers.w1 = thread->handle; + state.ctx->registers.w0 = constant::status::Success; + } + + void StartThread(DeviceState &state) { + auto handle = state.ctx->registers.w0; + try { + auto thread = state.process->GetHandle(handle); + state.logger->Debug("svcStartThread: Starting thread: 0x{:X}, PID: {}", handle, thread->pid); + thread->Start(); + } catch (const std::exception&) { + state.logger->Warn("svcStartThread: 'handle' invalid: 0x{:X}", handle); + state.ctx->registers.w0 = constant::status::InvHandle; + } + } + + void ExitThread(DeviceState &state) { + state.logger->Debug("svcExitProcess: Exiting current thread: {}", state.thread->pid); + state.os->KillThread(state.thread->pid); + } + + void SleepThread(DeviceState &state) { + auto in = state.ctx->registers.x0; + switch (in) { + case 0: + case 1: + case 2: + state.logger->Debug("svcSleepThread: Yielding thread: {}", in); + state.thread->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.thread->timeout = GetCurrTimeNs() + in; + state.thread->status = type::KThread::Status::Sleeping; + } + } + + void GetThreadPriority(DeviceState &state) { + auto handle = state.ctx->registers.w0; + try { + auto priority = state.process->GetHandle(handle)->priority; + state.ctx->registers.w1 = priority; + state.ctx->registers.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.ctx->registers.w0 = constant::status::InvHandle; + } + } + + void SetThreadPriority(DeviceState &state) { + auto handle = state.ctx->registers.w0; + auto priority = state.ctx->registers.w1; + try { + state.process->GetHandle(handle)->UpdatePriority(static_cast(priority)); + state.ctx->registers.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.ctx->registers.w0 = constant::status::InvHandle; + } + } + + void MapSharedMemory(DeviceState &state) { + try { + auto object = state.process->GetHandle(state.ctx->registers.w0); + u64 addr = state.ctx->registers.x1; + if ((addr & (PAGE_SIZE - 1U))) { + state.ctx->registers.w0 = constant::status::InvAddress; + state.logger->Warn("svcMapSharedMemory: 'address' not page aligned: 0x{:X}", addr); + return; + } + const u64 size = state.ctx->registers.x2; + if ((size & (PAGE_SIZE - 1U)) || !size) { + state.ctx->registers.w0 = constant::status::InvSize; + state.logger->Warn("svcMapSharedMemory: 'size' {}: {}", size ? "not page aligned" : "is zero", size); + return; + } + u32 perm = state.ctx->registers.w3; + memory::Permission permission = *reinterpret_cast(&perm); + if ((permission.w && !permission.r) || (permission.x && !permission.r)) { + state.logger->Warn("svcMapSharedMemory: 'permission' invalid: {}{}{}", permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-"); + state.ctx->registers.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.ctx->registers.w0 = constant::status::Success; + } catch (const std::exception &) { + state.logger->Warn("svcMapSharedMemory: 'handle' invalid: 0x{:X}", state.ctx->registers.w0); + state.ctx->registers.w0 = constant::status::InvHandle; + } + } + + void CreateTransferMemory(DeviceState &state) { + u64 addr = state.ctx->registers.x1; + if ((addr & (PAGE_SIZE - 1U))) { + state.ctx->registers.w0 = constant::status::InvAddress; + state.logger->Warn("svcCreateTransferMemory: 'address' not page aligned: {}", addr); + return; + } + u64 size = state.ctx->registers.x2; + if ((size & (PAGE_SIZE - 1U)) || !size) { + state.ctx->registers.w0 = constant::status::InvSize; + state.logger->Warn("svcCreateTransferMemory: 'size' {}: {}", size ? "not page aligned" : "is zero", size); + return; + } + u32 perm = state.ctx->registers.w3; + memory::Permission permission = *reinterpret_cast(&perm); + if ((permission.w && !permission.r) || (permission.x && !permission.r)) { + state.logger->Warn("svcCreateTransferMemory: 'permission' invalid: {}{}{}", permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-"); + state.ctx->registers.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.process->NewHandle(state.process->pid, addr, size, permission); + state.ctx->registers.w0 = constant::status::Success; + state.ctx->registers.w1 = shmem.handle; + } + + void CloseHandle(DeviceState &state) { + auto handle = static_cast(state.ctx->registers.w0); + try { + state.process->handleTable.erase(handle); + state.logger->Debug("svcCloseHandle: Closing handle: 0x{:X}", handle); + state.ctx->registers.w0 = constant::status::Success; + } catch(const std::exception&) { + state.logger->Warn("svcCloseHandle: 'handle' invalid: 0x{:X}", handle); + state.ctx->registers.w0 = constant::status::InvHandle; + } + } + + void ResetSignal(DeviceState &state) { + auto handle = state.ctx->registers.w0; + try { + auto &object = state.process->handleTable.at(handle); + switch (object->objectType) { + case (type::KType::KEvent): + std::static_pointer_cast(object)->ResetSignal(); + break; + case (type::KType::KProcess): + std::static_pointer_cast(object)->ResetSignal(); + break; + default: { + state.logger->Warn("svcResetSignal: 'handle' type invalid: 0x{:X} ({})", handle, object->objectType); + state.ctx->registers.w0 = constant::status::InvHandle; + return; + } + } + state.logger->Debug("svcResetSignal: Resetting signal: 0x{:X}", handle); + state.ctx->registers.w0 = constant::status::Success; + } catch(const std::out_of_range&) { + state.logger->Warn("svcResetSignal: 'handle' invalid: 0x{:X}", handle); + state.ctx->registers.w0 = constant::status::InvHandle; + return; + } + } + + void WaitSynchronization(DeviceState &state) { + auto numHandles = state.ctx->registers.w2; + if (numHandles > constant::MaxSyncHandles) { + state.ctx->registers.w0 = constant::status::MaxHandles; + return; + } + std::vector waitHandles(numHandles); + state.process->ReadMemory(waitHandles.data(), state.ctx->registers.x1, numHandles * sizeof(handle_t)); + std::string handleStr; + uint index{}; + for (const auto &handle : waitHandles) { + handleStr += fmt::format("* 0x{:X}\n", handle); + auto object = state.process->handleTable.at(handle); + switch (object->objectType) { + case type::KType::KProcess: + case type::KType::KThread: + case type::KType::KEvent: + case type::KType::KSession: + break; + default: { + state.ctx->registers.w0 = constant::status::InvHandle; + state.thread->ClearWaitObjects(); + return; + } + } + auto syncObject = std::static_pointer_cast(object); + if (syncObject->signalled) { + state.logger->Debug("svcWaitSynchronization: Signalled handle: 0x{:X}", handle); + state.ctx->registers.w0 = constant::status::Success; + state.ctx->registers.w1 = index; + state.thread->ClearWaitObjects(); + return; + } + state.thread->waitObjects.push_back(syncObject); + syncObject->waitThreads.emplace_back(state.thread->pid, index); + } + auto timeout = state.ctx->registers.x3; + state.logger->Debug("svcWaitSynchronization: Waiting on handles:\n{}Timeout: 0x{:X} ns", handleStr, timeout); + if (state.ctx->registers.x3 != std::numeric_limits::max()) + state.thread->timeout = GetCurrTimeNs() + timeout; + else + state.thread->timeout = 0; + state.thread->status = type::KThread::Status::WaitSync; + } + + void ArbitrateLock(DeviceState &state) { + auto addr = state.ctx->registers.x1; + if((addr & ((1UL << WORD_BIT) - 1U))) { + state.ctx->registers.w0 = constant::status::InvAddress; + state.logger->Warn("svcArbitrateLock: 'address' not word aligned: {}", addr); + return; + } + auto handle = state.ctx->registers.w2; + if (handle != state.thread->handle) + throw exception("svcArbitrateLock: Called from another thread"); + state.logger->Debug("svcArbitrateLock: Locking mutex at 0x{:X} for thread 0x{:X}", addr, handle); + state.process->MutexLock(addr); + state.ctx->registers.w0 = constant::status::Success; + } + + void ArbitrateUnlock(DeviceState &state) { + auto addr = state.ctx->registers.x0; + if((addr & ((1UL << WORD_BIT) - 1U))) { + state.ctx->registers.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.process->MutexUnlock(addr); + state.ctx->registers.w0 = constant::status::Success; + } + + void WaitProcessWideKeyAtomic(DeviceState &state) { + auto mtxAddr = state.ctx->registers.x0; + if((mtxAddr & ((1UL << WORD_BIT) - 1U))) { + state.ctx->registers.w0 = constant::status::InvAddress; + state.logger->Warn("svcWaitProcessWideKeyAtomic: mutex address not word aligned: {}", mtxAddr); + return; + } + auto handle = state.ctx->registers.w2; + if (handle != state.thread->handle) + throw exception("svcWaitProcessWideKeyAtomic: Called from another thread"); + state.process->MutexUnlock(mtxAddr); + auto condAddr = state.ctx->registers.x1; + auto &cvarVec = state.process->condVarMap[condAddr]; + for (auto thread = cvarVec.begin();; thread++) { + if ((*thread)->priority < state.thread->priority) { + cvarVec.insert(thread, state.thread); + break; + } else if (thread + 1 == cvarVec.end()) { + cvarVec.push_back(state.thread); + break; + } + } + auto timeout = state.ctx->registers.x3; + state.logger->Debug("svcWaitProcessWideKeyAtomic: Mutex: 0x{:X}, Conditional-Variable: 0x:{:X}, Timeout: {} ns", mtxAddr, condAddr, timeout); + state.thread->status = type::KThread::Status::WaitCondVar; + state.thread->timeout = GetCurrTimeNs() + timeout; + state.ctx->registers.w0 = constant::status::Success; + } + + void SignalProcessWideKey(DeviceState &state) { + auto address = state.ctx->registers.x0; + auto count = state.ctx->registers.w1; + state.ctx->registers.w0 = constant::status::Success; + if (!state.process->condVarMap.count(address)) { + state.logger->Debug("svcSignalProcessWideKey: No Conditional-Variable at 0x{:X}", address); + return; + } + auto &cvarVec = state.process->condVarMap.at(address); + count = std::min(count, static_cast(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.process->condVarMap.erase(address); + state.logger->Debug("svcSignalProcessWideKey: Signalling Conditional-Variable at 0x{:X} for {}", address, count); + } + + void GetSystemTick(DeviceState &state) { + u64 tick; + asm("STR X1, [SP, #-16]!\n\t" + "MRS %0, CNTVCT_EL0\n\t" + "MOV X1, #0xF800\n\t" + "MOVK X1, #0x124, lsl #16\n\t" + "MUL %0, %0, X1\n\t" + "MRS X1, CNTFRQ_EL0\n\t" + "UDIV %0, %0, X1\n\t" + "LDR X1, [SP], #16" : "=r"(tick)); + state.ctx->registers.x0 = tick; + } + + void ConnectToNamedPort(DeviceState &state) { + char port[constant::PortSize + 1]{0}; + state.process->ReadMemory(port, state.ctx->registers.x1, constant::PortSize); + handle_t handle{}; + if (std::strcmp(port, "sm:") == 0) + handle = state.os->serviceManager.NewSession(service::Service::sm); + else { + state.logger->Warn("svcConnectToNamedPort: Connecting to invalid port: '{}'", port); + state.ctx->registers.w0 = constant::status::NotFound; + return; + } + state.logger->Debug("svcConnectToNamedPort: Connecting to port '{}' at 0x{:X}", port, handle); + state.ctx->registers.w1 = handle; + state.ctx->registers.w0 = constant::status::Success; + } + + void SendSyncRequest(DeviceState &state) { + state.os->serviceManager.SyncRequestHandler(static_cast(state.ctx->registers.x0)); + state.ctx->registers.w0 = constant::status::Success; + } + + void GetThreadId(DeviceState &state) { + pid_t pid{}; + auto handle = state.ctx->registers.w1; + if (handle != constant::ThreadSelf) { + pid = state.process->GetHandle(handle)->pid; + } else + pid = state.thread->pid; + state.logger->Debug("svcGetThreadId: Handle: 0x{:X}, PID: {}", handle, pid); + state.ctx->registers.x1 = static_cast(pid); + state.ctx->registers.w0 = constant::status::Success; + } + + void OutputDebugString(DeviceState &state) { + std::string debug(state.ctx->registers.x1, '\0'); + state.process->ReadMemory(debug.data(), state.ctx->registers.x0, state.ctx->registers.x1); + if(debug.back() == '\n') + debug.pop_back(); + state.logger->Info("Debug Output: {}", debug); + state.ctx->registers.w0 = constant::status::Success; + } + + void GetInfo(DeviceState &state) { + auto id0 = state.ctx->registers.w1; + auto handle = state.ctx->registers.w2; + auto id1 = state.ctx->registers.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: + break; + case constant::infoState::AliasRegionBaseAddr: + out = constant::MapAddr; + break; + case constant::infoState::AliasRegionSize: + out = constant::MapSize; + break; + case constant::infoState::HeapRegionBaseAddr: + out = state.process->memoryRegionMap.at(memory::Region::Heap)->address; + break; + case constant::infoState::HeapRegionSize: + out = state.process->memoryRegionMap.at(memory::Region::Heap)->size; + break; + case constant::infoState::TotalMemoryAvailable: + out = constant::TotalPhyMem; + break; + case constant::infoState::TotalMemoryUsage: + out = state.process->memoryRegionMap.at(memory::Region::Heap)->address + state.process->mainThreadStackSz + state.process->GetProgramSize(); + break; + case constant::infoState::AddressSpaceBaseAddr: + out = constant::BaseAddr; + break; + case constant::infoState::AddressSpaceSize: + out = constant::BaseEnd; + break; + case constant::infoState::StackRegionBaseAddr: + out = state.thread->stackTop; + break; + case constant::infoState::StackRegionSize: + out = state.process->mainThreadStackSz; + break; + case constant::infoState::PersonalMmHeapSize: + out = constant::TotalPhyMem; + break; + case constant::infoState::PersonalMmHeapUsage: + out = state.process->memoryRegionMap.at(memory::Region::Heap)->address + state.process->mainThreadStackSz; + break; + case constant::infoState::TotalMemoryAvailableWithoutMmHeap: + out = constant::TotalPhyMem; // TODO: NPDM specifies SystemResourceSize, subtract that from this + break; + case constant::infoState::TotalMemoryUsedWithoutMmHeap: + out = state.process->memoryRegionMap.at(memory::Region::Heap)->address + state.process->mainThreadStackSz; // TODO: Same as above + break; + case constant::infoState::UserExceptionContextAddr: + out = state.process->tlsPages[0]->Get(0); + break; + default: + state.logger->Warn("svcGetInfo: Unimplemented case ID0: {}, ID1: {}", id0, id1); + state.ctx->registers.w0 = constant::status::Unimpl; + return; + } + state.logger->Debug("svcGetInfo: ID0: {}, ID1: {}, Out: 0x{:X}", id0, id1, out); + state.ctx->registers.x1 = out; + state.ctx->registers.w0 = constant::status::Success; + } +} diff --git a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp index f4123be4..ab7855d4 100644 --- a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp @@ -60,8 +60,9 @@ namespace skyline::kernel::type { info.size = size; info.type = static_cast(type); for (const auto ®ion : regionInfoVec) - if ((address >= region.address) && (address < (region.address + region.size))) + if ((address >= region.address) && (address < (region.address + region.size))) { info.memoryAttribute.isUncached = region.isUncached; + } info.memoryAttribute.isIpcLocked = (info.ipcRefCount > 0); info.memoryAttribute.isDeviceShared = (info.deviceRefCount > 0); info.r = permission.r; @@ -78,11 +79,13 @@ namespace skyline::kernel::type { KPrivateMemory::~KPrivateMemory() { try { - Registers fregs{}; - fregs.x0 = address; - fregs.x1 = size; - fregs.x8 = __NR_munmap; - state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.thread); + if(state.process) { + Registers fregs{}; + fregs.x0 = address; + fregs.x1 = size; + fregs.x8 = __NR_munmap; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.process->pid); + } } catch (const std::exception &) {} } }; diff --git a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp index 6bd588e0..096d1130 100644 --- a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp @@ -42,12 +42,12 @@ namespace skyline::kernel::type { KSharedMemory::~KSharedMemory() { try { - if (guest.valid()) { + if (guest.valid() && state.process) { Registers fregs{}; fregs.x0 = guest.address; fregs.x1 = guest.size; fregs.x8 = __NR_munmap; - state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.thread->pid); + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.process->pid); } if (kernel.valid()) UnmapSharedFunc(kernel.address, kernel.size); diff --git a/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp index ce50fe22..2b2300f8 100644 --- a/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp @@ -96,10 +96,12 @@ namespace skyline::kernel::type { KTransferMemory::~KTransferMemory() { if (owner) { try { - Registers fregs{}; - fregs.x0 = cAddress; - fregs.x1 = cSize; - state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, owner); + if(state.process) { + Registers fregs{}; + fregs.x0 = cAddress; + fregs.x1 = cSize; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.process->pid); + } } catch (const std::exception &) {} } else UnmapTransferFunc(cAddress, cSize); diff --git a/app/src/main/cpp/skyline/loader/loader.h b/app/src/main/cpp/skyline/loader/loader.h index 087123cd..d81deda1 100644 --- a/app/src/main/cpp/skyline/loader/loader.h +++ b/app/src/main/cpp/skyline/loader/loader.h @@ -17,7 +17,7 @@ namespace skyline::loader { * @param size The amount to read in bytes */ template - void ReadOffset(T *output, u64 offset, size_t size) { + inline void ReadOffset(T *output, u64 offset, size_t size) { pread64(romFd, output, size, offset); } diff --git a/app/src/main/cpp/skyline/nce.cpp b/app/src/main/cpp/skyline/nce.cpp index 6ea110db..d22e49ac 100644 --- a/app/src/main/cpp/skyline/nce.cpp +++ b/app/src/main/cpp/skyline/nce.cpp @@ -1,5 +1,4 @@ #include -#include #include "os.h" #include "jvm.h" #include "nce/guest.h" @@ -11,27 +10,43 @@ extern skyline::Mutex jniMtx; namespace skyline { void NCE::KernelThread(pid_t thread) { - state.thread = state.process->threadMap.at(thread); - state.ctx = reinterpret_cast(state.thread->ctxMemory->guest.address); - while (!Halt) { - if (state.ctx->state == ThreadState::WaitKernel) { - const u16 svc = static_cast(state.ctx->commandId); - try { - if (kernel::svc::SvcTable[svc]) { - state.logger->Debug("SVC called 0x{:X}", svc); - (*kernel::svc::SvcTable[svc])(state); - } else - throw exception("Unimplemented SVC 0x{:X}", svc); - } catch (const exception &e) { - throw exception("{} (SVC: 0x{:X})", e.what(), svc); + try { + state.thread = state.process->threadMap.at(thread); + state.ctx = reinterpret_cast(state.thread->ctxMemory->guest.address); + while (!Halt) { + if (state.ctx->state == ThreadState::WaitKernel) { + const u16 svc = static_cast(state.ctx->commandId); + try { + if (kernel::svc::SvcTable[svc]) { + state.logger->Debug("SVC called 0x{:X}", svc); + (*kernel::svc::SvcTable[svc])(state); + } else + throw exception("Unimplemented SVC 0x{:X}", svc); + } catch (const std::exception &e) { + throw exception("{} (SVC: 0x{:X})", e.what(), svc); + } + state.ctx->state = ThreadState::WaitRun; + } else if (state.ctx->state == ThreadState::GuestCrash) { + state.logger->Warn("Thread with PID {} has crashed due to signal: {}", thread, strsignal(state.ctx->commandId)); + ThreadTrace(); + break; } - state.ctx->state = ThreadState::WaitRun; } + } catch (std::exception &e) { + state.logger->Error(e.what()); + } catch (...) { + state.logger->Error("An unknown exception has occurred"); } + state.os->KillThread(thread); } NCE::NCE(DeviceState &state) : state(state) {} + NCE::~NCE() { + for (auto &thread : threadMap) + thread.second->join(); + } + void NCE::Execute() { while (!Halt && state.os->process) { std::lock_guard jniGd(jniMtx); @@ -41,45 +56,38 @@ namespace skyline { Halt = false; } - void NCE::ExecuteFunction(ThreadCall call, Registers &funcRegs, std::shared_ptr& thread) { - auto ctx = reinterpret_cast(thread->ctxMemory->kernel.address); - u32 cmdId = ctx->commandId; - Registers registers = ctx->registers; - while(ctx->state != ThreadState::WaitInit && ctx->state != ThreadState::WaitKernel); - ctx->registers = funcRegs; + void ExecuteFunctionCtx(ThreadCall call, Registers &funcRegs, ThreadContext *ctx) { ctx->commandId = static_cast(call); + Registers registers = ctx->registers; + while (ctx->state != ThreadState::WaitInit && ctx->state != ThreadState::WaitKernel); + ctx->registers = funcRegs; ctx->state = ThreadState::WaitFunc; - while(ctx->state != ThreadState::WaitInit && ctx->state != ThreadState::WaitKernel); - ctx->commandId = cmdId; + while (ctx->state != ThreadState::WaitInit && ctx->state != ThreadState::WaitKernel); funcRegs = ctx->registers; ctx->registers = registers; } + void NCE::ExecuteFunction(ThreadCall call, Registers &funcRegs, std::shared_ptr &thread) { + ExecuteFunctionCtx(call, funcRegs, reinterpret_cast(thread->ctxMemory->kernel.address)); + } + void NCE::ExecuteFunction(ThreadCall call, Registers &funcRegs, pid_t pid) { - auto ctx = reinterpret_cast(state.process->threadMap.at(pid)->ctxMemory->kernel.address); - ctx->commandId = static_cast(call); - Registers registers = ctx->registers; - while(ctx->state != ThreadState::WaitInit || ctx->state != ThreadState::WaitKernel); - ctx->registers = funcRegs; - ctx->state = ThreadState::WaitFunc; - while(ctx->state != ThreadState::WaitInit || ctx->state != ThreadState::WaitKernel); - funcRegs = ctx->registers; - ctx->registers = registers; + ExecuteFunctionCtx(call, funcRegs, reinterpret_cast(state.process->threadMap.at(pid)->ctxMemory->kernel.address)); } - void NCE::WaitThreadInit(std::shared_ptr& thread) { + void NCE::WaitThreadInit(std::shared_ptr &thread) { auto ctx = reinterpret_cast(thread->ctxMemory->kernel.address); - while(ctx->state == ThreadState::NotReady); + while (ctx->state == ThreadState::NotReady); } void NCE::StartThread(u64 entryArg, u32 handle, std::shared_ptr &thread) { auto ctx = reinterpret_cast(thread->ctxMemory->kernel.address); - while(ctx->state != ThreadState::WaitInit); + while (ctx->state != ThreadState::WaitInit); ctx->tpidrroEl0 = thread->tls; ctx->registers.x0 = entryArg; ctx->registers.x1 = handle; ctx->state = ThreadState::WaitRun; - while(ctx->state != ThreadState::Running); + state.logger->Debug("Starting thread with PID: {}", thread->pid); threadMap[thread->pid] = std::make_shared(&NCE::KernelThread, this, thread->pid); } @@ -88,10 +96,10 @@ namespace skyline { std::string trace; std::string regStr; ctx = ctx ? ctx : state.ctx; - if(numHist) { + if (numHist) { std::vector instrs(numHist); u64 size = (sizeof(u32) * numHist); - u64 offset = ctx->pc - size; + u64 offset = ctx->pc - size + (2 * sizeof(u32)); state.process->ReadMemory(instrs.data(), offset, size); for (auto &instr : instrs) { instr = __builtin_bswap32(instr); @@ -103,16 +111,17 @@ namespace skyline { offset += sizeof(u32); } } - for (u16 index = 0; index < constant::NumRegs - 1; index+=2) { - regStr += fmt::format("\nX{}: 0x{:X}, X{}: 0x{:X}", index, ctx->registers.regs[index], index+1, ctx->registers.regs[index+1]); + for (u16 index = 0; index < constant::NumRegs - 1; index += 2) { + regStr += fmt::format("\nX{}: 0x{:X}, X{}: 0x{:X}", index, ctx->registers.regs[index], index + 1, ctx->registers.regs[index + 1]); } - if(numHist) - state.logger->Debug("Process Trace:{}\nRaw Instructions: 0x{}\nCPU Context:{}", trace, raw, regStr); - else + if (numHist) { + state.logger->Debug("Process Trace:{}", trace); + state.logger->Debug("Raw Instructions: 0x{}\nCPU Context:{}", raw, regStr); + } else state.logger->Warn("CPU Context:{}", regStr); } - const std::array cntpctEl0X0 = { + const std::array cntpctEl0X0 = { 0xA9BF0BE1, // STP X1, X2, [SP, #-16]! 0x3C9F0FE0, // STR Q0, [SP, #-16]! 0x3C9F0FE1, // STR Q1, [SP, #-16]! @@ -129,10 +138,10 @@ namespace skyline { 0x9E790000, // FCVTZU X0, D0 0x3CC107E2, // LDR Q2, [SP], #16 0x3CC107E1, // LDR Q1, [SP], #16 - 0xA97F07C0, // LDP X1, X2, [LR, #-16] + 0xA8C10BE1, // LDP X1, X2, [SP], #16 }; - const std::array cntpctEl0X1 = { + const std::array cntpctEl0X1 = { 0xA9BF0BE0, // STP X0, X2, [SP, #-16]! 0x3C9F0FE0, // STR Q0, [SP, #-16]! 0x3C9F0FE1, // STR Q1, [SP, #-16]! @@ -149,10 +158,10 @@ namespace skyline { 0x9E790001, // FCVTZU X0, D0 0x3CC107E2, // LDR Q2, [SP], #16 0x3CC107E1, // LDR Q1, [SP], #16 - 0xA97F0BC0, // LDP X0, X2, [LR, #-16] + 0xA8C10BE0, // LDP X0, X2, [SP], #16 }; - const std::array cntpctEl0Xn = { + std::array cntpctEl0Xn = { 0xA9BF07E0, // STP X0, X1, [SP, #-16]! 0x3C9F0FE0, // STR Q0, [SP, #-16]! 0x3C9F0FE1, // STR Q1, [SP, #-16]! @@ -166,10 +175,10 @@ namespace skyline { 0x9E630021, // UCVTF D1, X1 0x1E621800, // FDIV D0, D0, D2 0x1E610800, // FMUL D0, D0, D1 - 0x9E790000, // FCVTZU Xn, D0 + 0x00000000, // FCVTZU Xn, D0 (Set at runtime) 0x3CC107E2, // LDR Q2, [SP], #16 0x3CC107E1, // LDR Q1, [SP], #16 - 0xA97F07C0, // LDP X0, X1, [LR, #-16] + 0xA8C107E0, // LDP X0, X1, [SP], #16 }; std::vector NCE::PatchCode(std::vector &code, u64 baseAddress, i64 offset) { @@ -179,30 +188,29 @@ namespace skyline { std::vector patch((guest::saveCtxSize + guest::loadCtxSize + guest::svcHandlerSize) / sizeof(u32)); - std::memcpy(patch.data(), reinterpret_cast(&guest::saveCtx), guest::saveCtxSize); + std::memcpy(patch.data(), reinterpret_cast(&guest::saveCtx), guest::saveCtxSize); offset += guest::saveCtxSize; - std::memcpy(reinterpret_cast(patch.data()) + guest::saveCtxSize, - reinterpret_cast(&guest::loadCtx), guest::loadCtxSize); + std::memcpy(reinterpret_cast(patch.data()) + guest::saveCtxSize, + reinterpret_cast(&guest::loadCtx), guest::loadCtxSize); offset += guest::loadCtxSize; - std::memcpy(reinterpret_cast(patch.data()) + guest::saveCtxSize + guest::loadCtxSize, - reinterpret_cast(&guest::svcHandler), guest::svcHandlerSize); + std::memcpy(reinterpret_cast(patch.data()) + guest::saveCtxSize + guest::loadCtxSize, + reinterpret_cast(&guest::svcHandler), guest::svcHandlerSize); offset += guest::svcHandlerSize; - for (u32* address = start;address < end;address++) { + for (u32 *address = start; address < end; address++) { auto instrSvc = reinterpret_cast(address); auto instrMrs = reinterpret_cast(address); if (instrSvc->Verify()) { - u64 pc = baseAddress + (address - start); instr::B bjunc(offset); constexpr u32 strLr = 0xF81F0FFE; // STR LR, [SP, #-16]! offset += sizeof(strLr); instr::BL bSvCtx(patchOffset - offset); offset += sizeof(bSvCtx); - auto movPc = instr::MoveU64Reg(regs::X0, pc); + auto movPc = instr::MoveU64Reg(regs::X0, baseAddress + (address - start)); offset += sizeof(u32) * movPc.size(); instr::Movz movCmd(regs::W1, static_cast(instrSvc->value)); offset += sizeof(movCmd); @@ -219,18 +227,18 @@ namespace skyline { *address = bjunc.raw; patch.push_back(strLr); patch.push_back(bSvCtx.raw); - for(auto& instr : movPc) + for (auto &instr : movPc) patch.push_back(instr); patch.push_back(movCmd.raw); patch.push_back(bSvcHandler.raw); patch.push_back(bLdCtx.raw); patch.push_back(ldrLr); patch.push_back(bret.raw); - } else if (instrMrs->Verify()) { + } else if (instrMrs->Verify()) { if (instrMrs->srcReg == constant::TpidrroEl0) { instr::B bjunc(offset); u32 strX0{}; - if(instrMrs->destReg != regs::X0) { + if (instrMrs->destReg != regs::X0) { strX0 = 0xF81F0FE0; // STR X0, [SP, #-16]! offset += sizeof(strX0); } @@ -240,7 +248,7 @@ namespace skyline { offset += sizeof(ldrTls); u32 movXn{}; u32 ldrX0{}; - if(instrMrs->destReg != regs::X0) { + if (instrMrs->destReg != regs::X0) { movXn = instr::Mov(regs::X(instrMrs->destReg), regs::X0).raw; offset += sizeof(movXn); ldrX0 = 0xF84107E0; // LDR X0, [SP], #16 @@ -250,15 +258,21 @@ namespace skyline { offset += sizeof(bret); *address = bjunc.raw; - if(strX0) patch.push_back(strX0); + if (strX0) + patch.push_back(strX0); patch.push_back(mrsX0); patch.push_back(ldrTls); - if(movXn) patch.push_back(movXn); - if(ldrX0) patch.push_back(ldrX0); + if (movXn) + patch.push_back(movXn); + if (ldrX0) + patch.push_back(ldrX0); patch.push_back(bret.raw); - } if (instrMrs->srcReg == constant::CntpctEl0) { + } else if (instrMrs->srcReg == constant::CntpctEl0) { + instr::Mrs mrs(constant::CntvctEl0, regs::X(instrMrs->destReg)); + *address = mrs.raw; + /* instr::B bjunc(offset); - if(instrMrs->destReg == 0) + if (instrMrs->destReg == 0) offset += cntpctEl0X0.size() * sizeof(u32); else if (instrMrs->destReg == 1) offset += cntpctEl0X1.size() * sizeof(u32); @@ -268,17 +282,20 @@ namespace skyline { offset += sizeof(bret); *address = bjunc.raw; - if(instrMrs->destReg == 0) - for(auto& instr : cntpctEl0X0) + if (instrMrs->destReg == 0) + for (auto &instr : cntpctEl0X0) patch.push_back(instr); else if (instrMrs->destReg == 1) - for(auto& instr : cntpctEl0X1) + for (auto &instr : cntpctEl0X1) patch.push_back(instr); - else - for(auto& instr : cntpctEl0Xn) + else { + cntpctEl0Xn[13] = instr::Fcvtzu(regs::X(instrMrs->destReg), 0).raw; + for (auto &instr : cntpctEl0Xn) patch.push_back(instr); + } patch.push_back(bret.raw); - } else if (instrMrs->srcReg == constant::CntfrqEl0) { + */ + } else if (instrMrs->srcReg == constant::CntfrqEl0) { instr::B bjunc(offset); auto movFreq = instr::MoveU32Reg(static_cast(instrMrs->destReg), constant::TegraX1Freq); offset += sizeof(u32) * movFreq.size(); @@ -286,7 +303,7 @@ namespace skyline { offset += sizeof(bret); *address = bjunc.raw; - for(auto& instr : movFreq) + for (auto &instr : movFreq) patch.push_back(instr); patch.push_back(bret.raw); } diff --git a/app/src/main/cpp/skyline/nce.h b/app/src/main/cpp/skyline/nce.h index ab9baec7..83012756 100644 --- a/app/src/main/cpp/skyline/nce.h +++ b/app/src/main/cpp/skyline/nce.h @@ -26,6 +26,11 @@ namespace skyline { public: NCE(DeviceState &state); + /** + * @brief The destructor for NCE, this calls join() on all the threads + */ + ~NCE(); + /** * @brief This function is the main event loop of the program */ diff --git a/app/src/main/cpp/skyline/nce/guest.cpp b/app/src/main/cpp/skyline/nce/guest.cpp index 70f08115..8ecbb123 100644 --- a/app/src/main/cpp/skyline/nce/guest.cpp +++ b/app/src/main/cpp/skyline/nce/guest.cpp @@ -1,3 +1,6 @@ +#include +#include +#include #include "guest_common.h" #define FORCE_INLINE __attribute__((always_inline)) inline // NOLINT(cppcoreguidelines-macro-usage) @@ -88,30 +91,17 @@ namespace skyline::guest { } void svcHandler(u64 pc, u32 svc) { - volatile ThreadContext* ctx; + volatile ThreadContext *ctx; asm("MRS %0, TPIDR_EL0":"=r"(ctx)); ctx->pc = pc; ctx->commandId = svc; - ctx->state = ThreadState::WaitKernel; - while(ctx->state == ThreadState::WaitKernel); - if(ctx->state == ThreadState::WaitRun) { - return; - } else { + while (true) { ctx->state = ThreadState::WaitKernel; - while(ctx->state == ThreadState::WaitKernel); - } - } - - void entry(u64 address) { - volatile ThreadContext* ctx; - asm("MRS %0, TPIDR_EL0":"=r"(ctx)); - while(true) { - ctx->state = ThreadState::WaitInit; - while (ctx->state == ThreadState::WaitInit); + while (ctx->state == ThreadState::WaitKernel); if (ctx->state == ThreadState::WaitRun) - break; - else if(ctx->state == ThreadState::WaitFunc) { - if(ctx->commandId == static_cast(ThreadCall::Syscall)) { + return; + else if (ctx->state == ThreadState::WaitFunc) { + if (ctx->commandId == static_cast(ThreadCall::Syscall)) { saveCtxStack(); loadCtxTls(); asm("STR LR, [SP, #-16]!\n\t" @@ -124,6 +114,47 @@ namespace skyline::guest { } } } + } + + void signalHandler(int signal, siginfo_t *info, ucontext_t *ucontext) { + volatile ThreadContext *ctx; + asm("MRS %0, TPIDR_EL0":"=r"(ctx)); + for (u8 index = 0; index < constant::NumRegs; index++) + ctx->registers.regs[index] = ucontext->uc_mcontext.regs[index]; + ctx->pc = ucontext->uc_mcontext.pc; + ctx->commandId = static_cast(signal); + ctx->state = ThreadState::GuestCrash; + exit(0); + } + + void entry(u64 address) { + volatile ThreadContext *ctx; + asm("MRS %0, TPIDR_EL0":"=r"(ctx)); + while (true) { + ctx->state = ThreadState::WaitInit; + while (ctx->state == ThreadState::WaitInit); + if (ctx->state == ThreadState::WaitRun) + break; + else if (ctx->state == ThreadState::WaitFunc) { + if (ctx->commandId == static_cast(ThreadCall::Syscall)) { + saveCtxStack(); + loadCtxTls(); + asm("STR LR, [SP, #-16]!\n\t" + "MOV LR, SP\n\t" + "SVC #0\n\t" + "MOV SP, LR\n\t" + "LDR LR, [SP], #16"); + saveCtxTls(); + loadCtxStack(); + } + } + } + struct sigaction sigact{ + .sa_sigaction = reinterpret_cast(reinterpret_cast(signalHandler)), + .sa_flags = SA_SIGINFO, + }; + for (int signal : {SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV}) + sigaction(signal, &sigact, nullptr); ctx->state = ThreadState::Running; asm("MOV LR, %0\n\t" "MOV X0, %1\n\t" @@ -156,7 +187,7 @@ namespace skyline::guest { "MOV X27, XZR\n\t" "MOV X28, XZR\n\t" "MOV X29, XZR\n\t" - "RET" :: "r"(address), "r"(ctx->registers.x0), "r"(ctx->registers.x1) : "x0","x1","lr"); + "RET"::"r"(address), "r"(ctx->registers.x0), "r"(ctx->registers.x1) : "x0", "x1", "lr"); __builtin_unreachable(); } } diff --git a/app/src/main/cpp/skyline/nce/guest_common.h b/app/src/main/cpp/skyline/nce/guest_common.h index 47271473..09fd4de2 100644 --- a/app/src/main/cpp/skyline/nce/guest_common.h +++ b/app/src/main/cpp/skyline/nce/guest_common.h @@ -127,6 +127,7 @@ namespace skyline { WaitRun = 3, //!< The thread should be ready to run WaitInit = 4, //!< The thread is waiting to be initialized WaitFunc = 5, //!< The kernel is waiting for the thread to run a function + GuestCrash = 6, //!< This is a notification to the kernel that the guest has crashed }; /** diff --git a/app/src/main/cpp/skyline/nce/instr.h b/app/src/main/cpp/skyline/nce/instr.h index 7e86f8b9..4460ba8b 100644 --- a/app/src/main/cpp/skyline/nce/instr.h +++ b/app/src/main/cpp/skyline/nce/instr.h @@ -396,5 +396,46 @@ namespace skyline { }; }; static_assert(sizeof(Mov) == sizeof(u32)); + + /** + * @brief A bit-field struct that encapsulates a FCVTZU (Scalar, Integer) instruction. See https://developer.arm.com/docs/ddi0602/d/simd-and-floating-point-instructions-alphabetic-order/fcvtzu-scalar-integer-floating-point-convert-to-unsigned-integer-rounding-toward-zero-scalar. + */ + struct Fcvtzu { + public: + /** + * @brief Creates a FCVTZU (Scalar, Integer) instruction + * @param destReg The destination Xn register to store the value in + * @param srcReg The source Dn register to retrieve the value from + */ + Fcvtzu(regs::X destReg, u8 srcReg) { + this->destReg = static_cast(destReg); + this->srcReg = static_cast(srcReg); + sig0 = 0xE40; + ftype = 1; + sig1 = 0x1E; + sf = 1; + } + + /** + * @brief Returns if the opcode is valid or not + * @return If the opcode represents a valid FCVTZU instruction + */ + inline bool Verify() { + return (sig0 == 0xE40 && sig1 == 0x1E); + } + + union { + struct __attribute__((packed)) { + u8 destReg : 5; + u8 srcReg : 5; + u32 sig0 : 12; + u8 ftype : 2; + u8 sig1 : 7; + u8 sf : 1; + }; + u32 raw{}; + }; + }; + static_assert(sizeof(Fcvtzu) == sizeof(u32)); } } diff --git a/app/src/main/cpp/skyline/services/am/appletController.cpp b/app/src/main/cpp/skyline/services/am/appletController.cpp index b556c24c..eea63c32 100644 --- a/app/src/main/cpp/skyline/services/am/appletController.cpp +++ b/app/src/main/cpp/skyline/services/am/appletController.cpp @@ -9,9 +9,9 @@ namespace skyline::service::am { ICommonStateGetter::ICommonStateGetter(const DeviceState &state, ServiceManager &manager) : messageEvent(std::make_shared(state)), BaseService(state, manager, false, Service::am_ICommonStateGetter, { {0x0, SFUNC(ICommonStateGetter::GetEventHandle)}, {0x1, SFUNC(ICommonStateGetter::ReceiveMessage)}, - {0x9, SFUNC(ICommonStateGetter::GetCurrentFocusState)}, {0x5, SFUNC(ICommonStateGetter::GetOperationMode)}, {0x6, SFUNC(ICommonStateGetter::GetPerformanceMode)}, + {0x9, SFUNC(ICommonStateGetter::GetCurrentFocusState)}, {0x3C, SFUNC(ICommonStateGetter::GetDefaultDisplayResolution)} }) { operationMode = static_cast(state.settings->GetBool("operation_mode")); diff --git a/app/src/main/cpp/skyline/services/hid/hid.cpp b/app/src/main/cpp/skyline/services/hid/hid.cpp index f6b9fd62..d8d51645 100644 --- a/app/src/main/cpp/skyline/services/hid/hid.cpp +++ b/app/src/main/cpp/skyline/services/hid/hid.cpp @@ -9,7 +9,7 @@ namespace skyline::service::hid { void IAppletResource::GetSharedMemoryHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { hidSharedMemory = std::make_shared(state, NULL, constant::hidSharedMemSize, memory::Permission(true, false, false), memory::Type::SharedMemory); auto handle = state.process->InsertItem(hidSharedMemory); - state.logger->Debug("HID Shared Memory Handle: {}", handle); + state.logger->Debug("HID Shared Memory Handle: 0x{:X}", handle); response.copyHandles.push_back(handle); } diff --git a/app/src/main/cpp/skyline/services/nvnflinger/dispdrv.cpp b/app/src/main/cpp/skyline/services/nvnflinger/dispdrv.cpp index c14abc68..61594196 100644 --- a/app/src/main/cpp/skyline/services/nvnflinger/dispdrv.cpp +++ b/app/src/main/cpp/skyline/services/nvnflinger/dispdrv.cpp @@ -20,11 +20,9 @@ namespace skyline::service::nvnflinger { case TransactionCode::RequestBuffer: state.gpu->bufferQueue.RequestBuffer(in, out); break; - case TransactionCode::DequeueBuffer: { - if (state.gpu->bufferQueue.DequeueBuffer(in, out, request.outputBuf.at(0))) - return; + case TransactionCode::DequeueBuffer: + state.gpu->bufferQueue.DequeueBuffer(in, out); break; - } case TransactionCode::QueueBuffer: state.gpu->bufferQueue.QueueBuffer(in, out); break; diff --git a/app/src/main/cpp/skyline/services/serviceman.cpp b/app/src/main/cpp/skyline/services/serviceman.cpp index e6cbcbc7..8984a5f8 100644 --- a/app/src/main/cpp/skyline/services/serviceman.cpp +++ b/app/src/main/cpp/skyline/services/serviceman.cpp @@ -120,10 +120,12 @@ namespace skyline::service { } handle_t ServiceManager::NewSession(const Service serviceType) { + std::lock_guard serviceGuard(mutex); return state.process->NewHandle(GetService(serviceType)).handle; } std::shared_ptr ServiceManager::NewService(const std::string &serviceName, type::KSession &session, ipc::IpcResponse &response) { + std::lock_guard serviceGuard(mutex); auto serviceObject = GetService(ServiceString.at(serviceName)); handle_t handle{}; if (response.isDomain) { @@ -139,6 +141,7 @@ namespace skyline::service { } void ServiceManager::RegisterService(std::shared_ptr serviceObject, type::KSession &session, ipc::IpcResponse &response) { // NOLINT(performance-unnecessary-value-param) + std::lock_guard serviceGuard(mutex); handle_t handle{}; if (response.isDomain) { session.domainTable[session.handleIndex] = serviceObject; @@ -152,6 +155,7 @@ namespace skyline::service { } void ServiceManager::CloseSession(const handle_t handle) { + std::lock_guard serviceGuard(mutex); auto session = state.process->GetHandle(handle); if (session->serviceStatus == type::KSession::ServiceStatus::Open) { if (session->isDomain) { @@ -164,6 +168,7 @@ namespace skyline::service { }; void ServiceManager::Loop() { + std::lock_guard serviceGuard(mutex); for (auto& [type, service] : serviceMap) if (service->hasLoop) service->Loop(); diff --git a/app/src/main/cpp/skyline/services/serviceman.h b/app/src/main/cpp/skyline/services/serviceman.h index bd2e69d8..4cce43b8 100644 --- a/app/src/main/cpp/skyline/services/serviceman.h +++ b/app/src/main/cpp/skyline/services/serviceman.h @@ -12,6 +12,7 @@ namespace skyline::service { private: const DeviceState &state; //!< The state of the device std::unordered_map> serviceMap; //!< A mapping from a Service to the underlying object + skyline::Mutex mutex; //!< This mutex is used to ensure concurrent access to services doesn't cause crashes /** * @param serviceType The type of service requested diff --git a/app/src/main/java/emu/skyline/MainActivity.kt b/app/src/main/java/emu/skyline/MainActivity.kt index e994068c..c10605d8 100644 --- a/app/src/main/java/emu/skyline/MainActivity.kt +++ b/app/src/main/java/emu/skyline/MainActivity.kt @@ -20,23 +20,23 @@ import emu.skyline.adapter.GameAdapter import emu.skyline.adapter.GameItem import emu.skyline.loader.BaseLoader import emu.skyline.loader.NroLoader -import emu.skyline.loader.TitleEntry import emu.skyline.utility.GameDialog import emu.skyline.utility.RandomAccessDocument import kotlinx.android.synthetic.main.main_activity.* import java.io.File import java.io.IOException -import java.util.* +import kotlin.concurrent.thread class MainActivity : AppCompatActivity(), View.OnClickListener { private lateinit var sharedPreferences: SharedPreferences private var adapter = GameAdapter(this) + fun notifyUser(text: String) { Snackbar.make(findViewById(android.R.id.content), text, Snackbar.LENGTH_SHORT).show() } - private fun findFile(ext: String, loader: BaseLoader, directory: DocumentFile, entries: MutableList): MutableList { + private fun findFile(ext: String, loader: BaseLoader, directory: DocumentFile, entries: Int = 0): Int { var mEntries = entries for (file in directory.listFiles()) { if (file.isDirectory) { @@ -46,8 +46,16 @@ class MainActivity : AppCompatActivity(), View.OnClickListener { if (file.name != null) { if (ext.equals(file.name?.substring((file.name!!.lastIndexOf(".")) + 1), ignoreCase = true)) { val document = RandomAccessDocument(this, file) - if (loader.verifyFile(document)) - mEntries.add(loader.getTitleEntry(document, file.uri)) + if (loader.verifyFile(document)) { + val entry = loader.getTitleEntry(document, file.uri) + val header = (mEntries == 0) + runOnUiThread { + if(header) + adapter.addHeader(getString(R.string.nro)) + adapter.addItem(GameItem(entry)) + } + mEntries++ + } document.close() } } @@ -68,29 +76,32 @@ class MainActivity : AppCompatActivity(), View.OnClickListener { Log.w("refreshFiles", "Ran into exception while loading: " + e.message) } } - try { - adapter.clear() - val entries: List = findFile("nro", NroLoader(this), DocumentFile.fromTreeUri(this, Uri.parse(sharedPreferences.getString("search_location", "")))!!, ArrayList()) - if (entries.isNotEmpty()) { - adapter.addHeader(getString(R.string.nro)) - for (entry in entries) - adapter.addItem(GameItem(entry)) - } else { - adapter.addHeader(getString(R.string.no_rom)) - } + thread(start = true) { try { - adapter.save(File(applicationInfo.dataDir + "/roms.bin")) - } catch (e: IOException) { - Log.w("refreshFiles", "Ran into exception while saving: " + e.message) + runOnUiThread{adapter.clear()} + val entries = findFile("nro", NroLoader(this), DocumentFile.fromTreeUri(this, Uri.parse(sharedPreferences.getString("search_location", "")))!!) + runOnUiThread { + if (entries == 0) + adapter.addHeader(getString(R.string.no_rom)) + try { + adapter.save(File(applicationInfo.dataDir + "/roms.bin")) + } catch (e: IOException) { + Log.w("refreshFiles", "Ran into exception while saving: " + e.message) + } + } + sharedPreferences.edit().putBoolean("refresh_required", false).apply() + } catch (e: IllegalArgumentException) { + runOnUiThread { + sharedPreferences.edit().remove("search_location").apply() + val intent = intent + finish() + startActivity(intent) + } + } catch (e: Exception) { + runOnUiThread { + notifyUser(e.message!!) + } } - sharedPreferences.edit().putBoolean("refresh_required", false).apply() - } catch (e: IllegalArgumentException) { - sharedPreferences.edit().remove("search_location").apply() - val intent = intent - finish() - startActivity(intent) - } catch (e: Exception) { - notifyUser(e.message!!) } }