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