Move from ptrace to junction branching and make kernel multithreaded

This commit is a huge step in the direction of better performance, as we move from ptrace to junction branching and have kernel call overhead similar to that of a native kernel call! In addition, this sets the base for the kernel to go fully multi-threaded. However, the kernel is currently not thread-safe and therefore this commit currently causes a crash.
This commit is contained in:
◱ PixelyIon 2020-01-07 08:06:08 +05:30 committed by ◱ PixelyIon
parent b84859d352
commit 970dde8c27
28 changed files with 1296 additions and 822 deletions

View File

@ -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

View File

@ -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<kernel::type::KProcess> &thisProcess, std::shared_ptr<kernel::type::KThread> &thisThread, std::shared_ptr<JvmManager> jvmManager, std::shared_ptr<Settings> settings, std::shared_ptr<Logger> 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<kernel::type::KProcess> &process, std::shared_ptr<JvmManager> jvmManager, std::shared_ptr<Settings> settings, std::shared_ptr<Logger> 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<NCE>(*this));
gpu = std::move(std::make_shared<gpu::GPU>(*this));
}
thread_local std::shared_ptr<kernel::type::KThread> DeviceState::thread = 0;
thread_local ThreadContext* DeviceState::ctx = 0;
}

View File

@ -11,24 +11,13 @@
#include <memory>
#include <fmt/format.h>
#include <sys/mman.h>
#include <sys/ptrace.h>
#include <cstdint>
#include <stdexcept>
#include <string>
#include <android/native_window.h>
#include <jni.h>
#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<bool> 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<kernel::type::KProcess> &thisProcess, std::shared_ptr<kernel::type::KThread> &thisThread, std::shared_ptr<JvmManager> jvmManager, std::shared_ptr<Settings> settings, std::shared_ptr<Logger> logger);
DeviceState(kernel::OS *os, std::shared_ptr<kernel::type::KProcess> &process, std::shared_ptr<JvmManager> jvmManager, std::shared_ptr<Settings> settings, std::shared_ptr<Logger> logger);
kernel::OS *os; //!< This holds a reference to the OS class
std::shared_ptr<kernel::type::KProcess> &process; //!< This holds a reference to the process object
std::shared_ptr<kernel::type::KThread> &thread; //!< This holds a reference to the current thread object
thread_local static std::shared_ptr<kernel::type::KThread> 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> nce; //!< This holds a reference to the NCE class
std::shared_ptr<gpu::GPU> gpu; //!< This holds a reference to the GPU class
std::shared_ptr<JvmManager> jvmManager; //!< This holds a reference to the JvmManager class

View File

@ -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();

View File

@ -4,6 +4,7 @@
#include "kernel/types/KEvent.h"
#include "gpu/display.h"
#include "gpu/devices/nvdevice.h"
#include <android/native_window.h>
namespace skyline::gpu {
/**

View File

@ -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

View File

@ -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<i32>(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<i32>(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));
}
}

View File

@ -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<bool>(memInfo.memoryAttribute.isUncached), memInfo.r ? "R" : "-", memInfo.w ? "W" : "-", memInfo.x ? "X" : "-");
state.process->WriteMemory<memory::MemoryInfo>(memInfo, state.nce->GetRegister(Xreg::X0));
state.nce->SetRegister(Wreg::W0, constant::status::Success);
state.process->WriteMemory<memory::MemoryInfo>(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<u8>(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<u8>(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<type::KThread>(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<type::KThread>(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<type::KThread>(handle)->UpdatePriority(static_cast<u8>(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<type::KSharedMemory>(state.nce->GetRegister(Wreg::W0));
u64 addr = state.nce->GetRegister(Xreg::X1);
auto object = state.process->GetHandle<type::KSharedMemory>(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<memory::Permission *>(&perm);
if ((permission.w && !permission.r) || (permission.x && !permission.r)) {
state.logger->Warn("svcMapSharedMemory: 'permission' invalid: {}{}{}", permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-");
state.nce->SetRegister(Wreg::W0, constant::status::InvPermission);
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<memory::Permission *>(&perm);
if ((permission.w && !permission.r) || (permission.x && !permission.r)) {
state.logger->Warn("svcCreateTransferMemory: 'permission' invalid: {}{}{}", permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-");
state.nce->SetRegister(Wreg::W0, constant::status::InvPermission);
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<type::KTransferMemory>(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<handle_t>(state.nce->GetRegister(Wreg::W0));
auto handle = static_cast<handle_t>(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<handle_t> 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<type::KSyncObject>(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<u64>::max())
if (state.ctx->registers.x3 != std::numeric_limits<u64>::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<handle_t>(state.nce->GetRegister(Xreg::X0)));
state.nce->SetRegister(Wreg::W0, constant::status::Success);
state.os->serviceManager.SyncRequestHandler(static_cast<handle_t>(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<type::KThread>(handle)->pid;
} else
pid = state.thread->pid;
state.logger->Debug("svcGetThreadId: Handle: 0x{:X}, PID: {}", handle, pid);
state.nce->SetRegister(Xreg::X1, static_cast<u64>(pid));
state.nce->SetRegister(Wreg::W0, constant::status::Success);
state.ctx->registers.x1 = static_cast<u64>(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;
}
}

View File

@ -1,28 +1,22 @@
#include "KPrivateMemory.h"
#include "KProcess.h"
#include <nce.h>
#include <asm/unistd.h>
namespace skyline::kernel::type {
u64 MapPrivateFunc(u64 dstAddress, u64 srcAddress, size_t size, u64 perms) {
dstAddress = reinterpret_cast<u64>(mmap(reinterpret_cast<void *>(dstAddress), size, static_cast<int>(perms), MAP_PRIVATE | MAP_ANONYMOUS | ((dstAddress) ? MAP_FIXED : 0), -1, 0)); // NOLINT(hicpp-signed-bitwise)
if (srcAddress) {
memcpy(reinterpret_cast<void *>(dstAddress), reinterpret_cast<const void *>(srcAddress), size);
mprotect(reinterpret_cast<void *>(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<u64>(permission.Get());
state.nce->ExecuteFunction(reinterpret_cast<void *>(MapPrivateFunc), fregs, pid ? pid : state.process->pid);
if (reinterpret_cast<void *>(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<KThread> 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<u64>(permission.Get());
fregs.x3 = static_cast<u64>(MAP_PRIVATE | MAP_ANONYMOUS | ((dstAddress) ? MAP_FIXED : 0)); // NOLINT(hicpp-signed-bitwise)
fregs.x4 = static_cast<u64>(-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<void *>(RemapPrivateFunc), fregs, state.process->pid);
if (reinterpret_cast<void *>(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<u64>(permission.Get());
state.nce->ExecuteFunction(reinterpret_cast<void *>(UpdatePermissionPrivateFunc), fregs, state.process->pid);
if (static_cast<int>(fregs.regs[0]) == -1)
Registers fregs{};
fregs.x0 = address;
fregs.x1 = size;
fregs.x2 = static_cast<u64>(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<void *>(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 &) {}
}
};

View File

@ -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<KThread> thread = 0);
/**
* @brief Remap a chunk of memory as to change the size occupied by it

View File

@ -3,6 +3,7 @@
#include <os.h>
#include <fcntl.h>
#include <unistd.h>
#include <asm/unistd.h>
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<KPrivateMemory>(0, 0, PAGE_SIZE, memory::Permission(true, true, false), memory::Type::ThreadLocal, pid).item;
auto tlsMem = NewHandle<KPrivateMemory>(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<TlsPage>(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<KThread>(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<type::KSharedMemory>& tlsMemory) : pid(pid), mainThreadStackSz(stackSize), KSyncObject(state, KType::KProcess) {
auto thread = NewHandle<KThread>(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<KThread> 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<pid_t>(fregs.regs[0]);
if (pid == -1)
throw exception("Cannot create thread: Address: 0x{:X}, Stack Top: 0x{:X}", entryPoint, stackTop);
auto process = NewHandle<KThread>(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<KPrivateMemory> KProcess::MapPrivateRegion(u64 address, size_t size, const memory::Permission perms, const memory::Type type, const memory::Region region) {
auto mem = NewHandle<KPrivateMemory>(address, 0, size, perms, type, pid);
auto mem = NewHandle<KPrivateMemory>(address, size, perms, type, threadMap.at(pid));
memoryMap[mem.item->address] = mem.item;
memoryRegionMap[region] = mem.item;
return mem;

View File

@ -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<type::KSharedMemory>& tlsMemory);
/**
* Close the file descriptor to the process's memory

View File

@ -3,6 +3,7 @@
#include <nce.h>
#include <android/sharedmem.h>
#include <unistd.h>
#include <asm/unistd.h>
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<u64>(permission.Get()), static_cast<u64>(fd));
address = reinterpret_cast<u64>(mmap(reinterpret_cast<void *>(address), size, permission.Get(), MAP_SHARED | ((address) ? MAP_FIXED : 0), static_cast<int>(fd), 0));
if (address == reinterpret_cast<u64>(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<u64>(permission.Get());
fregs.regs[3] = static_cast<u64>(fd);
state.nce->ExecuteFunction(reinterpret_cast<void *>(MapSharedFunc), fregs, state.process->pid);
if (reinterpret_cast<void *>(fregs.regs[0]) == MAP_FAILED)
Registers fregs{};
fregs.x0 = address;
fregs.x1 = size;
fregs.x2 = static_cast<u64>(permission.Get());
fregs.x3 = static_cast<u64>(MAP_SHARED | ((address) ? MAP_FIXED : 0)); // NOLINT(hicpp-signed-bitwise)
fregs.x4 = static_cast<u64>(fd);
fregs.x4 = static_cast<u64>(-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<void *>(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<void *>(RemapSharedFunc), fregs, state.process->pid);
if (reinterpret_cast<void *>(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<u64>(MAP_FAILED))
if (mremap(reinterpret_cast<void *>(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<u64>(guest.permission.Get());
state.nce->ExecuteFunction(reinterpret_cast<void *>(UpdatePermissionSharedFunc), fregs, state.process->pid);
if (static_cast<int>(fregs.regs[0]) == -1)
Registers fregs{};
fregs.x0 = guest.address;
fregs.x1 = guest.size;
fregs.x2 = static_cast<u64>(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<u64>(permission.Get())) == reinterpret_cast<u64>(MAP_FAILED))
if (mprotect(reinterpret_cast<void *>(kernel.address), kernel.size, permission.Get()) == reinterpret_cast<u64>(MAP_FAILED))
throw exception("An occurred while remapping shared region: {}", strerror(errno));
kernel.permission = permission;
}

View File

@ -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();

View File

@ -4,7 +4,8 @@
#include <nce.h>
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<type::KSharedMemory>& 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));
}
}

View File

@ -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<type::KSharedMemory> ctxMemory; //!< The KSharedMemory of the shared memory allocated by the guest process TLS
std::vector<std::shared_ptr<KSyncObject>> 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<type::KSharedMemory>& tlsMemory);
/**
* @brief Kills the thread and deallocates the memory allocated for stack.

View File

@ -1,6 +1,7 @@
#include "KTransferMemory.h"
#include <nce.h>
#include <os.h>
#include <asm/unistd.h>
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<u64 >(permission.Get());
state.nce->ExecuteFunction(reinterpret_cast<void *>(MapTransferFunc), fregs, pid);
if (reinterpret_cast<void *>(fregs.regs[0]) == MAP_FAILED)
Registers fregs{};
fregs.x0 = address;
fregs.x1 = size;
fregs.x2 = static_cast<u64 >(permission.Get());
fregs.x3 = static_cast<u64>(MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0)); // NOLINT(hicpp-signed-bitwise)
fregs.x4 = static_cast<u64>(-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<u64>(permission.Get()));
if (reinterpret_cast<void *>(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<u64 >(permission.Get());
state.nce->ExecuteFunction(reinterpret_cast<void *>(MapTransferFunc), fregs, process);
if (reinterpret_cast<void *>(fregs.regs[0]) == MAP_FAILED)
Registers fregs{};
fregs.x0 = address;
fregs.x1 = size;
fregs.x2 = static_cast<u64 >(permission.Get());
fregs.x3 = static_cast<u64>(MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0)); // NOLINT(hicpp-signed-bitwise)
fregs.x4 = static_cast<u64>(-1);
fregs.x8 = __NR_mmap;
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, process);
// state.nce->ExecuteFunction(reinterpret_cast<void *>(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<u64>(permission.Get()));
if (reinterpret_cast<void *>(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<u64 >(permission.Get());
state.nce->ExecuteFunction(reinterpret_cast<void *>(UnmapTransferFunc), fregs, owner);
if (reinterpret_cast<void *>(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<void *>(UnmapTransferFunc), fregs, owner);
if (fregs.x0 < 0)
throw exception("An error occurred while unmapping transfer memory in child process");
} else {
if (reinterpret_cast<void *>(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<void *>(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);
}

View File

@ -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
*/

View File

@ -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<kernel::type::KProcess> 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<u32> patch = state.nce->PatchCode(text, header.text.size + header.ro.size + header.data.size + header.bssSize);
std::vector<u32> 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();

View File

@ -1,237 +1,184 @@
#include <sched.h>
#include <linux/uio.h>
#include <linux/elf.h>
#include <mutex>
#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 &registers, pid_t pid) const {
iovec iov = {&registers, 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<ThreadContext *>(state.thread->ctxMemory->guest.address);
while (!Halt) {
if (state.ctx->state == ThreadState::WaitKernel) {
const u16 svc = static_cast<const u16>(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 &registers, pid_t pid) const {
iovec iov = {&registers, 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<instr::Brk *>(&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<u16>(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<Xreg>(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<u64>(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<kernel::type::KThread>& thread) {
auto ctx = reinterpret_cast<ThreadContext *>(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<u32>(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<u64>(func);
funcRegs.sp = backupRegs.sp;
funcRegs.regs[static_cast<uint>(Xreg::X30)] = reinterpret_cast<u64>(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<ThreadContext *>(state.process->threadMap.at(pid)->ctxMemory->kernel.address);
ctx->commandId = static_cast<u32>(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<u64>(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<kernel::type::KThread>& thread) {
auto ctx = reinterpret_cast<ThreadContext *>(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<kernel::type::KThread> &thread) {
auto ctx = reinterpret_cast<ThreadContext *>(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<std::thread>(&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<u32>(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<u32> 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<uint>(regId)];
}
const std::array<u32, 18> 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<uint>(regId)] = value;
}
const std::array<u32, 18> 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<u32 *>(&registerMap.at(pid ? pid : currPid).regs))[static_cast<uint>(regId) * 2];
}
const std::array<u32, 18> 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<u32 *>(&registerMap.at(pid ? pid : currPid).regs))[static_cast<uint>(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<u32> NCE::PatchCode(std::vector<u8>& code, i64 offset) {
u32 *address = reinterpret_cast<u32 *>(code.data());
u32 *end = address + (code.size() / sizeof(u32));
std::vector<u32> NCE::PatchCode(std::vector<u8> &code, u64 baseAddress, i64 offset) {
u32 *start = reinterpret_cast<u32 *>(code.data());
u32 *end = start + (code.size() / sizeof(u32));
i64 patchOffset = offset;
std::vector<u32> patch((guest::saveCtxSize + guest::loadCtxSize) / sizeof(u32));
std::vector<u32> patch((guest::saveCtxSize + guest::loadCtxSize + guest::svcHandlerSize) / sizeof(u32));
std::memcpy(patch.data(), reinterpret_cast<void*>(&guest::saveCtx), guest::saveCtxSize);
offset += guest::saveCtxSize;
@ -239,18 +186,29 @@ namespace skyline {
reinterpret_cast<void*>(&guest::loadCtx), guest::loadCtxSize);
offset += guest::loadCtxSize;
while (address < end) {
std::memcpy(reinterpret_cast<u8*>(patch.data()) + guest::saveCtxSize + guest::loadCtxSize,
reinterpret_cast<void*>(&guest::svcHandler), guest::svcHandlerSize);
offset += guest::svcHandlerSize;
for (u32* address = start;address < end;address++) {
auto instrSvc = reinterpret_cast<instr::Svc *>(address);
auto instrMrs = reinterpret_cast<instr::Mrs *>(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<u16>(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<u16>(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<u16>(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<regs::X>(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);
}

View File

@ -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<pid_t, user_pt_regs> 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<pid_t, std::shared_ptr<std::thread>> 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 &registers, 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 &registers, 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<kernel::type::KThread>& 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<kernel::type::KThread>& 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<kernel::type::KThread> &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<u32> PatchCode(std::vector<u8> &code, i64 offset);
std::vector<u32> PatchCode(std::vector<u8> &code, u64 baseAddress, i64 offset);
};
}

View File

@ -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

View File

@ -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<u32>(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();
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,149 @@
#pragma once
#include <cstdint>
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
};
}

View File

@ -0,0 +1,400 @@
#include <common.h>
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<i32>(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<i32>(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<u8>(destReg);
this->imm16 = imm16;
hw = static_cast<u8>(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<u8>(destReg);
this->imm16 = imm16;
hw = static_cast<u8>(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<u8>(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<u8>(destReg);
this->imm16 = imm16;
hw = static_cast<u8>(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<u8>(destReg);
this->imm16 = imm16;
hw = static_cast<u8>(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<u8>(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<u32, 4> MoveU64Reg(regs::X destReg, u64 value) {
union {
u64 val;
struct {
u16 v0;
u16 v16;
u16 v32;
u16 v64;
};
} val;
val.val = value;
std::array<u32, 4> 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<u32, 2> MoveU32Reg(regs::X destReg, u32 value) {
union {
u32 val;
struct {
u16 v0;
u16 v16;
};
} val;
val.val = value;
std::array<u32, 2> 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<u8>(destReg);
zeroReg = 0x1F;
imm6 = 0;
this->srcReg = static_cast<u8>(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<u8>(destReg);
zeroReg = 0x1F;
imm6 = 0;
this->srcReg = static_cast<u8>(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));
}
}

View File

@ -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>& jvmManager, std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings) : state(this, process, thisThread, jvmManager, settings, logger), serviceManager(state) {}
OS::OS(std::shared_ptr<JvmManager> &jvmManager, std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &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::Loader> loader;
if (romType == TitleFormat::NRO) {
loader::NroLoader loader(romFd);
loader.LoadProcessData(process, state);
loader = std::make_shared<loader::NroLoader>(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<type::KProcess> OS::CreateProcess(u64 address, size_t stackSize) {
std::shared_ptr<type::KProcess> OS::CreateProcess(u64 entry, u64 argument, size_t stackSize) {
madvise(reinterpret_cast<void *>(constant::BaseAddr), constant::BaseEnd, MADV_DONTFORK);
auto *stack = static_cast<u8 *>(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<size_t>(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<type::KSharedMemory>(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<void *>(tlsMem->guest.address), tlsMem->guest.size, MADV_DOFORK);
pid_t pid = clone(reinterpret_cast<int (*)(void *)>(&guest::entry), stack + stackSize, CLONE_FILES | CLONE_FS | CLONE_SETTLS | SIGCHLD, reinterpret_cast<void *>(entry), nullptr, reinterpret_cast<void*>(tlsMem->guest.address)); // NOLINT(hicpp-signed-bitwise)
if (pid == -1)
throw exception("Call to clone() has failed: {}", strerror(errno));
process = std::make_shared<kernel::type::KProcess>(state, pid, address, reinterpret_cast<u64>(stack), stackSize);
process = std::make_shared<kernel::type::KProcess>(state, pid, argument, reinterpret_cast<u64>(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);
}
}
}

View File

@ -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<type::KProcess> process; //!< The KProcess object for the emulator, representing the guest process
std::shared_ptr<type::KThread> 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<type::KProcess> CreateProcess(u64 address, size_t stackSize);
std::shared_ptr<type::KProcess> CreateProcess(u64 entry, u64 argument, size_t stackSize);
/**
* @brief Kill a particular thread