- oc_loader: major refactoring
This commit is contained in:
@@ -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.
|
||||
<details>
|
||||
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`
|
||||
</details>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "ldr_capabilities.hpp"
|
||||
#include "ldr_content_management.hpp"
|
||||
#include "ldr_development_manager.hpp"
|
||||
#include "ldr_launch_record.hpp"
|
||||
#include "ldr_meta.hpp"
|
||||
#include "ldr_patcher.hpp"
|
||||
#include "ldr_process_creation.hpp"
|
||||
#include "ldr_ro_manager.hpp"
|
||||
#include "oc/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<const util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
bool IsApplet(const Meta *meta) {
|
||||
return (MakeProgramInfoFlag(static_cast<const util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32)) & ProgramInfoFlag_ApplicationTypeMask) == ProgramInfoFlag_Applet;
|
||||
}
|
||||
|
||||
bool IsApplication(const Meta *meta) {
|
||||
return (MakeProgramInfoFlag(static_cast<const util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32)) & ProgramInfoFlag_ApplicationTypeMask) == ProgramInfoFlag_Application;
|
||||
}
|
||||
|
||||
Npdm::AddressSpaceType GetAddressSpaceType(const Meta *meta) {
|
||||
return static_cast<Npdm::AddressSpaceType>((meta->npdm->flags & Npdm::MetaFlag_AddressSpaceTypeMask) >> Npdm::MetaFlag_AddressSpaceTypeShift);
|
||||
}
|
||||
|
||||
Acid::PoolPartition GetPoolPartition(const Meta *meta) {
|
||||
return static_cast<Acid::PoolPartition>((meta->acid->flags & Acid::AcidFlag_PoolPartitionMask) >> Acid::AcidFlag_PoolPartitionShift);
|
||||
}
|
||||
|
||||
Result LoadAutoLoadHeaders(NsoHeader *nso_headers, bool *has_nso) {
|
||||
/* Clear NSOs. */
|
||||
std::memset(nso_headers, 0, sizeof(*nso_headers) * Nso_Count);
|
||||
std::memset(has_nso, 0, sizeof(*has_nso) * Nso_Count);
|
||||
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
fs::FileHandle file;
|
||||
if (R_SUCCEEDED(fs::OpenFile(std::addressof(file), GetNsoPath(i), fs::OpenMode_Read))) {
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
/* Read NSO header. */
|
||||
size_t read_size;
|
||||
R_TRY(fs::ReadFile(std::addressof(read_size), file, 0, nso_headers + i, sizeof(*nso_headers)));
|
||||
R_UNLESS(read_size == sizeof(*nso_headers), ldr::ResultInvalidNso());
|
||||
|
||||
has_nso[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheckAutoLoad(const NsoHeader *nso_headers, const bool *has_nso) {
|
||||
/* We must always have a main. */
|
||||
R_UNLESS(has_nso[Nso_Main], ldr::ResultInvalidNso());
|
||||
|
||||
/* If we don't have an RTLD, we must only have a main. */
|
||||
if (!has_nso[Nso_Rtld]) {
|
||||
for (size_t i = Nso_Main + 1; i < Nso_Count; i++) {
|
||||
R_UNLESS(!has_nso[i], ldr::ResultInvalidNso());
|
||||
}
|
||||
}
|
||||
|
||||
/* All NSOs must have zero text offset. */
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
R_UNLESS(nso_headers[i].text_dst_offset == 0, ldr::ResultInvalidNso());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
constexpr const ncm::ProgramId UnqualifiedApprovalProgramIds[] = {
|
||||
{ 0x010003F003A34000 }, /* Pokemon: Let's Go, Pikachu! */
|
||||
{ 0x0100152000022000 }, /* Mario Kart 8 Deluxe */
|
||||
{ 0x0100165003504000 }, /* Nintendo Labo Toy-Con 04: VR Kit */
|
||||
{ 0x0100187003A36000 }, /* Pokemon: Let's Go, Eevee! */
|
||||
{ 0x01002E5008C56000 }, /* Pokemon Sword [Live Tournament] */
|
||||
{ 0x01002FF008C24000 }, /* Ring Fit Adventure */
|
||||
{ 0x010049900F546001 }, /* Super Mario 3D All-Stars: Super Mario 64 */
|
||||
{ 0x010057D00ECE4000 }, /* Nintendo Switch Online (Nintendo 64) [for Japan] */
|
||||
{ 0x01006F8002326000 }, /* Animal Crossing: New Horizons */
|
||||
{ 0x01006FB00F50E000 }, /* [???] */
|
||||
{ 0x010070300F50C000 }, /* [???] */
|
||||
{ 0x010075100E8EC000 }, /* 马力欧卡丁车8 豪华版 [Mario Kart 8 Deluxe for China] */
|
||||
{ 0x01008DB008C2C000 }, /* Pokemon Shield */
|
||||
{ 0x01009AD008C4C000 }, /* Pokemon: Let's Go, Pikachu! [Kiosk] */
|
||||
{ 0x0100A66003384000 }, /* Hulu */
|
||||
{ 0x0100ABF008968000 }, /* Pokemon Sword */
|
||||
{ 0x0100C9A00ECE6000 }, /* Nintendo Switch Online (Nintendo 64) [for America] */
|
||||
{ 0x0100ED100BA3A000 }, /* Mario Kart Live: Home Circuit */
|
||||
{ 0x0100F38011CFE000 }, /* Animal Crossing: New Horizons Island Transfer Tool */
|
||||
{ 0x0100F6B011028000 }, /* 健身环大冒险 [Ring Fit Adventure for China] */
|
||||
};
|
||||
|
||||
/* Check that the unqualified approval programs are sorted. */
|
||||
static_assert([]() -> bool {
|
||||
for (size_t i = 0; i < util::size(UnqualifiedApprovalProgramIds) - 1; ++i) {
|
||||
if (UnqualifiedApprovalProgramIds[i].value >= UnqualifiedApprovalProgramIds[i + 1].value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}());
|
||||
|
||||
bool IsUnqualifiedApprovalProgramId(ncm::ProgramId program_id) {
|
||||
/* Check if the program id is one with unqualified approval. */
|
||||
return std::binary_search(std::begin(UnqualifiedApprovalProgramIds), std::end(UnqualifiedApprovalProgramIds), program_id);
|
||||
}
|
||||
|
||||
bool IsUnqualifiedApproval(const Meta *meta) {
|
||||
/* If the meta has unqualified approval flag, it's unqualified approval. */
|
||||
if (meta->acid->flags & ldr::Acid::AcidFlag_UnqualifiedApproval) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If the unqualified approval flag is not set, the program must be an application. */
|
||||
if (!IsApplication(meta)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* The program id must be a force unqualified approval program id. */
|
||||
return IsUnqualifiedApprovalProgramId(meta->acid->program_id_min) && meta->acid->program_id_min == meta->acid->program_id_max;
|
||||
}
|
||||
|
||||
Result ValidateMeta(const Meta *meta, const ncm::ProgramLocation &loc, const fs::CodeVerificationData &code_verification_data) {
|
||||
/* Validate version. */
|
||||
R_TRY(ValidateProgramVersion(loc.program_id, meta->npdm->version));
|
||||
|
||||
/* Validate program id. */
|
||||
R_UNLESS(meta->aci->program_id >= meta->acid->program_id_min, ldr::ResultInvalidProgramId());
|
||||
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<const util::BitPack32 *>(meta->acid_kac), meta->acid->kac_size / sizeof(util::BitPack32), static_cast<const util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32)));
|
||||
|
||||
/* If we have data to validate, validate it. */
|
||||
if (meta->check_verification_data) {
|
||||
const u8 *sig = code_verification_data.signature;
|
||||
const size_t sig_size = sizeof(code_verification_data.signature);
|
||||
const u8 *mod = static_cast<u8 *>(meta->modulus);
|
||||
const size_t mod_size = crypto::Rsa2048PssSha256Verifier::ModulusSize;
|
||||
const u8 *exp = fssystem::GetAcidSignatureKeyPublicExponent();
|
||||
const size_t exp_size = fssystem::AcidSignatureKeyPublicExponentSize;
|
||||
const u8 *hsh = code_verification_data.target_hash;
|
||||
const size_t hsh_size = sizeof(code_verification_data.target_hash);
|
||||
const bool is_signature_valid = crypto::VerifyRsa2048PssSha256WithHash(sig, sig_size, mod, mod_size, exp, exp_size, hsh, hsh_size);
|
||||
|
||||
/* If the signature check fails, we need to check if this is allowable. */
|
||||
if (!is_signature_valid) {
|
||||
/* We have to enforce signature checks on prod and when we have a signature to check on dev. */
|
||||
R_UNLESS(IsDevelopmentForAcidProductionCheck(), ldr::ResultInvalidNcaSignature());
|
||||
R_UNLESS(!code_verification_data.has_data, ldr::ResultInvalidNcaSignature());
|
||||
|
||||
/* There was no signature to check on dev. Check if this is acceptable. */
|
||||
R_UNLESS(IsUnqualifiedApproval(meta), ldr::ResultInvalidNcaSignature());
|
||||
}
|
||||
}
|
||||
|
||||
/* All good. */
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetCreateProcessFlags(u32 *out, const Meta *meta, const u32 ldr_flags) {
|
||||
const u8 meta_flags = meta->npdm->flags;
|
||||
|
||||
u32 flags = 0;
|
||||
|
||||
/* Set Is64Bit. */
|
||||
if (meta_flags & Npdm::MetaFlag_Is64Bit) {
|
||||
flags |= svc::CreateProcessFlag_Is64Bit;
|
||||
}
|
||||
|
||||
/* Set AddressSpaceType. */
|
||||
switch (GetAddressSpaceType(meta)) {
|
||||
case Npdm::AddressSpaceType_32Bit:
|
||||
flags |= svc::CreateProcessFlag_AddressSpace32Bit;
|
||||
break;
|
||||
case Npdm::AddressSpaceType_64BitDeprecated:
|
||||
flags |= svc::CreateProcessFlag_AddressSpace64BitDeprecated;
|
||||
break;
|
||||
case Npdm::AddressSpaceType_32BitWithoutAlias:
|
||||
flags |= svc::CreateProcessFlag_AddressSpace32BitWithoutAlias;
|
||||
break;
|
||||
case Npdm::AddressSpaceType_64Bit:
|
||||
flags |= svc::CreateProcessFlag_AddressSpace64Bit;
|
||||
break;
|
||||
default:
|
||||
R_THROW(ldr::ResultInvalidMeta());
|
||||
}
|
||||
|
||||
/* Set Enable Debug. */
|
||||
if (ldr_flags & CreateProcessFlag_EnableDebug) {
|
||||
flags |= svc::CreateProcessFlag_EnableDebug;
|
||||
}
|
||||
|
||||
/* Set Enable ASLR. */
|
||||
if (!(ldr_flags & CreateProcessFlag_DisableAslr)) {
|
||||
flags |= svc::CreateProcessFlag_EnableAslr;
|
||||
}
|
||||
|
||||
/* Set Is Application. */
|
||||
if (IsApplication(meta)) {
|
||||
flags |= svc::CreateProcessFlag_IsApplication;
|
||||
|
||||
/* 7.0.0+: Set OptimizeMemoryAllocation if relevant. */
|
||||
if (hos::GetVersion() >= hos::Version_7_0_0) {
|
||||
if (meta_flags & Npdm::MetaFlag_OptimizeMemoryAllocation) {
|
||||
flags |= svc::CreateProcessFlag_OptimizeMemoryAllocation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 5.0.0+ Set Pool Partition. */
|
||||
if (hos::GetVersion() >= hos::Version_5_0_0) {
|
||||
/* TODO: Nintendo no longer accepts Applet when pool partition == application. Would this break hbl/anything else in the hb ecosystem? */
|
||||
/* TODO: Nintendo uses a helper bool MakeSvcPoolPartitionFlag(u32 *out, Acid::PoolPartition partition); */
|
||||
switch (GetPoolPartition(meta)) {
|
||||
case Acid::PoolPartition_Application:
|
||||
if (IsApplet(meta)) {
|
||||
flags |= svc::CreateProcessFlag_PoolPartitionApplet;
|
||||
} else {
|
||||
flags |= svc::CreateProcessFlag_PoolPartitionApplication;
|
||||
}
|
||||
break;
|
||||
case Acid::PoolPartition_Applet:
|
||||
flags |= svc::CreateProcessFlag_PoolPartitionApplet;
|
||||
break;
|
||||
case Acid::PoolPartition_System:
|
||||
flags |= svc::CreateProcessFlag_PoolPartitionSystem;
|
||||
break;
|
||||
case Acid::PoolPartition_SystemNonSecure:
|
||||
flags |= svc::CreateProcessFlag_PoolPartitionSystemNonSecure;
|
||||
break;
|
||||
default:
|
||||
R_THROW(ldr::ResultInvalidMeta());
|
||||
}
|
||||
} else if (hos::GetVersion() >= hos::Version_4_0_0) {
|
||||
/* On 4.0.0+, the corresponding bit was simply "UseSecureMemory". */
|
||||
if (meta->acid->flags & Acid::AcidFlag_DeprecatedUseSecureMemory) {
|
||||
flags |= svc::CreateProcessFlag_DeprecatedUseSecureMemory;
|
||||
}
|
||||
}
|
||||
|
||||
/* 11.0.0+/meso Set Disable DAS merge. */
|
||||
if (meta_flags & Npdm::MetaFlag_DisableDeviceAddressSpaceMerge) {
|
||||
flags |= svc::CreateProcessFlag_DisableDeviceAddressSpaceMerge;
|
||||
}
|
||||
|
||||
*out = flags;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetCreateProcessParameter(svc::CreateProcessParameter *out, const Meta *meta, u32 flags, os::NativeHandle resource_limit) {
|
||||
/* Clear output. */
|
||||
std::memset(out, 0, sizeof(*out));
|
||||
|
||||
/* Set name, version, program id, resource limit handle. */
|
||||
std::memcpy(out->name, meta->npdm->program_name, sizeof(out->name) - 1);
|
||||
out->version = meta->npdm->version;
|
||||
out->program_id = meta->aci->program_id.value;
|
||||
out->reslimit = resource_limit;
|
||||
|
||||
/* Set flags. */
|
||||
R_TRY(GetCreateProcessFlags(std::addressof(out->flags), meta, flags));
|
||||
|
||||
/* 3.0.0+ System Resource Size. */
|
||||
if (hos::GetVersion() >= hos::Version_3_0_0) {
|
||||
/* Validate size is aligned. */
|
||||
R_UNLESS(util::IsAligned(meta->npdm->system_resource_size, os::MemoryBlockUnitSize), ldr::ResultInvalidSize());
|
||||
|
||||
/* Validate system resource usage. */
|
||||
if (meta->npdm->system_resource_size) {
|
||||
/* Process must be 64-bit. */
|
||||
R_UNLESS((out->flags & svc::CreateProcessFlag_AddressSpace64Bit), ldr::ResultInvalidMeta());
|
||||
|
||||
/* Process must be application or applet. */
|
||||
R_UNLESS(IsApplication(meta) || IsApplet(meta), ldr::ResultInvalidMeta());
|
||||
|
||||
/* Size must be less than or equal to max. */
|
||||
R_UNLESS(meta->npdm->system_resource_size <= SystemResourceSizeMax, ldr::ResultInvalidMeta());
|
||||
}
|
||||
out->system_resource_num_pages = meta->npdm->system_resource_size >> 12;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
u64 GenerateSecureRandom(u64 max) {
|
||||
/* Generate a cryptographically random number. */
|
||||
u64 rand;
|
||||
crypto::GenerateCryptographicallyRandomBytes(std::addressof(rand), sizeof(rand));
|
||||
|
||||
/* Coerce into range. */
|
||||
return rand % (max + 1);
|
||||
}
|
||||
|
||||
Result DecideAddressSpaceLayout(ProcessInfo *out, svc::CreateProcessParameter *out_param, const NsoHeader *nso_headers, const bool *has_nso, const ArgumentStore::Entry *argument) {
|
||||
/* Clear output. */
|
||||
out->args_address = 0;
|
||||
out->args_size = 0;
|
||||
std::memset(out->nso_address, 0, sizeof(out->nso_address));
|
||||
std::memset(out->nso_size, 0, sizeof(out->nso_size));
|
||||
|
||||
size_t total_size = 0;
|
||||
bool argument_allocated = false;
|
||||
|
||||
/* Calculate base offsets. */
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
if (has_nso[i]) {
|
||||
out->nso_address[i] = total_size;
|
||||
const size_t text_end = nso_headers[i].text_dst_offset + nso_headers[i].text_size;
|
||||
const size_t ro_end = nso_headers[i].ro_dst_offset + nso_headers[i].ro_size;
|
||||
const size_t rw_end = nso_headers[i].rw_dst_offset + nso_headers[i].rw_size + nso_headers[i].bss_size;
|
||||
out->nso_size[i] = text_end;
|
||||
out->nso_size[i] = std::max(out->nso_size[i], ro_end);
|
||||
out->nso_size[i] = std::max(out->nso_size[i], rw_end);
|
||||
out->nso_size[i] = util::AlignUp(out->nso_size[i], os::MemoryPageSize);
|
||||
|
||||
total_size += out->nso_size[i];
|
||||
|
||||
if (!argument_allocated && argument != nullptr) {
|
||||
out->args_address = total_size;
|
||||
out->args_size = util::AlignUp(2 * sizeof(u32) + argument->argument_size * 2 + ArgumentStore::ArgumentBufferSize, os::MemoryPageSize);
|
||||
total_size += out->args_size;
|
||||
argument_allocated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate ASLR. */
|
||||
uintptr_t aslr_start = 0;
|
||||
size_t aslr_size = 0;
|
||||
if (hos::GetVersion() >= hos::Version_2_0_0) {
|
||||
switch (out_param->flags & svc::CreateProcessFlag_AddressSpaceMask) {
|
||||
case svc::CreateProcessFlag_AddressSpace32Bit:
|
||||
case svc::CreateProcessFlag_AddressSpace32BitWithoutAlias:
|
||||
aslr_start = svc::AddressSmallMap32Start;
|
||||
aslr_size = svc::AddressSmallMap32Size;
|
||||
break;
|
||||
case svc::CreateProcessFlag_AddressSpace64BitDeprecated:
|
||||
aslr_start = svc::AddressSmallMap36Start;
|
||||
aslr_size = svc::AddressSmallMap36Size;
|
||||
break;
|
||||
case svc::CreateProcessFlag_AddressSpace64Bit:
|
||||
aslr_start = svc::AddressMap39Start;
|
||||
aslr_size = svc::AddressMap39Size;
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
} else {
|
||||
/* On 1.0.0, only 2 address space types existed. */
|
||||
if (out_param->flags & svc::CreateProcessFlag_AddressSpace64BitDeprecated) {
|
||||
aslr_start = svc::AddressSmallMap36Start;
|
||||
aslr_size = svc::AddressSmallMap36Size;
|
||||
} else {
|
||||
aslr_start = svc::AddressSmallMap32Start;
|
||||
aslr_size = svc::AddressSmallMap32Size;
|
||||
}
|
||||
}
|
||||
R_UNLESS(total_size <= aslr_size, svc::ResultOutOfMemory());
|
||||
|
||||
/* Set Create Process output. */
|
||||
uintptr_t aslr_slide = 0;
|
||||
size_t free_size = (aslr_size - total_size);
|
||||
if (out_param->flags & svc::CreateProcessFlag_EnableAslr) {
|
||||
aslr_slide = GenerateSecureRandom(free_size / os::MemoryBlockUnitSize) * os::MemoryBlockUnitSize;
|
||||
}
|
||||
|
||||
/* Set out. */
|
||||
aslr_start += aslr_slide;
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
if (has_nso[i]) {
|
||||
out->nso_address[i] += aslr_start;
|
||||
}
|
||||
}
|
||||
if (out->args_address) {
|
||||
out->args_address += aslr_start;
|
||||
}
|
||||
|
||||
out_param->code_address = aslr_start;
|
||||
out_param->code_num_pages = total_size >> 12;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result LoadAutoLoadModuleSegment(fs::FileHandle file, const NsoHeader::SegmentInfo *segment, size_t file_size, const u8 *file_hash, bool is_compressed, bool check_hash, uintptr_t map_base, uintptr_t map_end) {
|
||||
/* Select read size based on compression. */
|
||||
if (!is_compressed) {
|
||||
file_size = segment->size;
|
||||
}
|
||||
|
||||
/* Validate size. */
|
||||
R_UNLESS(file_size <= segment->size, ldr::ResultInvalidNso());
|
||||
R_UNLESS(segment->size <= std::numeric_limits<s32>::max(), ldr::ResultInvalidNso());
|
||||
|
||||
/* Load data from file. */
|
||||
uintptr_t load_address = is_compressed ? map_end - file_size : map_base;
|
||||
size_t read_size;
|
||||
R_TRY(fs::ReadFile(std::addressof(read_size), file, segment->file_offset, reinterpret_cast<void *>(load_address), file_size));
|
||||
R_UNLESS(read_size == file_size, ldr::ResultInvalidNso());
|
||||
|
||||
/* Uncompress if necessary. */
|
||||
if (is_compressed) {
|
||||
bool decompressed = (util::DecompressLZ4(reinterpret_cast<void *>(map_base), segment->size, reinterpret_cast<const void *>(load_address), file_size) == static_cast<int>(segment->size));
|
||||
R_UNLESS(decompressed, ldr::ResultInvalidNso());
|
||||
}
|
||||
|
||||
/* Check hash if necessary. */
|
||||
if (check_hash) {
|
||||
u8 hash[crypto::Sha256Generator::HashSize];
|
||||
crypto::GenerateSha256(hash, sizeof(hash), reinterpret_cast<void *>(map_base), segment->size);
|
||||
|
||||
R_UNLESS(std::memcmp(hash, file_hash, sizeof(hash)) == 0, ldr::ResultInvalidNso());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result LoadAutoLoadModule(os::NativeHandle process_handle, fs::FileHandle file, const NsoHeader *nso_header, uintptr_t nso_address, size_t nso_size) {
|
||||
/* Map and read data from file. */
|
||||
{
|
||||
/* Map the process memory. */
|
||||
void *mapped_memory = nullptr;
|
||||
R_TRY(os::MapProcessMemory(std::addressof(mapped_memory), process_handle, nso_address, nso_size, GenerateSecureRandom));
|
||||
ON_SCOPE_EXIT { os::UnmapProcessMemory(mapped_memory, process_handle, nso_address, nso_size); };
|
||||
|
||||
const uintptr_t map_address = reinterpret_cast<uintptr_t>(mapped_memory);
|
||||
|
||||
/* Load NSO segments. */
|
||||
R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Text]), nso_header->text_compressed_size, nso_header->text_hash, (nso_header->flags & NsoHeader::Flag_CompressedText) != 0,
|
||||
(nso_header->flags & NsoHeader::Flag_CheckHashText) != 0, map_address + nso_header->text_dst_offset, map_address + nso_size));
|
||||
R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Ro]), nso_header->ro_compressed_size, nso_header->ro_hash, (nso_header->flags & NsoHeader::Flag_CompressedRo) != 0,
|
||||
(nso_header->flags & NsoHeader::Flag_CheckHashRo) != 0, map_address + nso_header->ro_dst_offset, map_address + nso_size));
|
||||
R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Rw]), nso_header->rw_compressed_size, nso_header->rw_hash, (nso_header->flags & NsoHeader::Flag_CompressedRw) != 0,
|
||||
(nso_header->flags & NsoHeader::Flag_CheckHashRw) != 0, map_address + nso_header->rw_dst_offset, map_address + nso_size));
|
||||
|
||||
/* Clear unused space to zero. */
|
||||
const size_t text_end = nso_header->text_dst_offset + nso_header->text_size;
|
||||
const size_t ro_end = nso_header->ro_dst_offset + nso_header->ro_size;
|
||||
const size_t rw_end = nso_header->rw_dst_offset + nso_header->rw_size;
|
||||
std::memset(reinterpret_cast<void *>(map_address + 0), 0, nso_header->text_dst_offset);
|
||||
std::memset(reinterpret_cast<void *>(map_address + text_end), 0, nso_header->ro_dst_offset - text_end);
|
||||
std::memset(reinterpret_cast<void *>(map_address + ro_end), 0, nso_header->rw_dst_offset - ro_end);
|
||||
std::memset(reinterpret_cast<void *>(map_address + rw_end), 0, nso_header->bss_size);
|
||||
|
||||
/* Apply embedded patches. */
|
||||
ApplyEmbeddedPatchesToModule(nso_header->module_id, map_address, nso_size);
|
||||
|
||||
/* Apply IPS patches. */
|
||||
LocateAndApplyIpsPatchesToModule(nso_header->module_id, map_address, nso_size);
|
||||
|
||||
/* 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<ProgramArguments *>(map_address);
|
||||
std::memset(args, 0, sizeof(*args));
|
||||
args->allocated_size = process_info->args_size;
|
||||
args->arguments_size = argument->argument_size;
|
||||
std::memcpy(args->arguments, argument->argument, argument->argument_size);
|
||||
}
|
||||
|
||||
/* Set argument region permissions. */
|
||||
/* NOTE: Nintendo uses svc::SetProcessMemoryPermission directly here. */
|
||||
R_TRY(os::SetProcessMemoryPermission(process_info->process_handle, process_info->args_address, process_info->args_size, os::MemoryPermission_ReadWrite));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CreateProcessAndLoadAutoLoadModules(ProcessInfo *out, const Meta *meta, const NsoHeader *nso_headers, const bool *has_nso, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit) {
|
||||
/* Get CreateProcessParameter. */
|
||||
svc::CreateProcessParameter param;
|
||||
R_TRY(GetCreateProcessParameter(std::addressof(param), meta, flags, resource_limit));
|
||||
|
||||
/* Decide on an NSO layout. */
|
||||
R_TRY(DecideAddressSpaceLayout(out, std::addressof(param), nso_headers, has_nso, argument));
|
||||
|
||||
/* Actually create process. */
|
||||
svc::Handle process_handle;
|
||||
R_TRY(svc::CreateProcess(std::addressof(process_handle), std::addressof(param), static_cast<const u32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(u32)));
|
||||
|
||||
/* Set the output handle, and ensure that if we fail after this point we clean it up. */
|
||||
out->process_handle = process_handle;
|
||||
ON_RESULT_FAILURE { svc::CloseHandle(process_handle); };
|
||||
|
||||
/* Load all auto load modules. */
|
||||
R_RETURN(LoadAutoLoadModules(out, nso_headers, has_nso, argument));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* 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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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<EristaMtcTable *>(const_cast<u8 *>(EmptyMtcTable)),
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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;
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "mtc_timing_table.hpp"
|
||||
|
||||
#define CUST_REV 2
|
||||
|
||||
#ifdef ATMOSPHERE_IS_STRATOSPHERE
|
||||
#include <vapours/results/results_common.hpp>
|
||||
#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);
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
#ifndef ATMOSPHERE_IS_STRATOSPHERE
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
|
||||
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<EristaMtcTable *>(const_cast<u8 *>(EmptyMtcTable)),
|
||||
};
|
||||
}
|
||||
#endif
|
||||
@@ -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,
|
||||
};
|
||||
21
Source/Atmosphere/stratosphere/loader/source/oc/oc_suite.hpp
Normal file
21
Source/Atmosphere/stratosphere/loader/source/oc/oc_suite.hpp
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "oc_suite_common.hpp"
|
||||
#include "pcv/pcv.hpp"
|
||||
#include "ptm/ptm.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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef ATMOSPHERE_IS_STRATOSPHERE
|
||||
#include <stratosphere.hpp>
|
||||
#include <vapours/results/results_common.hpp>
|
||||
#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<typename Pointer>
|
||||
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<typename T>
|
||||
inline void PatchOffset(T* offset, T value) { static_assert(sizeof(T) < sizeof(u64)); *(offset) = value; }
|
||||
}
|
||||
@@ -13,30 +13,10 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ATMOSPHERE_IS_STRATOSPHERE
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
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<uintptr_t>(erista_buf), file_size);
|
||||
ams::ldr::oc::pcv::erista::Patch(reinterpret_cast<uintptr_t>(erista_buf), file_size);
|
||||
if (save_patched) {
|
||||
char* exec_path_erista = reinterpret_cast<char *>(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<uintptr_t>(mariko_buf), file_size);
|
||||
ams::ldr::oc::pcv::mariko::Patch(reinterpret_cast<uintptr_t>(mariko_buf), file_size);
|
||||
if (save_patched) {
|
||||
char* exec_path_mariko = reinterpret_cast<char *>(malloc(exec_path_patched_len));
|
||||
strlcpy(exec_path_mariko, exec_path, exec_path_patched_len);
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef ATMOSPHERE_IS_STRATOSPHERE
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
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
|
||||
53
Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp
Normal file
53
Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pcv.hpp"
|
||||
|
||||
namespace ams::ldr::oc::pcv {
|
||||
|
||||
Result MemFreqPllmLimit(u32* ptr) {
|
||||
clk_pll_param* entry = reinterpret_cast<clk_pll_param *>(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
|
||||
}
|
||||
|
||||
}
|
||||
130
Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp
Normal file
130
Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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<typename Table>
|
||||
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<u32 *>(reinterpret_cast<size_t>(src) + offset);
|
||||
u32* des_ent = reinterpret_cast<u32 *>(reinterpret_cast<size_t>(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);
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pcv_erista.hpp"
|
||||
|
||||
namespace ams::ldr::oc::pcv::erista {
|
||||
|
||||
Result CpuFreqCvbTable(u32* ptr) {
|
||||
cpu_freq_cvb_table_t* default_end = reinterpret_cast<cpu_freq_cvb_table_t *>(ptr);
|
||||
cpu_freq_cvb_table_t* new_start = default_end + 1;
|
||||
|
||||
// Validate existing table
|
||||
void* cpu_cvb_table_head = reinterpret_cast<u8 *>(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<void *>(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<u8 *>(ptr) - offsetof(EristaMtcTable, rate_khz) - i * sizeof(EristaMtcTable);
|
||||
table_list[i] = reinterpret_cast<EristaMtcTable *>(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<void *>(table_list[i]), static_cast<void *>(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<u32> 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<u32 *>(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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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);
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pcv_mariko.hpp"
|
||||
|
||||
namespace ams::ldr::oc::pcv::mariko {
|
||||
|
||||
Result CpuFreqVdd(u32* ptr) {
|
||||
dvfs_rail* entry = reinterpret_cast<dvfs_rail *>(reinterpret_cast<u8 *>(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<cpu_freq_cvb_table_t *>(ptr);
|
||||
cpu_freq_cvb_table_t* new_start = default_end + 1;
|
||||
|
||||
// Validate existing table
|
||||
void* cpu_cvb_table_head = reinterpret_cast<u8 *>(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<void *>(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_freq_cvb_table_t *>(cpu_cvb_table_head);
|
||||
for (size_t i = 0; i < table_size; i++) {
|
||||
if (entry->cvb_pll_param.c0 == CpuVoltOfficial * 1000) {
|
||||
PatchOffset(reinterpret_cast<u32 *>(&(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<gpu_cvb_pll_table_t *>(ptr);
|
||||
gpu_cvb_pll_table_t* new_start = default_end + 1;
|
||||
|
||||
// Validate existing table
|
||||
void* gpu_cvb_table_head = reinterpret_cast<u8 *>(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<void *>(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<clk_pll_param *>(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<u8 *>(ptr) - offsetof(MarikoMtcTable, rate_khz) - i * sizeof(MarikoMtcTable);
|
||||
table_list[i] = reinterpret_cast<MarikoMtcTable *>(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<void *>(tmp), reinterpret_cast<void *>(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<void *>(table_alt), reinterpret_cast<void *>(tmp), sizeof(MarikoMtcTable));
|
||||
|
||||
delete tmp;
|
||||
|
||||
PatchOffset(ptr, C.marikoEmcMaxClock);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MemFreqDvbTable(u32* ptr) {
|
||||
emc_dvb_dvfs_table_t* default_end = reinterpret_cast<emc_dvb_dvfs_table_t *>(ptr);
|
||||
emc_dvb_dvfs_table_t* new_start = default_end + 1;
|
||||
|
||||
// Validate existing table
|
||||
void* mem_dvb_table_head = reinterpret_cast<u8 *>(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<u32> 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<u32 *>(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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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 <shift>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);
|
||||
|
||||
}
|
||||
136
Source/Atmosphere/stratosphere/loader/source/oc/ptm/ptm.cpp
Normal file
136
Source/Atmosphere/stratosphere/loader/source/oc/ptm/ptm.cpp
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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<perf_conf_entry *>(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<u32 *>(ptr);
|
||||
if (PtmTablePatternFn(ptr32)) {
|
||||
confTable = reinterpret_cast<perf_conf_entry *>(ptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!confTable) {
|
||||
CRASH("confTable not found!");
|
||||
}
|
||||
|
||||
PatcherEntry<perf_conf_entry> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
45
Source/Atmosphere/stratosphere/loader/source/oc/ptm/ptm.hpp
Normal file
45
Source/Atmosphere/stratosphere/loader/source/oc/ptm/ptm.hpp
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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);
|
||||
|
||||
}
|
||||
74
Source/Atmosphere/stratosphere/loader/source/patch.py
Executable file
74
Source/Atmosphere/stratosphere/loader/source/patch.py
Executable file
@@ -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. */""")])
|
||||
29
Source/loaderConfigurator/dist/main.js
vendored
29
Source/loaderConfigurator/dist/main.js
vendored
@@ -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, "<b>DRAM Timing</b>\
|
||||
<li><b>0</b>: AUTO_ADJ_MARIKO_SAFE: Auto adjust timings for LPDDR4 ≤3733 Mbps specs, 8Gb density.</li>\
|
||||
<li><b>1</b>: AUTO_ADJ_MARIKO_4266: Auto adjust timings for LPDDR4X 4266 Mbps specs, 8Gb density.</li>\
|
||||
<li><b>2</b>: ENTIRE_TABLE_ERISTA: Not implemented.</li>\
|
||||
<li><b>3</b>: ENTIRE_TABLE_MARIKO: Not implemented.</li>", 0, [0, 3]),
|
||||
<li><b>1</b>: AUTO_ADJ_MARIKO_4266: Auto adjust timings for LPDDR4X 4266 Mbps specs, 8Gb density.</li>", 0, [0, 3]),
|
||||
new CustEntry("marikoCpuMaxClock", 4, "<b>Mariko CPU Max Clock in kHz</b>\
|
||||
<li>System default: 1785000</li>\
|
||||
<li>≥ 2193000 will enable overvolting (> 1120 mV)</li>", 2397000, [1785000, 3000000], 100, (x) => { return (x % 100) == 0; }),
|
||||
<li>≥ 2397000 will enable overvolting (> 1120 mV)</li>", 2397000, [1785000, 3000000], 100, (x) => (x % 100) == 0),
|
||||
new CustEntry("marikoCpuBoostClock", 4, "<b>Mariko CPU Boost Clock in kHz</b>\
|
||||
<li>System default: 1785000</li>\
|
||||
<li>Must not be higher than marikoCpuMaxClock</li>", 1785000, [1785000, 3000000], 100, (x) => { return (x % 100) == 0; }),
|
||||
<li>Must not be higher than marikoCpuMaxClock</li>", 1785000, [1785000, 3000000], 100, (x) => (x % 100) == 0),
|
||||
new CustEntry("marikoCpuMaxVolt", 4, "<b>Mariko CPU Max Voltage in mV</b>\
|
||||
<li>System default: 1120</li>\
|
||||
<li>Acceptable range: 1100 ≤ x ≤ 1300</li>", 1220, [1100, 1300]),
|
||||
<li>Acceptable range: 1100 ≤ x ≤ 1300</li>", 1235, [1100, 1300]),
|
||||
new CustEntry("marikoGpuMaxClock", 4, "<b>Mariko GPU Max Clock in kHz</b>\
|
||||
<li>System default: 921600</li>\
|
||||
<li>Tegra X1+ official maximum: 1267200</li>", 1305600, [768000, 1536000], 100, (x) => { return (x % 100) == 0; }),
|
||||
<li>Tegra X1+ official maximum: 1267200</li>", 1305600, [768000, 1536000], 100, (x) => (x % 100) == 0),
|
||||
new CustEntry("marikoEmcMaxClock", 4, "<b>Mariko RAM Max Clock in kHz</b>\
|
||||
<li>Values should be > 1600000, and divided evenly by 3200.</li>\
|
||||
<li><b>WARNING:</b> RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM</li>", 1996800, [1612800, 2400000], 3200, (x) => { return (x % 3200) == 0; }),
|
||||
<li><b>WARNING:</b> RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM</li>", 1996800, [1612800, 2400000], 3200, (x) => (x % 3200) == 0),
|
||||
new CustEntry("eristaCpuOCEnable", 4, "<b>Erista CPU Enable Overclock</b>\
|
||||
<li>Not usable unless CPU cvb table is filled in</li>", 0, [0, 1]),
|
||||
<li>Not tested</li>", 1, [0, 1]),
|
||||
new CustEntry("eristaCpuMaxVolt", 4, "<b>Erista CPU Max Voltage in mV</b>\
|
||||
<li>Acceptable range: 1100 ≤ x ≤ 1400</li>\
|
||||
<li>Not enabled by default</li>", 0, [0, 1400], 100, (x) => { return x >= 1100; }),
|
||||
<li>Acceptable range: 1100 ≤ x ≤ 1400</li>", 1257, [0, 1400], 100, (x) => x >= 1100),
|
||||
new CustEntry("eristaEmcMaxClock", 4, "<b>Erista RAM Max Clock in kHz</b>\
|
||||
<li>Values should be > 1600000, and divided evenly by 3200.</li>\
|
||||
<li><b>WARNING:</b> RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM</li>", 1862400, [1600000, 2400000], 3200, (x) => { return (x % 3200) == 0; }),
|
||||
<li><b>WARNING:</b> RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM</li>", 1862400, [1600000, 2400000], 3200, (x) => (x % 3200) == 0),
|
||||
new CustEntry("eristaEmcVolt", 4, "<b>Erista RAM Voltage in uV</b>\
|
||||
<li>Acceptable range: 1100000 ≤ x ≤ 1250000, and it should be divided evenly by 12500.</li>\
|
||||
<li>Not enabled by default</li>", 0, [0, 1250000], 12500, (x) => { return (x % 12500) == 0 && x >= 1100000; }),
|
||||
<li>Not enabled by default</li>", 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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
"<b>DRAM Timing</b>\
|
||||
<li><b>0</b>: AUTO_ADJ_MARIKO_SAFE: Auto adjust timings for LPDDR4 ≤3733 Mbps specs, 8Gb density.</li>\
|
||||
<li><b>1</b>: AUTO_ADJ_MARIKO_4266: Auto adjust timings for LPDDR4X 4266 Mbps specs, 8Gb density.</li>\
|
||||
<li><b>2</b>: ENTIRE_TABLE_ERISTA: Not implemented.</li>\
|
||||
<li><b>3</b>: ENTIRE_TABLE_MARIKO: Not implemented.</li>",
|
||||
<li><b>1</b>: AUTO_ADJ_MARIKO_4266: Auto adjust timings for LPDDR4X 4266 Mbps specs, 8Gb density.</li>",
|
||||
0,
|
||||
[0, 3],
|
||||
),
|
||||
@@ -42,11 +40,11 @@ function InitCustTable() {
|
||||
4,
|
||||
"<b>Mariko CPU Max Clock in kHz</b>\
|
||||
<li>System default: 1785000</li>\
|
||||
<li>≥ 2193000 will enable overvolting (> 1120 mV)</li>",
|
||||
<li>≥ 2397000 will enable overvolting (> 1120 mV)</li>",
|
||||
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() {
|
||||
"<b>Mariko CPU Max Voltage in mV</b>\
|
||||
<li>System default: 1120</li>\
|
||||
<li>Acceptable range: 1100 ≤ x ≤ 1300</li>",
|
||||
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,
|
||||
"<b>Erista CPU Enable Overclock</b>\
|
||||
<li>Not usable unless CPU cvb table is filled in</li>",
|
||||
0,
|
||||
<li>Not tested</li>",
|
||||
1,
|
||||
[0, 1]
|
||||
),
|
||||
new CustEntry(
|
||||
"eristaCpuMaxVolt",
|
||||
4,
|
||||
"<b>Erista CPU Max Voltage in mV</b>\
|
||||
<li>Acceptable range: 1100 ≤ x ≤ 1400</li>\
|
||||
<li>Not enabled by default</li>",
|
||||
0,
|
||||
<li>Acceptable range: 1100 ≤ x ≤ 1400</li>",
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user