diff --git a/Source/Atmosphere/stratosphere/loader/source/loader.json b/Source/Atmosphere/stratosphere/loader/source/loader.json new file mode 100644 index 00000000..172476b4 --- /dev/null +++ b/Source/Atmosphere/stratosphere/loader/source/loader.json @@ -0,0 +1,88 @@ +{ + "name": "Loader", + "title_id": "0x0100000000000001", + "main_thread_stack_size": "0x4000", + "main_thread_priority": 49, + "default_cpu_id": 3, + "process_category": 1, + "use_secure_memory": true, + "immortal": true, + "kernel_capabilities": [ + { + "type": "handle_table_size", + "value": 128 + }, + { + "type": "syscalls", + "value": { + "svcSetHeapSize" : "0x01", + "svcSetMemoryPermission" : "0x02", + "svcSetMemoryAttribute" : "0x03", + "svcMapMemory" : "0x04", + "svcUnmapMemory" : "0x05", + "svcQueryMemory" : "0x06", + "svcExitProcess" : "0x07", + "svcCreateThread" : "0x08", + "svcStartThread" : "0x09", + "svcExitThread" : "0x0A", + "svcSleepThread" : "0x0B", + "svcGetThreadPriority" : "0x0C", + "svcSetThreadPriority" : "0x0D", + "svcGetThreadCoreMask" : "0x0E", + "svcSetThreadCoreMask" : "0x0F", + "svcGetCurrentProcessorNumber" : "0x10", + "svcSignalEvent" : "0x11", + "svcClearEvent" : "0x12", + "svcMapSharedMemory" : "0x13", + "svcUnmapSharedMemory" : "0x14", + "svcCreateTransferMemory" : "0x15", + "svcCloseHandle" : "0x16", + "svcResetSignal" : "0x17", + "svcWaitSynchronization" : "0x18", + "svcCancelSynchronization" : "0x19", + "svcArbitrateLock" : "0x1A", + "svcArbitrateUnlock" : "0x1B", + "svcWaitProcessWideKeyAtomic" : "0x1C", + "svcSignalProcessWideKey" : "0x1D", + "svcGetSystemTick" : "0x1E", + "svcConnectToNamedPort" : "0x1F", + "svcSendSyncRequestLight" : "0x20", + "svcSendSyncRequest" : "0x21", + "svcSendSyncRequestWithUserBuffer" : "0x22", + "svcSendAsyncRequestWithUserBuffer" : "0x23", + "svcGetProcessId" : "0x24", + "svcGetThreadId" : "0x25", + "svcBreak" : "0x26", + "svcOutputDebugString" : "0x27", + "svcReturnFromException" : "0x28", + "svcGetInfo" : "0x29", + "svcWaitForAddress" : "0x34", + "svcSignalToAddress" : "0x35", + "svcSynchronizePreemptionState" : "0x36", + "svcCreateSession" : "0x40", + "svcAcceptSession" : "0x41", + "svcReplyAndReceiveLight" : "0x42", + "svcReplyAndReceive" : "0x43", + "svcReplyAndReceiveWithUserBuffer" : "0x44", + "svcCreateEvent" : "0x45", + "svcReadWriteRegister" : "0x4E", + "svcQueryIoMapping" : "0x55", + "svcSetProcessMemoryPermission" : "0x73", + "svcMapProcessMemory" : "0x74", + "svcUnmapProcessMemory" : "0x75", + "svcMapProcessCodeMemory" : "0x77", + "svcUnmapProcessCodeMemory" : "0x78", + "svcCreateProcess" : "0x79", + "svcCallSecureMonitor" : "0x7F" + } + }, { + "type": "map", + "value": { + "address": "0x7000F000", + "is_ro": false, + "size": "0x00001000", + "is_io": true + } + } + ] +} diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp index 92c19518..4d857dd8 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp @@ -46,7 +46,9 @@ volatile CustomizeTable C = { .marikoEmcMaxClock = 2133000, /* 1866MHz @ 1866tWRL is guaranteed to work on all Mariko units */ .marikoEmcVddqVolt = 600000, + .emcDvbShift = 0, +.marikoSocVmax = 0, /* 0 = stock limits. */ // Primary .t1_tRCD = 0, @@ -65,21 +67,21 @@ volatile CustomizeTable C = { /* Frequency where non low timings gets used. */ .timingEmcTbreak = DISABLED, -.low_t6_tRTW = DISABLED, -.low_t7_tWTR = DISABLED, +.low_t6_tRTW = 0, +.low_t7_tWTR = 0, .readLatency = { - DISABLED, - DISABLED, - DISABLED, - DISABLED, + /* 1333 */ 0, + /* 1600 */ 0, + /* 1866 */ 0, + /* 2133 */ 0, }, .writeLatency = { - DISABLED, - DISABLED, - DISABLED, - DISABLED, + /* 1333 */ 0, + /* 1600 */ 0, + /* 1866 */ 0, + /* 2133 */ 0, }, /* You can mix and match different latencies if needed */ diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp index 8fab8b06..354f77d6 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp @@ -93,6 +93,7 @@ typedef struct CustomizeTable { u32 marikoEmcMaxClock; u32 marikoEmcVddqVolt; u32 emcDvbShift; + u32 marikoSocVmax; // advanced config u32 t1_tRCD; u32 t2_tRP; diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp index 2205c4a3..475cec5e 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp @@ -52,6 +52,8 @@ namespace ams::ldr { R_DEFINE_ERROR_RESULT(UnsuccessfulPatcher, 1014); R_DEFINE_ERROR_RESULT(SafetyCheckFailure, 1015); R_DEFINE_ERROR_RESULT(InvalidMtcTablePattern, 1016); + R_DEFINE_ERROR_RESULT(InvalidSocVoltPattern, 1017); + R_DEFINE_ERROR_RESULT(InvalidSocVoltLimit, 1018); } namespace ams::ldr::hoc { diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp index 3b29b705..cbe5033f 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp @@ -154,6 +154,7 @@ namespace ams::ldr::hoc::pcv { { GET_MAX_OF_ARR(erista::maxEmcClocks), 1600'000, 2600'000, false, panic::Emc }, { C.marikoEmcMaxClock, 1600'000, 3500'000, false, panic::Emc }, { C.marikoEmcVddqVolt, 250'000, 700'000, false, panic::Emc }, + { C.marikoSocVmax, 1000, 1200, false, panic::Emc }, { eristaGpuDvfsMaxFreq, 768'000, 1152'000, false, panic::Gpu }, { marikoGpuDvfsMaxFreq, 768'000, 1536'000, false, panic::Gpu }, { C.marikoGpuVmax, 800, 960, false, panic::Gpu }, diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp index ac96bb19..e8f24261 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp @@ -26,6 +26,16 @@ namespace ams::ldr::hoc::pcv { constexpr u32 NopIns = 0x1f2003d5; + template + u32 *ScanAssembly(u32 *ptr, u32 scanLimit, u32 pattern, Compare comp) { + for (u32 i = 0; i < scanLimit; ++i) { + if (comp(pattern, ptr[i])) { + return ptr + i; + } + } + return nullptr; + } + inline auto asm_compare_no_rd = [](u32 ins1, u32 ins2) { return ((ins1 ^ ins2) >> 5) == 0; }; @@ -61,4 +71,75 @@ namespace ams::ldr::hoc::pcv { return ((ins1 & ImmMask) ^ (ins2 & ImmMask)) == 0; }; + /* Csel (Conditional Select) */ + /* + SF | Op | S | | RM | Cond | 0 | 0 | Rn | Rd + 31 | 30 | 29 | 28 27 26 25 24 23 | 20 19 18 17 16 | 15 14 13 12 | 11 | 10 | 9 8 7 6 5 | 4 3 2 1 0 + */ + inline auto AsmCompareCselNoReg = [](u32 ins1, u32 ins2) { + constexpr u32 ClearReg = ~(((1 << 10) - 1) | (((1 << 5) - 1) << 16)); + return ((ins1 & ClearReg) ^ (ins2 & ClearReg)) == 0; + }; + + /* Mul */ + /* + SF | Op54 | Op31 | RM | o0 | RA | RN | 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 + */ + inline auto AsmCompareMullNoReg = [](u32 ins1, u32 ins2) { + constexpr u32 ClearReg = ~(((1 << 10) - 1) | (((1 << 5) - 1) << 16)); + return ((ins1 & ClearReg) ^ (ins2 & ClearReg)) == 0; + }; + + /* Mul */ + /* MUL W11, W24, W26 */ + /* multiplies by 1000, mV -> uV */ + /* + SF | Op54 | Op31 | RM | o0 | RA | RN | 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 + */ + inline auto AsmGetMullRn = [](u32 ins) { + constexpr u32 Mask = ((1 << 5) - 1) << 5; + return (ins & Mask) >> 5; + }; + + inline auto AsmGetMullRm = [](u32 ins) { + constexpr u32 Mask = ((1 << 5) - 1) << 16; + return (ins & Mask) >> 16; + }; + + /* Subs (Shifted register) */ + /* + SF | Op | S | | Shift | 0 | RM | Imm6 | Rn | 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 + */ + inline auto AsmSubsSetRn = [](u32 ins, u8 rn) { + constexpr u32 RnMaskClear = ~(((1u << 5) - 1u) << 5); + constexpr u32 RnMaskSet = (1u << 5) - 1u; + + return (ins & RnMaskClear) | ((static_cast(rn) & RnMaskSet) << 5); + }; + + /* Subs (Immediate) */ + + /* + SF | Op | S | | Sh | Imm12 | Rn | 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 + */ + inline auto AsmSubsSetImm12 = [](u32 ins, u16 imm12) { + constexpr u32 ClearMask = ~(((1u << 12) - 1) << 10); + constexpr u32 SetImm12Mask = ( 1u << 12) - 1; + + return (ins & ClearMask) | ((imm12 & SetImm12Mask) << 10); + }; + + inline auto AsmSubsCompareNoReg = [](u32 ins1, u32 ins2) { + return ((ins1 ^ ins2) >> 10) == 0; + }; + + inline auto AsmCompareBrConNoImm19 = [](u32 ins1, u32 ins2) { + constexpr u32 ClearImm19 = ~(((1 << 19) - 1) << 5); + return (ins1 & ClearImm19) == (ins2 & ClearImm19); + }; + } diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp index 04fed544..d6f014a4 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp @@ -279,12 +279,6 @@ namespace ams::ldr::hoc::pcv::mariko { R_THROW(ldr::ResultInvalidGpuFreqMaxPattern()); } - /* Verify the limit. */ - /* TODO: Make this a little bit cleaner at some point. */ - if (AsmGetImm16(ins1) != (GpuClkOsLimit & 0xFFFF) || AsmGetImm16(ins2) != (GpuClkOsLimit >> 16)) { - R_THROW(ldr::ResultInvalidGpuFreqMaxPattern()); - } - u32 max_clock; switch (C.marikoGpuUV) { case 0: @@ -782,21 +776,43 @@ namespace ams::ldr::hoc::pcv::mariko { R_SKIP(); } + u32 max0 = 1050; + u32 max1 = 1025; + u32 max2 = 1000; s32 voltAdd = 25 * C.emcDvbShift; - #define DVB_VOLT(zero, one, two) std::min(zero + voltAdd, 1050), std::min(one + voltAdd, 1025), std::min(two + voltAdd, 1000), - DvbEntry emcDvbTableNew[] = { - { 204000, { 637, 637, 637, } }, - { 1331200, { 650, 637, 637, } }, - { 1600000, { 675, 650, 637, } }, - { 1866000, { DVB_VOLT(700, 675, 650) } }, - { 2133000, { DVB_VOLT(725, 700, 675) } }, - { 2400000, { DVB_VOLT(750, 725, 700) } }, - { 2666000, { DVB_VOLT(775, 750, 725) } }, - { 2933000, { DVB_VOLT(800, 775, 750) } }, - { 3200000, { DVB_VOLT(800, 800, 775) } }, - { 0xFFFFFFFF, { } }, + + if (C.marikoSocVmax && C.marikoSocVmax > 1000) { + max0 = C.marikoSocVmax; + max1 = C.marikoSocVmax; + max2 = C.marikoSocVmax; + } + + auto DvbVolt = [&](u32 zero, u32 one, u32 two) { + return std::array{ + std::min(zero + voltAdd, max0), + std::min(one + voltAdd, max1), + std::min(two + voltAdd, max2) + }; }; + #define DVB(v) \ + static_cast((v)[0]), \ + static_cast((v)[1]), \ + static_cast((v)[2]) + DvbEntry emcDvbTableNew[] = { + { 204000, { 637, 637, 637, } }, + { 1331200, { 650, 637, 637, } }, + { 1600000, { 675, 650, 637, } }, + { 1866000, { DVB(DvbVolt(700, 675, 650)) } }, + { 2133000, { DVB(DvbVolt(725, 700, 675)) } }, + { 2400000, { DVB(DvbVolt(750, 725, 700)) } }, + { 2666000, { DVB(DvbVolt(775, 750, 725)) } }, + { 2933000, { DVB(DvbVolt(800, 775, 750)) } }, + { 3200000, { DVB(DvbVolt(800, 800, 775)) } }, + { 0xFFFFFFFF, { } }, + }; + #undef DVB + u32 j = MtcTableCountDefault; for (u32 i = MtcTableCountDefault; i < newEmcList.size(); ++i) { if (newEmcList[i] >= emcDvbTableNew[j].freq && newEmcList[i] < emcDvbTableNew[j + 1].freq) { @@ -914,6 +930,124 @@ namespace ams::ldr::hoc::pcv::mariko { R_SUCCEED(); } + Result GetSocSpeedo(u32 &socSpeedo) { + constexpr u64 FusePhysicalAddress = 0x7000F000; + u64 virtualAddress = 0; + constexpr u64 Size = 0x1000; + + u64 outSize; + /* TODO: use svc::QueryMemoryMapping instead. */ + R_TRY(svcQueryMemoryMapping(&virtualAddress, &outSize, FusePhysicalAddress, Size)); + + constexpr u32 FuseOffset = 2048; + constexpr u32 SocSpeedoOffset = 308; + socSpeedo = *reinterpret_cast(virtualAddress + FuseOffset + SocSpeedoOffset); + + R_SUCCEED(); + } + + u32 GetSocProcessId(u32 socSpeedo) { + if (socSpeedo <= 1597) { + return 0; + } + + if (socSpeedo <= 1708) { + return 1; + } + + /* >= 1709. */ + return 2; + } + + Result SocVoltAsm(u32 *compareSpeedos) { + constexpr u32 VoltageScanLimit = 10; + /* Might actually be speedo id. */ + u32 *writeProcessId = ScanAssembly(compareSpeedos, VoltageScanLimit, SocVoltWriteProcessIdAsm, asm_compare_no_rd); + R_UNLESS(writeProcessId != nullptr, ldr::ResultInvalidSocVoltPattern()); + u8 writeProcessIdRd = asm_get_rd(*writeProcessId); + + /* This writes 1050mV. */ + u32 *writeVoltage = ScanAssembly(writeProcessId, VoltageScanLimit, SocVoltWriteVoltageAsm, asm_compare_no_rd); + R_UNLESS(writeVoltage != nullptr, ldr::ResultInvalidSocVoltPattern()); + u8 writeVoltageRd = asm_get_rd(*writeVoltage); + + /* A csel instruction is used to select the soc voltage limit register. */ + /* We care about its destination register since that is used for verification. */ + constexpr u32 VoltageSelectScanLimit = 24; + u32 *selectVoltage = ScanAssembly(writeVoltage, VoltageSelectScanLimit, SocVoltSelectRegisterAsm, AsmCompareCselNoReg); + R_UNLESS(selectVoltage != nullptr, ldr::ResultInvalidSocVoltPattern()); + /* Todo: check rm and rn? */ + u8 selectVoltageRd = asm_get_rd(*selectVoltage); + + /* rdCsel is then multiplied by 1000 to convert to uV. */ + /* This is pretty far down the function. */ + constexpr u32 MultiplierScanLimit = 200; + u32 *multiplier = ScanAssembly(selectVoltage, MultiplierScanLimit, SocVoltMultiplyVoltsAsm, AsmCompareMullNoReg); + R_UNLESS(multiplier != nullptr, ldr::ResultInvalidSocVoltPattern()); + u8 multiplierRn = AsmGetMullRn(*multiplier); + u8 multiplierRm = AsmGetMullRm(*multiplier); + /* One of the two registers has to be rdCsel. */ + R_UNLESS((multiplierRn == selectVoltageRd) || (multiplierRm == selectVoltageRd), ldr::ResultInvalidSocVoltPattern()); + u8 multiplierRd = asm_get_rd(*multiplier); + + /* Subs instruction is then used to verify against absolute limit. */ + u32 limitValidationPattern = AsmSubsSetRn(SocVoltValidateLimitAsm, multiplierRd); + u32 *limitValidation = ScanAssembly(multiplier, VoltageScanLimit, limitValidationPattern, AsmSubsCompareNoReg); + R_UNLESS(limitValidation != nullptr, ldr::ResultInvalidSocVoltPattern()); + + /* There is a b.gt instruction right after (checks for socVoltageCap < socVoltageMax). */ + u32 *branchToAbort = limitValidation + 1; + R_UNLESS(AsmCompareBrConNoImm19(*branchToAbort, SocVoltBranchToAbortAsm), ldr::ResultInvalidSocVoltPattern()); + + if (!C.marikoSocVmax || C.marikoSocVmax <= 1000) { + R_SKIP(); + } + + /* Adjust 1598 speedo minimum to ensure it always goes down process id 0 branch. */ + /* 2200 should be high enough :D */ + u32 compareSpeedosPatch = AsmSubsSetImm12(*compareSpeedos, 2200); + PATCH_OFFSET(compareSpeedos, compareSpeedosPatch); + + u32 socSpeedo = 0; + R_TRY(GetSocSpeedo(socSpeedo)); + + /* Adjust processId from 0 to [process id of switch booting this]. */ + /* We're overwriting the orr instruction entirly. */ + u32 processId = GetSocProcessId(socSpeedo); + u32 writeProcessIdPatch = asm_set_rd(asm_set_imm16(SocVoltWriteVoltageAsm, processId), writeProcessIdRd); + PATCH_OFFSET(writeProcessId, writeProcessIdPatch); + + /* Adjust voltage limit. */ + u32 voltageLimitPatch = asm_set_rd(asm_set_imm16(SocVoltWriteVoltageAsm, C.marikoSocVmax), writeVoltageRd); + PATCH_OFFSET(writeVoltage, voltageLimitPatch); + + /* Branches to an abort if limits are invalid -- we patch the branch instruction with NOP. */ + PATCH_OFFSET(branchToAbort, NopIns); + + R_SUCCEED(); + } + + Result SocVoltLimit(u32 *ptr) { + R_UNLESS(!std::memcmp(ptr - SocVoltLimitMaxDefaultIndex, socVoltLimitArray, sizeof(socVoltLimitArray)), ldr::ResultInvalidSocVoltLimit()); + if (!C.marikoSocVmax || C.marikoSocVmax <= SocVoltLimitOfficial) { + R_SKIP(); + } + + constexpr u32 Step = 25; + u32 maxVolt = C.marikoSocVmax; + if (maxVolt % Step) { + maxVolt = maxVolt / Step * Step; /* Round. */ + } + + u32 volt = SocVoltLimitOfficial; + for (u32 i = 1; i < DvfsTableEntryCount - SocVoltLimitMaxDefaultIndex && volt < maxVolt; ++i) { + volt += Step; + PATCH_OFFSET(ptr + i, volt); + } + + R_SUCCEED(); + } + void Patch(uintptr_t mapped_nso, size_t nso_size) { nsoStart = reinterpret_cast(mapped_nso); MtcGenerateFreqTables(); @@ -932,13 +1066,15 @@ namespace ams::ldr::hoc::pcv::mariko { { "GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn }, { "GPU PLL Max", &GpuFreqPllMax, 1, nullptr, GpuClkPllMax }, { "GPU PLL Limit", &GpuFreqPllLimit, 4, nullptr, GpuClkPllLimit }, - { "MEM Freq Mtc", &MemFreqMtcTable, 0, nullptr, EmcClkOSLimit }, + { "MEM Freq Mtc", &MemFreqMtcTable, 1, nullptr, EmcClkOSLimit }, { "MEM Freq Dvb", &MemFreqDvbTable, 1, nullptr, EmcClkOSLimit }, { "MEM Freq Max", &MemFreqMax, 0, nullptr, EmcClkOSLimit }, { "MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit }, { "MEM Vddq", &EmcVddqVolt, 2, nullptr, EmcVddqDefault }, { "MEM Vdd2", &MemVoltHandler, 2, nullptr, MemVdd2Default }, - { "Mem Table Asm", &MemMtcTableAsm, 0, &MemMtcGetGetTablePatternFn }, + { "MEM Table Asm", &MemMtcTableAsm, 1, &MemMtcGetGetTablePatternFn }, + { "SOC Volt Asm", &SocVoltAsm, 1, &SocVoltPatternFn }, + { "SOC Volt Limit", &SocVoltLimit, 1, nullptr, SocVoltLimitOfficial }, }; for (uintptr_t ptr = mapped_nso; ptr <= mapped_nso + nso_size - sizeof(MarikoMtcTable); ptr += sizeof(u32)) { diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp index 033d5f42..f93cadad 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp @@ -112,7 +112,7 @@ namespace ams::ldr::hoc::pcv::mariko { struct DvbEntry { u64 freq; - s32 volt[4] = {}; + u32 volt[4] = {}; }; constexpr DvbEntry EmcDvbTableDefault[] = { @@ -124,6 +124,27 @@ namespace ams::ldr::hoc::pcv::mariko { { 1600000, { 675, 650, 637, } }, }; + /* Movz */ + /* + 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 + */ + constexpr u32 SocVoltCompareSpeedoAsm = 0x7118FAFF; /* subs imm, compares to >=1598 max speedo and then goes down process id 1 route. */ + constexpr u32 SocVoltWriteProcessIdAsm = 0x2A1F03F4; /* orr, writes id 0. */ + constexpr u32 SocVoltWriteVoltageAsm = 0x52808358; /* Movz imm, writes 1050mV. */ + constexpr u32 SocVoltSelectRegisterAsm = 0x1A9A3118; /* Csel, selects the voltage -- we need the register of this. */ + constexpr u32 SocVoltMultiplyVoltsAsm = 0x1B1A7F0B; /* Mul, converts from mV -> uV */ + constexpr u32 SocVoltValidateLimitAsm = 0x6B0A017F; /* Subs, checks limits */ + constexpr u32 SocVoltBranchToAbortAsm = 0x540020AC; /* B.ge Branches to abort if limits are invalid. */ + + ALWAYS_INLINE bool SocVoltPatternFn(u32 *ptr) { + return asm_compare_no_rd(*ptr, SocVoltCompareSpeedoAsm); + } + + constexpr u32 SocVoltLimitOfficial = 1050; + constexpr u32 SocVoltLimitMaxDefaultIndex = 17; + static const u32 socVoltLimitArray[DvfsTableEntryCount] = { 637, 650, 675, 700, 725, 750, 775, 800, 825, 850, 875, 900, 925, 950, 975, 1000, 1025, 1050, }; + constexpr u32 EmcListDefault[] = { 204000, 1331200, 1600000, }; constexpr u32 EmcListSizeDefault = std::size(EmcListDefault); constexpr u32 EmcListEndDefault = EmcListSizeDefault - 1; @@ -260,8 +281,8 @@ namespace ams::ldr::hoc::pcv::mariko { /* Adrp */ /* - OP | ImmLow | ImmHigh | 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 + OP | ImmLow | | ImmHigh | 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 */ /* ADD (immediate) */