diff --git a/README.md b/README.md
index 3c0a3b20..a5f88766 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
Overclocking suite for Horizon OS (HOS) running on Atmosphere CFW.
-## DISCLAIMER: USE AT YOUR OWN RISK!
+**DISCLAIMER: USE AT YOUR OWN RISK!**
- Overclocking in general will shorten the lifespan of some hardware components.
@@ -119,9 +119,9 @@ Overclocking suite for Horizon OS (HOS) running on Atmosphere CFW.
Grab necessary patches from the repo, then compile sys-clk, ReverseNX-RT and Atmosphere loader with devkitpro.
-If you are to install nro forwarders, stub `ValidateAcidSignature()` with `R_SUCCEED();` in `Atmosphere/stratosphere/loader/source/ldr_meta.cpp` to make them work again.
+Before compiling Atmosphere loader, run `patch.py` in `Atmosphere/stratosphere/loader/source/` to insert oc module into loader sysmodule.
-Uncompress the kip to make it work with configurator: `hactool -t kip1 Atmosphere/stratosphere/loader/out/nintendo_nx_arm64_armv8a/release/loader.kip --uncompress=./loader.kip`
+When compilation is done, uncompress the kip to make it work with configurator: `hactool -t kip1 Atmosphere/stratosphere/loader/out/nintendo_nx_arm64_armv8a/release/loader.kip --uncompress=./loader.kip`
@@ -134,4 +134,5 @@ Uncompress the kip to make it work with configurator: `hactool -t kip1 Atmospher
- RetroNX team for [sys-clk](https://github.com/retronx-team/sys-clk)
- SciresM and Reswitched Team for the state-of-the-art [Atmosphere](https://github.com/Atmosphere-NX/Atmosphere) CFW of Switch
- Switchbrew [wiki](http://switchbrew.org/wiki/) for Switch in-depth info
+- Switchroot for their [modified L4T kernel and device tree]((https://gitlab.com/switchroot/kernel))
- ZatchyCatGames for RE and original OC loader patches for Atmosphere
diff --git a/Source/Atmosphere/stratosphere/loader/source/ldr_process_creation.cpp b/Source/Atmosphere/stratosphere/loader/source/ldr_process_creation.cpp
deleted file mode 100644
index dbdac49b..00000000
--- a/Source/Atmosphere/stratosphere/loader/source/ldr_process_creation.cpp
+++ /dev/null
@@ -1,753 +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 .
- */
-#include
-#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/ldr_oc_suite.hpp"
-
-namespace ams::ldr {
-
- namespace {
-
- /* Convenience defines. */
- constexpr size_t SystemResourceSizeMax = 0x1FE00000;
-
- /* Types. */
- enum NsoIndex {
- Nso_Rtld = 0,
- Nso_Main = 1,
- Nso_SubSdk0 = 2,
- Nso_SubSdk1 = 3,
- Nso_SubSdk2 = 4,
- Nso_SubSdk3 = 5,
- Nso_SubSdk4 = 6,
- Nso_SubSdk5 = 7,
- Nso_SubSdk6 = 8,
- Nso_SubSdk7 = 9,
- Nso_SubSdk8 = 10,
- Nso_SubSdk9 = 11,
- Nso_Sdk = 12,
- Nso_Count,
- };
-
- constexpr inline const char *NsoPaths[Nso_Count] = {
- ENCODE_ATMOSPHERE_CODE_PATH("/rtld"),
- ENCODE_ATMOSPHERE_CODE_PATH("/main"),
- 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];
-
- /* Pcv/Ptm check cache. */
- 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(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32));
- R_SUCCEED();
- }
-
- bool IsApplet(const Meta *meta) {
- return (MakeProgramInfoFlag(static_cast(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32)) & ProgramInfoFlag_ApplicationTypeMask) == ProgramInfoFlag_Applet;
- }
-
- bool IsApplication(const Meta *meta) {
- return (MakeProgramInfoFlag(static_cast(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32)) & ProgramInfoFlag_ApplicationTypeMask) == ProgramInfoFlag_Application;
- }
-
- Npdm::AddressSpaceType GetAddressSpaceType(const Meta *meta) {
- return static_cast((meta->npdm->flags & Npdm::MetaFlag_AddressSpaceTypeMask) >> Npdm::MetaFlag_AddressSpaceTypeShift);
- }
-
- Acid::PoolPartition GetPoolPartition(const Meta *meta) {
- return static_cast((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());
- 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. */
- R_TRY(TestCapability(static_cast(meta->acid_kac), meta->acid->kac_size / sizeof(util::BitPack32), static_cast(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(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;
- }
-
- *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::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(load_address), file_size));
- R_UNLESS(read_size == file_size, ldr::ResultInvalidNso());
-
- /* Uncompress if necessary. */
- if (is_compressed) {
- bool decompressed = (util::DecompressLZ4(reinterpret_cast(map_base), segment->size, reinterpret_cast(load_address), file_size) == static_cast(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(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) {
- /* 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(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(map_address + 0), 0, nso_header->text_dst_offset);
- std::memset(reinterpret_cast(map_address + text_end), 0, nso_header->ro_dst_offset - text_end);
- std::memset(reinterpret_cast(map_address + ro_end), 0, nso_header->rw_dst_offset - ro_end);
- std::memset(reinterpret_cast(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);
-
- /* 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);
- }
- }
-
- /* 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, 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) {
- /* 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]));
- }
- }
-
- /* 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(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(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));
- }
-
- }
-
- /* 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) {
- /* Mount code. */
- AMS_UNUSED(path);
- ScopedCodeMount mount(loc, override_status);
- R_TRY(mount.GetResult());
-
- /* Load meta, possibly from cache. */
- Meta meta;
- R_TRY(LoadMetaFromCache(std::addressof(meta), loc, override_status));
-
- /* 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) {
- Meta meta;
-
- /* Load Meta. */
- {
- AMS_UNUSED(path);
-
- ScopedCodeMount mount(loc);
- R_TRY(mount.GetResult());
- R_TRY(LoadMeta(std::addressof(meta), loc, mount.GetOverrideStatus()));
- 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();
- }
-
-}
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/Makefile b/Source/Atmosphere/stratosphere/loader/source/oc/Makefile
index 9496113d..57863400 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/Makefile
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/Makefile
@@ -1,9 +1,49 @@
-export CC := g++
+TARGET_EXEC := test
-all: test
+BUILD_DIR := ./build
+SRC_DIRS := ./
-test:
- $(CC) ldr_oc_suite.cpp test.cpp -o ./test -O2 -std=c++20 -Wall
+# Find all the C and C++ files we want to compile
+# Note the single quotes around the * expressions. Make will incorrectly expand these otherwise.
+SRCS := $(shell find $(SRC_DIRS) -name '*.cpp' -or -name '*.c' -or -name '*.s')
+# String substitution for every C/C++ file.
+# As an example, hello.cpp turns into ./build/hello.cpp.o
+OBJS := $(SRCS:%=$(BUILD_DIR)/%.o)
+
+# String substitution (suffix version without %).
+# As an example, ./build/hello.cpp.o turns into ./build/hello.cpp.d
+DEPS := $(OBJS:.o=.d)
+
+# Every folder in ./src will need to be passed to GCC so that it can find header files
+INC_DIRS := $(shell find $(SRC_DIRS) -type d)
+# Add a prefix to INC_DIRS. So moduleA would become -ImoduleA. GCC understands this -I flag
+INC_FLAGS := $(addprefix -I,$(INC_DIRS))
+
+CPPFLAGS := $(INC_FLAGS) -Wall -Werror -std=c++20 -Og -g
+
+# The final build step.
+$(TARGET_EXEC): $(OBJS)
+ @echo "Linking $@"
+ @$(CXX) $(OBJS) -o $@ $(LDFLAGS)
+
+# Build step for C source
+$(BUILD_DIR)/%.c.o: %.c
+ @mkdir -p $(dir $@)
+ @echo "$<"
+ @$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
+
+# Build step for C++ source
+$(BUILD_DIR)/%.cpp.o: %.cpp
+ @mkdir -p $(dir $@)
+ @echo "$<"
+ @$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
+
+.PHONY: clean
clean:
- rm ./test
+ @rm -r $(BUILD_DIR) $(TARGET_EXEC)
+
+# Include the .d makefiles. The - at the front suppresses the errors of missing
+# Makefiles. Initially, all the .d files will be missing, and we don't want those
+# errors to show up.
+-include $(DEPS)
\ No newline at end of file
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/ldr_oc_customize.inl b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp
similarity index 65%
rename from Source/Atmosphere/stratosphere/loader/source/oc/ldr_oc_customize.inl
rename to Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp
index ccb02f49..41731e13 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/ldr_oc_customize.inl
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp
@@ -1,28 +1,43 @@
+/*
+ * Copyright (C) Switch-OC-Suite
+ *
+ * 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 .
+ */
+
+#include "customize.hpp"
+
namespace ams::ldr::oc {
-#include "mtc_empty_table.inl"
-static const volatile CustomizeTable C = {
+
+volatile CustomizeTable C = {
/* DRAM Timing:
* AUTO_ADJ_MARIKO_SAFE: Auto adjust timings for LPDDR4 ≤3733 Mbps specs, 8Gb density (Default).
* AUTO_ADJ_MARIKO_4266: Auto adjust timings for LPDDR4X 4266 Mbps specs, 8Gb density.
- * ENTIRE_TABLE_ERISTA/ENTIRE_TABLE_MARIKO:
- * Replace the entire max mtc table with customized one (provided by user).
*/
.mtcConf = AUTO_ADJ_MARIKO_SAFE,
/* Mariko CPU:
* - Max Clock in kHz:
* Default: 1785000
- * >= 2193000 will enable overvolting (> 1120 mV)
+ * >= 2397000 will enable overvolting (> 1120 mV)
* - Boost Clock in kHz:
* Default: 1785000
* Boost clock will be applied when applications request higher CPU frequency for quicker loading.
* - Max Voltage in mV:
* Default voltage: 1120
- * Haven't tested anything higher than 1220.
*/
.marikoCpuMaxClock = 2397000,
.marikoCpuBoostClock = 1785000,
-.marikoCpuMaxVolt = 1220,
+.marikoCpuMaxVolt = 1235,
/* Mariko GPU:
* - Max Clock in kHz:
@@ -44,13 +59,12 @@ static const volatile CustomizeTable C = {
.marikoEmcMaxClock = 1996800,
/* Erista CPU:
- * Untested and not enabled by default.
+ * Not tested but enabled by default.
* - Enable Overclock
- * Require modificaitions towards NewCpuTables!
* - Max Voltage in mV
*/
-.eristaCpuOCEnable = 0,
-.eristaCpuMaxVolt = 0,
+.eristaCpuOCEnabled= 1,
+.eristaCpuMaxVolt = 1257,
/* Erista EMC:
* - RAM Clock in kHz
@@ -67,7 +81,6 @@ static const volatile CustomizeTable C = {
*/
.eristaEmcMaxClock = 1862400,
.eristaEmcVolt = 0,
-
-.eristaMtc = reinterpret_cast(const_cast(EmptyMtcTable)),
};
+
}
\ No newline at end of file
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp
new file mode 100644
index 00000000..f9f417d8
--- /dev/null
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) Switch-OC-Suite
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#define CUST_REV 2
+
+#include "oc_suite_common.hpp"
+
+namespace ams::ldr::oc {
+
+#include "mtc_timing_table.hpp"
+
+enum MtcConfig {
+ AUTO_ADJ_MARIKO_SAFE = 0,
+ AUTO_ADJ_MARIKO_4266 = 1,
+};
+
+typedef struct __attribute__((packed)) CustomizeTable {
+ u8 cust[4] = {'C', 'U', 'S', 'T'};
+ u16 custRev = CUST_REV;
+ u16 mtcConf = AUTO_ADJ_MARIKO_SAFE;
+ u32 marikoCpuMaxClock;
+ u32 marikoCpuBoostClock;
+ u32 marikoCpuMaxVolt;
+ u32 marikoGpuMaxClock;
+ u32 marikoEmcMaxClock;
+ u32 eristaCpuOCEnabled;
+ u32 eristaCpuMaxVolt;
+ u32 eristaEmcMaxClock;
+ u32 eristaEmcVolt;
+} CustomizeTable;
+
+extern volatile CustomizeTable C;
+
+}
\ No newline at end of file
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/ldr_oc_suite.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/ldr_oc_suite.cpp
deleted file mode 100644
index f4a3cb31..00000000
--- a/Source/Atmosphere/stratosphere/loader/source/oc/ldr_oc_suite.cpp
+++ /dev/null
@@ -1,1271 +0,0 @@
-/*
- * Copyright (C) Switch-OC-Suite
- *
- * 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 .
- */
-
-//#define EXPERIMENTAL
-
-#ifndef ATMOSPHERE_IS_STRATOSPHERE
-#include "ldr_oc_suite_test.hpp"
-#else
-#include
-#include "ldr_oc_suite.hpp"
-#include "ldr_oc_customize.inl"
-#endif
-
-namespace ams::ldr::oc {
- namespace pcv {
- Result MemPllmLimitHandler(u32* ptr) {
- clk_pll_param* entry = reinterpret_cast(ptr);
- R_UNLESS(entry->max_0 == entry->max_1, ldr::ResultInvalidMemPllmEntry());
-
- // Double the max clk simply
- u32 max_clk = entry->max_0 * 2;
- entry->max_0 = max_clk;
- entry->max_1 = max_clk;
- R_SUCCEED();
- }
-
- template
- Result MtcOverwrite(M* des, M* src) {
- constexpr u32 mtc_magic = 0x5F43544D;
- R_UNLESS(src->rev == mtc_magic, ldr::ResultInvalidMtcMagic());
-
- // Ignore params from dvfs_ver to clock_src;
- for (size_t offset = offsetof(M, clk_src_emc); offset < sizeof(M); offset += sizeof(u32)) {
- u32* src_ent = reinterpret_cast(reinterpret_cast(src) + offset);
- u32* des_ent = reinterpret_cast(reinterpret_cast(des) + offset);
- u32 src_val = *src_ent;
-
- constexpr u32 ignore_val = UINT32_MAX;
- if (src_val != ignore_val) {
- PatchOffset(des_ent, src_val);
- }
- }
-
- R_SUCCEED();
- }
- }
-
- namespace pcv::Mariko {
- constexpr u32 CpuClkOSLimit = 1785'000;
- constexpr u32 CpuClkOfficial = 1963'500;
- constexpr u32 CpuVoltOfficial = 1120;
- // constexpr u32 GpuClkOSLimit = 921'600;
- constexpr u32 GpuClkOfficial = 1267'200;
- constexpr u32 MemClkOSLimit = 1600'000;
- constexpr u32 MemClkOSAlt = 1331'200;
- constexpr u32 GpuClkPllLimit = 1300'000'000;
- constexpr u32 MemClkPllmLimit = 2133'000'000;
-
- constexpr u32 CommonDvfsEntryCnt = 32;
- /* CPU */
- constexpr u32 OldCpuDvfsEntryCnt = 18;
- constexpr cpu_freq_cvb_table_t NewCpuTables[] = {
- // OldCpuTables
- // { 204000, { 721589, -12695, 27 }, {} },
- // { 306000, { 747134, -14195, 27 }, {} },
- // { 408000, { 776324, -15705, 27 }, {} },
- // { 510000, { 809160, -17205, 27 }, {} },
- // { 612000, { 845641, -18715, 27 }, {} },
- // { 714000, { 885768, -20215, 27 }, {} },
- // { 816000, { 929540, -21725, 27 }, {} },
- // { 918000, { 976958, -23225, 27 }, {} },
- // { 1020000, { 1028021, -24725, 27 }, { 1120000 } },
- // { 1122000, { 1082730, -26235, 27 }, { 1120000 } },
- // { 1224000, { 1141084, -27735, 27 }, { 1120000 } },
- // { 1326000, { 1203084, -29245, 27 }, { 1120000 } },
- // { 1428000, { 1268729, -30745, 27 }, { 1120000 } },
- // { 1581000, { 1374032, -33005, 27 }, { 1120000 } },
- // { 1683000, { 1448791, -34505, 27 }, { 1120000 } },
- // { 1785000, { 1527196, -36015, 27 }, { 1120000 } },
- // { 1887000, { 1609246, -37515, 27 }, { 1120000 } },
- // { 1963500, { 1675751, -38635, 27 }, { 1120000 } },
- { 2091000, { 1785520, -40523, 27 }, { 1120000 } },
- { 2193000, { 1878755, -42027, 27 }, { 1120000 } },
- { 2295000, { 1975655, -43531, 27 }, { 1120000 } },
- { 2397000, { 2076220, -45036, 27 }, { 1120000 } },
- };
- static_assert(sizeof(NewCpuTables) <= sizeof(cpu_freq_cvb_table_t) * (CommonDvfsEntryCnt - OldCpuDvfsEntryCnt));
-
- /* GPU */
- constexpr u32 OldGpuDvfsEntryCnt = 17;
- constexpr gpu_cvb_pll_table_t NewGpuTables[] = {
- // OldGpuTables
- // { 76800, {}, { 610000, } },
- // { 153600, {}, { 610000, } },
- // { 230400, {}, { 610000, } },
- // { 307200, {}, { 610000, } },
- // { 384000, {}, { 610000, } },
- // { 460800, {}, { 610000, } },
- // { 537600, {}, { 801688, -10900, -163, 298, -10599, 162 } },
- // { 614400, {}, { 824214, -5743, -452, 238, -6325, 81 } },
- // { 691200, {}, { 848830, -3903, -552, 119, -4030, -2 } },
- // { 768000, {}, { 891575, -4409, -584, 0, -2849, 39 } },
- // { 844800, {}, { 940071, -5367, -602, -60, -63, -93 } },
- // { 921600, {}, { 986765, -6637, -614, -179, 1905, -13 } },
- // { 998400, {}, { 1098475, -13529, -497, -179, 3626, 9 } },
- // { 1075200, {}, { 1163644, -12688, -648, 0, 1077, 40 } },
- // { 1152000, {}, { 1204812, -9908, -830, 0, 1469, 110 } },
- // { 1228800, {}, { 1277303, -11675, -859, 0, 3722, 313 } },
- // { 1267200, {}, { 1335531, -12567, -867, 0, 3681, 559 } },
- { 1305600, {}, { 1374130, -13725, -859, 0, 4442, 576 } },
- };
- static_assert(sizeof(NewGpuTables) <= sizeof(gpu_cvb_pll_table_t) * (CommonDvfsEntryCnt - OldGpuDvfsEntryCnt));
-
- /* GPU Max Clock asm Pattern:
- *
- * MOV W11, #0x1000 MOV (wide immediate) 0x1000 0xB (11)
- * sf | opc | | hw | imm16 | Rd
- * #31 |30 29|28 27 26 25 24 23|22 21|20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 |4 3 2 1 0
- * 0 | 1 0 | 1 0 0 1 0 1| 0 0| 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 |0 1 0 1 1
- *
- * MOVK W11, #0xE, LSL#16 16 0xE 0xB (11)
- * sf | opc | | hw | imm16 | Rd
- * #31 |30 29|28 27 26 25 24 23|22 21|20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 |4 3 2 1 0
- * 0 | 1 1 | 1 0 0 1 0 1| 0 1| 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 |0 1 0 1 1
- */
- constexpr u32 gpuOfficialMarikoPattern[2] = {
- 0x52820000,
- 0x72A001C0
- };
- const u32 gpuMaxClockMarikoPattern[2] = {
- (gpuOfficialMarikoPattern[0] & 0xFFE00000) | ((C.marikoGpuMaxClock & 0xFFFF) << 5),
- (gpuOfficialMarikoPattern[1] & 0xFFE00000) | (((C.marikoGpuMaxClock >> 16) & 0xFFFF) << 5)
- };
-
- #define COMPARE_HIGH(val1, val2, bit_div) (((val1 ^ val2) >> bit_div) == 0)
-
- /* EMC */
- constexpr u32 MTC_TABLE_REV = 3;
- // DvbTable is all about frequency scaling along with CPU core voltage, no need to care about this for now.
-
- // constexpr emc_dvb_dvfs_table_t EmcDvbTable[6] =
- // {
- // { 204000, { 637, 637, 637, } },
- // { 408000, { 637, 637, 637, } },
- // { 800000, { 637, 637, 637, } },
- // { 1065600, { 637, 637, 637, } },
- // { 1331200, { 650, 637, 637, } },
- // { 1600000, { 675, 650, 637, } },
- // };
-
- void MtcPllmbDivHandler(MarikoMtcTable* table) {
- // Calculate DIVM and DIVN (clock divisors)
- // Common PLL oscillator is 38.4 MHz
- // PLLMB_OUT = 38.4 MHz / PLLLMB_DIVM * PLLMB_DIVN
- typedef struct {
- u8 numerator : 4;
- u8 denominator : 4;
- } pllmb_div;
-
- constexpr pllmb_div div[] = {
- {3, 4}, {1, 2}, {1, 4}, {0, 1}
- };
-
- constexpr u32 pll_osc_in = 38400;
-
- u32 divm {}, divn {};
- const u32 remainder = C.marikoEmcMaxClock % pll_osc_in;
- for (const auto &index : div) {
- if (remainder >= pll_osc_in * index.numerator / index.denominator) {
- divm = index.denominator;
- divn = C.marikoEmcMaxClock / pll_osc_in * divm + index.numerator;
- break;
- }
- }
-
- table->pllmb_divm = divm;
- table->pllmb_divn = divn;
- }
-
- void MtcTableAutoAdjust(MarikoMtcTable* table, const MarikoMtcTable* ref)
- {
- /* Official Tegra X1 TRM, sign up for nvidia developer program (free) to download:
- * https://developer.nvidia.com/embedded/dlc/tegra-x1-technical-reference-manual
- * Section 18.11: MC Registers
- *
- * Retail Mariko: 200FBGA 16Gb DDP LPDDR4X SDRAM x 2
- * x16/Ch, 1Ch/die, Double-die, 2Ch, 1CS(rank), 8Gb density per die
- * 64Mb x 16DQ x 8banks x 2channels = 2048MB (x32DQ) per package
- *
- * Devkit Mariko: 200FBGA 32Gb DDP LPDDR4X SDRAM x 2
- * x16/Ch, 1Ch/die, Quad-die, 2Ch, 2CS(rank), 8Gb density per die
- * X1+ EMC can R/W to both ranks at the same time, resulting in doubled DQ
- * 64Mb x 32DQ x 8banks x 2channels = 4096MB (x64DQ) per package
- *
- * If you have access to LPDDR4(X) specs or datasheets (from manufacturers or Google),
- * you'd better calculate timings yourself rather than relying on following algorithm.
- */
-
- #define ADJUST_PROP(TARGET, REF) \
- (u32)(std::ceil(REF + ((C.marikoEmcMaxClock-MemClkOSAlt)*(TARGET-REF))/(MemClkOSLimit-MemClkOSAlt)))
-
- #define ADJUST_PARAM(TARGET, REF) \
- TARGET = ADJUST_PROP(TARGET, REF);
-
- #define ADJUST_PARAM_TABLE(TABLE, PARAM, REF) ADJUST_PARAM(TABLE->PARAM, REF->PARAM)
-
- #define ADJUST_PARAM_ALL_REG(TABLE, PARAM, REF) \
- ADJUST_PARAM_TABLE(TABLE, burst_regs.PARAM, REF) \
- ADJUST_PARAM_TABLE(TABLE, shadow_regs_ca_train.PARAM, REF) \
- ADJUST_PARAM_TABLE(TABLE, shadow_regs_rdwr_train.PARAM, REF)
-
- #define WRITE_PARAM_ALL_REG(TABLE, PARAM, VALUE)\
- TABLE->burst_regs.PARAM = VALUE; \
- TABLE->shadow_regs_ca_train.PARAM = VALUE; \
- TABLE->shadow_regs_rdwr_train.PARAM = VALUE;
-
- ADJUST_PARAM_ALL_REG(table, emc_r2w, ref);
- ADJUST_PARAM_ALL_REG(table, emc_w2r, ref);
- ADJUST_PARAM_ALL_REG(table, emc_r2p, ref);
- ADJUST_PARAM_ALL_REG(table, emc_w2p, ref);
- ADJUST_PARAM_ALL_REG(table, emc_trtm, ref);
- ADJUST_PARAM_ALL_REG(table, emc_twtm, ref);
- ADJUST_PARAM_ALL_REG(table, emc_tratm, ref);
- ADJUST_PARAM_ALL_REG(table, emc_twatm, ref);
-
- ADJUST_PARAM_ALL_REG(table, emc_rw2pden, ref);
-
- ADJUST_PARAM_ALL_REG(table, emc_tclkstop, ref);
-
- ADJUST_PARAM_ALL_REG(table, emc_pmacro_dll_cfg_2, ref); // EMC_DLL_CFG_2_0: level select for VDDA?
-
- // ADJUST_PARAM_TABLE(table, dram_timings.rl); // not used on Mariko
-
- ADJUST_PARAM_TABLE(table, la_scale_regs.mc_mll_mpcorer_ptsa_rate, ref);
- ADJUST_PARAM_TABLE(table, la_scale_regs.mc_ptsa_grant_decrement, ref);
-
- // ADJUST_PARAM_TABLE(table, min_mrs_wait); // not used on LPDDR4X
- // ADJUST_PARAM_TABLE(table, latency); // not used
-
- /* Timings that are available in or can be derived from LPDDR4X datasheet or TRM */
- {
- const bool use_4266_spec = C.mtcConf == AUTO_ADJ_MARIKO_4266;
- // tCK_avg (average clock period) in ns
- const double tCK_avg = 1000'000. / C.marikoEmcMaxClock;
- // tRPpb (row precharge time per bank) in ns
- const u32 tRPpb = 18;
- // tRPab (row precharge time all banks) in ns
- const u32 tRPab = 21;
- // tRAS (row active time) in ns
- const u32 tRAS = 42;
- // tRC (ACTIVATE-ACTIVATE command period same bank) in ns
- const u32 tRC = tRPpb + tRAS;
- // tRFCab (refresh cycle time all banks) in ns for 8Gb density
- const u32 tRFCab = 280;
- // tRFCpb (refresh cycle time per bank) in ns for 8Gb density
- const u32 tRFCpb = 140;
- // tRCD (RAS-CAS delay) in ns
- const u32 tRCD = 18;
- // tRRD (Active bank-A to Active bank-B) in ns
- const double tRRD = use_4266_spec ? 7.5 : 10.;
- // tREFpb (average refresh interval per bank) in ns for 8Gb density
- const u32 tREFpb = 488;
- // tREFab (average refresh interval all 8 banks) in ns for 8Gb density
- // const u32 tREFab = tREFpb * 8;
- // #_of_rows per die for 8Gb density
- const u32 numOfRows = 65536;
- // {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 = u32(std::ceil((double(tREFpb) * C.marikoEmcMaxClock / numOfRows * 1.048 / 2 - 64))) / 4 * 4;
- // tPDEX2WR, tPDEX2RD (timing delay from exiting powerdown mode to a write/read command) in ns
- const u32 tPDEX2 = 10;
- // [Guessed] tACT2PDEN (timing delay from an activate, MRS or EMRS command to power-down entry) in ns
- const u32 tACT2PDEN = 14;
- // [Guessed] tPDEX2MRR (timing delay from exiting powerdown mode to MRR command) in ns
- const double tPDEX2MRR = 28.75;
- // [Guessed] tCKE2PDEN (timing delay from turning off CKE to power-down entry) in ns
- const double tCKE2PDEN = 8.5;
- // tXSR (SELF REFRESH exit to next valid command delay) in ns
- const double tXSR = tRFCab + 7.5;
- // tCKE (minimum CKE high pulse width) in ns
- const u32 tCKE = 8;
- // tCKELPD (minimum CKE low pulse width in SELF REFRESH) in ns
- const u32 tCKELPD = 15;
- // [Guessed] tPD (minimum CKE low pulse width in power-down mode) in ns
- const double tPD = 7.5;
- // tFAW (Four-bank Activate Window) in ns
- const u32 tFAW = use_4266_spec ? 30 : 40;
-
- #define GET_CYCLE_CEIL(PARAM) u32(std::ceil(double(PARAM) / tCK_avg))
-
- 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(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_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(tPDEX2));
- WRITE_PARAM_ALL_REG(table, emc_pdex2rd, GET_CYCLE_CEIL(tPDEX2));
- WRITE_PARAM_ALL_REG(table, emc_act2pden,GET_CYCLE_CEIL(tACT2PDEN));
- WRITE_PARAM_ALL_REG(table, emc_cke2pden,GET_CYCLE_CEIL(tCKE2PDEN));
- WRITE_PARAM_ALL_REG(table, emc_pdex2mrr,GET_CYCLE_CEIL(tPDEX2MRR));
- WRITE_PARAM_ALL_REG(table, emc_txsr, GET_CYCLE_CEIL(tXSR));
- WRITE_PARAM_ALL_REG(table, emc_txsrdll, GET_CYCLE_CEIL(tXSR));
- WRITE_PARAM_ALL_REG(table, emc_tcke, GET_CYCLE_CEIL(tCKE));
- WRITE_PARAM_ALL_REG(table, emc_tckesr, GET_CYCLE_CEIL(tCKELPD));
- WRITE_PARAM_ALL_REG(table, emc_tpd, GET_CYCLE_CEIL(tPD));
- 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_trefbw, REFRESH + 64);
-
- constexpr u32 MC_ARB_DIV = 4; // ?
- table->burst_mc_regs.mc_emem_arb_timing_rcd = std::ceil(GET_CYCLE_CEIL(tRCD) / MC_ARB_DIV - 2);
- table->burst_mc_regs.mc_emem_arb_timing_rp = std::ceil(GET_CYCLE_CEIL(tRPpb) / MC_ARB_DIV - 1);
- table->burst_mc_regs.mc_emem_arb_timing_rc = std::ceil(std::max(GET_CYCLE_CEIL(tRC), GET_CYCLE_CEIL(tRAS)+GET_CYCLE_CEIL(tRPpb)) / MC_ARB_DIV);
- table->burst_mc_regs.mc_emem_arb_timing_ras = std::ceil(GET_CYCLE_CEIL(tRAS) / MC_ARB_DIV - 2);
- table->burst_mc_regs.mc_emem_arb_timing_faw = std::ceil(GET_CYCLE_CEIL(tFAW) / MC_ARB_DIV - 1);
- table->burst_mc_regs.mc_emem_arb_timing_rrd = std::ceil(GET_CYCLE_CEIL(tRRD) / MC_ARB_DIV - 1);
- table->burst_mc_regs.mc_emem_arb_timing_rap2pre = std::ceil(table->burst_regs.emc_r2p / MC_ARB_DIV);
- table->burst_mc_regs.mc_emem_arb_timing_wap2pre = std::ceil(table->burst_regs.emc_w2p / MC_ARB_DIV);
- table->burst_mc_regs.mc_emem_arb_timing_r2w = std::ceil(table->burst_regs.emc_r2w / MC_ARB_DIV + 1);
- table->burst_mc_regs.mc_emem_arb_timing_w2r = std::ceil(table->burst_regs.emc_w2r / MC_ARB_DIV + 1);
- table->burst_mc_regs.mc_emem_arb_timing_rfcpb = std::ceil(GET_CYCLE_CEIL(tRFCpb) / MC_ARB_DIV + 1); // ?
- }
-
- #ifdef EXPERIMENTAL
- {
- #define ADJUST_PARAM_ROUND2_ALL_REG(TARGET_TABLE, REF_TABLE, PARAM) \
- TARGET_TABLE->burst_regs.PARAM = \
- ((ADJUST_PROP(TARGET_TABLE->burst_regs.PARAM, REF_TABLE->burst_regs.PARAM) + 1) >> 1) << 1; \
- TARGET_TABLE->shadow_regs_ca_train.PARAM = \
- ((ADJUST_PROP(TARGET_TABLE->shadow_regs_ca_train.PARAM, REF_TABLE->shadow_regs_ca_train.PARAM) + 1) >> 1) << 1; \
- TARGET_TABLE->shadow_regs_rdwr_train.PARAM = \
- ((ADJUST_PROP(TARGET_TABLE->shadow_regs_rdwr_train.PARAM, REF_TABLE->shadow_regs_rdwr_train.PARAM) + 1) >> 1) << 1;
-
- #define TRIM_BIT(IN_BITS, HIGH, LOW) \
- ((IN_BITS >> LOW) & ( (1u << (HIGH - LOW + 1u)) - 1u ))
-
- #define ADJUST_BIT(TARGET_PARAM, REF_PARAM, HIGH, LOW) \
- ADJUST_PROP(TRIM_BIT(TARGET_PARAM, HIGH, LOW), TRIM_BIT(REF_PARAM, HIGH, LOW))
-
- #define CLEAR_BIT(BITS, HIGH, LOW) \
- BITS = BITS & ~( ((1u << HIGH) << 1u) - (1u << LOW) );
-
- #define ADJUST_BIT_ALL_REG_SINGLE_OP(TARGET_TABLE, REF_TABLE, PARAM, HIGH, LOW, OPERATION) \
- TARGET_TABLE->burst_regs.PARAM = \
- (ADJUST_BIT(TARGET_TABLE->burst_regs.PARAM, REF_TABLE->burst_regs.PARAM, HIGH, LOW) << LOW) OPERATION; \
- TARGET_TABLE->shadow_regs_ca_train.PARAM = \
- (ADJUST_BIT(TARGET_TABLE->shadow_regs_ca_train.PARAM, REF_TABLE->shadow_regs_ca_train.PARAM, HIGH, LOW)) << LOW OPERATION; \
- TARGET_TABLE->shadow_regs_rdwr_train.PARAM = \
- (ADJUST_BIT(TARGET_TABLE->shadow_regs_rdwr_train.PARAM, REF_TABLE->shadow_regs_rdwr_train.PARAM, HIGH, LOW)) << LOW OPERATION;
-
- #define ADJUST_BIT_ALL_REG_PAIR(TARGET_TABLE, REF_TABLE, PARAM, HIGH1, LOW1, HIGH2, LOW2) \
- TARGET_TABLE->burst_regs.PARAM = \
- ADJUST_BIT(TARGET_TABLE->burst_regs.PARAM, REF_TABLE->burst_regs.PARAM, HIGH1, LOW1) << LOW1 \
- | ADJUST_BIT(TARGET_TABLE->burst_regs.PARAM, REF_TABLE->burst_regs.PARAM, HIGH2, LOW2) << LOW2; \
- TARGET_TABLE->shadow_regs_ca_train.PARAM = \
- ADJUST_BIT(TARGET_TABLE->shadow_regs_ca_train.PARAM, REF_TABLE->shadow_regs_ca_train.PARAM, HIGH1, LOW1) << LOW1 \
- | ADJUST_BIT(TARGET_TABLE->shadow_regs_ca_train.PARAM, REF_TABLE->shadow_regs_ca_train.PARAM, HIGH2, LOW2) << LOW2; \
- TARGET_TABLE->shadow_regs_rdwr_train.PARAM = \
- ADJUST_BIT(TARGET_TABLE->shadow_regs_rdwr_train.PARAM, REF_TABLE->shadow_regs_rdwr_train.PARAM, HIGH1, LOW1) << LOW1 \
- | ADJUST_BIT(TARGET_TABLE->shadow_regs_rdwr_train.PARAM, REF_TABLE->shadow_regs_rdwr_train.PARAM, HIGH2, LOW2) << LOW2;
-
- /* For latency allowance */
- #define ADJUST_INVERSE(TARGET) (TARGET * (MemClkOSLimit / 1000) / (C.marikoEmcMaxClock / 1000))
-
- /* emc_wdv, emc_wsv, emc_wev, emc_wdv_mask,
- emc_quse, emc_quse_width, emc_ibdly, emc_obdly,
- emc_einput, emc_einput_duration, emc_qrst, emc_qsafe,
- emc_rdv, emc_rdv_mask, emc_rdv_early, emc_rdv_early_mask */
- ADJUST_PARAM_ROUND2_ALL_REG(target_table, ref_table, emc_wdv);
- ADJUST_PARAM_ROUND2_ALL_REG(target_table, ref_table, emc_wsv);
- ADJUST_PARAM_ROUND2_ALL_REG(target_table, ref_table, emc_wev);
- ADJUST_PARAM_ROUND2_ALL_REG(target_table, ref_table, emc_wdv_mask);
-
- ADJUST_PARAM_ALL_REG(target_table, ref_table, emc_quse);
- ADJUST_PARAM_ALL_REG(target_table, ref_table, emc_quse_width);
-
- ADJUST_BIT_ALL_REG_SINGLE_OP(target_table, ref_table, emc_ibdly, 6,0, | (1 << 28));
- ADJUST_BIT_ALL_REG_SINGLE_OP(target_table, ref_table, emc_obdly, 5,0, | (1 << 28));
-
- ADJUST_PARAM_ALL_REG(target_table, ref_table, emc_einput);
- ADJUST_PARAM_ALL_REG(target_table, ref_table, emc_einput_duration);
-
- ADJUST_BIT_ALL_REG_SINGLE_OP(target_table, ref_table, emc_qrst, 6,0, | (6 << 16));
- ADJUST_PARAM_ALL_REG(target_table, ref_table, emc_qsafe);
-
- ADJUST_PARAM_ALL_REG(target_table, ref_table, emc_rdv);
- target_table->burst_regs.emc_rdv_mask = target_table->burst_regs.emc_rdv + 2;
- target_table->shadow_regs_ca_train.emc_rdv_mask = target_table->shadow_regs_ca_train.emc_rdv + 2;
- target_table->shadow_regs_rdwr_train.emc_rdv_mask = target_table->shadow_regs_rdwr_train.emc_rdv + 2;
-
- ADJUST_PARAM_ALL_REG(target_table, ref_table, emc_rdv_early);
- target_table->burst_regs.emc_rdv_early_mask = target_table->burst_regs.emc_rdv_early + 2;
- target_table->shadow_regs_ca_train.emc_rdv_early_mask = target_table->shadow_regs_ca_train.emc_rdv_early + 2;
- target_table->shadow_regs_rdwr_train.emc_rdv_early_mask = target_table->shadow_regs_rdwr_train.emc_rdv_early + 2;
-
- /* emc_pmacro_...,
- emc_zcal_wait_cnt, emc_mrs_wait_cnt(2),
- emc_pmacro_autocal_cfg_common, emc_dyn_self_ref_control, emc_qpop, emc_pmacro_cmd_pad_tx_ctrl,
- emc_tr_timing_0, emc_tr_rdv, emc_tr_qpop, emc_tr_rdv_mask, emc_tr_qsafe, emc_tr_qrst,
- emc_training_vref_settle */
- /* DDLL values */
- {
- #define OFFSET_ALL_REG(PARAM) \
- offsetof(MarikoMtcTable, burst_regs.PARAM), \
- offsetof(MarikoMtcTable, shadow_regs_ca_train.PARAM), \
- offsetof(MarikoMtcTable, shadow_regs_rdwr_train.PARAM) \
-
- /* Section 1: adjust HI bits: BIT 26:16 */
- const std::vector ddll_high = {
- OFFSET_ALL_REG(emc_pmacro_ob_ddll_long_dq_rank1_4),
- OFFSET_ALL_REG(emc_pmacro_ob_ddll_long_dq_rank1_5),
- OFFSET_ALL_REG(emc_pmacro_ob_ddll_long_dqs_rank0_4),
- OFFSET_ALL_REG(emc_pmacro_ob_ddll_long_dqs_rank0_5),
- OFFSET_ALL_REG(emc_pmacro_ob_ddll_long_dqs_rank1_4),
- OFFSET_ALL_REG(emc_pmacro_ob_ddll_long_dqs_rank1_5),
- OFFSET_ALL_REG(emc_pmacro_ddll_long_cmd_0),
- OFFSET_ALL_REG(emc_pmacro_ddll_long_cmd_1),
- OFFSET_ALL_REG(emc_pmacro_ddll_long_cmd_2),
- OFFSET_ALL_REG(emc_pmacro_ddll_long_cmd_3),
- OFFSET_ALL_REG(emc_pmacro_ddll_long_cmd_4),
- offsetof(MarikoMtcTable, trim_regs.emc_pmacro_ob_ddll_long_dq_rank0_0),
- offsetof(MarikoMtcTable, trim_regs.emc_pmacro_ob_ddll_long_dq_rank0_1),
- offsetof(MarikoMtcTable, trim_regs.emc_pmacro_ob_ddll_long_dq_rank0_2),
- offsetof(MarikoMtcTable, trim_regs.emc_pmacro_ob_ddll_long_dq_rank0_3),
- offsetof(MarikoMtcTable, trim_regs.emc_pmacro_ob_ddll_long_dq_rank0_4),
- offsetof(MarikoMtcTable, trim_regs.emc_pmacro_ob_ddll_long_dq_rank0_5),
- offsetof(MarikoMtcTable, trim_regs.emc_pmacro_ob_ddll_long_dq_rank1_0),
- offsetof(MarikoMtcTable, trim_regs.emc_pmacro_ob_ddll_long_dq_rank1_1),
- offsetof(MarikoMtcTable, trim_regs.emc_pmacro_ob_ddll_long_dq_rank1_2),
- offsetof(MarikoMtcTable, trim_regs.emc_pmacro_ob_ddll_long_dq_rank1_3),
- };
- for (const auto &offset : ddll_high)
- {
- u32 *ddll = reinterpret_cast(reinterpret_cast(table) + offset);
- u32 *ddll_ref = reinterpret_cast(reinterpret_cast(ref) + offset);
- u16 adjusted_ddll = ADJUST_BIT(*ddll, *ddll_ref, 26,16) & ((1 << (26-16)) - 1);
- CLEAR_BIT(*ddll, 26,16)
- *ddll |= adjusted_ddll << 16;
- }
-
- /* Section 2: adjust LOW bits: BIT 10:0 */
- const std::vector ddll_low = {
- OFFSET_ALL_REG(emc_pmacro_ob_ddll_long_dq_rank1_4),
- OFFSET_ALL_REG(emc_pmacro_ob_ddll_long_dq_rank1_5),
- OFFSET_ALL_REG(emc_pmacro_ob_ddll_long_dqs_rank0_0),
- OFFSET_ALL_REG(emc_pmacro_ob_ddll_long_dqs_rank0_1),
- OFFSET_ALL_REG(emc_pmacro_ob_ddll_long_dqs_rank0_3),
- OFFSET_ALL_REG(emc_pmacro_ob_ddll_long_dqs_rank0_4),
- OFFSET_ALL_REG(emc_pmacro_ob_ddll_long_dqs_rank1_0),
- OFFSET_ALL_REG(emc_pmacro_ob_ddll_long_dqs_rank1_1),
- OFFSET_ALL_REG(emc_pmacro_ob_ddll_long_dqs_rank1_3),
- OFFSET_ALL_REG(emc_pmacro_ob_ddll_long_dqs_rank1_4),
- OFFSET_ALL_REG(emc_pmacro_ddll_long_cmd_0),
- OFFSET_ALL_REG(emc_pmacro_ddll_long_cmd_1),
- OFFSET_ALL_REG(emc_pmacro_ddll_long_cmd_2),
- OFFSET_ALL_REG(emc_pmacro_ddll_long_cmd_3),
- OFFSET_ALL_REG(emc_pmacro_ddll_long_cmd_4),
- offsetof(MarikoMtcTable, trim_regs.emc_pmacro_ob_ddll_long_dq_rank0_0),
- offsetof(MarikoMtcTable, trim_regs.emc_pmacro_ob_ddll_long_dq_rank0_1),
- offsetof(MarikoMtcTable, trim_regs.emc_pmacro_ob_ddll_long_dq_rank0_2),
- offsetof(MarikoMtcTable, trim_regs.emc_pmacro_ob_ddll_long_dq_rank0_3),
- offsetof(MarikoMtcTable, trim_regs.emc_pmacro_ob_ddll_long_dq_rank0_4),
- offsetof(MarikoMtcTable, trim_regs.emc_pmacro_ob_ddll_long_dq_rank0_5),
- offsetof(MarikoMtcTable, trim_regs.emc_pmacro_ob_ddll_long_dq_rank1_0),
- offsetof(MarikoMtcTable, trim_regs.emc_pmacro_ob_ddll_long_dq_rank1_1),
- offsetof(MarikoMtcTable, trim_regs.emc_pmacro_ob_ddll_long_dq_rank1_2),
- offsetof(MarikoMtcTable, trim_regs.emc_pmacro_ob_ddll_long_dq_rank1_3),
- };
- for (const auto &offset : ddll_low)
- {
- u32 *ddll = reinterpret_cast(reinterpret_cast(table) + offset);
- u32 *ddll_ref = reinterpret_cast(reinterpret_cast(ref) + offset);
- u16 adjusted_ddll = ADJUST_BIT(*ddll, *ddll_ref, 10,0) & ((1 << 10) - 1);
- CLEAR_BIT(*ddll, 10,0)
- *ddll |= adjusted_ddll;
- }
- }
-
- ADJUST_BIT_ALL_REG_PAIR(target_table, ref_table, emc_zcal_wait_cnt, 21,16, 10,0)
- ADJUST_BIT_ALL_REG_PAIR(target_table, ref_table, emc_mrs_wait_cnt, 21,16, 10,0)
- ADJUST_BIT_ALL_REG_PAIR(target_table, ref_table, emc_mrs_wait_cnt2, 21,16, 10,0)
-
- ADJUST_BIT_ALL_REG_SINGLE_OP(target_table, ref_table, emc_auto_cal_channel, 5,0, | 0xC1E00300)
- ADJUST_BIT_ALL_REG_SINGLE_OP(target_table, ref_table, emc_pmacro_autocal_cfg_common, 5,0, | 8 << 8)
-
- ADJUST_BIT_ALL_REG_PAIR(target_table, ref_table, emc_dyn_self_ref_control, 31,31, 15,0)
-
- ADJUST_PARAM_ALL_REG(target_table, ref_table, emc_qpop);
-
- ADJUST_BIT_ALL_REG_SINGLE_OP(target_table, ref_table, emc_tr_timing_0, 9,0, | 0x1186100)
-
- ADJUST_PARAM_ALL_REG(target_table, ref_table, emc_tr_rdv);
- target_table->burst_regs.emc_tr_rdv_mask = target_table->burst_regs.emc_tr_rdv + 2;
- target_table->shadow_regs_ca_train.emc_tr_rdv_mask = target_table->shadow_regs_ca_train.emc_tr_rdv + 2;
- target_table->shadow_regs_rdwr_train.emc_tr_rdv_mask = target_table->shadow_regs_rdwr_train.emc_tr_rdv + 2;
-
- ADJUST_PARAM_ALL_REG(target_table, ref_table, emc_tr_qpop);
- ADJUST_PARAM_ALL_REG(target_table, ref_table, emc_tr_qsafe);
- ADJUST_BIT_ALL_REG_SINGLE_OP(target_table, ref_table, emc_tr_qrst, 6,0, | (6 << 16));
-
- ADJUST_BIT_ALL_REG_SINGLE_OP(target_table, ref_table, emc_training_vref_settle, 15,0, | (4 << 16));
-
- /* External Memory Arbitration Configuration */
- /* BIT 20:16 - EXTRA_TICKS_PER_UPDATE: 0 */
- /* BIT 8:0 - CYCLES_PER_UPDATE: 12(1600MHz), 10(1331.2MHz) */
- ADJUST_PARAM_TABLE(table, burst_mc_regs.mc_emem_arb_cfg, ref);
-
- /* External Memory Arbitration Configuration: Direction Arbiter: Turns */
- /* BIT 31:24 - W2R_TURN: approx. mc_emem_arb_timing_w2r */
- /* BIT 23:16 - R2W_TURN: approx. mc_emem_arb_timing_r2w */
- /* BIT 15:8 - W2W_TURN: 0 */
- /* BIT 7:0 - R2R_TURN: 0 */
- {
- u8 w2r_turn = table->burst_mc_regs.mc_emem_arb_timing_w2r;
- u8 r2w_turn = table->burst_mc_regs.mc_emem_arb_timing_r2w;
- table->burst_mc_regs.mc_emem_arb_da_turns = w2r_turn << 24 | r2w_turn << 16;
- }
-
- /* External Memory Arbitration Configuration: Direction Arbiter: Covers */
- /* BIT 23:16 - RCD_W_COVER: 13(1600MHz), 11(1331.2MHz) */
- /* BIT 15:8 - RCD_R_COVER: 8(1600MHz), 7(1331.2MHz) */
- /* BIT 7:0 - RC_COVER: approx. mc_emem_arb_timing_rc, 12(1600MHz), 9(1331.2MHz) */
- {
- u32 param_max = table->burst_mc_regs.mc_emem_arb_da_covers;
- u32 param_ref = ref->burst_mc_regs.mc_emem_arb_da_covers;
- u8 rcd_w_cover = ADJUST_BIT(param_max, param_ref, 23,16);
- u8 rcd_r_cover = (ADJUST_BIT(param_max, param_ref, 23,16) + 3) / 2;
- u8 rc_cover = table->burst_mc_regs.mc_emem_arb_timing_rc;
- table->burst_mc_regs.mc_emem_arb_da_covers = rcd_w_cover << 16 | rcd_r_cover << 8 | rc_cover;
- }
-
- /* External Memory Arbitration Configuration: Miscellaneous Thresholds (0) */
- /* BIT 20:16 - PRIORITY_INVERSION_ISO_THRESHOLD: 12(1600MHz), 10(1331.2MHz) */
- /* BIT 14:8 - PRIORITY_INVERSION_THRESHOLD: 36(1600MHz), 30(1331.2MHz) */
- /* BIT 7:0 - BC2AA_HOLDOFF_THRESHOLD: set to mc_emem_arb_timing_rc */
- {
- u32 param_max = table->burst_mc_regs.mc_emem_arb_misc0;
- u32 param_ref = ref->burst_mc_regs.mc_emem_arb_misc0;
- u8 priority_inversion_iso_threshold = ADJUST_BIT(param_max, param_ref, 20,16);
- u8 priority_inversion_threshold = 3 * ADJUST_BIT(param_max, param_ref, 20,16);
- u8 bc2aa_holdoff_threshold = table->burst_mc_regs.mc_emem_arb_timing_rc;
- CLEAR_BIT(table->burst_mc_regs.mc_emem_arb_misc0, 20,16)
- CLEAR_BIT(table->burst_mc_regs.mc_emem_arb_misc0, 14,8)
- CLEAR_BIT(table->burst_mc_regs.mc_emem_arb_misc0, 7,0)
- table->burst_mc_regs.mc_emem_arb_misc0 |=
- (priority_inversion_iso_threshold << 16 | priority_inversion_threshold << 8 | bc2aa_holdoff_threshold);
- }
-
- /* Latency allowance settings */
- {
- /* Section 1: adjust write latency */
- /* BIT 23:16 - ALLOWANCE_WRITE: 128(1600MHz), 153(1331.2MHz) */
- const uint32_t latency_write_offset[] = {
- offsetof(MarikoMtcTable, la_scale_regs.mc_latency_allowance_xusb_0),
- offsetof(MarikoMtcTable, la_scale_regs.mc_latency_allowance_xusb_1),
- offsetof(MarikoMtcTable, la_scale_regs.mc_latency_allowance_tsec_0),
- offsetof(MarikoMtcTable, la_scale_regs.mc_latency_allowance_sdmmca_0),
- offsetof(MarikoMtcTable, la_scale_regs.mc_latency_allowance_sdmmcaa_0),
- offsetof(MarikoMtcTable, la_scale_regs.mc_latency_allowance_sdmmc_0),
- offsetof(MarikoMtcTable, la_scale_regs.mc_latency_allowance_sdmmcab_0),
- offsetof(MarikoMtcTable, la_scale_regs.mc_latency_allowance_ppcs_1),
- offsetof(MarikoMtcTable, la_scale_regs.mc_latency_allowance_mpcore_0),
- offsetof(MarikoMtcTable, la_scale_regs.mc_latency_allowance_avpc_0),
- offsetof(MarikoMtcTable, la_scale_regs.mc_latency_allowance_gpu_0),
- offsetof(MarikoMtcTable, la_scale_regs.mc_latency_allowance_gpu2_0),
- offsetof(MarikoMtcTable, la_scale_regs.mc_latency_allowance_nvenc_0),
- offsetof(MarikoMtcTable, la_scale_regs.mc_latency_allowance_nvdec_0),
- offsetof(MarikoMtcTable, la_scale_regs.mc_latency_allowance_vic_0),
- offsetof(MarikoMtcTable, la_scale_regs.mc_latency_allowance_isp2_1),
- };
- for (uint32_t i = 0; i < sizeof(latency_write_offset)/sizeof(uint32_t); i++)
- {
- uint32_t *latency = reinterpret_cast(reinterpret_cast(target_table) + latency_write_offset[i]);
- CLEAR_BIT(*latency, 23,16)
- *latency |= ADJUST_INVERSE(128) << 16;
- }
-
- /* Section 2: adjust read latency */
- /* BIT 7:0 - ALLOWANCE_READ */
- const uint32_t latency_read_offset[] = {
- offsetof(MarikoMtcTable, la_scale_regs.mc_latency_allowance_hc_0),
- offsetof(MarikoMtcTable, la_scale_regs.mc_latency_allowance_hc_1),
- offsetof(MarikoMtcTable, la_scale_regs.mc_latency_allowance_gpu_0),
- offsetof(MarikoMtcTable, la_scale_regs.mc_latency_allowance_gpu2_0),
- offsetof(MarikoMtcTable, la_scale_regs.mc_latency_allowance_vic_0),
- offsetof(MarikoMtcTable, la_scale_regs.mc_latency_allowance_vi2_0),
- offsetof(MarikoMtcTable, la_scale_regs.mc_latency_allowance_isp2_1),
- };
- for (uint32_t i = 0; i < sizeof(latency_read_offset)/sizeof(uint32_t); i++)
- {
- uint32_t *latency = reinterpret_cast(reinterpret_cast(target_table) + latency_read_offset[i]);
- uint8_t adjusted_latency = ADJUST_INVERSE(TRIM_BIT(*latency, 7,0));
- CLEAR_BIT(*latency, 7,0)
- *latency |= adjusted_latency;
- }
- }
-
- /* PLLM and PLLMB control */
- {
- /*
- * CLK_RST_CONTROLLER_PLLM_SS_CTRL1:
- * BIT 31:16 : PLLM_SDM_SSC_MAX
- * BIT 15:0 : PLLM_SDM_SSC_MIN
- *
- * CLK_RST_CONTROLLER_PLLM_SS_CTRL2:
- * BIT 31:16 : PLLM_SDM_SSC_STEP
- * BIT 15:0 : PLLM_SDM_DIN
- *
- * pllm(b)_ss_ctrl1:
- * 1365, 342 (1600MHz)
- * 0xFAAB, 0xF404 (1331MHz)
- *
- * pllm(b)_ss_ctrl2:
- * 2, 1365 (1600MHz)
- * 6, 0xFAAB (1331MHz)
- *
- * No need to care about this if Spread Spectrum (SS) is disabled
- */
- // Disable PLL Spread Spectrum Control (degrades performance)
- table->pll_en_ssc = 0;
- table->pllm_ss_cfg = 1 << 30;
- }
-
- /* EMC misc. configuration */
- {
- /* ? Command Trigger: MRW, MRW2: MRW_OP - [PMC] data to be written ?
- *
- * EMC_MRW: MRW_OP
- * 1600 MHz: 0x54
- * 1331 MHz: 0x44
- * 1065 MHz: 0x34
- * 800 MHz: 0x34
- * 665 MHz: 0x14
- * 408 MHz: 0x04
- * 204 MHz: 0x04
- *
- * EMC_MRW2: MRW2_OP
- * 1600 MHz: 0x2D 45 5*9
- * 1331 MHz: 0x24 36 4*9
- * 1065 MHz: 0x1B 27 3*9
- * 800 MHz: 0x12 18 2*9
- * 665 MHz: 0x09 9 1*9
- * 408 MHz: 0x00
- * 204 MHz: 0x00
- */
- {
-
- }
-
- /* EMC_CFG_2 */
- /* BIT 5:3 - ZQ_EXTRA_DELAY: 6(1600MHz), 5(1331.2MHz), max possible value: 7 */
- {
- CLEAR_BIT(target_table->emc_cfg_2, 5,3)
- target_table->emc_cfg_2 |= 7 << 3;
- }
- }
- }
- #endif
- }
-
- Result CpuClockVddHandler(u32* ptr) {
- R_UNLESS(*(ptr + 2) == 0, ldr::ResultInvalidCpuClockVddEntry());
- R_UNLESS(*(ptr + 12) == 1525000, ldr::ResultInvalidCpuClockVddEntry());
-
- if (C.marikoCpuMaxClock)
- PatchOffset(ptr, C.marikoCpuMaxClock);
-
- R_SUCCEED();
- }
-
- Result CpuDvfsHandler(u32* ptr, uintptr_t nso_end_offset) {
- cpu_freq_cvb_table_t* entry_1963 = reinterpret_cast(ptr);
- cpu_freq_cvb_table_t* entry_free = entry_1963 + 1;
- cpu_freq_cvb_table_t* entry_204 = entry_free - 18;
- cpu_freq_cvb_table_t* entry_1020 = entry_204 + 8;
- uintptr_t entry_end_offset = reinterpret_cast(entry_free) + sizeof(NewCpuTables) - sizeof(u32);
-
- R_UNLESS(entry_end_offset < nso_end_offset, ldr::ResultOutOfRange());
- R_UNLESS(*(reinterpret_cast(entry_free)) == 0, ldr::ResultInvalidCpuDvfs());
- R_UNLESS(*(reinterpret_cast(entry_204)) == 204'000, ldr::ResultInvalidCpuDvfs());
- R_UNLESS(*(reinterpret_cast(entry_end_offset)) == 0, ldr::ResultInvalidCpuDvfs());
-
- if (C.marikoCpuMaxClock > CpuClkOfficial)
- std::memcpy(reinterpret_cast(entry_free), NewCpuTables, sizeof(NewCpuTables));
-
- // Patch CPU max volt in CPU dvfs table
- cpu_freq_cvb_table_t* entry_current = entry_1020;
- R_UNLESS(entry_current->cvb_pll_param.c0 == CpuVoltOfficial * 1000, ldr::ResultInvalidCpuDvfs());
-
- if (C.marikoCpuMaxVolt) {
- while (entry_current->cvb_pll_param.c0 == CpuVoltOfficial * 1000) {
- PatchOffset(reinterpret_cast(&(entry_current->cvb_pll_param)), C.marikoCpuMaxVolt * 1000);
- entry_current++;
- }
- }
-
- R_SUCCEED();
- }
-
- Result GpuDvfsHandler(u32* ptr, uintptr_t nso_end_offset) {
- gpu_cvb_pll_table_t* entry_1267 = reinterpret_cast(ptr);
- gpu_cvb_pll_table_t* entry_free = entry_1267 + 1;
- gpu_cvb_pll_table_t* entry_76_8 = entry_free - 17;
- uintptr_t entry_end_offset = reinterpret_cast(entry_free) + sizeof(NewGpuTables) - sizeof(u32);
-
- R_UNLESS(entry_end_offset < nso_end_offset, ldr::ResultOutOfRange());
- R_UNLESS(*(reinterpret_cast(entry_free)) == 0, ldr::ResultInvalidGpuDvfs());
- R_UNLESS(*(reinterpret_cast(entry_76_8)) == 76'800, ldr::ResultInvalidGpuDvfs());
- R_UNLESS(*(reinterpret_cast(entry_end_offset)) == 0, ldr::ResultInvalidGpuDvfs());
-
- if (C.marikoGpuMaxClock > GpuClkOfficial)
- std::memcpy(reinterpret_cast(entry_free), NewGpuTables, sizeof(NewGpuTables));
-
- R_SUCCEED();
- }
-
- Result CpuVoltRangeHandler(u32* ptr) {
- u32 value_cpu_min_volt = *(ptr - 1);
- switch (value_cpu_min_volt) {
- case 800:
- case 637:
- case 620:
- case 610:
- if (C.marikoCpuMaxVolt)
- PatchOffset(ptr, C.marikoCpuMaxVolt);
- R_SUCCEED();
- default:
- R_THROW(ldr::ResultInvalidCpuMinVolt());
- }
- }
-
- Result GpuMaxClockHandler(u32* ptr) {
- u32 value = *(ptr);
- u32* ptr_next = ptr + 1;
- u32 value_next = *(ptr_next);
- if (COMPARE_HIGH(value_next, gpuOfficialMarikoPattern[1], 5))
- {
- u32 reg_id = value & ((1 << 5) - 1);
- u32 reg_id_next = value_next & ((1 << 5) - 1);
- if (reg_id == reg_id_next)
- {
- if (C.marikoGpuMaxClock) {
- PatchOffset(ptr , gpuMaxClockMarikoPattern[0] | reg_id);
- PatchOffset(ptr_next, gpuMaxClockMarikoPattern[1] | reg_id);
- }
-
- R_SUCCEED();
- }
- }
- R_THROW(ldr::ResultInvalidGpuMaxClkPattern());
- }
-
- Result MtcTableHandler(u32* ptr) {
- MarikoMtcTable* const mtc_table_max = reinterpret_cast(ptr - offsetof(MarikoMtcTable, rate_khz) / sizeof(u32));
- MarikoMtcTable* const mtc_table_alt = mtc_table_max - 1;
- R_UNLESS(mtc_table_max->rev == MTC_TABLE_REV, ldr::ResultInvalidMtcTable());
- R_UNLESS(mtc_table_alt->rev == MTC_TABLE_REV, ldr::ResultInvalidMtcTable());
- R_UNLESS(mtc_table_alt->rate_khz == MemClkOSAlt, ldr::ResultInvalidMtcTable());
-
- MarikoMtcTable* const table = const_cast(C.marikoMtc);
- bool replace_entire_table = (C.mtcConf == ENTIRE_TABLE_MARIKO);
- if (replace_entire_table) {
- std::memcpy(reinterpret_cast(mtc_table_alt), reinterpret_cast(mtc_table_max), sizeof(MarikoMtcTable));
- return MtcOverwrite(mtc_table_max, table);
- }
-
- std::memcpy(reinterpret_cast(table), reinterpret_cast(mtc_table_max), sizeof(MarikoMtcTable));
- MtcTableAutoAdjust(mtc_table_max, mtc_table_alt);
- MtcPllmbDivHandler(mtc_table_max);
- std::memcpy(reinterpret_cast(mtc_table_alt), reinterpret_cast(table), sizeof(MarikoMtcTable));
- R_SUCCEED();
- }
-
- Result DvbTableHandler(u32* ptr) {
- emc_dvb_dvfs_table_t* dvb_max_entry = reinterpret_cast(ptr);
- emc_dvb_dvfs_table_t* dvb_1331_entry = dvb_max_entry - 1;
-
- u32* dvb_1331_offset = reinterpret_cast(dvb_1331_entry);
- R_UNLESS(*(dvb_1331_offset) == MemClkOSAlt, ldr::ResultInvalidDvbTable());
-
- PatchOffset(dvb_1331_offset, MemClkOSLimit);
- R_SUCCEED();
- }
-
- Result MemMaxClockHandler(u32* ptr) {
- u32 value_next = *(ptr + 1);
- u32 value_next2 = *(ptr + 2);
-
- // Mariko have 3 mtc tables (204/1331/1600 MHz), only these 3 frequencies could be set.
- // Replace 1331 MHz with 1600 MHz as perf @ 1331 MHz is crap.
- constexpr u32 mtc_min_volt = 1100;
- constexpr u32 dvb_entry_volt = 675;
-
- if (value_next == mtc_min_volt) {
- R_TRY(MtcTableHandler(ptr));
- } else if (value_next2 == dvb_entry_volt) {
- R_TRY(DvbTableHandler(ptr));
- }
-
- PatchOffset(ptr, C.marikoEmcMaxClock);
- R_SUCCEED();
- }
-
- Result GpuPllLimitHandler(u32* ptr) {
- u32 value_next = *(ptr + 1);
- R_UNLESS(value_next == 0, ldr::ResultInvalidGpuPllEntry());
-
- // Double the max clk simply
- u32 max_clk = *(ptr) * 2;
- PatchOffset(ptr, max_clk);
- R_SUCCEED();
- }
-
- void Patch(uintptr_t mapped_nso, size_t nso_size) {
- enum PatchSuccessCnt {
- MEM_CLOCK,
- CPU_CLOCK_VDD,
- CPU_TABLE,
- GPU_TABLE,
- CPU_MAX_VOLT,
- GPU_MAX_CLOCK,
- GPU_PLL_CLK,
- MEM_PLL_CLK,
- CNT_MAX,
- };
-
- u8 cnt[CNT_MAX] = {};
-
- for (uintptr_t ptr = mapped_nso;
- ptr <= mapped_nso + nso_size - sizeof(MarikoMtcTable);
- ptr += sizeof(u32))
- {
- u32* ptr32 = reinterpret_cast(ptr);
- u32 value = *(ptr32);
-
- switch (value) {
- case CpuClkOSLimit: [[unlikely]]
- if (R_SUCCEEDED(CpuClockVddHandler(ptr32)))
- cnt[CPU_CLOCK_VDD]++;
- continue;
- case CpuClkOfficial: [[unlikely]]
- if (R_SUCCEEDED(CpuDvfsHandler(ptr32, mapped_nso + nso_size)))
- cnt[CPU_TABLE]++;
- continue;
- case GpuClkOfficial: [[unlikely]]
- if (R_SUCCEEDED(GpuDvfsHandler(ptr32, mapped_nso + nso_size)))
- cnt[GPU_TABLE]++;
- continue;
- case CpuVoltOfficial:[[unlikely]]
- if (R_SUCCEEDED(CpuVoltRangeHandler(ptr32)))
- cnt[CPU_MAX_VOLT]++;
- continue;
- case MemClkOSLimit: [[unlikely]]
- if (R_SUCCEEDED(MemMaxClockHandler(ptr32)))
- cnt[MEM_CLOCK]++;
- continue;
- case GpuClkPllLimit: [[unlikely]]
- if (R_SUCCEEDED(GpuPllLimitHandler(ptr32)))
- cnt[GPU_PLL_CLK]++;
- continue;
- case MemClkPllmLimit:[[unlikely]]
- if (R_SUCCEEDED(MemPllmLimitHandler(ptr32)))
- cnt[MEM_PLL_CLK]++;
- continue;
- default: [[likely]]
- break;
- }
-
- if (COMPARE_HIGH(value, gpuOfficialMarikoPattern[0], 5)) {
- if (R_SUCCEEDED(GpuMaxClockHandler(ptr32)))
- cnt[GPU_MAX_CLOCK]++;
- continue;
- }
- }
-
- LOGGING("CpuClkOSLimit Count: %u\n"\
- "CpuClkOfficial Count: %u\n"\
- "GpuClkOfficial Count: %u\n"\
- "CpuVoltOfficial Count: %u\n"\
- "MemClkOSLimit Count: %u\n"\
- "GpuClkPllLimit Count: %u\n"\
- "MemClkPllmLimit Count: %u\n"\
- "GpuAsmPattern Count: %u",
- cnt[CPU_CLOCK_VDD],
- cnt[CPU_TABLE],
- cnt[GPU_TABLE],
- cnt[CPU_MAX_VOLT],
- cnt[MEM_CLOCK],
- cnt[GPU_PLL_CLK],
- cnt[MEM_PLL_CLK],
- cnt[GPU_MAX_CLOCK]);
-
- if (
- #ifndef ATMOSPHERE_IS_STRATOSPHERE
- !cnt[CPU_CLOCK_VDD] ||
- !cnt[CPU_TABLE] ||
- !cnt[GPU_TABLE] ||
- !cnt[CPU_MAX_VOLT] ||
- !cnt[GPU_PLL_CLK] ||
- !cnt[MEM_PLL_CLK] ||
- !cnt[GPU_MAX_CLOCK] ||
- #endif
- cnt[CPU_CLOCK_VDD] > 1 ||
- cnt[CPU_TABLE] > 1 ||
- cnt[GPU_TABLE] > 1 ||
- cnt[CPU_MAX_VOLT] > 13 ||
- cnt[GPU_PLL_CLK] > 1 ||
- cnt[MEM_PLL_CLK] > 2 ||
- cnt[GPU_MAX_CLOCK] > 2)
- {
- CRASH();
- }
- }
- }
-
- namespace pcv::Erista {
- constexpr u32 CpuClkOSLimit = 1785'000;
- constexpr u32 CpuVoltLimit1 = 1132;
- constexpr u32 CpuVoltLimit2 = 1170;
- constexpr u32 CpuVoltLimit3 = 1227;
- constexpr u32 MemVoltHOS = 1125'000;
- constexpr u32 MemClkOSLimit = 1600'000;
- constexpr u32 MemClkPllmLimit = 1866'000'000;
-
- constexpr cpu_freq_cvb_table_t NewCpuTables[] = {
- // OldCpuTables
- // { 204000, { 721094 }, {} },
- // { 306000, { 754040 }, {} },
- // { 408000, { 786986 }, {} },
- // { 510000, { 819932 }, {} },
- // { 612000, { 852878 }, {} },
- // { 714000, { 885824 }, {} },
- // { 816000, { 918770 }, {} },
- // { 918000, { 951716 }, {} },
- // { 1020000, { 984662 }, { -2875621, 358099, -8585 } },
- // { 1122000, { 1017608 }, { -52225, 104159, -2816 } },
- // { 1224000, { 1050554 }, { 1076868, 8356, -727 } },
- // { 1326000, { 1083500 }, { 2208191, -84659, 1240 } },
- // { 1428000, { 1116446 }, { 2519460, -105063, 1611 } },
- // { 1581000, { 1130000 }, { 2889664, -122173, 1834 } },
- // { 1683000, { 1168000 }, { 5100873, -279186, 4747 } },
- // { 1785000, { 1227500 }, { 5100873, -279186, 4747 } },
- { 1887000, {}, {} },
- { 1989000, {}, {} },
- { 2091000, {}, {} },
- };
-
- constexpr u32 MTC_TABLE_REV = 7;
-
- Result CpuDvfsHandler(u32* ptr, uintptr_t nso_end_offset) {
- cpu_freq_cvb_table_t* entry_1785 = reinterpret_cast(ptr);
- cpu_freq_cvb_table_t* entry_free = entry_1785 + 1;
- cpu_freq_cvb_table_t* entry_204 = entry_free - 16;
- uintptr_t entry_end_offset = reinterpret_cast(entry_free) + sizeof(NewCpuTables) - sizeof(u32);
-
- R_UNLESS(entry_end_offset < nso_end_offset, ldr::ResultOutOfRange());
- R_UNLESS(*(reinterpret_cast(entry_free)) == 0, ldr::ResultInvalidCpuDvfs());
- R_UNLESS(*(reinterpret_cast(entry_204)) == 204'000, ldr::ResultInvalidCpuDvfs());
- R_UNLESS(*(reinterpret_cast(entry_end_offset)) == 0, ldr::ResultInvalidCpuDvfs());
-
- if (C.eristaCpuOCEnable)
- std::memcpy(reinterpret_cast(entry_free), NewCpuTables, sizeof(NewCpuTables));
-
- R_SUCCEED();
- }
-
- Result CpuVoltRangeHandler(u32* ptr) {
- u32 value_cpu_min_volt = *(ptr - 1);
- switch (value_cpu_min_volt) {
- case 950:
- case 850:
- case 825:
- case 810:
- if (C.eristaCpuMaxVolt)
- PatchOffset(ptr, C.eristaCpuMaxVolt);
- R_SUCCEED();
- default:
- LOGGING("Invalid min voltage: %u @%p!", *(ptr-1), ptr-1);
- R_THROW(ldr::ResultInvalidCpuMinVolt());
- }
- }
-
- Result MtcTableHandler(u32* ptr) {
- u32 khz_list[] = { 1600000, 1331200, 1065600, 800000, 665600, 408000, 204000, 102000, 68000, 40800 };
- u32 khz_list_size = sizeof(khz_list) / sizeof(u32);
- EristaMtcTable* table_list[khz_list_size];
- table_list[0] = reinterpret_cast(ptr - offsetof(EristaMtcTable, rate_khz) / sizeof(u32));
-
- for (u32 i = 1; i < khz_list_size; i++ ) {
- table_list[i] = table_list[i-1] - 1;
- R_UNLESS(table_list[i]->rate_khz == khz_list[i], ldr::ResultInvalidMtcTable());
- R_UNLESS(table_list[i]->rev == MTC_TABLE_REV, ldr::ResultInvalidMtcTable());
- }
-
- // Make room for new mtc table, discarding useless 40.8 MHz table
- for (u32 i = khz_list_size - 1; i > 0; i--)
- std::memcpy(static_cast(table_list[i]), static_cast(table_list[i - 1]), sizeof(EristaMtcTable));
-
- bool replace_entire_table = (C.mtcConf == ENTIRE_TABLE_ERISTA);
- if (replace_entire_table) {
- EristaMtcTable* const mtc_table_max = reinterpret_cast(ptr - offsetof(EristaMtcTable, rate_khz) / sizeof(u32));
- EristaMtcTable* const table = const_cast(C.eristaMtc);
- return MtcOverwrite(mtc_table_max, table);
- }
-
- R_SUCCEED();
- }
-
- Result MemMaxClockHandler(u32* ptr) {
- u32 value_next = *(ptr + 1);
- constexpr u32 mtc_min_volt = 887;
-
- if (value_next == mtc_min_volt)
- R_TRY(MtcTableHandler(ptr));
-
- if (C.eristaEmcMaxClock > MemClkOSLimit)
- PatchOffset(ptr, C.eristaEmcMaxClock);
-
- R_SUCCEED();
- }
-
- Result MemVoltHandler(u32* ptr) {
- u32 emc_uv = C.eristaEmcVolt;
- if (emc_uv) {
- constexpr u32 uv_step = 12'500;
- if (emc_uv % uv_step)
- emc_uv = emc_uv / uv_step * uv_step;
-
- PatchOffset(ptr, emc_uv);
- }
-
- R_SUCCEED();
- }
-
- void Patch(uintptr_t mapped_nso, size_t nso_size) {
- enum PatchSuccessCnt {
- CPU_CLOCK,
- CPU_MAX_VOLT,
- MEM_CLOCK,
- MEM_VOLT,
- MEM_PLL_CLK,
- CNT_MAX,
- };
-
- u8 cnt[CNT_MAX] = {};
-
- for (uintptr_t ptr = mapped_nso;
- ptr <= mapped_nso + nso_size - sizeof(EristaMtcTable);
- ptr += sizeof(u32))
- {
- u32* ptr32 = reinterpret_cast(ptr);
- u32 value = *(ptr32);
-
- switch (value) {
- case CpuClkOSLimit: [[unlikely]]
- if (R_SUCCEEDED(CpuDvfsHandler(ptr32, mapped_nso + nso_size)))
- cnt[CPU_CLOCK]++;
- continue;
- case CpuVoltLimit1: [[unlikely]]
- case CpuVoltLimit2: [[unlikely]]
- case CpuVoltLimit3: [[unlikely]]
- if (R_SUCCEEDED(CpuVoltRangeHandler(ptr32)))
- cnt[CPU_MAX_VOLT]++;
- continue;
- case MemClkOSLimit: [[unlikely]]
- if (R_SUCCEEDED(MemMaxClockHandler(ptr32)))
- cnt[MEM_CLOCK]++;
- continue;
- case MemVoltHOS: [[unlikely]]
- if (R_SUCCEEDED(MemVoltHandler(ptr32)))
- cnt[MEM_VOLT]++;
- continue;
- case MemClkPllmLimit: [[unlikely]]
- if (R_SUCCEEDED(MemPllmLimitHandler(ptr32)))
- cnt[MEM_PLL_CLK]++;
- continue;
- default: [[likely]]
- break;
- }
- }
-
- LOGGING("CpuClkOSLimit Count: %u\n"\
- "CpuVoltLimit* Count: %u\n"\
- "MemClkOSLimit Count: %u\n"\
- "MemVoltHOS Count: %u\n"\
- "MemClkPllmLimit Count: %u",
- cnt[CPU_CLOCK],
- cnt[CPU_MAX_VOLT],
- cnt[MEM_CLOCK],
- cnt[MEM_VOLT],
- cnt[MEM_PLL_CLK]);
-
- if (
- #ifndef ATMOSPHERE_IS_STRATOSPHERE
- !cnt[CPU_CLOCK] ||
- !cnt[CPU_MAX_VOLT] ||
- !cnt[MEM_CLOCK] ||
- !cnt[MEM_VOLT] ||
- !cnt[MEM_PLL_CLK] ||
- #endif
- cnt[CPU_CLOCK] > 1 ||
- cnt[MEM_VOLT] > 2 ||
- cnt[MEM_PLL_CLK] > 2)
- {
- CRASH();
- }
- }
- }
-
- namespace pcv {
- void SafetyCheck() {
- if (C.custRev != CUST_REV ||
- C.marikoCpuMaxVolt >= 1300 ||
- C.eristaCpuMaxVolt >= 1400 ||
- (C.eristaEmcVolt &&
- (C.eristaEmcVolt < 600'000 || C.eristaEmcVolt > 1250'000)))
- {
- CRASH();
- }
- }
-
- void Patch(uintptr_t mapped_nso, size_t nso_size) {
- #ifdef ATMOSPHERE_IS_STRATOSPHERE
- SafetyCheck();
- bool isMariko = (spl::GetSocType() == spl::SocType_Mariko);
- if (isMariko)
- Mariko::Patch(mapped_nso, nso_size);
- else
- Erista::Patch(mapped_nso, nso_size);
- #endif
- }
- }
-
- namespace ptm {
- void Patch(uintptr_t mapped_nso, size_t nso_size) {
- #ifdef ATMOSPHERE_IS_STRATOSPHERE
- bool isMariko = (spl::GetSocType() == spl::SocType_Mariko);
- if (!isMariko)
- return;
- #endif
-
- perf_conf_entry* confTable = 0;
- constexpr u32 entryCnt = 16;
- constexpr u32 cpuPtmDefault = 1020'000'000;
- constexpr u32 cpuPtmDevOC = 1224'000'000;
- constexpr u32 cpuPtmBoost = 1785'000'000;
- const u32 cpuPtmBoostNew = (C.marikoCpuBoostClock ?
- C.marikoCpuBoostClock * 1000 : cpuPtmBoost);
- constexpr u32 memPtmLimit = 1600'000'000;
- constexpr u32 memPtmAlt = 1331'200'000;
- constexpr u32 memPtmClamp = 1065'600'000;
- const u32 memPtmMax = (C.marikoEmcMaxClock ?
- C.marikoEmcMaxClock * 1000 : memPtmLimit);
-
- enum PatchSuccessCnt {
- CPU_BOOST,
- MEM_MAX,
- MEM_ALT,
- CNT_MAX
- };
-
- u8 cnt[CNT_MAX] = {};
-
- for (uintptr_t ptr = mapped_nso;
- ptr <= mapped_nso + nso_size - sizeof(perf_conf_entry) * entryCnt;
- ptr += sizeof(u32))
- {
- u32* ptr32 = reinterpret_cast(ptr);
- u32 value = *(ptr32);
-
- if (value == cpuPtmDefault)
- {
- u32 value_next = *(ptr32 + 1);
- if (value_next != cpuPtmDefault)
- continue;
-
- confTable = reinterpret_cast(ptr - offsetof(perf_conf_entry, cpu_freq_1));
- break;
- }
- }
-
- if (!confTable) {
- LOGGING("confTable not found!");
- CRASH();
- }
-
- for (u32 i = 0; i < entryCnt; i++)
- {
- perf_conf_entry* entry_current = confTable + i;
-
- if (entry_current->cpu_freq_1 != entry_current->cpu_freq_2 ||
- entry_current->gpu_freq_1 != entry_current->gpu_freq_2 ||
- entry_current->emc_freq_1 != entry_current->emc_freq_2)
- {
- LOGGING("@%p: Invalid confTable entry", &entry_current);
- CRASH();
- }
-
- switch (entry_current->cpu_freq_1)
- {
- case cpuPtmBoost:
- PatchOffset(&(entry_current->cpu_freq_1), cpuPtmBoostNew);
- PatchOffset(&(entry_current->cpu_freq_2), cpuPtmBoostNew);
- cnt[CPU_BOOST]++;
- // LOGGING("0x%08x: CPU: Boost Freq", entry_current->conf_id);
- break;
- case cpuPtmDefault:
- case cpuPtmDevOC:
- break;
- default:
- LOGGING("Unknown CPU Freq: %u @%p!", entry_current->cpu_freq_1, &(entry_current->cpu_freq_1));
- CRASH();
- }
-
- switch (entry_current->emc_freq_1)
- {
- case memPtmLimit:
- PatchOffset(&(entry_current->emc_freq_1), memPtmMax);
- PatchOffset(&(entry_current->emc_freq_2), memPtmMax);
- cnt[MEM_MAX]++;
- // LOGGING("0x%08x: MEM: Max Freq", entry_current->conf_id);
- break;
- case memPtmAlt:
- case memPtmClamp:
- PatchOffset(&(entry_current->emc_freq_1), memPtmLimit);
- PatchOffset(&(entry_current->emc_freq_2), memPtmLimit);
- cnt[MEM_ALT]++;
- // LOGGING("0x%08x: MEM: Alt Freq", entry_current->conf_id);
- break;
- default:
- LOGGING("Unknown MEM Freq: %u @%p!", entry_current->emc_freq_1, &(entry_current->emc_freq_1));
- CRASH();
- }
- }
-
- LOGGING("cpuPtmBoost Count: %u\n"\
- "memPtmMax Count: %u\n"\
- "memPtmAlt Count: %u",
- cnt[CPU_BOOST],
- cnt[MEM_MAX],
- cnt[MEM_ALT]);
-
- if (cnt[CPU_BOOST] > 2 ||
- cnt[MEM_MAX] > 9 ||
- cnt[MEM_ALT] > 7)
- {
- CRASH();
- }
- }
- }
-
-}
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/ldr_oc_suite.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/ldr_oc_suite.hpp
deleted file mode 100644
index 149d1c88..00000000
--- a/Source/Atmosphere/stratosphere/loader/source/oc/ldr_oc_suite.hpp
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) Switch-OC-Suite
- *
- * 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 .
- */
-#pragma once
-#include "mtc_timing_table.hpp"
-
-#define CUST_REV 2
-
-#ifdef ATMOSPHERE_IS_STRATOSPHERE
-#include
-#define LOGGING(fmt, ...) ((void)0)
-#endif
-
-#define CRASH() { AMS_ABORT(); __builtin_unreachable(); }
-
-namespace ams::ldr {
- R_DEFINE_ERROR_RESULT(OutOfRange, 1000);
- R_DEFINE_ERROR_RESULT(InvalidMemPllmEntry, 1001);
- R_DEFINE_ERROR_RESULT(InvalidMtcMagic, 1002);
- R_DEFINE_ERROR_RESULT(InvalidMtcTable, 1003);
- R_DEFINE_ERROR_RESULT(InvalidDvbTable, 1004);
- R_DEFINE_ERROR_RESULT(InvalidCpuClockVddEntry, 1005);
- R_DEFINE_ERROR_RESULT(InvalidCpuDvfs, 1006);
- R_DEFINE_ERROR_RESULT(InvalidCpuMinVolt, 1007);
- R_DEFINE_ERROR_RESULT(InvalidGpuDvfs, 1008);
- R_DEFINE_ERROR_RESULT(InvalidGpuMaxClkPattern, 1009);
- R_DEFINE_ERROR_RESULT(InvalidGpuPllEntry, 1010);
-}
-
-namespace ams::ldr::oc {
- enum MtcConfig {
- AUTO_ADJ_MARIKO_SAFE = 0,
- AUTO_ADJ_MARIKO_4266 = 1,
- ENTIRE_TABLE_ERISTA = 2,
- ENTIRE_TABLE_MARIKO = 3,
- };
-
- typedef struct CustomizeTable {
- u8 cust[4] = {'C', 'U', 'S', 'T'};
- u16 custRev = CUST_REV;
- u16 mtcConf = AUTO_ADJ_MARIKO_SAFE;
- u32 marikoCpuMaxClock;
- u32 marikoCpuBoostClock;
- u32 marikoCpuMaxVolt;
- u32 marikoGpuMaxClock;
- u32 marikoEmcMaxClock;
- u32 eristaCpuOCEnable;
- u32 eristaCpuMaxVolt;
- u32 eristaEmcMaxClock;
- u32 eristaEmcVolt;
- union {
- EristaMtcTable* eristaMtc;
- MarikoMtcTable* marikoMtc;
- };
- } CustomizeTable;
-
- inline void PatchOffset(u32* offset, u32 value) { *(offset) = value; }
-
- namespace pcv {
- typedef struct cvb_coefficients {
- s32 c0 = 0;
- s32 c1 = 0;
- s32 c2 = 0;
- s32 c3 = 0;
- s32 c4 = 0;
- s32 c5 = 0;
- } cvb_coefficients;
-
- typedef struct cpu_freq_cvb_table_t {
- u64 freq;
- cvb_coefficients cvb_dfll_param;
- cvb_coefficients cvb_pll_param; // only c0 is reserved
- } cpu_freq_cvb_table_t;
-
- typedef struct gpu_cvb_pll_table_t {
- u64 freq;
- cvb_coefficients cvb_dfll_param; // empty, dfll clock source not selected
- cvb_coefficients cvb_pll_param;
- } gpu_cvb_pll_table_t;
-
- typedef struct emc_dvb_dvfs_table_t {
- u64 freq;
- s32 volt[4] = {0};
- } emc_dvb_dvfs_table_t;
-
- typedef struct clk_pll_param {
- u32 max_0;
- u32 unk[5];
- u32 max_1;
- } clk_pll_param;
-
- void Patch(uintptr_t mapped_nso, size_t nso_size);
- }
-
- namespace ptm {
- typedef struct {
- u32 conf_id;
- u32 cpu_freq_1;
- u32 cpu_freq_2;
- u32 gpu_freq_1;
- u32 gpu_freq_2;
- u32 emc_freq_1;
- u32 emc_freq_2;
- u32 padding;
- } perf_conf_entry;
-
- void Patch(uintptr_t mapped_nso, size_t nso_size);
- }
-}
\ No newline at end of file
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/ldr_oc_suite_test.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/ldr_oc_suite_test.hpp
deleted file mode 100644
index ea38dd4a..00000000
--- a/Source/Atmosphere/stratosphere/loader/source/oc/ldr_oc_suite_test.hpp
+++ /dev/null
@@ -1,43 +0,0 @@
-#ifndef ATMOSPHERE_IS_STRATOSPHERE
-#include
-#include
-#include
-#include
-
-typedef uint8_t u8;
-typedef uint16_t u16;
-typedef uint32_t u32;
-typedef int32_t s32;
-typedef uint64_t u64;
-typedef int Result;
-
-#define R_SUCCEEDED(arg) (arg == 0)
-#define R_FAILED(arg) (arg != 0)
-#define LOGGING(fmt, ...) { printf(fmt "\n\tin %s\n", ##__VA_ARGS__, __PRETTY_FUNCTION__); }
-#define AMS_ABORT() { fprintf(stderr, "Failed in %s!\n", __PRETTY_FUNCTION__); exit(-1); }
-#define R_SUCCEED() { return 0; }
-#define R_THROW(err) { return err; }
-#define R_TRY(expr) { Result _rc = (expr); if (R_FAILED(_rc)) { return _rc; } }
-#define R_UNLESS(expr, rc) { if (!(expr)) { return rc; } }
-
-#define R_DEFINE_ERROR_RESULT(name, rc) \
- inline Result Result##name() { return rc; }
-
-#include "ldr_oc_suite.hpp"
-
-namespace ams::ldr::oc {
- volatile u8 EmptyMtcTable[0x1340] = { 'M', 'T', 'C', '_', };
- static const volatile CustomizeTable C = {
- .mtcConf = AUTO_ADJ_MARIKO_SAFE,
- .marikoCpuMaxClock = 2397000,
- .marikoCpuMaxVolt = 1220,
- .marikoGpuMaxClock = 1305600,
- .marikoEmcMaxClock = 1996800,
- .eristaCpuOCEnable = 1,
- .eristaCpuMaxVolt = 1300,
- .eristaEmcMaxClock = 1862400,
- .eristaEmcVolt = 1200'000,
- .eristaMtc = reinterpret_cast(const_cast(EmptyMtcTable)),
- };
-}
-#endif
\ No newline at end of file
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/mtc_empty_table.inl b/Source/Atmosphere/stratosphere/loader/source/oc/mtc_empty_table.inl
deleted file mode 100644
index 47f069f5..00000000
--- a/Source/Atmosphere/stratosphere/loader/source/oc/mtc_empty_table.inl
+++ /dev/null
@@ -1,310 +0,0 @@
-volatile u8 EmptyMtcTable[0x1340] = {
- 'M', 'T', 'C', '_', 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-};
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/oc_suite.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/oc_suite.hpp
new file mode 100644
index 00000000..97cb5188
--- /dev/null
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/oc_suite.hpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) Switch-OC-Suite
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include "oc_suite_common.hpp"
+#include "pcv/pcv.hpp"
+#include "ptm/ptm.hpp"
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/oc_suite_common.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/oc_suite_common.hpp
new file mode 100644
index 00000000..bf14ed01
--- /dev/null
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/oc_suite_common.hpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) Switch-OC-Suite
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#ifdef ATMOSPHERE_IS_STRATOSPHERE
+ #include
+ #include
+ #define LOGGING(fmt, ...) ((void)0)
+ #define CRASH(msg, ...) { ams::diag::AbortImpl(msg, __PRETTY_FUNCTION__, "", 0); __builtin_unreachable(); }
+#else
+ #include "oc_suite_test.hpp"
+#endif
+
+#include "customize.hpp"
+
+namespace ams::ldr {
+ R_DEFINE_ERROR_RESULT(OutOfRange, 1000);
+ R_DEFINE_ERROR_RESULT(InvalidMemPllmEntry, 1001);
+ R_DEFINE_ERROR_RESULT(InvalidMtcMagic, 1002);
+ R_DEFINE_ERROR_RESULT(InvalidMtcTable, 1003);
+ R_DEFINE_ERROR_RESULT(InvalidDvbTable, 1004);
+ R_DEFINE_ERROR_RESULT(InvalidCpuFreqVddEntry, 1005);
+ R_DEFINE_ERROR_RESULT(InvalidCpuDvfs, 1006);
+ R_DEFINE_ERROR_RESULT(InvalidCpuMinVolt, 1007);
+ R_DEFINE_ERROR_RESULT(InvalidGpuDvfs, 1008);
+ R_DEFINE_ERROR_RESULT(InvalidGpuFreqMaxPattern, 1009);
+ R_DEFINE_ERROR_RESULT(InvalidGpuPllEntry, 1010);
+ R_DEFINE_ERROR_RESULT(UninitializedPatcher, 1011);
+ R_DEFINE_ERROR_RESULT(UnsuccessfulPatcher, 1012);
+}
+
+namespace ams::ldr::oc {
+ template
+ struct PatcherEntry {
+ using patternFn = bool(*)(Pointer* ptr);
+ using patcherFn = Result(*)(Pointer* ptr);
+
+ const char* description;
+ patcherFn patcher_fn = nullptr;
+ size_t maximum_patched_count = 0;
+ patternFn pattern_search_fn = nullptr;
+ Pointer value_search;
+
+ size_t patched_count = 0;
+
+ Result Apply(Pointer* ptr) {
+ Result res = patcher_fn(ptr);
+ if (R_SUCCEEDED(res))
+ patched_count++;
+
+ return res;
+ }
+
+ Result SearchAndApply(Pointer* ptr) {
+ bool searchOk = false;
+ if (pattern_search_fn) {
+ if (pattern_search_fn(ptr)) searchOk = true;
+ } else {
+ if (value_search == *(ptr)) searchOk = true;
+ }
+
+ if (searchOk)
+ return Apply(ptr);
+
+ R_THROW(ldr::ResultUnsuccessfulPatcher());
+ }
+
+ Result CheckResult() {
+ #ifndef ATMOSPHERE_IS_STRATOSPHERE
+ R_UNLESS(patched_count > 0, ldr::ResultUnsuccessfulPatcher());
+ #endif
+
+ if (maximum_patched_count)
+ R_UNLESS(patched_count <= maximum_patched_count, ldr::ResultUnsuccessfulPatcher());
+
+ R_SUCCEED();
+ }
+ };
+
+ template
+ inline void PatchOffset(T* offset, T value) { static_assert(sizeof(T) < sizeof(u64)); *(offset) = value; }
+}
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/test.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/oc_suite_test.cpp
similarity index 89%
rename from Source/Atmosphere/stratosphere/loader/source/oc/test.cpp
rename to Source/Atmosphere/stratosphere/loader/source/oc/oc_suite_test.cpp
index ca31cef4..eb470090 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/test.cpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/oc_suite_test.cpp
@@ -13,30 +13,10 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
+
#ifndef ATMOSPHERE_IS_STRATOSPHERE
-#include
-#include
-#include
-#include
-#include
-
-namespace ams::ldr::oc {
- namespace pcv {
- namespace Erista {
- void Patch(uintptr_t mapped_exe, size_t nso_size);
- }
-
- namespace Mariko {
- void Patch(uintptr_t mapped_exe, size_t nso_size);
- }
-
- void SafetyCheck();
- }
-
- namespace ptm {
- void Patch(uintptr_t mapped_nso, size_t nso_size);
- }
-}
+#include "oc_suite_test.hpp"
+#include "oc_suite.hpp"
void* loadExec(const char* file_loc, size_t* out_size) {
FILE* fp = fopen(file_loc, "rb");
@@ -131,7 +111,7 @@ int main(int argc, char** argv) {
std::memcpy(erista_buf, file_buffer, file_size);
printf("Patching %s for Erista...\n", pcv_opt);
- ams::ldr::oc::pcv::Erista::Patch(reinterpret_cast(erista_buf), file_size);
+ ams::ldr::oc::pcv::erista::Patch(reinterpret_cast(erista_buf), file_size);
if (save_patched) {
char* exec_path_erista = reinterpret_cast(malloc(exec_path_patched_len));
strlcpy(exec_path_erista, exec_path, exec_path_patched_len);
@@ -147,7 +127,7 @@ int main(int argc, char** argv) {
std::memcpy(mariko_buf, file_buffer, file_size);
printf("Patching %s for Mariko...\n", pcv_opt);
- ams::ldr::oc::pcv::Mariko::Patch(reinterpret_cast(mariko_buf), file_size);
+ ams::ldr::oc::pcv::mariko::Patch(reinterpret_cast(mariko_buf), file_size);
if (save_patched) {
char* exec_path_mariko = reinterpret_cast(malloc(exec_path_patched_len));
strlcpy(exec_path_mariko, exec_path, exec_path_patched_len);
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/oc_suite_test.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/oc_suite_test.hpp
new file mode 100644
index 00000000..93e4e7f1
--- /dev/null
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/oc_suite_test.hpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) Switch-OC-Suite
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#ifndef ATMOSPHERE_IS_STRATOSPHERE
+#include
+#include
+#include
+#include
+#include
+#include
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef int32_t s32;
+typedef uint64_t u64;
+typedef int Result;
+
+#define R_SUCCEEDED(arg) (arg == 0)
+#define R_FAILED(arg) (arg != 0)
+#define LOGGING(fmt, ...) { printf(fmt "\n", ##__VA_ARGS__); }
+#define CRASH(msg, ...) { fprintf(stderr, "%s\nFailed in %s!\n", msg, __PRETTY_FUNCTION__); exit(-1); }
+#define R_SUCCEED() { return 0; }
+#define R_THROW(err) { return err; }
+#define R_TRY(expr) { Result _rc = (expr); if (R_FAILED(_rc)) { return _rc; } }
+#define R_UNLESS(expr, rc) { if (!(expr)) { return rc; } }
+
+#define R_DEFINE_ERROR_RESULT(name, rc) \
+ inline Result Result##name() { return rc; }
+
+#endif
\ No newline at end of file
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp
new file mode 100644
index 00000000..ce67ed13
--- /dev/null
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) Switch-OC-Suite
+ *
+ * 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 .
+ */
+
+#include "pcv.hpp"
+
+namespace ams::ldr::oc::pcv {
+
+Result MemFreqPllmLimit(u32* ptr) {
+ clk_pll_param* entry = reinterpret_cast(ptr);
+ R_UNLESS(entry->freq == entry->vco_max, ldr::ResultInvalidMemPllmEntry());
+
+ // Double the max clk simply
+ u32 max_clk = entry->freq * 2;
+ entry->freq = max_clk;
+ entry->vco_max = max_clk;
+ R_SUCCEED();
+}
+
+void SafetyCheck() {
+ if (C.custRev != CUST_REV ||
+ C.marikoCpuMaxVolt >= 1300 ||
+ C.eristaCpuMaxVolt >= 1300 ||
+ (C.eristaEmcVolt && (C.eristaEmcVolt < 600'000 || C.eristaEmcVolt > 1250'000)))
+ {
+ CRASH("Triggered");
+ }
+}
+
+void Patch(uintptr_t mapped_nso, size_t nso_size) {
+ #ifdef ATMOSPHERE_IS_STRATOSPHERE
+ SafetyCheck();
+ bool isMariko = (spl::GetSocType() == spl::SocType_Mariko);
+ if (isMariko)
+ mariko::Patch(mapped_nso, nso_size);
+ else
+ erista::Patch(mapped_nso, nso_size);
+ #endif
+}
+
+}
\ No newline at end of file
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp
new file mode 100644
index 00000000..00976a80
--- /dev/null
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) Switch-OC-Suite
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include "../oc_suite_common.hpp"
+
+namespace ams::ldr::oc::pcv {
+
+typedef struct cvb_coefficients {
+ s32 c0 = 0;
+ s32 c1 = 0;
+ s32 c2 = 0;
+ s32 c3 = 0;
+ s32 c4 = 0;
+ s32 c5 = 0;
+} cvb_coefficients;
+
+typedef struct cpu_freq_cvb_table_t {
+ u64 freq;
+ cvb_coefficients cvb_dfll_param;
+ cvb_coefficients cvb_pll_param;
+} cpu_freq_cvb_table_t;
+static_assert(sizeof(cpu_freq_cvb_table_t) == 0x38);
+
+typedef struct gpu_cvb_pll_table_t {
+ u64 freq;
+ cvb_coefficients cvb_dfll_param;
+ cvb_coefficients cvb_pll_param;
+} gpu_cvb_pll_table_t;
+static_assert(sizeof(gpu_cvb_pll_table_t) == 0x38);
+
+typedef struct emc_dvb_dvfs_table_t {
+ u64 freq;
+ s32 volt[4] = {0};
+} emc_dvb_dvfs_table_t;
+
+typedef struct __attribute__((packed)) div_nmp {
+ u8 divn_shift;
+ u8 divn_width;
+ u8 divm_shift;
+ u8 divm_width;
+ u8 divp_shift;
+ u8 divp_width;
+ u8 override_divn_shift;
+ u8 override_divm_shift;
+ u8 override_divp_shift;
+} div_nmp;
+
+typedef struct __attribute__((packed)) clk_pll_param {
+ u32 freq;
+ u32 input_min;
+ u32 input_max;
+ u32 cf_min;
+ u32 cf_max;
+ u32 vco_min;
+ u32 vco_max;
+ s32 lock_delay;
+ u32 fixed_rate;
+ u32 unk_0;
+ struct div_nmp *div_nmp;
+ u32 unk_1[4];
+ void (*unk_fn)(u64* unk_struct); // set_defaults?
+} clk_pll_param;
+
+typedef struct __attribute__((packed)) dvfs_rail {
+ u32 id;
+ u32 unk_0[5];
+ u32 freq;
+ u32 unk_1[8];
+ u32 unk_flag;
+ u32 min_mv;
+ u32 step_mv;
+ u32 max_mv;
+ u32 unk_2[11];
+} dvfs_rail;
+
+constexpr u32 CpuClkOSLimit = 1785'000;
+
+constexpr u32 MemClkOSLimit = 1600'000;
+
+#define R_SKIP() R_SUCCEED()
+
+Result MemFreqPllmLimit(u32* ptr);
+
+template
+Result MemMtcTableClone(Table* des, Table* src) {
+ constexpr u32 mtc_magic = 0x5F43544D;
+ R_UNLESS(src->rev == mtc_magic, ldr::ResultInvalidMtcMagic());
+
+ // Skip params from dvfs_ver to clock_src;
+ for (size_t offset = offsetof(Table, clk_src_emc); offset < sizeof(Table); offset += sizeof(u32)) {
+ u32* src_ent = reinterpret_cast(reinterpret_cast(src) + offset);
+ u32* des_ent = reinterpret_cast(reinterpret_cast(des) + offset);
+ u32 src_val = *src_ent;
+
+ constexpr u32 placeholder_val = UINT32_MAX;
+ if (src_val != placeholder_val) {
+ PatchOffset(des_ent, src_val);
+ }
+ }
+
+ R_SUCCEED();
+};
+
+namespace erista {
+ void Patch(uintptr_t mapped_nso, size_t nso_size);
+}
+
+namespace mariko {
+ void Patch(uintptr_t mapped_nso, size_t nso_size);
+}
+
+void SafetyCheck();
+void Patch(uintptr_t mapped_nso, size_t nso_size);
+
+}
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp
new file mode 100644
index 00000000..ea378233
--- /dev/null
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) Switch-OC-Suite
+ *
+ * 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 .
+ */
+
+#include "pcv_erista.hpp"
+
+namespace ams::ldr::oc::pcv::erista {
+
+Result CpuFreqCvbTable(u32* ptr) {
+ cpu_freq_cvb_table_t* default_end = reinterpret_cast(ptr);
+ cpu_freq_cvb_table_t* new_start = default_end + 1;
+
+ // Validate existing table
+ void* cpu_cvb_table_head = reinterpret_cast(new_start) - sizeof(CpuCvbTableDefault);
+ bool validated = std::memcmp(cpu_cvb_table_head, CpuCvbTableDefault, sizeof(CpuCvbTableDefault)) == 0;
+ R_UNLESS(validated, ldr::ResultInvalidCpuDvfs());
+
+ if (!C.eristaCpuOCEnabled)
+ R_SKIP();
+
+ std::memcpy(reinterpret_cast(new_start), CpuCvbTableAppend, sizeof(CpuCvbTableAppend));
+
+ R_SUCCEED();
+}
+
+Result CpuVoltRange(u32* ptr) {
+ u32 min_volt_got = *(ptr - 1);
+ for (const auto& mv : CpuMinVolts) {
+ if (min_volt_got != mv)
+ continue;
+
+ if (!C.eristaCpuMaxVolt)
+ R_SKIP();
+
+ PatchOffset(ptr, C.eristaCpuMaxVolt);
+ R_SUCCEED();
+ }
+ R_THROW(ldr::ResultInvalidCpuMinVolt());
+}
+
+Result MemFreqMtcTable(u32* ptr) {
+ u32 khz_list[] = { 1600000, 1331200, 1065600, 800000, 665600, 408000, 204000, 102000, 68000, 40800 };
+ u32 khz_list_size = sizeof(khz_list) / sizeof(u32);
+
+ // Generate list for mtc table pointers
+ EristaMtcTable* table_list[khz_list_size];
+ for (u32 i = 0; i < khz_list_size; i++) {
+ u8* table = reinterpret_cast(ptr) - offsetof(EristaMtcTable, rate_khz) - i * sizeof(EristaMtcTable);
+ table_list[i] = reinterpret_cast(table);
+ R_UNLESS(table_list[i]->rate_khz == khz_list[i], ldr::ResultInvalidMtcTable());
+ R_UNLESS(table_list[i]->rev == MTC_TABLE_REV, ldr::ResultInvalidMtcTable());
+ }
+
+ if (C.eristaEmcMaxClock <= MemClkOSLimit)
+ R_SKIP();
+
+ // Make room for new mtc table, discarding useless 40.8 MHz table
+ // 40800 overwritten by 68000, ..., 1331200 overwritten by 1600000, leaving table_list[0] not overwritten
+ for (u32 i = khz_list_size - 1; i > 0; i--)
+ std::memcpy(static_cast(table_list[i]), static_cast(table_list[i - 1]), sizeof(EristaMtcTable));
+
+ PatchOffset(ptr, C.eristaEmcMaxClock);
+
+ R_SUCCEED();
+}
+
+Result MemFreqMax(u32* ptr) {
+ if (C.eristaEmcMaxClock <= MemClkOSLimit)
+ R_SKIP();
+
+ PatchOffset(ptr, C.eristaEmcMaxClock);
+
+ R_SUCCEED();
+}
+
+Result MemVoltHandler(u32* ptr) {
+ u32 emc_uv = C.eristaEmcVolt;
+ if (!emc_uv)
+ R_SKIP();
+
+ constexpr u32 uv_step = 12'500;
+ if (emc_uv % uv_step)
+ emc_uv = emc_uv / uv_step * uv_step; // rounding
+
+ PatchOffset(ptr, emc_uv);
+
+ R_SUCCEED();
+}
+
+void Patch(uintptr_t mapped_nso, size_t nso_size) {
+ PatcherEntry patches[] = {
+ { "CPU Freq Table", &CpuFreqCvbTable, 1, nullptr, CpuClkOSLimit },
+ { "CPU Volt Limit", &CpuVoltRange, 0, &CpuMaxVoltPatternFn },
+ { "MEM Freq Mtc", &MemFreqMtcTable, 0, nullptr, MemClkOSLimit },
+ { "MEM Freq Max", &MemFreqMax, 0, nullptr, MemClkOSLimit },
+ { "MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, MemClkPllmLimit },
+ { "MEM Volt", &MemVoltHandler, 2, nullptr, MemVoltHOS },
+ };
+
+ for (uintptr_t ptr = mapped_nso;
+ ptr <= mapped_nso + nso_size - sizeof(EristaMtcTable);
+ ptr += sizeof(u32))
+ {
+ u32* ptr32 = reinterpret_cast(ptr);
+ for (auto& entry : patches) {
+ if (R_SUCCEEDED(entry.SearchAndApply(ptr32)))
+ break;
+ }
+ }
+
+ for (auto& entry : patches) {
+ LOGGING("%s Count: %zu", entry.description, entry.patched_count);
+ if (R_FAILED(entry.CheckResult()))
+ CRASH(entry.description);
+ }
+}
+
+}
\ No newline at end of file
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.hpp
new file mode 100644
index 00000000..16b3149d
--- /dev/null
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.hpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) Switch-OC-Suite
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include "pcv.hpp"
+
+namespace ams::ldr::oc::pcv::erista {
+
+constexpr cpu_freq_cvb_table_t CpuCvbTableDefault[] = {
+ // CPU_PLL_CVB_TABLE_ODN
+ { 204000, { 721094 }, {} },
+ { 306000, { 754040 }, {} },
+ { 408000, { 786986 }, {} },
+ { 510000, { 819932 }, {} },
+ { 612000, { 852878 }, {} },
+ { 714000, { 885824 }, {} },
+ { 816000, { 918770 }, {} },
+ { 918000, { 951716 }, {} },
+ { 1020000, { 984662 }, { -2875621, 358099, -8585 } },
+ { 1122000, { 1017608 }, { -52225, 104159, -2816 } },
+ { 1224000, { 1050554 }, { 1076868, 8356, -727 } },
+ { 1326000, { 1083500 }, { 2208191, -84659, 1240 } },
+ { 1428000, { 1116446 }, { 2519460, -105063, 1611 } },
+ { 1581000, { 1130000 }, { 2889664, -122173, 1834 } },
+ { 1683000, { 1168000 }, { 5100873, -279186, 4747 } },
+ { 1785000, { 1227500 }, { 5100873, -279186, 4747 } },
+};
+
+constexpr cpu_freq_cvb_table_t CpuCvbTableAppend[] = {
+ { 1887000, { 1235000 }, { 5100873, -279186, 4747 } },
+ { 1989000, { 1235000 }, { 5100873, -279186, 4747 } },
+ { 2091000, { 1235000 }, { 5100873, -279186, 4747 } },
+};
+
+constexpr u32 CpuMinVolts[] = { 950, 850, 825, 810 };
+
+inline bool CpuMaxVoltPatternFn(u32* ptr32) {
+ u32 val = *ptr32;
+ return (val == 1132 || val == 1170 || val == 1227);
+}
+
+constexpr u32 MemVoltHOS = 1125'000;
+constexpr u32 MemClkPllmLimit = 1866'000'000;
+
+constexpr u32 MTC_TABLE_REV = 7;
+
+void Patch(uintptr_t mapped_nso, size_t nso_size);
+
+}
\ No newline at end of file
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp
new file mode 100644
index 00000000..f03da3d8
--- /dev/null
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) Switch-OC-Suite
+ *
+ * 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 .
+ */
+
+#include "pcv_mariko.hpp"
+
+namespace ams::ldr::oc::pcv::mariko {
+
+Result CpuFreqVdd(u32* ptr) {
+ dvfs_rail* entry = reinterpret_cast(reinterpret_cast(ptr) - offsetof(dvfs_rail, freq));
+
+ R_UNLESS(entry->id == 1, ldr::ResultInvalidCpuFreqVddEntry());
+ R_UNLESS(entry->min_mv == 250'000, ldr::ResultInvalidCpuFreqVddEntry());
+ R_UNLESS(entry->step_mv == 5000, ldr::ResultInvalidCpuFreqVddEntry());
+ R_UNLESS(entry->max_mv == 1525'000, ldr::ResultInvalidCpuFreqVddEntry());
+
+ if (!C.marikoCpuMaxClock)
+ R_SKIP();
+
+ PatchOffset(ptr, C.marikoCpuMaxClock);
+
+ R_SUCCEED();
+}
+
+Result CpuFreqCvbTable(u32* ptr) {
+ cpu_freq_cvb_table_t* default_end = reinterpret_cast(ptr);
+ cpu_freq_cvb_table_t* new_start = default_end + 1;
+
+ // Validate existing table
+ void* cpu_cvb_table_head = reinterpret_cast(new_start) - sizeof(CpuCvbTableDefault);
+ bool validated = std::memcmp(cpu_cvb_table_head, CpuCvbTableDefault, sizeof(CpuCvbTableDefault)) == 0;
+ R_UNLESS(validated, ldr::ResultInvalidCpuDvfs());
+
+ if (C.marikoCpuMaxClock > CpuClkOfficial)
+ std::memcpy(reinterpret_cast(new_start), CpuCvbTableAppend, sizeof(CpuCvbTableAppend));
+
+ // Patch CPU max volt in existing and appended CPU dvfs table
+ if (C.marikoCpuMaxVolt) {
+ size_t table_size = (sizeof(CpuCvbTableDefault) + sizeof(CpuCvbTableAppend)) / sizeof(cpu_freq_cvb_table_t);
+ cpu_freq_cvb_table_t* entry = static_cast(cpu_cvb_table_head);
+ for (size_t i = 0; i < table_size; i++) {
+ if (entry->cvb_pll_param.c0 == CpuVoltOfficial * 1000) {
+ PatchOffset(reinterpret_cast(&(entry->cvb_pll_param.c0)), C.marikoCpuMaxVolt * 1000);
+ }
+ entry++;
+ }
+ }
+
+ R_SUCCEED();
+}
+
+Result CpuVoltRange(u32* ptr) {
+ u32 min_volt_got = *(ptr - 1);
+ for (const auto& mv : CpuMinVolts) {
+ if (min_volt_got != mv)
+ continue;
+
+ if (!C.marikoCpuMaxVolt)
+ R_SKIP();
+
+ PatchOffset(ptr, C.marikoCpuMaxVolt);
+ R_SUCCEED();
+ }
+ R_THROW(ldr::ResultInvalidCpuMinVolt());
+}
+
+Result GpuFreqCvbTable(u32* ptr) {
+ gpu_cvb_pll_table_t* default_end = reinterpret_cast(ptr);
+ gpu_cvb_pll_table_t* new_start = default_end + 1;
+
+ // Validate existing table
+ void* gpu_cvb_table_head = reinterpret_cast(new_start) - sizeof(GpuCvbTableDefault);
+ bool validated = std::memcmp(gpu_cvb_table_head, GpuCvbTableDefault, sizeof(GpuCvbTableDefault)) == 0;
+ R_UNLESS(validated, ldr::ResultInvalidGpuDvfs());
+
+ if (C.marikoGpuMaxClock <= GpuClkOfficial)
+ R_SKIP();
+
+ std::memcpy(reinterpret_cast(new_start), GpuCvbTableAppend, sizeof(GpuCvbTableAppend));
+
+ R_SUCCEED();
+}
+
+Result GpuFreqMaxAsm(u32* ptr32) {
+ // Check if both two instructions match the pattern
+ u32 ins1 = *ptr32, ins2 = *(ptr32 + 1);
+ if (!(asm_compare_no_rd(ins1, asm_pattern[0]) && asm_compare_no_rd(ins2, asm_pattern[1])))
+ R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
+
+ // Both instructions should operate on the same register
+ u8 rd = asm_get_rd(ins1);
+ if (rd != asm_get_rd(ins2))
+ R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
+
+ if (!C.marikoGpuMaxClock)
+ R_SKIP();
+
+ u32 asm_patch[2] = {
+ asm_set_rd(asm_set_imm16(asm_pattern[0], C.marikoGpuMaxClock), rd),
+ asm_set_rd(asm_set_imm16(asm_pattern[1], C.marikoGpuMaxClock >> 16), rd)
+ };
+ PatchOffset(ptr32, asm_patch[0]);
+ PatchOffset(ptr32 + 1, asm_patch[1]);
+
+ R_SUCCEED();
+}
+
+Result GpuFreqPllLimit(u32* ptr) {
+ clk_pll_param* entry = reinterpret_cast(ptr);
+
+ // All zero except for freq
+ for (size_t i = 1; i < sizeof(clk_pll_param) / sizeof(u32); i++) {
+ R_UNLESS(*(ptr + i) == 0, ldr::ResultInvalidGpuPllEntry());
+ }
+
+ // Double the max clk simply
+ u32 max_clk = entry->freq * 2;
+ entry->freq = max_clk;
+ R_SUCCEED();
+}
+
+void MemMtcTableAutoAdjust(MarikoMtcTable* table, const MarikoMtcTable* ref) {
+ /* Official Tegra X1 TRM, sign up for nvidia developer program (free) to download:
+ * https://developer.nvidia.com/embedded/dlc/tegra-x1-technical-reference-manual
+ * Section 18.11: MC Registers
+ *
+ * Retail Mariko: 200FBGA 16Gb DDP LPDDR4X SDRAM x 2
+ * x16/Ch, 1Ch/die, Double-die, 2Ch, 1CS(rank), 8Gb density per die
+ * 64Mb x 16DQ x 8banks x 2channels = 2048MB (x32DQ) per package
+ *
+ * Devkit Mariko: 200FBGA 32Gb DDP LPDDR4X SDRAM x 2
+ * x16/Ch, 1Ch/die, Quad-die, 2Ch, 2CS(rank), 8Gb density per die
+ * X1+ EMC can R/W to both ranks at the same time, resulting in doubled DQ
+ * 64Mb x 32DQ x 8banks x 2channels = 4096MB (x64DQ) per package
+ *
+ * If you have access to LPDDR4(X) specs or datasheets (from manufacturers or Google),
+ * you'd better calculate timings yourself rather than relying on following algorithm.
+ */
+
+ #define ADJUST_PROP(TARGET, REF) \
+ (u32)(std::ceil(REF + ((C.marikoEmcMaxClock-MemClkOSAlt)*(TARGET-REF))/(MemClkOSLimit-MemClkOSAlt)))
+
+ #define ADJUST_PARAM(TARGET, REF) \
+ TARGET = ADJUST_PROP(TARGET, REF);
+
+ #define ADJUST_PARAM_TABLE(TABLE, PARAM, REF) ADJUST_PARAM(TABLE->PARAM, REF->PARAM)
+
+ #define ADJUST_PARAM_ALL_REG(TABLE, PARAM, REF) \
+ ADJUST_PARAM_TABLE(TABLE, burst_regs.PARAM, REF) \
+ ADJUST_PARAM_TABLE(TABLE, shadow_regs_ca_train.PARAM, REF) \
+ ADJUST_PARAM_TABLE(TABLE, shadow_regs_rdwr_train.PARAM, REF)
+
+ #define WRITE_PARAM_ALL_REG(TABLE, PARAM, VALUE)\
+ TABLE->burst_regs.PARAM = VALUE; \
+ TABLE->shadow_regs_ca_train.PARAM = VALUE; \
+ TABLE->shadow_regs_rdwr_train.PARAM = VALUE;
+
+ ADJUST_PARAM_ALL_REG(table, emc_r2w, ref);
+ ADJUST_PARAM_ALL_REG(table, emc_w2r, ref);
+ ADJUST_PARAM_ALL_REG(table, emc_r2p, ref);
+ ADJUST_PARAM_ALL_REG(table, emc_w2p, ref);
+ ADJUST_PARAM_ALL_REG(table, emc_trtm, ref);
+ ADJUST_PARAM_ALL_REG(table, emc_twtm, ref);
+ ADJUST_PARAM_ALL_REG(table, emc_tratm, ref);
+ ADJUST_PARAM_ALL_REG(table, emc_twatm, ref);
+
+ ADJUST_PARAM_ALL_REG(table, emc_rw2pden, ref);
+
+ ADJUST_PARAM_ALL_REG(table, emc_tclkstop, ref);
+
+ ADJUST_PARAM_ALL_REG(table, emc_pmacro_dll_cfg_2, ref); // EMC_DLL_CFG_2_0: level select for VDDA?
+
+ ADJUST_PARAM_TABLE(table, la_scale_regs.mc_mll_mpcorer_ptsa_rate, ref);
+ ADJUST_PARAM_TABLE(table, la_scale_regs.mc_ptsa_grant_decrement, ref);
+
+ /* Timings that are available in or can be derived from LPDDR4X datasheet or TRM */
+ const bool use_4266_spec = C.mtcConf == AUTO_ADJ_MARIKO_4266;
+ // tCK_avg (average clock period) in ns
+ const double tCK_avg = 1000'000. / C.marikoEmcMaxClock;
+ // tRPpb (row precharge time per bank) in ns
+ const u32 tRPpb = 18;
+ // tRPab (row precharge time all banks) in ns
+ const u32 tRPab = 21;
+ // tRAS (row active time) in ns
+ const u32 tRAS = 42;
+ // tRC (ACTIVATE-ACTIVATE command period same bank) in ns
+ const u32 tRC = tRPpb + tRAS;
+ // tRFCab (refresh cycle time all banks) in ns for 8Gb density
+ const u32 tRFCab = 280;
+ // tRFCpb (refresh cycle time per bank) in ns for 8Gb density
+ const u32 tRFCpb = 140;
+ // tRCD (RAS-CAS delay) in ns
+ const u32 tRCD = 18;
+ // tRRD (Active bank-A to Active bank-B) in ns
+ const double tRRD = use_4266_spec ? 7.5 : 10.;
+ // tREFpb (average refresh interval per bank) in ns for 8Gb density
+ const u32 tREFpb = 488;
+ // tREFab (average refresh interval all 8 banks) in ns for 8Gb density
+ // const u32 tREFab = tREFpb * 8;
+ // #_of_rows per die for 8Gb density
+ const u32 numOfRows = 65536;
+ // {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 = u32(std::ceil((double(tREFpb) * C.marikoEmcMaxClock / numOfRows * 1.048 / 2 - 64))) / 4 * 4;
+ // tPDEX2WR, tPDEX2RD (timing delay from exiting powerdown mode to a write/read command) in ns
+ const u32 tPDEX2 = 10;
+ // [Guessed] tACT2PDEN (timing delay from an activate, MRS or EMRS command to power-down entry) in ns
+ const u32 tACT2PDEN = 14;
+ // [Guessed] tPDEX2MRR (timing delay from exiting powerdown mode to MRR command) in ns
+ const double tPDEX2MRR = 28.75;
+ // [Guessed] tCKE2PDEN (timing delay from turning off CKE to power-down entry) in ns
+ const double tCKE2PDEN = 8.5;
+ // tXSR (SELF REFRESH exit to next valid command delay) in ns
+ const double tXSR = tRFCab + 7.5;
+ // tCKE (minimum CKE high pulse width) in ns
+ const u32 tCKE = 8;
+ // tCKELPD (minimum CKE low pulse width in SELF REFRESH) in ns
+ const u32 tCKELPD = 15;
+ // [Guessed] tPD (minimum CKE low pulse width in power-down mode) in ns
+ const double tPD = 7.5;
+ // tFAW (Four-bank Activate Window) in ns
+ const u32 tFAW = use_4266_spec ? 30 : 40;
+
+ #define GET_CYCLE_CEIL(PARAM) u32(std::ceil(double(PARAM) / tCK_avg))
+
+ 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(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_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(tPDEX2));
+ WRITE_PARAM_ALL_REG(table, emc_pdex2rd, GET_CYCLE_CEIL(tPDEX2));
+ WRITE_PARAM_ALL_REG(table, emc_act2pden,GET_CYCLE_CEIL(tACT2PDEN));
+ WRITE_PARAM_ALL_REG(table, emc_cke2pden,GET_CYCLE_CEIL(tCKE2PDEN));
+ WRITE_PARAM_ALL_REG(table, emc_pdex2mrr,GET_CYCLE_CEIL(tPDEX2MRR));
+ WRITE_PARAM_ALL_REG(table, emc_txsr, GET_CYCLE_CEIL(tXSR));
+ WRITE_PARAM_ALL_REG(table, emc_txsrdll, GET_CYCLE_CEIL(tXSR));
+ WRITE_PARAM_ALL_REG(table, emc_tcke, GET_CYCLE_CEIL(tCKE));
+ WRITE_PARAM_ALL_REG(table, emc_tckesr, GET_CYCLE_CEIL(tCKELPD));
+ WRITE_PARAM_ALL_REG(table, emc_tpd, GET_CYCLE_CEIL(tPD));
+ 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_trefbw, REFRESH + 64);
+
+ constexpr u32 MC_ARB_DIV = 4; // ?
+ table->burst_mc_regs.mc_emem_arb_timing_rcd = std::ceil(GET_CYCLE_CEIL(tRCD) / MC_ARB_DIV - 2);
+ table->burst_mc_regs.mc_emem_arb_timing_rp = std::ceil(GET_CYCLE_CEIL(tRPpb) / MC_ARB_DIV - 1);
+ table->burst_mc_regs.mc_emem_arb_timing_rc = std::ceil(std::max(GET_CYCLE_CEIL(tRC), GET_CYCLE_CEIL(tRAS)+GET_CYCLE_CEIL(tRPpb)) / MC_ARB_DIV);
+ table->burst_mc_regs.mc_emem_arb_timing_ras = std::ceil(GET_CYCLE_CEIL(tRAS) / MC_ARB_DIV - 2);
+ table->burst_mc_regs.mc_emem_arb_timing_faw = std::ceil(GET_CYCLE_CEIL(tFAW) / MC_ARB_DIV - 1);
+ table->burst_mc_regs.mc_emem_arb_timing_rrd = std::ceil(GET_CYCLE_CEIL(tRRD) / MC_ARB_DIV - 1);
+ table->burst_mc_regs.mc_emem_arb_timing_rap2pre = std::ceil(table->burst_regs.emc_r2p / MC_ARB_DIV);
+ table->burst_mc_regs.mc_emem_arb_timing_wap2pre = std::ceil(table->burst_regs.emc_w2p / MC_ARB_DIV);
+ table->burst_mc_regs.mc_emem_arb_timing_r2w = std::ceil(table->burst_regs.emc_r2w / MC_ARB_DIV + 1);
+ table->burst_mc_regs.mc_emem_arb_timing_w2r = std::ceil(table->burst_regs.emc_w2r / MC_ARB_DIV + 1);
+ table->burst_mc_regs.mc_emem_arb_timing_rfcpb = std::ceil(GET_CYCLE_CEIL(tRFCpb) / MC_ARB_DIV + 1); // ?
+}
+
+void MemMtcPllmbDivisor(MarikoMtcTable* table) {
+ // Calculate DIVM and DIVN (clock divisors)
+ // Common PLL oscillator is 38.4 MHz
+ // PLLMB_OUT = 38.4 MHz / PLLLMB_DIVM * PLLMB_DIVN
+ typedef struct {
+ u8 numerator : 4;
+ u8 denominator : 4;
+ } pllmb_div;
+
+ constexpr pllmb_div div[] = {
+ {3, 4}, {2, 3}, {1, 2}, {1, 3}, {1, 4}, {0, 2}
+ };
+
+ constexpr u32 pll_osc_in = 38'400;
+ u32 divm {}, divn {};
+ const u32 remainder = C.marikoEmcMaxClock % pll_osc_in;
+ for (const auto &index : div) {
+ // Round down
+ if (remainder >= pll_osc_in * index.numerator / index.denominator) {
+ divm = index.denominator;
+ divn = C.marikoEmcMaxClock / pll_osc_in * divm + index.numerator;
+ break;
+ }
+ }
+
+ table->pllmb_divm = divm;
+ table->pllmb_divn = divn;
+}
+
+Result MemFreqMtcTable(u32* ptr) {
+ u32 khz_list[] = { 1600000, 1331200, 204000 };
+ u32 khz_list_size = sizeof(khz_list) / sizeof(u32);
+
+ // Generate list for mtc table pointers
+ MarikoMtcTable* table_list[khz_list_size];
+ for (u32 i = 0; i < khz_list_size; i++) {
+ u8* table = reinterpret_cast(ptr) - offsetof(MarikoMtcTable, rate_khz) - i * sizeof(MarikoMtcTable);
+ table_list[i] = reinterpret_cast(table);
+ R_UNLESS(table_list[i]->rate_khz == khz_list[i], ldr::ResultInvalidMtcTable());
+ R_UNLESS(table_list[i]->rev == MTC_TABLE_REV, ldr::ResultInvalidMtcTable());
+ }
+
+ if (C.marikoEmcMaxClock <= MemClkOSLimit)
+ R_SKIP();
+
+ MarikoMtcTable *table_alt = table_list[1], *table_max = table_list[0];
+ MarikoMtcTable *tmp = new MarikoMtcTable;
+
+ // Copy unmodified 1600000 table to tmp
+ std::memcpy(reinterpret_cast(tmp), reinterpret_cast(table_max), sizeof(MarikoMtcTable));
+ // Adjust max freq mtc timing parameters with reference to 1331200 table
+ MemMtcTableAutoAdjust(table_max, table_alt);
+ MemMtcPllmbDivisor(table_max);
+ // Overwrite 13312000 table with unmodified 1600000 table copied back
+ std::memcpy(reinterpret_cast(table_alt), reinterpret_cast(tmp), sizeof(MarikoMtcTable));
+
+ delete tmp;
+
+ PatchOffset(ptr, C.marikoEmcMaxClock);
+
+ R_SUCCEED();
+}
+
+Result MemFreqDvbTable(u32* ptr) {
+ emc_dvb_dvfs_table_t* default_end = reinterpret_cast(ptr);
+ emc_dvb_dvfs_table_t* new_start = default_end + 1;
+
+ // Validate existing table
+ void* mem_dvb_table_head = reinterpret_cast(new_start) - sizeof(EmcDvbTableDefault);
+ bool validated = std::memcmp(mem_dvb_table_head, EmcDvbTableDefault, sizeof(EmcDvbTableDefault)) == 0;
+ R_UNLESS(validated, ldr::ResultInvalidDvbTable());
+
+ if (C.marikoEmcMaxClock <= MemClkOSLimit)
+ R_SKIP();
+
+ if (C.marikoEmcMaxClock <= 1862400) {
+ std::memcpy(new_start, default_end, sizeof(emc_dvb_dvfs_table_t));
+ } else {
+ emc_dvb_dvfs_table_t oc_table = { 2131200, { 700, 675, 650, } };
+ std::memcpy(new_start, &oc_table, sizeof(emc_dvb_dvfs_table_t));
+ }
+ new_start->freq = C.marikoEmcMaxClock;
+
+ R_SUCCEED();
+}
+
+Result MemFreqMax(u32* ptr) {
+ if (C.marikoEmcMaxClock <= MemClkOSLimit)
+ R_SKIP();
+
+ PatchOffset(ptr, C.marikoEmcMaxClock);
+ R_SUCCEED();
+}
+
+void Patch(uintptr_t mapped_nso, size_t nso_size) {
+ PatcherEntry patches[] = {
+ { "CPU Freq Vdd", &CpuFreqVdd, 1, nullptr, CpuClkOSLimit },
+ { "CPU Freq Table", &CpuFreqCvbTable, 1, nullptr, CpuClkOfficial },
+ { "CPU Volt Limit", &CpuVoltRange, 13, nullptr, CpuVoltOfficial },
+ { "GPU Freq Table", &GpuFreqCvbTable, 1, nullptr, GpuClkOfficial },
+ { "GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn },
+ { "GPU Freq PLL", &GpuFreqPllLimit, 1, nullptr, GpuClkPllLimit },
+ { "MEM Freq Mtc", &MemFreqMtcTable, 0, nullptr, MemClkOSLimit },
+ { "MEM Freq Dvb", &MemFreqDvbTable, 1, nullptr, MemClkOSLimit },
+ { "MEM Freq Max", &MemFreqMax, 0, nullptr, MemClkOSLimit },
+ { "MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, MemClkPllmLimit },
+ };
+
+ for (uintptr_t ptr = mapped_nso;
+ ptr <= mapped_nso + nso_size - sizeof(MarikoMtcTable);
+ ptr += sizeof(u32))
+ {
+ u32* ptr32 = reinterpret_cast(ptr);
+ for (auto& entry : patches) {
+ if (R_SUCCEEDED(entry.SearchAndApply(ptr32)))
+ break;
+ }
+ }
+
+ for (auto& entry : patches) {
+ LOGGING("%s Count: %zu", entry.description, entry.patched_count);
+ if (R_FAILED(entry.CheckResult()))
+ CRASH(entry.description);
+ }
+}
+
+}
\ No newline at end of file
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp
new file mode 100644
index 00000000..032a71b9
--- /dev/null
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) Switch-OC-Suite
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include "pcv.hpp"
+
+namespace ams::ldr::oc::pcv::mariko {
+
+constexpr cpu_freq_cvb_table_t CpuCvbTableDefault[] = {
+ // CPUB01_CVB_TABLE
+ { 204000, { 721589, -12695, 27 }, {} },
+ { 306000, { 747134, -14195, 27 }, {} },
+ { 408000, { 776324, -15705, 27 }, {} },
+ { 510000, { 809160, -17205, 27 }, {} },
+ { 612000, { 845641, -18715, 27 }, {} },
+ { 714000, { 885768, -20215, 27 }, {} },
+ { 816000, { 929540, -21725, 27 }, {} },
+ { 918000, { 976958, -23225, 27 }, {} },
+ { 1020000, { 1028021, -24725, 27 }, { 1120000 } },
+ { 1122000, { 1082730, -26235, 27 }, { 1120000 } },
+ { 1224000, { 1141084, -27735, 27 }, { 1120000 } },
+ { 1326000, { 1203084, -29245, 27 }, { 1120000 } },
+ { 1428000, { 1268729, -30745, 27 }, { 1120000 } },
+ { 1581000, { 1374032, -33005, 27 }, { 1120000 } },
+ { 1683000, { 1448791, -34505, 27 }, { 1120000 } },
+ { 1785000, { 1527196, -36015, 27 }, { 1120000 } },
+ { 1887000, { 1609246, -37515, 27 }, { 1120000 } },
+ { 1963500, { 1675751, -38635, 27 }, { 1120000 } },
+};
+
+constexpr cpu_freq_cvb_table_t CpuCvbTableAppend[] = {
+ { 2091000, { 1716501, -39395, 27 }, { 1120000 } },
+ { 2193000, { 1775132, -40505, 27 }, { 1120000 } },
+ { 2295000, { 1866287, -42005, 27 }, { 1120000 } },
+ // 2397000 kHz is not listed in l4t
+ { 2397000, { 1961107, -43506, 27 }, { 1120000 } },
+};
+
+constexpr u32 CpuMinVolts[] = { 800, 637, 620, 610 };
+
+constexpr u32 CpuClkOfficial = 1963'500;
+constexpr u32 CpuVoltOfficial = 1120;
+
+constexpr gpu_cvb_pll_table_t GpuCvbTableDefault[] = {
+ // GPUB01_NA_CVB_TABLE
+ { 76800, {}, { 610000, } },
+ { 153600, {}, { 610000, } },
+ { 230400, {}, { 610000, } },
+ { 307200, {}, { 610000, } },
+ { 384000, {}, { 610000, } },
+ { 460800, {}, { 610000, } },
+ { 537600, {}, { 801688, -10900, -163, 298, -10599, 162 } },
+ { 614400, {}, { 824214, -5743, -452, 238, -6325, 81 } },
+ { 691200, {}, { 848830, -3903, -552, 119, -4030, -2 } },
+ { 768000, {}, { 891575, -4409, -584, 0, -2849, 39 } },
+ { 844800, {}, { 940071, -5367, -602, -60, -63, -93 } },
+ { 921600, {}, { 986765, -6637, -614, -179, 1905, -13 } },
+ { 998400, {}, { 1098475, -13529, -497, -179, 3626, 9 } },
+ { 1075200, {}, { 1163644, -12688, -648, 0, 1077, 40 } },
+ { 1152000, {}, { 1204812, -9908, -830, 0, 1469, 110 } },
+ { 1228800, {}, { 1277303, -11675, -859, 0, 3722, 313 } },
+ { 1267200, {}, { 1335531, -12567, -867, 0, 3681, 559 } },
+};
+
+constexpr gpu_cvb_pll_table_t GpuCvbTableAppend[] = {
+ // 1305600 kHz is not listed in l4t
+ { 1305600, {}, { 1374130, -13725, -859, 0, 4442, 576 } },
+};
+
+constexpr u32 GpuClkOfficial = 1267'200;
+constexpr u32 GpuClkPllLimit = 1300'000'000;
+
+/* GPU Max Clock asm Pattern:
+ *
+ * MOV W11, #0x1000 MOV (wide immediate) 0x1000 0xB (11)
+ * sf | opc | | hw | imm16 | Rd
+ * #31 |30 29|28 27 26 25 24 23|22 21|20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 |4 3 2 1 0
+ * 0 | 1 0 | 1 0 0 1 0 1| 0 0| 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 |0 1 0 1 1
+ *
+ * MOVK W11, #0xE, LSL#16 16 0xE 0xB (11)
+ * sf | opc | | hw | imm16 | Rd
+ * #31 |30 29|28 27 26 25 24 23|22 21|20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 |4 3 2 1 0
+ * 0 | 1 1 | 1 0 0 1 0 1| 0 1| 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 |0 1 0 1 1
+ */
+inline constexpr u32 asm_pattern[] = { 0x52820000, 0x72A001C0 };
+inline auto asm_compare_no_rd = [](u32 ins1, u32 ins2) { return ((ins1 ^ ins2) >> 5) == 0; };
+inline auto asm_get_rd = [](u32 ins) { return ins & ((1 << 5) - 1); };
+inline auto asm_set_rd = [](u32 ins, u8 rd) { return (ins & 0xFFFFFFE0) | (rd & 0x1F); };
+inline auto asm_set_imm16 = [](u32 ins, u16 imm) { return (ins & 0xFFE0001F) | ((imm & 0xFFFF) << 5); };
+
+inline bool GpuMaxClockPatternFn(u32* ptr32) {
+ return asm_compare_no_rd(*ptr32, asm_pattern[0]);
+}
+
+constexpr emc_dvb_dvfs_table_t EmcDvbTableDefault[] = {
+ { 204000, { 637, 637, 637, } },
+ { 408000, { 637, 637, 637, } },
+ { 800000, { 637, 637, 637, } },
+ { 1065600, { 637, 637, 637, } },
+ { 1331200, { 650, 637, 637, } },
+ { 1600000, { 675, 650, 637, } },
+};
+
+constexpr emc_dvb_dvfs_table_t EmcDvbTableReplaced[] = {
+ { 1862400, { 675, 650, 637, } },
+ { 2133000, { 700, 675, 650, } },
+};
+
+constexpr u32 MemClkOSAlt = 1331'200;
+constexpr u32 MemClkPllmLimit = 2133'000'000;
+
+constexpr u32 MTC_TABLE_REV = 3;
+
+void Patch(uintptr_t mapped_nso, size_t nso_size);
+
+}
\ No newline at end of file
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/ptm/ptm.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/ptm/ptm.cpp
new file mode 100644
index 00000000..7429474a
--- /dev/null
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/ptm/ptm.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) Switch-OC-Suite
+ *
+ * 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 .
+ */
+
+#include "ptm.hpp"
+
+namespace ams::ldr::oc::ptm {
+
+Result CpuPtmBoost(perf_conf_entry* entry) {
+ if (!C.marikoCpuBoostClock)
+ R_SUCCEED();
+
+ u32 cpuPtmBoostNew = C.marikoCpuBoostClock * 1000;
+
+ PatchOffset(&(entry->cpu_freq_1), cpuPtmBoostNew);
+ PatchOffset(&(entry->cpu_freq_2), cpuPtmBoostNew);
+
+ R_SUCCEED();
+}
+
+Result MemPtmMax(perf_conf_entry* entry) {
+ if (!C.marikoEmcMaxClock)
+ R_SUCCEED();
+
+ u32 memPtmMax = C.marikoEmcMaxClock * 1000;
+
+ PatchOffset(&(entry->emc_freq_1), memPtmMax);
+ PatchOffset(&(entry->emc_freq_2), memPtmMax);
+
+ R_SUCCEED();
+}
+
+Result MemPtmAlt(perf_conf_entry* entry) {
+ PatchOffset(&(entry->emc_freq_1), memPtmLimit);
+ PatchOffset(&(entry->emc_freq_2), memPtmLimit);
+
+ R_SUCCEED();
+}
+
+bool PtmEntryIsValid(perf_conf_entry* entry) {
+ return (entry->cpu_freq_1 == entry->cpu_freq_2 &&
+ entry->gpu_freq_1 == entry->gpu_freq_2 &&
+ entry->emc_freq_1 == entry->emc_freq_2);
+}
+
+bool PtmTablePatternFn(u32* ptr) {
+ perf_conf_entry* entry = reinterpret_cast(ptr);
+ if (!PtmEntryIsValid(entry))
+ return false;
+
+ return entry->cpu_freq_1 == cpuPtmDefault;
+}
+
+void Patch(uintptr_t mapped_nso, size_t nso_size) {
+ #ifdef ATMOSPHERE_IS_STRATOSPHERE
+ bool isMariko = (spl::GetSocType() == spl::SocType_Mariko);
+ if (!isMariko)
+ return;
+ #endif
+
+ perf_conf_entry* confTable = nullptr;
+ for (uintptr_t ptr = mapped_nso;
+ ptr <= mapped_nso + nso_size - sizeof(perf_conf_entry) * entryCnt;
+ ptr += sizeof(u32))
+ {
+ u32* ptr32 = reinterpret_cast(ptr);
+ if (PtmTablePatternFn(ptr32)) {
+ confTable = reinterpret_cast(ptr);
+ break;
+ }
+ }
+
+ if (!confTable) {
+ CRASH("confTable not found!");
+ }
+
+ PatcherEntry patches[] = {
+ { "CPU Ptm Boost", &CpuPtmBoost, 2, },
+ { "MEM Ptm Max", &MemPtmMax, 9, },
+ { "MEM Ptm Alt", &MemPtmAlt, 7, },
+ };
+
+ for (u32 i = 0; i < entryCnt; i++) {
+ perf_conf_entry* entry = confTable + i;
+
+ if (!PtmEntryIsValid(entry)) {
+ LOGGING("@%p", &entry);
+ CRASH("Invalid ptm confTable entry");
+ }
+
+ switch (entry->cpu_freq_1) {
+ case cpuPtmBoost:
+ patches[0].Apply(entry);
+ break;
+ case cpuPtmDefault:
+ case cpuPtmDevOC:
+ break;
+ default:
+ LOGGING("%u (0x%08x) @%p", entry->cpu_freq_1, entry->conf_id, &(entry->cpu_freq_1));
+ CRASH("Unknown CPU Freq");
+ }
+
+ switch (entry->emc_freq_1) {
+ case memPtmLimit:
+ patches[1].Apply(entry);
+ break;
+ case memPtmAlt:
+ case memPtmClamp:
+ patches[2].Apply(entry);
+ break;
+ default:
+ LOGGING("%u (0x%08x) @%p", entry->emc_freq_1, entry->conf_id, &(entry->emc_freq_2));
+ CRASH("Unknown MEM Freq");
+ }
+ }
+
+ for (auto& patch : patches) {
+ LOGGING("%s Count: %zu", patch.description, patch.patched_count);
+ if (R_FAILED(patch.CheckResult()))
+ CRASH(patch.description);
+ }
+}
+
+}
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/ptm/ptm.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/ptm/ptm.hpp
new file mode 100644
index 00000000..38d6fb8d
--- /dev/null
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/ptm/ptm.hpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) Switch-OC-Suite
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include "../oc_suite_common.hpp"
+
+namespace ams::ldr::oc::ptm {
+
+typedef struct {
+ u32 conf_id;
+ u32 cpu_freq_1; // min-max pair?
+ u32 cpu_freq_2;
+ u32 gpu_freq_1;
+ u32 gpu_freq_2;
+ u32 emc_freq_1;
+ u32 emc_freq_2;
+ u32 padding;
+} perf_conf_entry;
+
+constexpr u32 entryCnt = 16;
+constexpr u32 cpuPtmDefault = 1020'000'000;
+constexpr u32 cpuPtmDevOC = 1224'000'000;
+constexpr u32 cpuPtmBoost = 1785'000'000;
+
+constexpr u32 memPtmLimit = 1600'000'000;
+constexpr u32 memPtmAlt = 1331'200'000;
+constexpr u32 memPtmClamp = 1065'600'000;
+
+void Patch(uintptr_t mapped_nso, size_t nso_size);
+
+}
\ No newline at end of file
diff --git a/Source/Atmosphere/stratosphere/loader/source/patch.py b/Source/Atmosphere/stratosphere/loader/source/patch.py
new file mode 100755
index 00000000..0cf2b7dc
--- /dev/null
+++ b/Source/Atmosphere/stratosphere/loader/source/patch.py
@@ -0,0 +1,74 @@
+#!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__)
+
+ldr_main = os.path.join(dir_path, "ldr_main.cpp")
+file_replace_str(ldr_main, [("constinit u8 g_heap_memory[16_KB];", "constinit u8 g_heap_memory[16_KB + 5_KB];")])
+
+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_suite.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. */""")])
diff --git a/Source/loaderConfigurator/dist/main.js b/Source/loaderConfigurator/dist/main.js
index 5272b623..321c1b52 100644
--- a/Source/loaderConfigurator/dist/main.js
+++ b/Source/loaderConfigurator/dist/main.js
@@ -9,7 +9,7 @@ function FindMagicOffset(buffer) {
}
throw new Error("Invalid loader.kip file");
}
-function CustEntry(name, size, desc, defval, minmax = [0, 1000000], step = 1, extra_validator = null) {
+function CustEntry(name, size, desc, defval, minmax = [0, 1000000], step = 1, extra_validator) {
this.name = name;
this.size = size;
this.desc = desc;
@@ -25,35 +25,32 @@ function InitCustTable() {
let cust = [
new CustEntry("mtcConf", 2, "DRAM Timing\
0: AUTO_ADJ_MARIKO_SAFE: Auto adjust timings for LPDDR4 ≤3733 Mbps specs, 8Gb density.\
- 1: AUTO_ADJ_MARIKO_4266: Auto adjust timings for LPDDR4X 4266 Mbps specs, 8Gb density.\
- 2: ENTIRE_TABLE_ERISTA: Not implemented.\
- 3: ENTIRE_TABLE_MARIKO: Not implemented.", 0, [0, 3]),
+ 1: AUTO_ADJ_MARIKO_4266: Auto adjust timings for LPDDR4X 4266 Mbps specs, 8Gb density.", 0, [0, 3]),
new CustEntry("marikoCpuMaxClock", 4, "Mariko CPU Max Clock in kHz\
System default: 1785000\
- ≥ 2193000 will enable overvolting (> 1120 mV)", 2397000, [1785000, 3000000], 100, (x) => { return (x % 100) == 0; }),
+ ≥ 2397000 will enable overvolting (> 1120 mV)", 2397000, [1785000, 3000000], 100, (x) => (x % 100) == 0),
new CustEntry("marikoCpuBoostClock", 4, "Mariko CPU Boost Clock in kHz\
System default: 1785000\
- Must not be higher than marikoCpuMaxClock", 1785000, [1785000, 3000000], 100, (x) => { return (x % 100) == 0; }),
+ Must not be higher than marikoCpuMaxClock", 1785000, [1785000, 3000000], 100, (x) => (x % 100) == 0),
new CustEntry("marikoCpuMaxVolt", 4, "Mariko CPU Max Voltage in mV\
System default: 1120\
- Acceptable range: 1100 ≤ x ≤ 1300", 1220, [1100, 1300]),
+ Acceptable range: 1100 ≤ x ≤ 1300", 1235, [1100, 1300]),
new CustEntry("marikoGpuMaxClock", 4, "Mariko GPU Max Clock in kHz\
System default: 921600\
- Tegra X1+ official maximum: 1267200", 1305600, [768000, 1536000], 100, (x) => { return (x % 100) == 0; }),
+ Tegra X1+ official maximum: 1267200", 1305600, [768000, 1536000], 100, (x) => (x % 100) == 0),
new CustEntry("marikoEmcMaxClock", 4, "Mariko RAM Max Clock in kHz\
Values should be > 1600000, and divided evenly by 3200.\
- WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM", 1996800, [1612800, 2400000], 3200, (x) => { return (x % 3200) == 0; }),
+ WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM", 1996800, [1612800, 2400000], 3200, (x) => (x % 3200) == 0),
new CustEntry("eristaCpuOCEnable", 4, "Erista CPU Enable Overclock\
- Not usable unless CPU cvb table is filled in", 0, [0, 1]),
+ Not tested", 1, [0, 1]),
new CustEntry("eristaCpuMaxVolt", 4, "Erista CPU Max Voltage in mV\
- Acceptable range: 1100 ≤ x ≤ 1400\
- Not enabled by default", 0, [0, 1400], 100, (x) => { return x >= 1100; }),
+ Acceptable range: 1100 ≤ x ≤ 1400", 1257, [0, 1400], 100, (x) => x >= 1100),
new CustEntry("eristaEmcMaxClock", 4, "Erista RAM Max Clock in kHz\
Values should be > 1600000, and divided evenly by 3200.\
- WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM", 1862400, [1600000, 2400000], 3200, (x) => { return (x % 3200) == 0; }),
+ WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM", 1862400, [1600000, 2400000], 3200, (x) => (x % 3200) == 0),
new CustEntry("eristaEmcVolt", 4, "Erista RAM Voltage in uV\
Acceptable range: 1100000 ≤ x ≤ 1250000, and it should be divided evenly by 12500.\
- Not enabled by default", 0, [0, 1250000], 12500, (x) => { return (x % 12500) == 0 && x >= 1100000; }),
+ Not enabled by default", 0, [0, 1250000], 12500, (x) => (x % 12500) == 0 && x >= 1100000),
];
return cust;
}
@@ -160,7 +157,7 @@ function UpdateHTMLForm(cust) {
tip.setAttribute("for", id);
div.appendChild(tip);
}
- form.appendChild(div);
+ form === null || form === void 0 ? void 0 : form.appendChild(div);
}
input.value = dict[i.name].value;
}
@@ -222,8 +219,8 @@ fileInput.addEventListener('change', (event) => {
reader.readAsArrayBuffer(event.target.files[0]);
reader.onloadend = (progEvent) => {
if (progEvent.target.readyState === FileReader.DONE) {
- buffer = progEvent.target.result;
try {
+ buffer = progEvent.target.result;
let offset = FindMagicOffset(buffer);
ParseCust(offset, buffer);
}
diff --git a/Source/loaderConfigurator/src/main.ts b/Source/loaderConfigurator/src/main.ts
index b2cb8271..0333ec8e 100644
--- a/Source/loaderConfigurator/src/main.ts
+++ b/Source/loaderConfigurator/src/main.ts
@@ -1,5 +1,5 @@
const CUST_REV = 2;
-var buffer;
+var buffer: string | ArrayBuffer;
function FindMagicOffset(buffer) {
let view = new DataView(buffer);
@@ -11,7 +11,7 @@ function FindMagicOffset(buffer) {
throw new Error("Invalid loader.kip file");
}
-function CustEntry(name, size, desc, defval, minmax = [0, 1_000_000], step = 1, extra_validator = null) {
+function CustEntry(name: string, size: number, desc: string, defval: number, minmax = [0, 1_000_000], step = 1, extra_validator?) {
this.name = name;
this.size = size;
this.desc = desc;
@@ -31,9 +31,7 @@ function InitCustTable() {
2,
"DRAM Timing\
0: AUTO_ADJ_MARIKO_SAFE: Auto adjust timings for LPDDR4 ≤3733 Mbps specs, 8Gb density.\
- 1: AUTO_ADJ_MARIKO_4266: Auto adjust timings for LPDDR4X 4266 Mbps specs, 8Gb density.\
- 2: ENTIRE_TABLE_ERISTA: Not implemented.\
- 3: ENTIRE_TABLE_MARIKO: Not implemented.",
+ 1: AUTO_ADJ_MARIKO_4266: Auto adjust timings for LPDDR4X 4266 Mbps specs, 8Gb density.",
0,
[0, 3],
),
@@ -42,11 +40,11 @@ function InitCustTable() {
4,
"Mariko CPU Max Clock in kHz\
System default: 1785000\
- ≥ 2193000 will enable overvolting (> 1120 mV)",
+ ≥ 2397000 will enable overvolting (> 1120 mV)",
2397_000,
[1785_000, 3000_000],
100,
- (x) => { return (x % 100) == 0; }
+ (x: number) => (x % 100) == 0
),
new CustEntry(
"marikoCpuBoostClock",
@@ -57,7 +55,7 @@ function InitCustTable() {
1785_000,
[1785_000, 3000_000],
100,
- (x) => { return (x % 100) == 0; }
+ (x: number) => (x % 100) == 0
),
new CustEntry(
"marikoCpuMaxVolt",
@@ -65,7 +63,7 @@ function InitCustTable() {
"Mariko CPU Max Voltage in mV\
System default: 1120\
Acceptable range: 1100 ≤ x ≤ 1300",
- 1220,
+ 1235,
[1100, 1300],
),
new CustEntry(
@@ -77,7 +75,7 @@ function InitCustTable() {
1305_600,
[768_000, 1536_000],
100,
- (x) => { return (x % 100) == 0; }
+ (x: number) => (x % 100) == 0
),
new CustEntry(
"marikoEmcMaxClock",
@@ -88,26 +86,25 @@ function InitCustTable() {
1996_800,
[1612_800, 2400_000],
3200,
- (x) => { return (x % 3200) == 0; }
+ (x: number) => (x % 3200) == 0
),
new CustEntry(
"eristaCpuOCEnable",
4,
"Erista CPU Enable Overclock\
- Not usable unless CPU cvb table is filled in",
- 0,
+ Not tested",
+ 1,
[0, 1]
),
new CustEntry(
"eristaCpuMaxVolt",
4,
"Erista CPU Max Voltage in mV\
- Acceptable range: 1100 ≤ x ≤ 1400\
- Not enabled by default",
- 0,
+ Acceptable range: 1100 ≤ x ≤ 1400",
+ 1257,
[0, 1400],
100,
- (x) => { return x >= 1100; }
+ (x: number) => x >= 1100
),
new CustEntry(
"eristaEmcMaxClock",
@@ -118,7 +115,7 @@ function InitCustTable() {
1862_400,
[1600_000, 2400_000],
3200,
- (x) => { return (x % 3200) == 0; }
+ (x: number) => (x % 3200) == 0
),
new CustEntry(
"eristaEmcVolt",
@@ -129,7 +126,7 @@ function InitCustTable() {
0,
[0, 1250_000],
12500,
- (x) => { return (x % 12500) == 0 && x >= 1100_000; }
+ (x: number) => (x % 12500) == 0 && x >= 1100000
),
];
return cust;
@@ -140,18 +137,18 @@ function ValidateCust(cust) {
if (i.value == 0)
continue;
if (i.value < i.min || i.value > i.max) {
- document.getElementById(i.name).focus();
+ document.getElementById(i.name)!.focus();
throw new Error(`Expected range: ${i.min} ≤ ${i.name} ≤ ${i.max}, got ${i.value}`);
}
if (i.validator && !i.validator(i.value)) {
- document.getElementById(i.name).focus();
+ document.getElementById(i.name)!.focus();
throw new Error(`Invalid value: ${i.value}(${i.name})\nValidator: ${i.validator}`);
}
}
}
function SaveCust(cust, buffer) {
- let dict = Object.assign({}, ...cust.map((x) => ({[x.name]: x})));
+ let dict = Object.assign({}, ...cust.map((x: { name: any; }) => ({[x.name]: x})));
let view = new DataView(buffer);
let storage = {};
for (let i of cust) {
@@ -166,7 +163,7 @@ function SaveCust(cust, buffer) {
view.setUint32(i.offset, i.value, true);
break;
default:
- document.getElementById(i.name).focus();
+ document.getElementById(i.name)!.focus();
throw new Error("Unknown size at " + i);
}
}
@@ -195,7 +192,7 @@ function LastSaved() {
function LoadLastSaved() {
if (LastSaved()) {
let storage = localStorage.getItem("last_saved");
- let sObj = JSON.parse(storage);
+ let sObj = JSON.parse(storage!);
for (let key in sObj) {
if (key == "custRev") {
continue;
@@ -205,16 +202,16 @@ function LoadLastSaved() {
}
}
-function LoadDefault(cust) {
- let dict = Object.assign({}, ...cust.map((x) => ({[x.name]: x})));
+function LoadDefault(cust: any[]) {
+ let dict = Object.assign({}, ...cust.map((x: { name: any; }) => ({[x.name]: x})));
for (let i of cust) {
let id = i.name;
(document.getElementById(id) as HTMLInputElement).value = i.defval;
}
}
-function UpdateHTMLForm(cust) {
- let dict = Object.assign({}, ...cust.map((x) => ({[x.name]: x})));
+function UpdateHTMLForm(cust: any[]) {
+ let dict = Object.assign({}, ...cust.map((x: { name: any; }) => ({[x.name]: x})));
let form = document.getElementById("form");
for (let i of cust) {
let id = i.name;
@@ -245,12 +242,12 @@ function UpdateHTMLForm(cust) {
div.appendChild(tip);
}
- form.appendChild(div);
+ form?.appendChild(div);
}
input.value = dict[i.name].value;
}
- let btn = document.getElementById("load");
+ let btn = document.getElementById("load")!;
btn.classList.remove("hide");
if (LastSaved()) {
btn.innerHTML = "Load Last Saved";
@@ -263,7 +260,7 @@ function UpdateHTMLForm(cust) {
});
}
- btn = document.getElementById("save");
+ btn = document.getElementById("save")!;
btn.classList.remove("hide");
btn.addEventListener('click', () => {
try {
@@ -294,7 +291,7 @@ function ParseCust(magicOffset, buffer) {
i.value = view.getUint32(offset, true);
break;
default:
- document.getElementById(i.name).focus();
+ document.getElementById(i.name)!.focus();
throw new Error("Unknown size at " + i);
}
offset += i.size;
@@ -306,10 +303,10 @@ function ParseCust(magicOffset, buffer) {
const fileInput = document.getElementById("file") as HTMLInputElement;
fileInput.addEventListener('change', (event) => {
let reader = new FileReader();
- reader.readAsArrayBuffer((event.target as HTMLInputElement).files[0]);
+ reader.readAsArrayBuffer((event.target as HTMLInputElement).files![0]);
reader.onloadend = (progEvent) => {
- if (progEvent.target.readyState === FileReader.DONE) {
- buffer = progEvent.target.result;
+ if (progEvent.target!.readyState === FileReader.DONE) {
+ buffer = progEvent.target!.result!;
try {
let offset = FindMagicOffset(buffer);
ParseCust(offset, buffer);