mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-05 05:15:08 +01:00
Fix CircularQueue
+ NPDM ACI0 & Kernel Capability Parsing
This commit is contained in:
parent
a5927c5c7b
commit
bc5c094860
@ -26,6 +26,19 @@
|
||||
|
||||
#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 {
|
||||
using u128 = __uint128_t; //!< Unsigned 128-bit integer
|
||||
using u64 = __uint64_t; //!< Unsigned 64-bit integer
|
||||
@ -79,7 +92,7 @@ namespace skyline {
|
||||
|
||||
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 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 {
|
||||
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
|
||||
*/
|
||||
template<typename S, typename... Args>
|
||||
|
@ -49,18 +49,11 @@ namespace skyline {
|
||||
produceCondition.wait(lock, [this]() { return start != end; });
|
||||
}
|
||||
|
||||
ssize_t size{};
|
||||
if (start < end)
|
||||
size = end - start;
|
||||
else
|
||||
size = (reinterpret_cast<Type *>(vector.end().base()) - start) + (end - reinterpret_cast<Type *>(vector.begin().base()));
|
||||
|
||||
while (size--) {
|
||||
function(*start);
|
||||
if (start + 1 != reinterpret_cast<Type *>(vector.end().base()))
|
||||
start++;
|
||||
else
|
||||
start = reinterpret_cast<Type *>(vector.begin().base());
|
||||
while (start != end) {
|
||||
auto next{start + 1};
|
||||
next = (next == reinterpret_cast<Type *>(vector.end().base())) ? reinterpret_cast<Type *>(vector.begin().base()) : next;
|
||||
function(*next);
|
||||
start = next;
|
||||
}
|
||||
|
||||
consumeCondition.notify_one();
|
||||
@ -81,12 +74,14 @@ namespace skyline {
|
||||
inline void Append(span<Type> buffer) {
|
||||
std::unique_lock lock(productionMutex);
|
||||
for (auto &item : buffer) {
|
||||
end = (end == reinterpret_cast<Type *>(vector.end().base()) - 1) ? reinterpret_cast<Type *>(vector.begin().base()) : end;
|
||||
if (start == end + 1) {
|
||||
auto next{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);
|
||||
consumeCondition.wait(consumeLock, [=]() { return start != end + 1; });
|
||||
consumeCondition.wait(consumeLock, [=]() { return next != start; });
|
||||
}
|
||||
*(end++) = item;
|
||||
*next = item;
|
||||
end = next++;
|
||||
}
|
||||
produceCondition.notify_one();
|
||||
}
|
||||
@ -99,12 +94,14 @@ namespace skyline {
|
||||
inline void AppendTranform(span<TransformedType> buffer, Transformation transformation) {
|
||||
std::unique_lock lock(productionMutex);
|
||||
for (auto &item : buffer) {
|
||||
end = (end == reinterpret_cast<Type *>(vector.end().base()) - 1) ? reinterpret_cast<Type *>(vector.begin().base()) : end;
|
||||
if (start == end + 1) {
|
||||
auto next{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);
|
||||
consumeCondition.wait(consumeLock, [=]() { return start != end + 1; });
|
||||
consumeCondition.wait(consumeLock, [=]() { return next != start; });
|
||||
}
|
||||
*(end++) = transformation(item);
|
||||
*next = transformation(item);
|
||||
end = next;
|
||||
}
|
||||
produceCondition.notify_one();
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ namespace skyline::crypto {
|
||||
|
||||
void KeyStore::ReadPairs(const std::shared_ptr<vfs::Backing> &backing, ReadPairsCallback callback) {
|
||||
std::vector<char> fileContent(backing->size);
|
||||
backing->Read(span(fileContent).cast<u8>());
|
||||
backing->Read(span(fileContent));
|
||||
|
||||
auto lineStart{fileContent.begin()};
|
||||
std::vector<char>::iterator lineEnd;
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
@ -182,7 +182,7 @@ namespace skyline {
|
||||
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
|
||||
AddressSpace39Bit = 3, //!< 39-bit address space used by 64-bit applications after 2.0.0
|
||||
};
|
||||
}
|
||||
|
||||
|
32
app/src/main/cpp/skyline/kernel/scheduler.h
Normal file
32
app/src/main/cpp/skyline/kernel/scheduler.h
Normal 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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include <os.h>
|
||||
#include <kernel/types/KProcess.h>
|
||||
#include <vfs/npdm.h>
|
||||
#include "results.h"
|
||||
#include "svc.h"
|
||||
|
||||
@ -239,7 +240,7 @@ namespace skyline::kernel::svc {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!constant::HosPriority.Valid(priority)) {
|
||||
if (!state.process->npdm.threadInfo.priority.Valid(priority)) {
|
||||
state.ctx->gpr.w0 = result::InvalidPriority;
|
||||
state.logger->Warn("svcCreateThread: 'priority' invalid: {}", priority);
|
||||
return;
|
||||
@ -251,7 +252,7 @@ namespace skyline::kernel::svc {
|
||||
|
||||
auto thread{state.process->CreateThread(entry, entryArgument, stackTop, priority, idealCore)};
|
||||
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.w0 = Result{};
|
||||
@ -316,7 +317,7 @@ 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)) {
|
||||
if (!state.process->npdm.threadInfo.priority.Valid(priority)) {
|
||||
state.logger->Warn("svcSetThreadPriority: 'priority' invalid: 0x{:X}", priority);
|
||||
state.ctx->gpr.w0 = result::InvalidPriority;
|
||||
return;
|
||||
@ -338,7 +339,7 @@ namespace skyline::kernel::svc {
|
||||
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.logger->Debug("svcGetThreadCoreMask: Writing thread #{}'s Ideal Core ({}) + Affinity Mask ({})", thread->id, idealCore, affinityMask);
|
||||
|
||||
state.ctx->gpr.x2 = affinityMask.to_ullong();
|
||||
state.ctx->gpr.w1 = idealCore;
|
||||
@ -351,29 +352,36 @@ namespace skyline::kernel::svc {
|
||||
|
||||
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};
|
||||
i32 coreId{static_cast<i32>(state.ctx->gpr.w1)};
|
||||
CoreMask 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 (coreId == IdealCoreUseProcessValue) {
|
||||
coreId = state.process->npdm.meta.idealCore;
|
||||
affinityMask.reset().set(coreId);
|
||||
} else if (coreId == IdealCoreNoUpdate) {
|
||||
coreId = thread->idealCore;
|
||||
} else if (coreId == IdealCoreDontCare) {
|
||||
coreId = __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);
|
||||
auto processMask{state.process->npdm.threadInfo.coreMask};
|
||||
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;
|
||||
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;
|
||||
|
||||
state.ctx->gpr.w0 = Result{};
|
||||
@ -735,7 +743,7 @@ namespace skyline::kernel::svc {
|
||||
// 3.0.0+
|
||||
TotalSystemResourceAvailable = 16,
|
||||
TotalSystemResourceUsage = 17,
|
||||
TitleId = 18,
|
||||
ProgramId = 18,
|
||||
// 4.0.0+
|
||||
PrivilegedProcessId = 19,
|
||||
// 5.0.0+
|
||||
@ -753,13 +761,18 @@ namespace skyline::kernel::svc {
|
||||
|
||||
u64 out{};
|
||||
switch (info) {
|
||||
case InfoState::AllowedCpuIdBitmask:
|
||||
case InfoState::AllowedThreadPriorityMask:
|
||||
case InfoState::IsCurrentProcessBeingDebugged:
|
||||
case InfoState::TitleId:
|
||||
case InfoState::PrivilegedProcessId:
|
||||
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:
|
||||
out = state.process->memory.alias.address;
|
||||
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());
|
||||
break;
|
||||
|
||||
case InfoState::ProgramId:
|
||||
out = state.process->npdm.aci0.programId;
|
||||
break;
|
||||
|
||||
case InfoState::TotalMemoryAvailableWithoutSystemResource:
|
||||
out = totalPhysicalMemory - state.process->npdm.meta.systemResourceSize;
|
||||
break;
|
||||
|
@ -4,7 +4,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <kernel/memory.h>
|
||||
#include <vfs/npdm.h>
|
||||
#include "KThread.h"
|
||||
#include "KTransferMemory.h"
|
||||
|
@ -5,40 +5,12 @@
|
||||
|
||||
#include <csetjmp>
|
||||
#include <nce/guest.h>
|
||||
#include <kernel/scheduler.h>
|
||||
#include "KSyncObject.h"
|
||||
#include "KPrivateMemory.h"
|
||||
#include "KSharedMemory.h"
|
||||
|
||||
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 {
|
||||
/**
|
||||
* @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;
|
||||
|
||||
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
|
||||
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);
|
||||
|
||||
|
@ -45,7 +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"));
|
||||
process->npdm = vfs::NPDM(nca.exeFs->OpenFile("main.npdm"), state);
|
||||
return LoadExeFs(nca.exeFs, process, state);
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ namespace skyline::loader {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,7 @@ namespace skyline::service::nvdrv::device {
|
||||
u32 numJobs; // In
|
||||
u32 flags; // In
|
||||
Fence fence; // Out
|
||||
u32 reserved[3]; // In
|
||||
u32 _res_[3]; // In
|
||||
} &data = buffer.as<Data>();
|
||||
|
||||
state.gpu->gpfifo.Initialize(data.numEntries);
|
||||
|
@ -48,7 +48,7 @@ namespace skyline::service::nvdrv::device {
|
||||
u32 smArchSpaVersion{0x503}; // Maxwell Generation 5.0.3
|
||||
u32 smArchWarpCount{0x80};
|
||||
u32 gpuVaBitCount{0x28};
|
||||
u32 reserved{};
|
||||
u32 _res_{};
|
||||
u64 flags{0x55}; // HAS_SYNCPOINTS | SUPPORT_SPARSE_ALLOCS | SUPPORT_CYCLE_STATS | SUPPORT_CYCLE_STATS_SNAPSHOT
|
||||
u32 twodClass{0x902D}; // FERMI_TWOD_A
|
||||
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) {
|
||||
struct Data {
|
||||
u32 maskBufSize; // In
|
||||
u32 reserved[3]; // In
|
||||
u32 _res_[3]; // In
|
||||
u64 maskBuf; // Out
|
||||
} &data = buffer.as<Data>();
|
||||
|
||||
|
@ -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> 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 reserved;
|
||||
bool reserved; //!< If the syncpoint is reserved or not, not to be confused with a reserved value
|
||||
};
|
||||
|
||||
const DeviceState &state;
|
||||
|
@ -44,6 +44,14 @@ namespace skyline::vfs {
|
||||
*/
|
||||
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
|
||||
* @param offset The offset to start reading from
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include "npdm.h"
|
||||
|
||||
namespace skyline::vfs {
|
||||
constexpr u32 MetaMagic = util::MakeMagic<u32>("META");
|
||||
constexpr u32 MetaMagic{util::MakeMagic<u32>("META")};
|
||||
|
||||
NPDM::NPDM() {
|
||||
constexpr i8 DefaultPriority{44}; // The default priority of an HOS process
|
||||
@ -27,17 +27,52 @@ namespace skyline::vfs {
|
||||
.systemResourceSize = DefaultSystemResourceSize,
|
||||
.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>();
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -4,13 +4,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "backing.h"
|
||||
#include <kernel/scheduler.h>
|
||||
#include <kernel/memory.h>
|
||||
|
||||
namespace skyline::vfs {
|
||||
namespace skyline {
|
||||
namespace vfs {
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/NPDM
|
||||
*/
|
||||
class NPDM {
|
||||
private:
|
||||
struct Section {
|
||||
u32 offset;
|
||||
u32 size;
|
||||
|
||||
template<class T>
|
||||
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:
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/NPDM#META
|
||||
@ -37,18 +53,70 @@ namespace skyline::vfs {
|
||||
std::array<char, 0x10> name; //!< "Application"
|
||||
std::array<u8, 0x10> productCode;
|
||||
u8 _unk3_[0x30];
|
||||
u32 aciOffset;
|
||||
u32 aciSize;
|
||||
u32 acidOffset;
|
||||
u32 acidSize;
|
||||
Section aci0;
|
||||
Section acid;
|
||||
} meta;
|
||||
|
||||
static_assert(sizeof(NpdmMeta::flags) == sizeof(u8));
|
||||
static_assert(sizeof(NpdmMeta) == 0x80);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
NPDM(const std::shared_ptr<vfs::Backing> &backing, const DeviceState &state);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ namespace skyline::vfs {
|
||||
fileDataOffset = stringTableOffset + header.stringTableSize;
|
||||
|
||||
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;
|
||||
|
||||
for (u32 entryOffset{sizeof(FsHeader)}; entryOffset < header.numFiles * entrySize; entryOffset += entrySize) {
|
||||
|
@ -17,7 +17,7 @@ namespace skyline::vfs {
|
||||
|
||||
if (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)};
|
||||
fileMap.emplace(fullPath, entry);
|
||||
@ -33,7 +33,7 @@ namespace skyline::vfs {
|
||||
std::string childPath{path};
|
||||
if (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);
|
||||
}
|
||||
|
||||
@ -90,7 +90,7 @@ namespace skyline::vfs {
|
||||
|
||||
if (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});
|
||||
}
|
||||
@ -108,7 +108,7 @@ namespace skyline::vfs {
|
||||
|
||||
if (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});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user