Match EOS

This commit is contained in:
souldbminersmwc
2025-08-31 16:33:36 -04:00
parent ca285351db
commit 602abcef60
4130 changed files with 779 additions and 651843 deletions

View File

@@ -1,62 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "ldr_argument_store.hpp"
namespace ams::ldr {
int ArgumentStore::FindIndex(Entry *map, ncm::ProgramId program_id) {
for (auto i = 0; i < ArgumentMapCount; ++i) {
if (map[i].program_id == program_id) {
return i;
}
}
return -1;
}
const ArgumentStore::Entry *ArgumentStore::Get(ncm::ProgramId program_id) {
/* Find the matching arguments entry. */
Entry *argument = nullptr;
if (auto idx = FindIndex(m_argument_map, program_id); idx >= 0) {
argument = m_argument_map + idx;
}
return argument;
}
Result ArgumentStore::Set(ncm::ProgramId program_id, const void *argument, size_t size) {
/* Check that the argument size is within bounds. */
R_UNLESS(size < ArgumentBufferSize, ldr::ResultArgumentOverflow());
/* Find either a matching arguments entry, or an empty entry. */
auto idx = FindIndex(m_argument_map, program_id);
if (idx < 0) {
idx = FindIndex(m_argument_map, ncm::InvalidProgramId);
}
R_UNLESS(idx >= 0, ldr::ResultArgumentCountOverflow());
/* Set the arguments in the entry. */
auto &entry = m_argument_map[idx];
entry.program_id = program_id;
entry.argument_size = size;
std::memcpy(entry.argument, argument, entry.argument_size);
R_SUCCEED();
}
}

View File

@@ -1,52 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::ldr {
class ArgumentStore {
public:
static constexpr size_t ArgumentBufferSize = 32_KB;
struct Entry {
ncm::ProgramId program_id;
size_t argument_size;
u8 argument[ArgumentBufferSize];
};
private:
static constexpr int ArgumentMapCount = 10;
private:
Entry m_argument_map[ArgumentMapCount];
public:
constexpr ArgumentStore() : m_argument_map{} {
this->Flush();
}
public:
const Entry *Get(ncm::ProgramId program_id);
Result Set(ncm::ProgramId program_id, const void *argument, size_t size);
constexpr Result Flush() {
for (auto &entry : m_argument_map) {
entry.program_id = ncm::InvalidProgramId;
}
R_SUCCEED();
}
private:
static int FindIndex(Entry *map, ncm::ProgramId program_id);
};
}

View File

@@ -1,453 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "ldr_capabilities.hpp"
namespace ams::ldr {
namespace {
/* Types. */
enum class CapabilityId {
KernelFlags = 3,
SyscallMask = 4,
MapRange = 6,
MapPage = 7,
MapRegion = 10,
InterruptPair = 11,
ApplicationType = 13,
KernelVersion = 14,
HandleTable = 15,
DebugFlags = 16,
Empty = 32,
};
template<size_t Index, size_t Count, typename T = u32>
using CapabilityField = util::BitPack32::Field<Index, Count, T>;
#define DEFINE_CAPABILITY_FIELD(name, prev, ...) \
using name = CapabilityField<prev::Next, __VA_ARGS__>; \
constexpr ALWAYS_INLINE typename name::Type Get##name() const { return this->Get<name>(); }
constexpr ALWAYS_INLINE CapabilityId GetCapabilityId(util::BitPack32 cap) {
return static_cast<CapabilityId>(util::CountTrailingZeros<u32>(~cap.value));
}
constexpr inline util::BitPack32 EmptyCapability = {~u32{}};
static_assert(GetCapabilityId(EmptyCapability) == CapabilityId::Empty);
#define CAPABILITY_CLASS_NAME(id) Capability##id
#define DEFINE_CAPABILITY_CLASS(id, member_functions) \
class CAPABILITY_CLASS_NAME(id) { \
public: \
static constexpr CapabilityId Id = CapabilityId::id; \
using IdBits = CapabilityField<0, static_cast<size_t>(Id) + 1>; \
static constexpr u32 IdBitsValue = (static_cast<u32>(1) << static_cast<size_t>(Id)) - 1; \
private: \
util::BitPack32 m_value; \
private: \
template<typename FieldType> \
constexpr ALWAYS_INLINE typename FieldType::Type Get() const { return m_value.Get<FieldType>(); } \
template<typename FieldType> \
constexpr ALWAYS_INLINE void Set(typename FieldType::Type fv) { m_value.Set<FieldType>(fv); } \
constexpr ALWAYS_INLINE u32 GetValue() const { return m_value.value; } \
public: \
constexpr ALWAYS_INLINE CAPABILITY_CLASS_NAME(id)(util::BitPack32 v) : m_value{v} { /* ... */ } \
\
static constexpr CAPABILITY_CLASS_NAME(id) Decode(util::BitPack32 v) { return CAPABILITY_CLASS_NAME(id)(v); } \
\
member_functions \
}; \
static_assert(std::is_trivially_destructible<CAPABILITY_CLASS_NAME(id)>::value)
/* Class definitions. */
DEFINE_CAPABILITY_CLASS(KernelFlags,
DEFINE_CAPABILITY_FIELD(MaximumThreadPriority, IdBits, 6);
DEFINE_CAPABILITY_FIELD(MinimumThreadPriority, MaximumThreadPriority, 6);
DEFINE_CAPABILITY_FIELD(MinimumCoreId, MinimumThreadPriority, 8);
DEFINE_CAPABILITY_FIELD(MaximumCoreId, MinimumCoreId, 8);
bool IsValid(const util::BitPack32 *kac, size_t kac_count) const {
for (size_t i = 0; i < kac_count; i++) {
if (GetCapabilityId(kac[i]) == Id) {
const auto restriction = Decode(kac[i]);
if (this->GetMinimumThreadPriority() < restriction.GetMinimumThreadPriority() ||
this->GetMaximumThreadPriority() > restriction.GetMaximumThreadPriority() ||
this->GetMinimumThreadPriority() > this->GetMaximumThreadPriority()) {
return false;
}
if (this->GetMinimumCoreId() < restriction.GetMinimumCoreId() ||
this->GetMaximumCoreId() > restriction.GetMaximumCoreId() ||
this->GetMinimumCoreId() > this->GetMaximumCoreId()) {
return false;
}
return true;
}
}
return false;
}
);
DEFINE_CAPABILITY_CLASS(SyscallMask,
DEFINE_CAPABILITY_FIELD(Mask, IdBits, 24);
DEFINE_CAPABILITY_FIELD(Index, Mask, 3);
bool IsValid(const util::BitPack32 *kac, size_t kac_count) const {
for (size_t i = 0; i < kac_count; i++) {
if (GetCapabilityId(kac[i]) == Id) {
const auto restriction = Decode(kac[i]);
if (this->GetIndex() == restriction.GetIndex() && this->GetMask() == restriction.GetMask()) {
return true;
}
}
}
return false;
}
);
DEFINE_CAPABILITY_CLASS(MapRange,
DEFINE_CAPABILITY_FIELD(AddressSize, IdBits, 24);
DEFINE_CAPABILITY_FIELD(Flag, AddressSize, 1, bool);
static constexpr size_t SizeMax = 0x100000;
bool IsValid(const util::BitPack32 next_cap, const util::BitPack32 *kac, size_t kac_count) const {
if (GetCapabilityId(next_cap) != Id) {
return false;
}
const auto next = Decode(next_cap);
const u32 start = this->GetAddressSize();
const u32 size = next.GetAddressSize();
const u32 end = start + size;
if (size >= SizeMax) {
return false;
}
for (size_t i = 0; i < kac_count; i++) {
if (GetCapabilityId(kac[i]) == Id) {
const auto restriction = Decode(kac[i++]);
if (i >= kac_count || GetCapabilityId(kac[i]) != Id) {
return false;
}
const auto restriction_next = Decode(kac[i]);
const u32 restriction_start = restriction.GetAddressSize();
const u32 restriction_size = restriction_next.GetAddressSize();
const u32 restriction_end = restriction_start + restriction_size;
if (restriction_size >= SizeMax) {
continue;
}
if (this->GetFlag() == restriction.GetFlag() && next.GetFlag() == restriction_next.GetFlag()) {
if (restriction_start <= start && start <= restriction_end && end <= restriction_end) {
return true;
}
}
}
}
return false;
}
);
DEFINE_CAPABILITY_CLASS(MapPage,
DEFINE_CAPABILITY_FIELD(Address, IdBits, 24);
bool IsValid(const util::BitPack32 *kac, size_t kac_count) const {
for (size_t i = 0; i < kac_count; i++) {
if (GetCapabilityId(kac[i]) == Id) {
const auto restriction = Decode(kac[i]);
if (this->GetValue() == restriction.GetValue()) {
return true;
}
}
}
return false;
}
);
enum class MemoryRegionType : u32 {
NoMapping = 0,
KernelTraceBuffer = 1,
OnMemoryBootImage = 2,
DTB = 3,
};
DEFINE_CAPABILITY_CLASS(MapRegion,
DEFINE_CAPABILITY_FIELD(Region0, IdBits, 6, MemoryRegionType);
DEFINE_CAPABILITY_FIELD(ReadOnly0, Region0, 1, bool);
DEFINE_CAPABILITY_FIELD(Region1, ReadOnly0, 6, MemoryRegionType);
DEFINE_CAPABILITY_FIELD(ReadOnly1, Region1, 1, bool);
DEFINE_CAPABILITY_FIELD(Region2, ReadOnly1, 6, MemoryRegionType);
DEFINE_CAPABILITY_FIELD(ReadOnly2, Region2, 1, bool);
static bool IsValidRegionType(const util::BitPack32 *kac, size_t kac_count, MemoryRegionType region_type, bool is_read_only) {
if (region_type != MemoryRegionType::NoMapping) {
for (size_t i = 0; i < kac_count; i++) {
if (GetCapabilityId(kac[i]) == Id) {
const auto restriction = Decode(kac[i]);
if ((restriction.GetRegion0() == region_type && (is_read_only || !restriction.GetReadOnly0())) ||
(restriction.GetRegion1() == region_type && (is_read_only || !restriction.GetReadOnly1())) ||
(restriction.GetRegion2() == region_type && (is_read_only || !restriction.GetReadOnly2())))
{
return true;
}
}
}
return false;
} else {
return true;
}
}
bool IsValid(const util::BitPack32 *kac, size_t kac_count) const {
return IsValidRegionType(kac, kac_count, this->GetRegion0(), this->GetReadOnly0()) &&
IsValidRegionType(kac, kac_count, this->GetRegion1(), this->GetReadOnly1()) &&
IsValidRegionType(kac, kac_count, this->GetRegion2(), this->GetReadOnly2());
}
);
DEFINE_CAPABILITY_CLASS(InterruptPair,
DEFINE_CAPABILITY_FIELD(InterruptId0, IdBits, 10);
DEFINE_CAPABILITY_FIELD(InterruptId1, InterruptId0, 10);
static constexpr u32 EmptyInterruptId = 0x3FF;
bool IsSingleIdValid(const u32 id, const util::BitPack32 *kac, size_t kac_count) const {
for (size_t i = 0; i < kac_count; i++) {
if (GetCapabilityId(kac[i]) == Id) {
const auto restriction = Decode(kac[i]);
if (restriction.GetInterruptId0() == EmptyInterruptId && restriction.GetInterruptId1() == EmptyInterruptId) {
return true;
}
if (restriction.GetInterruptId0() == id || restriction.GetInterruptId1() == id) {
return true;
}
}
}
return false;
}
bool IsValid(const util::BitPack32 *kac, size_t kac_count) const {
return IsSingleIdValid(this->GetInterruptId0(), kac, kac_count) && IsSingleIdValid(this->GetInterruptId1(), kac, kac_count);
}
);
DEFINE_CAPABILITY_CLASS(ApplicationType,
DEFINE_CAPABILITY_FIELD(ApplicationType, IdBits, 3);
bool IsValid(const util::BitPack32 *kac, size_t kac_count) const {
for (size_t i = 0; i < kac_count; i++) {
if (GetCapabilityId(kac[i]) == Id) {
const auto restriction = Decode(kac[i]);
return restriction.GetValue() == this->GetValue();
}
}
return false;
}
static constexpr util::BitPack32 Encode(u32 app_type) {
util::BitPack32 encoded{IdBitsValue};
encoded.Set<ApplicationType>(app_type);
return encoded;
}
);
DEFINE_CAPABILITY_CLASS(KernelVersion,
DEFINE_CAPABILITY_FIELD(MinorVersion, IdBits, 4);
DEFINE_CAPABILITY_FIELD(MajorVersion, MinorVersion, 13);
bool IsValid(const util::BitPack32 *kac, size_t kac_count) const {
for (size_t i = 0; i < kac_count; i++) {
if (GetCapabilityId(kac[i]) == Id) {
const auto restriction = Decode(kac[i]);
return restriction.GetValue() == this->GetValue();
}
}
return false;
}
);
DEFINE_CAPABILITY_CLASS(HandleTable,
DEFINE_CAPABILITY_FIELD(Size, IdBits, 10);
bool IsValid(const util::BitPack32 *kac, size_t kac_count) const {
for (size_t i = 0; i < kac_count; i++) {
if (GetCapabilityId(kac[i]) == Id) {
const auto restriction = Decode(kac[i]);
return this->GetSize() <= restriction.GetSize();
}
}
return false;
}
);
DEFINE_CAPABILITY_CLASS(DebugFlags,
DEFINE_CAPABILITY_FIELD(AllowDebug, IdBits, 1, bool);
DEFINE_CAPABILITY_FIELD(ForceDebugProd, AllowDebug, 1, bool);
DEFINE_CAPABILITY_FIELD(ForceDebug, ForceDebugProd, 1, bool);
bool IsValid(const util::BitPack32 *kac, size_t kac_count) const {
u32 total = 0;
if (this->GetAllowDebug()) { ++total; }
if (this->GetForceDebugProd()) { ++total; }
if (this->GetForceDebug()) { ++total; }
if (total > 1) {
return false;
}
for (size_t i = 0; i < kac_count; i++) {
if (GetCapabilityId(kac[i]) == Id) {
const auto restriction = Decode(kac[i]);
return (restriction.GetValue() & this->GetValue()) == this->GetValue();
}
}
return false;
}
static constexpr util::BitPack32 Encode(bool allow_debug, bool force_debug_prod, bool force_debug) {
util::BitPack32 encoded{IdBitsValue};
encoded.Set<AllowDebug>(allow_debug);
encoded.Set<ForceDebugProd>(force_debug_prod);
encoded.Set<ForceDebug>(force_debug);
return encoded;
}
);
}
/* Capabilities API. */
Result TestCapability(const util::BitPack32 *kacd, size_t kacd_count, const util::BitPack32 *kac, size_t kac_count) {
for (size_t i = 0; i < kac_count; i++) {
const auto cap = kac[i];
const auto id = GetCapabilityId(cap);
#define VALIDATE_CASE(id) \
case CapabilityId::id: \
R_UNLESS(Capability##id::Decode(cap).IsValid(kacd, kacd_count), ldr::ResultInvalidCapability##id()); \
break
switch (id) {
VALIDATE_CASE(KernelFlags);
VALIDATE_CASE(SyscallMask);
VALIDATE_CASE(MapPage);
VALIDATE_CASE(MapRegion);
VALIDATE_CASE(InterruptPair);
VALIDATE_CASE(ApplicationType);
VALIDATE_CASE(KernelVersion);
VALIDATE_CASE(HandleTable);
VALIDATE_CASE(DebugFlags);
case CapabilityId::MapRange:
{
/* Map Range needs extra logic because there it involves two sequential caps. */
i++;
R_UNLESS(i < kac_count, ldr::ResultInvalidCapabilityMapRange());
R_UNLESS(CapabilityMapRange::Decode(cap).IsValid(kac[i], kacd, kacd_count), ldr::ResultInvalidCapabilityMapRange());
}
break;
default:
R_UNLESS(id == CapabilityId::Empty, ldr::ResultUnknownCapability());
break;
}
#undef VALIDATE_CASE
}
R_SUCCEED();
}
u16 MakeProgramInfoFlag(const util::BitPack32 *kac, size_t count) {
u16 flags = 0;
for (size_t i = 0; i < count; ++i) {
const auto cap = kac[i];
switch (GetCapabilityId(cap)) {
case CapabilityId::ApplicationType:
{
const auto app_type = CapabilityApplicationType::Decode(cap).GetApplicationType() & ProgramInfoFlag_ApplicationTypeMask;
if (app_type != ProgramInfoFlag_InvalidType) {
flags |= app_type;
}
}
break;
case CapabilityId::DebugFlags:
if (CapabilityDebugFlags::Decode(cap).GetAllowDebug()) {
flags |= ProgramInfoFlag_AllowDebug;
}
break;
default:
break;
}
}
return flags;
}
void UpdateProgramInfoFlag(u16 flags, util::BitPack32 *kac, size_t count) {
for (size_t i = 0; i < count; ++i) {
const auto cap = kac[i];
switch (GetCapabilityId(cap)) {
case CapabilityId::ApplicationType:
kac[i] = CapabilityApplicationType::Encode(flags & ProgramInfoFlag_ApplicationTypeMask);
break;
case CapabilityId::DebugFlags:
kac[i] = CapabilityDebugFlags::Encode((flags & ProgramInfoFlag_AllowDebug) != 0, CapabilityDebugFlags::Decode(cap).GetForceDebugProd(), CapabilityDebugFlags::Decode(cap).GetForceDebug());
break;
default:
break;
}
}
}
void FixDebugCapabilityForHbl(util::BitPack32 *kac, size_t count) {
for (size_t i = 0; i < count; ++i) {
const auto cap = kac[i];
switch (GetCapabilityId(cap)) {
case CapabilityId::DebugFlags:
/* 19.0.0+ disallows more than one flag set; we are always DebugMode for kernel, so ForceDebug is the most powerful/flexible flag to set. */
kac[i] = CapabilityDebugFlags::Encode(false, false, true);
break;
default:
break;
}
}
}
void PreProcessCapability(util::BitPack32 *kac, size_t count) {
for (size_t i = 0; i < count; ++i) {
const auto cap = kac[i];
switch (GetCapabilityId(cap)) {
/* NOTE: Currently, there is no pre-processing necessary. */
default:
break;
}
}
}
}

View File

@@ -1,30 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::ldr {
Result TestCapability(const util::BitPack32 *kacd, size_t kacd_count, const util::BitPack32 *kac, size_t kac_count);
u16 MakeProgramInfoFlag(const util::BitPack32 *kac, size_t count);
void UpdateProgramInfoFlag(u16 flags, util::BitPack32 *kac, size_t count);
void FixDebugCapabilityForHbl(util::BitPack32 *kac, size_t count);
void PreProcessCapability(util::BitPack32 *kac, size_t count);
}

View File

@@ -1,169 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "ldr_content_management.hpp"
namespace ams::ldr {
namespace {
constinit os::SdkMutex g_scoped_code_mount_lock;
}
/* ScopedCodeMount functionality. */
ScopedCodeMount::ScopedCodeMount(const ncm::ProgramLocation &loc, const ldr::ProgramAttributes &attrs) : m_lk(g_scoped_code_mount_lock), m_has_status(false), m_mounted_ams(false), m_mounted_sd_or_code(false), m_mounted_code(false) {
m_result = this->Initialize(loc, attrs);
}
ScopedCodeMount::ScopedCodeMount(const ncm::ProgramLocation &loc, const cfg::OverrideStatus &o, const ldr::ProgramAttributes &attrs) : m_lk(g_scoped_code_mount_lock), m_override_status(o), m_has_status(true), m_mounted_ams(false), m_mounted_sd_or_code(false), m_mounted_code(false) {
m_result = this->Initialize(loc, attrs);
}
ScopedCodeMount::~ScopedCodeMount() {
/* Unmount filesystems. */
if (m_mounted_ams) {
fs::Unmount(AtmosphereCodeMountName);
}
if (m_mounted_sd_or_code) {
fs::Unmount(SdOrCodeMountName);
}
if (m_mounted_code) {
fs::Unmount(CodeMountName);
}
}
Result ScopedCodeMount::Initialize(const ncm::ProgramLocation &loc, const ldr::ProgramAttributes &attrs) {
/* Capture override status, if necessary. */
this->EnsureOverrideStatus(loc);
AMS_ABORT_UNLESS(m_has_status);
/* Get the content path. */
char content_path[fs::EntryNameLengthMax + 1];
R_TRY(GetProgramPath(content_path, sizeof(content_path), loc, attrs));
/* Get the content attributes. */
const auto content_attributes = attrs.content_attributes;
/* Mount the atmosphere code file system. */
R_TRY(fs::MountCodeForAtmosphereWithRedirection(std::addressof(m_ams_code_verification_data), AtmosphereCodeMountName, content_path, content_attributes, loc.program_id, static_cast<ncm::StorageId>(loc.storage_id), m_override_status.IsHbl(), m_override_status.IsProgramSpecific()));
m_mounted_ams = true;
/* Mount the sd or base code file system. */
R_TRY(fs::MountCodeForAtmosphere(std::addressof(m_sd_or_base_code_verification_data), SdOrCodeMountName, content_path, content_attributes, loc.program_id, static_cast<ncm::StorageId>(loc.storage_id)));
m_mounted_sd_or_code = true;
/* Mount the base code file system. */
if (R_SUCCEEDED(fs::MountCode(std::addressof(m_base_code_verification_data), CodeMountName, content_path, content_attributes, loc.program_id, static_cast<ncm::StorageId>(loc.storage_id)))) {
m_mounted_code = true;
}
R_SUCCEED();
}
void ScopedCodeMount::EnsureOverrideStatus(const ncm::ProgramLocation &loc) {
if (m_has_status) {
return;
}
m_override_status = cfg::CaptureOverrideStatus(loc.program_id);
m_has_status = true;
}
/* Redirection API. */
Result GetProgramPath(char *out_path, size_t out_size, const ncm::ProgramLocation &loc, const ldr::ProgramAttributes &attrs) {
/* Check for storage id none. */
if (static_cast<ncm::StorageId>(loc.storage_id) == ncm::StorageId::None) {
std::memset(out_path, 0, out_size);
std::memcpy(out_path, "/", std::min<size_t>(out_size, 2));
R_SUCCEED();
}
/* Get the content attributes. */
const auto content_attributes = attrs.content_attributes;
AMS_UNUSED(content_attributes);
lr::Path path;
/* Check that path registration is allowable. */
if (static_cast<ncm::StorageId>(loc.storage_id) == ncm::StorageId::Host) {
AMS_ABORT_UNLESS(spl::IsDevelopment());
}
/* Try to get the path from the registered resolver. */
lr::RegisteredLocationResolver reg;
R_TRY(lr::OpenRegisteredLocationResolver(std::addressof(reg)));
R_TRY_CATCH(reg.ResolveProgramPath(std::addressof(path), loc.program_id)) {
R_CATCH(lr::ResultProgramNotFound) {
/* Program wasn't found via registered resolver, fall back to the normal resolver. */
lr::LocationResolver lr;
R_TRY(lr::OpenLocationResolver(std::addressof(lr), static_cast<ncm::StorageId>(loc.storage_id)));
R_TRY(lr.ResolveProgramPath(std::addressof(path), loc.program_id));
}
} R_END_TRY_CATCH;
/* Fix directory separators in path. */
fs::Replace(path.str, sizeof(path.str), fs::StringTraits::AlternateDirectorySeparator, fs::StringTraits::DirectorySeparator);
/* Check that the path is valid. */
AMS_ABORT_UNLESS(path.IsValid());
/* Copy the output path. */
std::memset(out_path, 0, out_size);
std::memcpy(out_path, path.str, std::min(out_size, sizeof(path)));
R_SUCCEED();
}
Result RedirectProgramPath(const char *path, size_t size, const ncm::ProgramLocation &loc) {
/* Check for storage id none. */
if (static_cast<ncm::StorageId>(loc.storage_id) == ncm::StorageId::None) {
R_SUCCEED();
}
/* Open location resolver. */
lr::LocationResolver lr;
R_TRY(lr::OpenLocationResolver(std::addressof(lr), static_cast<ncm::StorageId>(loc.storage_id)));
/* Copy in path. */
lr::Path lr_path;
std::memcpy(lr_path.str, path, std::min(size, sizeof(lr_path.str)));
lr_path.str[sizeof(lr_path.str) - 1] = '\x00';
/* Redirect the path. */
lr.RedirectProgramPath(lr_path, loc.program_id);
R_SUCCEED();
}
Result RedirectHtmlDocumentPathForHbl(const ncm::ProgramLocation &loc) {
lr::Path path;
/* Open a location resolver. */
lr::LocationResolver lr;
R_TRY(lr::OpenLocationResolver(std::addressof(lr), static_cast<ncm::StorageId>(loc.storage_id)));
/* If there's already a Html Document path, we don't need to set one. */
R_SUCCEED_IF(R_SUCCEEDED(lr.ResolveApplicationHtmlDocumentPath(std::addressof(path), loc.program_id)));
/* We just need to set this to any valid NCA path. Let's use the executable path. */
R_TRY(lr.ResolveProgramPath(std::addressof(path), loc.program_id));
lr.RedirectApplicationHtmlDocumentPath(path, loc.program_id, loc.program_id);
R_SUCCEED();
}
}

View File

@@ -1,83 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::ldr {
/* Utility reference to make code mounting automatic. */
class ScopedCodeMount {
NON_COPYABLE(ScopedCodeMount);
NON_MOVEABLE(ScopedCodeMount);
private:
std::scoped_lock<os::SdkMutex> m_lk;
cfg::OverrideStatus m_override_status;
fs::CodeVerificationData m_ams_code_verification_data;
fs::CodeVerificationData m_sd_or_base_code_verification_data;
fs::CodeVerificationData m_base_code_verification_data;
Result m_result;
bool m_has_status;
bool m_mounted_ams;
bool m_mounted_sd_or_code;
bool m_mounted_code;
public:
ScopedCodeMount(const ncm::ProgramLocation &loc, const ldr::ProgramAttributes &attrs);
ScopedCodeMount(const ncm::ProgramLocation &loc, const cfg::OverrideStatus &override_status, const ldr::ProgramAttributes &attrs);
~ScopedCodeMount();
Result GetResult() const {
return m_result;
}
const cfg::OverrideStatus &GetOverrideStatus() const {
AMS_ABORT_UNLESS(m_has_status);
return m_override_status;
}
const fs::CodeVerificationData &GetAtmosphereCodeVerificationData() const {
return m_ams_code_verification_data;
}
const fs::CodeVerificationData &GetSdOrBaseCodeVerificationData() const {
return m_sd_or_base_code_verification_data;
}
const fs::CodeVerificationData &GetCodeVerificationData() const {
return m_base_code_verification_data;
}
private:
Result Initialize(const ncm::ProgramLocation &loc, const ldr::ProgramAttributes &attrs);
void EnsureOverrideStatus(const ncm::ProgramLocation &loc);
};
constexpr inline const char * const AtmosphereCodeMountName = "ams-code";
constexpr inline const char * const AtmosphereCompatMountName = "ams-cmpt";
constexpr inline const char * const SdOrCodeMountName = "sd-code";
constexpr inline const char * const CodeMountName = "code";
constexpr inline const char * const CompatMountName = "cmpt";
#define ENCODE_ATMOSPHERE_CODE_PATH(relative) "ams-code:" relative
#define ENCODE_ATMOSPHERE_CMPT_PATH(relative) "ams-cmpt:" relative
#define ENCODE_SD_OR_CODE_PATH(relative) "sd-code:" relative
#define ENCODE_CODE_PATH(relative) "code:" relative
#define ENCODE_CMPT_PATH(relative) "cmpt:" relative
/* Redirection API. */
Result GetProgramPath(char *out_path, size_t out_size, const ncm::ProgramLocation &loc, const ldr::ProgramAttributes &attrs);
Result RedirectProgramPath(const char *path, size_t size, const ncm::ProgramLocation &loc);
Result RedirectHtmlDocumentPathForHbl(const ncm::ProgramLocation &loc);
}

View File

@@ -1,64 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "ldr_launch_record.hpp"
namespace ams::ldr {
namespace {
bool g_development_for_acid_production_check = false;
bool g_development_for_anti_downgrade_check = false;
bool g_development_for_acid_signature_check = false;
bool g_enabled_program_verification = true;
}
void SetDevelopmentForAcidProductionCheck(bool development) {
g_development_for_acid_production_check = development;
}
void SetDevelopmentForAntiDowngradeCheck(bool development) {
g_development_for_anti_downgrade_check = development;
}
void SetDevelopmentForAcidSignatureCheck(bool development) {
g_development_for_acid_signature_check = development;
}
void SetEnabledProgramVerification(bool enabled) {
if (g_development_for_acid_signature_check) {
g_enabled_program_verification = enabled;
}
}
bool IsDevelopmentForAcidProductionCheck() {
return g_development_for_acid_production_check;
}
bool IsDevelopmentForAntiDowngradeCheck() {
return g_development_for_anti_downgrade_check;
}
bool IsDevelopmentForAcidSignatureCheck() {
return g_development_for_acid_signature_check;
}
bool IsEnabledProgramVerification() {
return g_enabled_program_verification;
}
}

View File

@@ -1,32 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::ldr {
/* Development Manager API. */
void SetDevelopmentForAcidProductionCheck(bool development);
void SetDevelopmentForAntiDowngradeCheck(bool development);
void SetDevelopmentForAcidSignatureCheck(bool development);
void SetEnabledProgramVerification(bool enabled);
bool IsDevelopmentForAcidProductionCheck();
bool IsDevelopmentForAntiDowngradeCheck();
bool IsDevelopmentForAcidSignatureCheck();
bool IsEnabledProgramVerification();
}

View File

@@ -1,97 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Patch fallback case to mov w0, #1 rather than retrieving settings flag. */
constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_9_0_0[] = {
{ 0x521C, "\x20\x00\x80\x52", 4 },
};
constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_10_0_0[] = {
{ 0x5494, "\x20\x00\x80\x52", 4 },
};
/* Patch getter functions to return 1. */
constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_11_0_0[] = {
{ 0x85DC, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
{ 0x866C, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
};
constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_12_0_0[] = {
{ 0x840C, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
{ 0x849C, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
};
constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_13_0_0[] = {
{ 0x8424, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
{ 0x84B4, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
};
constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_14_0_0[] = {
{ 0x7414, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
{ 0x74A4, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
};
constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_15_0_0[] = {
{ 0x7230, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
{ 0x72AC, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
};
constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_16_0_0[] = {
{ 0x718C, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
{ 0x7208, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
};
constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_17_0_0[] = {
{ 0x7170, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
{ 0x71EC, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
};
constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_18_0_0[] = {
{ 0x6DCC, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
{ 0x6E48, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
};
constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_19_0_0[] = {
{ 0x6D90, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
{ 0x6E10, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
};
constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_20_0_0[] = {
{ 0x7010, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
{ 0x7090, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
};
constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_20_1_0[] = {
{ 0x7010, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
{ 0x7090, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
};
constexpr inline const EmbeddedPatch Usb30ForceEnablePatches[] = {
{ ParseModuleId("C0D3F4E87E8B0FE9BBE9F1968A20767F3DC08E03"), util::size(Usb30ForceEnablePatches_9_0_0), Usb30ForceEnablePatches_9_0_0 },
{ ParseModuleId("B9C700CA8335F8BAA0D2041D8D09F772890BA988"), util::size(Usb30ForceEnablePatches_10_0_0), Usb30ForceEnablePatches_10_0_0 },
{ ParseModuleId("95BAF06A69650C215A5DD50CF8BD2A603E7AD3C2"), util::size(Usb30ForceEnablePatches_11_0_0), Usb30ForceEnablePatches_11_0_0 },
{ ParseModuleId("F3F88D90EF6B3B6358EDEBAF87D56FA88D3A081C"), util::size(Usb30ForceEnablePatches_12_0_0), Usb30ForceEnablePatches_12_0_0 }, /* 12.0.0 - 12.0.3 */
{ ParseModuleId("A109046278AA99353C20EC38B57C495B74A06D91"), util::size(Usb30ForceEnablePatches_12_0_0), Usb30ForceEnablePatches_12_0_0 }, /* 12.1.0 */
{ ParseModuleId("69384BF4A543C62C4342C94E5E2CED3C5A675251"), util::size(Usb30ForceEnablePatches_13_0_0), Usb30ForceEnablePatches_13_0_0 }, /* 13.0.0 - 13.1.0 */
{ ParseModuleId("1C97AFF30D48AFFEB74B28A530D30ABA0ABB9FFF"), util::size(Usb30ForceEnablePatches_14_0_0), Usb30ForceEnablePatches_14_0_0 }, /* 14.0.0 */
{ ParseModuleId("30B15A83E94D91750E7470795414AD1AE9C6A8DB"), util::size(Usb30ForceEnablePatches_15_0_0), Usb30ForceEnablePatches_15_0_0 }, /* 15.0.0 */
{ ParseModuleId("225865A442B4B66E8BD14B3E9450B901BDF29A40"), util::size(Usb30ForceEnablePatches_16_0_0), Usb30ForceEnablePatches_16_0_0 }, /* 16.0.0 */
{ ParseModuleId("70D4C2ABCD049F16B301186924367F813DA70248"), util::size(Usb30ForceEnablePatches_17_0_0), Usb30ForceEnablePatches_17_0_0 }, /* 17.0.0 */
{ ParseModuleId("4F21AE15E814FA46515C0401BB23D4F7ADCBF3F4"), util::size(Usb30ForceEnablePatches_18_0_0), Usb30ForceEnablePatches_18_0_0 }, /* 18.0.0 */
{ ParseModuleId("54BB9BB32C958E02752DC5E4AE8D016BFE1F5418"), util::size(Usb30ForceEnablePatches_19_0_0), Usb30ForceEnablePatches_19_0_0 }, /* 19.0.0 */
{ ParseModuleId("40E80E7442C0DFC985315E6F9E8C77229818AC0F"), util::size(Usb30ForceEnablePatches_20_0_0), Usb30ForceEnablePatches_20_0_0 }, /* 20.0.0 */
{ ParseModuleId("A5EF8D22EDF8A384E4135270ED596C1D2D524159"), util::size(Usb30ForceEnablePatches_20_1_0), Usb30ForceEnablePatches_20_1_0 }, /* 20.1.0 - 20.1.1 */
};

View File

@@ -1,80 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "ldr_launch_record.hpp"
namespace ams::ldr {
namespace {
static constexpr size_t MaxBootPrograms = 0x50;
constinit std::array<ncm::ProgramId, MaxBootPrograms> g_launched_boot_programs = [] {
std::array<ncm::ProgramId, MaxBootPrograms> arr = {};
for (size_t i = 0; i < MaxBootPrograms; ++i) {
arr[i] = ncm::InvalidProgramId;
}
return arr;
}();
constinit bool g_boot_programs_done = false;
bool HasLaunchedBootProgramImpl(ncm::ProgramId program_id) {
for (const auto &launched : g_launched_boot_programs) {
if (launched == program_id) {
return true;
}
}
return false;
}
void SetLaunchedBootProgramImpl(ncm::ProgramId program_id) {
for (size_t i = 0; i < MaxBootPrograms; ++i) {
if (g_launched_boot_programs[i] == ncm::InvalidProgramId) {
g_launched_boot_programs[i] = program_id;
return;
}
}
AMS_ABORT("Too many boot programs");
}
}
/* Launch Record API. */
bool HasLaunchedBootProgram(ncm::ProgramId program_id) {
return HasLaunchedBootProgramImpl(program_id);
}
void SetLaunchedBootProgram(ncm::ProgramId program_id) {
if (!g_boot_programs_done) {
SetLaunchedBootProgramImpl(program_id);
if (program_id == ncm::SystemAppletId::Qlaunch) {
g_boot_programs_done = true;
}
}
}
}
/* Loader wants to override this libstratosphere function, which is weakly linked. */
/* This is necessary to prevent circular dependencies. */
namespace ams::pm::info {
Result HasLaunchedBootProgram(bool *out, ncm::ProgramId program_id) {
*out = ldr::HasLaunchedBootProgram(program_id);
R_SUCCEED();
}
}

View File

@@ -1,25 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::ldr {
/* Launch Record API. */
bool HasLaunchedBootProgram(ncm::ProgramId program_id);
void SetLaunchedBootProgram(ncm::ProgramId program_id);
}

View File

@@ -1,138 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "ldr_argument_store.hpp"
#include "ldr_content_management.hpp"
#include "ldr_development_manager.hpp"
#include "ldr_process_creation.hpp"
#include "ldr_launch_record.hpp"
#include "ldr_loader_service.hpp"
namespace ams::ldr {
namespace {
constinit ArgumentStore g_argument_store;
bool IsValidProgramAttributes(const ldr::ProgramAttributes &attrs) {
/* Check that the attributes are valid; on NX, this means Platform = NX + no content attrs. */
if (attrs.platform != ncm::ContentMetaPlatform::Nx) {
return false;
}
if (attrs.content_attributes != fs::ContentAttributes_None) {
return false;
}
return true;
}
}
Result LoaderService::CreateProcess(os::NativeHandle *out, PinId pin_id, u32 flags, os::NativeHandle resource_limit, const ProgramAttributes &attrs) {
/* Check that the program attributes are valid. */
R_UNLESS(IsValidProgramAttributes(attrs), ldr::ResultInvalidProgramAttributes());
/* Get the location and override status. */
ncm::ProgramLocation loc;
cfg::OverrideStatus override_status;
R_TRY(ldr::GetProgramLocationAndOverrideStatusFromPinId(std::addressof(loc), std::addressof(override_status), pin_id));
/* Get the program path. */
char path[fs::EntryNameLengthMax];
R_TRY(GetProgramPath(path, sizeof(path), loc, attrs));
path[sizeof(path) - 1] = '\x00';
/* Create the process. */
R_RETURN(ldr::CreateProcess(out, pin_id, loc, override_status, path, g_argument_store.Get(loc.program_id), flags, resource_limit, attrs));
}
Result LoaderService::GetProgramInfo(ProgramInfo *out, cfg::OverrideStatus *out_status, const ncm::ProgramLocation &loc, const ProgramAttributes &attrs) {
/* Check that the program attributes are valid. */
R_UNLESS(IsValidProgramAttributes(attrs), ldr::ResultInvalidProgramAttributes());
/* Zero output. */
std::memset(out, 0, sizeof(*out));
/* Get the program path. */
char path[fs::EntryNameLengthMax];
R_TRY(GetProgramPath(path, sizeof(path), loc, attrs));
path[sizeof(path) - 1] = '\x00';
/* Get the program info. */
cfg::OverrideStatus status;
R_TRY(ldr::GetProgramInfo(out, std::addressof(status), loc, path, attrs));
if (loc.program_id != out->program_id) {
/* Redirect the program path. */
const ncm::ProgramLocation new_loc = ncm::ProgramLocation::Make(out->program_id, static_cast<ncm::StorageId>(loc.storage_id));
R_TRY(RedirectProgramPath(path, sizeof(path), new_loc));
/* Update the arguments, as needed. */
if (const auto *entry = g_argument_store.Get(loc.program_id); entry != nullptr) {
R_TRY(g_argument_store.Set(new_loc.program_id, entry->argument, entry->argument_size));
}
}
/* If we should, set the output status. */
if (out_status != nullptr) {
*out_status = status;
}
R_SUCCEED();
}
Result LoaderService::PinProgram(PinId *out, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &status) {
*out = {};
R_RETURN(ldr::PinProgram(out, loc, status));
}
Result LoaderService::UnpinProgram(PinId id) {
R_RETURN(ldr::UnpinProgram(id));
}
Result LoaderService::SetProgramArgument(ncm::ProgramId program_id, const void *argument, size_t size) {
R_RETURN(g_argument_store.Set(program_id, argument, size));
}
Result LoaderService::FlushArguments() {
R_RETURN(g_argument_store.Flush());
}
Result LoaderService::GetProcessModuleInfo(u32 *out_count, ModuleInfo *out, size_t max_out_count, os::ProcessId process_id) {
*out_count = 0;
std::memset(out, 0, max_out_count * sizeof(*out));
R_RETURN(ldr::GetProcessModuleInfo(out_count, out, max_out_count, process_id));
}
Result LoaderService::RegisterExternalCode(os::NativeHandle *out, ncm::ProgramId program_id) {
R_RETURN(fssystem::CreateExternalCode(out, program_id));
}
void LoaderService::UnregisterExternalCode(ncm::ProgramId program_id) {
fssystem::DestroyExternalCode(program_id);
}
void LoaderService::HasLaunchedBootProgram(bool *out, ncm::ProgramId program_id) {
*out = ldr::HasLaunchedBootProgram(program_id);
}
Result LoaderService::SetEnabledProgramVerification(bool enabled) {
ldr::SetEnabledProgramVerification(enabled);
R_SUCCEED();
}
}

View File

@@ -1,99 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::ldr {
class LoaderService {
public:
/* Official commands. */
Result CreateProcess(sf::OutMoveHandle proc_h, PinId id, u32 flags, sf::CopyHandle &&reslimit_h, const ProgramAttributes &attrs) {
/* Create a handle to set the output to when done. */
os::NativeHandle handle = os::InvalidNativeHandle;
ON_SCOPE_EXIT { proc_h.SetValue(handle, true); };
R_RETURN(this->CreateProcess(std::addressof(handle), id, flags, reslimit_h.GetOsHandle(), attrs));
}
Result GetProgramInfo(sf::Out<ProgramInfo> out_program_info, const ncm::ProgramLocation &loc, const ProgramAttributes &attrs) {
R_RETURN(this->GetProgramInfo(out_program_info.GetPointer(), nullptr, loc, attrs));
}
Result PinProgram(sf::Out<PinId> out_id, const ncm::ProgramLocation &loc) {
R_RETURN(this->PinProgram(out_id.GetPointer(), loc, cfg::OverrideStatus{}));
}
Result UnpinProgram(PinId id);
Result SetProgramArgumentDeprecated(ncm::ProgramId program_id, const sf::InPointerBuffer &args, u32 args_size) {
AMS_UNUSED(args_size);
R_RETURN(this->SetProgramArgument(program_id, args.GetPointer(), std::min<size_t>(args_size, args.GetSize())));
}
Result SetProgramArgument(ncm::ProgramId program_id, const sf::InPointerBuffer &args) {
R_RETURN(this->SetProgramArgument(program_id, args.GetPointer(), args.GetSize()));
}
Result FlushArguments();
Result GetProcessModuleInfo(sf::Out<u32> count, const sf::OutPointerArray<ModuleInfo> &out, os::ProcessId process_id) {
R_UNLESS(out.GetSize() <= std::numeric_limits<s32>::max(), ldr::ResultInvalidSize());
R_RETURN(this->GetProcessModuleInfo(count.GetPointer(), out.GetPointer(), out.GetSize(), process_id));
}
Result SetEnabledProgramVerification(bool enabled);
/* Atmosphere commands. */
Result AtmosphereRegisterExternalCode(sf::OutMoveHandle out, ncm::ProgramId program_id) {
/* Create a handle to set the output to when done. */
os::NativeHandle handle = os::InvalidNativeHandle;
ON_SCOPE_EXIT { out.SetValue(handle, true); };
R_RETURN(this->RegisterExternalCode(std::addressof(handle), program_id));
}
void AtmosphereUnregisterExternalCode(ncm::ProgramId program_id) {
return this->UnregisterExternalCode(program_id);
}
void AtmosphereHasLaunchedBootProgram(sf::Out<bool> out, ncm::ProgramId program_id) {
return this->HasLaunchedBootProgram(out.GetPointer(), program_id);
}
Result AtmosphereGetProgramInfo(sf::Out<ProgramInfo> out_program_info, sf::Out<cfg::OverrideStatus> out_status, const ncm::ProgramLocation &loc, const ProgramAttributes &attrs) {
R_RETURN(this->GetProgramInfo(out_program_info.GetPointer(), out_status.GetPointer(), loc, attrs));
}
Result AtmospherePinProgram(sf::Out<PinId> out_id, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &override_status) {
R_RETURN(this->PinProgram(out_id.GetPointer(), loc, override_status));
}
private:
Result CreateProcess(os::NativeHandle *out, PinId pin_id, u32 flags, os::NativeHandle resource_limit, const ProgramAttributes &attrs);
Result GetProgramInfo(ProgramInfo *out, cfg::OverrideStatus *out_status, const ncm::ProgramLocation &loc, const ProgramAttributes &attrs);
Result PinProgram(PinId *out, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &status);
Result SetProgramArgument(ncm::ProgramId program_id, const void *argument, size_t size);
Result GetProcessModuleInfo(u32 *out_count, ModuleInfo *out, size_t max_out_count, os::ProcessId process_id);
Result RegisterExternalCode(os::NativeHandle *out, ncm::ProgramId program_id);
void UnregisterExternalCode(ncm::ProgramId program_id);
void HasLaunchedBootProgram(bool *out, ncm::ProgramId program_id);
};
static_assert(ams::ldr::impl::IsIProcessManagerInterface<LoaderService>);
static_assert(ams::ldr::impl::IsIDebugMonitorInterface<LoaderService>);
static_assert(ams::ldr::impl::IsIShellInterface<LoaderService>);
}

View File

@@ -1,170 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "ldr_development_manager.hpp"
#include "ldr_loader_service.hpp"
namespace ams {
namespace ldr {
namespace {
constinit u8 g_heap_memory[16_KB];
lmem::HeapHandle g_server_heap_handle;
constinit ams::sf::ExpHeapAllocator g_server_allocator;
void *Allocate(size_t size) {
return lmem::AllocateFromExpHeap(g_server_heap_handle, size);
}
void Deallocate(void *p, size_t size) {
AMS_UNUSED(size);
return lmem::FreeToExpHeap(g_server_heap_handle, p);
}
void InitializeHeap() {
g_server_heap_handle = lmem::CreateExpHeap(g_heap_memory, sizeof(g_heap_memory), lmem::CreateOption_None);
g_server_allocator.Attach(g_server_heap_handle);
}
}
namespace {
struct ServerOptions {
static constexpr size_t PointerBufferSize = 0x420;
static constexpr size_t MaxDomains = 0;
static constexpr size_t MaxDomainObjects = 0;
static constexpr bool CanDeferInvokeRequest = false;
static constexpr bool CanManageMitmServers = false;
};
/* ldr:pm, ldr:shel, ldr:dmnt. */
enum PortIndex {
PortIndex_ProcessManager,
PortIndex_Shell,
PortIndex_DebugMonitor,
PortIndex_Count,
};
constexpr sm::ServiceName ProcessManagerServiceName = sm::ServiceName::Encode("ldr:pm");
constexpr size_t ProcessManagerMaxSessions = 1;
constexpr sm::ServiceName ShellServiceName = sm::ServiceName::Encode("ldr:shel");
constexpr size_t ShellMaxSessions = 3;
constexpr sm::ServiceName DebugMonitorServiceName = sm::ServiceName::Encode("ldr:dmnt");
constexpr size_t DebugMonitorMaxSessions = 3;
constinit sf::UnmanagedServiceObject<impl::IProcessManagerInterface, LoaderService> g_pm_service;
constinit sf::UnmanagedServiceObject<impl::IShellInterface, LoaderService> g_shell_service;
constinit sf::UnmanagedServiceObject<impl::IDebugMonitorInterface, LoaderService> g_dmnt_service;
constexpr size_t MaxSessions = ProcessManagerMaxSessions + ShellMaxSessions + DebugMonitorMaxSessions + 1;
using ServerManager = ams::sf::hipc::ServerManager<PortIndex_Count, ServerOptions, MaxSessions>;
ServerManager g_server_manager;
void RegisterServiceSessions() {
R_ABORT_UNLESS(g_server_manager.RegisterObjectForServer(g_pm_service.GetShared(), ProcessManagerServiceName, ProcessManagerMaxSessions));
R_ABORT_UNLESS(g_server_manager.RegisterObjectForServer(g_shell_service.GetShared(), ShellServiceName, ShellMaxSessions));
R_ABORT_UNLESS(g_server_manager.RegisterObjectForServer(g_dmnt_service.GetShared(), DebugMonitorServiceName, DebugMonitorMaxSessions));
}
void LoopProcess() {
g_server_manager.LoopProcess();
}
}
}
namespace init {
void InitializeSystemModule() {
/* Initialize heap. */
ldr::InitializeHeap();
/* Set fs allocator. */
fs::SetAllocator(ldr::Allocate, ldr::Deallocate);
/* Initialize services we need. */
R_ABORT_UNLESS(sm::Initialize());
fs::InitializeForSystem();
lr::Initialize();
R_ABORT_UNLESS(fsldrInitialize());
spl::Initialize();
/* Verify that we can sanely execute. */
ams::CheckApiVersion();
}
void FinalizeSystemModule() { /* ... */ }
void Startup() { /* ... */ }
}
void NORETURN Exit(int rc) {
AMS_UNUSED(rc);
AMS_ABORT("Exit called by immortal process");
}
void Main() {
/* Disable auto-abort in fs operations. */
fs::SetEnabledAutoAbort(false);
/* Set thread name. */
os::SetThreadNamePointer(os::GetCurrentThread(), AMS_GET_SYSTEM_THREAD_NAME(ldr, Main));
AMS_ASSERT(os::GetThreadPriority(os::GetCurrentThread()) == AMS_GET_SYSTEM_THREAD_PRIORITY(ldr, Main));
/* Configure development. */
/* NOTE: Nintendo really does call the getter function three times instead of caching the value. */
ldr::SetDevelopmentForAcidProductionCheck(spl::IsDevelopment());
ldr::SetDevelopmentForAntiDowngradeCheck(spl::IsDevelopment());
ldr::SetDevelopmentForAcidSignatureCheck(spl::IsDevelopment());
/* Register the loader services. */
ldr::RegisterServiceSessions();
/* Loop forever, servicing our services. */
ldr::LoopProcess();
/* This can never be reached. */
AMS_ASSUME(false);
}
}
/* Override operator new. */
void *operator new(size_t size) {
return ams::ldr::Allocate(size);
}
void *operator new(size_t size, const std::nothrow_t &) {
return ams::ldr::Allocate(size);
}
void operator delete(void *p) {
return ams::ldr::Deallocate(p, 0);
}
void operator delete(void *p, size_t size) {
return ams::ldr::Deallocate(p, size);
}

View File

@@ -1,298 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "ldr_capabilities.hpp"
#include "ldr_content_management.hpp"
#include "ldr_development_manager.hpp"
#include "ldr_meta.hpp"
namespace ams::ldr {
namespace {
/* Convenience definitions. */
constexpr size_t MetaCacheBufferSize = 0x8000;
constexpr inline const char AtmosphereMetaPath[] = ENCODE_ATMOSPHERE_CODE_PATH("/main.npdm");
constexpr inline const char SdOrBaseMetaPath[] = ENCODE_SD_OR_CODE_PATH("/main.npdm");
constexpr inline const char BaseMetaPath[] = ENCODE_CODE_PATH("/main.npdm");
/* Types. */
struct MetaCache {
Meta meta;
u8 buffer[MetaCacheBufferSize];
};
/* Global storage. */
ncm::ProgramId g_cached_program_id;
cfg::OverrideStatus g_cached_override_status;
MetaCache g_meta_cache;
MetaCache g_original_meta_cache;
/* Helpers. */
Result ValidateSubregion(size_t allowed_start, size_t allowed_end, size_t start, size_t size, size_t min_size = 0) {
R_UNLESS(size >= min_size, ldr::ResultInvalidMeta());
R_UNLESS(allowed_start <= start, ldr::ResultInvalidMeta());
R_UNLESS(start <= allowed_end, ldr::ResultInvalidMeta());
R_UNLESS(start + size <= allowed_end, ldr::ResultInvalidMeta());
R_SUCCEED();
}
Result ValidateNpdm(const Npdm *npdm, size_t size) {
/* Validate magic. */
R_UNLESS(npdm->magic == Npdm::Magic, ldr::ResultInvalidMeta());
/* Validate flags. */
constexpr u32 InvalidMetaFlagMask = 0x80000000;
R_UNLESS(!(npdm->flags & InvalidMetaFlagMask), ldr::ResultInvalidMeta());
/* Validate Acid extents. */
R_TRY(ValidateSubregion(sizeof(Npdm), size, npdm->acid_offset, npdm->acid_size, sizeof(Acid)));
/* Validate Aci extends. */
R_TRY(ValidateSubregion(sizeof(Npdm), size, npdm->aci_offset, npdm->aci_size, sizeof(Aci)));
R_SUCCEED();
}
Result ValidateAcid(const Acid *acid, size_t size) {
/* Validate magic. */
R_UNLESS(acid->magic == Acid::Magic, ldr::ResultInvalidMeta());
/* Validate that the acid is for production if not development. */
if (!IsDevelopmentForAcidProductionCheck()) {
R_UNLESS((acid->flags & Acid::AcidFlag_Production) != 0, ldr::ResultInvalidMeta());
}
/* Validate that the acid version is correct. */
constexpr u8 SupportedSdkMajorVersion = ams::svc::ConvertToSdkMajorVersion(ams::svc::SupportedKernelMajorVersion);
if (acid->unknown_209 < SupportedSdkMajorVersion) {
R_UNLESS(acid->version == 0, ldr::ResultInvalidMeta());
R_UNLESS(acid->unknown_209 == 0, ldr::ResultInvalidMeta());
}
/* Validate Fac, Sac, Kac. */
R_TRY(ValidateSubregion(sizeof(Acid), size, acid->fac_offset, acid->fac_size));
R_TRY(ValidateSubregion(sizeof(Acid), size, acid->sac_offset, acid->sac_size));
R_TRY(ValidateSubregion(sizeof(Acid), size, acid->kac_offset, acid->kac_size));
R_SUCCEED();
}
Result ValidateAci(const Aci *aci, size_t size) {
/* Validate magic. */
R_UNLESS(aci->magic == Aci::Magic, ldr::ResultInvalidMeta());
/* Validate Fah, Sac, Kac. */
R_TRY(ValidateSubregion(sizeof(Aci), size, aci->fah_offset, aci->fah_size));
R_TRY(ValidateSubregion(sizeof(Aci), size, aci->sac_offset, aci->sac_size));
R_TRY(ValidateSubregion(sizeof(Aci), size, aci->kac_offset, aci->kac_size));
R_SUCCEED();
}
const u8 *GetAcidSignatureModulus(ncm::ContentMetaPlatform platform, u8 key_generation, bool unk_unused) {
return fssystem::GetAcidSignatureKeyModulus(platform, !IsDevelopmentForAcidSignatureCheck(), key_generation, unk_unused);
}
size_t GetAcidSignatureModulusSize(ncm::ContentMetaPlatform platform, bool unk_unused) {
return fssystem::GetAcidSignatureKeyModulusSize(platform, unk_unused);
}
Result ValidateAcidSignature(Meta *meta, ncm::ContentMetaPlatform platform, bool unk_unused) {
/* Loader did not check signatures prior to 10.0.0. */
if (hos::GetVersion() < hos::Version_10_0_0) {
meta->check_verification_data = false;
R_SUCCEED();
}
/* Get the signature key generation. */
const auto signature_key_generation = meta->npdm->signature_key_generation;
R_UNLESS(fssystem::IsValidSignatureKeyGeneration(platform, signature_key_generation), ldr::ResultInvalidMeta());
/* Verify the signature. */
const u8 *sig = meta->acid->signature;
const size_t sig_size = sizeof(meta->acid->signature);
const u8 *mod = GetAcidSignatureModulus(platform, signature_key_generation, unk_unused);
const size_t mod_size = GetAcidSignatureModulusSize(platform, unk_unused);
const u8 *exp = fssystem::GetAcidSignatureKeyPublicExponent();
const size_t exp_size = fssystem::AcidSignatureKeyPublicExponentSize;
const u8 *msg = meta->acid->modulus;
const size_t msg_size = meta->acid->size;
const bool is_signature_valid = crypto::VerifyRsa2048PssSha256(sig, sig_size, mod, mod_size, exp, exp_size, msg, msg_size);
R_UNLESS(is_signature_valid || !IsEnabledProgramVerification(), ldr::ResultInvalidAcidSignature());
meta->check_verification_data = is_signature_valid;
R_SUCCEED();
}
Result LoadMetaFromFile(fs::FileHandle file, MetaCache *cache) {
/* Reset cache. */
cache->meta = {};
/* Read from file. */
s64 npdm_size = 0;
{
/* Get file size. */
R_TRY(fs::GetFileSize(std::addressof(npdm_size), file));
/* Read data into cache buffer. */
R_UNLESS(npdm_size <= static_cast<s64>(MetaCacheBufferSize), ldr::ResultMetaOverflow());
R_TRY(fs::ReadFile(file, 0, cache->buffer, npdm_size));
}
/* Ensure size is big enough. */
R_UNLESS(npdm_size >= static_cast<s64>(sizeof(Npdm)), ldr::ResultInvalidMeta());
/* Validate the meta. */
{
Meta *meta = std::addressof(cache->meta);
Npdm *npdm = reinterpret_cast<Npdm *>(cache->buffer);
R_TRY(ValidateNpdm(npdm, npdm_size));
Acid *acid = reinterpret_cast<Acid *>(cache->buffer + npdm->acid_offset);
Aci *aci = reinterpret_cast<Aci *>(cache->buffer + npdm->aci_offset);
R_TRY(ValidateAcid(acid, npdm->acid_size));
R_TRY(ValidateAci(aci, npdm->aci_size));
/* Set Meta members. */
meta->npdm = npdm;
meta->acid = acid;
meta->aci = aci;
meta->acid_fac = reinterpret_cast<u8 *>(acid) + acid->fac_offset;
meta->acid_sac = reinterpret_cast<u8 *>(acid) + acid->sac_offset;
meta->acid_kac = reinterpret_cast<u8 *>(acid) + acid->kac_offset;
meta->aci_fah = reinterpret_cast<u8 *>(aci) + aci->fah_offset;
meta->aci_sac = reinterpret_cast<u8 *>(aci) + aci->sac_offset;
meta->aci_kac = reinterpret_cast<u8 *>(aci) + aci->kac_offset;
meta->modulus = acid->modulus;
}
R_SUCCEED();
}
}
/* API. */
Result LoadMeta(Meta *out_meta, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &status, ncm::ContentMetaPlatform platform, bool unk_unused) {
/* Set the cached program id back to zero. */
g_cached_program_id = {};
/* Try to load meta from file. */
fs::FileHandle file;
R_TRY(fs::OpenFile(std::addressof(file), AtmosphereMetaPath, fs::OpenMode_Read));
{
ON_SCOPE_EXIT { fs::CloseFile(file); };
R_TRY(LoadMetaFromFile(file, std::addressof(g_meta_cache)));
}
/* Patch meta. Start by setting all program ids to the current program id. */
Meta *meta = std::addressof(g_meta_cache.meta);
meta->acid->program_id_min = loc.program_id;
meta->acid->program_id_max = loc.program_id;
meta->aci->program_id = loc.program_id;
/* For HBL, we need to copy some information from the base meta. */
if (status.IsHbl()) {
if (R_SUCCEEDED(fs::OpenFile(std::addressof(file), SdOrBaseMetaPath, fs::OpenMode_Read))) {
ON_SCOPE_EXIT { fs::CloseFile(file); };
if (R_SUCCEEDED(LoadMetaFromFile(file, std::addressof(g_original_meta_cache)))) {
Meta *o_meta = std::addressof(g_original_meta_cache.meta);
/* Fix pool partition. */
if (hos::GetVersion() >= hos::Version_5_0_0) {
meta->acid->flags = (meta->acid->flags & 0xFFFFFFC3) | (o_meta->acid->flags & 0x0000003C);
}
/* Fix flags. */
const u16 program_info_flags = MakeProgramInfoFlag(static_cast<const util::BitPack32 *>(o_meta->aci_kac), o_meta->aci->kac_size / sizeof(util::BitPack32));
UpdateProgramInfoFlag(program_info_flags, static_cast<util::BitPack32 *>(meta->acid_kac), meta->acid->kac_size / sizeof(util::BitPack32));
UpdateProgramInfoFlag(program_info_flags, static_cast<util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32));
}
}
/* Perform address space override. */
if (status.HasOverrideAddressSpace()) {
/* Clear the existing address space. */
meta->npdm->flags &= ~Npdm::MetaFlag_AddressSpaceTypeMask;
/* Set the new address space flag. */
switch (status.GetOverrideAddressSpaceFlags()) {
case cfg::impl::OverrideStatusFlag_AddressSpace32Bit: meta->npdm->flags |= (Npdm::AddressSpaceType_32Bit) << Npdm::MetaFlag_AddressSpaceTypeShift; break;
case cfg::impl::OverrideStatusFlag_AddressSpace64BitDeprecated: meta->npdm->flags |= (Npdm::AddressSpaceType_64BitDeprecated) << Npdm::MetaFlag_AddressSpaceTypeShift; break;
case cfg::impl::OverrideStatusFlag_AddressSpace32BitWithoutAlias: meta->npdm->flags |= (Npdm::AddressSpaceType_32BitWithoutAlias) << Npdm::MetaFlag_AddressSpaceTypeShift; break;
case cfg::impl::OverrideStatusFlag_AddressSpace64Bit: meta->npdm->flags |= (Npdm::AddressSpaceType_64Bit) << Npdm::MetaFlag_AddressSpaceTypeShift; break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
/* When hbl is applet, adjust main thread priority. */
if ((MakeProgramInfoFlag(static_cast<const util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32)) & ProgramInfoFlag_ApplicationTypeMask) == ProgramInfoFlag_Applet) {
constexpr auto HblMainThreadPriorityApplication = 44;
constexpr auto HblMainThreadPriorityApplet = 40;
if (meta->npdm->main_thread_priority == HblMainThreadPriorityApplication) {
meta->npdm->main_thread_priority = HblMainThreadPriorityApplet;
}
}
/* Fix the debug capabilities, to prevent needing a hbl recompilation. */
FixDebugCapabilityForHbl(static_cast<util::BitPack32 *>(meta->acid_kac), meta->acid->kac_size / sizeof(util::BitPack32));
FixDebugCapabilityForHbl(static_cast<util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32));
} else if (hos::GetVersion() >= hos::Version_10_0_0) {
/* If storage id is none, there is no base code filesystem, and thus it is impossible for us to validate. */
/* However, if we're an application, we are guaranteed a base code filesystem. */
if (static_cast<ncm::StorageId>(loc.storage_id) != ncm::StorageId::None || ncm::IsApplicationId(loc.program_id)) {
R_TRY(fs::OpenFile(std::addressof(file), BaseMetaPath, fs::OpenMode_Read));
ON_SCOPE_EXIT { fs::CloseFile(file); };
R_TRY(LoadMetaFromFile(file, std::addressof(g_original_meta_cache)));
R_TRY(ValidateAcidSignature(std::addressof(g_original_meta_cache.meta), platform, unk_unused));
meta->modulus = g_original_meta_cache.meta.modulus;
meta->check_verification_data = g_original_meta_cache.meta.check_verification_data;
}
}
/* Pre-process the capabilities. */
/* This is used to e.g. avoid passing memory region descriptor to older kernels. */
PreProcessCapability(static_cast<util::BitPack32 *>(meta->acid_kac), meta->acid->kac_size / sizeof(util::BitPack32));
PreProcessCapability(static_cast<util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32));
/* Set output. */
g_cached_program_id = loc.program_id;
g_cached_override_status = status;
*out_meta = *meta;
R_SUCCEED();
}
Result LoadMetaFromCache(Meta *out_meta, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &status, ncm::ContentMetaPlatform platform) {
if (g_cached_program_id != loc.program_id || g_cached_override_status != status) {
R_RETURN(LoadMeta(out_meta, loc, status, platform, false));
}
*out_meta = g_meta_cache.meta;
R_SUCCEED();
}
void InvalidateMetaCache() {
/* Set the cached program id back to zero. */
g_cached_program_id = {};
}
}

View File

@@ -1,43 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::ldr {
struct Meta {
Npdm *npdm;
Acid *acid;
Aci *aci;
void *acid_fac;
void *acid_sac;
void *acid_kac;
void *aci_fah;
void *aci_sac;
void *aci_kac;
void *modulus;
bool check_verification_data;
};
/* Meta API. */
Result LoadMeta(Meta *out_meta, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &status, ncm::ContentMetaPlatform platform, bool unk_unused);
Result LoadMetaFromCache(Meta *out_meta, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &status, ncm::ContentMetaPlatform platform);
void InvalidateMetaCache();
}

View File

@@ -1,145 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "ldr_patcher.hpp"
namespace ams::ldr {
namespace {
constexpr const char *NsoPatchesDirectory = "exefs_patches";
/* Exefs patches want to prevent modification of header, */
/* and also want to adjust offset relative to mapped location. */
constexpr size_t NsoPatchesProtectedSize = sizeof(NsoHeader);
constexpr size_t NsoPatchesProtectedOffset = sizeof(NsoHeader);
constexpr const char * const LoaderSdMountName = "#amsldr-sdpatch";
static_assert(sizeof(LoaderSdMountName) <= fs::MountNameLengthMax);
constinit os::SdkMutex g_ldr_sd_lock;
constinit bool g_mounted_sd;
constinit os::SdkMutex g_embedded_patch_lock;
constinit bool g_got_embedded_patch_settings;
constinit bool g_force_enable_usb30;
bool EnsureSdCardMounted() {
std::scoped_lock lk(g_ldr_sd_lock);
if (g_mounted_sd) {
return true;
}
if (!cfg::IsSdCardInitialized()) {
return false;
}
if (R_FAILED(fs::MountSdCard(LoaderSdMountName))) {
return false;
}
return (g_mounted_sd = true);
}
bool IsUsb30ForceEnabled() {
std::scoped_lock lk(g_embedded_patch_lock);
if (!g_got_embedded_patch_settings) {
g_force_enable_usb30 = spl::IsUsb30ForceEnabled();
g_got_embedded_patch_settings = true;
}
return g_force_enable_usb30;
}
consteval u8 ParseNybble(char c) {
AMS_ASSUME(('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f'));
if ('0' <= c && c <= '9') {
return c - '0' + 0x0;
} else if ('A' <= c && c <= 'F') {
return c - 'A' + 0xA;
} else /* if ('a' <= c && c <= 'f') */ {
return c - 'a' + 0xa;
}
}
consteval ro::ModuleId ParseModuleId(const char *str) {
/* Parse a static module id. */
ro::ModuleId module_id = {};
size_t ofs = 0;
while (str[0] != 0) {
AMS_ASSUME(ofs < sizeof(module_id));
AMS_ASSUME(str[1] != 0);
module_id.data[ofs] = (ParseNybble(str[0]) << 4) | (ParseNybble(str[1]) << 0);
str += 2;
ofs++;
}
return module_id;
}
struct EmbeddedPatchEntry {
uintptr_t offset;
const void * const data;
size_t size;
};
struct EmbeddedPatch {
ro::ModuleId module_id;
size_t num_entries;
const EmbeddedPatchEntry *entries;
};
#include "ldr_embedded_usb_patches.inc"
}
/* Apply IPS patches. */
void LocateAndApplyIpsPatchesToModule(const u8 *module_id_data, uintptr_t mapped_nso, size_t mapped_size) {
if (!EnsureSdCardMounted()) {
return;
}
ro::ModuleId module_id;
std::memcpy(std::addressof(module_id.data), module_id_data, sizeof(module_id.data));
ams::patcher::LocateAndApplyIpsPatchesToModule(LoaderSdMountName, NsoPatchesDirectory, NsoPatchesProtectedSize, NsoPatchesProtectedOffset, std::addressof(module_id), reinterpret_cast<u8 *>(mapped_nso), mapped_size);
}
/* Apply embedded patches. */
void ApplyEmbeddedPatchesToModule(const u8 *module_id_data, uintptr_t mapped_nso, size_t mapped_size) {
/* Make module id. */
ro::ModuleId module_id;
std::memcpy(std::addressof(module_id.data), module_id_data, sizeof(module_id.data));
if (IsUsb30ForceEnabled()) {
for (const auto &patch : Usb30ForceEnablePatches) {
if (std::memcmp(std::addressof(patch.module_id), std::addressof(module_id), sizeof(module_id)) == 0) {
for (size_t i = 0; i < patch.num_entries; ++i) {
const auto &entry = patch.entries[i];
if (entry.offset + entry.size <= mapped_size) {
std::memcpy(reinterpret_cast<void *>(mapped_nso + entry.offset), entry.data, entry.size);
}
}
}
}
}
}
}

View File

@@ -1,27 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::ldr {
/* Apply IPS patches. */
void LocateAndApplyIpsPatchesToModule(const u8 *module_id_data, uintptr_t mapped_nso, size_t mapped_size);
/* Apply embedded patches. */
void ApplyEmbeddedPatchesToModule(const u8 *module_id_data, uintptr_t mapped_nso, size_t mapped_size);
}

View File

@@ -1,772 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "ldr_capabilities.hpp"
#include "ldr_content_management.hpp"
#include "ldr_development_manager.hpp"
#include "ldr_launch_record.hpp"
#include "ldr_meta.hpp"
#include "ldr_patcher.hpp"
#include "ldr_process_creation.hpp"
#include "ldr_ro_manager.hpp"
#include "oc/oc_loader.hpp"
namespace ams::ldr {
namespace {
/* Convenience defines. */
constexpr size_t SystemResourceSizeMax = 0x1FE00000;
/* Types. */
enum NsoIndex {
Nso_Rtld = 0,
Nso_Main = 1,
Nso_Compat0 = 2,
Nso_Compat1 = 3,
Nso_Compat2 = 4,
Nso_Compat3 = 5,
Nso_Compat4 = 6,
Nso_Compat5 = 7,
Nso_Compat6 = 8,
Nso_Compat7 = 9,
Nso_Compat8 = 10,
Nso_Compat9 = 11,
Nso_SubSdk0 = 12,
Nso_SubSdk1 = 13,
Nso_SubSdk2 = 14,
Nso_SubSdk3 = 15,
Nso_SubSdk4 = 16,
Nso_SubSdk5 = 17,
Nso_SubSdk6 = 18,
Nso_SubSdk7 = 19,
Nso_SubSdk8 = 20,
Nso_SubSdk9 = 21,
Nso_Sdk = 22,
Nso_Count,
};
constexpr inline const char *NsoPaths[Nso_Count] = {
ENCODE_ATMOSPHERE_CODE_PATH("/rtld"),
ENCODE_ATMOSPHERE_CODE_PATH("/main"),
ENCODE_ATMOSPHERE_CMPT_PATH("/compat0"),
ENCODE_ATMOSPHERE_CMPT_PATH("/compat1"),
ENCODE_ATMOSPHERE_CMPT_PATH("/compat2"),
ENCODE_ATMOSPHERE_CMPT_PATH("/compat3"),
ENCODE_ATMOSPHERE_CMPT_PATH("/compat4"),
ENCODE_ATMOSPHERE_CMPT_PATH("/compat5"),
ENCODE_ATMOSPHERE_CMPT_PATH("/compat6"),
ENCODE_ATMOSPHERE_CMPT_PATH("/compat7"),
ENCODE_ATMOSPHERE_CMPT_PATH("/compat8"),
ENCODE_ATMOSPHERE_CMPT_PATH("/compat9"),
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk0"),
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk1"),
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk2"),
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk3"),
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk4"),
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk5"),
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk6"),
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk7"),
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk8"),
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk9"),
ENCODE_ATMOSPHERE_CODE_PATH("/sdk"),
};
constexpr const char *GetNsoPath(size_t idx) {
AMS_ABORT_UNLESS(idx < Nso_Count);
return NsoPaths[idx];
}
struct ProcessInfo {
os::NativeHandle process_handle;
uintptr_t args_address;
size_t args_size;
uintptr_t nso_address[Nso_Count];
size_t nso_size[Nso_Count];
};
/* Global NSO header cache. */
bool g_has_nso[Nso_Count];
NsoHeader g_nso_headers[Nso_Count];
bool g_is_pcv;
bool g_is_ptm;
Result ValidateProgramVersion(ncm::ProgramId program_id, u32 version) {
/* No version verification is done before 8.1.0. */
R_SUCCEED_IF(hos::GetVersion() < hos::Version_8_1_0);
/* No verification is done if development. */
R_SUCCEED_IF(IsDevelopmentForAntiDowngradeCheck());
/* TODO: Anti-downgrade checking does not make very much sense for us. Should we do anything? */
AMS_UNUSED(program_id, version);
R_SUCCEED();
}
/* Helpers. */
Result GetProgramInfoFromMeta(ProgramInfo *out, const Meta *meta) {
/* Copy basic info. */
out->main_thread_priority = meta->npdm->main_thread_priority;
out->default_cpu_id = meta->npdm->default_cpu_id;
out->main_thread_stack_size = meta->npdm->main_thread_stack_size;
out->program_id = meta->aci->program_id;
/* Copy access controls. */
size_t offset = 0;
#define COPY_ACCESS_CONTROL(source, which) \
({ \
const size_t size = meta->source->which##_size; \
R_UNLESS(offset + size <= sizeof(out->ac_buffer), ldr::ResultInternalError()); \
out->source##_##which##_size = size; \
std::memcpy(out->ac_buffer + offset, meta->source##_##which, size); \
offset += size; \
})
/* Copy all access controls to buffer. */
COPY_ACCESS_CONTROL(acid, sac);
COPY_ACCESS_CONTROL(aci, sac);
COPY_ACCESS_CONTROL(acid, fac);
COPY_ACCESS_CONTROL(aci, fah);
#undef COPY_ACCESS_CONTROL
/* Copy flags. */
out->flags = MakeProgramInfoFlag(static_cast<const util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32));
R_SUCCEED();
}
bool IsApplet(const Meta *meta) {
return (MakeProgramInfoFlag(static_cast<const util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32)) & ProgramInfoFlag_ApplicationTypeMask) == ProgramInfoFlag_Applet;
}
bool IsApplication(const Meta *meta) {
return (MakeProgramInfoFlag(static_cast<const util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32)) & ProgramInfoFlag_ApplicationTypeMask) == ProgramInfoFlag_Application;
}
Npdm::AddressSpaceType GetAddressSpaceType(const Meta *meta) {
return static_cast<Npdm::AddressSpaceType>((meta->npdm->flags & Npdm::MetaFlag_AddressSpaceTypeMask) >> Npdm::MetaFlag_AddressSpaceTypeShift);
}
Acid::PoolPartition GetPoolPartition(const Meta *meta) {
return static_cast<Acid::PoolPartition>((meta->acid->flags & Acid::AcidFlag_PoolPartitionMask) >> Acid::AcidFlag_PoolPartitionShift);
}
Result LoadAutoLoadHeaders(NsoHeader *nso_headers, bool *has_nso) {
/* Clear NSOs. */
std::memset(nso_headers, 0, sizeof(*nso_headers) * Nso_Count);
std::memset(has_nso, 0, sizeof(*has_nso) * Nso_Count);
for (size_t i = 0; i < Nso_Count; i++) {
fs::FileHandle file;
if (R_SUCCEEDED(fs::OpenFile(std::addressof(file), GetNsoPath(i), fs::OpenMode_Read))) {
ON_SCOPE_EXIT { fs::CloseFile(file); };
/* Read NSO header. */
size_t read_size;
R_TRY(fs::ReadFile(std::addressof(read_size), file, 0, nso_headers + i, sizeof(*nso_headers)));
R_UNLESS(read_size == sizeof(*nso_headers), ldr::ResultInvalidNso());
has_nso[i] = true;
}
}
R_SUCCEED();
}
Result CheckAutoLoad(const NsoHeader *nso_headers, const bool *has_nso) {
/* We must always have a main. */
R_UNLESS(has_nso[Nso_Main], ldr::ResultInvalidNso());
/* If we don't have an RTLD, we must only have a main. */
if (!has_nso[Nso_Rtld]) {
for (size_t i = Nso_Main + 1; i < Nso_Count; i++) {
R_UNLESS(!has_nso[i], ldr::ResultInvalidNso());
}
}
/* All NSOs must have zero text offset. */
for (size_t i = 0; i < Nso_Count; i++) {
R_UNLESS(nso_headers[i].text_dst_offset == 0, ldr::ResultInvalidNso());
}
R_SUCCEED();
}
constexpr const ncm::ProgramId UnqualifiedApprovalProgramIds[] = {
{ 0x010003F003A34000 }, /* Pokemon: Let's Go, Pikachu! */
{ 0x0100152000022000 }, /* Mario Kart 8 Deluxe */
{ 0x0100165003504000 }, /* Nintendo Labo Toy-Con 04: VR Kit */
{ 0x0100187003A36000 }, /* Pokemon: Let's Go, Eevee! */
{ 0x01002E5008C56000 }, /* Pokemon Sword [Live Tournament] */
{ 0x01002FF008C24000 }, /* Ring Fit Adventure */
{ 0x010049900F546001 }, /* Super Mario 3D All-Stars: Super Mario 64 */
{ 0x010057D00ECE4000 }, /* Nintendo Switch Online (Nintendo 64) [for Japan] */
{ 0x01006F8002326000 }, /* Animal Crossing: New Horizons */
{ 0x01006FB00F50E000 }, /* [???] */
{ 0x010070300F50C000 }, /* [???] */
{ 0x010075100E8EC000 }, /* 马力欧卡丁车8 豪华版 [Mario Kart 8 Deluxe for China] */
{ 0x01008DB008C2C000 }, /* Pokemon Shield */
{ 0x01009AD008C4C000 }, /* Pokemon: Let's Go, Pikachu! [Kiosk] */
{ 0x0100A66003384000 }, /* Hulu */
{ 0x0100ABF008968000 }, /* Pokemon Sword */
{ 0x0100C9A00ECE6000 }, /* Nintendo Switch Online (Nintendo 64) [for America] */
{ 0x0100ED100BA3A000 }, /* Mario Kart Live: Home Circuit */
{ 0x0100F38011CFE000 }, /* Animal Crossing: New Horizons Island Transfer Tool */
{ 0x0100F6B011028000 }, /* 健身环大冒险 [Ring Fit Adventure for China] */
};
/* Check that the unqualified approval programs are sorted. */
static_assert([]() -> bool {
for (size_t i = 0; i < util::size(UnqualifiedApprovalProgramIds) - 1; ++i) {
if (UnqualifiedApprovalProgramIds[i].value >= UnqualifiedApprovalProgramIds[i + 1].value) {
return false;
}
}
return true;
}());
bool IsUnqualifiedApprovalProgramId(ncm::ProgramId program_id) {
/* Check if the program id is one with unqualified approval. */
return std::binary_search(std::begin(UnqualifiedApprovalProgramIds), std::end(UnqualifiedApprovalProgramIds), program_id);
}
bool IsUnqualifiedApproval(const Meta *meta) {
/* If the meta has unqualified approval flag, it's unqualified approval. */
if (meta->acid->flags & ldr::Acid::AcidFlag_UnqualifiedApproval) {
return true;
}
/* If the unqualified approval flag is not set, the program must be an application. */
if (!IsApplication(meta)) {
return false;
}
/* The program id must be a force unqualified approval program id. */
return IsUnqualifiedApprovalProgramId(meta->acid->program_id_min) && meta->acid->program_id_min == meta->acid->program_id_max;
}
Result ValidateMeta(const Meta *meta, const ncm::ProgramLocation &loc, const fs::CodeVerificationData &code_verification_data) {
/* Validate version. */
R_TRY(ValidateProgramVersion(loc.program_id, meta->npdm->version));
/* Validate program id. */
R_UNLESS(meta->aci->program_id >= meta->acid->program_id_min, ldr::ResultInvalidProgramId());
g_is_pcv = meta->aci->program_id == ncm::SystemProgramId::Pcv;
g_is_ptm = meta->aci->program_id == ncm::SystemProgramId::Ptm;
R_UNLESS(meta->aci->program_id <= meta->acid->program_id_max, ldr::ResultInvalidProgramId());
/* Validate the kernel capabilities. */
R_TRY(TestCapability(static_cast<const util::BitPack32 *>(meta->acid_kac), meta->acid->kac_size / sizeof(util::BitPack32), static_cast<const util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32)));
/* If we have data to validate, validate it. */
if (meta->check_verification_data) {
const u8 *sig = code_verification_data.signature;
const size_t sig_size = sizeof(code_verification_data.signature);
const u8 *mod = static_cast<u8 *>(meta->modulus);
const size_t mod_size = crypto::Rsa2048PssSha256Verifier::ModulusSize;
const u8 *exp = fssystem::GetAcidSignatureKeyPublicExponent();
const size_t exp_size = fssystem::AcidSignatureKeyPublicExponentSize;
const u8 *hsh = code_verification_data.target_hash;
const size_t hsh_size = sizeof(code_verification_data.target_hash);
const bool is_signature_valid = crypto::VerifyRsa2048PssSha256WithHash(sig, sig_size, mod, mod_size, exp, exp_size, hsh, hsh_size);
/* If the signature check fails, we need to check if this is allowable. */
if (!is_signature_valid) {
/* We have to enforce signature checks on prod and when we have a signature to check on dev. */
R_UNLESS(IsDevelopmentForAcidProductionCheck(), ldr::ResultInvalidNcaSignature());
R_UNLESS(!code_verification_data.has_data, ldr::ResultInvalidNcaSignature());
/* There was no signature to check on dev. Check if this is acceptable. */
R_UNLESS(IsUnqualifiedApproval(meta), ldr::ResultInvalidNcaSignature());
}
}
/* All good. */
R_SUCCEED();
}
Result GetCreateProcessFlags(u32 *out, const Meta *meta, const u32 ldr_flags) {
const u8 meta_flags = meta->npdm->flags;
u32 flags = 0;
/* Set Is64Bit. */
if (meta_flags & Npdm::MetaFlag_Is64Bit) {
flags |= svc::CreateProcessFlag_Is64Bit;
}
/* Set AddressSpaceType. */
switch (GetAddressSpaceType(meta)) {
case Npdm::AddressSpaceType_32Bit:
flags |= svc::CreateProcessFlag_AddressSpace32Bit;
break;
case Npdm::AddressSpaceType_64BitDeprecated:
flags |= svc::CreateProcessFlag_AddressSpace64BitDeprecated;
break;
case Npdm::AddressSpaceType_32BitWithoutAlias:
flags |= svc::CreateProcessFlag_AddressSpace32BitWithoutAlias;
break;
case Npdm::AddressSpaceType_64Bit:
flags |= svc::CreateProcessFlag_AddressSpace64Bit;
break;
default:
R_THROW(ldr::ResultInvalidMeta());
}
/* Set Enable Debug. */
if (ldr_flags & CreateProcessFlag_EnableDebug) {
flags |= svc::CreateProcessFlag_EnableDebug;
}
/* Set Enable ASLR. */
if (!(ldr_flags & CreateProcessFlag_DisableAslr)) {
flags |= svc::CreateProcessFlag_EnableAslr;
}
/* Set Is Application. */
if (IsApplication(meta)) {
flags |= svc::CreateProcessFlag_IsApplication;
/* 7.0.0+: Set OptimizeMemoryAllocation if relevant. */
if (hos::GetVersion() >= hos::Version_7_0_0) {
if (meta_flags & Npdm::MetaFlag_OptimizeMemoryAllocation) {
flags |= svc::CreateProcessFlag_OptimizeMemoryAllocation;
}
}
}
/* 5.0.0+ Set Pool Partition. */
if (hos::GetVersion() >= hos::Version_5_0_0) {
/* TODO: Nintendo no longer accepts Applet when pool partition == application. Would this break hbl/anything else in the hb ecosystem? */
/* TODO: Nintendo uses a helper bool MakeSvcPoolPartitionFlag(u32 *out, Acid::PoolPartition partition); */
switch (GetPoolPartition(meta)) {
case Acid::PoolPartition_Application:
if (IsApplet(meta)) {
flags |= svc::CreateProcessFlag_PoolPartitionApplet;
} else {
flags |= svc::CreateProcessFlag_PoolPartitionApplication;
}
break;
case Acid::PoolPartition_Applet:
flags |= svc::CreateProcessFlag_PoolPartitionApplet;
break;
case Acid::PoolPartition_System:
flags |= svc::CreateProcessFlag_PoolPartitionSystem;
break;
case Acid::PoolPartition_SystemNonSecure:
flags |= svc::CreateProcessFlag_PoolPartitionSystemNonSecure;
break;
default:
R_THROW(ldr::ResultInvalidMeta());
}
} else if (hos::GetVersion() >= hos::Version_4_0_0) {
/* On 4.0.0+, the corresponding bit was simply "UseSecureMemory". */
if (meta->acid->flags & Acid::AcidFlag_DeprecatedUseSecureMemory) {
flags |= svc::CreateProcessFlag_DeprecatedUseSecureMemory;
}
}
/* 11.0.0+/meso Set Disable DAS merge. */
if (meta_flags & Npdm::MetaFlag_DisableDeviceAddressSpaceMerge) {
flags |= svc::CreateProcessFlag_DisableDeviceAddressSpaceMerge;
}
/* 18.0.0+/meso Set Alias region extra size. */
if (meta_flags & Npdm::MetaFlag_EnableAliasRegionExtraSize) {
flags |= svc::CreateProcessFlag_EnableAliasRegionExtraSize;
}
*out = flags;
R_SUCCEED();
}
Result GetCreateProcessParameter(svc::CreateProcessParameter *out, const Meta *meta, u32 flags, os::NativeHandle resource_limit) {
/* Clear output. */
std::memset(out, 0, sizeof(*out));
/* Set name, version, program id, resource limit handle. */
std::memcpy(out->name, meta->npdm->program_name, sizeof(out->name) - 1);
out->version = meta->npdm->version;
out->program_id = meta->aci->program_id.value;
out->reslimit = resource_limit;
/* Set flags. */
R_TRY(GetCreateProcessFlags(std::addressof(out->flags), meta, flags));
/* 3.0.0+ System Resource Size. */
if (hos::GetVersion() >= hos::Version_3_0_0) {
/* Validate size is aligned. */
R_UNLESS(util::IsAligned(meta->npdm->system_resource_size, os::MemoryBlockUnitSize), ldr::ResultInvalidSize());
/* Validate system resource usage. */
if (meta->npdm->system_resource_size) {
/* Process must be 64-bit. */
R_UNLESS((out->flags & svc::CreateProcessFlag_AddressSpace64Bit), ldr::ResultInvalidMeta());
/* Process must be application or applet. */
R_UNLESS(IsApplication(meta) || IsApplet(meta), ldr::ResultInvalidMeta());
/* Size must be less than or equal to max. */
R_UNLESS(meta->npdm->system_resource_size <= SystemResourceSizeMax, ldr::ResultInvalidMeta());
}
out->system_resource_num_pages = meta->npdm->system_resource_size >> 12;
}
R_SUCCEED();
}
u64 GenerateSecureRandom(u64 max) {
/* Generate a cryptographically random number. */
u64 rand;
crypto::GenerateCryptographicallyRandomBytes(std::addressof(rand), sizeof(rand));
/* Coerce into range. */
return rand % (max + 1);
}
Result DecideAddressSpaceLayout(ProcessInfo *out, svc::CreateProcessParameter *out_param, const NsoHeader *nso_headers, const bool *has_nso, const ArgumentStore::Entry *argument) {
/* Clear output. */
out->args_address = 0;
out->args_size = 0;
std::memset(out->nso_address, 0, sizeof(out->nso_address));
std::memset(out->nso_size, 0, sizeof(out->nso_size));
size_t total_size = 0;
bool argument_allocated = false;
/* Calculate base offsets. */
for (size_t i = 0; i < Nso_Count; i++) {
if (has_nso[i]) {
out->nso_address[i] = total_size;
const size_t text_end = nso_headers[i].text_dst_offset + nso_headers[i].text_size;
const size_t ro_end = nso_headers[i].ro_dst_offset + nso_headers[i].ro_size;
const size_t rw_end = nso_headers[i].rw_dst_offset + nso_headers[i].rw_size + nso_headers[i].bss_size;
out->nso_size[i] = text_end;
out->nso_size[i] = std::max(out->nso_size[i], ro_end);
out->nso_size[i] = std::max(out->nso_size[i], rw_end);
out->nso_size[i] = util::AlignUp(out->nso_size[i], os::MemoryPageSize);
total_size += out->nso_size[i];
if (!argument_allocated && argument != nullptr) {
out->args_address = total_size;
out->args_size = util::AlignUp(2 * sizeof(u32) + argument->argument_size * 2 + ArgumentStore::ArgumentBufferSize, os::MemoryPageSize);
total_size += out->args_size;
argument_allocated = true;
}
}
}
/* Calculate ASLR. */
uintptr_t aslr_start = 0;
size_t aslr_size = 0;
if (hos::GetVersion() >= hos::Version_2_0_0) {
switch (out_param->flags & svc::CreateProcessFlag_AddressSpaceMask) {
case svc::CreateProcessFlag_AddressSpace32Bit:
case svc::CreateProcessFlag_AddressSpace32BitWithoutAlias:
aslr_start = svc::AddressSmallMap32Start;
aslr_size = svc::AddressSmallMap32Size;
break;
case svc::CreateProcessFlag_AddressSpace64BitDeprecated:
aslr_start = svc::AddressSmallMap36Start;
aslr_size = svc::AddressSmallMap36Size;
break;
case svc::CreateProcessFlag_AddressSpace64Bit:
aslr_start = svc::AddressMap39Start;
aslr_size = svc::AddressMap39Size;
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
} else {
/* On 1.0.0, only 2 address space types existed. */
if (out_param->flags & svc::CreateProcessFlag_AddressSpace64BitDeprecated) {
aslr_start = svc::AddressSmallMap36Start;
aslr_size = svc::AddressSmallMap36Size;
} else {
aslr_start = svc::AddressSmallMap32Start;
aslr_size = svc::AddressSmallMap32Size;
}
}
R_UNLESS(total_size <= aslr_size, svc::ResultOutOfMemory());
/* Set Create Process output. */
uintptr_t aslr_slide = 0;
size_t free_size = (aslr_size - total_size);
if (out_param->flags & svc::CreateProcessFlag_EnableAslr) {
aslr_slide = GenerateSecureRandom(free_size / os::MemoryBlockUnitSize) * os::MemoryBlockUnitSize;
}
/* Set out. */
aslr_start += aslr_slide;
for (size_t i = 0; i < Nso_Count; i++) {
if (has_nso[i]) {
out->nso_address[i] += aslr_start;
}
}
if (out->args_address) {
out->args_address += aslr_start;
}
out_param->code_address = aslr_start;
out_param->code_num_pages = total_size >> 12;
R_SUCCEED();
}
Result LoadAutoLoadModuleSegment(fs::FileHandle file, const NsoHeader::SegmentInfo *segment, size_t file_size, const u8 *file_hash, bool is_compressed, bool check_hash, uintptr_t map_base, uintptr_t map_end) {
/* Select read size based on compression. */
if (!is_compressed) {
file_size = segment->size;
}
/* Validate size. */
R_UNLESS(file_size <= segment->size, ldr::ResultInvalidNso());
R_UNLESS(segment->size <= std::numeric_limits<s32>::max(), ldr::ResultInvalidNso());
/* Load data from file. */
uintptr_t load_address = is_compressed ? map_end - file_size : map_base;
size_t read_size;
R_TRY(fs::ReadFile(std::addressof(read_size), file, segment->file_offset, reinterpret_cast<void *>(load_address), file_size));
R_UNLESS(read_size == file_size, ldr::ResultInvalidNso());
/* Uncompress if necessary. */
if (is_compressed) {
bool decompressed = (util::DecompressLZ4(reinterpret_cast<void *>(map_base), segment->size, reinterpret_cast<const void *>(load_address), file_size) == static_cast<int>(segment->size));
R_UNLESS(decompressed, ldr::ResultInvalidNso());
}
/* Check hash if necessary. */
if (check_hash) {
u8 hash[crypto::Sha256Generator::HashSize];
crypto::GenerateSha256(hash, sizeof(hash), reinterpret_cast<void *>(map_base), segment->size);
R_UNLESS(std::memcmp(hash, file_hash, sizeof(hash)) == 0, ldr::ResultInvalidNso());
}
R_SUCCEED();
}
Result LoadAutoLoadModule(os::NativeHandle process_handle, fs::FileHandle file, const NsoHeader *nso_header, uintptr_t nso_address, size_t nso_size, bool prevent_code_reads) {
/* Map and read data from file. */
{
/* Map the process memory. */
void *mapped_memory = nullptr;
R_TRY(os::MapProcessMemory(std::addressof(mapped_memory), process_handle, nso_address, nso_size, GenerateSecureRandom));
ON_SCOPE_EXIT { os::UnmapProcessMemory(mapped_memory, process_handle, nso_address, nso_size); };
const uintptr_t map_address = reinterpret_cast<uintptr_t>(mapped_memory);
/* Load NSO segments. */
R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Text]), nso_header->text_compressed_size, nso_header->text_hash, (nso_header->flags & NsoHeader::Flag_CompressedText) != 0,
(nso_header->flags & NsoHeader::Flag_CheckHashText) != 0, map_address + nso_header->text_dst_offset, map_address + nso_size));
R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Ro]), nso_header->ro_compressed_size, nso_header->ro_hash, (nso_header->flags & NsoHeader::Flag_CompressedRo) != 0,
(nso_header->flags & NsoHeader::Flag_CheckHashRo) != 0, map_address + nso_header->ro_dst_offset, map_address + nso_size));
R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Rw]), nso_header->rw_compressed_size, nso_header->rw_hash, (nso_header->flags & NsoHeader::Flag_CompressedRw) != 0,
(nso_header->flags & NsoHeader::Flag_CheckHashRw) != 0, map_address + nso_header->rw_dst_offset, map_address + nso_size));
/* Clear unused space to zero. */
const size_t text_end = nso_header->text_dst_offset + nso_header->text_size;
const size_t ro_end = nso_header->ro_dst_offset + nso_header->ro_size;
const size_t rw_end = nso_header->rw_dst_offset + nso_header->rw_size;
std::memset(reinterpret_cast<void *>(map_address + 0), 0, nso_header->text_dst_offset);
std::memset(reinterpret_cast<void *>(map_address + text_end), 0, nso_header->ro_dst_offset - text_end);
std::memset(reinterpret_cast<void *>(map_address + ro_end), 0, nso_header->rw_dst_offset - ro_end);
std::memset(reinterpret_cast<void *>(map_address + rw_end), 0, nso_header->bss_size);
/* Apply embedded patches. */
ApplyEmbeddedPatchesToModule(nso_header->module_id, map_address, nso_size);
/* Apply IPS patches. */
LocateAndApplyIpsPatchesToModule(nso_header->module_id, map_address, nso_size);
if (g_is_pcv) {
oc::pcv::Patch(map_address, nso_size);
}
if (g_is_ptm) {
oc::ptm::Patch(map_address, nso_size);
}
}
/* Set permissions. */
const size_t text_size = util::AlignUp(nso_header->text_size, os::MemoryPageSize);
const size_t ro_size = util::AlignUp(nso_header->ro_size, os::MemoryPageSize);
const size_t rw_size = util::AlignUp(nso_header->rw_size + nso_header->bss_size, os::MemoryPageSize);
if (text_size) {
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->text_dst_offset, text_size, prevent_code_reads ? os::MemoryPermission_ExecuteOnly : os::MemoryPermission_ReadExecute));
}
if (ro_size) {
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->ro_dst_offset, ro_size, os::MemoryPermission_ReadOnly));
}
if (rw_size) {
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->rw_dst_offset, rw_size, os::MemoryPermission_ReadWrite));
}
R_SUCCEED();
}
Result LoadAutoLoadModules(const ProcessInfo *process_info, const NsoHeader *nso_headers, const bool *has_nso, const ArgumentStore::Entry *argument, bool prevent_code_reads) {
/* Load each NSO. */
for (size_t i = 0; i < Nso_Count; i++) {
if (has_nso[i]) {
fs::FileHandle file;
R_TRY(fs::OpenFile(std::addressof(file), GetNsoPath(i), fs::OpenMode_Read));
ON_SCOPE_EXIT { fs::CloseFile(file); };
R_TRY(LoadAutoLoadModule(process_info->process_handle, file, nso_headers + i, process_info->nso_address[i], process_info->nso_size[i], prevent_code_reads));
}
}
/* Load arguments, if present. */
if (argument != nullptr) {
/* Write argument data into memory. */
{
void *map_address = nullptr;
R_TRY(os::MapProcessMemory(std::addressof(map_address), process_info->process_handle, process_info->args_address, process_info->args_size, GenerateSecureRandom));
ON_SCOPE_EXIT { os::UnmapProcessMemory(map_address, process_info->process_handle, process_info->args_address, process_info->args_size); };
ProgramArguments *args = static_cast<ProgramArguments *>(map_address);
std::memset(args, 0, sizeof(*args));
args->allocated_size = process_info->args_size;
args->arguments_size = argument->argument_size;
std::memcpy(args->arguments, argument->argument, argument->argument_size);
}
/* Set argument region permissions. */
/* NOTE: Nintendo uses svc::SetProcessMemoryPermission directly here. */
R_TRY(os::SetProcessMemoryPermission(process_info->process_handle, process_info->args_address, process_info->args_size, os::MemoryPermission_ReadWrite));
}
R_SUCCEED();
}
Result CreateProcessAndLoadAutoLoadModules(ProcessInfo *out, const Meta *meta, const NsoHeader *nso_headers, const bool *has_nso, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit) {
/* Get CreateProcessParameter. */
svc::CreateProcessParameter param;
R_TRY(GetCreateProcessParameter(std::addressof(param), meta, flags, resource_limit));
/* Decide on an NSO layout. */
R_TRY(DecideAddressSpaceLayout(out, std::addressof(param), nso_headers, has_nso, argument));
/* Actually create process. */
svc::Handle process_handle;
R_TRY(svc::CreateProcess(std::addressof(process_handle), std::addressof(param), static_cast<const u32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(u32)));
/* Set the output handle, and ensure that if we fail after this point we clean it up. */
out->process_handle = process_handle;
ON_RESULT_FAILURE { svc::CloseHandle(process_handle); };
/* Load all auto load modules. */
R_RETURN(LoadAutoLoadModules(out, nso_headers, has_nso, argument, (meta->npdm->flags & ldr::Npdm::MetaFlag_PreventCodeReads) != 0));
}
}
/* Process Creation API. */
Result CreateProcess(os::NativeHandle *out, PinId pin_id, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &override_status, const char *path, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit, const ldr::ProgramAttributes &attrs) {
/* Mount code. */
AMS_UNUSED(path);
ScopedCodeMount mount(loc, override_status, attrs);
R_TRY(mount.GetResult());
/* Load meta, possibly from cache. */
Meta meta;
R_TRY(LoadMetaFromCache(std::addressof(meta), loc, override_status, attrs.platform));
/* Validate meta. */
R_TRY(ValidateMeta(std::addressof(meta), loc, mount.GetCodeVerificationData()));
/* Load, validate NSO headers. */
R_TRY(LoadAutoLoadHeaders(g_nso_headers, g_has_nso));
R_TRY(CheckAutoLoad(g_nso_headers, g_has_nso));
/* Actually create the process and load NSOs into process memory. */
ProcessInfo info;
R_TRY(CreateProcessAndLoadAutoLoadModules(std::addressof(info), std::addressof(meta), g_nso_headers, g_has_nso, argument, flags, resource_limit));
/* Register NSOs with the RoManager. */
{
/* Nintendo doesn't validate this get, but we do. */
os::ProcessId process_id = os::GetProcessId(info.process_handle);
/* Register new process. */
const auto as_type = GetAddressSpaceType(std::addressof(meta));
RoManager::GetInstance().RegisterProcess(pin_id, process_id, meta.aci->program_id, as_type == Npdm::AddressSpaceType_64Bit || as_type == Npdm::AddressSpaceType_64BitDeprecated);
/* Register all NSOs. */
for (size_t i = 0; i < Nso_Count; i++) {
if (g_has_nso[i]) {
RoManager::GetInstance().AddNso(pin_id, g_nso_headers[i].module_id, info.nso_address[i], info.nso_size[i]);
}
}
}
/* If we're overriding for HBL, perform HTML document redirection. */
if (override_status.IsHbl()) {
/* Don't validate result, failure is okay. */
RedirectHtmlDocumentPathForHbl(loc);
}
/* Clear the external code for the program. */
fssystem::DestroyExternalCode(loc.program_id);
/* Note that we've created the program. */
SetLaunchedBootProgram(loc.program_id);
/* Move the process handle to output. */
*out = info.process_handle;
R_SUCCEED();
}
Result GetProgramInfo(ProgramInfo *out, cfg::OverrideStatus *out_status, const ncm::ProgramLocation &loc, const char *path, const ldr::ProgramAttributes &attrs) {
Meta meta;
/* Load Meta. */
{
AMS_UNUSED(path);
ScopedCodeMount mount(loc, attrs);
R_TRY(mount.GetResult());
R_TRY(LoadMeta(std::addressof(meta), loc, mount.GetOverrideStatus(), attrs.platform, false));
if (out_status != nullptr) {
*out_status = mount.GetOverrideStatus();
}
}
return GetProgramInfoFromMeta(out, std::addressof(meta));
}
Result PinProgram(PinId *out_id, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &override_status) {
R_UNLESS(RoManager::GetInstance().Allocate(out_id, loc, override_status), ldr::ResultMaxProcess());
R_SUCCEED();
}
Result UnpinProgram(PinId id) {
R_UNLESS(RoManager::GetInstance().Free(id), ldr::ResultNotPinned());
R_SUCCEED();
}
Result GetProcessModuleInfo(u32 *out_count, ldr::ModuleInfo *out, size_t max_out_count, os::ProcessId process_id) {
R_UNLESS(RoManager::GetInstance().GetProcessModuleInfo(out_count, out, max_out_count, process_id), ldr::ResultNotPinned());
R_SUCCEED();
}
Result GetProgramLocationAndOverrideStatusFromPinId(ncm::ProgramLocation *out, cfg::OverrideStatus *out_status, PinId pin_id) {
R_UNLESS(RoManager::GetInstance().GetProgramLocationAndStatus(out, out_status, pin_id), ldr::ResultNotPinned());
R_SUCCEED();
}
}

View File

@@ -1,32 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "ldr_argument_store.hpp"
namespace ams::ldr {
/* Process Creation API. */
Result CreateProcess(os::NativeHandle *out, PinId pin_id, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &override_status, const char *path, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit, const ldr::ProgramAttributes &attrs);
Result GetProgramInfo(ProgramInfo *out, cfg::OverrideStatus *out_status, const ncm::ProgramLocation &loc, const char *path, const ldr::ProgramAttributes &attrs);
Result PinProgram(PinId *out_id, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &override_status);
Result UnpinProgram(PinId id);
Result GetProcessModuleInfo(u32 *out_count, ldr::ModuleInfo *out, size_t max_out_count, os::ProcessId process_id);
Result GetProgramLocationAndOverrideStatusFromPinId(ncm::ProgramLocation *out, cfg::OverrideStatus *out_status, PinId pin_id);
}

View File

@@ -1,181 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "ldr_ro_manager.hpp"
namespace ams::ldr {
bool RoManager::Allocate(PinId *out, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &status) {
/* Ensure that output pin id is set. */
*out = InvalidPinId;
/* Allocate a process info. */
auto *found = this->AllocateProcessInfo();
if (found == nullptr) {
return false;
}
/* Setup the process info. */
std::memset(found, 0, sizeof(*found));
found->pin_id = { ++m_pin_id };
found->program_location = loc;
found->override_status = status;
found->in_use = true;
/* Set the output pin id. */
*out = found->pin_id;
return true;
}
bool RoManager::Free(PinId pin_id) {
/* Find the process. */
auto *found = this->FindProcessInfo(pin_id);
if (found == nullptr) {
return false;
}
/* Set the process as not in use. */
found->in_use = false;
/* Set all the process's nsos as not in use. */
for (auto i = 0; i < NsoCount; ++i) {
found->nso_infos[i].in_use = false;
}
return true;
}
void RoManager::RegisterProcess(PinId pin_id, os::ProcessId process_id, ncm::ProgramId program_id, bool is_64_bit_address_space) {
/* Find the process. */
auto *found = this->FindProcessInfo(pin_id);
if (found == nullptr) {
return;
}
/* Set the process id and program id. */
found->process_id = process_id;
found->program_id = program_id;
AMS_UNUSED(is_64_bit_address_space);
}
bool RoManager::GetProgramLocationAndStatus(ncm::ProgramLocation *out, cfg::OverrideStatus *out_status, PinId pin_id) {
/* Find the process. */
auto *found = this->FindProcessInfo(pin_id);
if (found == nullptr) {
return false;
}
/* Set the output location/status. */
*out = found->program_location;
*out_status = found->override_status;
return true;
}
void RoManager::AddNso(PinId pin_id, const u8 *module_id, u64 address, u64 size) {
/* Find the process. */
auto *found = this->FindProcessInfo(pin_id);
if (found == nullptr) {
return;
}
/* Allocate an nso. */
auto *info = this->AllocateNsoInfo(found);
if (info == nullptr) {
return;
}
/* Copy the information into the nso info. */
std::memcpy(info->module_info.module_id, module_id, sizeof(info->module_info.module_id));
info->module_info.address = address;
info->module_info.size = size;
info->in_use = true;
}
bool RoManager::GetProcessModuleInfo(u32 *out_count, ModuleInfo *out, size_t max_out_count, os::ProcessId process_id) {
/* Find the process. */
auto *found = this->FindProcessInfo(process_id);
if (found == nullptr) {
return false;
}
/* Copy allocated nso module infos. */
size_t count = 0;
for (auto i = 0; i < NsoCount && count < max_out_count; ++i) {
/* Skip unallocated nsos. */
if (!found->nso_infos[i].in_use) {
continue;
}
/* Copy out the module info. */
out[count++] = found->nso_infos[i].module_info;
}
/* Set the output count. */
*out_count = count;
return true;
}
RoManager::ProcessInfo *RoManager::AllocateProcessInfo() {
for (auto i = 0; i < ProcessCount; ++i) {
if (!m_processes[i].in_use) {
return m_processes + i;
}
}
return nullptr;
}
RoManager::ProcessInfo *RoManager::FindProcessInfo(PinId pin_id) {
for (auto i = 0; i < ProcessCount; ++i) {
if (m_processes[i].in_use && m_processes[i].pin_id == pin_id) {
return m_processes + i;
}
}
return nullptr;
}
RoManager::ProcessInfo *RoManager::FindProcessInfo(os::ProcessId process_id) {
for (auto i = 0; i < ProcessCount; ++i) {
if (m_processes[i].in_use && m_processes[i].process_id == process_id) {
return m_processes + i;
}
}
return nullptr;
}
RoManager::ProcessInfo *RoManager::FindProcessInfo(ncm::ProgramId program_id) {
for (auto i = 0; i < ProcessCount; ++i) {
if (m_processes[i].in_use && m_processes[i].program_id == program_id) {
return m_processes + i;
}
}
return nullptr;
}
RoManager::NsoInfo *RoManager::AllocateNsoInfo(ProcessInfo *info) {
for (auto i = 0; i < NsoCount; ++i) {
if (!info->nso_infos[i].in_use) {
return info->nso_infos + i;
}
}
return nullptr;
}
}

View File

@@ -1,65 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::ldr {
class RoManager {
AMS_CONSTINIT_SINGLETON_TRAITS(RoManager);
public:
static constexpr PinId InvalidPinId = {};
static constexpr int ProcessCount = 0x40;
static constexpr int NsoCount = 0x20;
private:
struct NsoInfo {
bool in_use;
ldr::ModuleInfo module_info;
};
struct ProcessInfo {
bool in_use;
PinId pin_id;
os::ProcessId process_id;
ncm::ProgramId program_id;
cfg::OverrideStatus override_status;
ncm::ProgramLocation program_location;
NsoInfo nso_infos[NsoCount];
};
private:
ProcessInfo m_processes[ProcessCount];
u64 m_pin_id;
public:
bool Allocate(PinId *out, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &status);
bool Free(PinId pin_id);
void RegisterProcess(PinId pin_id, os::ProcessId process_id, ncm::ProgramId program_id, bool is_64_bit_address_space);
bool GetProgramLocationAndStatus(ncm::ProgramLocation *out, cfg::OverrideStatus *out_status, PinId pin_id);
void AddNso(PinId pin_id, const u8 *module_id, u64 address, u64 size);
bool GetProcessModuleInfo(u32 *out_count, ModuleInfo *out, size_t max_out_count, os::ProcessId process_id);
private:
ProcessInfo *AllocateProcessInfo();
ProcessInfo *FindProcessInfo(PinId pin_id);
ProcessInfo *FindProcessInfo(os::ProcessId process_id);
ProcessInfo *FindProcessInfo(ncm::ProgramId program_id);
NsoInfo *AllocateNsoInfo(ProcessInfo *info);
};
}

View File

@@ -44,7 +44,7 @@ volatile CustomizeTable C = {
* Value should be divided evenly by 12'500.
* Not enabled by default.
*/
.commonEmcMemVolt = 1212500,
.commonEmcMemVolt = 1237500,
/* Erista CPU:
* - Max Voltage in mV
@@ -77,41 +77,45 @@ volatile CustomizeTable C = {
* - System instabilities
* - NAND corruption
*/
.marikoEmcMaxClock = 2400000,
.marikoEmcMaxClock = 2428800,
/* - EMC Vddq (Mariko Only) Voltage in uV
* Range: 550'000 to 650'000 uV
* Value should be dicvided evenly by 5'000
* Value should be divided evenly by 5'000
* Default: 600'000
* Not enabled by default.
* This will not work without sys-clk-OC.
*/
.marikoEmcVddqVolt = 640000,
.marikoCpuUV = 1,
.marikoCpuUV = 2,
.marikoGpuUV = 2,
.commonGpuVoltOffset = 0,
.commonGpuVoltOffset = 10,
.marikoEmcDvbShift = 5,
.tRAS = 30,
.tRCD = 13,
.tREFpb = 9999, // MAX REFI
.tRFCpb = 80, // Eos value x2
.tRPpb = 13,
.tRRD = 3,
.R2W = 6,
.tWTR = 5,
.tFAW = 12,
.tWR = 8,
.ramTimingPresetOne = 4, // T1-3 EOS
.ramTimingPresetTwo = 2, // T4
.ramTimingPresetThree = 2, // Try all values from 0-6
.ramTimingPresetFour = 2, // EOS T5
.ramTimingPresetFive = 2, // EOS T7
.ramTimingPresetSix = 2, // EOS T8
.ramTimingPresetSeven = 2,
.marikoGpuVoltArray = {635, 635, 635, 635, 635, 635, 635, 635, 635, 635, 635, 635, 660, 685, 715, 745, 765, 785},
.marikoCpuHighVoltOffset = 70,
.marikoB3 = 179,
.marikoCpuHighUV = 0,
.marikoCpuHighUV = 8,
/* Advanced Settings:
* - Erista CPU DVFS Table:
@@ -162,15 +166,10 @@ volatile CustomizeTable C = {
{ 1887000, { 1609246, -37515, 27 }, { 1120000 } },
{ 1963500, { 1675751, -38635, 27 }, { 1120000 } },
// Appending table
{ 2091000, { 1580725, -33235, 113 }, { 1120000 } },
{ 2193000, { 1580725, -33235, 113 }, { 1235000 } },
{ 2295000, { 1635431, -34095, 113 }, { 1235000 } },
{ 2397000, { 1702903, -34955, 113 }, { 1235000 } },
{ 2499000, { 1754400, -35643, 113 }, { 1235000 } },
{ 2601000, { 1805897, -36331, 113 }, { 1235000 } },
{ 2703000, { 1857394, -37019, 113 }, { 1235000 } },
{ 2805000, { 1908891, -37707, 113 }, { 1235000 } },
{ 2907000, { 1960388, -38395, 113 }, { 1250000 } },
{ 2091000, { 1716501, -39395, 27 }, { 1235000 } },
{ 2193000, { 1775132, -40505, 27 }, { 1235000 } },
{ 2295000, { 1866287, -42005, 27 }, { 1235000 } },
{ 2397000, { 1961107, -43506, 27 }, { 1235000 } },
},
.marikoCpuDvfsTableSLT = {

View File

@@ -57,16 +57,13 @@ typedef struct CustomizeTable {
u32 commonGpuVoltOffset;
// advanced config
u32 marikoEmcDvbShift;
const u32 tRAS;
const u32 tRCD;
const u32 tREFpb;
const u32 tRFCpb;
const u32 tRPpb;
const double tRRD;
const u32 R2W;
const u32 tWTR;
const u32 tFAW;
const u32 tWR;
u32 ramTimingPresetOne;
u32 ramTimingPresetTwo;
u32 ramTimingPresetThree;
u32 ramTimingPresetFour;
u32 ramTimingPresetFive;
u32 ramTimingPresetSix;
u32 ramTimingPresetSeven;
//
u32 marikoGpuVoltArray[18];
u8 marikoCpuHighVoltOffset;

View File

@@ -26,7 +26,7 @@ struct MarikoTiming {
uint32_t emc_rfc_slr;
uint32_t emc_ras;
uint32_t emc_rp;
uint32_t emcR2W;
uint32_t emc_r2w;
uint32_t emc_w2r;
uint32_t emc_r2p;
uint32_t emc_w2p;
@@ -257,7 +257,7 @@ struct EristaTiming {
uint32_t emc_rfc_slr;
uint32_t emc_ras;
uint32_t emc_rp;
uint32_t emcR2W;
uint32_t emc_r2w;
uint32_t emc_w2r;
uint32_t emc_r2p;
uint32_t emc_w2p;
@@ -777,7 +777,7 @@ struct MarikoMtcTable {
uint32_t mc_emem_arb_timing_wap2pre;
uint32_t mc_emem_arb_timing_r2r;
uint32_t mc_emem_arb_timing_w2w;
uint32_t mc_emem_arb_timingR2W;
uint32_t mc_emem_arb_timing_r2w;
uint32_t mc_emem_arb_timing_ccdmw;
uint32_t mc_emem_arb_timing_w2r;
uint32_t mc_emem_arb_timing_rfcpb;
@@ -1170,7 +1170,7 @@ struct EristaMtcTable {
uint32_t mc_emem_arb_timing_wap2pre;
uint32_t mc_emem_arb_timing_r2r;
uint32_t mc_emem_arb_timing_w2w;
uint32_t mc_emem_arb_timingR2W;
uint32_t mc_emem_arb_timing_r2w;
uint32_t mc_emem_arb_timing_ccdmw;
uint32_t mc_emem_arb_timing_w2r;
uint32_t mc_emem_arb_timing_rfcpb;

View File

@@ -7,19 +7,57 @@ namespace ams::ldr::oc {
#define MIN(A, B) std::min(A, B)
#define CEIL(A) std::ceil(A)
#define FLOOR(A) std::floor(A)
//Preset One
const std::array<u32, 6> tRCD_values = {18, 17, 16, 15, 14, 13};
const std::array<u32, 6> tRP_values = {18, 17, 16, 15, 14, 13};
const std::array<u32, 6> tRAS_values = {42, 39, 36, 34, 32, 30};
// Preset Two
const std::array<double, 5> tRRD_values = {10, 7.5, 6, 4, 3};
const std::array<double, 5> tFAW_values = {40, 30, 24, 16, 12};
// Preset Three
const std::array<u32, 6> tWR_values = {18, 15, 15, 12, 12, 8};
const std::array<double, 6> tRTP_values = {7.5, 7.5, 6, 6, 4, 4};
// Preset Four
const std::array<u32, 6> tRFC_values = {140, 120, 100, 80, 70, 60};
// Preset Five
const std::array<u32, 6> tWTR_values = {10, 8, 6, 4, 2, 1};
// Preset Six
const std::array<u32, 5> tREFpb_values = {488, 976, 1952, 3256, 9999};
const u32 TIMING_PRESET_ONE = C.ramTimingPresetOne;
const u32 TIMING_PRESET_TWO = C.ramTimingPresetTwo;
const u32 TIMING_PRESET_THREE = C.ramTimingPresetThree;
const u32 TIMING_PRESET_FOUR = C.ramTimingPresetFour;
const u32 TIMING_PRESET_FIVE = C.ramTimingPresetFive;
const u32 TIMING_PRESET_SIX = C.ramTimingPresetSix;
const u32 TIMING_PRESET_SEVEN = C.ramTimingPresetSeven;
// Burst Length
const u32 BL = 16;
// tRFCpb (refresh cycle time per bank) in ns for 8Gb density
const u32 tRFCpb = !TIMING_PRESET_FOUR ? 140 : tRFC_values[TIMING_PRESET_FOUR-1];
// tRFCab (refresh cycle time all banks) in ns for 8Gb density
const u32 tRFCab = 2*C.tRFCpb;
const u32 tRFCab = !TIMING_PRESET_FOUR ? 280 : 2*tRFCpb;
// tRAS (row active time) in ns
const u32 tRAS = !TIMING_PRESET_ONE ? 42 : tRAS_values[TIMING_PRESET_ONE-1];
// tRPpb (row precharge time per bank) in ns
const u32 tRPpb = !TIMING_PRESET_ONE ? 18 : tRP_values[TIMING_PRESET_ONE-1];
// C.tRAS (row active time) in ns
// C.tRPpb (row precharge time per bank) in ns
// tRPab (row precharge time all banks) in ns
const u32 tRPab = C.tRPpb + 3;
const u32 tRPab = !TIMING_PRESET_ONE ? 21 : tRPpb + 3;
// tRC (ACTIVATE-ACTIVATE command period same bank) in ns
const u32 tRC = C.tRPpb + C.tRAS;
const u32 tRC = tRPpb + tRAS;
// DQS output access time from CK_t/CK_c
const double tDQSCK_min = 1.5;
@@ -37,23 +75,27 @@ namespace ams::ldr::oc {
const double tDQSQ = 0.18;
// Write-to-Read delay
const u32 tWTR = !TIMING_PRESET_FIVE ? 10 : tWTR_values[TIMING_PRESET_FIVE-1];
// Internal READ-to-PRE-CHARGE command delay in ns
const double tRTP = 7.5;
const double tRTP = !TIMING_PRESET_THREE ? 7.5 : tRTP_values[TIMING_PRESET_THREE-1];
// write recovery time
// const u32 tWR = 18;
const u32 tWR = !TIMING_PRESET_THREE ? 18 : tWR_values[TIMING_PRESET_THREE-1];
// Read to refresh delay
const u32 tR2REF = tRTP + C.tRPpb;
const u32 tR2REF = tRTP + tRPpb;
// C.tRCD (RAS-CAS delay) in ns
// C.tRRD (Active bank-A to Active bank-B) in ns
// tRCD (RAS-CAS delay) in ns
const u32 tRCD = !TIMING_PRESET_ONE ? 18 : tRCD_values[TIMING_PRESET_ONE-1];
// C.tREFpb (average refresh interval per bank) in ns for 8Gb density
// tRRD (Active bank-A to Active bank-B) in ns
const double tRRD = !TIMING_PRESET_TWO ? 10. : tRRD_values[TIMING_PRESET_TWO-1];
// tREFpb (average refresh interval per bank) in ns for 8Gb density
const u32 tREFpb = !TIMING_PRESET_SIX ? 488 : tREFpb_values[TIMING_PRESET_SIX-1];
// tREFab (average refresh interval all 8 banks) in ns for 8Gb density
// const u32 tREFab = C.tREFpb * 8;
// const u32 tREFab = tREFpb * 8;
// tPDEX2WR, tPDEX2RD (timing delay from exiting powerdown mode to a write/read command) in ns
// const u32 tPDEX2 = 10;
@@ -82,7 +124,9 @@ namespace ams::ldr::oc {
// Minimum self refresh time (entry to exit)
const u32 tSR = 15;
// tFAW (Four-bank Activate Window) in ns
// tFAW (Four-bank Activate Window) in ns
const u32 tFAW = !TIMING_PRESET_TWO ? 40 : tFAW_values[TIMING_PRESET_TWO-1];
// Valid Clock requirement before CKE Input HIGH in ns
const double tCKCKEH = 1.75;
@@ -94,13 +138,15 @@ namespace ams::ldr::oc {
const double tCK_avg = 1000'000. / C.eristaEmcMaxClock;
// Write Latency
const u32 WL = 14;
const u32 WL = 14 - 2*TIMING_PRESET_SEVEN;
// Read Latency
const u32 RL = 32;
const u32 RL = 32 - 4*TIMING_PRESET_SEVEN;
// minimum number of cycles from any read command to any write command, irrespective of bank
// minimum number of cycles from any read command to any write command, irrespective of bank
const u32 R2W = CEIL (RL + CEIL(tDQSCK_max/tCK_avg) + BL/2 - WL + tWPRE + FLOOR(tRPST)) + 6;
// Delay Time From WRITE-to-READ
const u32 W2R = WL + BL/2 + 1 + CEIL(C.tWTR/tCK_avg) - 6;
const u32 W2R = WL + BL/2 + 1 + CEIL(tWTR/tCK_avg) - 6;
// write-to-precharge time for commands to the same bank in cycles
const u32 WTP = WL + BL/2 + 1 + CEIL(tWR/tCK_avg) - 8;
@@ -110,14 +156,14 @@ namespace ams::ldr::oc {
// {REFRESH, REFRESH_LO} = max[(tREF/#_of_rows) / (emc_clk_period) - 64, (tREF/#_of_rows) / (emc_clk_period) * 97%]
// emc_clk_period = dram_clk / 2;
// 1600 MHz: 5894, but N' set to 6176 (~4.8% margin)
const u32 REFRESH = MIN((u32)65472, u32(std::ceil((double(C.tREFpb) * C.eristaEmcMaxClock / numOfRows * 1.048 / 2 - 64))) / 4 * 4);
const u32 REFRESH = MIN((u32)65472, u32(std::ceil((double(tREFpb) * C.eristaEmcMaxClock / numOfRows * 1.048 / 2 - 64))) / 4 * 4);
const u32 REFBW = MIN((u32)65536, REFRESH+64);
// Write With Auto Precharge to to Power-Down Entry
const u32 WTPDEN = WTP + 1 + CEIL(tDQSS_max/tCK_avg) + CEIL(tDQS2DQ_max/tCK_avg) + 6;
// Additional time after t XP hasexpired until the MRR commandmay be issued
const double tMRRI = C.tRCD + 3 * tCK_avg;
const double tMRRI = tRCD + 3 * tCK_avg;
// tPDEX2MRR (timing delay from exiting powerdown mode to MRR command) in ns
const double tPDEX2MRR = tXP + tMRRI;
@@ -126,14 +172,15 @@ namespace ams::ldr::oc {
// tCK_avg (average clock period) in ns
const double tCK_avg = 1000'000. / C.marikoEmcMaxClock;
// Write Latency
const u32 WL = 14;
const u32 WL = 14 - 2*TIMING_PRESET_SEVEN;
// Read Latency
const u32 RL = 32;
const u32 RL = 32 - 4*TIMING_PRESET_SEVEN;
// minimum number of cycles from any read command to any write command, irrespective of bank
const u32 R2W = CEIL (RL + CEIL(tDQSCK_max/tCK_avg) + BL/2 - WL + tWPRE + FLOOR(tRPST));
// Delay Time From WRITE-to-READ
const u32 W2R = WL + BL/2 + 1 + CEIL(C.tWTR/tCK_avg);
const u32 W2R = WL + BL/2 + 1 + CEIL(tWTR/tCK_avg);
// write-to-precharge time for commands to the same bank in cycles
const u32 WTP = WL + BL/2 + 1 + CEIL(tWR/tCK_avg);
@@ -155,14 +202,14 @@ namespace ams::ldr::oc {
// {REFRESH, REFRESH_LO} = max[(tREF/#_of_rows) / (emc_clk_period) - 64, (tREF/#_of_rows) / (emc_clk_period) * 97%]
// emc_clk_period = dram_clk / 2;
// 1600 MHz: 5894, but N' set to 6176 (~4.8% margin)
const u32 REFRESH = MIN((u32)65472, u32(std::ceil((double(C.tREFpb) * C.marikoEmcMaxClock / numOfRows * 1.048 / 2 - 64))) / 4 * 4);
const u32 REFRESH = MIN((u32)65472, u32(std::ceil((double(tREFpb) * C.marikoEmcMaxClock / numOfRows * 1.048 / 2 - 64))) / 4 * 4);
const u32 REFBW = MIN((u32)65536, REFRESH+64);
// Write With Auto Precharge to to Power-Down Entry
const u32 WTPDEN = WTP + 1 + CEIL(tDQSS_max/tCK_avg) + CEIL(tDQS2DQ_max/tCK_avg) + 6;
// Additional time after t XP hasexpired until the MRR commandmay be issued
const double tMRRI = C.tRCD + 3 * tCK_avg;
const double tMRRI = tRCD + 3 * tCK_avg;
// tPDEX2MRR (timing delay from exiting powerdown mode to MRR command) in ns
const double tPDEX2MRR = tXP + tMRRI;

View File

@@ -85,16 +85,16 @@ void MemMtcTableAutoAdjust(EristaMtcTable* table) {
WRITE_PARAM_ALL_REG(table, emc_rc, GET_CYCLE_CEIL(tRC));
WRITE_PARAM_ALL_REG(table, emc_rfc, GET_CYCLE_CEIL(tRFCab));
WRITE_PARAM_ALL_REG(table, emc_rfcpb, GET_CYCLE_CEIL(C.tRFCpb));
WRITE_PARAM_ALL_REG(table, emc_ras, GET_CYCLE_CEIL(C.tRAS));
WRITE_PARAM_ALL_REG(table, emc_rp, GET_CYCLE_CEIL(C.tRPpb));
WRITE_PARAM_ALL_REG(table, emcR2W, C.R2W);
WRITE_PARAM_ALL_REG(table, emc_rfcpb, GET_CYCLE_CEIL(tRFCpb));
WRITE_PARAM_ALL_REG(table, emc_ras, GET_CYCLE_CEIL(tRAS));
WRITE_PARAM_ALL_REG(table, emc_rp, GET_CYCLE_CEIL(tRPpb));
WRITE_PARAM_ALL_REG(table, emc_r2w, R2W);
WRITE_PARAM_ALL_REG(table, emc_w2r, W2R);
WRITE_PARAM_ALL_REG(table, emc_r2p, GET_CYCLE_CEIL(tRTP));
WRITE_PARAM_ALL_REG(table, emc_w2p, WTP);
WRITE_PARAM_ALL_REG(table, emc_rd_rcd, GET_CYCLE_CEIL(C.tRCD));
WRITE_PARAM_ALL_REG(table, emc_wr_rcd, GET_CYCLE_CEIL(C.tRCD));
WRITE_PARAM_ALL_REG(table, emc_rrd, GET_CYCLE_CEIL(C.tRRD));
WRITE_PARAM_ALL_REG(table, emc_rd_rcd, GET_CYCLE_CEIL(tRCD));
WRITE_PARAM_ALL_REG(table, emc_wr_rcd, GET_CYCLE_CEIL(tRCD));
WRITE_PARAM_ALL_REG(table, emc_rrd, GET_CYCLE_CEIL(tRRD));
WRITE_PARAM_ALL_REG(table, emc_refresh, REFRESH);
WRITE_PARAM_ALL_REG(table, emc_pre_refresh_req_cnt, REFRESH / 4);
WRITE_PARAM_ALL_REG(table, emc_pdex2wr, GET_CYCLE_CEIL(tXP));
@@ -111,7 +111,7 @@ void MemMtcTableAutoAdjust(EristaMtcTable* table) {
WRITE_PARAM_ALL_REG(table, emc_tcke, GET_CYCLE_CEIL(tCKE));
WRITE_PARAM_ALL_REG(table, emc_tckesr, GET_CYCLE_CEIL(tSR));
WRITE_PARAM_ALL_REG(table, emc_tpd, GET_CYCLE_CEIL(tCKE));
WRITE_PARAM_ALL_REG(table, emc_tfaw, GET_CYCLE_CEIL(C.tFAW));
WRITE_PARAM_ALL_REG(table, emc_tfaw, GET_CYCLE_CEIL(tFAW));
WRITE_PARAM_ALL_REG(table, emc_trpab, GET_CYCLE_CEIL(tRPab));
WRITE_PARAM_ALL_REG(table, emc_tclkstable, GET_CYCLE_CEIL(tCKCKEH));
WRITE_PARAM_ALL_REG(table, emc_tclkstop, GET_CYCLE_CEIL(tCKE)+8);
@@ -121,19 +121,19 @@ void MemMtcTableAutoAdjust(EristaMtcTable* table) {
constexpr u32 MC_ARB_DIV = 4;
constexpr u32 MC_ARB_SFA = 2;
table->burst_mc_regs.mc_emem_arb_timing_rcd = CEIL(GET_CYCLE_CEIL(C.tRCD) / MC_ARB_DIV) - 2;
table->burst_mc_regs.mc_emem_arb_timing_rp = CEIL(GET_CYCLE_CEIL(C.tRPpb) / MC_ARB_DIV) - 1 + MC_ARB_SFA;
table->burst_mc_regs.mc_emem_arb_timing_rcd = CEIL(GET_CYCLE_CEIL(tRCD) / MC_ARB_DIV) - 2;
table->burst_mc_regs.mc_emem_arb_timing_rp = CEIL(GET_CYCLE_CEIL(tRPpb) / MC_ARB_DIV) - 1 + MC_ARB_SFA;
table->burst_mc_regs.mc_emem_arb_timing_rc = CEIL(GET_CYCLE_CEIL(tRC) / MC_ARB_DIV) - 1;
table->burst_mc_regs.mc_emem_arb_timing_ras = CEIL(GET_CYCLE_CEIL(C.tRAS) / MC_ARB_DIV) - 2;
table->burst_mc_regs.mc_emem_arb_timing_faw = CEIL(GET_CYCLE_CEIL(C.tFAW) / MC_ARB_DIV) - 1;
table->burst_mc_regs.mc_emem_arb_timing_rrd = CEIL(GET_CYCLE_CEIL(C.tRRD) / MC_ARB_DIV) - 1;
table->burst_mc_regs.mc_emem_arb_timing_ras = CEIL(GET_CYCLE_CEIL(tRAS) / MC_ARB_DIV) - 2;
table->burst_mc_regs.mc_emem_arb_timing_faw = CEIL(GET_CYCLE_CEIL(tFAW) / MC_ARB_DIV) - 1;
table->burst_mc_regs.mc_emem_arb_timing_rrd = CEIL(GET_CYCLE_CEIL(tRRD) / MC_ARB_DIV) - 1;
table->burst_mc_regs.mc_emem_arb_timing_rap2pre = CEIL(GET_CYCLE_CEIL(tRTP) / MC_ARB_DIV);
table->burst_mc_regs.mc_emem_arb_timing_wap2pre = CEIL(WTP / MC_ARB_DIV);
//table->burst_mc_regs.mc_emem_arb_timing_r2r = CEIL(table->burst_regs.emc_rext / MC_ARB_DIV) - 1 + MC_ARB_SFA;
//table->burst_mc_regs.mc_emem_arb_timing_w2w = CEIL(table->burst_regs.emc_wext / MC_ARB_DIV) - 1 + MC_ARB_SFA;
table->burst_mc_regs.mc_emem_arb_timingR2W = CEIL(C.R2W / MC_ARB_DIV) - 1 + MC_ARB_SFA;
table->burst_mc_regs.mc_emem_arb_timing_r2w = CEIL(R2W / MC_ARB_DIV) - 1 + MC_ARB_SFA;
table->burst_mc_regs.mc_emem_arb_timing_w2r = CEIL(W2R / MC_ARB_DIV) - 1 + MC_ARB_SFA;
table->burst_mc_regs.mc_emem_arb_timing_rfcpb = CEIL(GET_CYCLE_CEIL(C.tRFCpb) / MC_ARB_DIV);
table->burst_mc_regs.mc_emem_arb_timing_rfcpb = CEIL(GET_CYCLE_CEIL(tRFCpb) / MC_ARB_DIV);
//table->burst_mc_regs.mc_emem_arb_timing_ccdmw = CEIL(tCCDMW / MC_ARB_DIV) -1 + MC_ARB_SFA;
}
@@ -144,58 +144,72 @@ void MemMtcTableCustomAdjust(EristaMtcTable* table) {
constexpr u32 MC_ARB_DIV = 4;
constexpr u32 MC_ARB_SFA = 2;
WRITE_PARAM_ALL_REG(table, emc_rc, GET_CYCLE_CEIL(tRC));
WRITE_PARAM_ALL_REG(table, emc_ras, GET_CYCLE_CEIL(C.tRAS));
WRITE_PARAM_ALL_REG(table, emc_rp, GET_CYCLE_CEIL(C.tRPpb));
WRITE_PARAM_ALL_REG(table, emc_trpab, GET_CYCLE_CEIL(tRPab));
WRITE_PARAM_ALL_REG(table, emc_rd_rcd, GET_CYCLE_CEIL(C.tRCD));
WRITE_PARAM_ALL_REG(table, emc_wr_rcd, GET_CYCLE_CEIL(C.tRCD));
WRITE_PARAM_ALL_REG(table, emc_pdex2mrr, GET_CYCLE_CEIL(tPDEX2MRR));
if (TIMING_PRESET_ONE) {
WRITE_PARAM_ALL_REG(table, emc_rc, GET_CYCLE_CEIL(tRC));
WRITE_PARAM_ALL_REG(table, emc_ras, GET_CYCLE_CEIL(tRAS));
WRITE_PARAM_ALL_REG(table, emc_rp, GET_CYCLE_CEIL(tRPpb));
WRITE_PARAM_ALL_REG(table, emc_trpab, GET_CYCLE_CEIL(tRPab));
WRITE_PARAM_ALL_REG(table, emc_rd_rcd, GET_CYCLE_CEIL(tRCD));
WRITE_PARAM_ALL_REG(table, emc_wr_rcd, GET_CYCLE_CEIL(tRCD));
WRITE_PARAM_ALL_REG(table, emc_pdex2mrr, GET_CYCLE_CEIL(tPDEX2MRR));
table->burst_mc_regs.mc_emem_arb_timing_rcd = CEIL(GET_CYCLE_CEIL(C.tRCD) / MC_ARB_DIV - 2);
table->burst_mc_regs.mc_emem_arb_timing_rc = CEIL(GET_CYCLE_CEIL(tRC) / MC_ARB_DIV - 1);
table->burst_mc_regs.mc_emem_arb_timing_rp = CEIL(GET_CYCLE_CEIL(C.tRPpb) / MC_ARB_DIV - 1 + MC_ARB_SFA);
table->burst_mc_regs.mc_emem_arb_timing_ras = CEIL(GET_CYCLE_CEIL(C.tRAS) / MC_ARB_DIV - 2);
table->burst_mc_regs.mc_emem_arb_timing_rcd = CEIL(GET_CYCLE_CEIL(tRCD) / MC_ARB_DIV - 2);
table->burst_mc_regs.mc_emem_arb_timing_rc = CEIL(GET_CYCLE_CEIL(tRC) / MC_ARB_DIV - 1);
table->burst_mc_regs.mc_emem_arb_timing_rp = CEIL(GET_CYCLE_CEIL(tRPpb) / MC_ARB_DIV - 1 + MC_ARB_SFA);
table->burst_mc_regs.mc_emem_arb_timing_ras = CEIL(GET_CYCLE_CEIL(tRAS) / MC_ARB_DIV - 2);
}
WRITE_PARAM_ALL_REG(table, emc_tfaw, GET_CYCLE_CEIL(C.tFAW));
WRITE_PARAM_ALL_REG(table, emc_rrd, GET_CYCLE_CEIL(C.tRRD));
if (TIMING_PRESET_TWO) {
WRITE_PARAM_ALL_REG(table, emc_tfaw, GET_CYCLE_CEIL(tFAW));
WRITE_PARAM_ALL_REG(table, emc_rrd, GET_CYCLE_CEIL(tRRD));
table->burst_mc_regs.mc_emem_arb_timing_faw = CEIL(GET_CYCLE_CEIL(C.tFAW) / MC_ARB_DIV) - 1;
table->burst_mc_regs.mc_emem_arb_timing_rrd = CEIL(GET_CYCLE_CEIL(C.tRRD) / MC_ARB_DIV) - 1;
table->burst_mc_regs.mc_emem_arb_timing_faw = CEIL(GET_CYCLE_CEIL(tFAW) / MC_ARB_DIV) - 1;
table->burst_mc_regs.mc_emem_arb_timing_rrd = CEIL(GET_CYCLE_CEIL(tRRD) / MC_ARB_DIV) - 1;
}
WRITE_PARAM_ALL_REG(table, emc_r2p, GET_CYCLE_CEIL(tRTP));
WRITE_PARAM_ALL_REG(table, emc_w2p, WTP);
WRITE_PARAM_ALL_REG(table, emc_rw2pden, WTPDEN);
if (TIMING_PRESET_THREE) {
WRITE_PARAM_ALL_REG(table, emc_r2p, GET_CYCLE_CEIL(tRTP));
WRITE_PARAM_ALL_REG(table, emc_w2p, WTP);
WRITE_PARAM_ALL_REG(table, emc_rw2pden, WTPDEN);
table->burst_mc_regs.mc_emem_arb_timing_rap2pre = CEIL(GET_CYCLE_CEIL(tRTP) / MC_ARB_DIV);
table->burst_mc_regs.mc_emem_arb_timing_wap2pre = CEIL(WTP / MC_ARB_DIV);
table->burst_mc_regs.mc_emem_arb_timing_rap2pre = CEIL(GET_CYCLE_CEIL(tRTP) / MC_ARB_DIV);
table->burst_mc_regs.mc_emem_arb_timing_wap2pre = CEIL(WTP / MC_ARB_DIV);
}
WRITE_PARAM_ALL_REG(table, emc_rfc, GET_CYCLE_CEIL(tRFCab));
WRITE_PARAM_ALL_REG(table, emc_rfcpb, GET_CYCLE_CEIL(C.tRFCpb));
WRITE_PARAM_ALL_REG(table, emc_txsr, MIN(GET_CYCLE_CEIL(tXSR), (u32)0x3fe));
WRITE_PARAM_ALL_REG(table, emc_txsrdll, MIN(GET_CYCLE_CEIL(tXSR), (u32)0x3fe));
if (TIMING_PRESET_FOUR) {
WRITE_PARAM_ALL_REG(table, emc_rfc, GET_CYCLE_CEIL(tRFCab));
WRITE_PARAM_ALL_REG(table, emc_rfcpb, GET_CYCLE_CEIL(tRFCpb));
WRITE_PARAM_ALL_REG(table, emc_txsr, MIN(GET_CYCLE_CEIL(tXSR), (u32)0x3fe));
WRITE_PARAM_ALL_REG(table, emc_txsrdll, MIN(GET_CYCLE_CEIL(tXSR), (u32)0x3fe));
table->burst_mc_regs.mc_emem_arb_timing_rfcpb = CEIL(GET_CYCLE_CEIL(C.tRFCpb) / MC_ARB_DIV);
table->burst_mc_regs.mc_emem_arb_timing_rfcpb = CEIL(GET_CYCLE_CEIL(tRFCpb) / MC_ARB_DIV);
}
WRITE_PARAM_ALL_REG(table, emc_w2r, W2R);
if (TIMING_PRESET_FIVE) {
WRITE_PARAM_ALL_REG(table, emc_w2r, W2R);
table->burst_mc_regs.mc_emem_arb_timing_w2r = CEIL(W2R / MC_ARB_DIV) - 1 + MC_ARB_SFA;
table->burst_mc_regs.mc_emem_arb_timing_w2r = CEIL(W2R / MC_ARB_DIV) - 1 + MC_ARB_SFA;
}
WRITE_PARAM_ALL_REG(table, emc_refresh, REFRESH);
WRITE_PARAM_ALL_REG(table, emc_pre_refresh_req_cnt, REFRESH / 4);
WRITE_PARAM_ALL_REG(table, emc_trefbw, REFBW);
if (TIMING_PRESET_SIX) {
WRITE_PARAM_ALL_REG(table, emc_refresh, REFRESH);
WRITE_PARAM_ALL_REG(table, emc_pre_refresh_req_cnt, REFRESH / 4);
WRITE_PARAM_ALL_REG(table, emc_trefbw, REFBW);
}
WRITE_PARAM_ALL_REG(table, emcR2W, C.R2W);
WRITE_PARAM_ALL_REG(table, emc_w2r, W2R);
WRITE_PARAM_ALL_REG(table, emc_w2p, WTP);
WRITE_PARAM_ALL_REG(table, emc_rw2pden, WTPDEN);
table->burst_mc_regs.mc_emem_arb_timing_wap2pre = CEIL(WTP / MC_ARB_DIV);
table->burst_mc_regs.mc_emem_arb_timingR2W = CEIL(C.R2W / MC_ARB_DIV) - 1 + MC_ARB_SFA;
table->burst_mc_regs.mc_emem_arb_timing_w2r = CEIL(W2R / MC_ARB_DIV) - 1 + MC_ARB_SFA;
if (TIMING_PRESET_SEVEN) {
WRITE_PARAM_ALL_REG(table, emc_r2w, R2W);
WRITE_PARAM_ALL_REG(table, emc_w2r, W2R);
WRITE_PARAM_ALL_REG(table, emc_w2p, WTP);
WRITE_PARAM_ALL_REG(table, emc_rw2pden, WTPDEN);
table->burst_mc_regs.mc_emem_arb_timing_wap2pre = CEIL(WTP / MC_ARB_DIV);
table->burst_mc_regs.mc_emem_arb_timing_r2w = CEIL(R2W / MC_ARB_DIV) - 1 + MC_ARB_SFA;
table->burst_mc_regs.mc_emem_arb_timing_w2r = CEIL(W2R / MC_ARB_DIV) - 1 + MC_ARB_SFA;
}
u32 DA_TURNS = 0;
DA_TURNS |= u8(table->burst_mc_regs.mc_emem_arb_timingR2W / 2) << 16; //C.R2W TURN
DA_TURNS |= u8(table->burst_mc_regs.mc_emem_arb_timing_r2w / 2) << 16; //R2W TURN
DA_TURNS |= u8(table->burst_mc_regs.mc_emem_arb_timing_w2r / 2) << 24; //W2R TURN
WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_da_turns, DA_TURNS);
u32 DA_COVERS = 0;

View File

@@ -186,10 +186,10 @@ void MemMtcTableAutoAdjust(MarikoMtcTable* table, const MarikoMtcTable* ref) {
WRITE_PARAM_ALL_REG(table, emc_rc, GET_CYCLE_CEIL(tRC));
WRITE_PARAM_ALL_REG(table, emc_rfc, GET_CYCLE_CEIL(tRFCab));
WRITE_PARAM_ALL_REG(table, emc_rfcpb, GET_CYCLE_CEIL(C.tRFCpb));
WRITE_PARAM_ALL_REG(table, emc_ras, GET_CYCLE_CEIL(C.tRAS));
WRITE_PARAM_ALL_REG(table, emc_rp, GET_CYCLE_CEIL(C.tRPpb));
WRITE_PARAM_ALL_REG(table, emcR2W, C.R2W);
WRITE_PARAM_ALL_REG(table, emc_rfcpb, GET_CYCLE_CEIL(tRFCpb));
WRITE_PARAM_ALL_REG(table, emc_ras, GET_CYCLE_CEIL(tRAS));
WRITE_PARAM_ALL_REG(table, emc_rp, GET_CYCLE_CEIL(tRPpb));
WRITE_PARAM_ALL_REG(table, emc_r2w, R2W);
WRITE_PARAM_ALL_REG(table, emc_w2r, W2R);
WRITE_PARAM_ALL_REG(table, emc_r2p, GET_CYCLE_CEIL(tRTP));
WRITE_PARAM_ALL_REG(table, emc_w2p, WTP);
@@ -198,9 +198,9 @@ void MemMtcTableAutoAdjust(MarikoMtcTable* table, const MarikoMtcTable* ref) {
WRITE_PARAM_ALL_REG(table, emc_tratm, RATM);
WRITE_PARAM_ALL_REG(table, emc_twatm, WATM);
//WRITE_PARAM_ALL_REG(table, emc_tr2ref, GET_CYCLE_CEIL(tR2REF));
WRITE_PARAM_ALL_REG(table, emc_rd_rcd, GET_CYCLE_CEIL(C.tRCD));
WRITE_PARAM_ALL_REG(table, emc_wr_rcd, GET_CYCLE_CEIL(C.tRCD));
WRITE_PARAM_ALL_REG(table, emc_rrd, GET_CYCLE_CEIL(C.tRRD));
WRITE_PARAM_ALL_REG(table, emc_rd_rcd, GET_CYCLE_CEIL(tRCD));
WRITE_PARAM_ALL_REG(table, emc_wr_rcd, GET_CYCLE_CEIL(tRCD));
WRITE_PARAM_ALL_REG(table, emc_rrd, GET_CYCLE_CEIL(tRRD));
WRITE_PARAM_ALL_REG(table, emc_rext, 26);
WRITE_PARAM_ALL_REG(table, emc_refresh, REFRESH);
WRITE_PARAM_ALL_REG(table, emc_pre_refresh_req_cnt, REFRESH / 4);
@@ -218,7 +218,7 @@ void MemMtcTableAutoAdjust(MarikoMtcTable* table, const MarikoMtcTable* ref) {
WRITE_PARAM_ALL_REG(table, emc_tcke, GET_CYCLE_CEIL(tCKE) + 1);
WRITE_PARAM_ALL_REG(table, emc_tckesr, GET_CYCLE_CEIL(tSR));
WRITE_PARAM_ALL_REG(table, emc_tpd, GET_CYCLE_CEIL(tCKE));
WRITE_PARAM_ALL_REG(table, emc_tfaw, GET_CYCLE_CEIL(C.tFAW));
WRITE_PARAM_ALL_REG(table, emc_tfaw, GET_CYCLE_CEIL(tFAW));
WRITE_PARAM_ALL_REG(table, emc_trpab, GET_CYCLE_CEIL(tRPab));
//WRITE_PARAM_ALL_REG(table, emc_tclkstable, GET_CYCLE_CEIL(tCKCKEH));
WRITE_PARAM_ALL_REG(table, emc_tclkstop, GET_CYCLE_CEIL(tCKE) + 8);
@@ -240,21 +240,21 @@ void MemMtcTableAutoAdjust(MarikoMtcTable* table, const MarikoMtcTable* ref) {
constexpr u32 MC_ARB_SFA = 2;
WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_cfg, C.marikoEmcMaxClock / (33.3 * 1000) / MC_ARB_DIV); //CYCLES_PER_UPDATE: The number of mcclk cycles per deadline timer update
WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_rcd, CEIL(GET_CYCLE_CEIL(C.tRCD) / MC_ARB_DIV) - 2)
WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_rp, CEIL(GET_CYCLE_CEIL(C.tRPpb) / MC_ARB_DIV) - 1 + MC_ARB_SFA)
WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_rcd, CEIL(GET_CYCLE_CEIL(tRCD) / MC_ARB_DIV) - 2)
WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_rp, CEIL(GET_CYCLE_CEIL(tRPpb) / MC_ARB_DIV) - 1 + MC_ARB_SFA)
WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_rc, CEIL(GET_CYCLE_CEIL(tRC) / MC_ARB_DIV) - 1)
WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_ras, CEIL(GET_CYCLE_CEIL(C.tRAS) / MC_ARB_DIV) - 2)
WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_faw, CEIL(GET_CYCLE_CEIL(C.tFAW) / MC_ARB_DIV) - 1)
WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_rrd, CEIL(GET_CYCLE_CEIL(C.tRRD) / MC_ARB_DIV) - 1)
WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_ras, CEIL(GET_CYCLE_CEIL(tRAS) / MC_ARB_DIV) - 2)
WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_faw, CEIL(GET_CYCLE_CEIL(tFAW) / MC_ARB_DIV) - 1)
WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_rrd, CEIL(GET_CYCLE_CEIL(tRRD) / MC_ARB_DIV) - 1)
WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_rap2pre, CEIL(GET_CYCLE_CEIL(tRTP) / MC_ARB_DIV))
WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_wap2pre, CEIL((WTP) / MC_ARB_DIV))
WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_r2r, CEIL(table->burst_regs.emc_rext / MC_ARB_DIV) - 1 + MC_ARB_SFA)
WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timingR2W, CEIL((C.R2W) / MC_ARB_DIV) - 1 + MC_ARB_SFA)
WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_r2w, CEIL((R2W) / MC_ARB_DIV) - 1 + MC_ARB_SFA)
WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_w2r, CEIL((W2R) / MC_ARB_DIV) - 1 + MC_ARB_SFA)
WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_rfcpb, CEIL(GET_CYCLE_CEIL(C.tRFCpb) / MC_ARB_DIV))
WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_rfcpb, CEIL(GET_CYCLE_CEIL(tRFCpb) / MC_ARB_DIV))
u32 DA_TURNS = 0;
DA_TURNS |= u8(table->burst_mc_regs.mc_emem_arb_timingR2W / 2) << 16; //C.R2W TURN
DA_TURNS |= u8(table->burst_mc_regs.mc_emem_arb_timing_r2w / 2) << 16; //R2W TURN
DA_TURNS |= u8(table->burst_mc_regs.mc_emem_arb_timing_w2r / 2) << 24; //W2R TURN
WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_da_turns, DA_TURNS);
u32 DA_COVERS = 0;
@@ -320,7 +320,7 @@ void MemMtcTableAutoAdjust(MarikoMtcTable* table, const MarikoMtcTable* ref) {
table->pllmb_ss_ctrl1 = 0x0b55fe01;
table->pllmb_ss_ctrl2 = 0x10170b55;
table->dram_timings.t_rp = C.tRPpb;
table->dram_timings.t_rp = tRPpb;
table->dram_timings.t_rfc = tRFCab;
//table->dram_timings.rl = 32;
@@ -334,64 +334,79 @@ void MemMtcTableCustomAdjust(MarikoMtcTable* table) {
constexpr u32 MC_ARB_DIV = 4;
constexpr u32 MC_ARB_SFA = 2;
WRITE_PARAM_ALL_REG(table, emc_rc, GET_CYCLE_CEIL(tRC));
WRITE_PARAM_ALL_REG(table, emc_ras, GET_CYCLE_CEIL(C.tRAS));
WRITE_PARAM_ALL_REG(table, emc_rp, GET_CYCLE_CEIL(C.tRPpb));
WRITE_PARAM_ALL_REG(table, emc_trpab, GET_CYCLE_CEIL(tRPab));
WRITE_PARAM_ALL_REG(table, emc_rd_rcd, GET_CYCLE_CEIL(C.tRCD));
WRITE_PARAM_ALL_REG(table, emc_wr_rcd, GET_CYCLE_CEIL(C.tRCD));
WRITE_PARAM_ALL_REG(table, emc_pdex2mrr,GET_CYCLE_CEIL(tPDEX2MRR));
if (TIMING_PRESET_ONE) {
WRITE_PARAM_ALL_REG(table, emc_rc, GET_CYCLE_CEIL(tRC));
WRITE_PARAM_ALL_REG(table, emc_ras, GET_CYCLE_CEIL(tRAS));
WRITE_PARAM_ALL_REG(table, emc_rp, GET_CYCLE_CEIL(tRPpb));
WRITE_PARAM_ALL_REG(table, emc_trpab, GET_CYCLE_CEIL(tRPab));
WRITE_PARAM_ALL_REG(table, emc_rd_rcd, GET_CYCLE_CEIL(tRCD));
WRITE_PARAM_ALL_REG(table, emc_wr_rcd, GET_CYCLE_CEIL(tRCD));
WRITE_PARAM_ALL_REG(table, emc_pdex2mrr,GET_CYCLE_CEIL(tPDEX2MRR));
table->burst_mc_regs.mc_emem_arb_timing_rcd = CEIL(GET_CYCLE_CEIL(C.tRCD) / MC_ARB_DIV) - 2;
table->burst_mc_regs.mc_emem_arb_timing_rc = CEIL(GET_CYCLE_CEIL(tRC) / MC_ARB_DIV) - 1;
table->burst_mc_regs.mc_emem_arb_timing_rp = CEIL(GET_CYCLE_CEIL(C.tRPpb) / MC_ARB_DIV) - 1 + MC_ARB_SFA;
table->burst_mc_regs.mc_emem_arb_timing_ras = CEIL(GET_CYCLE_CEIL(C.tRAS) / MC_ARB_DIV) - 2;
table->burst_mc_regs.mc_emem_arb_timing_rcd = CEIL(GET_CYCLE_CEIL(tRCD) / MC_ARB_DIV) - 2;
table->burst_mc_regs.mc_emem_arb_timing_rc = CEIL(GET_CYCLE_CEIL(tRC) / MC_ARB_DIV) - 1;
table->burst_mc_regs.mc_emem_arb_timing_rp = CEIL(GET_CYCLE_CEIL(tRPpb) / MC_ARB_DIV) - 1 + MC_ARB_SFA;
table->burst_mc_regs.mc_emem_arb_timing_ras = CEIL(GET_CYCLE_CEIL(tRAS) / MC_ARB_DIV) - 2;
WRITE_PARAM_ALL_REG(table, emc_tfaw, GET_CYCLE_CEIL(C.tFAW));
WRITE_PARAM_ALL_REG(table, emc_rrd, GET_CYCLE_CEIL(C.tRRD));
}
if (TIMING_PRESET_TWO) {
WRITE_PARAM_ALL_REG(table, emc_tfaw, GET_CYCLE_CEIL(tFAW));
WRITE_PARAM_ALL_REG(table, emc_rrd, GET_CYCLE_CEIL(tRRD));
table->burst_mc_regs.mc_emem_arb_timing_faw = CEIL(GET_CYCLE_CEIL(C.tFAW) / MC_ARB_DIV) - 1;
table->burst_mc_regs.mc_emem_arb_timing_rrd = CEIL(GET_CYCLE_CEIL(C.tRRD) / MC_ARB_DIV) - 1;
table->burst_mc_regs.mc_emem_arb_timing_faw = CEIL(GET_CYCLE_CEIL(tFAW) / MC_ARB_DIV) - 1;
table->burst_mc_regs.mc_emem_arb_timing_rrd = CEIL(GET_CYCLE_CEIL(tRRD) / MC_ARB_DIV) - 1;
}
WRITE_PARAM_ALL_REG(table, emc_r2p, GET_CYCLE_CEIL(tRTP));
WRITE_PARAM_ALL_REG(table, emc_w2p, WTP);
WRITE_PARAM_ALL_REG(table, emc_tratm, RATM);
WRITE_PARAM_ALL_REG(table, emc_twatm, WATM);
WRITE_PARAM_ALL_REG(table, emc_rw2pden, WTPDEN);
if (TIMING_PRESET_THREE) {
WRITE_PARAM_ALL_REG(table, emc_r2p, GET_CYCLE_CEIL(tRTP));
WRITE_PARAM_ALL_REG(table, emc_w2p, WTP);
WRITE_PARAM_ALL_REG(table, emc_tratm, RATM);
WRITE_PARAM_ALL_REG(table, emc_twatm, WATM);
WRITE_PARAM_ALL_REG(table, emc_rw2pden, WTPDEN);
table->burst_mc_regs.mc_emem_arb_timing_rap2pre = CEIL(GET_CYCLE_CEIL(tRTP) / MC_ARB_DIV);
table->burst_mc_regs.mc_emem_arb_timing_wap2pre = CEIL(WTP / MC_ARB_DIV);
table->burst_mc_regs.mc_emem_arb_timing_rap2pre = CEIL(GET_CYCLE_CEIL(tRTP) / MC_ARB_DIV);
table->burst_mc_regs.mc_emem_arb_timing_wap2pre = CEIL(WTP / MC_ARB_DIV);
}
WRITE_PARAM_ALL_REG(table, emc_rfc, GET_CYCLE_CEIL(tRFCab));
WRITE_PARAM_ALL_REG(table, emc_rfcpb, GET_CYCLE_CEIL(C.tRFCpb));
WRITE_PARAM_ALL_REG(table, emc_txsr, MIN(GET_CYCLE_CEIL(tXSR), (u32)0x3fe));
WRITE_PARAM_ALL_REG(table, emc_txsrdll, MIN(GET_CYCLE_CEIL(tXSR), (u32)0x3fe));
if (TIMING_PRESET_FOUR) {
WRITE_PARAM_ALL_REG(table, emc_rfc, GET_CYCLE_CEIL(tRFCab));
WRITE_PARAM_ALL_REG(table, emc_rfcpb, GET_CYCLE_CEIL(tRFCpb));
WRITE_PARAM_ALL_REG(table, emc_txsr, MIN(GET_CYCLE_CEIL(tXSR), (u32)0x3fe));
WRITE_PARAM_ALL_REG(table, emc_txsrdll, MIN(GET_CYCLE_CEIL(tXSR), (u32)0x3fe));
table->burst_mc_regs.mc_emem_arb_timing_rfcpb = CEIL(GET_CYCLE_CEIL(C.tRFCpb) / MC_ARB_DIV);
table->burst_mc_regs.mc_emem_arb_timing_rfcpb = CEIL(GET_CYCLE_CEIL(tRFCpb) / MC_ARB_DIV);
}
WRITE_PARAM_ALL_REG(table, emc_w2r, W2R);
if (TIMING_PRESET_FIVE) {
WRITE_PARAM_ALL_REG(table, emc_w2r, W2R);
table->burst_mc_regs.mc_emem_arb_timing_w2r = CEIL(W2R / MC_ARB_DIV) - 1 + MC_ARB_SFA;
table->burst_mc_regs.mc_emem_arb_timing_w2r = CEIL(W2R / MC_ARB_DIV) - 1 + MC_ARB_SFA;
}
WRITE_PARAM_ALL_REG(table, emc_refresh, REFRESH);
WRITE_PARAM_ALL_REG(table, emc_pre_refresh_req_cnt, REFRESH / 4);
WRITE_PARAM_ALL_REG(table, emc_trefbw, REFBW);
if (TIMING_PRESET_SIX) {
WRITE_PARAM_ALL_REG(table, emc_refresh, REFRESH);
WRITE_PARAM_ALL_REG(table, emc_pre_refresh_req_cnt, REFRESH / 4);
WRITE_PARAM_ALL_REG(table, emc_trefbw, REFBW);
}
WRITE_PARAM_ALL_REG(table, emcR2W, C.R2W);
WRITE_PARAM_ALL_REG(table, emc_w2r, W2R);
WRITE_PARAM_ALL_REG(table, emc_w2p, WTP);
WRITE_PARAM_ALL_REG(table, emc_trtm, RTM);
WRITE_PARAM_ALL_REG(table, emc_twtm, WTM);
WRITE_PARAM_ALL_REG(table, emc_tratm, RATM);
WRITE_PARAM_ALL_REG(table, emc_twatm, WATM);
WRITE_PARAM_ALL_REG(table, emc_rw2pden, WTPDEN);
if (TIMING_PRESET_SEVEN) {
WRITE_PARAM_ALL_REG(table, emc_r2w, R2W);
WRITE_PARAM_ALL_REG(table, emc_w2r, W2R);
WRITE_PARAM_ALL_REG(table, emc_w2p, WTP);
WRITE_PARAM_ALL_REG(table, emc_trtm, RTM);
WRITE_PARAM_ALL_REG(table, emc_twtm, WTM);
WRITE_PARAM_ALL_REG(table, emc_tratm, RATM);
WRITE_PARAM_ALL_REG(table, emc_twatm, WATM);
WRITE_PARAM_ALL_REG(table, emc_rw2pden, WTPDEN);
table->burst_mc_regs.mc_emem_arb_timing_wap2pre = CEIL(WTP / MC_ARB_DIV);
table->burst_mc_regs.mc_emem_arb_timingR2W = CEIL(C.R2W / MC_ARB_DIV) - 1 + MC_ARB_SFA;
table->burst_mc_regs.mc_emem_arb_timing_w2r = CEIL(W2R / MC_ARB_DIV) - 1 + MC_ARB_SFA;
table->burst_mc_regs.mc_emem_arb_timing_wap2pre = CEIL(WTP / MC_ARB_DIV);
table->burst_mc_regs.mc_emem_arb_timing_r2w = CEIL(R2W / MC_ARB_DIV) - 1 + MC_ARB_SFA;
table->burst_mc_regs.mc_emem_arb_timing_w2r = CEIL(W2R / MC_ARB_DIV) - 1 + MC_ARB_SFA;
}
u32 DA_TURNS = 0;
DA_TURNS |= u8(table->burst_mc_regs.mc_emem_arb_timingR2W / 2) << 16; //C.R2W TURN
DA_TURNS |= u8(table->burst_mc_regs.mc_emem_arb_timing_r2w / 2) << 16; //R2W TURN
DA_TURNS |= u8(table->burst_mc_regs.mc_emem_arb_timing_w2r / 2) << 24; //W2R TURN
WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_da_turns, DA_TURNS);
u32 DA_COVERS = 0;

View File

@@ -1,74 +0,0 @@
#!python3
import sys
import os
def file_replace_str(file_path, search_replace_list):
assert file_path
assert search_replace_list
with open(file_path, "r") as f:
content = f.read()
for entry in search_replace_list:
(search, replace) = entry
if search in content:
content = content.replace(search, replace)
else:
assert replace in content, f"Pattern \"{search}\" not found"
with open(file_path, "w") as f:
f.write(content)
dir_path = os.path.dirname(__file__)
os.chdir(dir_path)
os.system("git reset --hard")
ldr_process_creation = os.path.join(dir_path, "ldr_process_creation.cpp")
file_replace_str(ldr_process_creation,
[("""#include "ldr_ro_manager.hpp"
namespace ams::ldr {""",
"""#include "ldr_ro_manager.hpp"
#include "oc/oc_loader.hpp"
namespace ams::ldr {"""),
(""" NsoHeader g_nso_headers[Nso_Count];
Result ValidateProgramVersion(ncm::ProgramId program_id, u32 version) {""",
""" NsoHeader g_nso_headers[Nso_Count];
/* Pcv/Ptm check cache. */
bool g_is_pcv;
bool g_is_ptm;
Result ValidateProgramVersion(ncm::ProgramId program_id, u32 version) {"""),
(""" R_UNLESS(meta->aci->program_id <= meta->acid->program_id_max, ldr::ResultInvalidProgramId());
/* Validate the kernel capabilities. */""",
""" R_UNLESS(meta->aci->program_id <= meta->acid->program_id_max, ldr::ResultInvalidProgramId());
/* Check if nca is pcv or ptm */
g_is_pcv = meta->aci->program_id == ncm::SystemProgramId::Pcv;
g_is_ptm = meta->aci->program_id == ncm::SystemProgramId::Ptm;
/* Validate the kernel capabilities. */"""),
(""" LocateAndApplyIpsPatchesToModule(nso_header->module_id, map_address, nso_size);
}""",
""" LocateAndApplyIpsPatchesToModule(nso_header->module_id, map_address, nso_size);
/* Apply pcv and ptm patches. */
if (g_is_pcv)
oc::pcv::Patch(map_address, nso_size);
if (g_is_ptm)
oc::ptm::Patch(map_address, nso_size);
}""")])
ldr_meta = os.path.join(dir_path, "ldr_meta.cpp")
file_replace_str(ldr_meta,
[(""" Result ValidateAcidSignature(Meta *meta) {
/* Loader did not check signatures prior to 10.0.0. */""",
""" Result ValidateAcidSignature(Meta *meta) {
R_SUCCEED();
/* Loader did not check signatures prior to 10.0.0. */""")])