From 5942cfd68dc5bf0557e5f51a19be75c8dd52b718 Mon Sep 17 00:00:00 2001 From: Lightos1 <124387232+Lightos1@users.noreply.github.com> Date: Thu, 9 Apr 2026 11:32:00 +0200 Subject: [PATCH] ldr: refactor asm --- .../stratosphere/loader/source/oc/pcv/pcv.hpp | 56 ++----------------- .../loader/source/oc/pcv/pcv_asm.hpp | 47 ++++++++++++++++ .../loader/source/oc/pcv/pcv_erista.cpp | 12 +++- .../loader/source/oc/pcv/pcv_mariko.cpp | 6 +- 4 files changed, 64 insertions(+), 57 deletions(-) create mode 100644 Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp index 6b82fe10..4314b643 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp @@ -22,6 +22,7 @@ #include "../oc_common.hpp" #include "pcv_common.hpp" +#include "pcv_asm.hpp" namespace ams::ldr::hoc::pcv { @@ -103,30 +104,10 @@ namespace ams::ldr::hoc::pcv { * 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 auto AsmGetImm16 = [](u32 ins) { - return static_cast((ins >> 5) & 0xFFFF); - }; + inline constexpr u32 GpuAsmPattern[] = {0x52820000, 0x72A001C0}; inline bool GpuMaxClockPatternFn(u32 *ptr32) { - return asm_compare_no_rd(*ptr32, asm_pattern[0]); + return asm_compare_no_rd(*ptr32, GpuAsmPattern[0]); } constexpr emc_dvb_dvfs_table_t EmcDvbTableDefault[] = { @@ -192,11 +173,6 @@ namespace ams::ldr::hoc::pcv { constexpr u16 CpuMinVolts[] = { 950, 850, 825, 810 }; - inline bool CpuMaxVoltPatternFn(u32* ptr32) { - u32 val = *ptr32; - return (val == 1132 || val == 1170 || val == 1227); - } - static const u32 gpuVoltDvfsPattern[] = { 810, 1150, 1000, 100, 1000, 10, }; static_assert(sizeof(gpuVoltDvfsPattern) == (sizeof(u32) * 6), "Invalid gpuVoltDvfsPattern"); @@ -215,32 +191,10 @@ namespace ams::ldr::hoc::pcv { * #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 auto AsmGetImm16 = [](u32 ins) { - return static_cast((ins >> 5) & 0xFFFF); - }; + inline constexpr u32 GpuAsmPattern[] = { 0x52820000, 0x72A001C0 }; inline bool GpuMaxClockPatternFn(u32 *ptr32) { - return asm_compare_no_rd(*ptr32, asm_pattern[0]); + return asm_compare_no_rd(*ptr32, GpuAsmPattern[0]); }; constexpr cvb_entry_t GpuCvbTableDefault[] = { diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp new file mode 100644 index 00000000..bf6d0b1d --- /dev/null +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) Switch-OC-Suite + * + * Copyright (c) 2023 hanai3Bi + * + * Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "../oc_common.hpp" + +namespace ams::ldr::hoc::pcv { + + 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 auto AsmGetImm16 = [](u32 ins) { + return static_cast((ins >> 5) & 0xFFFF); + }; + +} diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp index 96ae3e4e..9ee2612e 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp @@ -134,7 +134,7 @@ namespace ams::ldr::hoc::pcv::erista { 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]))) + if (!(asm_compare_no_rd(ins1, GpuAsmPattern[0]) && asm_compare_no_rd(ins2, GpuAsmPattern[1]))) R_THROW(ldr::ResultInvalidGpuFreqMaxPattern()); // Both instructions should operate on the same register @@ -142,6 +142,12 @@ namespace ams::ldr::hoc::pcv::erista { if (rd != asm_get_rd(ins2)) 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.eristaGpuUV) { case 0: @@ -158,8 +164,8 @@ namespace ams::ldr::hoc::pcv::erista { break; } u32 asm_patch[2] = { - asm_set_rd(asm_set_imm16(asm_pattern[0], max_clock), rd), - asm_set_rd(asm_set_imm16(asm_pattern[1], max_clock >> 16), rd)}; + asm_set_rd(asm_set_imm16(GpuAsmPattern[0], max_clock), rd), + asm_set_rd(asm_set_imm16(GpuAsmPattern[1], max_clock >> 16), rd)}; PATCH_OFFSET(ptr32, asm_patch[0]); PATCH_OFFSET(ptr32 + 1, asm_patch[1]); 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 98b8d4b2..6e9313f5 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp @@ -268,7 +268,7 @@ namespace ams::ldr::hoc::pcv::mariko { 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]))) { + if (!(asm_compare_no_rd(ins1, GpuAsmPattern[0]) && asm_compare_no_rd(ins2, GpuAsmPattern[1]))) { R_THROW(ldr::ResultInvalidGpuFreqMaxPattern()); } @@ -301,8 +301,8 @@ namespace ams::ldr::hoc::pcv::mariko { } u32 asm_patch[2] = { - asm_set_rd(asm_set_imm16(asm_pattern[0], max_clock), rd), - asm_set_rd(asm_set_imm16(asm_pattern[1], max_clock >> 16), rd) + asm_set_rd(asm_set_imm16(GpuAsmPattern[0], max_clock), rd), + asm_set_rd(asm_set_imm16(GpuAsmPattern[1], max_clock >> 16), rd) }; PATCH_OFFSET(ptr32, asm_patch[0]);