diff --git a/bootloader/hos/hos.c b/bootloader/hos/hos.c index 346b988..95b6e08 100644 --- a/bootloader/hos/hos.c +++ b/bootloader/hos/hos.c @@ -3,6 +3,7 @@ * Copyright (c) 2018 st4rk * Copyright (c) 2018 Ced2911 * Copyright (C) 2018 CTCaer + * Copyright (c) 2018 balika011 * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -128,6 +129,8 @@ static void _se_lock() SE(SE_KEY_TABLE_ACCESS_LOCK_OFFSET) = 0; // Make all key access regs secure only. SE(SE_RSA_KEYTABLE_ACCESS_LOCK_OFFSET) = 0; // Make all RSA access regs secure only. SE(SE_SECURITY_0) &= 0xFFFFFFFB; // Make access lock regs secure only. + memset((void *) IPATCH_BASE, 0, 13); + SB(SB_CSR) = 0x10; // Protected IROM enable. // This is useful for documenting the bits in the SE config registers, so we can keep it around. /*gfx_printf(&gfx_con, "SE(SE_SECURITY_0) = %08X\n", SE(SE_SECURITY_0)); diff --git a/bootloader/main.c b/bootloader/main.c index 755afa7..d3cafff 100644 --- a/bootloader/main.c +++ b/bootloader/main.c @@ -4,6 +4,7 @@ * Copyright (c) 2018 Rajko Stojadinovic * Copyright (c) 2018 CTCaer * Copyright (c) 2018 Reisyukaku + * Copyright (c) 2018 balika011 * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -2908,6 +2909,78 @@ void fix_battery_desync() btn_wait(); } +void ipatch_process(u32 offset, u32 value) +{ + gfx_printf(&gfx_con, "%8x %8x", BOOTROM_BASE + offset, value); + u8 lo = value & 0xff; + switch (value >> 8) + { + case 0xdf: + gfx_printf(&gfx_con, " svc #0x%02x", lo); + break; + case 0x20: + gfx_printf(&gfx_con, " movs r0, #0x%02x", lo); + break; + } + gfx_puts(&gfx_con, "\n"); +} + +void bootrom_ipatches_info() +{ + gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); + gfx_con_setpos(&gfx_con, 0, 0); + + u32 res = fuse_read_ipatch(ipatch_process); + if (res != 0) + EPRINTFARGS("Failed to read ipatches. Error: %d", res); + + gfx_puts(&gfx_con, "\nPress POWER to dump them to SD Card.\nPress VOL to go to the menu.\n"); + + u32 btn = btn_wait(); + if (btn & BTN_POWER) + { + if (sd_mount()) + { + char path[64]; + u32 iram_evp_thunks[0x200]; + u32 iram_evp_thunks_len = sizeof(iram_evp_thunks); + res = fuse_read_evp_thunk(iram_evp_thunks, &iram_evp_thunks_len); + if (res == 0) + { + emmcsn_path_impl(path, "/dumps", "evp_thunks.bin", NULL); + if (!sd_save_to_file((u8 *)iram_evp_thunks, iram_evp_thunks_len, path)) + gfx_puts(&gfx_con, "\nevp_thunks.bin saved!\n"); + } + else + EPRINTFARGS("Failed to read evp_thunks. Error: %d", res); + + u32 words[0x100]; + read_raw_ipatch_fuses(words); + emmcsn_path_impl(path, "/dumps", "ipatches.bin", NULL); + if (!sd_save_to_file((u8 *)words, sizeof(words), path)) + gfx_puts(&gfx_con, "\nipatches.bin saved!\n"); + + emmcsn_path_impl(path, "/dumps", "bootrom_patched.bin", NULL); + if (!sd_save_to_file((u8 *)BOOTROM_BASE, BOOTROM_SIZE, path)) + gfx_puts(&gfx_con, "\nbootrom_patched.bin saved!\n"); + + u8 ipatch_backup[13]; + memcpy(ipatch_backup, (void *) IPATCH_BASE, 13); + memset((void*)IPATCH_BASE, 0, 13); + + emmcsn_path_impl(path, "/dumps", "bootrom_unpatched.bin", NULL); + if (!sd_save_to_file((u8 *)BOOTROM_BASE, BOOTROM_SIZE, path)) + gfx_puts(&gfx_con, "\nbootrom_unpatched.bin saved!\n"); + + memcpy((void*)IPATCH_BASE, ipatch_backup, 13); + + sd_unmount(); + } + + btn_wait(); + } +} + void about() { static const char credits[] = @@ -3044,6 +3117,7 @@ ment_t ment_tools[] = { MDEF_HANDLER("Fix battery de-sync", fix_battery_desync), MDEF_HANDLER("Unset archive bit (switch folder)", fix_sd_switch_attr), MDEF_HANDLER("Unset archive bit (all sd files)", fix_sd_all_attr), + MDEF_HANDLER("Ipatches & bootrom info", bootrom_ipatches_info), //MDEF_HANDLER("Fix fuel gauge configuration", fix_fuel_gauge_configuration), //MDEF_HANDLER("Reset all battery cfg", reset_pmic_fuel_gauge_charger_config), MDEF_CHGLINE(), diff --git a/bootloader/soc/fuse.c b/bootloader/soc/fuse.c index bf41d8d..95ddfd3 100644 --- a/bootloader/soc/fuse.c +++ b/bootloader/soc/fuse.c @@ -1,5 +1,7 @@ /* * Copyright (c) 2018 naehrwert + * Copyright (c) 2018 shuffle2 + * Copyright (c) 2018 balika011 * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -14,9 +16,39 @@ * along with this program. If not, see . */ +#include + #include "../soc/fuse.h" #include "../soc/t210.h" +#define ARRAYSIZE(x) (sizeof(x) / sizeof(*x)) + +static const u32 evp_thunk_template[] = { + 0xe92d0007, // STMFD SP!, {R0-R2} + 0xe1a0200e, // MOV R2, LR + 0xe2422002, // SUB R2, R2, #2 + 0xe5922000, // LDR R2, [R2] + 0xe20220ff, // AND R2, R2, #0xFF + 0xe1a02082, // MOV R2, R2,LSL#1 + 0xe59f001c, // LDR R0, =evp_thunk_template + 0xe59f101c, // LDR R1, =thunk_end + 0xe0411000, // SUB R1, R1, R0 + 0xe59f0018, // LDR R0, =iram_evp_thunks + 0xe0800001, // ADD R0, R0, R1 + 0xe0822000, // ADD R2, R2, R0 + 0xe3822001, // ORR R2, R2, #1 + 0xe8bd0003, // LDMFD SP!, {R0,R1} + 0xe12fff12, // BX R2 + 0x001007b0, // off_1007EC DCD evp_thunk_template + 0x001007f8, // off_1007F0 DCD thunk_end + 0x40004c30, // off_1007F4 DCD iram_evp_thunks + // thunk_end is here +}; +static const u32 evp_thunk_template_len = sizeof(evp_thunk_template); + +// treated as 12bit values +static const u32 hash_vals[] = {1, 2, 4, 8, 0, 3, 5, 6, 7, 9, 10, 11}; + void fuse_disable_program() { FUSE(FUSE_DISABLEREGPROGRAM) = 1; @@ -26,3 +58,268 @@ u32 fuse_read_odm(u32 idx) { return FUSE(FUSE_RESERVED_ODMX(idx)); } + +void fuse_wait_idle() +{ + u32 ctrl; + do + { + ctrl = FUSE(FUSE_CTRL); + } while (((ctrl >> 16) & 0x1f) != 4); +} + +u32 parity32_even(u32 *words, u32 count) +{ + u32 acc = words[0]; + for (u32 i = 1; i < count; i++) + { + acc ^= words[i]; + } + u32 lo = ((acc & 0xffff) ^ (acc >> 16)) & 0xff; + u32 hi = ((acc & 0xffff) ^ (acc >> 16)) >> 8; + u32 x = hi ^ lo; + lo = ((x & 0xf) ^ (x >> 4)) & 3; + hi = ((x & 0xf) ^ (x >> 4)) >> 2; + x = hi ^ lo; + + return (x & 1) ^ (x >> 1); +} + +int patch_hash_one(u32 *word) +{ + u32 bits20_31 = *word & 0xfff00000; + u32 parity_bit = parity32_even(&bits20_31, 1); + u32 hash = 0; + for (u32 i = 0; i < 12; i++) + { + if (*word & (1 << (20 + i))) + { + hash ^= hash_vals[i]; + } + } + if (hash == 0) + { + if (parity_bit == 0) + { + return 0; + } + *word ^= 1 << 24; + return 1; + } + if (parity_bit == 0) + { + return 3; + } + for (u32 i = 0; i < ARRAYSIZE(hash_vals); i++) + { + if (hash_vals[i] == hash) + { + *word ^= 1 << (20 + i); + return 1; + } + } + return 2; +} + +int patch_hash_multi(u32 *words, u32 count) +{ + u32 parity_bit = parity32_even(words, count); + u32 bits0_14 = words[0] & 0x7fff; + u32 bit15 = words[0] & 0x8000; + u32 bits16_19 = words[0] & 0xf0000; + + u32 hash = 0; + words[0] = bits16_19; + for (u32 i = 0; i < count; i++) + { + u32 w = words[i]; + if (w) + { + for (u32 bitpos = 0; bitpos < 32; bitpos++) + { + if ((w >> bitpos) & 1) + { + hash ^= 0x4000 + i * 32 + bitpos; + } + } + } + } + hash ^= bits0_14; + // stupid but this is what original code does. + // equivalent to original words[0] &= 0xfff00000 + words[0] = bits16_19 ^ bit15 ^ bits0_14; + + if (hash == 0) + { + if (parity_bit == 0) + { + return 0; + } + words[0] ^= 0x8000; + return 1; + } + if (parity_bit == 0) + { + return 3; + } + u32 bitcount = hash - 0x4000; + if (bitcount < 16 || bitcount >= count * 32) + { + u32 num_set = 0; + for (u32 bitpos = 0; bitpos < 15; bitpos++) + { + if ((hash >> bitpos) & 1) + { + num_set++; + } + } + if (num_set != 1) + { + return 2; + } + words[0] ^= hash; + return 1; + } + words[bitcount / 32] ^= 1 << (hash & 0x1f); + return 1; +} + +int fuse_read_ipatch(void (*ipatch)(u32 offset, u32 value)) +{ + u32 words[80]; + u32 word_count; + u32 word_addr; + u32 word0 = 0; + u32 total_read = 0; + + word_count = FUSE(FUSE_FIRST_BOOTROM_PATCH_SIZE); + word_count &= 0x7f; + word_addr = 191; + + while (word_count) + { + total_read += word_count; + if (total_read >= ARRAYSIZE(words)) + { + break; + } + + for (u32 i = 0; i < word_count; i++) + { + FUSE(FUSE_ADDR) = word_addr--; + FUSE(FUSE_CTRL) = (FUSE(FUSE_ADDR) & ~FUSE_CMD_MASK) | FUSE_READ; + fuse_wait_idle(); + words[i] = FUSE(FUSE_RDATA); + } + + word0 = words[0]; + if (patch_hash_multi(words, word_count) >= 2) + { + return 1; + } + u32 ipatch_count = (words[0] >> 16) & 0xf; + if (ipatch_count) + { + for (u32 i = 0; i < ipatch_count; i++) + { + u32 word = words[i]; + u32 addr = (word >> 16) * 2; + u32 data = word & 0xffff; + + ipatch(addr, data); + } + } + words[0] = word0; + if ((word0 >> 25) == 0) + break; + if (patch_hash_one(&word0) >= 2) + { + return 3; + } + word_count = word0 >> 25; + } + + return 0; +} + +int fuse_read_evp_thunk(u32 *iram_evp_thunks, u32 *iram_evp_thunks_len) +{ + u32 words[80]; + u32 word_count; + u32 word_addr; + u32 word0 = 0; + u32 total_read = 0; + int evp_thunk_written = 0; + void *evp_thunk_dst_addr = 0; + + memset(iram_evp_thunks, 0, *iram_evp_thunks_len); + + word_count = FUSE(FUSE_FIRST_BOOTROM_PATCH_SIZE); + word_count &= 0x7f; + word_addr = 191; + + while (word_count) + { + total_read += word_count; + if (total_read >= ARRAYSIZE(words)) + { + break; + } + + for (u32 i = 0; i < word_count; i++) + { + FUSE(FUSE_ADDR) = word_addr--; + FUSE(FUSE_CTRL) = (FUSE(FUSE_ADDR) & ~FUSE_CMD_MASK) | FUSE_READ; + fuse_wait_idle(); + words[i] = FUSE(FUSE_RDATA); + } + + word0 = words[0]; + if (patch_hash_multi(words, word_count) >= 2) + { + return 1; + } + u32 ipatch_count = (words[0] >> 16) & 0xf; + u32 insn_count = word_count - ipatch_count - 1; + if (insn_count) + { + if (!evp_thunk_written) + { + evp_thunk_dst_addr = (void *)iram_evp_thunks; + + memcpy(evp_thunk_dst_addr, (void *)evp_thunk_template, evp_thunk_template_len); + evp_thunk_dst_addr += evp_thunk_template_len; + evp_thunk_written = 1; + *iram_evp_thunks_len = evp_thunk_template_len; + + //write32(TEGRA_EXCEPTION_VECTORS_BASE + 0x208, iram_evp_thunks); + } + + u32 thunk_patch_len = insn_count * sizeof(u32); + memcpy(evp_thunk_dst_addr, &words[ipatch_count + 1], thunk_patch_len); + evp_thunk_dst_addr += thunk_patch_len; + *iram_evp_thunks_len += thunk_patch_len; + } + words[0] = word0; + if ((word0 >> 25) == 0) + break; + if (patch_hash_one(&word0) >= 2) + { + return 3; + } + word_count = word0 >> 25; + } + + return 0; +} + +void read_raw_ipatch_fuses(u32 *words) +{ + for (u32 i = 0; i < 0x100; i++) + { + FUSE(FUSE_ADDR) = i; + FUSE(FUSE_CTRL) = (FUSE(FUSE_ADDR) & ~FUSE_CMD_MASK) | FUSE_READ; + fuse_wait_idle(); + words[i] = FUSE(FUSE_RDATA); + } +} \ No newline at end of file diff --git a/bootloader/soc/fuse.h b/bootloader/soc/fuse.h index ae066d0..1145478 100644 --- a/bootloader/soc/fuse.h +++ b/bootloader/soc/fuse.h @@ -1,5 +1,7 @@ /* * Copyright (c) 2018 naehrwert + * Copyright (c) 2018 shuffle2 + * Copyright (c) 2018 balika011 * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -34,11 +36,22 @@ #define FUSE_DISABLEREGPROGRAM 0x2C #define FUSE_WRITE_ACCESS_SW 0x30 #define FUSE_PWR_GOOD_SW 0x34 +#define FUSE_FIRST_BOOTROM_PATCH_SIZE 0x19c + +/*! Fuse commands. */ +#define FUSE_READ 0x1 +#define FUSE_WRITE 0x2 +#define FUSE_SENSE 0x3 +#define FUSE_CMD_MASK 0x3 /*! Fuse cache registers. */ #define FUSE_RESERVED_ODMX(x) (0x1C8 + 4 * (x)) void fuse_disable_program(); u32 fuse_read_odm(u32 idx); +void fuse_wait_idle(); +int fuse_read_ipatch(void (*ipatch)(u32 offset, u32 value)); +int fuse_read_evp_thunk(u32 *iram_evp_thunks, u32 *iram_evp_thunks_len); +void read_raw_ipatch_fuses(u32 *words); #endif diff --git a/bootloader/soc/t210.h b/bootloader/soc/t210.h index f41139c..5e42ca1 100644 --- a/bootloader/soc/t210.h +++ b/bootloader/soc/t210.h @@ -19,6 +19,9 @@ #include "../utils/types.h" +#define BOOTROM_BASE 0x100000 +#define BOOTROM_SIZE 0x18000 + #define HOST1X_BASE 0x50000000 #define BPMP_CACHE_BASE 0x50040000 #define DISPLAY_A_BASE 0x54200000 @@ -41,6 +44,7 @@ #define GPIO_7_BASE (GPIO_BASE + 0x600) #define GPIO_8_BASE (GPIO_BASE + 0x700) #define EXCP_VEC_BASE 0x6000F000 +#define IPATCH_BASE 0x6001DC00 #define APB_MISC_BASE 0x70000000 #define PINMUX_AUX_BASE 0x70003000 #define UART_BASE 0x70006000