diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 852c4ca0..8069f74f 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -22,7 +22,8 @@ include_directories(${source_DIR}/skyline) add_library(skyline SHARED ${source_DIR}/main.cpp ${source_DIR}/skyline/common.cpp - ${source_DIR}/skyline/guest.S + ${source_DIR}/skyline/nce/guest.S + ${source_DIR}/skyline/nce/guest.cpp ${source_DIR}/skyline/nce.cpp ${source_DIR}/skyline/jvm.cpp ${source_DIR}/skyline/gpu.cpp diff --git a/app/src/main/cpp/skyline/common.cpp b/app/src/main/cpp/skyline/common.cpp index e66ca49e..2abbc7a0 100644 --- a/app/src/main/cpp/skyline/common.cpp +++ b/app/src/main/cpp/skyline/common.cpp @@ -5,18 +5,15 @@ namespace skyline { void Mutex::lock() { - while (flag.exchange(true, std::memory_order_relaxed)); - std::atomic_thread_fence(std::memory_order_acquire); + while (flag.test_and_set(std::memory_order_acquire)); } void Mutex::unlock() { - std::atomic_thread_fence(std::memory_order_release); - flag.store(false, std::memory_order_relaxed); + flag.clear(std::memory_order_release); } bool Mutex::try_lock() { - bool fal = false; - return flag.compare_exchange_strong(fal, true, std::memory_order_relaxed); + return !flag.test_and_set(std::memory_order_acquire); } Settings::Settings(const int preferenceFd) { @@ -90,10 +87,13 @@ namespace skyline { logFile.flush(); } - DeviceState::DeviceState(kernel::OS *os, std::shared_ptr &thisProcess, std::shared_ptr &thisThread, std::shared_ptr jvmManager, std::shared_ptr settings, std::shared_ptr logger) - : os(os), jvmManager(std::move(jvmManager)), settings(std::move(settings)), logger(std::move(logger)), process(thisProcess), thread(thisThread) { + DeviceState::DeviceState(kernel::OS *os, std::shared_ptr &process, std::shared_ptr jvmManager, std::shared_ptr settings, std::shared_ptr logger) + : os(os), jvmManager(std::move(jvmManager)), settings(std::move(settings)), logger(std::move(logger)), process(process) { // We assign these later as they use the state in their constructor and we don't want null pointers nce = std::move(std::make_shared(*this)); gpu = std::move(std::make_shared(*this)); } + + thread_local std::shared_ptr DeviceState::thread = 0; + thread_local ThreadContext* DeviceState::ctx = 0; } diff --git a/app/src/main/cpp/skyline/common.h b/app/src/main/cpp/skyline/common.h index 252e7d88..8e5186e3 100644 --- a/app/src/main/cpp/skyline/common.h +++ b/app/src/main/cpp/skyline/common.h @@ -11,24 +11,13 @@ #include #include #include -#include #include #include #include -#include #include +#include "nce/guest_common.h" namespace skyline { - using u128 = __uint128_t; //!< Unsigned 128-bit integer - using u64 = __uint64_t; //!< Unsigned 64-bit integer - using u32 = __uint32_t; //!< Unsigned 32-bit integer - using u16 = __uint16_t; //!< Unsigned 16-bit integer - using u8 = __uint8_t; //!< Unsigned 8-bit integer - using i128 = __int128_t; //!< Signed 128-bit integer - using i64 = __int64_t; //!< Signed 64-bit integer - using i32 = __int32_t; //!< Signed 32-bit integer - using i16 = __int16_t; //!< Signed 16-bit integer - using i8 = __int8_t; //!< Signed 8-bit integer using handle_t = u32; //!< The type of a kernel handle namespace constant { @@ -36,8 +25,7 @@ namespace skyline { constexpr u64 BaseAddr = 0x8000000; //!< The address space base constexpr u64 MapAddr = BaseAddr + 0x80000000; //!< The address of the map region constexpr u64 HeapAddr = MapAddr + 0x1000000000; //!< The address of the heap region - constexpr u64 BaseSize = 0x7FF8000000; //!< The size of the address space - constexpr u64 BaseEnd = BaseAddr + BaseSize; //!< The end of the address space + constexpr u64 BaseEnd = 0x7FFFFFFFFF; //!< The end of the address space constexpr u64 MapSize = 0x1000000000; //!< The size of the map region constexpr u64 TotalPhyMem = 0xF8000000; // ~4 GB of RAM constexpr size_t DefStackSize = 0x1E8480; //!< The default amount of stack: 2 MB @@ -52,6 +40,8 @@ namespace skyline { constexpr u16 SvcLast = 0x7F; //!< The index of the last SVC constexpr u16 BrkRdy = 0xFF; //!< This is reserved for our kernel's to know when a process/thread is ready constexpr u32 TpidrroEl0 = 0x5E83; //!< ID of TPIDRRO_EL0 in MRS + constexpr u32 CntfrqEl0 = 0x5F00; //!< ID of CNTFRQ_EL0 in MRS + constexpr u32 TegraX1Freq = 0x124F800; //!< The clock frequency of the Tegra X1 constexpr u32 CntpctEl0 = 0x5F01; //!< ID of CNTPCT_EL0 in MRS constexpr u32 CntvctEl0 = 0x5F02; //!< ID of CNTVCT_EL0 in MRS // Kernel @@ -106,18 +96,11 @@ namespace skyline { NSP, //!< The NSP format from "nspwn" exploit: https://switchbrew.org/wiki/Switch_System_Flaws }; - /** - * Read about ARMv8 registers here: https://developer.arm.com/docs/100878/latest/registers - */ - enum class Xreg { X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30 }; - enum class Wreg { W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14, W15, W16, W17, W18, W19, W20, W21, W22, W23, W24, W25, W26, W27, W28, W29, W30 }; - enum class Sreg { Sp, Pc, PState }; - /** * @brief The Mutex class is a wrapper around an atomic bool used for synchronization */ class Mutex { - std::atomic flag{false}; //!< This atomic bool holds the status of the lock + std::atomic_flag flag = ATOMIC_FLAG_INIT; //!< An atomic flag to hold the state of the mutex public: /** @@ -303,11 +286,12 @@ namespace skyline { * @brief This struct is used to hold the state of a device */ struct DeviceState { - DeviceState(kernel::OS *os, std::shared_ptr &thisProcess, std::shared_ptr &thisThread, std::shared_ptr jvmManager, std::shared_ptr settings, std::shared_ptr logger); + DeviceState(kernel::OS *os, std::shared_ptr &process, std::shared_ptr jvmManager, std::shared_ptr settings, std::shared_ptr logger); kernel::OS *os; //!< This holds a reference to the OS class std::shared_ptr &process; //!< This holds a reference to the process object - std::shared_ptr &thread; //!< This holds a reference to the current thread object + thread_local static std::shared_ptr thread; //!< This holds a reference to the current thread object + thread_local static ThreadContext* ctx; //!< This holds the context of the thread std::shared_ptr nce; //!< This holds a reference to the NCE class std::shared_ptr gpu; //!< This holds a reference to the GPU class std::shared_ptr jvmManager; //!< This holds a reference to the JvmManager class diff --git a/app/src/main/cpp/skyline/gpu.cpp b/app/src/main/cpp/skyline/gpu.cpp index 09ba3008..066946fe 100644 --- a/app/src/main/cpp/skyline/gpu.cpp +++ b/app/src/main/cpp/skyline/gpu.cpp @@ -24,7 +24,7 @@ namespace skyline::gpu { void GPU::Loop() { if (surfaceUpdate) { - if (state.jvmManager->CheckNull(Surface)) + if (Surface == nullptr) return; window = ANativeWindow_fromSurface(state.jvmManager->GetEnv(), Surface); ANativeWindow_acquire(window); @@ -33,7 +33,7 @@ namespace skyline::gpu { format = ANativeWindow_getFormat(window); surfaceUpdate = true; } else - surfaceUpdate = state.jvmManager->CheckNull(Surface); + surfaceUpdate = (Surface == nullptr); if (!bufferQueue.displayQueue.empty()) { auto &buffer = bufferQueue.displayQueue.front(); bufferQueue.displayQueue.pop(); diff --git a/app/src/main/cpp/skyline/gpu.h b/app/src/main/cpp/skyline/gpu.h index 4ee7c5c1..18964653 100644 --- a/app/src/main/cpp/skyline/gpu.h +++ b/app/src/main/cpp/skyline/gpu.h @@ -4,6 +4,7 @@ #include "kernel/types/KEvent.h" #include "gpu/display.h" #include "gpu/devices/nvdevice.h" +#include namespace skyline::gpu { /** diff --git a/app/src/main/cpp/skyline/guest.S b/app/src/main/cpp/skyline/guest.S deleted file mode 100644 index 27976782..00000000 --- a/app/src/main/cpp/skyline/guest.S +++ /dev/null @@ -1,46 +0,0 @@ -.text -.global saveCtx -saveCtx: - STR LR, [SP, #-16]! - MRS LR, TPIDR_EL0 - #LDR LR, [LR] - STP X0, X1, [LR, #0] - STP X2, X3, [LR, #16] - STP X4, X5, [LR, #32] - STP X6, X7, [LR, #48] - STP X8, X9, [LR, #64] - STP X10, X11, [LR, #80] - STP X12, X13, [LR, #96] - STP X14, X15, [LR, #112] - STP X16, X17, [LR, #128] - STP X18, X19, [LR, #144] - STP X20, X21, [LR, #160] - STP X22, X23, [LR, #176] - STP X24, X25, [LR, #192] - STP X26, X27, [LR, #208] - STP X28, X29, [LR, #224] - LDR LR, [SP], #16 - RET - -.global loadCtx -loadCtx: - STR LR, [SP, #-16]! - MRS LR, TPIDR_EL0 - #LDR LR, [LR] - LDP X0, X1, [LR, #0] - LDP X2, X3, [LR, #16] - LDP X4, X5, [LR, #32] - LDP X6, X7, [LR, #48] - LDP X8, X9, [LR, #64] - LDP X10, X11, [LR, #80] - LDP X12, X13, [LR, #96] - LDP X14, X15, [LR, #112] - LDP X16, X17, [LR, #128] - LDP X18, X19, [LR, #144] - LDP X20, X21, [LR, #160] - LDP X22, X23, [LR, #176] - LDP X24, X25, [LR, #192] - LDP X26, X27, [LR, #208] - LDP X28, X29, [LR, #224] - LDR LR, [SP], #16 - RET diff --git a/app/src/main/cpp/skyline/guest.h b/app/src/main/cpp/skyline/guest.h deleted file mode 100644 index 5cd9a46a..00000000 --- a/app/src/main/cpp/skyline/guest.h +++ /dev/null @@ -1,179 +0,0 @@ -#pragma once - -namespace skyline { - namespace guest { - constexpr size_t saveCtxSize = 20 * sizeof(u32); - constexpr size_t loadCtxSize = 20 * sizeof(u32); - extern "C" void saveCtx(void); - extern "C" void loadCtx(void); - } - - namespace instr { - /** - * @brief A bit-field struct that encapsulates a BRK instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/brk-breakpoint-instruction. - */ - struct Brk { - /** - * @brief Creates a BRK instruction with a specific immediate value, used for generating BRK opcodes - * @param value The immediate value of the instruction - */ - explicit Brk(u16 value) { - start = 0x0; // First 5 bits of a BRK instruction are 0 - this->value = value; - end = 0x6A1; // Last 11 bits of a BRK instruction stored as u16 - } - - /** - * @brief Returns if the opcode is valid or not - * @return If the opcode represents a valid BRK instruction - */ - inline bool Verify() { - return (start == 0x0 && end == 0x6A1); - } - - union { - struct { - u8 start : 5; - u32 value : 16; - u16 end : 11; - }; - u32 raw{}; - }; - }; - - static_assert(sizeof(Brk) == sizeof(u32)); - - /** - * @brief A bit-field struct that encapsulates a SVC instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/svc-supervisor-call. - */ - struct Svc { - /** - * @brief Returns if the opcode is valid or not - * @return If the opcode represents a valid SVC instruction - */ - inline bool Verify() { - return (start == 0x1 && end == 0x6A0); - } - - union { - struct { - u8 start : 5; - u32 value : 16; - u16 end : 11; - }; - u32 raw{}; - }; - }; - - static_assert(sizeof(Svc) == sizeof(u32)); - - /** - * @brief A bit-field struct that encapsulates a MRS instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/mrs-move-system-register. - */ - struct Mrs { - /** - * @brief Creates a MRS instruction, used for generating BRK opcodes - * @param srcReg The source system register - * @param dstReg The destination Xn register - */ - Mrs(u32 srcReg, u8 dstReg) { - this->srcReg = srcReg; - this->dstReg = dstReg; - end = 0xD53; // Last 12 bits of a MRS instruction stored as u16 - } - - /** - * @brief Returns if the opcode is valid or not - * @return If the opcode represents a valid MRS instruction - */ - inline bool Verify() { - return (end == 0xD53); - } - - union { - struct { - u8 dstReg : 5; - u32 srcReg : 15; - u16 end : 12; - }; - u32 raw{}; - }; - }; - - static_assert(sizeof(Mrs) == sizeof(u32)); - - /** - * @brief A bit-field struct that encapsulates a B instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/b-branch. - */ - struct B { - public: - explicit B(i64 offset) { - this->offset = static_cast(offset / 4); - end = 0x5; - } - - /** - * @brief Returns the offset of the instruction - * @return The offset encoded within the instruction - */ - inline i32 Offset() { - return offset * 4; - } - - /** - * @brief Returns if the opcode is valid or not - * @return If the opcode represents a valid Branch instruction - */ - inline bool Verify() { - return (end == 0x5); - } - - union { - struct { - i32 offset : 26; - u8 end : 6; - }; - u32 raw{}; - }; - }; - - static_assert(sizeof(B) == sizeof(u32)); - - /** - * @brief A bit-field struct that encapsulates a BL instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/b-branch. - */ - struct BL { - public: - explicit BL(i64 offset) { - this->offset = static_cast(offset / 4); - end = 0x25; - } - - /** - * @brief Returns the offset of the instruction - * @return The offset encoded within the instruction - */ - inline i32 Offset() { - return offset * 4; - } - - /** - * @brief Returns if the opcode is valid or not - * @return If the opcode represents a valid Branch instruction - */ - inline bool Verify() { - return (end == 0x85); - } - - union { - struct { - i32 offset : 26; - u8 end : 6; - }; - u32 raw{}; - }; - }; - - static_assert(sizeof(BL) == sizeof(u32)); - } -} diff --git a/app/src/main/cpp/skyline/kernel/svc.cpp b/app/src/main/cpp/skyline/kernel/svc.cpp index 30161954..2ef62af9 100644 --- a/app/src/main/cpp/skyline/kernel/svc.cpp +++ b/app/src/main/cpp/skyline/kernel/svc.cpp @@ -3,10 +3,10 @@ namespace skyline::kernel::svc { void SetHeapSize(DeviceState &state) { - const u32 size = state.nce->GetRegister(Wreg::W1); + const u32 size = state.ctx->registers.w1; if(size%constant::HeapSizeDiv != 0) { - state.nce->SetRegister(Xreg::X1, 0); - state.nce->SetRegister(Wreg::W0, constant::status::InvSize); + state.ctx->registers.x1 = 0; + state.ctx->registers.w0 = constant::status::InvSize; state.logger->Warn("svcSetHeapSize: 'size' not divisible by 2MB: {}", size); return; } @@ -19,29 +19,29 @@ namespace skyline::kernel::svc { state.process->UnmapPrivateRegion(memory::Region::Heap); heap = state.process->MapPrivateRegion(constant::HeapAddr, size, {true, true, false}, memory::Type::Heap, memory::Region::Heap).item; } - state.nce->SetRegister(Wreg::W0, constant::status::Success); - state.nce->SetRegister(Xreg::X1, heap->address); + state.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.nce->GetRegister(Xreg::X0); + const u64 addr = state.ctx->registers.x0; if((addr & (PAGE_SIZE - 1U))) { - state.nce->SetRegister(Wreg::W0, constant::status::InvAddress); + state.ctx->registers.w0 = constant::status::InvAddress; state.logger->Warn("svcSetMemoryAttribute: 'address' not page aligned: {}", addr); return; } - const u64 size = state.nce->GetRegister(Xreg::X1); + const u64 size = state.ctx->registers.x1; if((size & (PAGE_SIZE - 1U)) || !size) { - state.nce->SetRegister(Wreg::W0, constant::status::InvSize); + state.ctx->registers.w0 = constant::status::InvSize; state.logger->Warn("svcSetMemoryAttribute: 'size' {}: {}", size ? "not page aligned" : "is zero", size); return; } - u32 mask = state.nce->GetRegister(Wreg::W2); - u32 value = state.nce->GetRegister(Wreg::W3); + u32 mask = state.ctx->registers.w2; + u32 value = state.ctx->registers.w3; u32 maskedValue = mask | value; if(maskedValue != mask) { - state.nce->SetRegister(Wreg::W0, constant::status::InvCombination); + state.ctx->registers.w0 = constant::status::InvCombination; state.logger->Warn("svcSetMemoryAttribute: 'mask' invalid: 0x{:X}, 0x{:X}", mask, value); return; } @@ -63,17 +63,17 @@ namespace skyline::kernel::svc { } } if(!found) { - state.nce->SetRegister(Wreg::W0, constant::status::InvAddress); + 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.nce->SetRegister(Wreg::W0, constant::status::Success); + state.ctx->registers.w0 = constant::status::Success; } void QueryMemory(DeviceState &state) { memory::MemoryInfo memInfo{}; - u64 addr = state.nce->GetRegister(Xreg::X2); + u64 addr = state.ctx->registers.x2; bool found = false; for (const auto&[address, region] : state.process->memoryMap) { if (addr >= address && addr < (address + region->size)) { @@ -112,8 +112,8 @@ namespace skyline::kernel::svc { } } 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.process->WriteMemory(memInfo, state.nce->GetRegister(Xreg::X0)); - state.nce->SetRegister(Wreg::W0, constant::status::Success); + state.process->WriteMemory(memInfo, state.ctx->registers.x0); + state.ctx->registers.w0 = constant::status::Success; } void ExitProcess(DeviceState &state) { @@ -122,30 +122,30 @@ namespace skyline::kernel::svc { } void CreateThread(DeviceState &state) { - u64 entryAddr = state.nce->GetRegister(Xreg::X1); - u64 entryArg = state.nce->GetRegister(Xreg::X2); - u64 stackTop = state.nce->GetRegister(Xreg::X3); - u8 priority = static_cast(state.nce->GetRegister(Wreg::W4)); + 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.nce->SetRegister(Wreg::W0, constant::status::InvAddress); + 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.nce->SetRegister(Wreg::W1, thread->handle); - state.nce->SetRegister(Wreg::W0, constant::status::Success); + state.ctx->registers.w1 = thread->handle; + state.ctx->registers.w0 = constant::status::Success; } void StartThread(DeviceState &state) { - auto handle = state.nce->GetRegister(Wreg::W0); + 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.nce->SetRegister(Wreg::W0, constant::status::InvHandle); + state.ctx->registers.w0 = constant::status::InvHandle; } } @@ -155,7 +155,7 @@ namespace skyline::kernel::svc { } void SleepThread(DeviceState &state) { - auto in = state.nce->GetRegister(Xreg::X0); + auto in = state.ctx->registers.x0; switch (in) { case 0: case 1: @@ -171,102 +171,102 @@ namespace skyline::kernel::svc { } void GetThreadPriority(DeviceState &state) { - auto handle = state.nce->GetRegister(Wreg::W0); + auto handle = state.ctx->registers.w0; try { auto priority = state.process->GetHandle(handle)->priority; - state.nce->SetRegister(Wreg::W1, priority); - state.nce->SetRegister(Wreg::W0, constant::status::Success); + 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.nce->SetRegister(Wreg::W0, constant::status::InvHandle); + state.ctx->registers.w0 = constant::status::InvHandle; } } void SetThreadPriority(DeviceState &state) { - auto handle = state.nce->GetRegister(Wreg::W0); - auto priority = state.nce->GetRegister(Wreg::W1); + auto handle = state.ctx->registers.w0; + auto priority = state.ctx->registers.w1; try { state.process->GetHandle(handle)->UpdatePriority(static_cast(priority)); - state.nce->SetRegister(Wreg::W0, constant::status::Success); + 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.nce->SetRegister(Wreg::W0, constant::status::InvHandle); + state.ctx->registers.w0 = constant::status::InvHandle; } } void MapSharedMemory(DeviceState &state) { try { - auto object = state.process->GetHandle(state.nce->GetRegister(Wreg::W0)); - u64 addr = state.nce->GetRegister(Xreg::X1); + auto object = state.process->GetHandle(state.ctx->registers.w0); + u64 addr = state.ctx->registers.x1; if ((addr & (PAGE_SIZE - 1U))) { - state.nce->SetRegister(Wreg::W0, constant::status::InvAddress); + state.ctx->registers.w0 = constant::status::InvAddress; state.logger->Warn("svcMapSharedMemory: 'address' not page aligned: 0x{:X}", addr); return; } - const u64 size = state.nce->GetRegister(Xreg::X2); + const u64 size = state.ctx->registers.x2; if ((size & (PAGE_SIZE - 1U)) || !size) { - state.nce->SetRegister(Wreg::W0, constant::status::InvSize); + state.ctx->registers.w0 = constant::status::InvSize; state.logger->Warn("svcMapSharedMemory: 'size' {}: {}", size ? "not page aligned" : "is zero", size); return; } - u32 perm = state.nce->GetRegister(Wreg::W3); + 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.nce->SetRegister(Wreg::W0, constant::status::InvPermission); + 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.nce->SetRegister(Wreg::W0, constant::status::Success); + state.ctx->registers.w0 = constant::status::Success; } catch (const std::exception &) { - state.logger->Warn("svcMapSharedMemory: 'handle' invalid: 0x{:X}", state.nce->GetRegister(Wreg::W0)); - state.nce->SetRegister(Wreg::W0, constant::status::InvHandle); + state.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.nce->GetRegister(Xreg::X1); + u64 addr = state.ctx->registers.x1; if ((addr & (PAGE_SIZE - 1U))) { - state.nce->SetRegister(Wreg::W0, constant::status::InvAddress); + state.ctx->registers.w0 = constant::status::InvAddress; state.logger->Warn("svcCreateTransferMemory: 'address' not page aligned: {}", addr); return; } - u64 size = state.nce->GetRegister(Xreg::X2); + u64 size = state.ctx->registers.x2; if ((size & (PAGE_SIZE - 1U)) || !size) { - state.nce->SetRegister(Wreg::W0, constant::status::InvSize); + state.ctx->registers.w0 = constant::status::InvSize; state.logger->Warn("svcCreateTransferMemory: 'size' {}: {}", size ? "not page aligned" : "is zero", size); return; } - u32 perm = state.nce->GetRegister(Wreg::W3); + 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.nce->SetRegister(Wreg::W0, constant::status::InvPermission); + 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.nce->SetRegister(Wreg::W0, constant::status::Success); - state.nce->SetRegister(Wreg::W1, shmem.handle); + state.ctx->registers.w0 = constant::status::Success; + state.ctx->registers.w1 = shmem.handle; } void CloseHandle(DeviceState &state) { - auto handle = static_cast(state.nce->GetRegister(Wreg::W0)); + auto handle = static_cast(state.ctx->registers.w0); try { state.process->handleTable.erase(handle); state.logger->Debug("svcCloseHandle: Closing handle: 0x{:X}", handle); - state.nce->SetRegister(Wreg::W0, constant::status::Success); + state.ctx->registers.w0 = constant::status::Success; } catch(const std::exception&) { state.logger->Warn("svcCloseHandle: 'handle' invalid: 0x{:X}", handle); - state.nce->SetRegister(Wreg::W0, constant::status::InvHandle); + state.ctx->registers.w0 = constant::status::InvHandle; } } void ResetSignal(DeviceState &state) { - auto handle = state.nce->GetRegister(Wreg::W0); + auto handle = state.ctx->registers.w0; try { auto &object = state.process->handleTable.at(handle); switch (object->objectType) { @@ -278,27 +278,27 @@ namespace skyline::kernel::svc { break; default: { state.logger->Warn("svcResetSignal: 'handle' type invalid: 0x{:X} ({})", handle, object->objectType); - state.nce->SetRegister(Wreg::W0, constant::status::InvHandle); + state.ctx->registers.w0 = constant::status::InvHandle; return; } } state.logger->Debug("svcResetSignal: Resetting signal: 0x{:X}", handle); - state.nce->SetRegister(Wreg::W0, constant::status::Success); + state.ctx->registers.w0 = constant::status::Success; } catch(const std::out_of_range&) { state.logger->Warn("svcResetSignal: 'handle' invalid: 0x{:X}", handle); - state.nce->SetRegister(Wreg::W0, constant::status::InvHandle); + state.ctx->registers.w0 = constant::status::InvHandle; return; } } void WaitSynchronization(DeviceState &state) { - auto numHandles = state.nce->GetRegister(Wreg::W2); + auto numHandles = state.ctx->registers.w2; if (numHandles > constant::MaxSyncHandles) { - state.nce->SetRegister(Wreg::W0, constant::status::MaxHandles); + state.ctx->registers.w0 = constant::status::MaxHandles; return; } std::vector waitHandles(numHandles); - state.process->ReadMemory(waitHandles.data(), state.nce->GetRegister(Xreg::X1), numHandles * sizeof(handle_t)); + state.process->ReadMemory(waitHandles.data(), state.ctx->registers.x1, numHandles * sizeof(handle_t)); std::string handleStr; uint index{}; for (const auto &handle : waitHandles) { @@ -311,7 +311,7 @@ namespace skyline::kernel::svc { case type::KType::KSession: break; default: { - state.nce->SetRegister(Wreg::W0, constant::status::InvHandle); + state.ctx->registers.w0 = constant::status::InvHandle; state.thread->ClearWaitObjects(); return; } @@ -319,17 +319,17 @@ namespace skyline::kernel::svc { auto syncObject = std::static_pointer_cast(object); if (syncObject->signalled) { state.logger->Debug("svcWaitSynchronization: Signalled handle: 0x{:X}", handle); - state.nce->SetRegister(Wreg::W0, constant::status::Success); - state.nce->SetRegister(Wreg::W1, index); + 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.nce->GetRegister(Xreg::X3); + auto timeout = state.ctx->registers.x3; state.logger->Debug("svcWaitSynchronization: Waiting on handles:\n{}Timeout: 0x{:X} ns", handleStr, timeout); - if (state.nce->GetRegister(Xreg::X3) != std::numeric_limits::max()) + if (state.ctx->registers.x3 != std::numeric_limits::max()) state.thread->timeout = GetCurrTimeNs() + timeout; else state.thread->timeout = 0; @@ -337,44 +337,44 @@ namespace skyline::kernel::svc { } void ArbitrateLock(DeviceState &state) { - auto addr = state.nce->GetRegister(Xreg::X1); + auto addr = state.ctx->registers.x1; if((addr & ((1UL << WORD_BIT) - 1U))) { - state.nce->SetRegister(Wreg::W0, constant::status::InvAddress); + state.ctx->registers.w0 = constant::status::InvAddress; state.logger->Warn("svcArbitrateLock: 'address' not word aligned: {}", addr); return; } - auto handle = state.nce->GetRegister(Wreg::W2); + 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.nce->SetRegister(Wreg::W0, constant::status::Success); + state.ctx->registers.w0 = constant::status::Success; } void ArbitrateUnlock(DeviceState &state) { - auto addr = state.nce->GetRegister(Xreg::X0); + auto addr = state.ctx->registers.x0; if((addr & ((1UL << WORD_BIT) - 1U))) { - state.nce->SetRegister(Wreg::W0, constant::status::InvAddress); + 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.nce->SetRegister(Wreg::W0, constant::status::Success); + state.ctx->registers.w0 = constant::status::Success; } void WaitProcessWideKeyAtomic(DeviceState &state) { - auto mtxAddr = state.nce->GetRegister(Xreg::X0); + auto mtxAddr = state.ctx->registers.x0; if((mtxAddr & ((1UL << WORD_BIT) - 1U))) { - state.nce->SetRegister(Wreg::W0, constant::status::InvAddress); + state.ctx->registers.w0 = constant::status::InvAddress; state.logger->Warn("svcWaitProcessWideKeyAtomic: mutex address not word aligned: {}", mtxAddr); return; } - auto handle = state.nce->GetRegister(Wreg::W2); + 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.nce->GetRegister(Xreg::X1); + auto condAddr = state.ctx->registers.x1; auto &cvarVec = state.process->condVarMap[condAddr]; for (auto thread = cvarVec.begin();; thread++) { if ((*thread)->priority < state.thread->priority) { @@ -385,17 +385,17 @@ namespace skyline::kernel::svc { break; } } - auto timeout = state.nce->GetRegister(Xreg::X3); + 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.nce->SetRegister(Wreg::W0, constant::status::Success); + state.ctx->registers.w0 = constant::status::Success; } void SignalProcessWideKey(DeviceState &state) { - auto address = state.nce->GetRegister(Xreg::X0); - auto count = state.nce->GetRegister(Wreg::W1); - state.nce->SetRegister(Wreg::W0, constant::status::Success); + 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; @@ -420,55 +420,55 @@ namespace skyline::kernel::svc { "MRS X1, CNTFRQ_EL0\n\t" "UDIV %0, %0, X1\n\t" "LDR X1, [SP], #16" : "=r"(tick)); - state.nce->SetRegister(Xreg::X0, tick); + state.ctx->registers.x0 = tick; } void ConnectToNamedPort(DeviceState &state) { char port[constant::PortSize + 1]{0}; - state.process->ReadMemory(port, state.nce->GetRegister(Xreg::X1), constant::PortSize); + 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.nce->SetRegister(Wreg::W0, constant::status::NotFound); + state.ctx->registers.w0 = constant::status::NotFound; return; } state.logger->Debug("svcConnectToNamedPort: Connecting to port '{}' at 0x{:X}", port, handle); - state.nce->SetRegister(Wreg::W1, handle); - state.nce->SetRegister(Wreg::W0, constant::status::Success); + state.ctx->registers.w1 = handle; + state.ctx->registers.w0 = constant::status::Success; } void SendSyncRequest(DeviceState &state) { - state.os->serviceManager.SyncRequestHandler(static_cast(state.nce->GetRegister(Xreg::X0))); - state.nce->SetRegister(Wreg::W0, constant::status::Success); + 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.nce->GetRegister(Wreg::W1); + 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.nce->SetRegister(Xreg::X1, static_cast(pid)); - state.nce->SetRegister(Wreg::W0, constant::status::Success); + state.ctx->registers.x1 = static_cast(pid); + state.ctx->registers.w0 = constant::status::Success; } void OutputDebugString(DeviceState &state) { - std::string debug(state.nce->GetRegister(Xreg::X1), '\0'); - state.process->ReadMemory(debug.data(), state.nce->GetRegister(Xreg::X0), state.nce->GetRegister(Xreg::X1)); + 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.nce->SetRegister(Wreg::W0, constant::status::Success); + state.ctx->registers.w0 = constant::status::Success; } void GetInfo(DeviceState &state) { - auto id0 = state.nce->GetRegister(Wreg::W1); - auto handle = state.nce->GetRegister(Wreg::W2); - auto id1 = state.nce->GetRegister(Xreg::X3); + 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: @@ -499,7 +499,7 @@ namespace skyline::kernel::svc { out = constant::BaseAddr; break; case constant::infoState::AddressSpaceSize: - out = constant::BaseSize; + out = constant::BaseEnd; break; case constant::infoState::StackRegionBaseAddr: out = state.thread->stackTop; @@ -524,11 +524,11 @@ namespace skyline::kernel::svc { break; default: state.logger->Warn("svcGetInfo: Unimplemented case ID0: {}, ID1: {}", id0, id1); - state.nce->SetRegister(Wreg::W0, constant::status::Unimpl); + state.ctx->registers.w0 = constant::status::Unimpl; return; } state.logger->Debug("svcGetInfo: ID0: {}, ID1: {}, Out: 0x{:X}", id0, id1, out); - state.nce->SetRegister(Xreg::X1, out); - state.nce->SetRegister(Wreg::W0, constant::status::Success); + 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 9d0b30a1..f4123be4 100644 --- a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp @@ -1,28 +1,22 @@ #include "KPrivateMemory.h" #include "KProcess.h" #include +#include namespace skyline::kernel::type { - u64 MapPrivateFunc(u64 dstAddress, u64 srcAddress, size_t size, u64 perms) { - dstAddress = reinterpret_cast(mmap(reinterpret_cast(dstAddress), size, static_cast(perms), MAP_PRIVATE | MAP_ANONYMOUS | ((dstAddress) ? MAP_FIXED : 0), -1, 0)); // NOLINT(hicpp-signed-bitwise) - if (srcAddress) { - memcpy(reinterpret_cast(dstAddress), reinterpret_cast(srcAddress), size); - mprotect(reinterpret_cast(srcAddress), size, PROT_NONE); - } - return dstAddress; - } - - KPrivateMemory::KPrivateMemory(const DeviceState &state, u64 dstAddress, u64 srcAddress, size_t size, memory::Permission permission, const memory::Type type, const pid_t pid) : state(state), address(dstAddress), size(size), permission(permission), type(type), KObject(state, KType::KPrivateMemory) { - user_pt_regs fregs = {0}; - fregs.regs[0] = dstAddress; - fregs.regs[1] = srcAddress; - fregs.regs[2] = size; - fregs.regs[3] = static_cast(permission.Get()); - state.nce->ExecuteFunction(reinterpret_cast(MapPrivateFunc), fregs, pid ? pid : state.process->pid); - if (reinterpret_cast(fregs.regs[0]) == MAP_FAILED) + KPrivateMemory::KPrivateMemory(const DeviceState &state, u64 dstAddress, size_t size, memory::Permission permission, const memory::Type type, std::shared_ptr thread) : state(state), address(dstAddress), size(size), permission(permission), type(type), KObject(state, KType::KPrivateMemory) { + Registers fregs{}; + fregs.x0 = dstAddress; + fregs.x1 = size; + fregs.x2 = static_cast(permission.Get()); + fregs.x3 = static_cast(MAP_PRIVATE | MAP_ANONYMOUS | ((dstAddress) ? MAP_FIXED : 0)); // NOLINT(hicpp-signed-bitwise) + fregs.x4 = static_cast(-1); + fregs.x8 = __NR_mmap; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, thread); + if (fregs.x0 < 0) throw exception("An error occurred while mapping private region in child process"); if (!this->address) - this->address = fregs.regs[0]; + this->address = fregs.x0; } u64 RemapPrivateFunc(u64 address, size_t oldSize, size_t size, u64 flags) { @@ -30,15 +24,16 @@ namespace skyline::kernel::type { } u64 KPrivateMemory::Resize(size_t newSize, bool canMove) { - user_pt_regs fregs = {0}; - fregs.regs[0] = address; - fregs.regs[1] = size; - fregs.regs[2] = newSize; - fregs.regs[3] = canMove ? MREMAP_MAYMOVE : 0; - state.nce->ExecuteFunction(reinterpret_cast(RemapPrivateFunc), fregs, state.process->pid); - if (reinterpret_cast(fregs.regs[0]) == MAP_FAILED) + Registers fregs{}; + fregs.x0 = address; + fregs.x1 = size; + fregs.x2 = newSize; + fregs.x3 = canMove ? MREMAP_MAYMOVE : 0; + fregs.x8 = __NR_mremap; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.thread); + if (fregs.x0 < 0) throw exception("An error occurred while remapping private region in child process"); - address = fregs.regs[0]; + address = fregs.x0; size = newSize; return address; } @@ -48,12 +43,13 @@ namespace skyline::kernel::type { } void KPrivateMemory::UpdatePermission(memory::Permission permission) { - user_pt_regs fregs = {0}; - fregs.regs[0] = address; - fregs.regs[1] = size; - fregs.regs[2] = static_cast(permission.Get()); - state.nce->ExecuteFunction(reinterpret_cast(UpdatePermissionPrivateFunc), fregs, state.process->pid); - if (static_cast(fregs.regs[0]) == -1) + Registers fregs{}; + fregs.x0 = address; + fregs.x1 = size; + fregs.x2 = static_cast(permission.Get()); + fregs.x8 = __NR_mprotect; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.thread); + if (fregs.x0 < 0) throw exception("An error occurred while updating private region's permissions in child process"); this->permission = permission; } @@ -82,11 +78,11 @@ namespace skyline::kernel::type { KPrivateMemory::~KPrivateMemory() { try { - user_pt_regs fregs = {0}; - fregs.regs[0] = address; - fregs.regs[1] = size; - state.nce->ExecuteFunction(reinterpret_cast(UnmapPrivateFunc), fregs, state.process->pid); - } catch (const std::exception &) { - } + Registers fregs{}; + fregs.x0 = address; + fregs.x1 = size; + fregs.x8 = __NR_munmap; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.thread); + } catch (const std::exception &) {} } }; diff --git a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.h b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.h index e3b8204e..72146f52 100644 --- a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.h +++ b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.h @@ -23,13 +23,12 @@ namespace skyline::kernel::type { /** * @param state The state of the device * @param dstAddress The address to map to (If NULL then an arbitrary address is picked) - * @param srcAddress The address to map from (If NULL then no copy is performed) * @param size The size of the allocation * @param permission The permissions for the allocated memory * @param type The type of the memory - * @param pid The PID of the me + * @param thread The thread to execute the calls on */ - KPrivateMemory(const DeviceState &state, u64 dstAddress, u64 srcAddress, size_t size, memory::Permission permission, const memory::Type type, const pid_t pid=0); + KPrivateMemory(const DeviceState &state, u64 dstAddress, size_t size, memory::Permission permission, const memory::Type type, std::shared_ptr thread = 0); /** * @brief Remap a chunk of memory as to change the size occupied by it diff --git a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp index 466a5ce6..c1532f3d 100644 --- a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace skyline::kernel::type { KProcess::TlsPage::TlsPage(u64 address) : address(address) {} @@ -29,7 +30,7 @@ namespace skyline::kernel::type { if (!tlsPage->Full()) return tlsPage->ReserveSlot(); } - auto tlsMem = NewHandle(0, 0, PAGE_SIZE, memory::Permission(true, true, false), memory::Type::ThreadLocal, pid).item; + auto tlsMem = NewHandle(0, PAGE_SIZE, memory::Permission(true, true, false), memory::Type::ThreadLocal, threadMap.at(pid)).item; memoryMap[tlsMem->address] = tlsMem; tlsPages.push_back(std::make_shared(tlsMem->address)); auto &tlsPage = tlsPages.back(); @@ -38,9 +39,12 @@ namespace skyline::kernel::type { return tlsPage->ReserveSlot(); } - KProcess::KProcess(const DeviceState &state, pid_t pid, u64 entryPoint, u64 stackBase, u64 stackSize) : pid(pid), mainThreadStackSz(stackSize), KSyncObject(state, KType::KProcess) { - state.nce->WaitRdy(pid); - threadMap[pid] = NewHandle(pid, entryPoint, 0x0, stackBase + stackSize, GetTlsSlot(), constant::DefaultPriority, this).item; + KProcess::KProcess(const DeviceState &state, pid_t pid, u64 entryPoint, u64 stackBase, u64 stackSize, std::shared_ptr& tlsMemory) : pid(pid), mainThreadStackSz(stackSize), KSyncObject(state, KType::KProcess) { + auto thread = NewHandle(pid, entryPoint, 0x0, stackBase + stackSize, 0, constant::DefaultPriority, this, tlsMemory).item; + // Remove GetTlsSlot from KThread ctor and cleanup ctor in general + threadMap[pid] = thread; + state.nce->WaitThreadInit(thread); + thread->tls = GetTlsSlot(); MapPrivateRegion(constant::HeapAddr, constant::DefHeapSize, {true, true, false}, memory::Type::Heap, memory::Region::Heap); memFd = open(fmt::format("/proc/{}/mem", pid).c_str(), O_RDWR | O_CLOEXEC); // NOLINT(hicpp-signed-bitwise) if (memFd == -1) @@ -65,16 +69,23 @@ namespace skyline::kernel::type { } std::shared_ptr KProcess::CreateThread(u64 entryPoint, u64 entryArg, u64 stackTop, u8 priority) { - user_pt_regs fregs = {0}; + /* + * Future Reference: + * https://android.googlesource.com/platform/bionic/+/master/libc/bionic/clone.cpp + * https://android.googlesource.com/platform/bionic/+/master/libc/arch-arm64/bionic/__bionic_clone.S + Registers fregs{}; fregs.regs[0] = entryPoint; fregs.regs[1] = stackTop; - state.nce->ExecuteFunction((void *) CreateThreadFunc, fregs, pid); + fregs.x8 = __NR_clone; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.thread->pid); auto pid = static_cast(fregs.regs[0]); if (pid == -1) throw exception("Cannot create thread: Address: 0x{:X}, Stack Top: 0x{:X}", entryPoint, stackTop); auto process = NewHandle(pid, entryPoint, entryArg, stackTop, GetTlsSlot(), priority, this).item; threadMap[pid] = process; return process; + */ + return 0; } void KProcess::ReadMemory(void *destination, u64 offset, size_t size) const { @@ -90,7 +101,7 @@ namespace skyline::kernel::type { } KProcess::HandleOut KProcess::MapPrivateRegion(u64 address, size_t size, const memory::Permission perms, const memory::Type type, const memory::Region region) { - auto mem = NewHandle(address, 0, size, perms, type, pid); + auto mem = NewHandle(address, size, perms, type, threadMap.at(pid)); memoryMap[mem.item->address] = mem.item; memoryRegionMap[region] = mem.item; return mem; diff --git a/app/src/main/cpp/skyline/kernel/types/KProcess.h b/app/src/main/cpp/skyline/kernel/types/KProcess.h index 3546350f..432404a8 100644 --- a/app/src/main/cpp/skyline/kernel/types/KProcess.h +++ b/app/src/main/cpp/skyline/kernel/types/KProcess.h @@ -91,8 +91,9 @@ namespace skyline::kernel::type { * @param entryPoint The address to start execution at * @param stackBase The base of the stack * @param stackSize The size of the stack + * @param tlsMemory The KSharedMemory object for TLS memory allocated by the guest process */ - KProcess(const DeviceState &state, pid_t pid, u64 entryPoint, u64 stackBase, u64 stackSize); + KProcess(const DeviceState &state, pid_t pid, u64 entryPoint, u64 stackBase, u64 stackSize, std::shared_ptr& tlsMemory); /** * Close the file descriptor to the process's memory diff --git a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp index c2c57c02..6bd588e0 100644 --- a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace skyline::kernel::type { u64 MapSharedFunc(u64 address, size_t size, u64 perms, u64 fd) { @@ -13,23 +14,26 @@ namespace skyline::kernel::type { fd = ASharedMemory_create("", size); if (fd < 0) throw exception("An error occurred while creating shared memory: {}", fd); - address = MapSharedFunc(address, size, static_cast(permission.Get()), static_cast(fd)); + address = reinterpret_cast(mmap(reinterpret_cast(address), size, permission.Get(), MAP_SHARED | ((address) ? MAP_FIXED : 0), static_cast(fd), 0)); if (address == reinterpret_cast(MAP_FAILED)) // NOLINT(hicpp-signed-bitwise) throw exception("An occurred while mapping shared region: {}", strerror(errno)); kernel = {address, size, permission}; } u64 KSharedMemory::Map(u64 address, u64 size, memory::Permission permission) { - user_pt_regs fregs = {0}; - fregs.regs[0] = address; - fregs.regs[1] = size; - fregs.regs[2] = static_cast(permission.Get()); - fregs.regs[3] = static_cast(fd); - state.nce->ExecuteFunction(reinterpret_cast(MapSharedFunc), fregs, state.process->pid); - if (reinterpret_cast(fregs.regs[0]) == MAP_FAILED) + Registers fregs{}; + fregs.x0 = address; + fregs.x1 = size; + fregs.x2 = static_cast(permission.Get()); + fregs.x3 = static_cast(MAP_SHARED | ((address) ? MAP_FIXED : 0)); // NOLINT(hicpp-signed-bitwise) + fregs.x4 = static_cast(fd); + fregs.x4 = static_cast(-1); + fregs.x8 = __NR_mmap; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.thread->pid); + if (fregs.x0 < 0) throw exception("An error occurred while mapping shared region in child process"); - guest = {fregs.regs[0], size, permission}; - return fregs.regs[0]; + guest = {fregs.x0, size, permission}; + return fregs.x0; } u64 UnmapSharedFunc(u64 address, size_t size) { @@ -39,15 +43,15 @@ namespace skyline::kernel::type { KSharedMemory::~KSharedMemory() { try { if (guest.valid()) { - user_pt_regs fregs = {0}; - fregs.regs[0] = guest.address; - fregs.regs[1] = guest.size; - state.nce->ExecuteFunction(reinterpret_cast(UnmapSharedFunc), fregs, state.process->pid); + Registers fregs{}; + fregs.x0 = guest.address; + fregs.x1 = guest.size; + fregs.x8 = __NR_munmap; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.thread->pid); } if (kernel.valid()) UnmapSharedFunc(kernel.address, kernel.size); - } catch (const std::exception &) { - } + } catch (const std::exception &) {} close(fd); } @@ -57,17 +61,17 @@ namespace skyline::kernel::type { void KSharedMemory::Resize(size_t size) { if (guest.valid()) { - user_pt_regs fregs = {0}; - fregs.regs[0] = guest.address; - fregs.regs[1] = guest.size; - fregs.regs[2] = size; - state.nce->ExecuteFunction(reinterpret_cast(RemapSharedFunc), fregs, state.process->pid); - if (reinterpret_cast(fregs.regs[0]) == MAP_FAILED) + Registers fregs{}; + fregs.x0 = guest.address; + fregs.x1 = guest.size; + fregs.x2 = size; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.thread->pid); + if (fregs.x0 < 0) throw exception("An error occurred while remapping shared region in child process"); guest.size = size; } if (kernel.valid()) { - if (RemapSharedFunc(kernel.address, kernel.size, size) == reinterpret_cast(MAP_FAILED)) + if (mremap(reinterpret_cast(kernel.address), kernel.size, size, 0) == MAP_FAILED) throw exception("An occurred while remapping shared region: {}", strerror(errno)); kernel.size = size; } @@ -79,17 +83,17 @@ namespace skyline::kernel::type { void KSharedMemory::UpdatePermission(memory::Permission permission, bool host) { if (guest.valid() && !host) { - user_pt_regs fregs = {0}; - fregs.regs[0] = guest.address; - fregs.regs[1] = guest.size; - fregs.regs[2] = static_cast(guest.permission.Get()); - state.nce->ExecuteFunction(reinterpret_cast(UpdatePermissionSharedFunc), fregs, state.process->pid); - if (static_cast(fregs.regs[0]) == -1) + Registers fregs{}; + fregs.x0 = guest.address; + fregs.x1 = guest.size; + fregs.x2 = static_cast(guest.permission.Get()); + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.thread->pid); + if (fregs.x0 < 0) throw exception("An error occurred while updating shared region's permissions in child process"); guest.permission = permission; } if (kernel.valid() && host) { - if (UpdatePermissionSharedFunc(kernel.address, kernel.size, static_cast(permission.Get())) == reinterpret_cast(MAP_FAILED)) + if (mprotect(reinterpret_cast(kernel.address), kernel.size, permission.Get()) == reinterpret_cast(MAP_FAILED)) throw exception("An occurred while remapping shared region: {}", strerror(errno)); kernel.permission = permission; } diff --git a/app/src/main/cpp/skyline/kernel/types/KSyncObject.cpp b/app/src/main/cpp/skyline/kernel/types/KSyncObject.cpp index 2e4d168f..b623c746 100644 --- a/app/src/main/cpp/skyline/kernel/types/KSyncObject.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KSyncObject.cpp @@ -8,7 +8,7 @@ namespace skyline::kernel::type { void KSyncObject::Signal() { for (const auto &info : waitThreads) { - state.nce->SetRegister(Wreg::W1, info.index); + state.ctx->registers.w1 = info.index; state.process->threadMap.at(info.process)->status = KThread::Status::Runnable; } waitThreads.clear(); diff --git a/app/src/main/cpp/skyline/kernel/types/KThread.cpp b/app/src/main/cpp/skyline/kernel/types/KThread.cpp index 1dd26c0d..e8fe5f2a 100644 --- a/app/src/main/cpp/skyline/kernel/types/KThread.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KThread.cpp @@ -4,7 +4,8 @@ #include namespace skyline::kernel::type { - KThread::KThread(const DeviceState &state, handle_t handle, pid_t self_pid, u64 entryPoint, u64 entryArg, u64 stackTop, u64 tls, u8 priority, KProcess *parent) : handle(handle), pid(self_pid), entryPoint(entryPoint), entryArg(entryArg), stackTop(stackTop), tls(tls), priority(priority), parent(parent), KSyncObject(state, KType::KThread) { + KThread::KThread(const DeviceState &state, handle_t handle, pid_t self_pid, u64 entryPoint, u64 entryArg, u64 stackTop, u64 tls, u8 priority, KProcess *parent, std::shared_ptr& tlsMemory) : handle(handle), pid(self_pid), entryPoint(entryPoint), entryArg(entryArg), stackTop(stackTop), tls(tls), priority(priority), parent(parent), ctxMemory(tlsMemory), KSyncObject(state, + KType::KThread) { UpdatePriority(priority); } @@ -17,7 +18,7 @@ namespace skyline::kernel::type { if (pid == parent->pid) parent->status = KProcess::Status::Started; status = Status::Running; - state.nce->StartProcess(entryPoint, entryArg, stackTop, handle, pid); + state.nce->StartThread(entryArg, handle, parent->threadMap.at(pid)); } } diff --git a/app/src/main/cpp/skyline/kernel/types/KThread.h b/app/src/main/cpp/skyline/kernel/types/KThread.h index 261d5445..374f678d 100644 --- a/app/src/main/cpp/skyline/kernel/types/KThread.h +++ b/app/src/main/cpp/skyline/kernel/types/KThread.h @@ -1,6 +1,7 @@ #pragma once #include "KSyncObject.h" +#include "KSharedMemory.h" namespace skyline::kernel::type { /** @@ -23,6 +24,7 @@ namespace skyline::kernel::type { Runnable, //!< The thread is ready to run after waiting Dead //!< The thread is dead and not running } status = Status::Created; //!< The state of the thread + std::shared_ptr ctxMemory; //!< The KSharedMemory of the shared memory allocated by the guest process TLS std::vector> waitObjects; //!< A vector holding the objects this thread is waiting for u64 timeout{}; //!< The end of a timeout for svcWaitSynchronization or the end of the sleep period for svcSleepThread handle_t handle; // The handle of the object in the handle table @@ -41,8 +43,9 @@ namespace skyline::kernel::type { * @param tls The address of the TLS slot assigned * @param priority The priority of the thread in Nintendo format * @param parent The parent process of this thread + * @param tlsMemory The KSharedMemory object for TLS memory allocated by the guest process */ - KThread(const DeviceState &state, handle_t handle, pid_t self_pid, u64 entryPoint, u64 entryArg, u64 stackTop, u64 tls, u8 priority, KProcess *parent); + KThread(const DeviceState &state, handle_t handle, pid_t self_pid, u64 entryPoint, u64 entryArg, u64 stackTop, u64 tls, u8 priority, KProcess *parent, std::shared_ptr& tlsMemory); /** * @brief Kills the thread and deallocates the memory allocated for stack. diff --git a/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp index 662cb1b3..ce50fe22 100644 --- a/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp @@ -1,6 +1,7 @@ #include "KTransferMemory.h" #include #include +#include namespace skyline::kernel::type { u64 MapTransferFunc(u64 address, size_t size, u64 perms) { @@ -9,14 +10,17 @@ namespace skyline::kernel::type { KTransferMemory::KTransferMemory(const DeviceState &state, pid_t pid, u64 address, size_t size, const memory::Permission permission) : owner(pid), cSize(size), permission(permission), KObject(state, KType::KTransferMemory) { if (pid) { - user_pt_regs fregs = {0}; - fregs.regs[0] = address; - fregs.regs[1] = size; - fregs.regs[2] = static_cast(permission.Get()); - state.nce->ExecuteFunction(reinterpret_cast(MapTransferFunc), fregs, pid); - if (reinterpret_cast(fregs.regs[0]) == MAP_FAILED) + Registers fregs{}; + fregs.x0 = address; + fregs.x1 = size; + fregs.x2 = static_cast(permission.Get()); + fregs.x3 = static_cast(MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0)); // NOLINT(hicpp-signed-bitwise) + fregs.x4 = static_cast(-1); + fregs.x8 = __NR_mmap; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, pid); + if (fregs.x0 < 0) throw exception("An error occurred while mapping shared region in child process"); - cAddress = fregs.regs[0]; + cAddress = fregs.x0; } else { address = MapTransferFunc(address, size, static_cast(permission.Get())); if (reinterpret_cast(address) == MAP_FAILED) @@ -31,14 +35,18 @@ namespace skyline::kernel::type { u64 KTransferMemory::Transfer(pid_t process, u64 address, u64 size) { if (process) { - user_pt_regs fregs = {0}; - fregs.regs[0] = address; - fregs.regs[1] = size; - fregs.regs[2] = static_cast(permission.Get()); - state.nce->ExecuteFunction(reinterpret_cast(MapTransferFunc), fregs, process); - if (reinterpret_cast(fregs.regs[0]) == MAP_FAILED) + Registers fregs{}; + fregs.x0 = address; + fregs.x1 = size; + fregs.x2 = static_cast(permission.Get()); + fregs.x3 = static_cast(MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0)); // NOLINT(hicpp-signed-bitwise) + fregs.x4 = static_cast(-1); + fregs.x8 = __NR_mmap; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, process); + // state.nce->ExecuteFunction(reinterpret_cast(MapTransferFunc), fregs, process); + if (fregs.x0 < 0) throw exception("An error occurred while mapping transfer memory in child process"); - address = fregs.regs[0]; + address = fregs.x0; } else { address = MapTransferFunc(address, size, static_cast(permission.Get())); if (reinterpret_cast(address) == MAP_FAILED) @@ -52,12 +60,13 @@ namespace skyline::kernel::type { } else throw exception("Transferring from kernel to kernel is not supported"); if (owner) { - user_pt_regs fregs = {0}; - fregs.regs[0] = address; - fregs.regs[1] = size; - fregs.regs[2] = static_cast(permission.Get()); - state.nce->ExecuteFunction(reinterpret_cast(UnmapTransferFunc), fregs, owner); - if (reinterpret_cast(fregs.regs[0]) == MAP_FAILED) + Registers fregs{}; + fregs.x0 = address; + fregs.x1 = size; + fregs.x8 = __NR_munmap; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, owner); + // state.nce->ExecuteFunction(reinterpret_cast(UnmapTransferFunc), fregs, owner); + if (fregs.x0 < 0) throw exception("An error occurred while unmapping transfer memory in child process"); } else { if (reinterpret_cast(UnmapTransferFunc(address, size)) == MAP_FAILED) @@ -87,12 +96,11 @@ namespace skyline::kernel::type { KTransferMemory::~KTransferMemory() { if (owner) { try { - user_pt_regs fregs = {0}; - fregs.regs[0] = cAddress; - fregs.regs[1] = cSize; - state.nce->ExecuteFunction(reinterpret_cast(UnmapTransferFunc), fregs, owner); - } catch (const std::exception &) { - } + Registers fregs{}; + fregs.x0 = cAddress; + fregs.x1 = cSize; + state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, owner); + } 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 d3b94649..087123cd 100644 --- a/app/src/main/cpp/skyline/loader/loader.h +++ b/app/src/main/cpp/skyline/loader/loader.h @@ -22,6 +22,8 @@ namespace skyline::loader { } public: + u64 mainEntry{}; //!< The address of the actual entry point for the application + /** * @param filePath The path to the ROM file */ diff --git a/app/src/main/cpp/skyline/loader/nro.cpp b/app/src/main/cpp/skyline/loader/nro.cpp index f4d12622..74010a56 100644 --- a/app/src/main/cpp/skyline/loader/nro.cpp +++ b/app/src/main/cpp/skyline/loader/nro.cpp @@ -6,6 +6,7 @@ namespace skyline::loader { ReadOffset((u32 *) &header, 0x0, sizeof(NroHeader)); if (header.magic != constant::NroMagic) throw exception("Invalid NRO magic! 0x{0:X}", header.magic); + mainEntry = constant::BaseAddr; } void NroLoader::LoadProcessData(const std::shared_ptr process, const DeviceState &state) { @@ -17,7 +18,7 @@ namespace skyline::loader { ReadOffset(rodata.data(), header.ro.offset, header.ro.size); ReadOffset(data.data(), header.data.offset, header.data.size); - std::vector patch = state.nce->PatchCode(text, header.text.size + header.ro.size + header.data.size + header.bssSize); + std::vector patch = state.nce->PatchCode(text, constant::BaseAddr, header.text.size + header.ro.size + header.data.size + header.bssSize); u64 textSize = text.size(); u64 rodataSize = rodata.size(); diff --git a/app/src/main/cpp/skyline/nce.cpp b/app/src/main/cpp/skyline/nce.cpp index 0d635dd6..6ea110db 100644 --- a/app/src/main/cpp/skyline/nce.cpp +++ b/app/src/main/cpp/skyline/nce.cpp @@ -1,237 +1,184 @@ #include -#include -#include +#include #include "os.h" #include "jvm.h" -#include "guest.h" +#include "nce/guest.h" +#include "nce/instr.h" +#include "kernel/svc.h" extern bool Halt; extern skyline::Mutex jniMtx; namespace skyline { - void NCE::ReadRegisters(user_pt_regs ®isters, pid_t pid) const { - iovec iov = {®isters, sizeof(registers)}; - long status = ptrace(PTRACE_GETREGSET, pid ? pid : currPid, NT_PRSTATUS, &iov); - if (status == -1) - throw exception("Cannot read registers, PID: {}, Error: {}", pid, strerror(errno)); + 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); + } + state.ctx->state = ThreadState::WaitRun; + } + } } - void NCE::WriteRegisters(user_pt_regs ®isters, pid_t pid) const { - iovec iov = {®isters, sizeof(registers)}; - long status = ptrace(PTRACE_SETREGSET, pid ? pid : currPid, NT_PRSTATUS, &iov); - if (status == -1) - throw exception("Cannot write registers, PID: {}, Error: {}", pid, strerror(errno)); - } - - instr::Brk NCE::ReadBrk(u64 address, pid_t pid) const { - long status = ptrace(PTRACE_PEEKDATA, pid ? pid : currPid, address, NULL); - if (status == -1) - throw exception("Cannot read instruction from memory, Address: {}, PID: {}, Error: {}", address, pid, strerror(errno)); - return *(reinterpret_cast(&status)); - } - - NCE::NCE(const DeviceState &state) : state(state) {} + NCE::NCE(DeviceState &state) : state(state) {} void NCE::Execute() { - int status = 0; while (!Halt && state.os->process) { std::lock_guard jniGd(jniMtx); - for (const auto &thread : state.os->process->threadMap) { - state.os->thisThread = thread.second; - currPid = thread.first; - auto &currRegs = registerMap[currPid]; - if (state.thread->status == kernel::type::KThread::Status::Running) { - if (waitpid(state.thread->pid, &status, WNOHANG) == state.thread->pid) { - if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP || WSTOPSIG(status) == SIGSTOP)) { // NOLINT(hicpp-signed-bitwise) - ReadRegisters(currRegs); - auto instr = ReadBrk(currRegs.pc); - if (instr.Verify()) { - // We store the instruction value as the immediate value in BRK. 0x0 to 0x7F are SVC, 0x80 to 0x9E is MRS for TPIDRRO_EL0. - if (instr.value <= constant::SvcLast) { - state.os->SvcHandler(static_cast(instr.value)); - if (state.thread->status != kernel::type::KThread::Status::Running) - continue; - } else if (instr.value > constant::SvcLast && instr.value <= constant::SvcLast + constant::NumRegs) { - // Catch MRS that reads the value of TPIDRRO_EL0 (TLS) - SetRegister(static_cast(instr.value - (constant::SvcLast + 1)), state.thread->tls); - } else if (instr.value == constant::BrkRdy) - continue; - else { - ProcessTrace(); - throw exception("Received unhandled BRK: 0x{:X}", static_cast(instr.value)); - } - } - currRegs.pc += sizeof(u32); - WriteRegisters(currRegs); - ResumeProcess(); - } else { - try { - state.logger->Warn("Thread threw unknown signal, PID: {}, Stop Signal: {}", currPid, strsignal(WSTOPSIG(status))); // NOLINT(hicpp-signed-bitwise) - ProcessTrace(); - } catch (const exception &) { - state.logger->Warn("Thread threw unknown signal, PID: {}, Stop Signal: {}", currPid, strsignal(WSTOPSIG(status))); // NOLINT(hicpp-signed-bitwise) - } - state.os->KillThread(currPid); - } - } - } else if ((state.thread->status == kernel::type::KThread::Status::WaitSync || state.thread->status == kernel::type::KThread::Status::Sleeping || state.thread->status == kernel::type::KThread::Status::WaitCondVar) && state.thread->timeout != 0) { // timeout == 0 means sleep forever - if (state.thread->timeout <= GetCurrTimeNs()) { - if(state.thread->status != kernel::type::KThread::Status::Sleeping) - state.logger->Info("An event has timed out: {}", state.thread->status); - if (state.thread->status == kernel::type::KThread::Status::WaitSync || state.thread->status == kernel::type::KThread::Status::WaitCondVar) - SetRegister(Wreg::W0, constant::status::Timeout); - state.thread->status = kernel::type::KThread::Status::Runnable; - } - } - if (state.thread->status == kernel::type::KThread::Status::Runnable) { - state.thread->ClearWaitObjects(); - state.thread->status = kernel::type::KThread::Status::Running; - currRegs.pc += sizeof(u32); - WriteRegisters(currRegs); - ResumeProcess(); - } - } state.os->serviceManager.Loop(); state.gpu->Loop(); } + Halt = false; } - void BrkLr() { - asm("BRK #0xFF"); // BRK #constant::brkRdy + 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; + ctx->commandId = static_cast(call); + ctx->state = ThreadState::WaitFunc; + while(ctx->state != ThreadState::WaitInit && ctx->state != ThreadState::WaitKernel); + ctx->commandId = cmdId; + funcRegs = ctx->registers; + ctx->registers = registers; } - void NCE::ExecuteFunction(void *func, user_pt_regs &funcRegs, pid_t pid) { - pid = pid ? pid : currPid; - bool wasRunning = PauseProcess(pid); - user_pt_regs backupRegs{}; - ReadRegisters(backupRegs, pid); - funcRegs.pc = reinterpret_cast(func); - funcRegs.sp = backupRegs.sp; - funcRegs.regs[static_cast(Xreg::X30)] = reinterpret_cast(BrkLr); // Set LR to 'brk_lr' so the application will hit a breakpoint after the function returns - WriteRegisters(funcRegs, pid); - ResumeProcess(pid); - funcRegs = WaitRdy(pid); - WriteRegisters(backupRegs, pid); - if (wasRunning) - ResumeProcess(pid); + 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; } - user_pt_regs NCE::WaitRdy(pid_t pid) { - int status; - user_pt_regs regs{}; - waitpid(pid, &status, 0); - if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP)) { // NOLINT(hicpp-signed-bitwise) - ReadRegisters(regs, pid); - auto instr = ReadBrk(regs.pc, pid); - if (instr.Verify() && instr.value == constant::BrkRdy) { - regs.pc += 4; // Increment program counter by a single instruction (32 bits) - WriteRegisters(regs, pid); - return regs; - } else - throw exception("An unknown BRK was hit during WaitRdy, PID: {}, BRK value: {}", pid, static_cast(instr.value)); - } else - throw exception("An unknown signal was caused during WaitRdy, PID: {}, Status: 0x{:X}, Signal: {}", pid, status, strsignal(WSTOPSIG(status))); // NOLINT(hicpp-signed-bitwise) + void NCE::WaitThreadInit(std::shared_ptr& thread) { + auto ctx = reinterpret_cast(thread->ctxMemory->kernel.address); + while(ctx->state == ThreadState::NotReady); } - bool NCE::PauseProcess(pid_t pid) const { - pid = pid ? pid : currPid; - int status = 0; - waitpid(pid, &status, WNOHANG); - bool wasStopped = WIFSTOPPED(status); // NOLINT(hicpp-signed-bitwise) - if (wasStopped) { - if ((kill(pid, SIGSTOP) != -1) && (waitpid(pid, nullptr, WNOHANG) != -1)) - return true; - else - throw exception("Cannot pause process: {}, Error: {}", pid, strerror(errno)); - } else - return false; + void NCE::StartThread(u64 entryArg, u32 handle, std::shared_ptr &thread) { + auto ctx = reinterpret_cast(thread->ctxMemory->kernel.address); + 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); + threadMap[thread->pid] = std::make_shared(&NCE::KernelThread, this, thread->pid); } - void NCE::ResumeProcess(pid_t pid) const { - long status = ptrace(PTRACE_CONT, pid ? pid : currPid, NULL, NULL); - if (status == -1) - throw exception("Cannot resume process: {}, Error: {}", pid, strerror(errno)); - } - - void NCE::StartProcess(u64 entryPoint, u64 entryArg, u64 stackTop, u32 handle, pid_t pid) const { - user_pt_regs regs{0}; - regs.sp = stackTop; - regs.pc = entryPoint; - regs.regs[0] = entryArg; - regs.regs[1] = handle; - WriteRegisters(regs, pid); - ResumeProcess(pid); - } - - void NCE::ProcessTrace(u16 numHist, pid_t pid) { - pid = pid ? pid : currPid; - user_pt_regs regs{}; - ReadRegisters(regs, pid); - u64 offset = regs.pc - (sizeof(u32) * numHist); - std::string raw{}; - state.logger->Debug("Process Trace:"); - for (; offset <= (regs.pc + sizeof(u32)); offset += sizeof(u32)) { - u32 instr = __builtin_bswap32(static_cast(ptrace(PTRACE_PEEKDATA, pid, offset, NULL))); - if (offset == regs.pc) - state.logger->Debug("-> 0x{:X} : 0x{:08X}", offset, instr); - else - state.logger->Debug(" 0x{:X} : 0x{:08X}", offset, instr); - raw += fmt::format("{:08X}", instr); - } - state.logger->Debug("Raw Instructions: 0x{}", raw); + void NCE::ThreadTrace(u16 numHist, ThreadContext *ctx) { + std::string raw; + std::string trace; std::string regStr; + ctx = ctx ? ctx : state.ctx; + if(numHist) { + std::vector instrs(numHist); + u64 size = (sizeof(u32) * numHist); + u64 offset = ctx->pc - size; + state.process->ReadMemory(instrs.data(), offset, size); + for (auto &instr : instrs) { + instr = __builtin_bswap32(instr); + if (offset == ctx->pc) + trace += fmt::format("\n-> 0x{:X} : 0x{:08X}", offset, instr); + else + trace += fmt::format("\n 0x{:X} : 0x{:08X}", offset, instr); + raw += fmt::format("{:08X}", instr); + offset += sizeof(u32); + } + } for (u16 index = 0; index < constant::NumRegs - 1; index+=2) { - regStr += fmt::format("\nX{}: 0x{:X}, X{}: 0x{:X}", index, regs.regs[index], index+1, regs.regs[index+1]); + regStr += fmt::format("\nX{}: 0x{:X}, X{}: 0x{:X}", index, ctx->registers.regs[index], index+1, ctx->registers.regs[index+1]); } - state.logger->Debug("CPU Context:\nSP: 0x{:X}\nLR: 0x{:X}\nPSTATE: 0x{:X}{}", regs.sp, regs.regs[30], regs.pstate, regStr); + if(numHist) + state.logger->Debug("Process Trace:{}\nRaw Instructions: 0x{}\nCPU Context:{}", trace, raw, regStr); + else + state.logger->Warn("CPU Context:{}", regStr); } - u64 NCE::GetRegister(Xreg regId, pid_t pid) { - return registerMap.at(pid ? pid : currPid).regs[static_cast(regId)]; - } + const std::array cntpctEl0X0 = { + 0xA9BF0BE1, // STP X1, X2, [SP, #-16]! + 0x3C9F0FE0, // STR Q0, [SP, #-16]! + 0x3C9F0FE1, // STR Q1, [SP, #-16]! + 0x3C9F0FE2, // STR Q2, [SP, #-16]! + 0xD53BE001, // MRS X1, CNTFRQ_EL0 + 0xD53BE042, // MRS X2, CNTVCT_EL0 + 0x9E630020, // UCVTF D0, X0 + 0xD2C9F001, // MOV X1, 87411174408192 + 0xF2E82E41, // MOVK X1, 0x4172, LSL 48 + 0x9E670022, // FMOV D2, X1 + 0x9E630041, // UCVTF D1, X1 + 0x1E621800, // FDIV D0, D0, D2 + 0x1E610800, // FMUL D0, D0, D1 + 0x9E790000, // FCVTZU X0, D0 + 0x3CC107E2, // LDR Q2, [SP], #16 + 0x3CC107E1, // LDR Q1, [SP], #16 + 0xA97F07C0, // LDP X1, X2, [LR, #-16] + }; - void NCE::SetRegister(Xreg regId, u64 value, pid_t pid) { - registerMap.at(pid ? pid : currPid).regs[static_cast(regId)] = value; - } + const std::array cntpctEl0X1 = { + 0xA9BF0BE0, // STP X0, X2, [SP, #-16]! + 0x3C9F0FE0, // STR Q0, [SP, #-16]! + 0x3C9F0FE1, // STR Q1, [SP, #-16]! + 0x3C9F0FE2, // STR Q2, [SP, #-16]! + 0xD53BE000, // MRS X0, CNTFRQ_EL0 + 0xD53BE042, // MRS X2, CNTVCT_EL0 + 0x9E630020, // UCVTF D0, X0 + 0xD2C9F000, // MOV X0, 87411174408192 + 0xF2E82E40, // MOVK X0, 0x4172, LSL 48 + 0x9E670002, // FMOV D2, X0 + 0x9E630041, // UCVTF D1, X2 + 0x1E621800, // FDIV D0, D0, D2 + 0x1E610800, // FMUL D0, D0, D1 + 0x9E790001, // FCVTZU X0, D0 + 0x3CC107E2, // LDR Q2, [SP], #16 + 0x3CC107E1, // LDR Q1, [SP], #16 + 0xA97F0BC0, // LDP X0, X2, [LR, #-16] + }; - u32 NCE::GetRegister(Wreg regId, pid_t pid) { - return (reinterpret_cast(®isterMap.at(pid ? pid : currPid).regs))[static_cast(regId) * 2]; - } + const std::array cntpctEl0Xn = { + 0xA9BF07E0, // STP X0, X1, [SP, #-16]! + 0x3C9F0FE0, // STR Q0, [SP, #-16]! + 0x3C9F0FE1, // STR Q1, [SP, #-16]! + 0x3C9F0FE2, // STR Q2, [SP, #-16]! + 0xD53BE000, // MRS X0, CNTFRQ_EL0 + 0xD53BE041, // MRS X1, CNTVCT_EL0 + 0x9E630000, // UCVTF D0, X0 + 0xD2C9F000, // MOV X0, 87411174408192 + 0xF2E82E40, // MOVK X0, 0x4172, LSL 48 + 0x9E670002, // FMOV D2, X0 + 0x9E630021, // UCVTF D1, X1 + 0x1E621800, // FDIV D0, D0, D2 + 0x1E610800, // FMUL D0, D0, D1 + 0x9E790000, // FCVTZU Xn, D0 + 0x3CC107E2, // LDR Q2, [SP], #16 + 0x3CC107E1, // LDR Q1, [SP], #16 + 0xA97F07C0, // LDP X0, X1, [LR, #-16] + }; - void NCE::SetRegister(Wreg regId, u32 value, pid_t pid) { - (reinterpret_cast(®isterMap.at(pid ? pid : currPid).regs))[static_cast(regId) * 2] = value; - } - - u64 NCE::GetRegister(Sreg regId, pid_t pid) { - pid = pid ? pid : currPid; - switch (regId) { - case Sreg::Pc: - return registerMap.at(pid).pc; - case Sreg::Sp: - return registerMap.at(pid).sp; - case Sreg::PState: - return registerMap.at(pid).pstate; - } - } - - void NCE::SetRegister(Sreg regId, u32 value, pid_t pid) { - pid = pid ? pid : currPid; - switch (regId) { - case Sreg::Pc: - registerMap.at(pid).pc = value; - case Sreg::Sp: - registerMap.at(pid).sp = value; - case Sreg::PState: - registerMap.at(pid).pstate = value; - } - } - - std::vector NCE::PatchCode(std::vector& code, i64 offset) { - u32 *address = reinterpret_cast(code.data()); - u32 *end = address + (code.size() / sizeof(u32)); + std::vector NCE::PatchCode(std::vector &code, u64 baseAddress, i64 offset) { + u32 *start = reinterpret_cast(code.data()); + u32 *end = start + (code.size() / sizeof(u32)); i64 patchOffset = offset; - std::vector patch((guest::saveCtxSize + guest::loadCtxSize) / sizeof(u32)); + std::vector patch((guest::saveCtxSize + guest::loadCtxSize + guest::svcHandlerSize) / sizeof(u32)); + std::memcpy(patch.data(), reinterpret_cast(&guest::saveCtx), guest::saveCtxSize); offset += guest::saveCtxSize; @@ -239,18 +186,29 @@ namespace skyline { reinterpret_cast(&guest::loadCtx), guest::loadCtxSize); offset += guest::loadCtxSize; - while (address < end) { + 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++) { 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::Brk brk(static_cast(instrSvc->value)); - offset += sizeof(brk); instr::BL bSvCtx(patchOffset - offset); offset += sizeof(bSvCtx); + + auto movPc = instr::MoveU64Reg(regs::X0, pc); + offset += sizeof(u32) * movPc.size(); + instr::Movz movCmd(regs::W1, static_cast(instrSvc->value)); + offset += sizeof(movCmd); + instr::BL bSvcHandler((patchOffset + guest::saveCtxSize + guest::loadCtxSize) - offset); + offset += sizeof(bSvcHandler); + instr::BL bLdCtx((patchOffset + guest::saveCtxSize) - offset); offset += sizeof(bLdCtx); constexpr u32 ldrLr = 0xF84107FE; // LDR LR, [SP], #16 @@ -260,28 +218,79 @@ namespace skyline { *address = bjunc.raw; patch.push_back(strLr); - patch.push_back(brk.raw); patch.push_back(bSvCtx.raw); + 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()) { if (instrMrs->srcReg == constant::TpidrroEl0) { instr::B bjunc(offset); - instr::Brk brk(static_cast(constant::SvcLast + 1 + instrMrs->dstReg)); - offset += sizeof(u32); + u32 strX0{}; + if(instrMrs->destReg != regs::X0) { + strX0 = 0xF81F0FE0; // STR X0, [SP, #-16]! + offset += sizeof(strX0); + } + u32 mrsX0 = 0xD53BD040; // MRS X0, TPIDR_EL0 + offset += sizeof(mrsX0); + u32 ldrTls = 0xF9408000; // LDR X0, [X0, #256] + offset += sizeof(ldrTls); + u32 movXn{}; + u32 ldrX0{}; + if(instrMrs->destReg != regs::X0) { + movXn = instr::Mov(regs::X(instrMrs->destReg), regs::X0).raw; + offset += sizeof(movXn); + ldrX0 = 0xF84107E0; // LDR X0, [SP], #16 + offset += sizeof(ldrX0); + } instr::B bret(-offset + sizeof(u32)); - offset += sizeof(u32); + offset += sizeof(bret); *address = bjunc.raw; - patch.push_back(brk.raw); + 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); + patch.push_back(bret.raw); + } if (instrMrs->srcReg == constant::CntpctEl0) { + instr::B bjunc(offset); + if(instrMrs->destReg == 0) + offset += cntpctEl0X0.size() * sizeof(u32); + else if (instrMrs->destReg == 1) + offset += cntpctEl0X1.size() * sizeof(u32); + else + offset += cntpctEl0Xn.size() * sizeof(u32); + instr::B bret(-offset + sizeof(u32)); + offset += sizeof(bret); + + *address = bjunc.raw; + if(instrMrs->destReg == 0) + for(auto& instr : cntpctEl0X0) + patch.push_back(instr); + else if (instrMrs->destReg == 1) + for(auto& instr : cntpctEl0X1) + patch.push_back(instr); + else + for(auto& instr : cntpctEl0Xn) + patch.push_back(instr); + patch.push_back(bret.raw); + } 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(); + instr::B bret(-offset + sizeof(u32)); + offset += sizeof(bret); + + *address = bjunc.raw; + for(auto& instr : movFreq) + patch.push_back(instr); patch.push_back(bret.raw); - } else if (instrMrs->srcReg == constant::CntpctEl0) { - instr::Mrs mrs(constant::CntvctEl0, instrMrs->dstReg); - *address = mrs.raw; } } - address++; offset -= sizeof(u32); patchOffset -= sizeof(u32); } diff --git a/app/src/main/cpp/skyline/nce.h b/app/src/main/cpp/skyline/nce.h index 508fa0ef..ab9baec7 100644 --- a/app/src/main/cpp/skyline/nce.h +++ b/app/src/main/cpp/skyline/nce.h @@ -9,146 +9,72 @@ #include "kernel/types/KSharedMemory.h" namespace skyline { - namespace instr { - struct Brk; - } - /** * @brief The NCE (Native Code Execution) class is responsible for managing the state of catching instructions and directly controlling processes/threads */ class NCE { private: - pid_t currPid = 0; //!< The PID of the process currently being handled, this is so the PID won't have to be passed into functions like ReadRegister redundantly - std::unordered_map registerMap; //!< A map of all PIDs and their corresponding registers (Whenever they were last updated) - const DeviceState &state; //!< The state of the device + DeviceState &state; //!< The state of the device + std::unordered_map> threadMap; //!< This maps all of the host threads to their corresponding kernel thread /** - * @brief Reads process registers into the `registers` variable - * @param registers A set of registers to fill with values from the process - * @param pid The PID of the process (Defaults to currPid) + * @brief This function is the event loop of a kernel thread managing a guest thread + * @param thread The PID of the thread to manage */ - void ReadRegisters(user_pt_regs ®isters, pid_t pid = 0) const; - - /** - * @brief Writes process registers from the `registers` variable - * @param registers The registers to be written by the process - * @param pid The PID of the process (Defaults to currPid) - */ - void WriteRegisters(user_pt_regs ®isters, pid_t pid = 0) const; - - /** - * @brief Reads a BRK instruction, this is used to get it's immediate value - * @param address The address of the BRK instruction - * @param pid The PID of the process (Defaults to currPid) - * @return An instance of BRK with the corresponding values - */ - instr::Brk ReadBrk(u64 address, pid_t pid = 0) const; + void KernelThread(pid_t thread); public: - NCE(const DeviceState &state); + NCE(DeviceState &state); /** - * @brief Start the event loop of executing the program + * @brief This function is the main event loop of the program */ void Execute(); /** * @brief Execute any arbitrary function on a particular child process - * @param func The entry point of the function - * @param funcRegs A set of registers to run the function with (PC, SP and X29 are replaced) + * @param call The specific call to execute + * @param funcRegs A set of registers to run the function + * @param thread The thread to execute the function on + */ + void ExecuteFunction(ThreadCall call, Registers &funcRegs, std::shared_ptr& thread); + + /** + * @brief Execute any arbitrary function on a particular child process + * @param call The specific call to execute + * @param funcRegs A set of registers to run the function * @param pid The PID of the process */ - void ExecuteFunction(void *func, user_pt_regs &funcRegs, pid_t pid); + void ExecuteFunction(ThreadCall call, Registers &funcRegs, pid_t pid); /** - * @brief Waits till a process calls "BRK #constant::brk_rdy" - * @param pid The PID of the process - * @return The registers after the BRK + * @brief Waits till a thread is ready to execute commands + * @param thread The KThread to wait for initialization */ - user_pt_regs WaitRdy(pid_t pid); + void WaitThreadInit(std::shared_ptr& thread); /** - * @brief Pauses a particular process if was not already paused - * @param pid The PID of the process (Defaults to currPid) - * @return If the application was paused beforehand - */ - bool PauseProcess(pid_t pid = 0) const; - - /** - * @brief Resumes a particular process, does nothing if it was already running - * @param pid The PID of the process (Defaults to currPid) - */ - void ResumeProcess(pid_t pid = 0) const; - - /** - * @brief Starts a particular process, sets the registers to their expected values and jumps to address - * @param entryPoint The address to jump to + * @brief Sets the X0 and X1 registers in a thread and starts it and it's kernel thread * @param entryArg The argument to pass in for the entry function - * @param stackTop The top of the stack - * @param handle The handle of the main thread (Set to value of 1st register) - * @param pid The PID of the process (Defaults to currPid) + * @param handle The handle of the main thread + * @param thread The thread to set the registers and start + * @note This function will block forever if the thread has already started */ - void StartProcess(u64 entryPoint, u64 entryArg, u64 stackTop, u32 handle, pid_t pid) const; + void StartThread(u64 entryArg, u32 handle, std::shared_ptr &thread); /** * @brief This prints out a trace and the CPU context - * @param numHist The amount of previous instructions to print - * @param pid The PID of the process (Defaults to currPid) + * @param numHist The amount of previous instructions to print (Can be 0) + * @param ctx The ThreadContext of the thread to log */ - void ProcessTrace(u16 numHist = 10, pid_t pid = 0); - - /** - * @brief Get the value of a Xn register - * @param regId The ID of the register - * @param pid The PID of the process (Defaults to currPid) - * @return The value of the register - */ - u64 GetRegister(Xreg regId, pid_t pid = 0); - - /** - * @brief Set the value of a Xn register - * @param regId The ID of the register - * @param value The value to set - * @param pid The PID of the process (Defaults to currPid) - */ - void SetRegister(Xreg regId, u64 value, pid_t pid = 0); - - /** - * @brief Get the value of a Wn register - * @param regId The ID of the register - * @param pid The PID of the process (Defaults to currPid) - * @return The value in the register - */ - u32 GetRegister(Wreg regId, pid_t pid = 0); - - /** - * @brief Set the value of a Wn register - * @param regId The ID of the register - * @param value The value to set - * @param pid The PID of the process (Defaults to currPid) - */ - void SetRegister(Wreg regId, u32 value, pid_t pid = 0); - - /** - * @brief Get the value of a special register - * @param regId The ID of the register - * @param pid The PID of the process (Defaults to currPid) - * @return The value in the register - */ - u64 GetRegister(Sreg regId, pid_t pid = 0); - - /** - * @brief Set the value of a special register - * @param regId The ID of the register - * @param value The value to set - * @param pid The PID of the process (Defaults to currPid) - */ - void SetRegister(Sreg regId, u32 value, pid_t pid = 0); + void ThreadTrace(u16 numHist = 10, ThreadContext *ctx = nullptr); /** * @brief This patches specific parts of the code * @param code A vector with the code to be patched + * @param baseAddress The address at which the code is mapped + * @param offset The offset of the code block from the base address */ - std::vector PatchCode(std::vector &code, i64 offset); + std::vector PatchCode(std::vector &code, u64 baseAddress, i64 offset); }; } diff --git a/app/src/main/cpp/skyline/nce/guest.S b/app/src/main/cpp/skyline/nce/guest.S new file mode 100644 index 00000000..6fd59fc0 --- /dev/null +++ b/app/src/main/cpp/skyline/nce/guest.S @@ -0,0 +1,44 @@ +.text +.global saveCtx +saveCtx: + STR LR, [SP, #-16]! + MRS LR, TPIDR_EL0 + STP X0, X1, [LR, #16] + STP X2, X3, [LR, #32] + STP X4, X5, [LR, #48] + STP X6, X7, [LR, #64] + STP X8, X9, [LR, #80] + STP X10, X11, [LR, #96] + STP X12, X13, [LR, #112] + STP X14, X15, [LR, #128] + STP X16, X17, [LR, #144] + STP X18, X19, [LR, #160] + STP X20, X21, [LR, #176] + STP X22, X23, [LR, #192] + STP X24, X25, [LR, #208] + STP X26, X27, [LR, #224] + STP X28, X29, [LR, #240] + LDR LR, [SP], #16 + RET + +.global loadCtx +loadCtx: + STR LR, [SP, #-16]! + MRS LR, TPIDR_EL0 + LDP X0, X1, [LR, #16] + LDP X2, X3, [LR, #32] + LDP X4, X5, [LR, #48] + LDP X6, X7, [LR, #64] + LDP X8, X9, [LR, #80] + LDP X10, X11, [LR, #96] + LDP X12, X13, [LR, #112] + LDP X14, X15, [LR, #128] + LDP X16, X17, [LR, #144] + LDP X18, X19, [LR, #160] + LDP X20, X21, [LR, #176] + LDP X22, X23, [LR, #192] + LDP X24, X25, [LR, #208] + LDP X26, X27, [LR, #224] + LDP X28, X29, [LR, #240] + LDR LR, [SP], #16 + RET diff --git a/app/src/main/cpp/skyline/nce/guest.cpp b/app/src/main/cpp/skyline/nce/guest.cpp new file mode 100644 index 00000000..70f08115 --- /dev/null +++ b/app/src/main/cpp/skyline/nce/guest.cpp @@ -0,0 +1,162 @@ +#include "guest_common.h" + +#define FORCE_INLINE __attribute__((always_inline)) inline // NOLINT(cppcoreguidelines-macro-usage) + +namespace skyline::guest { + FORCE_INLINE void saveCtxStack() { + asm("SUB SP, SP, #240\n\t" + "STP X0, X1, [SP, #0]\n\t" + "STP X2, X3, [SP, #16]\n\t" + "STP X4, X5, [SP, #32]\n\t" + "STP X6, X7, [SP, #48]\n\t" + "STP X8, X9, [SP, #64]\n\t" + "STP X10, X11, [SP, #80]\n\t" + "STP X12, X13, [SP, #96]\n\t" + "STP X14, X15, [SP, #112]\n\t" + "STP X16, X17, [SP, #128]\n\t" + "STP X18, X19, [SP, #144]\n\t" + "STP X20, X21, [SP, #160]\n\t" + "STP X22, X23, [SP, #176]\n\t" + "STP X24, X25, [SP, #192]\n\t" + "STP X26, X27, [SP, #208]\n\t" + "STP X28, X29, [SP, #224]" + ); + } + + FORCE_INLINE void loadCtxStack() { + asm("LDP X0, X1, [SP, #0]\n\t" + "LDP X2, X3, [SP, #16]\n\t" + "LDP X4, X5, [SP, #32]\n\t" + "LDP X6, X7, [SP, #48]\n\t" + "LDP X8, X9, [SP, #64]\n\t" + "LDP X10, X11, [SP, #80]\n\t" + "LDP X12, X13, [SP, #96]\n\t" + "LDP X14, X15, [SP, #112]\n\t" + "LDP X16, X17, [SP, #128]\n\t" + "LDP X18, X19, [SP, #144]\n\t" + "LDP X20, X21, [SP, #160]\n\t" + "LDP X22, X23, [SP, #176]\n\t" + "LDP X24, X25, [SP, #192]\n\t" + "LDP X26, X27, [SP, #208]\n\t" + "LDP X28, X29, [SP, #224]\n\t" + "ADD SP, SP, #240" + ); + } + + FORCE_INLINE void saveCtxTls() { + asm("STR LR, [SP, #-16]!\n\t" + "MRS LR, TPIDR_EL0\n\t" + "STP X0, X1, [LR, #16]\n\t" + "STP X2, X3, [LR, #32]\n\t" + "STP X4, X5, [LR, #48]\n\t" + "STP X6, X7, [LR, #64]\n\t" + "STP X8, X9, [LR, #80]\n\t" + "STP X10, X11, [LR, #96]\n\t" + "STP X12, X13, [LR, #112]\n\t" + "STP X14, X15, [LR, #128]\n\t" + "STP X16, X17, [LR, #144]\n\t" + "STP X18, X19, [LR, #160]\n\t" + "STP X20, X21, [LR, #176]\n\t" + "STP X22, X23, [LR, #192]\n\t" + "STP X24, X25, [LR, #208]\n\t" + "STP X26, X27, [LR, #224]\n\t" + "STP X28, X29, [LR, #240]\n\t" + "LDR LR, [SP], #16" + ); + } + + FORCE_INLINE void loadCtxTls() { + asm("STR LR, [SP, #-16]!\n\t" + "MRS LR, TPIDR_EL0\n\t" + "LDP X0, X1, [LR, #16]\n\t" + "LDP X2, X3, [LR, #32]\n\t" + "LDP X4, X5, [LR, #48]\n\t" + "LDP X6, X7, [LR, #64]\n\t" + "LDP X8, X9, [LR, #80]\n\t" + "LDP X10, X11, [LR, #96]\n\t" + "LDP X12, X13, [LR, #112]\n\t" + "LDP X14, X15, [LR, #128]\n\t" + "LDP X16, X17, [LR, #144]\n\t" + "LDP X18, X19, [LR, #160]\n\t" + "LDP X20, X21, [LR, #176]\n\t" + "LDP X22, X23, [LR, #192]\n\t" + "LDP X24, X25, [LR, #208]\n\t" + "LDP X26, X27, [LR, #224]\n\t" + "LDP X28, X29, [LR, #240]\n\t" + "LDR LR, [SP], #16" + ); + } + + void svcHandler(u64 pc, u32 svc) { + 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 { + 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); + 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(); + } + } + } + ctx->state = ThreadState::Running; + asm("MOV LR, %0\n\t" + "MOV X0, %1\n\t" + "MOV X1, %2\n\t" + "MOV X2, XZR\n\t" + "MOV X3, XZR\n\t" + "MOV X4, XZR\n\t" + "MOV X5, XZR\n\t" + "MOV X6, XZR\n\t" + "MOV X7, XZR\n\t" + "MOV X8, XZR\n\t" + "MOV X9, XZR\n\t" + "MOV X10, XZR\n\t" + "MOV X11, XZR\n\t" + "MOV X12, XZR\n\t" + "MOV X13, XZR\n\t" + "MOV X14, XZR\n\t" + "MOV X15, XZR\n\t" + "MOV X16, XZR\n\t" + "MOV X17, XZR\n\t" + "MOV X18, XZR\n\t" + "MOV X19, XZR\n\t" + "MOV X20, XZR\n\t" + "MOV X21, XZR\n\t" + "MOV X22, XZR\n\t" + "MOV X23, XZR\n\t" + "MOV X24, XZR\n\t" + "MOV X25, XZR\n\t" + "MOV X26, XZR\n\t" + "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"); + __builtin_unreachable(); + } +} diff --git a/app/src/main/cpp/skyline/nce/guest.h b/app/src/main/cpp/skyline/nce/guest.h new file mode 100644 index 00000000..be77f0ab --- /dev/null +++ b/app/src/main/cpp/skyline/nce/guest.h @@ -0,0 +1,13 @@ +#pragma once + +namespace skyline { + namespace guest { + constexpr size_t saveCtxSize = 20 * sizeof(u32); + constexpr size_t loadCtxSize = 20 * sizeof(u32); + constexpr size_t svcHandlerSize = 200 * sizeof(u32); + void entry(u64 address); + extern "C" void saveCtx(void); + extern "C" void loadCtx(void); + void svcHandler(u64 pc, u32 svc); + } +} diff --git a/app/src/main/cpp/skyline/nce/guest_common.h b/app/src/main/cpp/skyline/nce/guest_common.h new file mode 100644 index 00000000..47271473 --- /dev/null +++ b/app/src/main/cpp/skyline/nce/guest_common.h @@ -0,0 +1,149 @@ +#pragma once + +#include + +namespace skyline { + using u128 = __uint128_t; //!< Unsigned 128-bit integer + using u64 = __uint64_t; //!< Unsigned 64-bit integer + using u32 = __uint32_t; //!< Unsigned 32-bit integer + using u16 = __uint16_t; //!< Unsigned 16-bit integer + using u8 = __uint8_t; //!< Unsigned 8-bit integer + using i128 = __int128_t; //!< Signed 128-bit integer + using i64 = __int64_t; //!< Signed 64-bit integer + using i32 = __int32_t; //!< Signed 32-bit integer + using i16 = __int16_t; //!< Signed 16-bit integer + using i8 = __int8_t; //!< Signed 8-bit integer + + /** + * @brief This union holds the state of all the general purpose registers in the guest + * @note Read about ARMv8 registers here: https://developer.arm.com/docs/100878/latest/registers + * @note X30 or LR is not provided as it is reserved for other uses + */ + union Registers { + u64 regs[30]; + struct { + u64 x0; + u64 x1; + u64 x2; + u64 x3; + u64 x4; + u64 x5; + u64 x6; + u64 x7; + u64 x8; + u64 x9; + u64 x10; + u64 x11; + u64 x12; + u64 x13; + u64 x14; + u64 x15; + u64 x16; + u64 x17; + u64 x18; + u64 x19; + u64 x20; + u64 x21; + u64 x22; + u64 x23; + u64 x24; + u64 x25; + u64 x26; + u64 x27; + u64 x28; + u64 x29; + }; + struct { + u32 w0; + u32 __w0__; + u32 w1; + u32 __w1__; + u32 w2; + u32 __w2__; + u32 w3; + u32 __w3__; + u32 w4; + u32 __w4__; + u32 w5; + u32 __w5__; + u32 w6; + u32 __w6__; + u32 w7; + u32 __w7__; + u32 w8; + u32 __w8__; + u32 w9; + u32 __w9__; + u32 w10; + u32 __w10__; + u32 w11; + u32 __w11__; + u32 w12; + u32 __w12__; + u32 w13; + u32 __w13__; + u32 w14; + u32 __w14__; + u32 w15; + u32 __w15__; + u32 w16; + u32 __w16__; + u32 w17; + u32 __w17__; + u32 w18; + u32 __w18__; + u32 w19; + u32 __w19__; + u32 w20; + u32 __w20__; + u32 w21; + u32 __w21__; + u32 w22; + u32 __w22__; + u32 w23; + u32 __w23__; + u32 w24; + u32 __w24__; + u32 w25; + u32 __w25__; + u32 w26; + u32 __w26__; + u32 w27; + u32 __w27__; + u32 w28; + u32 __w28__; + u32 w29; + u32 __w29__; + }; + }; + + /** + * @brief This enumeration is used to convey the state of a thread to the kernel + */ + enum class ThreadState : u32 { + NotReady = 0, //!< The thread hasn't yet entered the entry handler + Running = 1, //!< The thread is currently executing code + WaitKernel = 2, //!< The thread is currently waiting on the kernel + 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 + }; + + /** + * @brief This enumeration holds the functions that can be run on the guest process + */ + enum class ThreadCall : u32 { + Syscall = 0x100, //!< A linux syscall needs to be called from the guest + }; + + /** + * @brief This structure holds the context of a thread during kernel calls + */ + struct ThreadContext { + ThreadState state; //!< The state of the guest + u32 commandId; //!< The command ID of the current kernel call/function call + u64 pc; //!< The program counter register on the guest + Registers registers; //!< The general purpose registers on the guest + u64 tpidrroEl0; //!< The value for TPIDRRO_EL0 for the current thread + }; +} diff --git a/app/src/main/cpp/skyline/nce/instr.h b/app/src/main/cpp/skyline/nce/instr.h new file mode 100644 index 00000000..7e86f8b9 --- /dev/null +++ b/app/src/main/cpp/skyline/nce/instr.h @@ -0,0 +1,400 @@ +#include + +namespace skyline { + namespace regs { + enum X { X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30 }; + enum W { W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14, W15, W16, W17, W18, W19, W20, W21, W22, W23, W24, W25, W26, W27, W28, W29, W30 }; + enum S { Sp, Pc }; + } + + namespace instr { + /** + * @brief A bit-field struct that encapsulates a BRK instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/brk-breakpoint-instruction. + */ + struct Brk { + /** + * @brief Creates a BRK instruction with a specific immediate value, used for generating BRK opcodes + * @param value The immediate value of the instruction + */ + explicit Brk(u16 value) { + sig0 = 0x0; // First 5 bits of a BRK instruction are 0 + this->value = value; + sig1 = 0x6A1; // Last 11 bits of a BRK instruction stored as u16 + } + + /** + * @brief Returns if the opcode is valid or not + * @return If the opcode represents a valid BRK instruction + */ + inline bool Verify() { + return (sig0 == 0x0 && sig1 == 0x6A1); + } + + union { + struct { + u8 sig0 : 5; + u32 value : 16; + u16 sig1 : 11; + }; + u32 raw{}; + }; + }; + static_assert(sizeof(Brk) == sizeof(u32)); + + /** + * @brief A bit-field struct that encapsulates a SVC instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/svc-supervisor-call. + */ + struct Svc { + /** + * @brief Returns if the opcode is valid or not + * @return If the opcode represents a valid SVC instruction + */ + inline bool Verify() { + return (sig0 == 0x1 && sig1 == 0x6A0); + } + + union { + struct { + u8 sig0 : 5; + u32 value : 16; + u16 sig1 : 11; + }; + u32 raw{}; + }; + }; + static_assert(sizeof(Svc) == sizeof(u32)); + + /** + * @brief A bit-field struct that encapsulates a MRS instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/mrs-move-system-register. + */ + struct Mrs { + /** + * @brief Creates a MRS instruction, used for generating BRK opcodes + * @param srcReg The source system register + * @param dstReg The destination Xn register + */ + Mrs(u32 srcReg, regs::X dstReg) { + this->srcReg = srcReg; + this->destReg = dstReg; + sig = 0xD53; // Last 12 bits of a MRS instruction stored as u16 + } + + /** + * @brief Returns if the opcode is valid or not + * @return If the opcode represents a valid MRS instruction + */ + inline bool Verify() { + return (sig == 0xD53); + } + + union { + struct { + u8 destReg : 5; + u32 srcReg : 15; + u16 sig : 12; + }; + u32 raw{}; + }; + }; + static_assert(sizeof(Mrs) == sizeof(u32)); + + /** + * @brief A bit-field struct that encapsulates a B instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/b-branch. + */ + struct B { + public: + /** + * @brief Creates a B instruction with a specific offset + * @param offset The offset to encode in the instruction (Should be 32-bit aligned) + */ + explicit B(i64 offset) { + this->offset = static_cast(offset / 4); + sig = 0x5; + } + + /** + * @brief Returns the offset of the instruction + * @return The offset encoded within the instruction + */ + inline i32 Offset() { + return offset * 4; + } + + /** + * @brief Returns if the opcode is valid or not + * @return If the opcode represents a valid Branch instruction + */ + inline bool Verify() { + return (sig == 0x5); + } + + union { + struct { + i32 offset : 26; + u8 sig : 6; + }; + u32 raw{}; + }; + }; + static_assert(sizeof(B) == sizeof(u32)); + + /** + * @brief A bit-field struct that encapsulates a BL instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/b-branch. + */ + struct BL { + public: + /** + * @brief Creates a BL instruction with a specific offset + * @param offset The offset to encode in the instruction (Should be 32-bit aligned) + */ + explicit BL(i64 offset) { + this->offset = static_cast(offset / 4); + sig = 0x25; + } + + /** + * @brief Returns the offset of the instruction + * @return The offset encoded within the instruction + */ + inline i32 Offset() { + return offset * 4; + } + + /** + * @brief Returns if the opcode is valid or not + * @return If the opcode represents a valid Branch Linked instruction + */ + inline bool Verify() { + return (sig == 0x85); + } + + union { + struct { + i32 offset : 26; + u8 sig : 6; + }; + u32 raw{}; + }; + }; + static_assert(sizeof(BL) == sizeof(u32)); + + /** + * @brief A bit-field struct that encapsulates a MOVZ instruction. See https://developer.arm.com/docs/ddi0596/e/base-instructions-alphabetic-order/movz-move-wide-with-zero. + */ + struct Movz { + public: + /** + * @brief Creates a MOVZ instruction + * @param destReg The destination Xn register to store the value in + * @param imm16 The 16-bit value to store + * @param shift The offset (in bits and 16-bit aligned) in the register to store the value at + */ + Movz(regs::X destReg, u16 imm16, u8 shift = 0) { + this->destReg = static_cast(destReg); + this->imm16 = imm16; + hw = static_cast(shift / 16); + sig = 0xA5; + sf = 1; + } + + /** + * @brief Creates a MOVZ instruction + * @param destReg The destination Wn register to store the value in + * @param imm16 The 16-bit value to store + * @param shift The offset (in bits and 16-bit aligned) in the register to store the value at + */ + Movz(regs::W destReg, u16 imm16, u8 shift = 0) { + this->destReg = static_cast(destReg); + this->imm16 = imm16; + hw = static_cast(shift / 16); + sig = 0xA5; + sf = 0; + } + + /** + * @brief Returns the offset of the instruction + * @return The offset encoded within the instruction + */ + inline u8 Shift() { + return static_cast(hw * 16); + } + + /** + * @brief Returns if the opcode is valid or not + * @return If the opcode represents a valid MOVZ instruction + */ + inline bool Verify() { + return (sig == 0xA5); + } + + union { + struct __attribute__((packed)) { + u8 destReg : 5; + u16 imm16 : 16; + u8 hw : 2; + u8 sig : 8; + u8 sf : 1; + }; + u32 raw{}; + }; + }; + static_assert(sizeof(Movz) == sizeof(u32)); + + /** + * @brief A bit-field struct that encapsulates a MOVK instruction. See https://developer.arm.com/docs/ddi0596/e/base-instructions-alphabetic-order/movk-move-wide-with-keep. + */ + struct Movk { + public: + /** + * @brief Creates a MOVK instruction + * @param destReg The destination Xn register to store the value in + * @param imm16 The 16-bit value to store + * @param shift The offset (in bits and 16-bit aligned) in the register to store the value at + */ + Movk(regs::X destReg, u16 imm16, u8 shift = 0) { + this->destReg = static_cast(destReg); + this->imm16 = imm16; + hw = static_cast(shift / 16); + sig = 0xE5; + sf = 1; + } + + /** + * @brief Creates a MOVK instruction + * @param destReg The destination Wn register to store the value in + * @param imm16 The 16-bit value to store + * @param shift The offset (in bits and 16-bit aligned) in the register to store the value at + */ + Movk(regs::W destReg, u16 imm16, u8 shift = 0) { + this->destReg = static_cast(destReg); + this->imm16 = imm16; + hw = static_cast(shift / 16); + sig = 0xE5; + sf = 0; + } + + /** + * @brief Returns the offset of the instruction + * @return The offset encoded within the instruction + */ + inline u8 Shift() { + return static_cast(hw * 16); + } + + /** + * @brief Returns if the opcode is valid or not + * @return If the opcode represents a valid MOVK instruction + */ + inline bool Verify() { + return (sig == 0xE5); + } + + union { + struct __attribute__((packed)) { + u8 destReg : 5; + u16 imm16 : 16; + u8 hw : 2; + u8 sig : 8; + u8 sf : 1; + }; + u32 raw{}; + }; + }; + static_assert(sizeof(Movk) == sizeof(u32)); + + const std::array MoveU64Reg(regs::X destReg, u64 value) { + union { + u64 val; + struct { + u16 v0; + u16 v16; + u16 v32; + u16 v64; + }; + } val; + val.val = value; + std::array instr; + instr::Movz mov0(destReg, val.v0, 0); + instr[0] = mov0.raw; + instr::Movk mov16(destReg, val.v16, 16); + instr[1] = mov16.raw; + instr::Movk mov32(destReg, val.v32, 32); + instr[2] = mov32.raw; + instr::Movk mov64(destReg, val.v64, 48); + instr[3] = mov64.raw; + return instr; + } + + const std::array MoveU32Reg(regs::X destReg, u32 value) { + union { + u32 val; + struct { + u16 v0; + u16 v16; + }; + } val; + val.val = value; + std::array instr; + instr::Movz mov0(destReg, val.v0, 0); + instr[0] = mov0.raw; + instr::Movk mov16(destReg, val.v16, 16); + instr[1] = mov16.raw; + return instr; + } + + /** + * @brief A bit-field struct that encapsulates a MOV (Register) instruction. See https://developer.arm.com/docs/ddi0596/e/base-instructions-alphabetic-order/mov-register-move-register-an-alias-of-orr-shifted-register. + */ + struct Mov { + public: + /** + * @brief Creates a MOV (Register) instruction + * @param destReg The destination Xn register to store the value in + * @param srcReg The source Xn register to retrieve the value from + */ + Mov(regs::X destReg, regs::X srcReg) { + this->destReg = static_cast(destReg); + zeroReg = 0x1F; + imm6 = 0; + this->srcReg = static_cast(srcReg); + sig = 0x150; + sf = 1; + } + + /** + * @brief Creates a MOV instruction + * @param destReg The destination Wn register to store the value in + * @param srcReg The source Wn register to retrieve the value from + */ + Mov(regs::W destReg, regs::W srcReg) { + this->destReg = static_cast(destReg); + zeroReg = 0x1F; + imm6 = 0; + this->srcReg = static_cast(srcReg); + sig = 0x150; + sf = 0; + } + + /** + * @brief Returns if the opcode is valid or not + * @return If the opcode represents a valid MOVZ instruction + */ + inline bool Verify() { + return (sig == 0x150); + } + + union { + struct __attribute__((packed)) { + u8 destReg : 5; + u8 zeroReg : 5; + u8 imm6 : 6; + u8 srcReg : 5; + u16 sig : 10; + u8 sf : 1; + }; + u32 raw{}; + }; + }; + static_assert(sizeof(Mov) == sizeof(u32)); + } +} diff --git a/app/src/main/cpp/skyline/os.cpp b/app/src/main/cpp/skyline/os.cpp index bafac64f..de8a7638 100644 --- a/app/src/main/cpp/skyline/os.cpp +++ b/app/src/main/cpp/skyline/os.cpp @@ -1,42 +1,39 @@ #include "os.h" -#include "kernel/svc.h" #include "loader/nro.h" +#include "nce/guest.h" namespace skyline::kernel { - OS::OS(std::shared_ptr& jvmManager, std::shared_ptr &logger, std::shared_ptr &settings) : state(this, process, thisThread, jvmManager, settings, logger), serviceManager(state) {} + OS::OS(std::shared_ptr &jvmManager, std::shared_ptr &logger, std::shared_ptr &settings) : state(this, process, jvmManager, settings, logger), serviceManager(state) {} void OS::Execute(const int romFd, const TitleFormat romType) { - auto process = CreateProcess(constant::BaseAddr, constant::DefStackSize); + std::shared_ptr loader; if (romType == TitleFormat::NRO) { - loader::NroLoader loader(romFd); - loader.LoadProcessData(process, state); + loader = std::make_shared(romFd); } else throw exception("Unsupported ROM extension."); + auto process = CreateProcess(loader->mainEntry, 0, constant::DefStackSize); + loader->LoadProcessData(process, state); process->threadMap.at(process->pid)->Start(); // The kernel itself is responsible for starting the main thread state.nce->Execute(); } - /** - * Function executed by all child processes after cloning - */ - int ExecuteChild(void *) { - ptrace(PTRACE_TRACEME); - asm volatile("Brk #0xFF"); // BRK #constant::brkRdy (So we know when the thread/process is ready) - return 0; - } - - std::shared_ptr OS::CreateProcess(u64 address, size_t stackSize) { + std::shared_ptr OS::CreateProcess(u64 entry, u64 argument, size_t stackSize) { + madvise(reinterpret_cast(constant::BaseAddr), constant::BaseEnd, MADV_DONTFORK); auto *stack = static_cast(mmap(nullptr, stackSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS | MAP_STACK, -1, 0)); // NOLINT(hicpp-signed-bitwise) + madvise(stack, reinterpret_cast(stack) + stackSize, MADV_DOFORK); if (stack == MAP_FAILED) throw exception("Failed to allocate stack memory"); if (mprotect(stack, PAGE_SIZE, PROT_NONE)) { munmap(stack, stackSize); throw exception("Failed to create guard pages"); } - pid_t pid = clone(&ExecuteChild, stack + stackSize, CLONE_FILES | CLONE_FS | SIGCHLD, nullptr); // NOLINT(hicpp-signed-bitwise) + auto tlsMem = std::make_shared(state, 0, (sizeof(ThreadContext) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1), memory::Permission(true, true, 0), memory::Type::Reserved); // NOLINT(hicpp-signed-bitwise) + tlsMem->guest = tlsMem->kernel; + madvise(reinterpret_cast(tlsMem->guest.address), tlsMem->guest.size, MADV_DOFORK); + pid_t pid = clone(reinterpret_cast(&guest::entry), stack + stackSize, CLONE_FILES | CLONE_FS | CLONE_SETTLS | SIGCHLD, reinterpret_cast(entry), nullptr, reinterpret_cast(tlsMem->guest.address)); // NOLINT(hicpp-signed-bitwise) if (pid == -1) throw exception("Call to clone() has failed: {}", strerror(errno)); - process = std::make_shared(state, pid, address, reinterpret_cast(stack), stackSize); + process = std::make_shared(state, pid, argument, reinterpret_cast(stack), stackSize, tlsMem); state.logger->Debug("Successfully created process with PID: {}", pid); return process; } @@ -54,16 +51,4 @@ namespace skyline::kernel { process->threadMap.erase(pid); } } - - void OS::SvcHandler(const u16 svc) { - try { - if (svc::SvcTable[svc]) { - state.logger->Debug("SVC called 0x{:X}", svc); - (*svc::SvcTable[svc])(state); - } else - throw exception("Unimplemented SVC 0x{:X}", svc); - } catch(const exception& e) { - throw exception("{} (SVC: 0x{:X})", e.what(), svc); - } - } } diff --git a/app/src/main/cpp/skyline/os.h b/app/src/main/cpp/skyline/os.h index 61a44a4a..2e1a49e3 100644 --- a/app/src/main/cpp/skyline/os.h +++ b/app/src/main/cpp/skyline/os.h @@ -7,7 +7,6 @@ #include "kernel/types/KProcess.h" #include "kernel/types/KThread.h" #include "services/serviceman.h" -#include "nce.h" #include "gpu.h" namespace skyline::kernel { @@ -20,7 +19,6 @@ namespace skyline::kernel { public: std::shared_ptr process; //!< The KProcess object for the emulator, representing the guest process - std::shared_ptr thisThread; //!< The corresponding KThread object of the thread that's called an SVC service::ServiceManager serviceManager; //!< This manages all of the service functions /** @@ -39,11 +37,12 @@ namespace skyline::kernel { /** * @brief Creates a new process - * @param address The address of the initial function + * @param entry The entry point for the new process + * @param argument The argument for the initial function * @param stackSize The size of the main stack * @return An instance of the KProcess of the created process */ - std::shared_ptr CreateProcess(u64 address, size_t stackSize); + std::shared_ptr CreateProcess(u64 entry, u64 argument, size_t stackSize); /** * @brief Kill a particular thread