Fix CircularQueue + NPDM ACI0 & Kernel Capability Parsing

This commit is contained in:
◱ PixelyIon 2020-11-20 23:57:53 +05:30 committed by ◱ PixelyIon
parent a5927c5c7b
commit bc5c094860
19 changed files with 277 additions and 136 deletions

View File

@ -26,6 +26,19 @@
#define FORCE_INLINE __attribute__((always_inline)) inline // NOLINT(cppcoreguidelines-macro-usage) #define FORCE_INLINE __attribute__((always_inline)) inline // NOLINT(cppcoreguidelines-macro-usage)
namespace fmt {
/**
* @brief A std::bitset formatter for {fmt}
*/
template <size_t N>
struct formatter<std::bitset<N>>: formatter<std::string> {
template <typename FormatContext>
constexpr auto format(const std::bitset<N> &s, FormatContext &ctx) {
return formatter<std::string>::format(s.to_string(), ctx);
}
};
}
namespace skyline { namespace skyline {
using u128 = __uint128_t; //!< Unsigned 128-bit integer using u128 = __uint128_t; //!< Unsigned 128-bit integer
using u64 = __uint64_t; //!< Unsigned 64-bit integer using u64 = __uint64_t; //!< Unsigned 64-bit integer
@ -79,7 +92,7 @@ namespace skyline {
namespace util { namespace util {
/** /**
* @brief A way to implicitly cast all pointers to u64s, this is used for libfmt as we use 0x{:X} to print pointers * @brief A way to implicitly cast all pointers to u64s, this is used for {fmt} as we use 0x{:X} to print pointers
* @note There's the exception of signed char pointers as they represent C Strings * @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 casted to u64 or passed through fmt::ptr * @note This does not cover std::shared_ptr or std::unique_ptr and those will have to be explicitly casted to u64 or passed through fmt::ptr
*/ */
@ -96,12 +109,12 @@ namespace skyline {
} }
/** /**
* @brief A wrapper over std::runtime_error with libfmt formatting * @brief A wrapper over std::runtime_error with {fmt} formatting
*/ */
class exception : public std::runtime_error { class exception : public std::runtime_error {
public: public:
/** /**
* @param formatStr The exception string to be written, with libfmt formatting * @param formatStr The exception string to be written, with {fmt} formatting
* @param args The arguments based on format_str * @param args The arguments based on format_str
*/ */
template<typename S, typename... Args> template<typename S, typename... Args>

View File

@ -49,18 +49,11 @@ namespace skyline {
produceCondition.wait(lock, [this]() { return start != end; }); produceCondition.wait(lock, [this]() { return start != end; });
} }
ssize_t size{}; while (start != end) {
if (start < end) auto next{start + 1};
size = end - start; next = (next == reinterpret_cast<Type *>(vector.end().base())) ? reinterpret_cast<Type *>(vector.begin().base()) : next;
else function(*next);
size = (reinterpret_cast<Type *>(vector.end().base()) - start) + (end - reinterpret_cast<Type *>(vector.begin().base())); start = next;
while (size--) {
function(*start);
if (start + 1 != reinterpret_cast<Type *>(vector.end().base()))
start++;
else
start = reinterpret_cast<Type *>(vector.begin().base());
} }
consumeCondition.notify_one(); consumeCondition.notify_one();
@ -81,12 +74,14 @@ namespace skyline {
inline void Append(span<Type> buffer) { inline void Append(span<Type> buffer) {
std::unique_lock lock(productionMutex); std::unique_lock lock(productionMutex);
for (auto &item : buffer) { for (auto &item : buffer) {
end = (end == reinterpret_cast<Type *>(vector.end().base()) - 1) ? reinterpret_cast<Type *>(vector.begin().base()) : end; auto next{end + 1};
if (start == end + 1) { next = (next == reinterpret_cast<Type *>(vector.end().base())) ? reinterpret_cast<Type *>(vector.begin().base()) : next;
if (next == start) {
std::unique_lock consumeLock(consumptionMutex); std::unique_lock consumeLock(consumptionMutex);
consumeCondition.wait(consumeLock, [=]() { return start != end + 1; }); consumeCondition.wait(consumeLock, [=]() { return next != start; });
} }
*(end++) = item; *next = item;
end = next++;
} }
produceCondition.notify_one(); produceCondition.notify_one();
} }
@ -99,12 +94,14 @@ namespace skyline {
inline void AppendTranform(span<TransformedType> buffer, Transformation transformation) { inline void AppendTranform(span<TransformedType> buffer, Transformation transformation) {
std::unique_lock lock(productionMutex); std::unique_lock lock(productionMutex);
for (auto &item : buffer) { for (auto &item : buffer) {
end = (end == reinterpret_cast<Type *>(vector.end().base()) - 1) ? reinterpret_cast<Type *>(vector.begin().base()) : end; auto next{end + 1};
if (start == end + 1) { next = (next == reinterpret_cast<Type *>(vector.end().base())) ? reinterpret_cast<Type *>(vector.begin().base()) : next;
if (next == start) {
std::unique_lock consumeLock(consumptionMutex); std::unique_lock consumeLock(consumptionMutex);
consumeCondition.wait(consumeLock, [=]() { return start != end + 1; }); consumeCondition.wait(consumeLock, [=]() { return next != start; });
} }
*(end++) = transformation(item); *next = transformation(item);
end = next;
} }
produceCondition.notify_one(); produceCondition.notify_one();
} }

View File

@ -15,7 +15,7 @@ namespace skyline::crypto {
void KeyStore::ReadPairs(const std::shared_ptr<vfs::Backing> &backing, ReadPairsCallback callback) { void KeyStore::ReadPairs(const std::shared_ptr<vfs::Backing> &backing, ReadPairsCallback callback) {
std::vector<char> fileContent(backing->size); std::vector<char> fileContent(backing->size);
backing->Read(span(fileContent).cast<u8>()); backing->Read(span(fileContent));
auto lineStart{fileContent.begin()}; auto lineStart{fileContent.begin()};
std::vector<char>::iterator lineEnd; std::vector<char>::iterator lineEnd;

View File

@ -68,7 +68,7 @@ namespace skyline::gpu {
}; };
}; };
constexpr u64 Address() { constexpr u64 Address() const {
return (static_cast<u64>(getHi) << 32) | (static_cast<u64>(get) << 2); return (static_cast<u64>(getHi) << 32) | (static_cast<u64>(get) << 2);
} }
}; };

View File

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

View File

@ -0,0 +1,32 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <common.h>
namespace skyline {
namespace constant {
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)
}
namespace kernel {
using CoreMask = std::bitset<constant::CoreCount>;
struct Priority {
u8 min;
u8 max;
constexpr u64 Mask() const {
u64 mask{};
for (u8 i{min}; i <= max; i++)
mask |= 1 << i;
return mask;
}
constexpr bool Valid(i8 value) const {
return (value >= min) && (value <= max);
}
};
}
}

View File

@ -3,6 +3,7 @@
#include <os.h> #include <os.h>
#include <kernel/types/KProcess.h> #include <kernel/types/KProcess.h>
#include <vfs/npdm.h>
#include "results.h" #include "results.h"
#include "svc.h" #include "svc.h"
@ -239,7 +240,7 @@ namespace skyline::kernel::svc {
return; return;
} }
if (!constant::HosPriority.Valid(priority)) { if (!state.process->npdm.threadInfo.priority.Valid(priority)) {
state.ctx->gpr.w0 = result::InvalidPriority; state.ctx->gpr.w0 = result::InvalidPriority;
state.logger->Warn("svcCreateThread: 'priority' invalid: {}", priority); state.logger->Warn("svcCreateThread: 'priority' invalid: {}", priority);
return; return;
@ -251,7 +252,7 @@ namespace skyline::kernel::svc {
auto thread{state.process->CreateThread(entry, entryArgument, stackTop, priority, idealCore)}; auto thread{state.process->CreateThread(entry, entryArgument, stackTop, priority, idealCore)};
if (thread) { if (thread) {
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.logger->Debug("svcCreateThread: Created thread #{} with handle 0x{:X} (Entry Point: 0x{:X}, Argument: 0x{:X}, Stack Pointer: 0x{:X}, Priority: {}, Ideal Core: {})", thread->id, thread->handle, entry, entryArgument, stackTop, priority, idealCore);
state.ctx->gpr.w1 = thread->handle; state.ctx->gpr.w1 = thread->handle;
state.ctx->gpr.w0 = Result{}; state.ctx->gpr.w0 = Result{};
@ -316,7 +317,7 @@ namespace skyline::kernel::svc {
void SetThreadPriority(const DeviceState &state) { void SetThreadPriority(const DeviceState &state) {
KHandle handle{state.ctx->gpr.w0}; KHandle handle{state.ctx->gpr.w0};
u32 priority{state.ctx->gpr.w1}; u32 priority{state.ctx->gpr.w1};
if (!constant::HosPriority.Valid(priority)) { if (!state.process->npdm.threadInfo.priority.Valid(priority)) {
state.logger->Warn("svcSetThreadPriority: 'priority' invalid: 0x{:X}", priority); state.logger->Warn("svcSetThreadPriority: 'priority' invalid: 0x{:X}", priority);
state.ctx->gpr.w0 = result::InvalidPriority; state.ctx->gpr.w0 = result::InvalidPriority;
return; return;
@ -338,7 +339,7 @@ namespace skyline::kernel::svc {
auto thread{state.process->GetHandle<type::KThread>(handle)}; auto thread{state.process->GetHandle<type::KThread>(handle)};
auto idealCore{thread->idealCore}; auto idealCore{thread->idealCore};
auto affinityMask{thread->affinityMask}; auto affinityMask{thread->affinityMask};
state.logger->Debug("svcGetThreadCoreMask: Writing thread #{}'s Ideal Core ({}) + Affinity Mask ({})", thread->id, idealCore, affinityMask.to_string()); state.logger->Debug("svcGetThreadCoreMask: Writing thread #{}'s Ideal Core ({}) + Affinity Mask ({})", thread->id, idealCore, affinityMask);
state.ctx->gpr.x2 = affinityMask.to_ullong(); state.ctx->gpr.x2 = affinityMask.to_ullong();
state.ctx->gpr.w1 = idealCore; state.ctx->gpr.w1 = idealCore;
@ -351,29 +352,36 @@ namespace skyline::kernel::svc {
void SetThreadCoreMask(const DeviceState &state) { void SetThreadCoreMask(const DeviceState &state) {
KHandle handle{state.ctx->gpr.w0}; KHandle handle{state.ctx->gpr.w0};
i32 idealCore{static_cast<i32>(state.ctx->gpr.w1)}; i32 coreId{static_cast<i32>(state.ctx->gpr.w1)};
std::bitset<constant::CoreCount> affinityMask{state.ctx->gpr.x2}; CoreMask affinityMask{state.ctx->gpr.x2};
try { try {
auto thread{state.process->GetHandle<type::KThread>(handle)}; auto thread{state.process->GetHandle<type::KThread>(handle)};
if (idealCore == IdealCoreUseProcessValue) { if (coreId == IdealCoreUseProcessValue) {
idealCore = state.process->npdm.meta.idealCore; coreId = state.process->npdm.meta.idealCore;
affinityMask.reset().set(idealCore); affinityMask.reset().set(coreId);
} else if (idealCore == IdealCoreNoUpdate) { } else if (coreId == IdealCoreNoUpdate) {
idealCore = thread->idealCore; coreId = thread->idealCore;
} else if (idealCore == IdealCoreDontCare) { } else if (coreId == IdealCoreDontCare) {
idealCore = __builtin_ctzll(affinityMask.to_ullong()); // The first enabled core in the affinity mask coreId = __builtin_ctzll(affinityMask.to_ullong()); // The first enabled core in the affinity mask
} }
if (affinityMask.none() || !affinityMask.test(idealCore)) { auto processMask{state.process->npdm.threadInfo.coreMask};
state.logger->Warn("svcSetThreadCoreMask: 'affinityMask' invalid: {} (Ideal Core: {})", affinityMask.to_string(), idealCore); if ((processMask | affinityMask) == processMask) {
state.logger->Warn("svcSetThreadCoreMask: 'affinityMask' invalid: {} (Process Mask: {})", affinityMask, processMask);
state.ctx->gpr.w0 = result::InvalidCoreId;
return;
}
if (affinityMask.none() || !affinityMask.test(coreId)) {
state.logger->Warn("svcSetThreadCoreMask: 'affinityMask' invalid: {} (Ideal Core: {})", affinityMask, coreId);
state.ctx->gpr.w0 = result::InvalidCombination; state.ctx->gpr.w0 = result::InvalidCombination;
return; return;
} }
state.logger->Debug("svcSetThreadCoreMask: Setting thread #{}'s Ideal Core ({}) + Affinity Mask ({})", thread->id, idealCore, affinityMask.to_string()); state.logger->Debug("svcSetThreadCoreMask: Setting thread #{}'s Ideal Core ({}) + Affinity Mask ({})", thread->id, coreId, affinityMask);
thread->idealCore = idealCore; thread->idealCore = coreId;
thread->affinityMask = affinityMask; thread->affinityMask = affinityMask;
state.ctx->gpr.w0 = Result{}; state.ctx->gpr.w0 = Result{};
@ -735,7 +743,7 @@ namespace skyline::kernel::svc {
// 3.0.0+ // 3.0.0+
TotalSystemResourceAvailable = 16, TotalSystemResourceAvailable = 16,
TotalSystemResourceUsage = 17, TotalSystemResourceUsage = 17,
TitleId = 18, ProgramId = 18,
// 4.0.0+ // 4.0.0+
PrivilegedProcessId = 19, PrivilegedProcessId = 19,
// 5.0.0+ // 5.0.0+
@ -753,13 +761,18 @@ namespace skyline::kernel::svc {
u64 out{}; u64 out{};
switch (info) { switch (info) {
case InfoState::AllowedCpuIdBitmask:
case InfoState::AllowedThreadPriorityMask:
case InfoState::IsCurrentProcessBeingDebugged: case InfoState::IsCurrentProcessBeingDebugged:
case InfoState::TitleId:
case InfoState::PrivilegedProcessId: case InfoState::PrivilegedProcessId:
break; break;
case InfoState::AllowedCpuIdBitmask:
out = state.process->npdm.threadInfo.coreMask.to_ullong();
break;
case InfoState::AllowedThreadPriorityMask:
out = state.process->npdm.threadInfo.priority.Mask();
break;
case InfoState::AliasRegionBaseAddr: case InfoState::AliasRegionBaseAddr:
out = state.process->memory.alias.address; out = state.process->memory.alias.address;
break; break;
@ -813,6 +826,10 @@ namespace skyline::kernel::svc {
out = std::min(static_cast<size_t>(state.process->npdm.meta.systemResourceSize), state.process->memory.GetKMemoryBlockSize()); out = std::min(static_cast<size_t>(state.process->npdm.meta.systemResourceSize), state.process->memory.GetKMemoryBlockSize());
break; break;
case InfoState::ProgramId:
out = state.process->npdm.aci0.programId;
break;
case InfoState::TotalMemoryAvailableWithoutSystemResource: case InfoState::TotalMemoryAvailableWithoutSystemResource:
out = totalPhysicalMemory - state.process->npdm.meta.systemResourceSize; out = totalPhysicalMemory - state.process->npdm.meta.systemResourceSize;
break; break;

View File

@ -4,7 +4,6 @@
#pragma once #pragma once
#include <list> #include <list>
#include <kernel/memory.h>
#include <vfs/npdm.h> #include <vfs/npdm.h>
#include "KThread.h" #include "KThread.h"
#include "KTransferMemory.h" #include "KTransferMemory.h"

View File

@ -5,40 +5,12 @@
#include <csetjmp> #include <csetjmp>
#include <nce/guest.h> #include <nce/guest.h>
#include <kernel/scheduler.h>
#include "KSyncObject.h" #include "KSyncObject.h"
#include "KPrivateMemory.h" #include "KPrivateMemory.h"
#include "KSharedMemory.h" #include "KSharedMemory.h"
namespace skyline { namespace skyline {
namespace kernel::type {
struct Priority {
i8 low; //!< The low range of priority
i8 high; //!< The high range of priority
/**
* @param priority The priority range of the value
* @param value The priority value to rescale
* @return The rescaled priority value according to this range
*/
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)));
}
/**
* @param value The priority value to check for validity
* @return If the supplied priority value is valid
*/
constexpr bool Valid(i8 value) const {
return (value >= low) && (value <= high);
}
};
}
namespace constant {
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 HosPriority{0, 63}; //!< The range of priorities for Horizon OS
}
namespace kernel::type { namespace kernel::type {
/** /**
* @brief KThread manages a single thread of execution which is responsible for running guest code and kernel code which is invoked by the guest * @brief KThread manages a single thread of execution which is responsible for running guest code and kernel code which is invoked by the guest
@ -67,9 +39,9 @@ namespace skyline {
void *stackTop; void *stackTop;
i8 priority; i8 priority;
i8 idealCore; i8 idealCore; //!< The ideal CPU core for this thread to run on
i8 coreId; //!< The CPU core on which this thread is running 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 CoreMask affinityMask{}; //!< A mask of CPU cores this thread is allowed to run on
KThread(const DeviceState &state, KHandle handle, KProcess *parent, size_t id, void *entry, u64 argument, void *stackTop, i8 priority, i8 idealCore); KThread(const DeviceState &state, KHandle handle, KProcess *parent, size_t id, void *entry, u64 argument, void *stackTop, i8 priority, i8 idealCore);

View File

@ -45,7 +45,7 @@ namespace skyline::loader {
} }
void *NcaLoader::LoadProcessData(const std::shared_ptr<kernel::type::KProcess> process, const DeviceState &state) { void *NcaLoader::LoadProcessData(const std::shared_ptr<kernel::type::KProcess> process, const DeviceState &state) {
process->npdm = vfs::NPDM(nca.exeFs->OpenFile("main.npdm")); process->npdm = vfs::NPDM(nca.exeFs->OpenFile("main.npdm"), state);
return LoadExeFs(nca.exeFs, process, state); return LoadExeFs(nca.exeFs, process, state);
} }
} }

View File

@ -36,7 +36,7 @@ namespace skyline::loader {
} }
void *NspLoader::LoadProcessData(const std::shared_ptr<kernel::type::KProcess> process, const DeviceState &state) { void *NspLoader::LoadProcessData(const std::shared_ptr<kernel::type::KProcess> process, const DeviceState &state) {
process->npdm = vfs::NPDM(programNca->exeFs->OpenFile("main.npdm")); process->npdm = vfs::NPDM(programNca->exeFs->OpenFile("main.npdm"), state);
return NcaLoader::LoadExeFs(programNca->exeFs, process, state); return NcaLoader::LoadExeFs(programNca->exeFs, process, state);
} }

View File

@ -107,7 +107,7 @@ namespace skyline::service::nvdrv::device {
u32 numJobs; // In u32 numJobs; // In
u32 flags; // In u32 flags; // In
Fence fence; // Out Fence fence; // Out
u32 reserved[3]; // In u32 _res_[3]; // In
} &data = buffer.as<Data>(); } &data = buffer.as<Data>();
state.gpu->gpfifo.Initialize(data.numEntries); state.gpu->gpfifo.Initialize(data.numEntries);

View File

@ -48,7 +48,7 @@ namespace skyline::service::nvdrv::device {
u32 smArchSpaVersion{0x503}; // Maxwell Generation 5.0.3 u32 smArchSpaVersion{0x503}; // Maxwell Generation 5.0.3
u32 smArchWarpCount{0x80}; u32 smArchWarpCount{0x80};
u32 gpuVaBitCount{0x28}; u32 gpuVaBitCount{0x28};
u32 reserved{}; u32 _res_{};
u64 flags{0x55}; // HAS_SYNCPOINTS | SUPPORT_SPARSE_ALLOCS | SUPPORT_CYCLE_STATS | SUPPORT_CYCLE_STATS_SNAPSHOT u64 flags{0x55}; // HAS_SYNCPOINTS | SUPPORT_SPARSE_ALLOCS | SUPPORT_CYCLE_STATS | SUPPORT_CYCLE_STATS_SNAPSHOT
u32 twodClass{0x902D}; // FERMI_TWOD_A u32 twodClass{0x902D}; // FERMI_TWOD_A
u32 threedClass{0xB197}; // MAXWELL_B u32 threedClass{0xB197}; // MAXWELL_B
@ -92,7 +92,7 @@ namespace skyline::service::nvdrv::device {
NvStatus NvHostCtrlGpu::GetTpcMasks(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) { NvStatus NvHostCtrlGpu::GetTpcMasks(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
struct Data { struct Data {
u32 maskBufSize; // In u32 maskBufSize; // In
u32 reserved[3]; // In u32 _res_[3]; // In
u64 maskBuf; // Out u64 maskBuf; // Out
} &data = buffer.as<Data>(); } &data = buffer.as<Data>();

View File

@ -18,7 +18,7 @@ namespace skyline::service::nvdrv {
std::atomic<u32> counterMin; //!< The least value the syncpoint can be (The value it was when it was last synchronized with host1x) std::atomic<u32> counterMin; //!< The least value the syncpoint can be (The value it was when it was last synchronized with host1x)
std::atomic<u32> counterMax; //!< The maximum value the syncpoint can reach according to the current usage std::atomic<u32> counterMax; //!< The maximum value the syncpoint can reach according to the current usage
bool interfaceManaged; //!< If the syncpoint is managed by a host1x client interface, a client interface is a HW block that can handle host1x transactions on behalf of a host1x client (Which would otherwise need to be manually synced using PIO which is synchronous and requires direct cooperation of the CPU) bool interfaceManaged; //!< If the syncpoint is managed by a host1x client interface, a client interface is a HW block that can handle host1x transactions on behalf of a host1x client (Which would otherwise need to be manually synced using PIO which is synchronous and requires direct cooperation of the CPU)
bool reserved; bool reserved; //!< If the syncpoint is reserved or not, not to be confused with a reserved value
}; };
const DeviceState &state; const DeviceState &state;

View File

@ -44,6 +44,14 @@ namespace skyline::vfs {
*/ */
virtual size_t Read(span<u8> output, size_t offset = 0) = 0; virtual size_t Read(span<u8> output, size_t offset = 0) = 0;
/**
* @brief Implicit casting for reading into spans of different types
*/
template<class T, typename std::enable_if<!std::is_same_v<T, u8>, bool>::type = true>
inline size_t Read(span<T> output, size_t offset = 0) {
return Read(output.template cast<u8>(), offset);
}
/** /**
* @brief Read bytes from the backing at a particular offset into an object * @brief Read bytes from the backing at a particular offset into an object
* @param offset The offset to start reading from * @param offset The offset to start reading from

View File

@ -5,7 +5,7 @@
#include "npdm.h" #include "npdm.h"
namespace skyline::vfs { namespace skyline::vfs {
constexpr u32 MetaMagic = util::MakeMagic<u32>("META"); constexpr u32 MetaMagic{util::MakeMagic<u32>("META")};
NPDM::NPDM() { NPDM::NPDM() {
constexpr i8 DefaultPriority{44}; // The default priority of an HOS process constexpr i8 DefaultPriority{44}; // The default priority of an HOS process
@ -27,17 +27,52 @@ namespace skyline::vfs {
.systemResourceSize = DefaultSystemResourceSize, .systemResourceSize = DefaultSystemResourceSize,
.name = "Application", .name = "Application",
}; };
aci0 = {
.magic = MetaMagic,
};
threadInfo = {
.coreMask = 0b1110,
.priority = {0, 63},
};
} }
NPDM::NPDM(const std::shared_ptr<vfs::Backing> &backing) { NPDM::NPDM(const std::shared_ptr<vfs::Backing> &backing, const DeviceState &state) {
meta = backing->Read<NpdmMeta>(); meta = backing->Read<NpdmMeta>();
if (meta.magic != MetaMagic) if (meta.magic != MetaMagic)
throw exception("NPDM Meta Magic isn't correct: 0x{:X} (\"META\" = 0x{:X})", 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)) if (!util::PageAligned(meta.mainThreadStackSize))
throw exception("NPDM Main Thread Stack isn't page aligned: 0x{:X}", meta.mainThreadStackSize); throw exception("NPDM Main Thread Stack isn't page aligned: 0x{:X}", meta.mainThreadStackSize);
aci0 = meta.aci0.Read<NpdmAci0>(backing);
constexpr u32 Aci0Magic{util::MakeMagic<u32>("ACI0")};
if (aci0.magic != Aci0Magic)
throw exception("NPDM ACI0 Magic isn't correct: 0x{:X} (\"ACI0\" = 0x{:X})", aci0.magic, Aci0Magic);
std::vector<NpdmKernelCapability> capabilities(aci0.kernelCapability.size / sizeof(NpdmKernelCapability));
backing->Read(span(capabilities), meta.aci0.offset + aci0.kernelCapability.offset);
for (auto capability : capabilities) {
auto trailingOnes{__builtin_ctz(~capability.raw)};
switch (trailingOnes) {
case 3:
threadInfo.priority = kernel::Priority{capability.threadInfo.lowestPriority, capability.threadInfo.highestPriority};
threadInfo.coreMask = {};
for (u8 core{capability.threadInfo.minCoreId}; core <= capability.threadInfo.maxCoreId; core++)
threadInfo.coreMask.set(core);
break;
case 14:
kernelVersion.minorVersion = capability.kernelVersion.minorVersion;
kernelVersion.majorVersion = capability.kernelVersion.majorVersion;
break;
}
}
if (!threadInfo.priority.Valid(meta.mainThreadPriority))
throw exception("NPDM Main Thread Priority isn't valid: {} ({} - {})", meta.mainThreadPriority, threadInfo.priority.min, threadInfo.priority.max);
if (!threadInfo.coreMask.test(meta.idealCore))
throw exception("NPDM Ideal Core isn't valid: {} ({})", meta.idealCore, threadInfo.coreMask);
state.logger->Debug("NPDM Metadata:\nTitle: ID: {:X}, Version: {}\nMain Thread: Priority: {}, Stack Size: 0x{:X}\nScheduler: Ideal Core: {}, Core Mask: {}, Priority: {} - {}\nKernel Version: v{}.{}", aci0.programId, meta.version, meta.mainThreadPriority, meta.mainThreadStackSize, meta.idealCore, threadInfo.coreMask, threadInfo.priority.min, threadInfo.priority.max, kernelVersion.majorVersion, kernelVersion.minorVersion);
} }
} }

View File

@ -4,51 +4,119 @@
#pragma once #pragma once
#include "backing.h" #include "backing.h"
#include <kernel/scheduler.h>
#include <kernel/memory.h> #include <kernel/memory.h>
namespace skyline::vfs { namespace skyline {
/** namespace vfs {
* @url https://switchbrew.org/wiki/NPDM
*/
class NPDM {
public:
/** /**
* @url https://switchbrew.org/wiki/NPDM#META * @url https://switchbrew.org/wiki/NPDM
*/ */
struct __attribute__((packed)) NpdmMeta { class NPDM {
u32 magic; //!< "META" private:
u32 acidSignatureKeyGeneration; struct Section {
u32 _unk0_; u32 offset;
union { u32 size;
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)); template<class T>
static_assert(sizeof(NpdmMeta) == 0x80); inline T Read(const std::shared_ptr<vfs::Backing> &backing, size_t baseOffset = 0) {
if (sizeof(T) > size)
throw exception("Section size ({}) smaller than Read type size ({})", size, sizeof(T));
return backing->Read<T>(baseOffset + offset);
}
};
static_assert(sizeof(Section) == sizeof(u64));
public: public:
NPDM(); /**
* @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];
Section aci0;
Section acid;
} meta;
static_assert(sizeof(NpdmMeta::flags) == sizeof(u8));
static_assert(sizeof(NpdmMeta) == 0x80);
NPDM(const std::shared_ptr<vfs::Backing> &backing); /**
}; * @url https://switchbrew.org/wiki/NPDM#ACI0
* @note Offsets in this are all relative to ACI0, not the start of the file
*/
struct __attribute__((packed)) NpdmAci0 {
u32 magic; //!< "ACI0"
u32 _res0_[3];
u64 programId;
u64 _res1_;
Section fsAccessControl;
Section srvAccessControl;
Section kernelCapability;
u64 _res2_;
} aci0;
static_assert(sizeof(NpdmAci0) == 0x40);
/**
* @url https://switchbrew.org/wiki/NPDM#KernelCapability
*/
union __attribute__((packed)) NpdmKernelCapability {
/**
* @url https://switchbrew.org/wiki/NPDM#ThreadInfo
*/
struct __attribute__((packed)) {
u8 pattern : 4; //!< 0b0111
u8 highestPriority : 6;
u8 lowestPriority : 6;
u8 minCoreId : 8;
u8 maxCoreId : 8;
} threadInfo;
/**
* @url https://switchbrew.org/wiki/NPDM#KernelVersion
*/
struct __attribute__((packed)) {
u16 pattern : 15; //!< 0b011111111111111
u8 minorVersion : 4;
u16 majorVersion : 13;
} kernelVersion;
u32 raw;
};
static_assert(sizeof(NpdmKernelCapability) == sizeof(u32));
struct {
kernel::Priority priority;
kernel::CoreMask coreMask;
} threadInfo;
struct {
u8 minorVersion;
u16 majorVersion;
} kernelVersion{};
public:
NPDM();
NPDM(const std::shared_ptr<vfs::Backing> &backing, const DeviceState &state);
};
}
} }

View File

@ -20,7 +20,7 @@ namespace skyline::vfs {
fileDataOffset = stringTableOffset + header.stringTableSize; fileDataOffset = stringTableOffset + header.stringTableSize;
std::vector<char> stringTable(header.stringTableSize + 1); std::vector<char> stringTable(header.stringTableSize + 1);
backing->Read(span(stringTable).first(header.stringTableSize).cast<u8>(), stringTableOffset); backing->Read(span(stringTable).first(header.stringTableSize), stringTableOffset);
stringTable[header.stringTableSize] = 0; stringTable[header.stringTableSize] = 0;
for (u32 entryOffset{sizeof(FsHeader)}; entryOffset < header.numFiles * entrySize; entryOffset += entrySize) { for (u32 entryOffset{sizeof(FsHeader)}; entryOffset < header.numFiles * entrySize; entryOffset += entrySize) {

View File

@ -17,7 +17,7 @@ namespace skyline::vfs {
if (entry.nameSize) { if (entry.nameSize) {
std::vector<char> name(entry.nameSize); std::vector<char> name(entry.nameSize);
backing->Read(span(name).cast<u8>(), header.fileMetaTableOffset + offset + sizeof(RomFsFileEntry)); backing->Read(span(name), header.fileMetaTableOffset + offset + sizeof(RomFsFileEntry));
std::string fullPath{path + (path.empty() ? "" : "/") + std::string(name.data(), entry.nameSize)}; std::string fullPath{path + (path.empty() ? "" : "/") + std::string(name.data(), entry.nameSize)};
fileMap.emplace(fullPath, entry); fileMap.emplace(fullPath, entry);
@ -33,7 +33,7 @@ namespace skyline::vfs {
std::string childPath{path}; std::string childPath{path};
if (entry.nameSize) { if (entry.nameSize) {
std::vector<char> name(entry.nameSize); std::vector<char> name(entry.nameSize);
backing->Read(span(name).cast<u8>(), header.dirMetaTableOffset + offset + sizeof(RomFsDirectoryEntry)); backing->Read(span(name), header.dirMetaTableOffset + offset + sizeof(RomFsDirectoryEntry));
childPath = path + (path.empty() ? "" : "/") + std::string(name.data(), entry.nameSize); childPath = path + (path.empty() ? "" : "/") + std::string(name.data(), entry.nameSize);
} }
@ -90,7 +90,7 @@ namespace skyline::vfs {
if (romFsFileEntry.nameSize) { if (romFsFileEntry.nameSize) {
std::vector<char> name(romFsFileEntry.nameSize); std::vector<char> name(romFsFileEntry.nameSize);
backing->Read(span(name).cast<u8>(), header.fileMetaTableOffset + offset + sizeof(RomFileSystem::RomFsFileEntry)); backing->Read(span(name), header.fileMetaTableOffset + offset + sizeof(RomFileSystem::RomFsFileEntry));
contents.emplace_back(Entry{std::string(name.data(), romFsFileEntry.nameSize), EntryType::File, romFsFileEntry.size}); contents.emplace_back(Entry{std::string(name.data(), romFsFileEntry.nameSize), EntryType::File, romFsFileEntry.size});
} }
@ -108,7 +108,7 @@ namespace skyline::vfs {
if (romFsDirectoryEntry.nameSize) { if (romFsDirectoryEntry.nameSize) {
std::vector<char> name(romFsDirectoryEntry.nameSize); std::vector<char> name(romFsDirectoryEntry.nameSize);
backing->Read(span(name).cast<u8>(), header.dirMetaTableOffset + offset + sizeof(RomFileSystem::RomFsDirectoryEntry)); backing->Read(span(name), header.dirMetaTableOffset + offset + sizeof(RomFileSystem::RomFsDirectoryEntry));
contents.emplace_back(Entry{std::string(name.data(), romFsDirectoryEntry.nameSize), EntryType::Directory}); contents.emplace_back(Entry{std::string(name.data(), romFsDirectoryEntry.nameSize), EntryType::Directory});
} }