Rework NCE/KThread/KProcess + Remove Guest Process

This commit is contained in:
◱ PixelyIon 2020-10-07 21:11:13 +05:30 committed by ◱ PixelyIon
parent 878cb24389
commit 90127740f0
29 changed files with 434 additions and 609 deletions

View File

@ -50,7 +50,6 @@ add_library(skyline SHARED
${source_DIR}/skyline/gpu/syncpoint.cpp ${source_DIR}/skyline/gpu/syncpoint.cpp
${source_DIR}/skyline/gpu/texture.cpp ${source_DIR}/skyline/gpu/texture.cpp
${source_DIR}/skyline/gpu/engines/maxwell_3d.cpp ${source_DIR}/skyline/gpu/engines/maxwell_3d.cpp
${source_DIR}/skyline/input.cpp
${source_DIR}/skyline/input/npad.cpp ${source_DIR}/skyline/input/npad.cpp
${source_DIR}/skyline/input/npad_device.cpp ${source_DIR}/skyline/input/npad_device.cpp
${source_DIR}/skyline/input/touch.cpp ${source_DIR}/skyline/input/touch.cpp

View File

@ -28,12 +28,14 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
fps = 0; fps = 0;
frametime = 0; frametime = 0;
/*
std::signal(SIGTERM, signalHandler); std::signal(SIGTERM, signalHandler);
std::signal(SIGSEGV, signalHandler); std::signal(SIGSEGV, signalHandler);
std::signal(SIGINT, signalHandler); std::signal(SIGINT, signalHandler);
std::signal(SIGILL, signalHandler); std::signal(SIGILL, signalHandler);
std::signal(SIGABRT, signalHandler); std::signal(SIGABRT, signalHandler);
std::signal(SIGFPE, signalHandler); std::signal(SIGFPE, signalHandler);
*/
setpriority(PRIO_PROCESS, static_cast<id_t>(gettid()), -8); // Set the priority of this process to the highest value setpriority(PRIO_PROCESS, static_cast<id_t>(gettid()), -8); // Set the priority of this process to the highest value

View File

@ -58,7 +58,7 @@ namespace skyline {
namespace constant { namespace constant {
// Memory // Memory
constexpr u64 BaseAddress{0x8000000}; //!< The address space base constexpr u64 BaseAddress{0x8000000}; //!< The address space base
constexpr u64 DefStackSize{0x1E8480}; //!< The default amount of stack: 2 MB constexpr u64 DefaultStackSize{0x1E8480}; //!< The default amount of stack: 2 MB
// Display // Display
constexpr u16 HandheldResolutionW{1280}; //!< The width component of the handheld resolution constexpr u16 HandheldResolutionW{1280}; //!< The width component of the handheld resolution
constexpr u16 HandheldResolutionH{720}; //!< The height component of the handheld resolution constexpr u16 HandheldResolutionH{720}; //!< The height component of the handheld resolution
@ -71,16 +71,18 @@ namespace skyline {
namespace util { namespace util {
/** /**
* @brief A way to implicitly cast all typed pointers to void pointers, this is used for libfmt as it requires wrapping non-void pointers with fmt::ptr * @brief A way to implicitly cast all typed pointers to void pointers, this is used for libfmt as it requires wrapping non-void pointers with fmt::ptr
* @note There's the exception of signed char pointers as they represent C Strings
* @note This does not cover std::shared_ptr or std::unique_ptr and those will have to be explicitly passed through fmt::ptr * @note This does not cover std::shared_ptr or std::unique_ptr and those will have to be explicitly passed through fmt::ptr
*/ */
template<class T> template<class T>
constexpr T VoidCast(T item) { constexpr auto FmtCast(T object) {
return item; if constexpr (std::is_pointer<T>::value)
} if constexpr (std::is_same<char, typename std::remove_cv<typename std::remove_pointer<T>::type>::type>::value)
return reinterpret_cast<typename std::common_type<char *, T>::type>(object);
template <class T> else
constexpr const void* VoidCast(T* ptr) { return reinterpret_cast<const void *>(object);
return ptr; else
return object;
} }
} }
@ -94,7 +96,7 @@ namespace skyline {
* @param args The arguments based on format_str * @param args The arguments based on format_str
*/ */
template<typename S, typename... Args> template<typename S, typename... Args>
inline exception(const S &formatStr, Args &&... args) : runtime_error(fmt::format(formatStr, util::VoidCast(args)...)) {} inline exception(const S &formatStr, Args &&... args) : runtime_error(fmt::format(formatStr, util::FmtCast(args)...)) {}
}; };
namespace util { namespace util {
@ -444,7 +446,7 @@ namespace skyline {
template<typename S, typename... Args> template<typename S, typename... Args>
inline void Error(const S &formatStr, Args &&... args) { inline void Error(const S &formatStr, Args &&... args) {
if (LogLevel::Error <= configLevel) { if (LogLevel::Error <= configLevel) {
Write(LogLevel::Error, fmt::format(formatStr, util::VoidCast(args)...)); Write(LogLevel::Error, fmt::format(formatStr, util::FmtCast(args)...));
} }
} }
@ -456,7 +458,7 @@ namespace skyline {
template<typename S, typename... Args> template<typename S, typename... Args>
inline void Warn(const S &formatStr, Args &&... args) { inline void Warn(const S &formatStr, Args &&... args) {
if (LogLevel::Warn <= configLevel) { if (LogLevel::Warn <= configLevel) {
Write(LogLevel::Warn, fmt::format(formatStr, util::VoidCast(args)...)); Write(LogLevel::Warn, fmt::format(formatStr, util::FmtCast(args)...));
} }
} }
@ -468,7 +470,7 @@ namespace skyline {
template<typename S, typename... Args> template<typename S, typename... Args>
inline void Info(const S &formatStr, Args &&... args) { inline void Info(const S &formatStr, Args &&... args) {
if (LogLevel::Info <= configLevel) { if (LogLevel::Info <= configLevel) {
Write(LogLevel::Info, fmt::format(formatStr, util::VoidCast(args)...)); Write(LogLevel::Info, fmt::format(formatStr, util::FmtCast(args)...));
} }
} }
@ -480,7 +482,7 @@ namespace skyline {
template<typename S, typename... Args> template<typename S, typename... Args>
inline void Debug(const S &formatStr, Args &&... args) { inline void Debug(const S &formatStr, Args &&... args) {
if (LogLevel::Debug <= configLevel) { if (LogLevel::Debug <= configLevel) {
Write(LogLevel::Debug, fmt::format(formatStr, util::VoidCast(args)...)); Write(LogLevel::Debug, fmt::format(formatStr, util::FmtCast(args)...));
} }
} }
}; };
@ -527,7 +529,6 @@ namespace skyline {
void List(const std::shared_ptr<Logger> &logger); void List(const std::shared_ptr<Logger> &logger);
}; };
class NCE;
class JvmManager; class JvmManager;
namespace gpu { namespace gpu {
class GPU; class GPU;

View File

@ -1,8 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include "input.h"
namespace skyline::input {
Input::Input(const DeviceState &state) : state(state), kHid(std::make_shared<kernel::type::KSharedMemory>(state, sizeof(HidSharedMemory))), hid(reinterpret_cast<HidSharedMemory *>(kHid->kernel.ptr)), npad(state, hid), touch(state, hid) {}
}

View File

@ -24,6 +24,6 @@ namespace skyline::input {
NpadManager npad; NpadManager npad;
TouchManager touch; TouchManager touch;
Input(const DeviceState &state); Input(const DeviceState &state) : state(state), kHid(std::make_shared<kernel::type::KSharedMemory>(state, sizeof(HidSharedMemory))), hid(reinterpret_cast<HidSharedMemory *>(kHid->kernel.ptr)), npad(state, hid), touch(state, hid) {}
}; };
} }

View File

@ -6,7 +6,7 @@
namespace skyline::kernel::ipc { namespace skyline::kernel::ipc {
IpcRequest::IpcRequest(bool isDomain, const DeviceState &state) : isDomain(isDomain) { IpcRequest::IpcRequest(bool isDomain, const DeviceState &state) : isDomain(isDomain) {
auto tls{state.thread->tls}; auto tls{state.ctx->tpidrroEl0};
u8 *pointer{tls}; u8 *pointer{tls};
header = reinterpret_cast<CommandHeader *>(pointer); header = reinterpret_cast<CommandHeader *>(pointer);
@ -64,7 +64,7 @@ namespace skyline::kernel::ipc {
pointer += sizeof(BufferDescriptorABW); pointer += sizeof(BufferDescriptorABW);
} }
auto offset{reinterpret_cast<u64>(pointer) - reinterpret_cast<u64>(tls)}; // We calculate the relative offset as the absolute one might differ auto offset{pointer - tls}; // We calculate the relative offset as the absolute one might differ
auto padding{util::AlignUp(offset, constant::IpcPaddingSum) - offset}; // Calculate the amount of padding at the front auto padding{util::AlignUp(offset, constant::IpcPaddingSum) - offset}; // Calculate the amount of padding at the front
pointer += padding; pointer += padding;
@ -129,7 +129,7 @@ namespace skyline::kernel::ipc {
IpcResponse::IpcResponse(const DeviceState &state) : state(state) {} IpcResponse::IpcResponse(const DeviceState &state) : state(state) {}
void IpcResponse::WriteResponse(bool isDomain) { void IpcResponse::WriteResponse(bool isDomain) {
auto tls{state.thread->tls}; auto tls{state.ctx->tpidrroEl0};
u8 *pointer{tls}; u8 *pointer{tls};
memset(tls, 0, constant::TlsIpcSize); memset(tls, 0, constant::TlsIpcSize);
@ -156,7 +156,7 @@ namespace skyline::kernel::ipc {
} }
} }
auto offset{reinterpret_cast<u64>(pointer) - reinterpret_cast<u64>(tls)}; // We calculate the relative offset as the absolute one might differ auto offset{pointer - tls}; // We calculate the relative offset as the absolute one might differ
auto padding{util::AlignUp(offset, constant::IpcPaddingSum) - offset}; // Calculate the amount of padding at the front auto padding{util::AlignUp(offset, constant::IpcPaddingSum) - offset}; // Calculate the amount of padding at the front
pointer += padding; pointer += padding;

View File

@ -7,8 +7,7 @@
namespace skyline::kernel { namespace skyline::kernel {
MemoryManager::MemoryManager(const DeviceState &state) : state(state) {} MemoryManager::MemoryManager(const DeviceState &state) : state(state) {}
void MemoryManager::InitializeRegions(u8* codeStart, u64 size, memory::AddressSpaceType type) { void MemoryManager::InitializeVmm(memory::AddressSpaceType type) {
u64 address{reinterpret_cast<u64>(codeStart)};
switch (type) { switch (type) {
case memory::AddressSpaceType::AddressSpace32Bit: case memory::AddressSpaceType::AddressSpace32Bit:
throw exception("32-bit address spaces are not supported"); throw exception("32-bit address spaces are not supported");
@ -18,6 +17,32 @@ namespace skyline::kernel {
addressSpace.size = 1UL << 36; addressSpace.size = 1UL << 36;
base.address = constant::BaseAddress; base.address = constant::BaseAddress;
base.size = 0xFF8000000; base.size = 0xFF8000000;
break;
}
case memory::AddressSpaceType::AddressSpace39Bit: {
addressSpace.address = 0;
addressSpace.size = 1UL << 39;
base.address = constant::BaseAddress;
base.size = 0x7FF8000000;
break;
}
default:
throw exception("VMM initialization with unknown address space");
}
chunks = {ChunkDescriptor{
.ptr = reinterpret_cast<u8 *>(base.address),
.size = base.size,
.state = memory::states::Unmapped,
}};
}
void MemoryManager::InitializeRegions(u8 *codeStart, u64 size) {
u64 address{reinterpret_cast<u64>(codeStart)};
switch (addressSpace.size) {
case 1UL << 36: {
code.address = base.address; code.address = base.address;
code.size = 0x78000000; code.size = 0x78000000;
if (code.address > address || (code.size - (address - code.address)) < size) if (code.address > address || (code.size - (address - code.address)) < size)
@ -33,11 +58,7 @@ namespace skyline::kernel {
break; break;
} }
case memory::AddressSpaceType::AddressSpace39Bit: { case 1UL << 39: {
addressSpace.address = 0;
addressSpace.size = 1UL << 39;
base.address = constant::BaseAddress;
base.size = 0x7FF8000000;
code.address = util::AlignDown(address, 0x200000); code.address = util::AlignDown(address, 0x200000);
code.size = util::AlignUp(address + size, 0x200000) - code.address; code.size = util::AlignUp(address + size, 0x200000) - code.address;
alias.address = code.address + code.size; alias.address = code.address + code.size;
@ -50,13 +71,10 @@ namespace skyline::kernel {
tlsIo.size = 0x1000000000; tlsIo.size = 0x1000000000;
break; break;
} }
}
chunks = {ChunkDescriptor{ default:
.ptr = reinterpret_cast<u8*>(base.address), throw exception("Regions initialized without VMM initialization");
.size = base.size, }
.state = memory::states::Unmapped,
}};
state.logger->Debug("Region Map:\nCode Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nAlias Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nHeap Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nStack Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nTLS/IO Region: 0x{:X} - 0x{:X} (Size: 0x{:X})", code.address, code.address + code.size, code.size, alias.address, alias.address + alias.size, alias.size, heap.address, heap state.logger->Debug("Region Map:\nCode Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nAlias Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nHeap Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nStack Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nTLS/IO Region: 0x{:X} - 0x{:X} (Size: 0x{:X})", code.address, code.address + code.size, code.size, alias.address, alias.address + alias.size, alias.size, heap.address, heap
.address + heap.size, heap.size, stack.address, stack.address + stack.size, stack.size, tlsIo.address, tlsIo.address + tlsIo.size, tlsIo.size); .address + heap.size, heap.size, stack.address, stack.address + stack.size, stack.size, tlsIo.address, tlsIo.address + tlsIo.size, tlsIo.size);

View File

@ -170,7 +170,7 @@ namespace skyline {
} }
}; };
enum class AddressSpaceType { enum class AddressSpaceType : u8 {
AddressSpace32Bit, //!< 32-bit address space used by 32-bit applications AddressSpace32Bit, //!< 32-bit address space used by 32-bit applications
AddressSpace36Bit, //!< 36-bit address space used by 64-bit applications before 2.0.0 AddressSpace36Bit, //!< 36-bit address space used by 64-bit applications before 2.0.0
AddressSpace39Bit, //!< 39-bit address space used by 64-bit applications after 2.0.0 AddressSpace39Bit, //!< 39-bit address space used by 64-bit applications after 2.0.0
@ -218,16 +218,17 @@ namespace skyline {
MemoryManager(const DeviceState &state); MemoryManager(const DeviceState &state);
/** /**
* @brief Initializes all of the regions in the address space * @note This should be called before any mappings in the VMM or calls to InitalizeRegions are done
*/ */
void InitializeRegions(u8* codeStart, u64 size, memory::AddressSpaceType type); void InitializeVmm(memory::AddressSpaceType type);
void InitializeRegions(u8* codeStart, u64 size);
void InsertChunk(const ChunkDescriptor &chunk); void InsertChunk(const ChunkDescriptor &chunk);
std::optional<ChunkDescriptor> Get(void* ptr); std::optional<ChunkDescriptor> Get(void* ptr);
/** /**
* @brief The total amount of space in bytes occupied by all memory mappings
* @return The cumulative size of all memory mappings in bytes * @return The cumulative size of all memory mappings in bytes
*/ */
size_t GetProgramSize(); size_t GetProgramSize();

View File

@ -52,7 +52,7 @@ namespace skyline::kernel::svc {
return; return;
} }
auto chunk{state.os->memory.Get(pointer)}; auto chunk{state.process->memory.Get(pointer)};
if (!chunk) { if (!chunk) {
state.ctx->registers.w0 = result::InvalidAddress; state.ctx->registers.w0 = result::InvalidAddress;
state.logger->Warn("svcSetMemoryAttribute: Cannot find memory region: 0x{:X}", pointer); state.logger->Warn("svcSetMemoryAttribute: Cannot find memory region: 0x{:X}", pointer);
@ -69,7 +69,7 @@ namespace skyline::kernel::svc {
newChunk.ptr = pointer; newChunk.ptr = pointer;
newChunk.size = size; newChunk.size = size;
newChunk.attributes.isUncached = value.isUncached; newChunk.attributes.isUncached = value.isUncached;
state.os->memory.InsertChunk(newChunk); state.process->memory.InsertChunk(newChunk);
state.logger->Debug("svcSetMemoryAttribute: Set caching to {} at 0x{:X} for 0x{:X} bytes", bool(value.isUncached), pointer, size); state.logger->Debug("svcSetMemoryAttribute: Set caching to {} at 0x{:X} for 0x{:X} bytes", bool(value.isUncached), pointer, size);
state.ctx->registers.w0 = Result{}; state.ctx->registers.w0 = Result{};
@ -92,14 +92,14 @@ namespace skyline::kernel::svc {
return; return;
} }
auto stack{state.os->memory.stack}; auto stack{state.process->memory.stack};
if (!stack.IsInside(destination)) { if (!stack.IsInside(destination)) {
state.ctx->registers.w0 = result::InvalidMemoryRegion; state.ctx->registers.w0 = result::InvalidMemoryRegion;
state.logger->Warn("svcMapMemory: Destination not within stack region: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size); state.logger->Warn("svcMapMemory: Destination not within stack region: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
return; return;
} }
auto chunk{state.os->memory.Get(source)}; auto chunk{state.process->memory.Get(source)};
if (!chunk) { if (!chunk) {
state.ctx->registers.w0 = result::InvalidAddress; state.ctx->registers.w0 = result::InvalidAddress;
state.logger->Warn("svcMapMemory: Source has no descriptor: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size); state.logger->Warn("svcMapMemory: Source has no descriptor: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
@ -140,15 +140,15 @@ namespace skyline::kernel::svc {
return; return;
} }
auto stack{state.os->memory.stack}; auto stack{state.process->memory.stack};
if (!stack.IsInside(source)) { if (!stack.IsInside(source)) {
state.ctx->registers.w0 = result::InvalidMemoryRegion; state.ctx->registers.w0 = result::InvalidMemoryRegion;
state.logger->Warn("svcUnmapMemory: Source not within stack region: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size); state.logger->Warn("svcUnmapMemory: Source not within stack region: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
return; return;
} }
auto sourceChunk{state.os->memory.Get(source)}; auto sourceChunk{state.process->memory.Get(source)};
auto destChunk{state.os->memory.Get(destination)}; auto destChunk{state.process->memory.Get(destination)};
if (!sourceChunk || !destChunk) { if (!sourceChunk || !destChunk) {
state.ctx->registers.w0 = result::InvalidAddress; state.ctx->registers.w0 = result::InvalidAddress;
state.logger->Warn("svcUnmapMemory: Addresses have no descriptor: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size); state.logger->Warn("svcUnmapMemory: Addresses have no descriptor: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
@ -183,7 +183,7 @@ namespace skyline::kernel::svc {
memory::MemoryInfo memInfo{}; memory::MemoryInfo memInfo{};
auto pointer{reinterpret_cast<u8*>(state.ctx->registers.x2)}; auto pointer{reinterpret_cast<u8*>(state.ctx->registers.x2)};
auto chunk{state.os->memory.Get(pointer)}; auto chunk{state.process->memory.Get(pointer)};
if (chunk) { if (chunk) {
memInfo = { memInfo = {
@ -198,7 +198,7 @@ namespace skyline::kernel::svc {
state.logger->Debug("svcQueryMemory: Address: 0x{:X}, Size: 0x{:X}, Type: 0x{:X}, Is Uncached: {}, Permissions: {}{}{}", memInfo.address, memInfo.size, memInfo.type, bool(chunk->attributes.isUncached), chunk->permission.r ? 'R' : '-', chunk->permission.w ? 'W' : '-', chunk->permission.x ? 'X' : '-'); state.logger->Debug("svcQueryMemory: Address: 0x{:X}, Size: 0x{:X}, Type: 0x{:X}, Is Uncached: {}, Permissions: {}{}{}", memInfo.address, memInfo.size, memInfo.type, bool(chunk->attributes.isUncached), chunk->permission.r ? 'R' : '-', chunk->permission.w ? 'W' : '-', chunk->permission.x ? 'X' : '-');
} else { } else {
auto addressSpaceEnd{reinterpret_cast<u64>(state.os->memory.addressSpace.address + state.os->memory.addressSpace.size)}; auto addressSpaceEnd{reinterpret_cast<u64>(state.process->memory.addressSpace.address + state.process->memory.addressSpace.size)};
memInfo = { memInfo = {
.address = addressSpaceEnd, .address = addressSpaceEnd,
@ -215,24 +215,28 @@ namespace skyline::kernel::svc {
} }
void ExitProcess(DeviceState &state) { void ExitProcess(DeviceState &state) {
state.logger->Debug("svcExitProcess: Exiting current process: {}", state.process->pid); state.logger->Debug("svcExitProcess: Exiting process");
state.os->KillThread(state.process->pid); //state.os->KillThread(state.process->pid);
} }
void CreateThread(DeviceState &state) { void CreateThread(DeviceState &state) {
auto entryAddress{state.ctx->registers.x1}; auto entry{reinterpret_cast<void*>(state.ctx->registers.x1)};
auto entryArgument{state.ctx->registers.x2}; auto entryArgument{state.ctx->registers.x2};
auto stackTop{state.ctx->registers.x3}; auto stackTop{reinterpret_cast<u8*>(state.ctx->registers.x3)};
auto priority{static_cast<i8>(state.ctx->registers.w4)}; auto priority{static_cast<i8>(state.ctx->registers.w4)};
if (!state.thread->switchPriority.Valid(priority)) { if (!constant::HosPriority.Valid(priority)) {
state.ctx->registers.w0 = result::InvalidAddress; state.ctx->registers.w0 = result::InvalidAddress;
state.logger->Warn("svcCreateThread: 'priority' invalid: {}", priority); state.logger->Warn("svcCreateThread: 'priority' invalid: {}", priority);
return; return;
} }
auto thread{state.process->CreateThread(entryAddress, entryArgument, stackTop, priority)}; auto stack{state.process->GetMemoryObject(stackTop)};
state.logger->Debug("svcCreateThread: Created thread with handle 0x{:X} (Entry Point: 0x{:X}, Argument: 0x{:X}, Stack Pointer: 0x{:X}, Priority: {}, TID: {})", thread->handle, entryAddress, entryArgument, stackTop, priority, thread->tid); if (!stack)
throw exception("svcCreateThread: Cannot find memory object in handle table for thread stack: 0x{:X}", stackTop);
auto thread{state.process->CreateThread(entry, entryArgument, priority, static_pointer_cast<type::KPrivateMemory>(stack->item))};
state.logger->Debug("svcCreateThread: Created thread with handle 0x{:X} (Entry Point: 0x{:X}, Argument: 0x{:X}, Stack Pointer: 0x{:X}, Priority: {}, ID: {})", thread->handle, entry, entryArgument, stackTop, priority, thread->id);
state.ctx->registers.w1 = thread->handle; state.ctx->registers.w1 = thread->handle;
state.ctx->registers.w0 = Result{}; state.ctx->registers.w0 = Result{};
@ -242,7 +246,7 @@ namespace skyline::kernel::svc {
auto handle{state.ctx->registers.w0}; auto handle{state.ctx->registers.w0};
try { try {
auto thread{state.process->GetHandle<type::KThread>(handle)}; auto thread{state.process->GetHandle<type::KThread>(handle)};
state.logger->Debug("svcStartThread: Starting thread: 0x{:X}, PID: {}", handle, thread->tid); state.logger->Debug("svcStartThread: Starting thread: 0x{:X}, PID: {}", handle, thread->id);
thread->Start(); thread->Start();
state.ctx->registers.w0 = Result{}; state.ctx->registers.w0 = Result{};
} catch (const std::exception &) { } catch (const std::exception &) {
@ -252,8 +256,8 @@ namespace skyline::kernel::svc {
} }
void ExitThread(DeviceState &state) { void ExitThread(DeviceState &state) {
state.logger->Debug("svcExitThread: Exiting current thread: {}", state.thread->tid); state.logger->Debug("svcExitThread: Exiting current thread: {}", state.thread->id);
state.os->KillThread(state.thread->tid); state.os->KillThread(state.thread->id);
} }
void SleepThread(DeviceState &state) { void SleepThread(DeviceState &state) {
@ -389,7 +393,7 @@ namespace skyline::kernel::svc {
void ResetSignal(DeviceState &state) { void ResetSignal(DeviceState &state) {
auto handle{state.ctx->registers.w0}; auto handle{state.ctx->registers.w0};
try { try {
auto &object{state.process->handles.at(handle)}; auto object{state.process->GetHandle(handle)};
switch (object->objectType) { switch (object->objectType) {
case type::KType::KEvent: case type::KType::KEvent:
std::static_pointer_cast<type::KEvent>(object)->ResetSignal(); std::static_pointer_cast<type::KEvent>(object)->ResetSignal();
@ -431,7 +435,7 @@ namespace skyline::kernel::svc {
for (const auto &handle : waitHandles) { for (const auto &handle : waitHandles) {
handleStr += fmt::format("* 0x{:X}\n", handle); handleStr += fmt::format("* 0x{:X}\n", handle);
auto object{state.process->handles.at(handle)}; auto object{state.process->GetHandle(handle)};
switch (object->objectType) { switch (object->objectType) {
case type::KType::KProcess: case type::KType::KProcess:
case type::KType::KThread: case type::KType::KThread:
@ -612,9 +616,9 @@ namespace skyline::kernel::svc {
pid_t pid{}; pid_t pid{};
if (handle != threadSelf) if (handle != threadSelf)
pid = state.process->GetHandle<type::KThread>(handle)->tid; pid = state.process->GetHandle<type::KThread>(handle)->id;
else else
pid = state.thread->tid; pid = state.thread->id;
state.logger->Debug("svcGetThreadId: Handle: 0x{:X}, PID: {}", handle, pid); state.logger->Debug("svcGetThreadId: Handle: 0x{:X}, PID: {}", handle, pid);
@ -650,19 +654,19 @@ namespace skyline::kernel::svc {
break; break;
case constant::infoState::AliasRegionBaseAddr: case constant::infoState::AliasRegionBaseAddr:
out = state.os->memory.alias.address; out = state.process->memory.alias.address;
break; break;
case constant::infoState::AliasRegionSize: case constant::infoState::AliasRegionSize:
out = state.os->memory.alias.size; out = state.process->memory.alias.size;
break; break;
case constant::infoState::HeapRegionBaseAddr: case constant::infoState::HeapRegionBaseAddr:
out = state.os->memory.heap.address; out = state.process->memory.heap.address;
break; break;
case constant::infoState::HeapRegionSize: case constant::infoState::HeapRegionSize:
out = state.os->memory.heap.size; out = state.process->memory.heap.size;
break; break;
case constant::infoState::TotalMemoryAvailable: case constant::infoState::TotalMemoryAvailable:
@ -670,23 +674,23 @@ namespace skyline::kernel::svc {
break; break;
case constant::infoState::TotalMemoryUsage: case constant::infoState::TotalMemoryUsage:
out = state.process->heap->size + constant::DefStackSize + state.os->memory.GetProgramSize(); out = state.process->heap->size + constant::DefaultStackSize + state.process->memory.GetProgramSize();
break; break;
case constant::infoState::AddressSpaceBaseAddr: case constant::infoState::AddressSpaceBaseAddr:
out = state.os->memory.base.address; out = state.process->memory.base.address;
break; break;
case constant::infoState::AddressSpaceSize: case constant::infoState::AddressSpaceSize:
out = state.os->memory.base.size; out = state.process->memory.base.size;
break; break;
case constant::infoState::StackRegionBaseAddr: case constant::infoState::StackRegionBaseAddr:
out = state.os->memory.stack.address; out = state.process->memory.stack.address;
break; break;
case constant::infoState::StackRegionSize: case constant::infoState::StackRegionSize:
out = state.os->memory.stack.size; out = state.process->memory.stack.size;
break; break;
case constant::infoState::PersonalMmHeapSize: case constant::infoState::PersonalMmHeapSize:
@ -694,7 +698,7 @@ namespace skyline::kernel::svc {
break; break;
case constant::infoState::PersonalMmHeapUsage: case constant::infoState::PersonalMmHeapUsage:
out = state.process->heap->size + constant::DefStackSize; out = state.process->heap->size + constant::DefaultStackSize;
break; break;
case constant::infoState::TotalMemoryAvailableWithoutMmHeap: case constant::infoState::TotalMemoryAvailableWithoutMmHeap:
@ -702,7 +706,7 @@ namespace skyline::kernel::svc {
break; break;
case constant::infoState::TotalMemoryUsedWithoutMmHeap: case constant::infoState::TotalMemoryUsedWithoutMmHeap:
out = state.process->heap->size + constant::DefStackSize; // TODO: Same as above out = state.process->heap->size + constant::DefaultStackSize; // TODO: Same as above
break; break;
case constant::infoState::UserExceptionContextAddr: case constant::infoState::UserExceptionContextAddr:

View File

@ -14,18 +14,16 @@ namespace skyline::kernel::type {
if (ptr && !util::PageAligned(ptr)) if (ptr && !util::PageAligned(ptr))
throw exception("KPrivateMemory was created with non-page-aligned address: 0x{:X}", ptr); throw exception("KPrivateMemory was created with non-page-aligned address: 0x{:X}", ptr);
ptr = reinterpret_cast<u8*>(mmap(ptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, ptr ? MAP_FIXED : 0, 0, 0)); this->ptr = reinterpret_cast<u8*>(mmap(ptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, (ptr ? MAP_FIXED : 0) | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
if (ptr == MAP_FAILED) if (this->ptr == MAP_FAILED)
throw exception("An occurred while mapping private memory: {}", strerror(errno)); throw exception("An occurred while mapping private memory: {} with {} @ 0x{:X}", strerror(errno), ptr, size);
state.os->memory.InsertChunk(ChunkDescriptor{ state.process->memory.InsertChunk(ChunkDescriptor{
.ptr = ptr, .ptr = this->ptr,
.size = size, .size = size,
.permission = permission, .permission = permission,
.state = memState, .state = memState,
}); });
this->ptr = ptr;
} }
void KPrivateMemory::Resize(size_t nSize) { void KPrivateMemory::Resize(size_t nSize) {
@ -34,13 +32,13 @@ namespace skyline::kernel::type {
throw exception("An occurred while resizing private memory: {}", strerror(errno)); throw exception("An occurred while resizing private memory: {}", strerror(errno));
if (nSize < size) { if (nSize < size) {
state.os->memory.InsertChunk(ChunkDescriptor{ state.process->memory.InsertChunk(ChunkDescriptor{
.ptr = ptr + nSize, .ptr = ptr + nSize,
.size = size - nSize, .size = size - nSize,
.state = memory::states::Unmapped, .state = memory::states::Unmapped,
}); });
} else if (size < nSize) { } else if (size < nSize) {
state.os->memory.InsertChunk(ChunkDescriptor{ state.process->memory.InsertChunk(ChunkDescriptor{
.ptr = ptr + size, .ptr = ptr + size,
.size = nSize - size, .size = nSize - size,
.permission = permission, .permission = permission,
@ -59,7 +57,7 @@ namespace skyline::kernel::type {
if (memState.value == memory::states::CodeStatic.value && permission.w) if (memState.value == memory::states::CodeStatic.value && permission.w)
memState = memory::states::CodeMutable; memState = memory::states::CodeMutable;
state.os->memory.InsertChunk(ChunkDescriptor{ state.process->memory.InsertChunk(ChunkDescriptor{
.ptr = ptr, .ptr = ptr,
.size = size, .size = size,
.permission = permission, .permission = permission,
@ -69,7 +67,7 @@ namespace skyline::kernel::type {
KPrivateMemory::~KPrivateMemory() { KPrivateMemory::~KPrivateMemory() {
munmap(ptr, size); munmap(ptr, size);
state.os->memory.InsertChunk(ChunkDescriptor{ state.process->memory.InsertChunk(ChunkDescriptor{
.ptr = ptr, .ptr = ptr,
.size = size, .size = size,
.state = memory::states::Unmapped, .state = memory::states::Unmapped,

View File

@ -1,108 +1,62 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <fcntl.h>
#include <unistd.h>
#include <sys/uio.h>
#include <asm/unistd.h>
#include <nce/guest.h>
#include <nce.h> #include <nce.h>
#include <os.h> #include <os.h>
#include "KProcess.h" #include "KProcess.h"
namespace skyline::kernel::type { namespace skyline::kernel::type {
KProcess::TlsPage::TlsPage(u8* ptr) : ptr(ptr) {} KProcess::WaitStatus::WaitStatus(i8 priority, KHandle handle) : priority(priority), handle(handle) {}
KProcess::WaitStatus::WaitStatus(i8 priority, KHandle handle, u32 *mutex) : priority(priority), handle(handle), mutex(mutex) {}
KProcess::TlsPage::TlsPage(const std::shared_ptr<KPrivateMemory> &memory) : memory(memory) {}
u8 *KProcess::TlsPage::ReserveSlot() { u8 *KProcess::TlsPage::ReserveSlot() {
if (Full()) if (Full())
throw exception("Trying to get TLS slot from full page"); throw exception("Trying to reserve TLS slot in full page");
return Get(index++);
slot[index] = true;
return Get(index++); // ++ on right will cause increment after evaluation of expression
} }
u8* KProcess::TlsPage::Get(u8 slotNo) { u8 *KProcess::TlsPage::Get(u8 index) {
if (slotNo >= constant::TlsSlots) if (index >= constant::TlsSlots)
throw exception("TLS slot is out of range"); throw exception("TLS slot is out of range");
return memory->ptr + (constant::TlsSlotSize * index);
return ptr + (constant::TlsSlotSize * slotNo);
} }
bool KProcess::TlsPage::Full() { bool KProcess::TlsPage::Full() {
return slot[constant::TlsSlots - 1]; return index == constant::TlsSlots;
} }
u8* KProcess::GetTlsSlot() { KProcess::KProcess(const DeviceState &state) : memory(state), KSyncObject(state, KType::KProcess) {}
void KProcess::InitializeHeap() {
constexpr size_t DefaultHeapSize{0x200000};
heap.make_shared(state, reinterpret_cast<u8 *>(state.process->memory.heap.address), DefaultHeapSize, memory::Permission{true, true, false}, memory::states::Heap);
}
u8 *KProcess::AllocateTlsSlot() {
for (auto &tlsPage: tlsPages) for (auto &tlsPage: tlsPages)
if (!tlsPage->Full()) if (!tlsPage->Full())
return tlsPage->ReserveSlot(); return tlsPage->ReserveSlot();
u8* ptr; u8 *ptr = tlsPages.empty() ? reinterpret_cast<u8 *>(state.process->memory.tlsIo.address) : ((*(tlsPages.end() - 1))->memory->ptr + PAGE_SIZE);
if (tlsPages.empty()) { auto tlsPage{std::make_shared<TlsPage>(std::make_shared<KPrivateMemory>(state, ptr, PAGE_SIZE, memory::Permission(true, true, false), memory::states::ThreadLocal))};
auto region{state.os->memory.tlsIo}; tlsPages.push_back(tlsPage);
ptr = reinterpret_cast<u8*>(region.size ? region.address : 0);
} else {
ptr = (*(tlsPages.end() - 1))->ptr + PAGE_SIZE;
}
auto tlsMem{NewHandle<KPrivateMemory>(ptr, PAGE_SIZE, memory::Permission(true, true, false), memory::states::ThreadLocal).item};
tlsPages.push_back(std::make_shared<TlsPage>(tlsMem->ptr));
auto &tlsPage{tlsPages.back()};
if (tlsPages.empty())
tlsPage->ReserveSlot(); // User-mode exception handling tlsPage->ReserveSlot(); // User-mode exception handling
return tlsPage->ReserveSlot(); return tlsPage->ReserveSlot();
} }
void KProcess::InitializeMemory() { std::shared_ptr<KThread> KProcess::CreateThread(void *entry, u64 argument, i8 priority, const std::shared_ptr<KPrivateMemory> &stack) {
constexpr size_t DefHeapSize{0x200000}; // The default amount of heap auto thread{NewHandle<KThread>(this, threads.size(), entry, argument, priority, stack).item};
heap = NewHandle<KPrivateMemory>(reinterpret_cast<u8*>(state.os->memory.heap.address), DefHeapSize, memory::Permission{true, true, false}, memory::states::Heap).item; threads.push_back(thread);
threads[pid]->tls = GetTlsSlot();
}
KProcess::KProcess(const DeviceState &state, pid_t pid, u64 entryPoint, std::shared_ptr<type::KSharedMemory> &stack, std::shared_ptr<type::KSharedMemory> &tlsMemory) : pid(pid), stack(stack), KSyncObject(state, KType::KProcess) {
constexpr u8 DefaultPriority{44}; // The default priority of a process
auto thread{NewHandle<KThread>(pid, entryPoint, 0, reinterpret_cast<u64>(stack->guest.ptr + stack->guest.size), nullptr, DefaultPriority, this, tlsMemory).item};
threads[pid] = thread;
state.nce->WaitThreadInit(thread);
memFd = open(fmt::format("/proc/{}/mem", pid).c_str(), O_RDWR | O_CLOEXEC);
if (memFd == -1)
throw exception("Cannot open file descriptor to /proc/{}/mem, \"{}\"", pid, strerror(errno));
}
KProcess::~KProcess() {
close(memFd);
status = Status::Exiting;
}
std::shared_ptr<KThread> KProcess::CreateThread(u64 entryPoint, u64 entryArg, u64 stackTop, i8 priority) {
auto size{(sizeof(ThreadContext) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1)};
auto tlsMem{std::make_shared<type::KSharedMemory>(state, size, memory::states::Reserved)};
Registers fregs{
.x0 = CLONE_THREAD | CLONE_SIGHAND | CLONE_PTRACE | CLONE_FS | CLONE_VM | CLONE_FILES | CLONE_IO,
.x1 = stackTop,
.x3 = reinterpret_cast<u64>(tlsMem->Map(nullptr, size, memory::Permission{true, true, false})),
.x8 = __NR_clone,
.x5 = reinterpret_cast<u64>(&guest::GuestEntry),
.x6 = entryPoint,
};
state.nce->ExecuteFunction(ThreadCall::Clone, fregs);
if (static_cast<int>(fregs.x0) < 0)
throw exception("Cannot create thread: Address: 0x{:X}, Stack Top: 0x{:X}", entryPoint, stackTop);
auto pid{static_cast<pid_t>(fregs.x0)};
auto thread{NewHandle<KThread>(pid, entryPoint, entryArg, stackTop, GetTlsSlot(), priority, this, tlsMem).item};
threads[pid] = thread;
return thread; return thread;
} }
std::optional<KProcess::HandleOut<KMemory>> KProcess::GetMemoryObject(u8 *ptr) { std::optional<KProcess::HandleOut<KMemory>> KProcess::GetMemoryObject(u8 *ptr) {
std::shared_lock lock(handleMutex);
for (KHandle index{}; index < handles.size(); index++) { for (KHandle index{}; index < handles.size(); index++) {
auto &object{handles[index]}; auto &object{handles[index]};
switch (object->objectType) { switch (object->objectType) {

View File

@ -4,8 +4,8 @@
#pragma once #pragma once
#include <list> #include <list>
#include <kernel/memory.h>
#include "KThread.h" #include "KThread.h"
#include "KPrivateMemory.h"
#include "KTransferMemory.h" #include "KTransferMemory.h"
#include "KSession.h" #include "KSession.h"
#include "KEvent.h" #include "KEvent.h"
@ -20,11 +20,28 @@ namespace skyline {
namespace kernel::type { namespace kernel::type {
/** /**
* @brief The KProcess class is responsible for holding the state of a process * @brief KProcess manages process-global state such as memory, kernel handles allocated to the process and synchronization primitives
*/ */
class KProcess : public KSyncObject { class KProcess : public KSyncObject {
private: private:
KHandle handleIndex{constant::BaseHandleIndex}; //!< The index of the handle that will be allocated next std::vector<std::shared_ptr<KObject>> handles;
std::shared_mutex handleMutex;
struct WaitStatus {
std::atomic_bool flag{false};
i8 priority;
KHandle handle;
u32* mutex{};
WaitStatus(i8 priority, KHandle handle);
WaitStatus(i8 priority, KHandle handle, u32* mutex);
};
std::unordered_map<u64, std::vector<std::shared_ptr<WaitStatus>>> mutexes; //!< A map from a mutex's address to a vector of Mutex objects for threads waiting on it
std::unordered_map<u64, std::list<std::shared_ptr<WaitStatus>>> conditionals; //!< A map from a conditional variable's address to a vector of threads waiting on it
Mutex mutexLock;
Mutex conditionalLock;
/** /**
* @brief The status of a single TLS page (A page is 4096 bytes on ARMv8) * @brief The status of a single TLS page (A page is 4096 bytes on ARMv8)
@ -33,44 +50,37 @@ namespace skyline {
* @url https://switchbrew.org/wiki/Thread_Local_Storage * @url https://switchbrew.org/wiki/Thread_Local_Storage
*/ */
struct TlsPage { struct TlsPage {
u8* ptr;
u8 index{}; //!< The slots are assigned sequentially, this holds the index of the last TLS slot reserved u8 index{}; //!< The slots are assigned sequentially, this holds the index of the last TLS slot reserved
bool slot[constant::TlsSlots]{}; //!< An array of booleans denoting which TLS slots are reserved std::shared_ptr<KPrivateMemory> memory;
TlsPage(u8* ptr); TlsPage(const std::shared_ptr<KPrivateMemory>& memory);
/**
* @brief Reserves a single 0x200 byte TLS slot
* @return The address of the reserved slot
*/
u8* ReserveSlot(); u8* ReserveSlot();
/** u8* Get(u8 index);
* @brief Returns the address of a particular slot
* @param slotNo The number of the slot to be returned
* @return The address of the specified slot
*/
u8* Get(u8 slotNo);
/**
* @brief Returns boolean on if the TLS page has free slots or not
* @return If the whole page is full or not
*/
bool Full(); bool Full();
}; };
/**
* @return The address of a free TLS slot
*/
u8* GetTlsSlot();
/**
* @brief Initializes heap and the initial TLS page
*/
void InitializeMemory();
public: public:
friend OS; MemoryManager memory;
std::shared_ptr<KPrivateMemory> heap;
std::vector<std::shared_ptr<KThread>> threads;
std::vector<std::shared_ptr<TlsPage>> tlsPages;
KProcess(const DeviceState &state);
/**
* @note This requires VMM regions to be initialized, it will map heap at an arbitrary location otherwise
*/
void InitializeHeap();
/**
* @return A 0x200 TLS slot allocated inside the TLS/IO region
*/
u8* AllocateTlsSlot();
std::shared_ptr<KThread> CreateThread(void *entry, u64 argument = 0, i8 priority = 44, const std::shared_ptr<KPrivateMemory> &stack = nullptr);
/** /**
* @brief The output for functions that return created kernel objects * @brief The output for functions that return created kernel objects
@ -82,96 +92,40 @@ namespace skyline {
KHandle handle; //!< The handle of the object in the process KHandle handle; //!< The handle of the object in the process
}; };
enum class Status {
Created, //!< The process was created but the main thread has not started yet
Started, //!< The process has been started
Exiting, //!< The process is exiting
} status = Status::Created;
/**
* @brief Metadata on a thread waiting for mutexes or conditional variables
*/
struct WaitStatus {
std::atomic_bool flag{false}; //!< The underlying atomic flag of the thread
u8 priority; //!< The priority of the thread
KHandle handle; //!< The handle of the thread
u32* mutex{};
WaitStatus(u8 priority, KHandle handle) : priority(priority), handle(handle) {}
WaitStatus(u8 priority, KHandle handle, u32* mutex) : priority(priority), handle(handle), mutex(mutex) {}
};
pid_t pid; //!< The PID of the process or TGID of the threads
int memFd; //!< The file descriptor to the memory of the process
std::vector<std::shared_ptr<KObject>> handles; //!< A vector of KObject which corresponds to the handle
std::unordered_map<pid_t, std::shared_ptr<KThread>> threads; //!< A mapping from a PID to it's corresponding KThread object
std::unordered_map<u64, std::vector<std::shared_ptr<WaitStatus>>> mutexes; //!< A map from a mutex's address to a vector of Mutex objects for threads waiting on it
std::unordered_map<u64, std::list<std::shared_ptr<WaitStatus>>> conditionals; //!< A map from a conditional variable's address to a vector of threads waiting on it
std::vector<std::shared_ptr<TlsPage>> tlsPages; //!< A vector of all allocated TLS pages
std::shared_ptr<type::KSharedMemory> stack; //!< The shared memory used to hold the stack of the main thread
std::shared_ptr<KPrivateMemory> heap; //!< The kernel memory object backing the allocated heap
Mutex mutexLock; //!< Synchronizes all concurrent guest mutex operations
Mutex conditionalLock; //!< Synchronizes all concurrent guest conditional variable operations
/**
* @param pid The PID of the main thread
* @param entryPoint The entry point of execution for the guest
* @param tlsMemory The KSharedMemory object for TLS memory allocated by the guest process
*/
KProcess(const DeviceState &state, pid_t pid, u64 entryPoint, std::shared_ptr<type::KSharedMemory> &stack, std::shared_ptr<type::KSharedMemory> &tlsMemory);
/**
* Close the file descriptor to the process's memory
*/
~KProcess();
/**
* @brief Create a thread in this process
* @param entryPoint The address of the initial function
* @param entryArg An argument to the function
* @param stackTop The top of the stack
* @param priority The priority of the thread
* @return An instance of KThread class for the corresponding thread
*/
std::shared_ptr<KThread> CreateThread(u64 entryPoint, u64 entryArg, u64 stackTop, i8 priority);
/** /**
* @brief Creates a new handle to a KObject and adds it to the process handle_table * @brief Creates a new handle to a KObject and adds it to the process handle_table
* @tparam objectClass The class of the kernel object to create * @tparam objectClass The class of the kernel object to create
* @param args The arguments for the kernel object except handle, pid and state * @param args The arguments for the kernel object except handle, pid and state
* @return A shared pointer to the corresponding object
*/ */
template<typename objectClass, typename ...objectArgs> template<typename objectClass, typename ...objectArgs>
HandleOut<objectClass> NewHandle(objectArgs... args) { HandleOut<objectClass> NewHandle(objectArgs... args) {
std::unique_lock lock(handleMutex);
std::shared_ptr<objectClass> item; std::shared_ptr<objectClass> item;
if constexpr (std::is_same<objectClass, KThread>()) if constexpr (std::is_same<objectClass, KThread>())
item = std::make_shared<objectClass>(state, handleIndex, args...); item = std::make_shared<objectClass>(state, constant::BaseHandleIndex + handles.size(), args...);
else else
item = std::make_shared<objectClass>(state, args...); item = std::make_shared<objectClass>(state, args...);
handles.push_back(std::static_pointer_cast<KObject>(item)); handles.push_back(std::static_pointer_cast<KObject>(item));
return {item, handleIndex++}; return {item, static_cast<KHandle>((constant::BaseHandleIndex + handles.size()) - 1)};
} }
/** /**
* @brief Inserts an item into the process handle table * @brief Inserts an item into the process handle table
* @param item The item to insert
* @return The handle of the corresponding item in the handle table * @return The handle of the corresponding item in the handle table
*/ */
template<typename objectClass> template<typename objectClass>
KHandle InsertItem(std::shared_ptr<objectClass> &item) { KHandle InsertItem(std::shared_ptr<objectClass> &item) {
std::unique_lock lock(handleMutex);
handles.push_back(std::static_pointer_cast<KObject>(item)); handles.push_back(std::static_pointer_cast<KObject>(item));
return handleIndex++; return static_cast<KHandle>((constant::BaseHandleIndex + handles.size()) - 1);
} }
/** template<typename objectClass = KObject>
* @brief Returns the underlying kernel object for a handle
* @tparam objectClass The class of the kernel object present in the handle
* @param handle The handle of the object
* @return A shared pointer to the object
*/
template<typename objectClass>
std::shared_ptr<objectClass> GetHandle(KHandle handle) { std::shared_ptr<objectClass> GetHandle(KHandle handle) {
std::shared_lock lock(handleMutex);
KType objectType; KType objectType;
if constexpr(std::is_same<objectClass, KThread>()) if constexpr(std::is_same<objectClass, KThread>())
objectType = KType::KThread; objectType = KType::KThread;
@ -202,6 +156,11 @@ namespace skyline {
} }
} }
template<>
std::shared_ptr<KObject> GetHandle<KObject>(KHandle handle) {
return handles.at(handle - constant::BaseHandleIndex);
}
/** /**
* @brief Retrieves a kernel memory object that owns the specified address * @brief Retrieves a kernel memory object that owns the specified address
* @param address The address to look for * @param address The address to look for

View File

@ -26,12 +26,12 @@ namespace skyline::kernel::type {
if (ptr && !util::PageAligned(ptr)) if (ptr && !util::PageAligned(ptr))
throw exception("KSharedMemory was mapped to a non-page-aligned address: 0x{:X}", ptr); throw exception("KSharedMemory was mapped to a non-page-aligned address: 0x{:X}", ptr);
guest.ptr = reinterpret_cast<u8*>(mmap(ptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | (ptr ? MAP_FIXED : 0), fd, 0)); guest.ptr = reinterpret_cast<u8*>(mmap(ptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | (ptr ? MAP_FIXED_NOREPLACE : 0), fd, 0));
if (guest.ptr == MAP_FAILED) if (guest.ptr == MAP_FAILED)
throw exception("An error occurred while mapping shared memory in guest"); throw exception("An error occurred while mapping shared memory in guest");
guest.size = size; guest.size = size;
state.os->memory.InsertChunk(ChunkDescriptor{ state.process->memory.InsertChunk(ChunkDescriptor{
.ptr = guest.ptr, .ptr = guest.ptr,
.size = size, .size = size,
.permission = permission, .permission = permission,
@ -51,7 +51,7 @@ namespace skyline::kernel::type {
if (guest.ptr == MAP_FAILED) if (guest.ptr == MAP_FAILED)
throw exception("An error occurred while updating shared memory's permissions in guest"); throw exception("An error occurred while updating shared memory's permissions in guest");
state.os->memory.InsertChunk(ChunkDescriptor{ state.process->memory.InsertChunk(ChunkDescriptor{
.ptr = ptr, .ptr = ptr,
.size = size, .size = size,
.permission = permission, .permission = permission,
@ -66,7 +66,7 @@ namespace skyline::kernel::type {
if (guest.Valid()) { if (guest.Valid()) {
munmap(guest.ptr, guest.size); munmap(guest.ptr, guest.size);
state.os->memory.InsertChunk(ChunkDescriptor{ state.process->memory.InsertChunk(ChunkDescriptor{
.ptr = guest.ptr, .ptr = guest.ptr,
.size = guest.size, .size = guest.size,
.state = memory::states::Unmapped, .state = memory::states::Unmapped,

View File

@ -1,14 +1,15 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <sys/types.h>
#include <sys/resource.h> #include <sys/resource.h>
#include <unistd.h>
#include <nce.h> #include <nce.h>
#include "KThread.h" #include <os.h>
#include "KProcess.h" #include "KProcess.h"
namespace skyline::kernel::type { namespace skyline::kernel::type {
KThread::KThread(const DeviceState &state, KHandle handle, pid_t selfTid, u64 entryPoint, u64 entryArg, u64 stackTop, u8* tls, i8 priority, KProcess *parent, const std::shared_ptr<type::KSharedMemory> &tlsMemory) : handle(handle), tid(selfTid), entryPoint(entryPoint), entryArg(entryArg), stackTop(stackTop), tls(tls), priority(priority), parent(parent), ctxMemory(tlsMemory), KSyncObject(state, KThread::KThread(const DeviceState &state, KHandle handle, KProcess *parent, size_t id, void *entry, u64 argument, i8 priority, const std::shared_ptr<KPrivateMemory> &stack) : handle(handle), parent(parent), id(id), entry(entry), entryArgument(argument), stack(stack), KSyncObject(state, KType::KThread) {
KType::KThread) {
UpdatePriority(priority); UpdatePriority(priority);
} }
@ -16,30 +17,135 @@ namespace skyline::kernel::type {
Kill(); Kill();
} }
void KThread::Start() { void KThread::StartThread() {
if (status == Status::Created) { pthread_setname_np(pthread_self(), fmt::format("HOS-{}", id).c_str());
if (tid == parent->pid)
parent->status = KProcess::Status::Started;
status = Status::Running;
state.nce->StartThread(entryArg, handle, parent->threads.at(tid)); ctx.sp = stack->ptr + stack->size;
if (!ctx.tpidrroEl0)
ctx.tpidrroEl0 = parent->AllocateTlsSlot();
ctx.nce = state.nce.get();
state.ctx = &ctx;
struct sigaction sigact{
.sa_sigaction = &NCE::SignalHandler,
.sa_flags = SA_SIGINFO,
};
//for (int signal : {SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV})
// sigaction(signal, &sigact, nullptr);
asm volatile(
"MSR TPIDR_EL0, %x0\n\t" // Set TLS to ThreadContext
"MOV X0, SP\n\t"
"STR X0, [%x0, #16]\n\t" // Store SP in ThreadContext
"MOV LR, %x1\n\t" // Store entry in Link Register so it is jumped to on return
"MOV X0, %x2\n\t" // Store the argument in X0
"MOV X1, %x3\n\t" // Store the thread handle in X1, NCA applications require this
"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"
"MSR FPSR, XZR\n\t"
"MSR FPCR, XZR\n\t"
"DUP V0.16B, WZR\n\t"
"DUP V1.16B, WZR\n\t"
"DUP V2.16B, WZR\n\t"
"DUP V3.16B, WZR\n\t"
"DUP V4.16B, WZR\n\t"
"DUP V5.16B, WZR\n\t"
"DUP V6.16B, WZR\n\t"
"DUP V7.16B, WZR\n\t"
"DUP V8.16B, WZR\n\t"
"DUP V9.16B, WZR\n\t"
"DUP V10.16B, WZR\n\t"
"DUP V11.16B, WZR\n\t"
"DUP V12.16B, WZR\n\t"
"DUP V13.16B, WZR\n\t"
"DUP V14.16B, WZR\n\t"
"DUP V15.16B, WZR\n\t"
"DUP V16.16B, WZR\n\t"
"DUP V17.16B, WZR\n\t"
"DUP V18.16B, WZR\n\t"
"DUP V19.16B, WZR\n\t"
"DUP V20.16B, WZR\n\t"
"DUP V21.16B, WZR\n\t"
"DUP V22.16B, WZR\n\t"
"DUP V23.16B, WZR\n\t"
"DUP V24.16B, WZR\n\t"
"DUP V25.16B, WZR\n\t"
"DUP V26.16B, WZR\n\t"
"DUP V27.16B, WZR\n\t"
"DUP V28.16B, WZR\n\t"
"DUP V29.16B, WZR\n\t"
"DUP V30.16B, WZR\n\t"
"DUP V31.16B, WZR\n\t"
"RET"
:
: "r"(&ctx), "r"(entry), "r"(entryArgument), "r"(handle)
: "x0", "x1", "lr"
);
__builtin_unreachable();
}
void KThread::Start(bool self) {
if (!running) {
running = true;
state.logger->Debug("Starting thread #{}", id);
if (!stack) {
stack = stack.make_shared(state, reinterpret_cast<u8 *>(state.process->memory.stack.address), constant::DefaultStackSize, memory::Permission{true, true, false}, memory::states::Stack);
if (mprotect(stack->ptr, PAGE_SIZE, PROT_NONE))
throw exception("Failed to create guard page for thread stack at 0x{:X}", stack->ptr);
}
if (self)
StartThread();
else
thread.emplace(&KThread::StartThread, this);
} }
} }
void KThread::Kill() { void KThread::Kill() {
if (status != Status::Dead) { if (running) {
status = Status::Dead; running = false;
Signal();
tgkill(parent->pid, tid, SIGTERM); Signal();
exit(0);
//tgkill(gettgid(), tid, SIGTERM);
} }
} }
void KThread::UpdatePriority(i8 priority) { void KThread::UpdatePriority(i8 priority) {
this->priority = priority; this->priority = priority;
auto priorityValue{androidPriority.Rescale(switchPriority, priority)}; auto priorityValue{constant::AndroidPriority.Rescale(constant::HosPriority, priority)};
if (setpriority(PRIO_PROCESS, static_cast<id_t>(tid), priorityValue) == -1) if (setpriority(PRIO_PROCESS, getpid(), priorityValue) == -1)
throw exception("Couldn't set process priority to {} for PID: {}", priorityValue, tid); throw exception("Couldn't set thread priority to {} for #{}", priorityValue, id);
} }
} }

View File

@ -4,21 +4,11 @@
#pragma once #pragma once
#include "KSyncObject.h" #include "KSyncObject.h"
#include "KPrivateMemory.h"
#include "KSharedMemory.h" #include "KSharedMemory.h"
namespace skyline::kernel::type { namespace skyline {
/** namespace kernel::type {
* @brief KThread class is responsible for holding the state of a thread
*/
class KThread : public KSyncObject {
private:
KProcess *parent; //!< The parent process of this thread
u64 entryPoint; //!< The address to start execution at
u64 entryArg; //!< An argument to pass to the process on entry
/**
* @brief A range of priorities for a corresponding system
*/
struct Priority { struct Priority {
i8 low; //!< The low range of priority i8 low; //!< The low range of priority
i8 high; //!< The high range of priority i8 high; //!< The high range of priority
@ -28,7 +18,7 @@ namespace skyline::kernel::type {
* @param value The priority value to rescale * @param value The priority value to rescale
* @return The rescaled priority value according to this range * @return The rescaled priority value according to this range
*/ */
constexpr i8 Rescale(const Priority &priority, i8 value) { constexpr i8 Rescale(const Priority &priority, i8 value) const {
return static_cast<i8>(priority.low + ((static_cast<float>(priority.high - priority.low) / static_cast<float>(priority.low - priority.high)) * (static_cast<float>(value) - priority.low))); return static_cast<i8>(priority.low + ((static_cast<float>(priority.high - priority.low) / static_cast<float>(priority.low - priority.high)) * (static_cast<float>(value) - priority.low)));
} }
@ -36,61 +26,61 @@ namespace skyline::kernel::type {
* @param value The priority value to check for validity * @param value The priority value to check for validity
* @return If the supplied priority value is valid * @return If the supplied priority value is valid
*/ */
constexpr bool Valid(i8 value) { constexpr bool Valid(i8 value) const {
return (value >= low) && (value <= high); return (value >= low) && (value <= high);
} }
}; };
}
namespace constant {
constexpr i8 DefaultPriority{44}; // The default priority of an HOS process
constexpr kernel::type::Priority AndroidPriority{19, -8}; //!< The range of priorities for Android
constexpr kernel::type::Priority HosPriority{0, 63}; //!< The range of priorities for Horizon OS
}
namespace kernel::type {
/**
* @brief KThread manages a single thread of execution which is responsible for running guest code and kernel code which is invoked by the guest
*/
class KThread : public KSyncObject {
private:
KProcess *parent;
std::optional<std::thread> thread; //!< If this KThread is backed by a host thread then this'll hold it
void StartThread();
public: public:
enum class Status { std::atomic<bool> running{false};
Created, //!< The thread has been created but has not been started yet
Running, //!< The thread is running currently
Dead, //!< The thread is dead and not running
} status = Status::Created;
std::atomic<bool> cancelSync{false}; //!< This is to flag to a thread to cancel a synchronization call it currently is in std::atomic<bool> cancelSync{false}; //!< This is to flag to a thread to cancel a synchronization call it currently is in
std::shared_ptr<type::KSharedMemory> ctxMemory; //!< The KSharedMemory of the shared memory allocated by the guest process TLS
KHandle handle; // The handle of the object in the handle table
pid_t tid; //!< The Linux Thread ID of the current thread
u64 stackTop; //!< The top of the stack (Where it starts growing downwards from)
u8* tls; //!< The address of TLS (Thread Local Storage) slot assigned to the current thread
i8 priority; //!< The priority of a thread in Nintendo format
Priority androidPriority{19, -8}; //!< The range of priorities for Android KHandle handle;
Priority switchPriority{0, 63}; //!< The range of priorities for the Nintendo Switch size_t id; //!< Index of thread in parent process's KThread vector
/** std::shared_ptr<KPrivateMemory> stack;
* @param handle The handle of the current thread ThreadContext ctx{};
* @param selfTid The TID of this thread
* @param entryPoint The address to start execution at void* entry;
* @param entryArg An argument to pass to the process on entry u64 entryArgument;
* @param stackTop The top of the stack i8 priority;
* @param tls The address of the TLS slot assigned
* @param priority The priority of the thread in Nintendo format KThread(const DeviceState &state, KHandle handle, KProcess *parent, size_t id, void *entry, u64 argument = 0, i8 priority = constant::DefaultPriority, const std::shared_ptr<KPrivateMemory>& stack = nullptr);
* @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, KHandle handle, pid_t selfTid, u64 entryPoint, u64 entryArg, u64 stackTop, u8* tls, i8 priority, KProcess *parent, const std::shared_ptr<type::KSharedMemory> &tlsMemory);
/**
* @brief Kills the thread and deallocates the memory allocated for stack.
*/
~KThread(); ~KThread();
/** /**
* @brief Starts this thread process * @param self If the calling thread should jump directly into guest code or if a new thread should be created for it
* @note If the thread is already running then this does nothing
* @note 'stack' will be created if it wasn't set prior to calling this
*/ */
void Start(); void Start(bool self = false);
/**
* @brief Kills the thread process
*/
void Kill(); void Kill();
/** /**
* @brief Update the priority level for the process. * @brief Sets the host priority using setpriority with a rescaled the priority from HOS to Android
* @details Set the priority of the current thread to `priority` using setpriority [https://linux.die.net/man/3/setpriority]. We rescale the priority from Nintendo scale to that of Android. * @note It also affects guest scheduler behavior, this isn't purely for host
* @param priority The priority of the thread in Nintendo format
*/ */
void UpdatePriority(i8 priority); void UpdatePriority(i8 priority);
}; };
} }
}

View File

@ -29,21 +29,21 @@ namespace skyline::loader {
u64 padding{util::AlignUp(patchSize, PAGE_SIZE) - patchSize}; u64 padding{util::AlignUp(patchSize, PAGE_SIZE) - patchSize};
process->NewHandle<kernel::type::KPrivateMemory>(base + executable.text.offset, textSize, memory::Permission{true, false, true}, memory::states::CodeStatic); // R-X process->NewHandle<kernel::type::KPrivateMemory>(base + executable.text.offset, textSize, memory::Permission{true, false, true}, memory::states::CodeStatic); // R-X
state.logger->Debug("Successfully mapped section .text @ 0x{0:X}, Size = 0x{1:X}", base + executable.text.offset, textSize); state.logger->Debug("Successfully mapped section .text @ {}, Size = 0x{:X}", base + executable.text.offset, textSize);
process->NewHandle<kernel::type::KPrivateMemory>(base + executable.ro.offset, roSize, memory::Permission{true, false, false}, memory::states::CodeReadOnly); // R-- process->NewHandle<kernel::type::KPrivateMemory>(base + executable.ro.offset, roSize, memory::Permission{true, false, false}, memory::states::CodeReadOnly); // R--
state.logger->Debug("Successfully mapped section .rodata @ 0x{0:X}, Size = 0x{1:X}", base + executable.ro.offset, roSize); state.logger->Debug("Successfully mapped section .rodata @ {}, Size = 0x{:X}", base + executable.ro.offset, roSize);
process->NewHandle<kernel::type::KPrivateMemory>(base + executable.data.offset, dataSize, memory::Permission{true, true, false}, memory::states::CodeMutable); // RW- process->NewHandle<kernel::type::KPrivateMemory>(base + executable.data.offset, dataSize, memory::Permission{true, true, false}, memory::states::CodeMutable); // RW-
state.logger->Debug("Successfully mapped section .data @ 0x{0:X}, Size = 0x{1:X}", base + executable.data.offset, dataSize); state.logger->Debug("Successfully mapped section .data @ {}, Size = 0x{:X}", base + executable.data.offset, dataSize);
process->NewHandle<kernel::type::KPrivateMemory>(base + patchOffset, patchSize + padding, memory::Permission{true, true, true}, memory::states::CodeMutable); // RWX process->NewHandle<kernel::type::KPrivateMemory>(base + patchOffset, patchSize + padding, memory::Permission{true, true, true}, memory::states::CodeMutable); // RWX
state.logger->Debug("Successfully mapped section .patch @ 0x{0:X}, Size = 0x{1:X}", base + patchOffset, patchSize + padding); state.logger->Debug("Successfully mapped section .patch @ {}, Size = 0x{:X}", base + patchOffset, patchSize + padding);
std::memcpy(executable.text.contents.data(), base + executable.text.offset, textSize); std::memcpy(base + executable.text.offset, executable.text.contents.data(), textSize);
std::memcpy(executable.ro.contents.data(), base + executable.ro.offset, roSize); std::memcpy(base + executable.ro.offset, executable.ro.contents.data(), roSize);
std::memcpy(executable.data.contents.data(), base + executable.data.offset, dataSize - executable.bssSize); std::memcpy(base + executable.data.offset, executable.data.contents.data(), dataSize - executable.bssSize);
std::memcpy(patch.data(), base + patchOffset, patchSize); std::memcpy(base + patchOffset, patch.data(), patchSize);
return {base, patchOffset + patchSize + padding}; return {base, patchOffset + patchSize + padding};
} }

View File

@ -70,17 +70,10 @@ namespace skyline::loader {
virtual ~Loader() = default; virtual ~Loader() = default;
/**
* @return The icon of the loaded application
*/
virtual std::vector<u8> GetIcon() { virtual std::vector<u8> GetIcon() {
return std::vector<u8>(); return std::vector<u8>();
} }
/**
* @brief Loads in the data of the main process
* @param process The process to load in the data
*/
virtual void LoadProcessData(const std::shared_ptr<kernel::type::KProcess> process, const DeviceState &state) = 0; virtual void LoadProcessData(const std::shared_ptr<kernel::type::KProcess> process, const DeviceState &state) = 0;
}; };
} }

View File

@ -2,8 +2,7 @@
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <nce.h> #include <nce.h>
#include <os.h> #include <kernel/types/KProcess.h>
#include <kernel/memory.h>
#include "nso.h" #include "nso.h"
#include "nca.h" #include "nca.h"
@ -21,6 +20,8 @@ namespace skyline::loader {
if (nsoFile == nullptr) if (nsoFile == nullptr)
throw exception("Cannot load an ExeFS that doesn't contain rtld"); throw exception("Cannot load an ExeFS that doesn't contain rtld");
state.process->memory.InitializeVmm(memory::AddressSpaceType::AddressSpace39Bit);
auto loadInfo{NsoLoader::LoadNso(nsoFile, process, state)}; auto loadInfo{NsoLoader::LoadNso(nsoFile, process, state)};
u64 offset{loadInfo.size}; u64 offset{loadInfo.size};
u8* base{loadInfo.base}; u8* base{loadInfo.base};
@ -38,7 +39,7 @@ namespace skyline::loader {
offset += loadInfo.size; offset += loadInfo.size;
} }
state.os->memory.InitializeRegions(base, offset, memory::AddressSpaceType::AddressSpace39Bit); state.process->memory.InitializeRegions(base, offset);
} }
void NcaLoader::LoadProcessData(const std::shared_ptr<kernel::type::KProcess> process, const DeviceState &state) { void NcaLoader::LoadProcessData(const std::shared_ptr<kernel::type::KProcess> process, const DeviceState &state) {

View File

@ -2,8 +2,7 @@
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <nce.h> #include <nce.h>
#include <os.h> #include <kernel/types/KProcess.h>
#include <kernel/memory.h>
#include <vfs/nacp.h> #include <vfs/nacp.h>
#include <vfs/region_backing.h> #include <vfs/region_backing.h>
#include "nro.h" #include "nro.h"
@ -59,7 +58,8 @@ namespace skyline::loader {
nroExecutable.bssSize = header.bssSize; nroExecutable.bssSize = header.bssSize;
state.process->memory.InitializeVmm(memory::AddressSpaceType::AddressSpace39Bit);
auto loadInfo{LoadExecutable(process, state, nroExecutable)}; auto loadInfo{LoadExecutable(process, state, nroExecutable)};
state.os->memory.InitializeRegions(loadInfo.base, loadInfo.size, memory::AddressSpaceType::AddressSpace39Bit); state.process->memory.InitializeRegions(loadInfo.base, loadInfo.size);
} }
} }

View File

@ -3,8 +3,7 @@
#include <lz4.h> #include <lz4.h>
#include <nce.h> #include <nce.h>
#include <os.h> #include <kernel/types/KProcess.h>
#include <kernel/memory.h>
#include "nso.h" #include "nso.h"
namespace skyline::loader { namespace skyline::loader {
@ -56,8 +55,8 @@ namespace skyline::loader {
} }
void NsoLoader::LoadProcessData(const std::shared_ptr<kernel::type::KProcess> process, const DeviceState &state) { void NsoLoader::LoadProcessData(const std::shared_ptr<kernel::type::KProcess> process, const DeviceState &state) {
state.process->memory.InitializeVmm(memory::AddressSpaceType::AddressSpace39Bit);
auto loadInfo{LoadNso(backing, process, state)}; auto loadInfo{LoadNso(backing, process, state)};
state.process->memory.InitializeRegions(loadInfo.base, loadInfo.size);
state.os->memory.InitializeRegions(loadInfo.base, loadInfo.size, memory::AddressSpaceType::AddressSpace39Bit);
} }
} }

View File

@ -18,6 +18,7 @@ extern skyline::GroupMutex JniMtx;
namespace skyline { namespace skyline {
void NCE::KernelThread(pid_t thread) { void NCE::KernelThread(pid_t thread) {
/*
state.jvm->AttachThread(); state.jvm->AttachThread();
try { try {
state.thread = state.process->threads.at(thread); state.thread = state.process->threads.at(thread);
@ -81,6 +82,7 @@ namespace skyline {
} }
state.jvm->DetachThread(); state.jvm->DetachThread();
*/
} }
NCE::NCE(DeviceState &state) : state(state) {} NCE::NCE(DeviceState &state) : state(state) {}
@ -111,55 +113,18 @@ namespace skyline {
} }
} }
/** inline ThreadContext *GetContext() {
* This function will not work if optimizations are enabled as ThreadContext isn't volatile ThreadContext *ctx;
* and due to that is not read on every iteration of the while loop. asm("MRS %0, TPIDR_EL0":"=r"(ctx));
* However, making ThreadContext or parts of it volatile slows down the applications as a whole. return ctx;
* So, we opted to use the hacky solution and disable optimizations for this single function.
*/
void ExecuteFunctionCtx(ThreadCall call, Registers &funcRegs, ThreadContext *ctx) __attribute__ ((optnone)) {
ctx->threadCall = 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;
} }
void NCE::ExecuteFunction(ThreadCall call, Registers &funcRegs, std::shared_ptr<kernel::type::KThread> &thread) { void NCE::SignalHandler(int signal, const siginfo &info, const ucontext &context) {
ExecuteFunctionCtx(call, funcRegs, reinterpret_cast<ThreadContext *>(thread->ctxMemory->kernel.ptr));
} }
void NCE::ExecuteFunction(ThreadCall call, Registers &funcRegs) { void NCE::SignalHandler(int signal, siginfo *info, void *context) {
if (state.process->status == kernel::type::KProcess::Status::Exiting) (GetContext()->nce)->SignalHandler(signal, *info, *reinterpret_cast<ucontext*>(context));
throw exception("Executing function on Exiting process");
auto thread{state.thread ? state.thread : state.process->threads.at(state.process->pid)};
ExecuteFunctionCtx(call, funcRegs, reinterpret_cast<ThreadContext *>(thread->ctxMemory->kernel.ptr));
}
void NCE::WaitThreadInit(std::shared_ptr<kernel::type::KThread> &thread) __attribute__ ((optnone)) {
auto ctx{reinterpret_cast<ThreadContext *>(thread->ctxMemory->kernel.ptr)};
while (ctx->state == ThreadState::NotReady);
}
void NCE::StartThread(u64 entryArg, u32 handle, std::shared_ptr<kernel::type::KThread> &thread) {
auto ctx{reinterpret_cast<ThreadContext *>(thread->ctxMemory->kernel.ptr)};
while (ctx->state != ThreadState::WaitInit);
ctx->tpidrroEl0 = reinterpret_cast<u64>(thread->tls);
ctx->registers.x0 = entryArg;
ctx->registers.x1 = handle;
ctx->state = ThreadState::WaitRun;
state.logger->Debug("Starting kernel thread for guest thread: {}", thread->tid);
threadMap[thread->tid] = std::make_shared<std::thread>(&NCE::KernelThread, this, thread->tid);
} }
void NCE::ThreadTrace(u16 instructionCount, ThreadContext *ctx) { void NCE::ThreadTrace(u16 instructionCount, ThreadContext *ctx) {
@ -170,7 +135,7 @@ namespace skyline {
ctx = ctx ? ctx : state.ctx; ctx = ctx ? ctx : state.ctx;
if (instructionCount) { if (instructionCount) {
auto offset{ctx->pc - ((instructionCount - 2) * sizeof(u32))}; auto offset{ctx->pc - (instructionCount * sizeof(u32)) + (2 * sizeof(u32))};
span instructions(reinterpret_cast<u32 *>(offset), instructionCount); span instructions(reinterpret_cast<u32 *>(offset), instructionCount);
for (auto &instruction : instructions) { for (auto &instruction : instructions) {
@ -186,8 +151,8 @@ namespace skyline {
} }
} }
if (ctx->faultAddress) //if (ctx->faultAddress)
regStr += fmt::format("\nFault Address: 0x{:X}", ctx->faultAddress); // regStr += fmt::format("\nFault Address: 0x{:X}", ctx->faultAddress);
if (ctx->sp) if (ctx->sp)
regStr += fmt::format("\nStack Pointer: 0x{:X}", ctx->sp); regStr += fmt::format("\nStack Pointer: 0x{:X}", ctx->sp);

View File

@ -24,50 +24,20 @@ namespace skyline {
public: public:
NCE(DeviceState &state); NCE(DeviceState &state);
/**
* @brief The destructor for NCE, this calls join() on all the threads
*/
~NCE(); ~NCE();
/**
* @brief The main event loop of the program
*/
void Execute(); void Execute();
/** void SignalHandler(int signal, const siginfo &info, const ucontext &ucontext);
* @brief Execute any arbitrary function on a specific child thread
* @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 the child process * @brief A delegator to the real signal handler after restoring context
* @param call The specific call to execute
* @param funcRegs A set of registers to run the function
*/ */
void ExecuteFunction(ThreadCall call, Registers &funcRegs); static void SignalHandler(int signal, siginfo *info, void *context);
/**
* @brief Waits till a thread is ready to execute commands
* @param thread The KThread to wait for initialization
*/
void WaitThreadInit(std::shared_ptr<kernel::type::KThread> &thread);
/**
* @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 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 StartThread(u64 entryArg, u32 handle, std::shared_ptr<kernel::type::KThread> &thread);
/** /**
* @brief Prints out a trace and the CPU context * @brief Prints out a trace and the CPU context
* @param instructionCount The amount of previous instructions to print (Can be 0) * @param instructionCount The amount of previous instructions to print (Can be 0)
* @param ctx The ThreadContext of the thread to log
*/ */
void ThreadTrace(u16 instructionCount = 10, ThreadContext *ctx = nullptr); void ThreadTrace(u16 instructionCount = 10, ThreadContext *ctx = nullptr);

View File

@ -22,7 +22,6 @@ SaveCtx:
STP X26, X27, [LR, #224] STP X26, X27, [LR, #224]
STP X28, X29, [LR, #240] STP X28, X29, [LR, #240]
LDR LR, [SP], #16 LDR LR, [SP], #16
DSB ST
RET RET
.global LoadCtx .global LoadCtx

View File

@ -99,6 +99,7 @@ namespace skyline::guest {
* @note Do not use any functions that cannot be inlined from this, as this function is placed at an arbitrary address in the guest. In addition, do not use any static variables or globals as the .bss section is not copied into the guest. * @note Do not use any functions that cannot be inlined from this, as this function is placed at an arbitrary address in the guest. In addition, do not use any static variables or globals as the .bss section is not copied into the guest.
*/ */
void SvcHandler(u64 pc, u16 svc) { void SvcHandler(u64 pc, u16 svc) {
/*
volatile ThreadContext *ctx; volatile ThreadContext *ctx;
asm("MRS %0, TPIDR_EL0":"=r"(ctx)); asm("MRS %0, TPIDR_EL0":"=r"(ctx));
@ -206,6 +207,7 @@ namespace skyline::guest {
} }
ctx->state = ThreadState::Running; ctx->state = ThreadState::Running;
*/
} }
[[noreturn]] void Exit(int) { [[noreturn]] void Exit(int) {
@ -217,6 +219,7 @@ namespace skyline::guest {
} }
[[noreturn]] void SignalHandler(int signal, siginfo_t *info, ucontext_t *ucontext) { [[noreturn]] void SignalHandler(int signal, siginfo_t *info, ucontext_t *ucontext) {
/*
volatile ThreadContext *ctx; volatile ThreadContext *ctx;
asm("MRS %0, TPIDR_EL0":"=r"(ctx)); asm("MRS %0, TPIDR_EL0":"=r"(ctx));
@ -234,9 +237,11 @@ namespace skyline::guest {
if (ctx->state == ThreadState::WaitRun) if (ctx->state == ThreadState::WaitRun)
Exit(0); Exit(0);
} }
*/
} }
void GuestEntry(u64 address) { void GuestEntry(u64 address) {
/*
volatile ThreadContext *ctx; volatile ThreadContext *ctx;
asm("MRS %0, TPIDR_EL0":"=r"(ctx)); asm("MRS %0, TPIDR_EL0":"=r"(ctx));
@ -270,90 +275,6 @@ namespace skyline::guest {
*(src++) = *(dest++); *(src++) = *(dest++);
} }
} }
*/
struct sigaction sigact{
.sa_sigaction = reinterpret_cast<void (*)(int, struct siginfo *, void *)>(reinterpret_cast<void *>(SignalHandler)),
.sa_flags = SA_SIGINFO,
};
for (int signal : {SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV})
sigaction(signal, &sigact, nullptr);
sigact = {
.sa_handler = Exit,
};
sigaction(SIGTERM, &sigact, nullptr);
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"
"MSR FPSR, XZR\n\t"
"MSR FPCR, XZR\n\t"
"DUP V0.16B, WZR\n\t"
"DUP V1.16B, WZR\n\t"
"DUP V2.16B, WZR\n\t"
"DUP V3.16B, WZR\n\t"
"DUP V4.16B, WZR\n\t"
"DUP V5.16B, WZR\n\t"
"DUP V6.16B, WZR\n\t"
"DUP V7.16B, WZR\n\t"
"DUP V8.16B, WZR\n\t"
"DUP V9.16B, WZR\n\t"
"DUP V10.16B, WZR\n\t"
"DUP V11.16B, WZR\n\t"
"DUP V12.16B, WZR\n\t"
"DUP V13.16B, WZR\n\t"
"DUP V14.16B, WZR\n\t"
"DUP V15.16B, WZR\n\t"
"DUP V16.16B, WZR\n\t"
"DUP V17.16B, WZR\n\t"
"DUP V18.16B, WZR\n\t"
"DUP V19.16B, WZR\n\t"
"DUP V20.16B, WZR\n\t"
"DUP V21.16B, WZR\n\t"
"DUP V22.16B, WZR\n\t"
"DUP V23.16B, WZR\n\t"
"DUP V24.16B, WZR\n\t"
"DUP V25.16B, WZR\n\t"
"DUP V26.16B, WZR\n\t"
"DUP V27.16B, WZR\n\t"
"DUP V28.16B, WZR\n\t"
"DUP V29.16B, WZR\n\t"
"DUP V30.16B, WZR\n\t"
"DUP V31.16B, WZR\n\t"
"RET"::"r"(address), "r"(ctx->registers.x0), "r"(ctx->registers.x1) : "x0", "x1", "lr");
__builtin_unreachable();
} }
} }

View File

@ -16,12 +16,6 @@ namespace skyline {
constexpr size_t SvcHandlerSize{400 * sizeof(u32)}; //!< The size of the SvcHandler (Debug) function in 32-bit ARMv8 instructions constexpr size_t SvcHandlerSize{400 * sizeof(u32)}; //!< The size of the SvcHandler (Debug) function in 32-bit ARMv8 instructions
#endif #endif
/**
* @brief The entry point for all guest threads
* @param address The address of the actual thread entry point
*/
void GuestEntry(u64 address);
/** /**
* @brief Saves the context from CPU registers into TLS * @brief Saves the context from CPU registers into TLS
*/ */

View File

@ -122,38 +122,17 @@ namespace skyline {
}; };
}; };
enum class ThreadState : u8 { class NCE;
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
GuestCrash = 6, //!< This is a notification to the kernel that the guest has crashed
};
/** /**
* @brief The functions that can be run on the guest process * @brief The context of a thread during kernel calls, it is stored for each thread
*/
enum class ThreadCall : u8 {
Syscall = 1, //!< A linux syscall needs to be called from the guest
Memcopy = 2, //!< To copy memory from one location to another
Clone = 3, //!< Use the clone syscall to create a new thread
};
/**
* @brief The context of a thread during kernel calls, it is stored in TLS on each guest thread
*/ */
struct ThreadContext { struct ThreadContext {
ThreadState state; //!< The state of the guest u8* pc; //!< The program counter on the guest
ThreadCall threadCall; //!< The function to run in the guest process u8* sp; //!< The stack pointer on the guest
u16 svc; //!< The SVC ID of the current kernel call
u32 signal; //!< The signal caught by the guest process
u64 pc; //!< The program counter register on the guest
Registers registers; //!< The general purpose registers on the guest Registers registers; //!< The general purpose registers on the guest
u64 tpidrroEl0; //!< The value for TPIDRRO_EL0 for the current thread u8* tpidrroEl0; //!< The value for TPIDRRO_EL0 for the current thread
u64 tpidrEl0; //!< The value for TPIDR_EL0 for the current thread u8* tpidrEl0; //!< The value for TPIDR_EL0 for the current thread
u64 faultAddress; //!< The address a fault has occurred at during guest crash NCE* nce; //!< An instance of the NCE class, used by trampoline functions to call class methods
u64 sp; //!< The current location of the stack pointer set during guest crash
}; };
} }

View File

@ -3,7 +3,6 @@
#include "nce.h" #include "nce.h"
#include "nce/guest.h" #include "nce/guest.h"
#include "kernel/memory.h"
#include "kernel/types/KProcess.h" #include "kernel/types/KProcess.h"
#include "vfs/os_backing.h" #include "vfs/os_backing.h"
#include "loader/nro.h" #include "loader/nro.h"
@ -13,51 +12,33 @@
#include "os.h" #include "os.h"
namespace skyline::kernel { namespace skyline::kernel {
OS::OS(std::shared_ptr<JvmManager> &jvmManager, std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings, const std::string &appFilesPath) : state(this, process, jvmManager, settings, logger), memory(state), serviceManager(state), appFilesPath(appFilesPath) {} OS::OS(std::shared_ptr<JvmManager> &jvmManager, std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings, const std::string &appFilesPath) : state(this, process, jvmManager, settings, logger), serviceManager(state), appFilesPath(appFilesPath) {}
void OS::Execute(int romFd, loader::RomFormat romType) { void OS::Execute(int romFd, loader::RomFormat romType) {
auto romFile{std::make_shared<vfs::OsBacking>(romFd)}; auto romFile{std::make_shared<vfs::OsBacking>(romFd)};
auto keyStore{std::make_shared<crypto::KeyStore>(appFilesPath)}; auto keyStore{std::make_shared<crypto::KeyStore>(appFilesPath)};
if (romType == loader::RomFormat::NRO) { if (romType == loader::RomFormat::NRO)
state.loader = std::make_shared<loader::NroLoader>(romFile); state.loader = std::make_shared<loader::NroLoader>(romFile);
} else if (romType == loader::RomFormat::NSO) { else if (romType == loader::RomFormat::NSO)
state.loader = std::make_shared<loader::NsoLoader>(romFile); state.loader = std::make_shared<loader::NsoLoader>(romFile);
} else if (romType == loader::RomFormat::NCA) { else if (romType == loader::RomFormat::NCA)
state.loader = std::make_shared<loader::NcaLoader>(romFile, keyStore); state.loader = std::make_shared<loader::NcaLoader>(romFile, keyStore);
} else if (romType == loader::RomFormat::NSP) { else if (romType == loader::RomFormat::NSP)
state.loader = std::make_shared<loader::NspLoader>(romFile, keyStore); state.loader = std::make_shared<loader::NspLoader>(romFile, keyStore);
} else { else
throw exception("Unsupported ROM extension."); throw exception("Unsupported ROM extension.");
}
process = CreateProcess(constant::BaseAddress, 0, constant::DefStackSize); process = std::make_shared<kernel::type::KProcess>(state);
state.loader->LoadProcessData(process, state); state.loader->LoadProcessData(process, state);
process->InitializeMemory(); process->InitializeHeap();
process->threads.at(process->pid)->Start(); // The kernel itself is responsible for starting the main thread process->CreateThread(reinterpret_cast<void*>(constant::BaseAddress))->Start();
state.nce->Execute(); state.nce->Execute();
} }
std::shared_ptr<type::KProcess> OS::CreateProcess(u64 entry, u64 argument, size_t stackSize) {
auto stack{std::make_shared<type::KSharedMemory>(state, stackSize, memory::states::Stack)};
stack->guest = stack->kernel;
if (mprotect(stack->guest.ptr, PAGE_SIZE, PROT_NONE))
throw exception("Failed to create guard pages");
auto tlsMem{std::make_shared<type::KSharedMemory>(state, (sizeof(ThreadContext) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1), memory::states::Reserved)};
tlsMem->guest = tlsMem->kernel;
auto pid{clone(reinterpret_cast<int (*)(void *)>(&guest::GuestEntry), stack->guest.ptr + stackSize, CLONE_FILES | CLONE_FS | CLONE_SETTLS | SIGCHLD, reinterpret_cast<void *>(entry), nullptr, tlsMem->guest.ptr)};
if (pid == -1)
throw exception("Call to clone() has failed: {}", strerror(errno));
state.logger->Debug("Successfully created process with PID: {}", pid);
return std::make_shared<kernel::type::KProcess>(state, pid, entry, stack, tlsMem);
}
void OS::KillThread(pid_t pid) { void OS::KillThread(pid_t pid) {
/*
if (process->pid == pid) { if (process->pid == pid) {
state.logger->Debug("Killing process with PID: {}", pid); state.logger->Debug("Killing process with PID: {}", pid);
for (auto &thread: process->threads) for (auto &thread: process->threads)
@ -66,5 +47,6 @@ namespace skyline::kernel {
state.logger->Debug("Killing thread with TID: {}", pid); state.logger->Debug("Killing thread with TID: {}", pid);
process->threads.at(pid)->Kill(); process->threads.at(pid)->Kill();
} }
*/
} }
} }

View File

@ -3,7 +3,6 @@
#pragma once #pragma once
#include "kernel/memory.h"
#include "loader/loader.h" #include "loader/loader.h"
#include "services/serviceman.h" #include "services/serviceman.h"
@ -16,7 +15,6 @@ namespace skyline::kernel {
DeviceState state; DeviceState state;
std::shared_ptr<type::KProcess> process; std::shared_ptr<type::KProcess> process;
service::ServiceManager serviceManager; service::ServiceManager serviceManager;
MemoryManager memory;
std::string appFilesPath; //!< The full path to the app's files directory std::string appFilesPath; //!< The full path to the app's files directory
/** /**

View File

@ -8,7 +8,7 @@ namespace skyline::service::am {
IWindowController::IWindowController(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {} IWindowController::IWindowController(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {}
Result IWindowController::GetAppletResourceUserId(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IWindowController::GetAppletResourceUserId(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
response.Push(static_cast<u64>(state.process->pid)); response.Push(static_cast<u64>(0));
return {}; return {};
} }