diff --git a/README.md b/README.md index 1babbe0e..67c4c6e3 100644 --- a/README.md +++ b/README.md @@ -108,11 +108,9 @@ I'd appreciate if someone is willing to contribute or upload latest binaries. Bu | RAM Volt | N/A | Disabled | | RAM Timing | Auto-Adjusted | Disabled | - - No parser/editor currently, although you could easily customize those with a hex editor: - - Search for ASCII string `CUST` - - All values are little-endian - - Switch to override or replace mode, NOT insert mode - - See `ldr_oc_suite.hpp` for config struct + - Loader configurator + - Grab `ldr_config.py` from the root of repo and modify values in `cust_conf` dict. + - `python ldr_config.py loader.kip -s` will save your configuration in-place. 5. **Hekate-ipl bootloader** - Rename the kip to `loader.kip` and add `kip1=atmosphere/kips/loader.kip` in `bootloader/hekate_ipl.ini` diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/ldr_oc_customize.inc b/Source/Atmosphere/stratosphere/loader/source/oc/ldr_oc_customize.inc index cbb05b67..c11efb0a 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/ldr_oc_customize.inc +++ b/Source/Atmosphere/stratosphere/loader/source/oc/ldr_oc_customize.inc @@ -5,7 +5,7 @@ static const volatile CustomizeTable C = { * 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. + * Replace the entire max mtc table with customized one (provided by user). */ .mtcConf = AUTO_ADJ_MARIKO_SAFE, diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/ldr_oc_suite.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/ldr_oc_suite.cpp index 5dcf03dd..c8fb196c 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/ldr_oc_suite.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/ldr_oc_suite.cpp @@ -1032,7 +1032,7 @@ namespace ams::ldr::oc { constexpr u32 uv_step = 12'500; if (emc_uv % uv_step) emc_uv = emc_uv / uv_step * uv_step; - PatchOffset(ptr, emc_uv); + PatchOffset(ptr, emc_uv); } return ResultSuccess(); diff --git a/ldr_config.py b/ldr_config.py new file mode 100755 index 00000000..36c23a02 --- /dev/null +++ b/ldr_config.py @@ -0,0 +1,164 @@ +#!python + +cust_conf = { +# DRAM Timing: +# 0: AUTO_ADJ_MARIKO_SAFE: Auto adjust timings for LPDDR4 ≤3733 Mbps specs, 8Gb density (Default). +# 1: AUTO_ADJ_MARIKO_4266: Auto adjust timings for LPDDR4X 4266 Mbps specs, 8Gb density. +# 2: ENTIRE_TABLE_ERISTA: +# 3: ENTIRE_TABLE_MARIKO: Replace the entire max mtc table with customized one (provided by user). + "mtcConf": 0, +# Mariko CPU: +# - Max Clock in kHz: +# Default: 1785000 +# >= 2193000 will enable overvolting (> 1120 mV) +# - Max Voltage in mV: +# Default voltage: 1120 +# Haven't tested anything higher than 1220. + "marikoCpuMaxClock": 2397000, + "marikoCpuMaxVolt": 1220, +# Mariko GPU: +# - Max Clock in kHz: +# Default: 921600 +# NVIDIA Maximum: 1267200 + "marikoGpuMaxClock": 1305600, +# Mariko EMC: +# - RAM Clock in kHz: +# Values should be > 1600000, and divided evenly by 9600 or 12800. +# [WARNING] +# RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM: +# - Graphical glitches +# - System instabilities +# - NAND corruption +# Timings from auto-adjustment have been tested safe for up to 1996.8 MHz for all DRAM chips. + "marikoEmcMaxClock": 1996800, +# Erista CPU: +# Not tested and not enabled by default. +# - Enable Overclock +# Require modificaitions towards NewCpuTables! +# - Max Voltage in mV + "eristaCpuOCEnable": 0, + "eristaCpuMaxVolt": 0, +# Erista EMC: +# - RAM Clock in kHz +# [WARNING] +# RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM: +# - Graphical glitches +# - System instabilities +# - NAND corruption +# - RAM Voltage in uV +# Range: 600'000 to 1250'000 uV +# Value should be divided evenly by 12'500 +# Default(HOS): 1125'000 +# Not enabled by default. + "eristaEmcMaxClock": 1862400, + "eristaEmcVolt": 0 + } + +cust_range = {"mtcConf": (0, 3), + "marikoCpuMaxClock": (1785000, 3000000), + "marikoCpuMaxVolt": (1100, 1300), + "marikoGpuMaxClock": (768000, 1536000), + "marikoEmcMaxClock": (1612800, 2400000), + "eristaCpuMaxVolt": (1100, 1400), + "eristaEmcMaxClock": (1600000, 2400000), + "eristaEmcVolt": (1100000, 1250000)} + +import struct +import csv +from pprint import pprint +import argparse + +cust_rev = 1 +cust_head = ["cust", "custRev"] +cust_body = ["mtcConf", + "marikoCpuMaxClock", "marikoCpuMaxVolt", "marikoGpuMaxClock", "marikoEmcMaxClock", + "eristaCpuOCEnable", "eristaCpuMaxVolt", "eristaEmcMaxClock", "eristaEmcVolt"] +cust_key = [*cust_head, *cust_body] + +parser = argparse.ArgumentParser(description='Loader Configurator v'+str(cust_rev)) +parser.add_argument("file", help="Path of loader.kip") +parser.add_argument("--save", "-s", action="store_true", help="Save configuration to loader.kip") +parser.add_argument("--ignore", action="store_true", help="Ignore range safety check") +args = parser.parse_args() + +def CSVRead(file_loc): + with open(file_loc, 'r') as file: + rd = csv.reader(file) + dct = {rows[0]:rows[1] for rows in rd} + file.close() + return dct + +def CSVWrite(file_loc, dct): + with open(file_loc, 'w') as file: + for key, value in dct.items(): + file.write('{0},{1}\n'.format(key, value)) + file.close() + +def CustSafetyCheck(cust): + warn_cnt = 0 + for i in cust_body: + val = int(cust[i]) + if val and val not in range(*cust_range[i]): + warn_cnt += 1 + print("[!] %s = %u (Expected range: %u ≤ value ≤ %u)" % (i, val, *cust_range[i])) + return warn_cnt + +def KIPCustHandler(file_loc, cust={}): + if len(cust) != 0: + missing = set(cust_body) - set(cust.keys()) + if missing: + print("Invalid cust dict! Missing: %s" % missing) + return + if CustSafetyCheck(cust) and args.ignore == False: + return + + with open(file_loc, "rb") as file: + header_str = b'KIP1Loader' + header = file.read(len(header_str)) + file.seek(0) + cust_magic = b'CUST' + cust_pos = file.read().find(cust_magic) + + if header != header_str or cust_pos == -1: + print("Invalid kip file!") + return False + + file.seek(cust_pos) + cust_fmt = '<4s2H8I' + cust_size = struct.calcsize(cust_fmt) + cust_buf = file.read(cust_size) + cust_val = struct.unpack(cust_fmt, cust_buf) + cust_dict = dict(zip(cust_key, cust_val)) + file.close() + + if cust_dict['custRev'] != cust_rev: + print("custRev does NOT match, expected: %u, got: %u!" % (cust_rev, cust_dict['custRev'])) + return False + + if cust == {}: + [cust_dict.pop(key) for key in cust_head] + return cust_dict + + with open(file_loc, "rb+") as file: + cust_head_fmt = '<4s1H' + cust_body_fmt = '<1H8I' + cust_bin = struct.pack(cust_body_fmt, *[cust[i] for i in cust_body]) + file.seek(cust_pos + struct.calcsize(cust_head_fmt)) + file.write(cust_bin) + file.close() + print("Done!") + +def main(file_loc, save=False): + cust = KIPCustHandler(file_loc) + if not cust: + return + print("Configuration from file:") + pprint(cust, sort_dicts=False) + + if save: + print("Saving new configuration...:") + pprint(cust_conf, sort_dicts=False) + KIPCustHandler(file_loc, cust_conf) + +if __name__ == "__main__": + main(args.file, args.save)