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!!) } }