Merge branch 'main' of https://github.com/horizon-OC/horizon-OC
This commit is contained in:
@@ -813,12 +813,6 @@ namespace ams::ldr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result CreateProcessAndLoadAutoLoadModules(ProcessInfo *out, const Meta *meta, const AutoLoadModuleContext &ctx, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit) {
|
Result CreateProcessAndLoadAutoLoadModules(ProcessInfo *out, const Meta *meta, const AutoLoadModuleContext &ctx, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit) {
|
||||||
/* Append extra .bss for 64LUT */
|
|
||||||
/* TODO: REMOVE THIS. */
|
|
||||||
if (g_is_pcv && ctx.main_nso_idx >= 0) {
|
|
||||||
g_nso_headers[ctx.main_nso_idx].bss_size += static_cast<u32>(hoc::pcv::HocPcvScratchSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get CreateProcessParameter. */
|
/* Get CreateProcessParameter. */
|
||||||
svc::CreateProcessParameter param;
|
svc::CreateProcessParameter param;
|
||||||
R_TRY(GetCreateProcessParameter(std::addressof(param), meta, flags, resource_limit));
|
R_TRY(GetCreateProcessParameter(std::addressof(param), meta, flags, resource_limit));
|
||||||
|
|||||||
@@ -202,10 +202,6 @@ namespace ams::ldr::hoc::pcv {
|
|||||||
Result MemFreqPllmLimit(u32 *ptr);
|
Result MemFreqPllmLimit(u32 *ptr);
|
||||||
Result MemVoltHandler(u32 *ptr); // Used for Erista MEM Vdd2 + EMC Vddq or Mariko MEM Vdd2
|
Result MemVoltHandler(u32 *ptr); // Used for Erista MEM Vdd2 + EMC Vddq or Mariko MEM Vdd2
|
||||||
|
|
||||||
/* Extra pcv .bss */
|
|
||||||
constexpr size_t HocPcvScratchSize = 0x2000;
|
|
||||||
constexpr size_t HocBusFreqBufOffset = 0x1000; /* start of the SOC bus region */
|
|
||||||
|
|
||||||
void SafetyCheck();
|
void SafetyCheck();
|
||||||
void Patch(uintptr_t mapped_nso, size_t nso_size, uintptr_t cave, size_t cave_size, uintptr_t nso_address, uintptr_t data_arena);
|
void Patch(uintptr_t mapped_nso, size_t nso_size, uintptr_t cave, size_t cave_size, uintptr_t nso_address, uintptr_t data_arena);
|
||||||
|
|
||||||
|
|||||||
@@ -180,12 +180,6 @@ namespace ams::ldr::hoc::pcv {
|
|||||||
// The pcv SoC-voltage DVB table is a fixed 32-entry region. (it doesn't need to be larger)
|
// The pcv SoC-voltage DVB table is a fixed 32-entry region. (it doesn't need to be larger)
|
||||||
constexpr size_t DvbTableCapacity = 32;
|
constexpr size_t DvbTableCapacity = 32;
|
||||||
|
|
||||||
// extra .bss location, 0 until Patch() runs.
|
|
||||||
inline uintptr_t g_pcv_scratch = 0;
|
|
||||||
|
|
||||||
inline uintptr_t g_pcv_cave = 0;
|
|
||||||
inline size_t g_pcv_cave_size = 0;
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
size_t GetDvfsTableEntryCount(T *table_head) {
|
size_t GetDvfsTableEntryCount(T *table_head) {
|
||||||
using NT = std::remove_const_t<std::remove_volatile_t<T>>;
|
using NT = std::remove_const_t<std::remove_volatile_t<T>>;
|
||||||
|
|||||||
@@ -145,6 +145,10 @@ namespace ams::ldr::hoc::pcv {
|
|||||||
|
|
||||||
uintptr_t CaveVa() const { return ToVa(reinterpret_cast<const void *>(m_cave)); }
|
uintptr_t CaveVa() const { return ToVa(reinterpret_cast<const void *>(m_cave)); }
|
||||||
|
|
||||||
|
uintptr_t CaveBase() const { return m_cave; }
|
||||||
|
|
||||||
|
u32 *Reserve(size_t words) { return this->AllocCode(words); }
|
||||||
|
|
||||||
Result CopyPayload() {
|
Result CopyPayload() {
|
||||||
R_TRY(this->CheckEnabled());
|
R_TRY(this->CheckEnabled());
|
||||||
|
|
||||||
|
|||||||
@@ -617,23 +617,24 @@ namespace ams::ldr::hoc::pcv::mariko {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* hook payload data + buffers */
|
||||||
|
struct HookPayloadData {
|
||||||
|
struct {
|
||||||
|
u64 orig;
|
||||||
|
u32 *buf[2]; /* VAs of the bus freq buffers */
|
||||||
|
u32 counter;
|
||||||
|
} busFreqReloc;
|
||||||
|
};
|
||||||
|
DEFINE_HOOK_PAYLOAD_PTR(HookPayloadData, m_HookPayloadData);
|
||||||
|
|
||||||
|
/* The new replacement buffer */
|
||||||
|
struct BusFreqBuf { u32 e[256]; };
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
std::vector<u32> newEmcList;
|
std::vector<u32> newEmcList;
|
||||||
u32 *nsoStart;
|
u32 *nsoStart;
|
||||||
size_t g_nso_size = 0;
|
size_t g_nso_size = 0;
|
||||||
uintptr_t g_cave_cursor = 0;
|
u32 *g_busInitSite = nullptr; /* VccSharedBusInit entry, cached by BusFreqReloc */
|
||||||
}
|
|
||||||
|
|
||||||
static uintptr_t CaveReserve(size_t count) {
|
|
||||||
if (g_pcv_cave == 0 || g_cave_cursor == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (g_cave_cursor + count * sizeof(u32) > g_pcv_cave + g_pcv_cave_size) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
const uintptr_t entry = g_cave_cursor;
|
|
||||||
g_cave_cursor += count * sizeof(u32);
|
|
||||||
return entry;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if HOC_UART_LOG
|
#if HOC_UART_LOG
|
||||||
@@ -641,7 +642,7 @@ namespace ams::ldr::hoc::pcv::mariko {
|
|||||||
Result NvLogUartRedirect(u32 *ptr) {
|
Result NvLogUartRedirect(u32 *ptr) {
|
||||||
const uintptr_t mapped_nso = reinterpret_cast<uintptr_t>(nsoStart);
|
const uintptr_t mapped_nso = reinterpret_cast<uintptr_t>(nsoStart);
|
||||||
const size_t nso_size = g_nso_size;
|
const size_t nso_size = g_nso_size;
|
||||||
const uintptr_t textEnd = g_pcv_cave; /* .text ends where the cave begins */
|
const uintptr_t textEnd = Hooks().CaveBase(); /* .text ends where the cave begins */
|
||||||
const uintptr_t vsnprintf_addr = reinterpret_cast<uintptr_t>(ptr);
|
const uintptr_t vsnprintf_addr = reinterpret_cast<uintptr_t>(ptr);
|
||||||
|
|
||||||
/* NvLog via the VDD_SOC log */
|
/* NvLog via the VDD_SOC log */
|
||||||
@@ -685,10 +686,10 @@ namespace ams::ldr::hoc::pcv::mariko {
|
|||||||
R_THROW(ldr::ResultInvalidNvLogRedirect());
|
R_THROW(ldr::ResultInvalidNvLogRedirect());
|
||||||
}
|
}
|
||||||
|
|
||||||
const uintptr_t helper = CaveReserve(40);
|
const uintptr_t helper = reinterpret_cast<uintptr_t>(Hooks().Reserve(40));
|
||||||
if (helper == 0) {
|
if (helper == 0) {
|
||||||
LOGGING("NvLogRedirect: cave unavailable (cave=%lx size=%lx)",
|
LOGGING("NvLogRedirect: cave unavailable (cave=%lx free=%lx)",
|
||||||
static_cast<unsigned long>(g_pcv_cave), static_cast<unsigned long>(g_pcv_cave_size));
|
static_cast<unsigned long>(Hooks().CaveBase()), static_cast<unsigned long>(Hooks().CaveFree()));
|
||||||
R_THROW(ldr::ResultInvalidNvLogRedirect());
|
R_THROW(ldr::ResultInvalidNvLogRedirect());
|
||||||
}
|
}
|
||||||
u32 *t = reinterpret_cast<u32 *>(helper);
|
u32 *t = reinterpret_cast<u32 *>(helper);
|
||||||
@@ -726,7 +727,7 @@ namespace ams::ldr::hoc::pcv::mariko {
|
|||||||
emit(RetIns);
|
emit(RetIns);
|
||||||
|
|
||||||
/* Redirect the call sites as patching the actual function causes crash */
|
/* Redirect the call sites as patching the actual function causes crash */
|
||||||
const uintptr_t roStart = g_pcv_cave + g_pcv_cave_size; /* module .rodata start */
|
const uintptr_t roStart = Hooks().CaveBase() + Hooks().CaveSize(); /* module .rodata start */
|
||||||
size_t patchedSites = 0;
|
size_t patchedSites = 0;
|
||||||
if (HOC_PCV_NVLOG_PATCH) {
|
if (HOC_PCV_NVLOG_PATCH) {
|
||||||
for (u32 *p = nsoStart; reinterpret_cast<uintptr_t>(p + 1) <= textEnd; ++p) {
|
for (u32 *p = nsoStart; reinterpret_cast<uintptr_t>(p + 1) <= textEnd; ++p) {
|
||||||
@@ -759,47 +760,55 @@ namespace ams::ldr::hoc::pcv::mariko {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Relocate C2/C3Bus to avoid issues*/
|
/* Relocate C3bus/C2bus freq buffers */
|
||||||
Result BusFreqReloc(u32 *ptr) {
|
Result BusFreqReloc(u32 *ptr) {
|
||||||
const u32 busReg = AsmGetRn(ptr[0]); /* ldr Xbuf,[Xbus,#0x10] : bus struct pointer */
|
/* Entry shape: sub sp,sp,#imm ; stp x29,x30,[sp,#0x10]. */
|
||||||
const u32 bufReg = asm_get_rd(ptr[0]); /* : freq-buffer arg */
|
for (u32 i = 1; i <= 48; ++i) {
|
||||||
const u32 bufOff = AsmGetLdStImm64Off(ptr[0]); /* : bus->freqBuf offset */
|
u32 *p = ptr - i;
|
||||||
const u32 cntReg = asm_get_rd(ptr[1]); /* add Xcnt,Xbus,#0x18 : arg2 (&count) */
|
if (p - 1 < nsoStart) {
|
||||||
const u32 railReg = asm_get_rd(ptr[2]); /* str Xrail,[Xbus,#0x50]: arg0 (rail) */
|
break;
|
||||||
u32 *call = ptr + 3; /* the bl to relocate */
|
}
|
||||||
const uintptr_t realFn = AsmBranchTarget(*call, reinterpret_cast<uintptr_t>(call));
|
if (p[0] == 0xA9017BFDu /* stp x29,x30,[sp,#0x10] */
|
||||||
|
&& (p[-1] & 0xFFC003FFu) == 0xD10003FFu) { /* sub sp,sp,#imm12 */
|
||||||
/* Pick 3 scratch registers */
|
g_busInitSite = p - 1;
|
||||||
u32 s[3], sc = 0;
|
R_SUCCEED();
|
||||||
for (u32 r = 9; r <= 15 && sc < 3; ++r) {
|
|
||||||
if (r != busReg && r != bufReg && r != cntReg && r != railReg) {
|
|
||||||
s[sc++] = r;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
R_UNLESS(sc == 3, ldr::ResultInvalidBusFreqReloc());
|
R_THROW(ldr::ResultInvalidBusFreqReloc());
|
||||||
|
}
|
||||||
|
|
||||||
const uintptr_t tramp = CaveReserve(9);
|
/* Replace VCCBusInit entry with new buffer. */
|
||||||
R_UNLESS(tramp != 0, ldr::ResultInvalidBusFreqReloc());
|
HOOK_PAYLOAD_FN u64 VccSharedBusInitHook(u64 *bus) {
|
||||||
|
HookPayloadData *data = HOOK_PAYLOAD_PTR(HookPayloadData, m_HookPayloadData);
|
||||||
|
const u32 idx = data->busFreqReloc.counter;
|
||||||
|
if (idx < 2) {
|
||||||
|
data->busFreqReloc.counter = idx + 1;
|
||||||
|
bus[2] = reinterpret_cast<u64>(data->busFreqReloc.buf[idx]); /* bus[0x10] = big buffer */
|
||||||
|
}
|
||||||
|
using OrigFn = u64 (*)(u64 *);
|
||||||
|
u64 result = reinterpret_cast<OrigFn>(data->busFreqReloc.orig)(bus);
|
||||||
|
__asm__ __volatile__("" : "+r"(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
const uintptr_t region = g_pcv_scratch + HocBusFreqBufOffset; /* [0]=counter, +0x10 + i*0x400 = bufs */
|
Result InstallHooks() {
|
||||||
u32 *t = reinterpret_cast<u32 *>(tramp);
|
R_TRY(Hooks().CheckEnabled()); /* CopyPayload already done at the top of Patch() */
|
||||||
size_t n = 0;
|
|
||||||
auto emit = [&](u32 ins) { t[n] = ins; ++n; };
|
auto *data = Hooks().BindData(m_HookPayloadData);
|
||||||
emit(AsmMakeAdrp(tramp + n * 4, region, s[0])); /* adrp s0,<region> */
|
R_UNLESS(data != nullptr, ldr::ResultHookDataOutOfMemory());
|
||||||
emit(AsmMakeAddImm64(s[0], s[0], region & 0xFFFu)); /* add s0,s0,#lo */
|
|
||||||
emit(AsmMakeLdrImm32(s[1], s[0], 0x00)); /* s1 = counter */
|
R_UNLESS(g_busInitSite != nullptr, ldr::ResultInvalidBusFreqReloc());
|
||||||
emit(AsmMakeAddImm64(s[2], s[1], 1)); /* s2 = counter+1 */
|
for (u32 i = 0; i < 2; ++i) {
|
||||||
emit(AsmMakeStrImm32(s[2], s[0], 0x00)); /* counter++ */
|
auto *buf = Hooks().DataAlloc<BusFreqBuf>();
|
||||||
emit(AsmMakeAddImm64(s[0], s[0], 0x10)); /* s0 = region+0x10 (buffers) */
|
R_UNLESS(buf != nullptr, ldr::ResultHookDataOutOfMemory());
|
||||||
emit(AsmMakeAddShiftedReg64(bufReg, s[0], s[1], 10)); /* Xbuf = s0 + counter*0x400 */
|
data->busFreqReloc.buf[i] = reinterpret_cast<u32 *>(Hooks().ToVa(buf));
|
||||||
emit(AsmMakeStrImm64(bufReg, busReg, bufOff)); /* bus[freqBuf] = Xbuf */
|
}
|
||||||
emit(AsmMakeB(tramp + n * 4, realFn)); /* tail-call the real function */
|
data->busFreqReloc.counter = 0;
|
||||||
|
|
||||||
|
uintptr_t orig = 0;
|
||||||
|
R_TRY(INSTALL_IMPL_HOOK_ORIG(g_busInitSite, VccSharedBusInitHook, std::addressof(orig)));
|
||||||
|
data->busFreqReloc.orig = orig;
|
||||||
|
|
||||||
PATCH_OFFSET(call, AsmMakeBl(reinterpret_cast<uintptr_t>(call), tramp));
|
|
||||||
const uintptr_t base = reinterpret_cast<uintptr_t>(nsoStart);
|
|
||||||
(void) base;
|
|
||||||
LOGGING("BusFreqReloc: call@+%lx -> tramp@+%lx realfn@+%lx (bus=x%u buf=x%u off=0x%x scratch=x%u,x%u,x%u)",
|
|
||||||
reinterpret_cast<uintptr_t>(call) - base, tramp - base, realFn - base, busReg, bufReg, bufOff, s[0], s[1], s[2]);
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1479,11 +1488,14 @@ namespace ams::ldr::hoc::pcv::mariko {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Patch(uintptr_t mapped_nso, size_t nso_size) {
|
void Patch(uintptr_t mapped_nso, size_t nso_size) {
|
||||||
nsoStart = reinterpret_cast<u32 *>(mapped_nso);
|
nsoStart = reinterpret_cast<u32 *>(mapped_nso);
|
||||||
|
g_nso_size = nso_size;
|
||||||
|
|
||||||
g_pcv_scratch = mapped_nso + nso_size - HocPcvScratchSize;
|
/* Copy the hook payload into the cave first, patchers can allocate code past it. */
|
||||||
g_nso_size = nso_size;
|
if (R_FAILED(Hooks().CopyPayload())) {
|
||||||
g_cave_cursor = g_pcv_cave; /* start the .text-cave bump allocator (0 if unavailable) */
|
panic::SmcError(panic::Patch);
|
||||||
|
CRASH("CopyPayload");
|
||||||
|
}
|
||||||
|
|
||||||
MtcGenerateFreqTables();
|
MtcGenerateFreqTables();
|
||||||
|
|
||||||
@@ -1540,6 +1552,11 @@ namespace ams::ldr::hoc::pcv::mariko {
|
|||||||
CRASH(entry.description);
|
CRASH(entry.description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (R_FAILED(InstallHooks())) {
|
||||||
|
panic::SmcError(panic::Patch);
|
||||||
|
CRASH("InstallHooks");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include "../oc_common.hpp"
|
#include "../oc_common.hpp"
|
||||||
#include "pcv_common.hpp"
|
#include "pcv_common.hpp"
|
||||||
#include "pcv_asm.hpp"
|
#include "pcv_asm.hpp"
|
||||||
|
#include "pcv_hook.hpp"
|
||||||
|
|
||||||
namespace ams::ldr::hoc::pcv::mariko {
|
namespace ams::ldr::hoc::pcv::mariko {
|
||||||
|
|
||||||
@@ -372,12 +373,6 @@ namespace ams::ldr::hoc::pcv::mariko {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline bool BusFreqRelocPatternFn(u32 *ptr) {
|
inline bool BusFreqRelocPatternFn(u32 *ptr) {
|
||||||
if (g_pcv_scratch == 0 || g_pcv_cave == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (reinterpret_cast<uintptr_t>(ptr + 4) > g_pcv_cave) { /* the call site lives in .text */
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!(AsmIsLdrImm64(ptr[0]) && AsmGetLdStImm64Off(ptr[0]) == 0x10)) return false; /* ldr Xbuf,[Xbus,#0x10] */
|
if (!(AsmIsLdrImm64(ptr[0]) && AsmGetLdStImm64Off(ptr[0]) == 0x10)) return false; /* ldr Xbuf,[Xbus,#0x10] */
|
||||||
if (!(AsmIsAddImm64(ptr[1]) && AsmGetImm12(ptr[1]) == 0x18)) return false; /* add Xcnt,Xbus,#0x18 */
|
if (!(AsmIsAddImm64(ptr[1]) && AsmGetImm12(ptr[1]) == 0x18)) return false; /* add Xcnt,Xbus,#0x18 */
|
||||||
if (!(AsmIsStrImm64(ptr[2]) && AsmGetLdStImm64Off(ptr[2]) == 0x50)) return false; /* str Xrail,[Xbus,#0x50]*/
|
if (!(AsmIsStrImm64(ptr[2]) && AsmGetLdStImm64Off(ptr[2]) == 0x50)) return false; /* str Xrail,[Xbus,#0x50]*/
|
||||||
@@ -387,10 +382,10 @@ namespace ams::ldr::hoc::pcv::mariko {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline bool ForceVerbosityPatternFn(u32 *ptr) {
|
inline bool ForceVerbosityPatternFn(u32 *ptr) {
|
||||||
if (HOC_PCV_FORCE_VERBOSITY == 0 || g_pcv_cave == 0) {
|
if (HOC_PCV_FORCE_VERBOSITY == 0 || Hooks().CaveBase() == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (reinterpret_cast<uintptr_t>(ptr + 11) > g_pcv_cave) { /* .text only */
|
if (reinterpret_cast<uintptr_t>(ptr + 11) > Hooks().CaveBase()) { /* .text only */
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ptr[0] != 0xA9BE7BFDu || ptr[1] != 0xF9000BF3u || ptr[2] != 0x910003FDu) return false; /* stp/str/mov x29,sp */
|
if (ptr[0] != 0xA9BE7BFDu || ptr[1] != 0xF9000BF3u || ptr[2] != 0x910003FDu) return false; /* stp/str/mov x29,sp */
|
||||||
@@ -409,10 +404,10 @@ namespace ams::ldr::hoc::pcv::mariko {
|
|||||||
inline constexpr u32 NvLogVsnSig[] = { 0xD10483FFu, 0xA9107BFDu, 0xF9008BFCu, 0x910403FDu, 0xF100003Fu };
|
inline constexpr u32 NvLogVsnSig[] = { 0xD10483FFu, 0xA9107BFDu, 0xF9008BFCu, 0x910403FDu, 0xF100003Fu };
|
||||||
|
|
||||||
inline bool NvLogVsnprintfPatternFn(u32 *ptr) {
|
inline bool NvLogVsnprintfPatternFn(u32 *ptr) {
|
||||||
if (HOC_UART_LOG == 0 || g_pcv_cave == 0) {
|
if (HOC_UART_LOG == 0 || Hooks().CaveBase() == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (reinterpret_cast<uintptr_t>(ptr + std::size(NvLogVsnSig)) > g_pcv_cave) { /* must sit in .text */
|
if (reinterpret_cast<uintptr_t>(ptr + std::size(NvLogVsnSig)) > Hooks().CaveBase()) { /* must sit in .text */
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (size_t k = 0; k < std::size(NvLogVsnSig); ++k) {
|
for (size_t k = 0; k < std::size(NvLogVsnSig); ++k) {
|
||||||
|
|||||||
@@ -229,7 +229,7 @@ std::vector<std::string> ConfigInfoStrings(HocClkConfigValue val, bool isMariko,
|
|||||||
return {
|
return {
|
||||||
"The step that RAM clocks take.",
|
"The step that RAM clocks take.",
|
||||||
"Options:",
|
"Options:",
|
||||||
" - 33MHz - 3 MHz step (ex. 1600, 1633, 1666, 1700, etc.)",
|
" - 33MHz - 33 MHz step (ex. 1600, 1633, 1666, 1700, etc.)",
|
||||||
" - 66MHz - 66 MHz step (ex. 1600, 1666, 1733, etc.)",
|
" - 66MHz - 66 MHz step (ex. 1600, 1666, 1733, etc.)",
|
||||||
" - 100MHz - 100 MHz step (ex. 1600, 1700, 1800, etc.)",
|
" - 100MHz - 100 MHz step (ex. 1600, 1700, 1800, etc.)",
|
||||||
" - 133MHz - 133 MHz step (ex. 1600, 1733, 1866, etc.)",
|
" - 133MHz - 133 MHz step (ex. 1600, 1733, 1866, etc.)",
|
||||||
|
|||||||
Reference in New Issue
Block a user