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/texture.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_device.cpp
${source_DIR}/skyline/input/touch.cpp

View File

@ -28,12 +28,14 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
fps = 0;
frametime = 0;
/*
std::signal(SIGTERM, signalHandler);
std::signal(SIGSEGV, signalHandler);
std::signal(SIGINT, signalHandler);
std::signal(SIGILL, signalHandler);
std::signal(SIGABRT, signalHandler);
std::signal(SIGFPE, signalHandler);
*/
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 {
// Memory
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
constexpr u16 HandheldResolutionW{1280}; //!< The width component of the handheld resolution
constexpr u16 HandheldResolutionH{720}; //!< The height component of the handheld resolution
@ -71,16 +71,18 @@ namespace skyline {
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
* @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
*/
template <class T>
constexpr T VoidCast(T item) {
return item;
}
template <class T>
constexpr const void* VoidCast(T* ptr) {
return ptr;
template<class T>
constexpr auto FmtCast(T object) {
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);
else
return reinterpret_cast<const void *>(object);
else
return object;
}
}
@ -94,7 +96,7 @@ namespace skyline {
* @param args The arguments based on format_str
*/
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 {
@ -444,7 +446,7 @@ namespace skyline {
template<typename S, typename... Args>
inline void Error(const S &formatStr, Args &&... args) {
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>
inline void Warn(const S &formatStr, Args &&... args) {
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>
inline void Info(const S &formatStr, Args &&... args) {
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>
inline void Debug(const S &formatStr, Args &&... args) {
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);
};
class NCE;
class JvmManager;
namespace 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;
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 {
IpcRequest::IpcRequest(bool isDomain, const DeviceState &state) : isDomain(isDomain) {
auto tls{state.thread->tls};
auto tls{state.ctx->tpidrroEl0};
u8 *pointer{tls};
header = reinterpret_cast<CommandHeader *>(pointer);
@ -64,7 +64,7 @@ namespace skyline::kernel::ipc {
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
pointer += padding;
@ -129,7 +129,7 @@ namespace skyline::kernel::ipc {
IpcResponse::IpcResponse(const DeviceState &state) : state(state) {}
void IpcResponse::WriteResponse(bool isDomain) {
auto tls{state.thread->tls};
auto tls{state.ctx->tpidrroEl0};
u8 *pointer{tls};
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
pointer += padding;

View File

@ -7,8 +7,7 @@
namespace skyline::kernel {
MemoryManager::MemoryManager(const DeviceState &state) : state(state) {}
void MemoryManager::InitializeRegions(u8* codeStart, u64 size, memory::AddressSpaceType type) {
u64 address{reinterpret_cast<u64>(codeStart)};
void MemoryManager::InitializeVmm(memory::AddressSpaceType type) {
switch (type) {
case memory::AddressSpaceType::AddressSpace32Bit:
throw exception("32-bit address spaces are not supported");
@ -18,6 +17,32 @@ namespace skyline::kernel {
addressSpace.size = 1UL << 36;
base.address = constant::BaseAddress;
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.size = 0x78000000;
if (code.address > address || (code.size - (address - code.address)) < size)
@ -33,11 +58,7 @@ namespace skyline::kernel {
break;
}
case memory::AddressSpaceType::AddressSpace39Bit: {
addressSpace.address = 0;
addressSpace.size = 1UL << 39;
base.address = constant::BaseAddress;
base.size = 0x7FF8000000;
case 1UL << 39: {
code.address = util::AlignDown(address, 0x200000);
code.size = util::AlignUp(address + size, 0x200000) - code.address;
alias.address = code.address + code.size;
@ -50,13 +71,10 @@ namespace skyline::kernel {
tlsIo.size = 0x1000000000;
break;
}
}
chunks = {ChunkDescriptor{
.ptr = reinterpret_cast<u8*>(base.address),
.size = base.size,
.state = memory::states::Unmapped,
}};
default:
throw exception("Regions initialized without VMM initialization");
}
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);
@ -95,7 +113,7 @@ namespace skyline::kernel {
}
}
std::optional<ChunkDescriptor> MemoryManager::Get(void* ptr) {
std::optional<ChunkDescriptor> MemoryManager::Get(void *ptr) {
std::shared_lock lock(mutex);
auto chunk{std::upper_bound(chunks.begin(), chunks.end(), reinterpret_cast<u8 *>(ptr), [](const u8 *ptr, const ChunkDescriptor &chunk) -> bool { return ptr < chunk.ptr; })};

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
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
@ -218,16 +218,17 @@ namespace skyline {
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);
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
*/
size_t GetProgramSize();

View File

@ -52,7 +52,7 @@ namespace skyline::kernel::svc {
return;
}
auto chunk{state.os->memory.Get(pointer)};
auto chunk{state.process->memory.Get(pointer)};
if (!chunk) {
state.ctx->registers.w0 = result::InvalidAddress;
state.logger->Warn("svcSetMemoryAttribute: Cannot find memory region: 0x{:X}", pointer);
@ -69,7 +69,7 @@ namespace skyline::kernel::svc {
newChunk.ptr = pointer;
newChunk.size = size;
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.ctx->registers.w0 = Result{};
@ -92,14 +92,14 @@ namespace skyline::kernel::svc {
return;
}
auto stack{state.os->memory.stack};
auto stack{state.process->memory.stack};
if (!stack.IsInside(destination)) {
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);
return;
}
auto chunk{state.os->memory.Get(source)};
auto chunk{state.process->memory.Get(source)};
if (!chunk) {
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);
@ -140,15 +140,15 @@ namespace skyline::kernel::svc {
return;
}
auto stack{state.os->memory.stack};
auto stack{state.process->memory.stack};
if (!stack.IsInside(source)) {
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);
return;
}
auto sourceChunk{state.os->memory.Get(source)};
auto destChunk{state.os->memory.Get(destination)};
auto sourceChunk{state.process->memory.Get(source)};
auto destChunk{state.process->memory.Get(destination)};
if (!sourceChunk || !destChunk) {
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);
@ -183,7 +183,7 @@ namespace skyline::kernel::svc {
memory::MemoryInfo memInfo{};
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) {
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' : '-');
} 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 = {
.address = addressSpaceEnd,
@ -215,24 +215,28 @@ namespace skyline::kernel::svc {
}
void ExitProcess(DeviceState &state) {
state.logger->Debug("svcExitProcess: Exiting current process: {}", state.process->pid);
state.os->KillThread(state.process->pid);
state.logger->Debug("svcExitProcess: Exiting process");
//state.os->KillThread(state.process->pid);
}
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 stackTop{state.ctx->registers.x3};
auto stackTop{reinterpret_cast<u8*>(state.ctx->registers.x3)};
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.logger->Warn("svcCreateThread: 'priority' invalid: {}", priority);
return;
}
auto thread{state.process->CreateThread(entryAddress, entryArgument, stackTop, priority)};
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);
auto stack{state.process->GetMemoryObject(stackTop)};
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.w0 = Result{};
@ -242,7 +246,7 @@ namespace skyline::kernel::svc {
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->tid);
state.logger->Debug("svcStartThread: Starting thread: 0x{:X}, PID: {}", handle, thread->id);
thread->Start();
state.ctx->registers.w0 = Result{};
} catch (const std::exception &) {
@ -252,8 +256,8 @@ namespace skyline::kernel::svc {
}
void ExitThread(DeviceState &state) {
state.logger->Debug("svcExitThread: Exiting current thread: {}", state.thread->tid);
state.os->KillThread(state.thread->tid);
state.logger->Debug("svcExitThread: Exiting current thread: {}", state.thread->id);
state.os->KillThread(state.thread->id);
}
void SleepThread(DeviceState &state) {
@ -389,7 +393,7 @@ namespace skyline::kernel::svc {
void ResetSignal(DeviceState &state) {
auto handle{state.ctx->registers.w0};
try {
auto &object{state.process->handles.at(handle)};
auto object{state.process->GetHandle(handle)};
switch (object->objectType) {
case type::KType::KEvent:
std::static_pointer_cast<type::KEvent>(object)->ResetSignal();
@ -431,7 +435,7 @@ namespace skyline::kernel::svc {
for (const auto &handle : waitHandles) {
handleStr += fmt::format("* 0x{:X}\n", handle);
auto object{state.process->handles.at(handle)};
auto object{state.process->GetHandle(handle)};
switch (object->objectType) {
case type::KType::KProcess:
case type::KType::KThread:
@ -612,9 +616,9 @@ namespace skyline::kernel::svc {
pid_t pid{};
if (handle != threadSelf)
pid = state.process->GetHandle<type::KThread>(handle)->tid;
pid = state.process->GetHandle<type::KThread>(handle)->id;
else
pid = state.thread->tid;
pid = state.thread->id;
state.logger->Debug("svcGetThreadId: Handle: 0x{:X}, PID: {}", handle, pid);
@ -650,19 +654,19 @@ namespace skyline::kernel::svc {
break;
case constant::infoState::AliasRegionBaseAddr:
out = state.os->memory.alias.address;
out = state.process->memory.alias.address;
break;
case constant::infoState::AliasRegionSize:
out = state.os->memory.alias.size;
out = state.process->memory.alias.size;
break;
case constant::infoState::HeapRegionBaseAddr:
out = state.os->memory.heap.address;
out = state.process->memory.heap.address;
break;
case constant::infoState::HeapRegionSize:
out = state.os->memory.heap.size;
out = state.process->memory.heap.size;
break;
case constant::infoState::TotalMemoryAvailable:
@ -670,23 +674,23 @@ namespace skyline::kernel::svc {
break;
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;
case constant::infoState::AddressSpaceBaseAddr:
out = state.os->memory.base.address;
out = state.process->memory.base.address;
break;
case constant::infoState::AddressSpaceSize:
out = state.os->memory.base.size;
out = state.process->memory.base.size;
break;
case constant::infoState::StackRegionBaseAddr:
out = state.os->memory.stack.address;
out = state.process->memory.stack.address;
break;
case constant::infoState::StackRegionSize:
out = state.os->memory.stack.size;
out = state.process->memory.stack.size;
break;
case constant::infoState::PersonalMmHeapSize:
@ -694,7 +698,7 @@ namespace skyline::kernel::svc {
break;
case constant::infoState::PersonalMmHeapUsage:
out = state.process->heap->size + constant::DefStackSize;
out = state.process->heap->size + constant::DefaultStackSize;
break;
case constant::infoState::TotalMemoryAvailableWithoutMmHeap:
@ -702,7 +706,7 @@ namespace skyline::kernel::svc {
break;
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;
case constant::infoState::UserExceptionContextAddr:

View File

@ -14,18 +14,16 @@ namespace skyline::kernel::type {
if (ptr && !util::PageAligned(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));
if (ptr == MAP_FAILED)
throw exception("An occurred while mapping private memory: {}", strerror(errno));
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 (this->ptr == MAP_FAILED)
throw exception("An occurred while mapping private memory: {} with {} @ 0x{:X}", strerror(errno), ptr, size);
state.os->memory.InsertChunk(ChunkDescriptor{
.ptr = ptr,
state.process->memory.InsertChunk(ChunkDescriptor{
.ptr = this->ptr,
.size = size,
.permission = permission,
.state = memState,
});
this->ptr = ptr;
}
void KPrivateMemory::Resize(size_t nSize) {
@ -34,13 +32,13 @@ namespace skyline::kernel::type {
throw exception("An occurred while resizing private memory: {}", strerror(errno));
if (nSize < size) {
state.os->memory.InsertChunk(ChunkDescriptor{
state.process->memory.InsertChunk(ChunkDescriptor{
.ptr = ptr + nSize,
.size = size - nSize,
.state = memory::states::Unmapped,
});
} else if (size < nSize) {
state.os->memory.InsertChunk(ChunkDescriptor{
state.process->memory.InsertChunk(ChunkDescriptor{
.ptr = ptr + size,
.size = nSize - size,
.permission = permission,
@ -59,7 +57,7 @@ namespace skyline::kernel::type {
if (memState.value == memory::states::CodeStatic.value && permission.w)
memState = memory::states::CodeMutable;
state.os->memory.InsertChunk(ChunkDescriptor{
state.process->memory.InsertChunk(ChunkDescriptor{
.ptr = ptr,
.size = size,
.permission = permission,
@ -69,7 +67,7 @@ namespace skyline::kernel::type {
KPrivateMemory::~KPrivateMemory() {
munmap(ptr, size);
state.os->memory.InsertChunk(ChunkDescriptor{
state.process->memory.InsertChunk(ChunkDescriptor{
.ptr = ptr,
.size = size,
.state = memory::states::Unmapped,

View File

@ -1,110 +1,64 @@
// SPDX-License-Identifier: MPL-2.0
// 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 <os.h>
#include "KProcess.h"
namespace skyline::kernel::type {
KProcess::TlsPage::TlsPage(u8* ptr) : ptr(ptr) {}
KProcess::WaitStatus::WaitStatus(i8 priority, KHandle handle) : priority(priority), handle(handle) {}
u8* KProcess::TlsPage::ReserveSlot() {
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() {
if (Full())
throw exception("Trying to get TLS slot from full page");
slot[index] = true;
return Get(index++); // ++ on right will cause increment after evaluation of expression
throw exception("Trying to reserve TLS slot in full page");
return Get(index++);
}
u8* KProcess::TlsPage::Get(u8 slotNo) {
if (slotNo >= constant::TlsSlots)
u8 *KProcess::TlsPage::Get(u8 index) {
if (index >= constant::TlsSlots)
throw exception("TLS slot is out of range");
return ptr + (constant::TlsSlotSize * slotNo);
return memory->ptr + (constant::TlsSlotSize * index);
}
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)
if (!tlsPage->Full())
return tlsPage->ReserveSlot();
u8* ptr;
if (tlsPages.empty()) {
auto region{state.os->memory.tlsIo};
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
u8 *ptr = tlsPages.empty() ? reinterpret_cast<u8 *>(state.process->memory.tlsIo.address) : ((*(tlsPages.end() - 1))->memory->ptr + PAGE_SIZE);
auto tlsPage{std::make_shared<TlsPage>(std::make_shared<KPrivateMemory>(state, ptr, PAGE_SIZE, memory::Permission(true, true, false), memory::states::ThreadLocal))};
tlsPages.push_back(tlsPage);
tlsPage->ReserveSlot(); // User-mode exception handling
return tlsPage->ReserveSlot();
}
void KProcess::InitializeMemory() {
constexpr size_t DefHeapSize{0x200000}; // The default amount of heap
heap = NewHandle<KPrivateMemory>(reinterpret_cast<u8*>(state.os->memory.heap.address), DefHeapSize, memory::Permission{true, true, false}, memory::states::Heap).item;
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;
std::shared_ptr<KThread> KProcess::CreateThread(void *entry, u64 argument, i8 priority, const std::shared_ptr<KPrivateMemory> &stack) {
auto thread{NewHandle<KThread>(this, threads.size(), entry, argument, priority, stack).item};
threads.push_back(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++) {
auto& object{handles[index]};
auto &object{handles[index]};
switch (object->objectType) {
case type::KType::KPrivateMemory:
case type::KType::KSharedMemory:
@ -121,7 +75,7 @@ namespace skyline::kernel::type {
return std::nullopt;
}
bool KProcess::MutexLock(u32* mutex, KHandle owner) {
bool KProcess::MutexLock(u32 *mutex, KHandle owner) {
std::unique_lock lock(mutexLock);
auto &mtxWaiters{mutexes[reinterpret_cast<u64>(mutex)]};
@ -159,7 +113,7 @@ namespace skyline::kernel::type {
return true;
}
bool KProcess::MutexUnlock(u32* mutex) {
bool KProcess::MutexUnlock(u32 *mutex) {
std::unique_lock lock(mutexLock);
auto &mtxWaiters{mutexes[reinterpret_cast<u64>(mutex)]};
@ -186,7 +140,7 @@ namespace skyline::kernel::type {
return true;
}
bool KProcess::ConditionalVariableWait(void* conditional, u32* mutex, u64 timeout) {
bool KProcess::ConditionalVariableWait(void *conditional, u32 *mutex, u64 timeout) {
std::unique_lock lock(conditionalLock);
auto &condWaiters{conditionals[reinterpret_cast<u64>(conditional)]};
@ -227,7 +181,7 @@ namespace skyline::kernel::type {
return !timedOut;
}
void KProcess::ConditionalVariableSignal(void* conditional, u64 amount) {
void KProcess::ConditionalVariableSignal(void *conditional, u64 amount) {
std::unique_lock condLock(conditionalLock);
auto &condWaiters{conditionals[reinterpret_cast<u64>(conditional)]};

View File

@ -4,8 +4,8 @@
#pragma once
#include <list>
#include <kernel/memory.h>
#include "KThread.h"
#include "KPrivateMemory.h"
#include "KTransferMemory.h"
#include "KSession.h"
#include "KEvent.h"
@ -20,11 +20,28 @@ namespace skyline {
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 {
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)
@ -33,44 +50,37 @@ namespace skyline {
* @url https://switchbrew.org/wiki/Thread_Local_Storage
*/
struct TlsPage {
u8* ptr;
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();
/**
* @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);
u8* Get(u8 index);
/**
* @brief Returns boolean on if the TLS page has free slots or not
* @return If the whole page is full or not
*/
bool Full();
};
/**
* @return The address of a free TLS slot
*/
u8* GetTlsSlot();
/**
* @brief Initializes heap and the initial TLS page
*/
void InitializeMemory();
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
@ -82,96 +92,40 @@ namespace skyline {
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
* @tparam objectClass The class of the kernel object to create
* @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>
HandleOut<objectClass> NewHandle(objectArgs... args) {
std::unique_lock lock(handleMutex);
std::shared_ptr<objectClass> item;
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
item = std::make_shared<objectClass>(state, args...);
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
* @param item The item to insert
* @return The handle of the corresponding item in the handle table
*/
template<typename objectClass>
KHandle InsertItem(std::shared_ptr<objectClass> &item) {
std::unique_lock lock(handleMutex);
handles.push_back(std::static_pointer_cast<KObject>(item));
return handleIndex++;
return static_cast<KHandle>((constant::BaseHandleIndex + handles.size()) - 1);
}
/**
* @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>
template<typename objectClass = KObject>
std::shared_ptr<objectClass> GetHandle(KHandle handle) {
std::shared_lock lock(handleMutex);
KType objectType;
if constexpr(std::is_same<objectClass, 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
* @param address The address to look for

View File

@ -26,12 +26,12 @@ namespace skyline::kernel::type {
if (ptr && !util::PageAligned(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)
throw exception("An error occurred while mapping shared memory in guest");
guest.size = size;
state.os->memory.InsertChunk(ChunkDescriptor{
state.process->memory.InsertChunk(ChunkDescriptor{
.ptr = guest.ptr,
.size = size,
.permission = permission,
@ -51,7 +51,7 @@ namespace skyline::kernel::type {
if (guest.ptr == MAP_FAILED)
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,
.size = size,
.permission = permission,
@ -66,7 +66,7 @@ namespace skyline::kernel::type {
if (guest.Valid()) {
munmap(guest.ptr, guest.size);
state.os->memory.InsertChunk(ChunkDescriptor{
state.process->memory.InsertChunk(ChunkDescriptor{
.ptr = guest.ptr,
.size = guest.size,
.state = memory::states::Unmapped,

View File

@ -1,14 +1,15 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <sys/types.h>
#include <sys/resource.h>
#include <unistd.h>
#include <nce.h>
#include "KThread.h"
#include <os.h>
#include "KProcess.h"
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,
KType::KThread) {
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) {
UpdatePriority(priority);
}
@ -16,30 +17,135 @@ namespace skyline::kernel::type {
Kill();
}
void KThread::Start() {
if (status == Status::Created) {
if (tid == parent->pid)
parent->status = KProcess::Status::Started;
status = Status::Running;
void KThread::StartThread() {
pthread_setname_np(pthread_self(), fmt::format("HOS-{}", id).c_str());
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() {
if (status != Status::Dead) {
status = Status::Dead;
Signal();
if (running) {
running = false;
tgkill(parent->pid, tid, SIGTERM);
Signal();
exit(0);
//tgkill(gettgid(), tid, SIGTERM);
}
}
void KThread::UpdatePriority(i8 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)
throw exception("Couldn't set process priority to {} for PID: {}", priorityValue, tid);
if (setpriority(PRIO_PROCESS, getpid(), priorityValue) == -1)
throw exception("Couldn't set thread priority to {} for #{}", priorityValue, id);
}
}

View File

@ -4,21 +4,11 @@
#pragma once
#include "KSyncObject.h"
#include "KPrivateMemory.h"
#include "KSharedMemory.h"
namespace skyline::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
*/
namespace skyline {
namespace kernel::type {
struct Priority {
i8 low; //!< The low 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
* @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)));
}
@ -36,61 +26,61 @@ namespace skyline::kernel::type {
* @param value The priority value to check for validity
* @return If the supplied priority value is valid
*/
constexpr bool Valid(i8 value) {
constexpr bool Valid(i8 value) const {
return (value >= low) && (value <= high);
}
};
}
public:
enum class Status {
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::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
Priority switchPriority{0, 63}; //!< The range of priorities for the Nintendo Switch
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 {
/**
* @param handle The handle of the current thread
* @param selfTid The TID of this thread
* @param entryPoint The address to start execution at
* @param entryArg An argument to pass to the process on entry
* @param stackTop The top of the stack
* @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
* @brief KThread manages a single thread of execution which is responsible for running guest code and kernel code which is invoked by the guest
*/
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);
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
/**
* @brief Kills the thread and deallocates the memory allocated for stack.
*/
~KThread();
void StartThread();
/**
* @brief Starts this thread process
*/
void Start();
public:
std::atomic<bool> running{false};
std::atomic<bool> cancelSync{false}; //!< This is to flag to a thread to cancel a synchronization call it currently is in
/**
* @brief Kills the thread process
*/
void Kill();
KHandle handle;
size_t id; //!< Index of thread in parent process's KThread vector
/**
* @brief Update the priority level for the process.
* @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.
* @param priority The priority of the thread in Nintendo format
*/
void UpdatePriority(i8 priority);
};
std::shared_ptr<KPrivateMemory> stack;
ThreadContext ctx{};
void* entry;
u64 entryArgument;
i8 priority;
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);
~KThread();
/**
* @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(bool self = false);
void Kill();
/**
* @brief Sets the host priority using setpriority with a rescaled the priority from HOS to Android
* @note It also affects guest scheduler behavior, this isn't purely for host
*/
void UpdatePriority(i8 priority);
};
}
}

View File

@ -29,21 +29,21 @@ namespace skyline::loader {
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
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--
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-
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
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(executable.ro.contents.data(), base + executable.ro.offset, roSize);
std::memcpy(executable.data.contents.data(), base + executable.data.offset, dataSize - executable.bssSize);
std::memcpy(patch.data(), base + patchOffset, patchSize);
std::memcpy(base + executable.text.offset, executable.text.contents.data(), textSize);
std::memcpy(base + executable.ro.offset, executable.ro.contents.data(), roSize);
std::memcpy(base + executable.data.offset, executable.data.contents.data(), dataSize - executable.bssSize);
std::memcpy(base + patchOffset, patch.data(), patchSize);
return {base, patchOffset + patchSize + padding};
}

View File

@ -70,17 +70,10 @@ namespace skyline::loader {
virtual ~Loader() = default;
/**
* @return The icon of the loaded application
*/
virtual std::vector<u8> GetIcon() {
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;
};
}

View File

@ -2,8 +2,7 @@
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <nce.h>
#include <os.h>
#include <kernel/memory.h>
#include <kernel/types/KProcess.h>
#include "nso.h"
#include "nca.h"
@ -21,6 +20,8 @@ namespace skyline::loader {
if (nsoFile == nullptr)
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)};
u64 offset{loadInfo.size};
u8* base{loadInfo.base};
@ -38,7 +39,7 @@ namespace skyline::loader {
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) {

View File

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

View File

@ -18,6 +18,7 @@ extern skyline::GroupMutex JniMtx;
namespace skyline {
void NCE::KernelThread(pid_t thread) {
/*
state.jvm->AttachThread();
try {
state.thread = state.process->threads.at(thread);
@ -81,6 +82,7 @@ namespace skyline {
}
state.jvm->DetachThread();
*/
}
NCE::NCE(DeviceState &state) : state(state) {}
@ -111,55 +113,18 @@ namespace skyline {
}
}
/**
* This function will not work if optimizations are enabled as ThreadContext isn't volatile
* and due to that is not read on every iteration of the while loop.
* However, making ThreadContext or parts of it volatile slows down the applications as a whole.
* 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;
inline ThreadContext *GetContext() {
ThreadContext *ctx;
asm("MRS %0, TPIDR_EL0":"=r"(ctx));
return ctx;
}
void NCE::ExecuteFunction(ThreadCall call, Registers &funcRegs, std::shared_ptr<kernel::type::KThread> &thread) {
ExecuteFunctionCtx(call, funcRegs, reinterpret_cast<ThreadContext *>(thread->ctxMemory->kernel.ptr));
void NCE::SignalHandler(int signal, const siginfo &info, const ucontext &context) {
}
void NCE::ExecuteFunction(ThreadCall call, Registers &funcRegs) {
if (state.process->status == kernel::type::KProcess::Status::Exiting)
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::SignalHandler(int signal, siginfo *info, void *context) {
(GetContext()->nce)->SignalHandler(signal, *info, *reinterpret_cast<ucontext*>(context));
}
void NCE::ThreadTrace(u16 instructionCount, ThreadContext *ctx) {
@ -170,8 +135,8 @@ namespace skyline {
ctx = ctx ? ctx : state.ctx;
if (instructionCount) {
auto offset{ctx->pc - ((instructionCount - 2) * sizeof(u32))};
span instructions(reinterpret_cast<u32*>(offset), instructionCount);
auto offset{ctx->pc - (instructionCount * sizeof(u32)) + (2 * sizeof(u32))};
span instructions(reinterpret_cast<u32 *>(offset), instructionCount);
for (auto &instruction : instructions) {
instruction = __builtin_bswap32(instruction);
@ -186,8 +151,8 @@ namespace skyline {
}
}
if (ctx->faultAddress)
regStr += fmt::format("\nFault Address: 0x{:X}", ctx->faultAddress);
//if (ctx->faultAddress)
// regStr += fmt::format("\nFault Address: 0x{:X}", ctx->faultAddress);
if (ctx->sp)
regStr += fmt::format("\nStack Pointer: 0x{:X}", ctx->sp);

View File

@ -24,50 +24,20 @@ namespace skyline {
public:
NCE(DeviceState &state);
/**
* @brief The destructor for NCE, this calls join() on all the threads
*/
~NCE();
/**
* @brief The main event loop of the program
*/
void Execute();
/**
* @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);
void SignalHandler(int signal, const siginfo &info, const ucontext &ucontext);
/**
* @brief Execute any arbitrary function on the child process
* @param call The specific call to execute
* @param funcRegs A set of registers to run the function
* @brief A delegator to the real signal handler after restoring context
*/
void ExecuteFunction(ThreadCall call, Registers &funcRegs);
/**
* @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);
static void SignalHandler(int signal, siginfo *info, void *context);
/**
* @brief Prints out a trace and the CPU context
* @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);

View File

@ -22,7 +22,6 @@ SaveCtx:
STP X26, X27, [LR, #224]
STP X28, X29, [LR, #240]
LDR LR, [SP], #16
DSB ST
RET
.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.
*/
void SvcHandler(u64 pc, u16 svc) {
/*
volatile ThreadContext *ctx;
asm("MRS %0, TPIDR_EL0":"=r"(ctx));
@ -206,6 +207,7 @@ namespace skyline::guest {
}
ctx->state = ThreadState::Running;
*/
}
[[noreturn]] void Exit(int) {
@ -217,6 +219,7 @@ namespace skyline::guest {
}
[[noreturn]] void SignalHandler(int signal, siginfo_t *info, ucontext_t *ucontext) {
/*
volatile ThreadContext *ctx;
asm("MRS %0, TPIDR_EL0":"=r"(ctx));
@ -234,9 +237,11 @@ namespace skyline::guest {
if (ctx->state == ThreadState::WaitRun)
Exit(0);
}
*/
}
void GuestEntry(u64 address) {
/*
volatile ThreadContext *ctx;
asm("MRS %0, TPIDR_EL0":"=r"(ctx));
@ -270,90 +275,6 @@ namespace skyline::guest {
*(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
#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
*/

View File

@ -122,38 +122,17 @@ namespace skyline {
};
};
enum class ThreadState : u8 {
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
};
class NCE;
/**
* @brief The functions that can be run on the guest process
*/
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
* @brief The context of a thread during kernel calls, it is stored for each thread
*/
struct ThreadContext {
ThreadState state; //!< The state of the guest
ThreadCall threadCall; //!< The function to run in the guest process
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
u8* pc; //!< The program counter on the guest
u8* sp; //!< The stack pointer on the guest
Registers registers; //!< The general purpose registers on the guest
u64 tpidrroEl0; //!< The value for TPIDRRO_EL0 for the current thread
u64 tpidrEl0; //!< The value for TPIDR_EL0 for the current thread
u64 faultAddress; //!< The address a fault has occurred at during guest crash
u64 sp; //!< The current location of the stack pointer set during guest crash
u8* tpidrroEl0; //!< The value for TPIDRRO_EL0 for the current thread
u8* tpidrEl0; //!< The value for TPIDR_EL0 for the current thread
NCE* nce; //!< An instance of the NCE class, used by trampoline functions to call class methods
};
}

View File

@ -3,7 +3,6 @@
#include "nce.h"
#include "nce/guest.h"
#include "kernel/memory.h"
#include "kernel/types/KProcess.h"
#include "vfs/os_backing.h"
#include "loader/nro.h"
@ -13,51 +12,33 @@
#include "os.h"
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) {
auto romFile{std::make_shared<vfs::OsBacking>(romFd)};
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);
} else if (romType == loader::RomFormat::NSO) {
else if (romType == loader::RomFormat::NSO)
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);
} else if (romType == loader::RomFormat::NSP) {
else if (romType == loader::RomFormat::NSP)
state.loader = std::make_shared<loader::NspLoader>(romFile, keyStore);
} else {
else
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);
process->InitializeMemory();
process->threads.at(process->pid)->Start(); // The kernel itself is responsible for starting the main thread
process->InitializeHeap();
process->CreateThread(reinterpret_cast<void*>(constant::BaseAddress))->Start();
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) {
/*
if (process->pid == pid) {
state.logger->Debug("Killing process with PID: {}", pid);
for (auto &thread: process->threads)
@ -66,5 +47,6 @@ namespace skyline::kernel {
state.logger->Debug("Killing thread with TID: {}", pid);
process->threads.at(pid)->Kill();
}
*/
}
}

View File

@ -3,7 +3,6 @@
#pragma once
#include "kernel/memory.h"
#include "loader/loader.h"
#include "services/serviceman.h"
@ -16,7 +15,6 @@ namespace skyline::kernel {
DeviceState state;
std::shared_ptr<type::KProcess> process;
service::ServiceManager serviceManager;
MemoryManager memory;
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) {}
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 {};
}