mirror of
https://github.com/skyline-emu/skyline.git
synced 2025-01-10 19:09:21 +01:00
Implement NPDM, Core Mask SVCs + Fix VMM bug + Introduce Verbose Log Level
This commit is contained in:
parent
324381908b
commit
c65c91e1bc
@ -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)
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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)...));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
};
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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====");
|
||||
}
|
||||
}
|
||||
|
43
app/src/main/cpp/skyline/vfs/npdm.cpp
Normal file
43
app/src/main/cpp/skyline/vfs/npdm.cpp
Normal 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);
|
||||
}
|
||||
}
|
54
app/src/main/cpp/skyline/vfs/npdm.h
Normal file
54
app/src/main/cpp/skyline/vfs/npdm.h
Normal 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);
|
||||
};
|
||||
}
|
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user