Implement NPDM, Core Mask SVCs + Fix VMM bug + Introduce Verbose Log Level

This commit is contained in:
◱ PixelyIon 2020-10-21 22:39:35 +05:30 committed by ◱ PixelyIon
parent 324381908b
commit c65c91e1bc
20 changed files with 291 additions and 102 deletions

View File

@ -36,12 +36,18 @@ add_library(skyline SHARED
${source_DIR}/skyline/nce/guest.S
${source_DIR}/skyline/nce.cpp
${source_DIR}/skyline/jvm.cpp
${source_DIR}/skyline/os.cpp
${source_DIR}/skyline/kernel/memory.cpp
${source_DIR}/skyline/kernel/ipc.cpp
${source_DIR}/skyline/kernel/svc.cpp
${source_DIR}/skyline/kernel/types/KProcess.cpp
${source_DIR}/skyline/kernel/types/KThread.cpp
${source_DIR}/skyline/kernel/types/KSharedMemory.cpp
${source_DIR}/skyline/kernel/types/KPrivateMemory.cpp
${source_DIR}/skyline/audio.cpp
${source_DIR}/skyline/audio/track.cpp
${source_DIR}/skyline/audio/resampler.cpp
${source_DIR}/skyline/audio/adpcm_decoder.cpp
${source_DIR}/skyline/crypto/aes_cipher.cpp
${source_DIR}/skyline/crypto/key_store.cpp
${source_DIR}/skyline/gpu.cpp
${source_DIR}/skyline/gpu/macro_interpreter.cpp
${source_DIR}/skyline/gpu/memory_manager.cpp
@ -52,19 +58,21 @@ add_library(skyline SHARED
${source_DIR}/skyline/input/npad.cpp
${source_DIR}/skyline/input/npad_device.cpp
${source_DIR}/skyline/input/touch.cpp
${source_DIR}/skyline/os.cpp
${source_DIR}/skyline/crypto/aes_cipher.cpp
${source_DIR}/skyline/crypto/key_store.cpp
${source_DIR}/skyline/loader/loader.cpp
${source_DIR}/skyline/loader/nro.cpp
${source_DIR}/skyline/loader/nso.cpp
${source_DIR}/skyline/loader/nca.cpp
${source_DIR}/skyline/loader/nsp.cpp
${source_DIR}/skyline/kernel/memory.cpp
${source_DIR}/skyline/kernel/ipc.cpp
${source_DIR}/skyline/kernel/svc.cpp
${source_DIR}/skyline/kernel/types/KProcess.cpp
${source_DIR}/skyline/kernel/types/KThread.cpp
${source_DIR}/skyline/kernel/types/KSharedMemory.cpp
${source_DIR}/skyline/kernel/types/KPrivateMemory.cpp
${source_DIR}/skyline/vfs/os_filesystem.cpp
${source_DIR}/skyline/vfs/partition_filesystem.cpp
${source_DIR}/skyline/vfs/ctr_encrypted_backing.cpp
${source_DIR}/skyline/vfs/rom_filesystem.cpp
${source_DIR}/skyline/vfs/os_backing.cpp
${source_DIR}/skyline/vfs/nacp.cpp
${source_DIR}/skyline/vfs/npdm.cpp
${source_DIR}/skyline/vfs/nca.cpp
${source_DIR}/skyline/services/serviceman.cpp
${source_DIR}/skyline/services/base_service.cpp
${source_DIR}/skyline/services/common/parcel.cpp
@ -149,13 +157,6 @@ add_library(skyline SHARED
${source_DIR}/skyline/services/ssl/ISslService.cpp
${source_DIR}/skyline/services/ssl/ISslContext.cpp
${source_DIR}/skyline/services/prepo/IPrepoService.cpp
${source_DIR}/skyline/vfs/os_filesystem.cpp
${source_DIR}/skyline/vfs/partition_filesystem.cpp
${source_DIR}/skyline/vfs/ctr_encrypted_backing.cpp
${source_DIR}/skyline/vfs/rom_filesystem.cpp
${source_DIR}/skyline/vfs/os_backing.cpp
${source_DIR}/skyline/vfs/nacp.cpp
${source_DIR}/skyline/vfs/nca.cpp
)
target_link_libraries(skyline vulkan android fmt tinyxml2 oboe lz4_static mbedtls::mbedcrypto)

View File

@ -141,8 +141,8 @@ namespace skyline {
}
void Logger::Write(LogLevel level, std::string str) {
constexpr std::array<char, 4> levelCharacter{'0', '1', '2', '3'}; // The LogLevel as written out to a file
constexpr std::array<int, 4> levelAlog{ANDROID_LOG_ERROR, ANDROID_LOG_WARN, ANDROID_LOG_INFO, ANDROID_LOG_DEBUG}; // This corresponds to LogLevel and provides it's equivalent for NDK Logging
constexpr std::array<char, 5> levelCharacter{'0', '1', '2', '3', '4'}; // The LogLevel as written out to a file
constexpr std::array<int, 5> levelAlog{ANDROID_LOG_ERROR, ANDROID_LOG_WARN, ANDROID_LOG_INFO, ANDROID_LOG_DEBUG, ANDROID_LOG_VERBOSE}; // This corresponds to LogLevel and provides it's equivalent for NDK Logging
__android_log_write(levelAlog[static_cast<u8>(level)], "emu-cpp", str.c_str());

View File

@ -436,6 +436,7 @@ namespace skyline {
Warn,
Info,
Debug,
Verbose,
};
LogLevel configLevel; //!< The minimum level of logs to write
@ -453,22 +454,11 @@ namespace skyline {
/**
* @brief Writes a header, should only be used for emulation starting and ending
* @param str The value to be written
*/
void WriteHeader(const std::string &str);
/**
* @brief Write a log to the log file
* @param level The level of the log
* @param str The value to be written
*/
void Write(LogLevel level, std::string str);
/**
* @brief Write an error log with libfmt formatting
* @param formatStr The value to be written, with libfmt formatting
* @param args The arguments based on format_str
*/
template<typename S, typename... Args>
inline void Error(const S &formatStr, Args &&... args) {
if (LogLevel::Error <= configLevel) {
@ -476,11 +466,6 @@ namespace skyline {
}
}
/**
* @brief Write a debug log with libfmt formatting
* @param formatStr The value to be written, with libfmt formatting
* @param args The arguments based on format_str
*/
template<typename S, typename... Args>
inline void Warn(const S &formatStr, Args &&... args) {
if (LogLevel::Warn <= configLevel) {
@ -488,11 +473,6 @@ namespace skyline {
}
}
/**
* @brief Write a debug log with libfmt formatting
* @param formatStr The value to be written, with libfmt formatting
* @param args The arguments based on format_str
*/
template<typename S, typename... Args>
inline void Info(const S &formatStr, Args &&... args) {
if (LogLevel::Info <= configLevel) {
@ -500,17 +480,19 @@ namespace skyline {
}
}
/**
* @brief Write a debug log with libfmt formatting
* @param formatStr The value to be written, with libfmt formatting
* @param args The arguments based on format_str
*/
template<typename S, typename... Args>
inline void Debug(const S &formatStr, Args &&... args) {
if (LogLevel::Debug <= configLevel) {
Write(LogLevel::Debug, fmt::format(formatStr, util::FmtCast(args)...));
}
}
template<typename S, typename... Args>
inline void Verbose(const S &formatStr, Args &&... args) {
if (LogLevel::Verbose <= configLevel) {
Write(LogLevel::Verbose, fmt::format(formatStr, util::FmtCast(args)...));
}
}
};
/**

View File

@ -31,7 +31,7 @@ namespace skyline::kernel::ipc {
auto bufX{reinterpret_cast<BufferDescriptorX *>(pointer)};
if (bufX->Pointer()) {
inputBuf.emplace_back(bufX->Pointer(), static_cast<u16>(bufX->size));
state.logger->Debug("Buf X #{}: 0x{:X}, 0x{:X}, #{}", index, bufX->Pointer(), static_cast<u16>(bufX->size), static_cast<u16>(bufX->Counter()));
state.logger->Verbose("Buf X #{}: 0x{:X}, 0x{:X}, #{}", index, bufX->Pointer(), static_cast<u16>(bufX->size), static_cast<u16>(bufX->Counter()));
}
pointer += sizeof(BufferDescriptorX);
}
@ -40,7 +40,7 @@ namespace skyline::kernel::ipc {
auto bufA{reinterpret_cast<BufferDescriptorABW *>(pointer)};
if (bufA->Pointer()) {
inputBuf.emplace_back(bufA->Pointer(), bufA->Size());
state.logger->Debug("Buf A #{}: 0x{:X}, 0x{:X}", index, bufA->Pointer(), static_cast<u64>(bufA->Size()));
state.logger->Verbose("Buf A #{}: 0x{:X}, 0x{:X}", index, bufA->Pointer(), static_cast<u64>(bufA->Size()));
}
pointer += sizeof(BufferDescriptorABW);
}
@ -49,7 +49,7 @@ namespace skyline::kernel::ipc {
auto bufB{reinterpret_cast<BufferDescriptorABW *>(pointer)};
if (bufB->Pointer()) {
outputBuf.emplace_back(bufB->Pointer(), bufB->Size());
state.logger->Debug("Buf B #{}: 0x{:X}, 0x{:X}", index, bufB->Pointer(), static_cast<u64>(bufB->Size()));
state.logger->Verbose("Buf B #{}: 0x{:X}, 0x{:X}", index, bufB->Pointer(), static_cast<u64>(bufB->Size()));
}
pointer += sizeof(BufferDescriptorABW);
}
@ -59,7 +59,7 @@ namespace skyline::kernel::ipc {
if (bufW->Pointer()) {
outputBuf.emplace_back(bufW->Pointer(), bufW->Size());
outputBuf.emplace_back(bufW->Pointer(), bufW->Size());
state.logger->Debug("Buf W #{}: 0x{:X}, 0x{:X}", index, bufW->Pointer(), static_cast<u16>(bufW->Size()));
state.logger->Verbose("Buf W #{}: 0x{:X}, 0x{:X}", index, bufW->Pointer(), static_cast<u16>(bufW->Size()));
}
pointer += sizeof(BufferDescriptorABW);
}
@ -103,26 +103,26 @@ namespace skyline::kernel::ipc {
auto bufC{reinterpret_cast<BufferDescriptorC *>(pointer)};
if (bufC->address) {
outputBuf.emplace_back(bufC->Pointer(), static_cast<u16>(bufC->size));
state.logger->Debug("Buf C: 0x{:X}, 0x{:X}", bufC->Pointer(), static_cast<u16>(bufC->size));
state.logger->Verbose("Buf C: 0x{:X}, 0x{:X}", bufC->Pointer(), static_cast<u16>(bufC->size));
}
} else if (header->cFlag > BufferCFlag::SingleDescriptor) {
for (u8 index{}; (static_cast<u8>(header->cFlag) - 2) > index; index++) { // (cFlag - 2) C descriptors are present
auto bufC{reinterpret_cast<BufferDescriptorC *>(pointer)};
if (bufC->address) {
outputBuf.emplace_back(bufC->Pointer(), static_cast<u16>(bufC->size));
state.logger->Debug("Buf C #{}: 0x{:X}, 0x{:X}", index, bufC->Pointer(), static_cast<u16>(bufC->size));
state.logger->Verbose("Buf C #{}: 0x{:X}, 0x{:X}", index, bufC->Pointer(), static_cast<u16>(bufC->size));
}
pointer += sizeof(BufferDescriptorC);
}
}
if (header->type == CommandType::Request || header->type == CommandType::RequestWithContext) {
state.logger->Debug("Header: Input No: {}, Output No: {}, Raw Size: {}", inputBuf.size(), outputBuf.size(), static_cast<u64>(cmdArgSz));
state.logger->Verbose("Header: Input No: {}, Output No: {}, Raw Size: {}", inputBuf.size(), outputBuf.size(), static_cast<u64>(cmdArgSz));
if (header->handleDesc)
state.logger->Debug("Handle Descriptor: Send PID: {}, Copy Count: {}, Move Count: {}", static_cast<bool>(handleDesc->sendPid), static_cast<u32>(handleDesc->copyCount), static_cast<u32>(handleDesc->moveCount));
state.logger->Verbose("Handle Descriptor: Send PID: {}, Copy Count: {}, Move Count: {}", static_cast<bool>(handleDesc->sendPid), static_cast<u32>(handleDesc->copyCount), static_cast<u32>(handleDesc->moveCount));
if (isDomain)
state.logger->Debug("Domain Header: Command: {}, Input Object Count: {}, Object ID: 0x{:X}", domain->command, domain->inputCount, domain->objectId);
state.logger->Debug("Command ID: 0x{:X}", static_cast<u32>(payload->value));
state.logger->Verbose("Domain Header: Command: {}, Input Object Count: {}, Object ID: 0x{:X}", domain->command, domain->inputCount, domain->objectId);
state.logger->Verbose("Command ID: 0x{:X}", static_cast<u32>(payload->value));
}
}
@ -183,6 +183,6 @@ namespace skyline::kernel::ipc {
}
}
state.logger->Debug("Output: Raw Size: {}, Command ID: 0x{:X}, Copy Handles: {}, Move Handles: {}", static_cast<u32>(header->rawSize), static_cast<u32>(payloadHeader->value), copyHandles.size(), moveHandles.size());
state.logger->Verbose("Output: Raw Size: {}, Command ID: 0x{:X}, Copy Handles: {}, Move Handles: {}", static_cast<u32>(header->rawSize), static_cast<u32>(payloadHeader->value), copyHandles.size(), moveHandles.size());
}
}

View File

@ -12,6 +12,7 @@ namespace skyline::kernel {
void MemoryManager::InitializeVmm(memory::AddressSpaceType type) {
switch (type) {
case memory::AddressSpaceType::AddressSpace32Bit:
case memory::AddressSpaceType::AddressSpace32BitNoReserved:
throw exception("32-bit address spaces are not supported");
case memory::AddressSpaceType::AddressSpace36Bit: {
@ -119,7 +120,7 @@ namespace skyline::kernel {
if (upper == chunks.begin())
throw exception("InsertChunk: Chunk inserted outside address space: 0x{:X} - 0x{:X} and 0x{:X} - 0x{:X}", upper->ptr, upper->ptr + upper->size, chunk.ptr, chunk.ptr + chunk.size);
upper = chunks.erase(upper, std::upper_bound(upper, chunks.end(), chunk.ptr + chunk.size, [](const u8 *ptr, const ChunkDescriptor &chunk) -> bool { return ptr < chunk.ptr; }));
upper = chunks.erase(upper, std::upper_bound(upper, chunks.end(), chunk.ptr + chunk.size, [](const u8 *ptr, const ChunkDescriptor &chunk) -> bool { return ptr < chunk.ptr + chunk.size; }));
if (upper != chunks.end() && upper->ptr < chunk.ptr + chunk.size) {
auto end{upper->ptr + upper->size};
upper->ptr = chunk.ptr + chunk.size;

View File

@ -179,8 +179,9 @@ namespace skyline {
};
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
AddressSpace32Bit = 0, //!< 32-bit address space used by 32-bit applications
AddressSpace36Bit = 1, //!< 36-bit address space used by 64-bit applications before 2.0.0
AddressSpace32BitNoReserved = 2, //!< 32-bit address space without the map region
AddressSpace39Bit, //!< 39-bit address space used by 64-bit applications after 2.0.0
};
}

View File

@ -219,14 +219,26 @@ namespace skyline::kernel::svc {
exit(0);
}
constexpr i32 IdealCoreDontCare{-1};
constexpr i32 IdealCoreUseProcessValue{-2};
constexpr i32 IdealCoreNoUpdate{-3};
void CreateThread(const DeviceState &state) {
auto entry{reinterpret_cast<void *>(state.ctx->gpr.x1)};
auto entryArgument{state.ctx->gpr.x2};
auto stackTop{reinterpret_cast<u8 *>(state.ctx->gpr.x3)};
auto priority{static_cast<i8>(static_cast<u32>(state.ctx->gpr.w4))};
auto idealCore{static_cast<i8>(static_cast<u32>(state.ctx->gpr.w5))};
idealCore = (idealCore == IdealCoreUseProcessValue) ? state.process->npdm.meta.idealCore : idealCore;
if (idealCore < 0 || idealCore >= constant::CoreCount) {
state.ctx->gpr.w0 = result::InvalidCoreId;
state.logger->Warn("svcCreateThread: 'idealCore' invalid: {}", idealCore);
return;
}
if (!constant::HosPriority.Valid(priority)) {
state.ctx->gpr.w0 = result::InvalidAddress;
state.ctx->gpr.w0 = result::InvalidPriority;
state.logger->Warn("svcCreateThread: 'priority' invalid: {}", priority);
return;
}
@ -235,7 +247,7 @@ namespace skyline::kernel::svc {
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, stackTop, priority)};
auto thread{state.process->CreateThread(entry, entryArgument, stackTop, priority, idealCore)};
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->gpr.w1 = thread->handle;
@ -283,8 +295,9 @@ namespace skyline::kernel::svc {
void GetThreadPriority(const DeviceState &state) {
KHandle handle{state.ctx->gpr.w1};
try {
auto priority{state.process->GetHandle<type::KThread>(handle)->priority};
state.logger->Debug("svcGetThreadPriority: Writing thread priority {}", priority);
auto thread{state.process->GetHandle<type::KThread>(handle)};
auto priority{thread->priority};
state.logger->Debug("svcGetThreadPriority: Retrieving thread #{}'s priority: {}", thread->id, priority);
state.ctx->gpr.w1 = priority;
state.ctx->gpr.w0 = Result{};
@ -297,10 +310,15 @@ namespace skyline::kernel::svc {
void SetThreadPriority(const DeviceState &state) {
KHandle handle{state.ctx->gpr.w0};
u32 priority{state.ctx->gpr.w1};
if (!constant::HosPriority.Valid(priority)) {
state.logger->Warn("svcSetThreadPriority: 'priority' invalid: 0x{:X}", priority);
state.ctx->gpr.w0 = result::InvalidPriority;
return;
}
try {
state.logger->Debug("svcSetThreadPriority: Setting thread priority to {}", priority);
state.process->GetHandle<type::KThread>(handle)->UpdatePriority(static_cast<u8>(priority));
auto thread{state.process->GetHandle<type::KThread>(handle)};
state.logger->Debug("svcSetThreadPriority: Setting thread priority to {}", thread->id, priority);
thread->UpdatePriority(static_cast<u8>(priority));
state.ctx->gpr.w0 = Result{};
} catch (const std::exception &) {
state.logger->Warn("svcSetThreadPriority: 'handle' invalid: 0x{:X}", handle);
@ -308,6 +326,62 @@ namespace skyline::kernel::svc {
}
}
void GetThreadCoreMask(const DeviceState &state) {
KHandle handle{state.ctx->gpr.w2};
try {
auto thread{state.process->GetHandle<type::KThread>(handle)};
auto idealCore{thread->idealCore};
auto affinityMask{thread->affinityMask};
state.logger->Debug("svcGetThreadCoreMask: Writing thread #{}'s Ideal Core ({}) + Affinity Mask ({})", thread->id, idealCore, affinityMask.to_string());
state.ctx->gpr.x2 = affinityMask.to_ullong();
state.ctx->gpr.w1 = idealCore;
state.ctx->gpr.w0 = Result{};
} catch (const std::exception &) {
state.logger->Warn("svcGetThreadCoreMask: 'handle' invalid: 0x{:X}", handle);
state.ctx->gpr.w0 = result::InvalidHandle;
}
}
void SetThreadCoreMask(const DeviceState &state) {
KHandle handle{state.ctx->gpr.w0};
i32 idealCore{static_cast<i32>(state.ctx->gpr.w1)};
std::bitset<constant::CoreCount> affinityMask{state.ctx->gpr.x2};
try {
auto thread{state.process->GetHandle<type::KThread>(handle)};
if (idealCore == IdealCoreUseProcessValue) {
idealCore = state.process->npdm.meta.idealCore;
affinityMask.reset().set(idealCore);
} else if (idealCore == IdealCoreNoUpdate) {
idealCore = thread->idealCore;
} else if (idealCore == IdealCoreDontCare) {
idealCore = __builtin_ctzll(affinityMask.to_ullong()); // The first enabled core in the affinity mask
}
if (affinityMask.none() || !affinityMask.test(idealCore)) {
state.logger->Warn("svcSetThreadCoreMask: 'affinityMask' invalid: {} (Ideal Core: {})", affinityMask.to_string(), idealCore);
state.ctx->gpr.w0 = result::InvalidCombination;
return;
}
state.logger->Debug("svcSetThreadCoreMask: Setting thread #{}'s Ideal Core ({}) + Affinity Mask ({})", thread->id, idealCore, affinityMask.to_string());
thread->idealCore = idealCore;
thread->affinityMask = affinityMask;
state.ctx->gpr.w0 = Result{};
} catch (const std::exception &) {
state.logger->Warn("svcSetThreadCoreMask: 'handle' invalid: 0x{:X}", handle);
state.ctx->gpr.w0 = result::InvalidHandle;
}
}
void GetCurrentProcessorNumber(const DeviceState &state) {
state.logger->Debug("svcGetCurrentProcessorNumber: Writing current core for thread #{}: {}", state.thread->id, state.thread->coreId);
state.ctx->gpr.w0 = state.thread->coreId;
}
void ClearEvent(const DeviceState &state) {
auto object{state.process->GetHandle<type::KEvent>(state.ctx->gpr.w0)};
object->signalled = false;
@ -612,18 +686,12 @@ namespace skyline::kernel::svc {
}
void GetThreadId(const DeviceState &state) {
constexpr KHandle threadSelf{0xFFFF8000}; // The handle used by threads to refer to themselves
KHandle handle{state.ctx->gpr.w1};
pid_t pid{};
if (handle != threadSelf)
pid = state.process->GetHandle<type::KThread>(handle)->id;
else
pid = state.thread->id;
size_t pid{state.process->GetHandle<type::KThread>(handle)->id};
state.logger->Debug("svcGetThreadId: Handle: 0x{:X}, PID: {}", handle, pid);
state.ctx->gpr.x1 = static_cast<u64>(pid);
state.ctx->gpr.x1 = pid;
state.ctx->gpr.w0 = Result{};
}
@ -706,7 +774,11 @@ namespace skyline::kernel::svc {
break;
case InfoState::TotalMemoryUsage:
out = state.process->memory.GetMemoryUsage();
out = state.process->memory.GetMemoryUsage() + state.process->memory.GetKMemoryBlockSize();
break;
case InfoState::RandomEntropy:
out = util::GetTimeTicks();
break;
case InfoState::AddressSpaceBaseAddr:
@ -726,20 +798,20 @@ namespace skyline::kernel::svc {
break;
case InfoState::TotalSystemResourceAvailable:
out = totalPhysicalMemory; // TODO: NPDM specifies this in it's PersonalMmHeapSize field
out = state.process->npdm.meta.systemResourceSize;
break;
case InfoState::TotalSystemResourceUsage:
// A very rough approximation of what this should be on the Switch, the amount of memory allocated for storing the memory blocks (https://switchbrew.org/wiki/Kernel_objects#KMemoryBlockManager)
out = state.process->memory.GetKMemoryBlockSize();
out = std::min(static_cast<size_t>(state.process->npdm.meta.systemResourceSize), state.process->memory.GetKMemoryBlockSize());
break;
case InfoState::TotalMemoryAvailableWithoutSystemResource:
out = totalPhysicalMemory; // TODO: Subtract TotalSystemResourceAvailable from this
out = totalPhysicalMemory - state.process->npdm.meta.systemResourceSize;
break;
case InfoState::TotalMemoryUsageWithoutSystemResource:
out = state.process->memory.GetMemoryUsage();
out = state.process->memory.GetMemoryUsage(); // Our regular estimates don't contain the system resources
break;
case InfoState::UserExceptionContextAddr:

View File

@ -78,6 +78,24 @@ namespace skyline::kernel::svc {
*/
void SetThreadPriority(const DeviceState &state);
/**
* @brief Get core mask of provided thread handle
* @url https://switchbrew.org/wiki/SVC#GetThreadCoreMask
*/
void GetThreadCoreMask(const DeviceState &state);
/**
* @brief Set core mask of provided thread handle
* @url https://switchbrew.org/wiki/SVC#SetThreadCoreMask
*/
void SetThreadCoreMask(const DeviceState &state);
/**
* @brief Returns the core on which the current thread is running
* @url https://switchbrew.org/wiki/SVC#GetCurrentProcessorNumber
*/
void GetCurrentProcessorNumber(const DeviceState &state);
/**
* @brief Clears a KEvent of it's signal
* @url https://switchbrew.org/wiki/SVC#ClearEvent
@ -210,9 +228,9 @@ namespace skyline::kernel::svc {
SleepThread, // 0x0B
GetThreadPriority, // 0x0C
SetThreadPriority, // 0x0D
nullptr, // 0x0E
nullptr, // 0x0F
nullptr, // 0x10
GetThreadCoreMask, // 0x0E
SetThreadCoreMask, // 0x0F
GetCurrentProcessorNumber, // 0x10
nullptr, // 0x11
ClearEvent, // 0x12
MapSharedMemory, // 0x13

View File

@ -49,15 +49,14 @@ namespace skyline::kernel::type {
return tlsPage->ReserveSlot();
}
std::shared_ptr<KThread> KProcess::CreateThread(void *entry, u64 argument, void *stackTop, i8 priority) {
std::shared_ptr<KThread> KProcess::CreateThread(void *entry, u64 argument, void *stackTop, i8 priority, i8 idealCore) {
if (!stackTop && threads.empty()) { //!< Main thread stack is created by the kernel and owned by the process
constexpr u64 DefaultStackSize{0x200000}; //!< The default amount of stack: 2 MB
mainThreadStack = mainThreadStack.make_shared(state, reinterpret_cast<u8 *>(state.process->memory.stack.address), DefaultStackSize, memory::Permission{true, true, false}, memory::states::Stack);
mainThreadStack = mainThreadStack.make_shared(state, reinterpret_cast<u8 *>(state.process->memory.stack.address), state.process->npdm.meta.mainThreadStackSize, memory::Permission{true, true, false}, memory::states::Stack);
if (mprotect(mainThreadStack->ptr, PAGE_SIZE, PROT_NONE))
throw exception("Failed to create guard page for thread stack at 0x{:X}", mainThreadStack->ptr);
stackTop = mainThreadStack->ptr + mainThreadStack->size;
}
auto thread{NewHandle<KThread>(this, threads.size(), entry, argument, stackTop, priority).item};
auto thread{NewHandle<KThread>(this, threads.size(), entry, argument, stackTop, (priority == -1) ? state.process->npdm.meta.mainThreadPriority : priority, (idealCore == -1) ? state.process->npdm.meta.idealCore : idealCore).item};
threads.push_back(thread);
return thread;
}

View File

@ -5,6 +5,7 @@
#include <list>
#include <kernel/memory.h>
#include <vfs/npdm.h>
#include "KThread.h"
#include "KTransferMemory.h"
#include "KSession.h"
@ -70,6 +71,7 @@ namespace skyline {
std::shared_ptr<KPrivateMemory> heap;
std::vector<std::shared_ptr<KThread>> threads;
std::vector<std::shared_ptr<TlsPage>> tlsPages;
vfs::NPDM npdm;
KProcess(const DeviceState &state);
@ -83,7 +85,10 @@ namespace skyline {
*/
u8* AllocateTlsSlot();
std::shared_ptr<KThread> CreateThread(void *entry, u64 argument = 0, void *stackTop = nullptr, i8 priority = constant::DefaultPriority);
/**
* @note The default values are for the main thread and will use values from the NPDM
*/
std::shared_ptr<KThread> CreateThread(void *entry, u64 argument = 0, void *stackTop = nullptr, i8 priority = -1, i8 idealCore = -1);
/**
* @brief The output for functions that return created kernel objects
@ -130,9 +135,12 @@ namespace skyline {
std::shared_lock lock(handleMutex);
KType objectType;
if constexpr(std::is_same<objectClass, KThread>())
if constexpr(std::is_same<objectClass, KThread>()) {
constexpr KHandle threadSelf{0xFFFF8000}; // The handle used by threads to refer to themselves
if (handle == threadSelf)
return state.thread;
objectType = KType::KThread;
else if constexpr(std::is_same<objectClass, KProcess>())
} else if constexpr(std::is_same<objectClass, KProcess>())
objectType = KType::KProcess;
else if constexpr(std::is_same<objectClass, KSharedMemory>())
objectType = KType::KSharedMemory;

View File

@ -12,7 +12,8 @@
#include "KProcess.h"
namespace skyline::kernel::type {
KThread::KThread(const DeviceState &state, KHandle handle, KProcess *parent, size_t id, void *entry, u64 argument, void *stackTop, i8 priority) : handle(handle), parent(parent), id(id), entry(entry), entryArgument(argument), stackTop(stackTop), KSyncObject(state, KType::KThread) {
KThread::KThread(const DeviceState &state, KHandle handle, KProcess *parent, size_t id, void *entry, u64 argument, void *stackTop, i8 priority, i8 idealCore) : handle(handle), parent(parent), id(id), entry(entry), entryArgument(argument), stackTop(stackTop), idealCore(idealCore), coreId(idealCore), KSyncObject(state, KType::KThread) {
affinityMask.set(coreId);
UpdatePriority(priority);
}

View File

@ -34,7 +34,7 @@ namespace skyline {
}
namespace constant {
constexpr i8 DefaultPriority{44}; // The default priority of an HOS process
constexpr u8 CoreCount{4}; // The amount of cores an HOS process can be scheduled onto (User applications can only be on the first 3 cores, the last one is reserved for the system)
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
}
@ -62,9 +62,13 @@ namespace skyline {
void* entry;
u64 entryArgument;
void* stackTop;
i8 priority;
KThread(const DeviceState &state, KHandle handle, KProcess *parent, size_t id, void *entry, u64 argument, void *stackTop, i8 priority = constant::DefaultPriority);
i8 priority;
i8 idealCore;
i8 coreId; //!< The CPU core on which this thread is running
std::bitset<constant::CoreCount> affinityMask{}; //!< The CPU core on which this thread is running
KThread(const DeviceState &state, KHandle handle, KProcess *parent, size_t id, void *entry, u64 argument, void *stackTop, i8 priority, i8 idealCore);
~KThread();

View File

@ -66,8 +66,8 @@ namespace skyline::loader {
static ExecutableLoadInfo LoadExecutable(const std::shared_ptr<kernel::type::KProcess> process, const DeviceState &state, Executable &executable, size_t offset = 0);
public:
std::shared_ptr<vfs::NACP> nacp; //!< The NACP of the current application
std::shared_ptr<vfs::Backing> romFs; //!< The RomFS of the current application
std::optional<vfs::NACP> nacp;
std::shared_ptr<vfs::Backing> romFs;
virtual ~Loader() = default;

View File

@ -26,7 +26,7 @@ namespace skyline::loader {
u8* base{loadInfo.base};
void* entry{loadInfo.entry};
state.logger->Info("Loaded nso 'rtld' at 0x{:X}", base);
state.logger->Info("Loaded nso 'rtld' at 0x{:X} (.text @ 0x{:X})", base, entry);
for (const auto &nso : {"main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
nsoFile = exeFs->OpenFile(nso);
@ -35,7 +35,7 @@ namespace skyline::loader {
continue;
loadInfo = NsoLoader::LoadNso(nsoFile, process, state, offset);
state.logger->Info("Loaded nso '{}' at 0x{:X}", nso, base + offset);
state.logger->Info("Loaded '{}.nso' at 0x{:X} (.text @ 0x{:X})", nso, base + offset, loadInfo.entry);
offset += loadInfo.size;
}
@ -45,6 +45,7 @@ namespace skyline::loader {
}
void* NcaLoader::LoadProcessData(const std::shared_ptr<kernel::type::KProcess> process, const DeviceState &state) {
process->npdm = vfs::NPDM(nca.exeFs->OpenFile("main.npdm"));
return LoadExeFs(nca.exeFs, process, state);
}
}

View File

@ -22,7 +22,7 @@ namespace skyline::loader {
throw exception("Invalid ASET magic! 0x{0:X}", assetHeader.magic);
NroAssetSection &nacpHeader{assetHeader.nacp};
nacp = std::make_shared<vfs::NACP>(std::make_shared<vfs::RegionBacking>(backing, header.size + nacpHeader.offset, nacpHeader.size));
nacp.emplace(std::make_shared<vfs::RegionBacking>(backing, header.size + nacpHeader.offset, nacpHeader.size));
NroAssetSection &romFsHeader{assetHeader.romFs};
romFs = std::make_shared<vfs::RegionBacking>(backing, header.size + romFsHeader.offset, romFsHeader.size);

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <kernel/types/KProcess.h>
#include "nca.h"
#include "nsp.h"
@ -31,10 +32,11 @@ namespace skyline::loader {
romFs = programNca->romFs;
controlRomFs = std::make_shared<vfs::RomFileSystem>(controlNca->romFs);
nacp = std::make_shared<vfs::NACP>(controlRomFs->OpenFile("control.nacp"));
nacp.emplace(controlRomFs->OpenFile("control.nacp"));
}
void* NspLoader::LoadProcessData(const std::shared_ptr<kernel::type::KProcess> process, const DeviceState &state) {
process->npdm = vfs::NPDM(programNca->exeFs->OpenFile("main.npdm"));
return NcaLoader::LoadExeFs(programNca->exeFs, process, state);
}

View File

@ -136,8 +136,8 @@ namespace skyline::service {
void ServiceManager::SyncRequestHandler(KHandle handle) {
auto session{state.process->GetHandle<type::KSession>(handle)};
state.logger->Debug("----Start----");
state.logger->Debug("Handle is 0x{:X}", handle);
state.logger->Verbose("----IPC Start----");
state.logger->Verbose("Handle is 0x{:X}", handle);
if (session->isOpen) {
ipc::IpcRequest request(session->isDomain, state);
@ -205,6 +205,6 @@ namespace skyline::service {
} else {
state.logger->Warn("svcSendSyncRequest called on closed handle: 0x{:X}", handle);
}
state.logger->Debug("====End====");
state.logger->Verbose("====IPC End====");
}
}

View File

@ -0,0 +1,43 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <kernel/types/KThread.h>
#include "npdm.h"
namespace skyline::vfs {
constexpr u32 MetaMagic = util::MakeMagic<u32>("META");
NPDM::NPDM() {
constexpr i8 DefaultPriority{44}; // The default priority of an HOS process
constexpr i8 DefaultCore{0}; // The default core for an HOS process
constexpr u64 DefaultStackSize{0x200000}; //!< The default amount of stack: 2 MiB
constexpr u64 DefaultSystemResourceSize{0x1FE00000}; //!< The amount of memory reserved for system resources, it's the maximum at 510 MiB
meta = {
.magic = MetaMagic,
.flags = {
{
.is64Bit = true,
.type = memory::AddressSpaceType::AddressSpace39Bit,
.optimizeMemoryAllocation = false,
}
},
.mainThreadPriority = DefaultPriority,
.idealCore = DefaultCore,
.mainThreadStackSize = DefaultStackSize,
.systemResourceSize = DefaultSystemResourceSize,
.name = "Application",
};
}
NPDM::NPDM(const std::shared_ptr<vfs::Backing> &backing) {
meta = backing->Read<NpdmMeta>();
if(meta.magic != MetaMagic)
throw exception("NPDM Meta Magic isn't correct: 0x{:X} (\"META\" = 0x{:X})", meta.magic, MetaMagic);
if (!constant::HosPriority.Valid(meta.mainThreadPriority))
throw exception("NPDM Main Thread Priority isn't valid: {}", meta.mainThreadStackSize);
if (meta.idealCore >= constant::CoreCount)
throw exception("NPDM Ideal Core isn't valid: {}", meta.idealCore);
if (!util::PageAligned(meta.mainThreadStackSize))
throw exception("NPDM Main Thread Stack isn't page aligned: 0x{:X}", meta.mainThreadStackSize);
}
}

View File

@ -0,0 +1,54 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include "backing.h"
#include <kernel/memory.h>
namespace skyline::vfs {
/**
* @url https://switchbrew.org/wiki/NPDM
*/
class NPDM {
public:
/**
* @url https://switchbrew.org/wiki/NPDM#META
*/
struct __attribute__((packed)) NpdmMeta {
u32 magic; //!< "META"
u32 acidSignatureKeyGeneration;
u32 _unk0_;
union {
struct {
bool is64Bit : 1;
memory::AddressSpaceType type : 2;
bool optimizeMemoryAllocation : 1;
};
u8 raw{};
} flags;
u8 _unk1_;
u8 mainThreadPriority;
u8 idealCore;
u32 _unk2_;
u32 systemResourceSize; //!< 3.0.0+
u32 version;
u32 mainThreadStackSize;
std::array<char, 0x10> name; //!< "Application"
std::array<u8, 0x10> productCode;
u8 _unk3_[0x30];
u32 aciOffset;
u32 aciSize;
u32 acidOffset;
u32 acidSize;
} meta;
static_assert(sizeof(NpdmMeta::flags) == sizeof(u8));
static_assert(sizeof(NpdmMeta) == 0x80);
public:
NPDM();
NPDM(const std::shared_ptr<vfs::Backing> &backing);
};
}

View File

@ -5,12 +5,14 @@
<item>Warn</item>
<item>Info</item>
<item>Debug</item>
<item>Verbose</item>
</string-array>
<string-array name="log_level_val">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
</string-array>
<string-array name="layout_type">
<item>List</item>