/* * Copyright (c) 2018 naehrwert * * Copyright (c) 2018-2020 CTCaer * * 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 . */ #include #include #include #include "config.h" #include #include #include "gfx/logos.h" #include "gfx/tui.h" #include "hos/hos.h" #include "hos/secmon_exo.h" #include "hos/sept.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "storage/emummc.h" #include "storage/nx_emmc.h" #include #include #include #include #include #include #include "frontend/fe_emmc_tools.h" #include "frontend/fe_tools.h" #include "frontend/fe_info.h" hekate_config h_cfg; boot_cfg_t __attribute__((section ("._boot_cfg"))) b_cfg; const volatile ipl_ver_meta_t __attribute__((section ("._ipl_version"))) ipl_ver = { .magic = BL_MAGIC, .version = (BL_VER_MJ + '0') | ((BL_VER_MN + '0') << 8) | ((BL_VER_HF + '0') << 16), .rsvd0 = 0, .rsvd1 = 0 }; volatile nyx_storage_t *nyx_str = (nyx_storage_t *)NYX_STORAGE_ADDR; void emmcsn_path_impl(char *path, char *sub_dir, char *filename, sdmmc_storage_t *storage) { sdmmc_storage_t storage2; sdmmc_t sdmmc; char emmcSN[9]; bool init_done = false; memcpy(path, "backup", 7); f_mkdir(path); if (!storage) { if (!sdmmc_storage_init_mmc(&storage2, &sdmmc, SDMMC_BUS_WIDTH_8, SDHCI_TIMING_MMC_HS400)) memcpy(emmcSN, "00000000", 9); else { init_done = true; itoa(storage2.cid.serial, emmcSN, 16); } } else itoa(storage->cid.serial, emmcSN, 16); u32 sub_dir_len = strlen(sub_dir); // Can be a null-terminator. u32 filename_len = strlen(filename); // Can be a null-terminator. memcpy(path + strlen(path), "/", 2); memcpy(path + strlen(path), emmcSN, 9); f_mkdir(path); memcpy(path + strlen(path), sub_dir, sub_dir_len + 1); if (sub_dir_len) f_mkdir(path); memcpy(path + strlen(path), "/", 2); memcpy(path + strlen(path), filename, filename_len + 1); if (init_done) sdmmc_storage_end(&storage2); } void check_power_off_from_hos() { // Power off on AutoRCM wakeup from HOS shutdown. For modchips/dongles. u8 hosWakeup = i2c_recv_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_IRQTOP); if (hosWakeup & MAX77620_IRQ_TOP_RTC_MASK) { sd_end(); // Stop the alarm, in case we injected too fast. max77620_rtc_stop_alarm(); if (h_cfg.autohosoff == 1) { gfx_clear_grey(0x1B); u8 *BOOTLOGO = (void *)malloc(0x4000); blz_uncompress_srcdest(BOOTLOGO_BLZ, SZ_BOOTLOGO_BLZ, BOOTLOGO, SZ_BOOTLOGO); gfx_set_rect_grey(BOOTLOGO, X_BOOTLOGO, Y_BOOTLOGO, 326, 544); display_backlight_brightness(10, 5000); display_backlight_brightness(100, 25000); msleep(600); display_backlight_brightness(0, 20000); } power_off(); } } // This is a safe and unused DRAM region for our payloads. #define RELOC_META_OFF 0x7C #define PATCHED_RELOC_SZ 0x94 #define PATCHED_RELOC_STACK 0x40007000 #define PATCHED_RELOC_ENTRY 0x40010000 #define EXT_PAYLOAD_ADDR 0xC0000000 #define RCM_PAYLOAD_ADDR (EXT_PAYLOAD_ADDR + ALIGN(PATCHED_RELOC_SZ, 0x10)) #define COREBOOT_END_ADDR 0xD0000000 #define CBFS_DRAM_EN_ADDR 0x4003e000 #define CBFS_DRAM_MAGIC 0x4452414D // "DRAM" static void *coreboot_addr; void reloc_patcher(u32 payload_dst, u32 payload_src, u32 payload_size) { memcpy((u8 *)payload_src, (u8 *)IPL_LOAD_ADDR, PATCHED_RELOC_SZ); volatile reloc_meta_t *relocator = (reloc_meta_t *)(payload_src + RELOC_META_OFF); relocator->start = payload_dst - ALIGN(PATCHED_RELOC_SZ, 0x10); relocator->stack = PATCHED_RELOC_STACK; relocator->end = payload_dst + payload_size; relocator->ep = payload_dst; if (payload_size == 0x7000) { memcpy((u8 *)(payload_src + ALIGN(PATCHED_RELOC_SZ, 0x10)), coreboot_addr, 0x7000); //Bootblock *(vu32 *)CBFS_DRAM_EN_ADDR = CBFS_DRAM_MAGIC; } } bool is_ipl_updated(void *buf, char *path, bool force) { ipl_ver_meta_t *update_ft = (ipl_ver_meta_t *)(buf + PATCHED_RELOC_SZ + sizeof(boot_cfg_t)); bool magic_valid = update_ft->magic == ipl_ver.magic; bool force_update = force && !magic_valid; bool is_valid_old = magic_valid && (byte_swap_32(update_ft->version) < byte_swap_32(ipl_ver.version)); // Check if newer version. if (!force && magic_valid) if (byte_swap_32(update_ft->version) > byte_swap_32(ipl_ver.version)) return false; // Update if old or broken. if (force_update || is_valid_old) { FIL fp; volatile reloc_meta_t *reloc = (reloc_meta_t *)(IPL_LOAD_ADDR + RELOC_META_OFF); boot_cfg_t *tmp_cfg = malloc(sizeof(boot_cfg_t)); memset(tmp_cfg, 0, sizeof(boot_cfg_t)); f_open(&fp, path, FA_WRITE | FA_CREATE_ALWAYS); f_write(&fp, (u8 *)reloc->start, reloc->end - reloc->start, NULL); // Write needed tag in case injected ipl uses old versioning. f_write(&fp, "ICTC49", 6, NULL); // Reset boot storage configuration. f_lseek(&fp, PATCHED_RELOC_SZ); f_write(&fp, tmp_cfg, sizeof(boot_cfg_t), NULL); f_close(&fp); free(tmp_cfg); } return true; } int launch_payload(char *path, bool update) { if (!update) gfx_clear_grey(0x1B); gfx_con_setpos(0, 0); if (sd_mount()) { FIL fp; if (f_open(&fp, path, FA_READ)) { gfx_con.mute = 0; EPRINTFARGS("Payload file is missing!\n(%s)", path); goto out; } // Read and copy the payload to our chosen address void *buf; u32 size = f_size(&fp); if (size < 0x30000) buf = (void *)RCM_PAYLOAD_ADDR; else { coreboot_addr = (void *)(COREBOOT_END_ADDR - size); buf = coreboot_addr; } if (f_read(&fp, buf, size, NULL)) { f_close(&fp); goto out; } f_close(&fp); if (update && is_ipl_updated(buf, path, false)) goto out; sd_end(); if (size < 0x30000) { if (update) memcpy((u8 *)(RCM_PAYLOAD_ADDR + PATCHED_RELOC_SZ), &b_cfg, sizeof(boot_cfg_t)); // Transfer boot cfg. else reloc_patcher(PATCHED_RELOC_ENTRY, EXT_PAYLOAD_ADDR, ALIGN(size, 0x10)); hw_reinit_workaround(false, byte_swap_32(*(u32 *)(buf + size - sizeof(u32)))); } else { reloc_patcher(PATCHED_RELOC_ENTRY, EXT_PAYLOAD_ADDR, 0x7000); hw_reinit_workaround(true, 0); } // Some cards (Sandisk U1), do not like a fast power cycle. Wait min 100ms. sdmmc_storage_init_wait_sd(); void (*ext_payload_ptr)() = (void *)EXT_PAYLOAD_ADDR; void (*update_ptr)() = (void *)RCM_PAYLOAD_ADDR; // Launch our payload. if (!update) (*ext_payload_ptr)(); else { EMC(EMC_SCRATCH0) |= EMC_HEKA_UPD; (*update_ptr)(); } } out: if (!update) sd_end(); return 1; } void auto_launch_update() { if (EMC(EMC_SCRATCH0) & EMC_HEKA_UPD) EMC(EMC_SCRATCH0) &= ~EMC_HEKA_UPD; else if (sd_mount()) { if (!f_stat("bootloader/update.bin", NULL)) launch_payload("bootloader/update.bin", true); } } void launch_tools() { u8 max_entries = 61; char *filelist = NULL; char *file_sec = NULL; char *dir = NULL; ment_t *ments = (ment_t *)malloc(sizeof(ment_t) * (max_entries + 3)); gfx_clear_grey(0x1B); gfx_con_setpos(0, 0); if (sd_mount()) { dir = (char *)malloc(256); memcpy(dir, "bootloader/payloads", 20); filelist = dirlist(dir, NULL, false, false); u32 i = 0; if (filelist) { // Build configuration menu. ments[0].type = MENT_BACK; ments[0].caption = "Back"; ments[1].type = MENT_CHGLINE; while (true) { if (i > max_entries || !filelist[i * 256]) break; ments[i + 2].type = INI_CHOICE; ments[i + 2].caption = &filelist[i * 256]; ments[i + 2].data = &filelist[i * 256]; i++; } } if (i > 0) { memset(&ments[i + 2], 0, sizeof(ment_t)); menu_t menu = { ments, "Choose a file to launch", 0, 0 }; file_sec = (char *)tui_do_menu(&menu); if (!file_sec) { free(ments); free(dir); free(filelist); sd_end(); return; } } else EPRINTF("No payloads or modules found."); free(ments); free(filelist); } else { free(ments); goto out; } if (file_sec) { memcpy(dir + strlen(dir), "/", 2); memcpy(dir + strlen(dir), file_sec, strlen(file_sec) + 1); launch_payload(dir, false); EPRINTF("Failed to launch payload."); } out: sd_end(); free(dir); btn_wait(); } void ini_list_launcher() { u8 max_entries = 61; char *payload_path = NULL; char *emummc_path = NULL; ini_sec_t *cfg_sec = NULL; LIST_INIT(ini_list_sections); gfx_clear_grey(0x1B); gfx_con_setpos(0, 0); if (sd_mount()) { if (ini_parse(&ini_list_sections, "bootloader/ini", true)) { // Build configuration menu. ment_t *ments = (ment_t *)malloc(sizeof(ment_t) * (max_entries + 3)); ments[0].type = MENT_BACK; ments[0].caption = "Back"; ments[1].type = MENT_CHGLINE; u32 i = 2; LIST_FOREACH_ENTRY(ini_sec_t, ini_sec, &ini_list_sections, link) { if (!strcmp(ini_sec->name, "config") || ini_sec->type == INI_COMMENT || ini_sec->type == INI_NEWLINE) continue; ments[i].type = ini_sec->type; ments[i].caption = ini_sec->name; ments[i].data = ini_sec; if (ini_sec->type == MENT_CAPTION) ments[i].color = ini_sec->color; i++; if ((i - 1) > max_entries) break; } if (i > 2) { memset(&ments[i], 0, sizeof(ment_t)); menu_t menu = { ments, "Launch ini configurations", 0, 0 }; cfg_sec = (ini_sec_t *)tui_do_menu(&menu); if (cfg_sec) { u32 non_cfg = 1; for (int j = 2; j < i; j++) { if (ments[j].type != INI_CHOICE) non_cfg++; if (ments[j].data == cfg_sec) { b_cfg.boot_cfg = BOOT_CFG_FROM_LAUNCH; b_cfg.autoboot = j - non_cfg; b_cfg.autoboot_list = 1; break; } } } payload_path = ini_check_payload_section(cfg_sec); if (cfg_sec) { LIST_FOREACH_ENTRY(ini_kv_t, kv, &cfg_sec->kvs, link) { if (!strcmp("emummc_force_disable", kv->key)) h_cfg.emummc_force_disable = atoi(kv->val); else if (!strcmp("emupath", kv->key)) emummc_path = kv->val; } } if (emummc_path && !emummc_set_path(emummc_path)) { EPRINTF("emupath is wrong!"); goto wrong_emupath; } if (cfg_sec && !payload_path) check_sept(cfg_sec); if (!cfg_sec) { free(ments); return; } } else EPRINTF("No extra configs found."); free(ments); } else EPRINTF("Could not find any ini\nin bootloader/ini!"); } if (!cfg_sec) goto out; if (payload_path) { launch_payload(payload_path, false); EPRINTF("Failed to launch payload."); free(payload_path); } else if (!hos_launch(cfg_sec)) { wrong_emupath: EPRINTF("Failed to launch HOS."); if (emummc_path) { sd_mount(); emummc_load_cfg(); } } out: btn_wait(); } void launch_firmware() { u8 max_entries = 61; char *payload_path = NULL; char *emummc_path = NULL; ini_sec_t *cfg_sec = NULL; LIST_INIT(ini_sections); gfx_clear_grey(0x1B); gfx_con_setpos(0, 0); if (sd_mount()) { if (ini_parse(&ini_sections, "bootloader/hekate_ipl.ini", false)) { // Build configuration menu. ment_t *ments = (ment_t *)malloc(sizeof(ment_t) * (max_entries + 6)); ments[0].type = MENT_BACK; ments[0].caption = "Back"; ments[1].type = MENT_CHGLINE; ments[2].type = MENT_HANDLER; ments[2].caption = "Payloads..."; ments[2].handler = launch_tools; ments[3].type = MENT_HANDLER; ments[3].caption = "More configs..."; ments[3].handler = ini_list_launcher; ments[4].type = MENT_CHGLINE; u32 i = 5; LIST_FOREACH_ENTRY(ini_sec_t, ini_sec, &ini_sections, link) { if (!strcmp(ini_sec->name, "config") || ini_sec->type == INI_COMMENT || ini_sec->type == INI_NEWLINE) continue; ments[i].type = ini_sec->type; ments[i].caption = ini_sec->name; ments[i].data = ini_sec; if (ini_sec->type == MENT_CAPTION) ments[i].color = ini_sec->color; i++; if ((i - 4) > max_entries) break; } if (i < 6) { ments[i].type = MENT_CAPTION; ments[i].caption = "No main configs found..."; ments[i].color = 0xFFFFDD00; i++; } memset(&ments[i], 0, sizeof(ment_t)); menu_t menu = { ments, "Launch configurations", 0, 0 }; cfg_sec = (ini_sec_t *)tui_do_menu(&menu); if (cfg_sec) { u8 non_cfg = 4; for (int j = 5; j < i; j++) { if (ments[j].type != INI_CHOICE) non_cfg++; if (ments[j].data == cfg_sec) { b_cfg.boot_cfg = BOOT_CFG_FROM_LAUNCH; b_cfg.autoboot = j - non_cfg; b_cfg.autoboot_list = 0; break; } } } payload_path = ini_check_payload_section(cfg_sec); if (cfg_sec) { LIST_FOREACH_ENTRY(ini_kv_t, kv, &cfg_sec->kvs, link) { if (!strcmp("emummc_force_disable", kv->key)) h_cfg.emummc_force_disable = atoi(kv->val); if (!strcmp("emupath", kv->key)) emummc_path = kv->val; } } if (emummc_path && !emummc_set_path(emummc_path)) { EPRINTF("emupath is wrong!"); goto wrong_emupath; } if (cfg_sec && !payload_path) check_sept(cfg_sec); if (!cfg_sec) { free(ments); sd_end(); return; } free(ments); } else EPRINTF("Could not open 'bootloader/hekate_ipl.ini'.\nMake sure it exists!"); } if (!cfg_sec) { gfx_puts("\nUsing default launch configuration...\n"); gfx_puts("\nPress POWER to Continue.\nPress VOL to go to the menu."); u32 btn = btn_wait(); if (!(btn & BTN_POWER)) goto out; } if (payload_path) { launch_payload(payload_path, false); EPRINTF("Failed to launch payload."); free(payload_path); } else if (!hos_launch(cfg_sec)) { wrong_emupath: EPRINTF("Failed to launch HOS."); if (emummc_path) { sd_mount(); emummc_load_cfg(); } } out: sd_end(); h_cfg.emummc_force_disable = false; btn_wait(); } #define NYX_VER_OFF 0x9C void nyx_load_run() { sd_mount(); u8 *nyx = sd_file_read("bootloader/sys/nyx.bin", NULL); if (!nyx) return; sd_end(); u32 expected_nyx_ver = ((NYX_VER_MJ + '0') << 24) | ((NYX_VER_MN + '0') << 16) | ((NYX_VER_HF + '0') << 8); u32 nyx_ver = byte_swap_32(*(u32 *)(nyx + NYX_VER_OFF)); gfx_clear_grey(0x1B); u8 *BOOTLOGO = (void *)malloc(0x4000); blz_uncompress_srcdest(BOOTLOGO_BLZ, SZ_BOOTLOGO_BLZ, BOOTLOGO, SZ_BOOTLOGO); gfx_set_rect_grey(BOOTLOGO, X_BOOTLOGO, Y_BOOTLOGO, 326, 544); free(BOOTLOGO); display_backlight_brightness(h_cfg.backlight, 1000); // Check if Nyx version is old. if (nyx_ver < expected_nyx_ver) { h_cfg.errors |= ERR_SYSOLD_NYX; gfx_con_setpos(0, 0); WPRINTF("Old Nyx GUI found! There will be dragons!\n"); WPRINTF("\nUpdate your bootloader folder!\n\n"); WPRINTF("Press any key..."); msleep(1000); btn_wait(); } nyx_str->info.errors = h_cfg.errors; nyx_str->cfg = 0; if (b_cfg.extra_cfg) { if (b_cfg.extra_cfg & EXTRA_CFG_NYX_DUMP) { b_cfg.extra_cfg &= ~(EXTRA_CFG_NYX_DUMP); nyx_str->cfg |= NYX_CFG_DUMP; } if (b_cfg.extra_cfg & EXTRA_CFG_NYX_BIS) { b_cfg.extra_cfg &= ~(EXTRA_CFG_NYX_BIS); nyx_str->cfg |= NYX_CFG_BIS; } if (b_cfg.extra_cfg & EXTRA_CFG_NYX_UMS) { b_cfg.extra_cfg &= ~(EXTRA_CFG_NYX_UMS); nyx_str->cfg |= NYX_CFG_UMS; nyx_str->cfg |= b_cfg.ums << 24; } } nyx_str->version = ipl_ver.version - 0x303030; // Convert ASCII to numbers. //memcpy((u8 *)nyx_str->irama, (void *)IRAM_BASE, 0x8000); volatile reloc_meta_t *reloc = (reloc_meta_t *)(IPL_LOAD_ADDR + RELOC_META_OFF); memcpy((u8 *)nyx_str->hekate, (u8 *)reloc->start, reloc->end - reloc->start); void (*nyx_ptr)() = (void *)nyx; bpmp_mmu_disable(); bpmp_clk_rate_set(BPMP_CLK_NORMAL); minerva_periodic_training(); // Some cards (Sandisk U1), do not like a fast power cycle. Wait min 100ms. sdmmc_storage_init_wait_sd(); (*nyx_ptr)(); } static ini_sec_t *get_ini_sec_from_id(ini_sec_t *ini_sec, char **bootlogoCustomEntry, char **emummc_path) { ini_sec_t *cfg_sec = NULL; LIST_FOREACH_ENTRY(ini_kv_t, kv, &ini_sec->kvs, link) { if (!strcmp("id", kv->key)) { if (b_cfg.id[0] && kv->val[0] && !strcmp(b_cfg.id, kv->val)) cfg_sec = ini_sec; else break; } if (!strcmp("logopath", kv->key)) *bootlogoCustomEntry = kv->val; if (!strcmp("emummc_force_disable", kv->key)) h_cfg.emummc_force_disable = atoi(kv->val); if (!strcmp("emupath", kv->key)) *emummc_path = kv->val; } if (!cfg_sec) { *bootlogoCustomEntry = NULL; *emummc_path = NULL; h_cfg.emummc_force_disable = false; } return cfg_sec; } static void _auto_launch_firmware() { if(b_cfg.extra_cfg & (EXTRA_CFG_NYX_DUMP | EXTRA_CFG_NYX_BIS)) { if (!h_cfg.sept_run) EMC(EMC_SCRATCH0) |= EMC_HEKA_UPD; check_sept(NULL); } else if (b_cfg.extra_cfg & EXTRA_CFG_NYX_UMS) EMC(EMC_SCRATCH0) |= EMC_HEKA_UPD; if (!h_cfg.sept_run) auto_launch_update(); u8 *BOOTLOGO = NULL; char *payload_path = NULL; char *emummc_path = NULL; u32 btn = 0; bool boot_from_id = (b_cfg.boot_cfg & BOOT_CFG_FROM_ID) && (b_cfg.boot_cfg & BOOT_CFG_AUTOBOOT_EN); if (boot_from_id) b_cfg.id[7] = 0; struct _bmp_data { u32 size; u32 size_x; u32 size_y; u32 offset; u32 pos_x; u32 pos_y; }; struct _bmp_data bmpData; bool bootlogoFound = false; char *bootlogoCustomEntry = NULL; if (!(b_cfg.boot_cfg & BOOT_CFG_FROM_LAUNCH)) gfx_con.mute = true; ini_sec_t *cfg_sec = NULL; LIST_INIT(ini_sections); LIST_INIT(ini_list_sections); if (sd_mount()) { if (f_stat("bootloader/hekate_ipl.ini", NULL)) create_config_entry(); if (ini_parse(&ini_sections, "bootloader/hekate_ipl.ini", false)) { u32 configEntry = 0; u32 boot_entry_id = 0; // Load configuration. LIST_FOREACH_ENTRY(ini_sec_t, ini_sec, &ini_sections, link) { // Skip other ini entries for autoboot. if (ini_sec->type == INI_CHOICE) { if (!strcmp(ini_sec->name, "config")) { configEntry = 1; LIST_FOREACH_ENTRY(ini_kv_t, kv, &ini_sec->kvs, link) { if (!strcmp("autoboot", kv->key)) h_cfg.autoboot = atoi(kv->val); else if (!strcmp("autoboot_list", kv->key)) h_cfg.autoboot_list = atoi(kv->val); else if (!strcmp("bootwait", kv->key)) h_cfg.bootwait = atoi(kv->val); else if (!strcmp("backlight", kv->key)) h_cfg.backlight = atoi(kv->val); else if (!strcmp("autohosoff", kv->key)) h_cfg.autohosoff = atoi(kv->val); else if (!strcmp("autonogc", kv->key)) h_cfg.autonogc = atoi(kv->val); else if (!strcmp("updater2p", kv->key)) h_cfg.updater2p = atoi(kv->val); else if (!strcmp("brand", kv->key)) { h_cfg.brand = malloc(strlen(kv->val) + 1); strcpy(h_cfg.brand, kv->val); } else if (!strcmp("tagline", kv->key)) { h_cfg.tagline = malloc(strlen(kv->val) + 1); strcpy(h_cfg.tagline, kv->val); } } boot_entry_id++; // Override autoboot, otherwise save it for a possbile sept run. if (b_cfg.boot_cfg & BOOT_CFG_AUTOBOOT_EN) { h_cfg.autoboot = b_cfg.autoboot; h_cfg.autoboot_list = b_cfg.autoboot_list; } else { b_cfg.autoboot = h_cfg.autoboot; b_cfg.autoboot_list = h_cfg.autoboot_list; } continue; } if (boot_from_id) cfg_sec = get_ini_sec_from_id(ini_sec, &bootlogoCustomEntry, &emummc_path); else if (h_cfg.autoboot == boot_entry_id && configEntry) { cfg_sec = ini_sec; LIST_FOREACH_ENTRY(ini_kv_t, kv, &cfg_sec->kvs, link) { if (!strcmp("logopath", kv->key)) bootlogoCustomEntry = kv->val; else if (!strcmp("emummc_force_disable", kv->key)) h_cfg.emummc_force_disable = atoi(kv->val); else if (!strcmp("emupath", kv->key)) emummc_path = kv->val; } } if (cfg_sec) break; boot_entry_id++; } } if (h_cfg.autohosoff && !(b_cfg.boot_cfg & BOOT_CFG_AUTOBOOT_EN)) check_power_off_from_hos(); if (h_cfg.autoboot_list || (boot_from_id && !cfg_sec)) { if (boot_from_id && cfg_sec) goto skip_list; cfg_sec = NULL; boot_entry_id = 1; bootlogoCustomEntry = NULL; if (ini_parse(&ini_list_sections, "bootloader/ini", true)) { LIST_FOREACH_ENTRY(ini_sec_t, ini_sec_list, &ini_list_sections, link) { if (ini_sec_list->type == INI_CHOICE) { if (!strcmp(ini_sec_list->name, "config")) continue; if (boot_from_id) cfg_sec = get_ini_sec_from_id(ini_sec_list, &bootlogoCustomEntry, &emummc_path); else if (h_cfg.autoboot == boot_entry_id) { h_cfg.emummc_force_disable = false; cfg_sec = ini_sec_list; LIST_FOREACH_ENTRY(ini_kv_t, kv, &cfg_sec->kvs, link) { if (!strcmp("logopath", kv->key)) bootlogoCustomEntry = kv->val; else if (!strcmp("emummc_force_disable", kv->key)) h_cfg.emummc_force_disable = atoi(kv->val); else if (!strcmp("emupath", kv->key)) emummc_path = kv->val; } } if (cfg_sec) break; boot_entry_id++; } } } } skip_list: // Add missing configuration entry. if (!configEntry) create_config_entry(); if (!cfg_sec) goto out; // No configurations or auto boot is disabled. } else goto out; // Can't load hekate_ipl.ini. } else goto out; u8 *bitmap = NULL; if (!(b_cfg.boot_cfg & BOOT_CFG_FROM_LAUNCH) && h_cfg.bootwait && !h_cfg.sept_run) { u32 fsize; if (bootlogoCustomEntry) // Check if user set custom logo path at the boot entry. bitmap = (u8 *)sd_file_read(bootlogoCustomEntry, &fsize); if (!bitmap) // Custom entry bootlogo not found, trying default custom one. bitmap = (u8 *)sd_file_read("bootloader/bootlogo.bmp", &fsize); if (bitmap) { // Get values manually to avoid unaligned access. bmpData.size = bitmap[2] | bitmap[3] << 8 | bitmap[4] << 16 | bitmap[5] << 24; bmpData.offset = bitmap[10] | bitmap[11] << 8 | bitmap[12] << 16 | bitmap[13] << 24; bmpData.size_x = bitmap[18] | bitmap[19] << 8 | bitmap[20] << 16 | bitmap[21] << 24; bmpData.size_y = bitmap[22] | bitmap[23] << 8 | bitmap[24] << 16 | bitmap[25] << 24; // Sanity check. if (bitmap[0] == 'B' && bitmap[1] == 'M' && bitmap[28] == 32 && // Only 32 bit BMPs allowed. bmpData.size_x <= 720 && bmpData.size_y <= 1280) { if (bmpData.size <= fsize && ((bmpData.size - bmpData.offset) < 0x400000)) { // Avoid unaligned access from BM 2-byte MAGIC and remove header. BOOTLOGO = (u8 *)malloc(0x400000); memcpy(BOOTLOGO, bitmap + bmpData.offset, bmpData.size - bmpData.offset); free(bitmap); // Center logo if res < 720x1280. bmpData.pos_x = (720 - bmpData.size_x) >> 1; bmpData.pos_y = (1280 - bmpData.size_y) >> 1; // Get background color from 1st pixel. if (bmpData.size_x < 720 || bmpData.size_y < 1280) gfx_clear_color(*(u32 *)BOOTLOGO); bootlogoFound = true; } } else free(bitmap); } // Render boot logo. if (bootlogoFound) { gfx_render_bmp_argb((u32 *)BOOTLOGO, bmpData.size_x, bmpData.size_y, bmpData.pos_x, bmpData.pos_y); } else { gfx_clear_grey(0x1B); BOOTLOGO = (void *)malloc(0x4000); blz_uncompress_srcdest(BOOTLOGO_BLZ, SZ_BOOTLOGO_BLZ, BOOTLOGO, SZ_BOOTLOGO); gfx_set_rect_grey(BOOTLOGO, X_BOOTLOGO, Y_BOOTLOGO, 326, 544); } free(BOOTLOGO); } if (b_cfg.boot_cfg & BOOT_CFG_FROM_LAUNCH) display_backlight_brightness(h_cfg.backlight, 0); else if (!h_cfg.sept_run && h_cfg.bootwait) display_backlight_brightness(h_cfg.backlight, 1000); // Wait before booting. If VOL- is pressed go into bootloader menu. if (!h_cfg.sept_run && !(b_cfg.boot_cfg & BOOT_CFG_FROM_LAUNCH)) { btn = btn_wait_timeout_single(h_cfg.bootwait * 1000, BTN_VOL_DOWN | BTN_SINGLE); if (btn & BTN_VOL_DOWN) goto out; } payload_path = ini_check_payload_section(cfg_sec); if (payload_path) { launch_payload(payload_path, false); free(payload_path); goto payload_error; } else { if (b_cfg.boot_cfg & BOOT_CFG_TO_EMUMMC) emummc_set_path(b_cfg.emummc_path); else if (emummc_path && !emummc_set_path(emummc_path)) { gfx_con.mute = false; EPRINTF("emupath is wrong!"); goto wrong_emupath; } check_sept(cfg_sec); hos_launch(cfg_sec); wrong_emupath: EPRINTF("\nFailed to launch HOS!"); if (emummc_path || b_cfg.boot_cfg & BOOT_CFG_TO_EMUMMC) { sd_mount(); emummc_load_cfg(); } payload_error: gfx_con.mute = 0; gfx_printf("\nPress any key...\n"); display_backlight_brightness(h_cfg.backlight, 1000); msleep(500); btn_wait(); } out: gfx_con.mute = false; // Clear boot reasons from binary. if (b_cfg.boot_cfg & (BOOT_CFG_FROM_ID | BOOT_CFG_TO_EMUMMC)) memset(b_cfg.xt_str, 0, sizeof(b_cfg.xt_str)); b_cfg.boot_cfg &= BOOT_CFG_SEPT_RUN; h_cfg.emummc_force_disable = false; nyx_load_run(); sd_end(); } static void _patched_rcm_protection() { sdmmc_storage_t storage; sdmmc_t sdmmc; u32 chip_id = (APB_MISC(APB_MISC_GP_HIDREV) >> 4) & 0xF; if (!h_cfg.rcm_patched || chip_id != GP_HIDREV_MAJOR_T210) return; // Check if AutoRCM is enabled and protect from a permanent brick. if (!sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_BUS_WIDTH_8, SDHCI_TIMING_MMC_HS400)) return; u8 *tempbuf = (u8 *)malloc(0x200); sdmmc_storage_set_mmc_partition(&storage, EMMC_BOOT0); u8 corr_mod_byte0; int i, sect = 0; if ((fuse_read_odm(4) & 3) != 3) corr_mod_byte0 = 0xF7; else corr_mod_byte0 = 0x37; for (i = 0; i < 4; i++) { sect = (0x200 + (0x4000 * i)) / NX_EMMC_BLOCKSIZE; sdmmc_storage_read(&storage, sect, 1, tempbuf); // If AutoRCM is enabled, disable it. if (tempbuf[0x10] != corr_mod_byte0) { tempbuf[0x10] = corr_mod_byte0; sdmmc_storage_write(&storage, sect, 1, tempbuf); } } free(tempbuf); sdmmc_storage_end(&storage); } #define EXCP_EN_ADDR 0x4003FFFC #define EXCP_MAGIC 0x30505645 // EVP0 #define EXCP_TYPE_ADDR 0x4003FFF8 #define EXCP_TYPE_RESET 0x545352 // RST #define EXCP_TYPE_UNDEF 0x464455 // UDF #define EXCP_TYPE_PABRT 0x54424150 // PABT #define EXCP_TYPE_DABRT 0x54424144 // DABT #define EXCP_LR_ADDR 0x4003FFF4 static void _show_errors() { u32 *excp_enabled = (u32 *)EXCP_EN_ADDR; u32 *excp_type = (u32 *)EXCP_TYPE_ADDR; u32 *excp_lr = (u32 *)EXCP_LR_ADDR; if (*excp_enabled == EXCP_MAGIC) h_cfg.errors |= ERR_EXCEPT_ENB; //! FIXME: Find a better way to identify if that scratch has proper data. if (0 && PMC(APBDEV_PMC_SCRATCH37) & PMC_SCRATCH37_KERNEL_PANIC_FLAG) { // Set error and clear flag. h_cfg.errors |= ERR_L4T_KERNEL; PMC(APBDEV_PMC_SCRATCH37) &= ~PMC_SCRATCH37_KERNEL_PANIC_FLAG; } if (h_cfg.errors) { gfx_clear_grey(0x1B); gfx_con_setpos(0, 0); display_backlight_brightness(150, 1000); if (h_cfg.errors & ERR_LIBSYS_LP0) WPRINTF("Missing LP0 (sleep mode) lib!\n"); if (h_cfg.errors & ERR_LIBSYS_MTC) WPRINTF("Missing or old Minerva lib!\n"); if (h_cfg.errors & ~(ERR_EXCEPT_ENB | ERR_L4T_KERNEL)) { WPRINTF("\nUpdate bootloader folder!\n\n"); } if (h_cfg.errors & ERR_EXCEPT_ENB) { WPRINTFARGS("An exception occurred (LR %08X):\n", *excp_lr); switch (*excp_type) { case EXCP_TYPE_RESET: WPRINTF("RST"); break; case EXCP_TYPE_UNDEF: WPRINTF("UNDEF"); break; case EXCP_TYPE_PABRT: WPRINTF("PABRT"); break; case EXCP_TYPE_DABRT: WPRINTF("DABRT"); break; } WPRINTF("\n"); // Clear the exception. *excp_enabled = 0; } if (h_cfg.errors & ERR_L4T_KERNEL) { WPRINTF("Panic occurred while running L4T.\n"); if (!sd_save_to_file((void *)PSTORE_ADDR, PSTORE_SZ, "L4T_panic.bin")) WPRINTF("PSTORE saved to L4T_panic.bin\n"); } WPRINTF("Press any key..."); msleep(1000); btn_wait(); } } static void _check_low_battery() { int enough_battery; int batt_volt = 3500; int charge_status = 0; bq24193_get_property(BQ24193_ChargeStatus, &charge_status); max17050_get_property(MAX17050_AvgVCELL, &batt_volt); enough_battery = charge_status ? 3250 : 3000; if (batt_volt > enough_battery) goto out; // Prepare battery icon resources. u8 *battery_res = malloc(ALIGN(SZ_BATTERY_EMPTY, 0x1000)); blz_uncompress_srcdest(BATTERY_EMPTY_BLZ, SZ_BATTERY_EMPTY_BLZ, battery_res, SZ_BATTERY_EMPTY); u8 *battery_icon = malloc(0x95A); // 21x38x3 u8 *charging_icon = malloc(0x2F4); // 21x12x3 u8 *no_charging_icon = calloc(0x2F4, 1); memcpy(charging_icon, battery_res, 0x2F4); memcpy(battery_icon, battery_res + 0x2F4, 0x95A); u32 battery_icon_y_pos = 1280 - 16 - Y_BATTERY_EMPTY_BATT; u32 charging_icon_y_pos = 1280 - 16 - Y_BATTERY_EMPTY_BATT - 12 - Y_BATTERY_EMPTY_CHRG; free(battery_res); charge_status = !charge_status; u32 timer = 0; bool screen_on = false; while (true) { bpmp_msleep(250); // Refresh battery stats. int current_charge_status = 0; bq24193_get_property(BQ24193_ChargeStatus, ¤t_charge_status); max17050_get_property(MAX17050_AvgVCELL, &batt_volt); enough_battery = current_charge_status ? 3250 : 3000; if (batt_volt > enough_battery) break; // Refresh charging icon. if (screen_on && (charge_status != current_charge_status)) { if (current_charge_status) gfx_set_rect_rgb(charging_icon, X_BATTERY_EMPTY, Y_BATTERY_EMPTY_CHRG, 16, charging_icon_y_pos); else gfx_set_rect_rgb(no_charging_icon, X_BATTERY_EMPTY, Y_BATTERY_EMPTY_CHRG, 16, charging_icon_y_pos); } // Check if it's time to turn off display. if (screen_on && timer < get_tmr_ms()) { if (!current_charge_status) { max77620_low_battery_monitor_config(true); power_off(); } display_end(); screen_on = false; } // Check if charging status changed or Power button was pressed. if ((charge_status != current_charge_status) || (btn_wait_timeout_single(0, BTN_POWER) & BTN_POWER)) { if (!screen_on) { display_init(); u32 *fb = display_init_framebuffer_pitch(); gfx_init_ctxt(fb, 720, 1280, 720); gfx_set_rect_rgb(battery_icon, X_BATTERY_EMPTY, Y_BATTERY_EMPTY_BATT, 16, battery_icon_y_pos); if (current_charge_status) gfx_set_rect_rgb(charging_icon, X_BATTERY_EMPTY, Y_BATTERY_EMPTY_CHRG, 16, charging_icon_y_pos); else gfx_set_rect_rgb(no_charging_icon, X_BATTERY_EMPTY, Y_BATTERY_EMPTY_CHRG, 16, charging_icon_y_pos); display_backlight_pwm_init(); display_backlight_brightness(100, 1000); screen_on = true; } timer = get_tmr_ms() + 15000; } // Check if forcefully continuing. if (btn_read_vol() == (BTN_VOL_UP | BTN_VOL_DOWN)) break; charge_status = current_charge_status; } display_end(); free(battery_icon); free(charging_icon); free(no_charging_icon); out: // Re enable Low Battery Monitor shutdown. max77620_low_battery_monitor_config(true); } static void _about() { static const char credits[] = "\nhekate (c) 2018 naehrwert, st4rk\n\n" "CTCaer mod (c) 2018 CTCaer\n" " ___________________________________________\n\n" "Thanks to: %kderrek, nedwill, plutoo,\n" " shuffle2, smea, thexyz, yellows8%k\n" " ___________________________________________\n\n" "Greetings to: fincs, hexkyz, SciresM,\n" " Shiny Quagsire, WinterMute\n" " ___________________________________________\n\n" "Open source and free packages used:\n\n" " - FatFs R0.13b,\n" " Copyright (c) 2018, ChaN\n\n" " - bcl-1.2.0,\n" " Copyright (c) 2003-2006, Marcus Geelnard\n\n" " - Atmosphere (Exo st/types, prc id patches),\n" " Copyright (c) 2018-2019, Atmosphere-NX\n\n" " - elfload,\n" " Copyright (c) 2014, Owen Shepherd\n" " Copyright (c) 2018, M4xw\n" " ___________________________________________\n\n"; static const char octopus[] = " %k___\n" " .-' `'.\n" " / \\\n" " | ;\n" " | | ___.--,\n" " _.._ |0) = (0) | _.---'`__.-( (_.\n" " __.--'`_.. '.__.\\ '--. \\_.-' ,.--'` `\"\"`\n" " ( ,.--'` ',__ /./; ;, '.__.'` __\n" " _`) ) .---.__.' / | |\\ \\__..--\"\" \"\"\"--.,_\n" " `---' .'.''-._.-'`_./ /\\ '. \\ _.--''````'''--._`-.__.'\n" " | | .' _.-' | | \\ \\ '. `----`\n" " \\ \\/ .' \\ \\ '. '-._)\n" " \\/ / \\ \\ `=.__`'-.\n" " / /\\ `) ) / / `\"\".`\\\n" " , _.-'.'\\ \\ / / ( ( / /\n" " `--'` ) ) .-'.' '.'. | (\n" " (/` ( (` ) ) '-; %k[switchbrew]%k\n" " ` '-; (-'%k"; gfx_clear_grey(0x1B); gfx_con_setpos(0, 0); gfx_printf(credits, 0xFF00CCFF, 0xFFCCCCCC); gfx_con.fntsz = 8; gfx_printf(octopus, 0xFF00CCFF, 0xFF00FFCC, 0xFF00CCFF, 0xFFCCCCCC); btn_wait(); } ment_t ment_options[] = { MDEF_BACK(), MDEF_CHGLINE(), MDEF_HANDLER("Auto boot", config_autoboot), MDEF_HANDLER("Boot delay", config_bootdelay), MDEF_HANDLER("Auto NoGC", config_nogc), MDEF_HANDLER("Auto HOS power off", config_auto_hos_poweroff), MDEF_HANDLER("Backlight", config_backlight), MDEF_END() }; menu_t menu_options = { ment_options, "Options", 0, 0 }; ment_t ment_cinfo[] = { MDEF_BACK(), MDEF_CHGLINE(), MDEF_CAPTION("---- SoC Info ----", 0xFF0AB9E6), MDEF_HANDLER("Ipatches & bootrom", bootrom_ipatches_info), MDEF_HANDLER("Fuses", print_fuseinfo), //MDEF_HANDLER("Print kfuse info", print_kfuseinfo), //MDEF_HANDLER("Print TSEC keys", print_tsec_key), MDEF_CHGLINE(), MDEF_CAPTION("-- Storage Info --", 0xFF0AB9E6), MDEF_HANDLER("eMMC", print_mmc_info), MDEF_HANDLER("SD Card", print_sdcard_info), MDEF_CHGLINE(), MDEF_CAPTION("------ Misc ------", 0xFF0AB9E6), MDEF_HANDLER("Battery", print_battery_info), MDEF_END() }; menu_t menu_cinfo = { ment_cinfo, "Console Info", 0, 0 }; ment_t ment_restore[] = { MDEF_BACK(), MDEF_CHGLINE(), MDEF_CAPTION("------ Full --------", 0xFF0AB9E6), MDEF_HANDLER("Restore eMMC BOOT0/1", restore_emmc_boot), MDEF_HANDLER("Restore eMMC RAW GPP", restore_emmc_rawnand), MDEF_CHGLINE(), MDEF_CAPTION("-- GPP Partitions --", 0xFF0AB9E6), MDEF_HANDLER("Restore GPP partitions", restore_emmc_gpp_parts), MDEF_END() }; menu_t menu_restore = { ment_restore, "Restore Options", 0, 0 }; ment_t ment_backup[] = { MDEF_BACK(), MDEF_CHGLINE(), MDEF_CAPTION("------ Full --------", 0xFF0AB9E6), MDEF_HANDLER("Backup eMMC BOOT0/1", dump_emmc_boot), MDEF_HANDLER("Backup eMMC RAW GPP", dump_emmc_rawnand), MDEF_CHGLINE(), MDEF_CAPTION("-- GPP Partitions --", 0xFF0AB9E6), MDEF_HANDLER("Backup eMMC SYS", dump_emmc_system), MDEF_HANDLER("Backup eMMC USER", dump_emmc_user), MDEF_END() }; menu_t menu_backup = { ment_backup, "Backup Options", 0, 0 }; ment_t ment_tools[] = { MDEF_BACK(), MDEF_CHGLINE(), //MDEF_CAPTION("-- Backup & Restore --", 0xFF0AB9E6), //MDEF_MENU("Backup", &menu_backup), //MDEF_MENU("Restore", &menu_restore), //MDEF_CHGLINE(), //MDEF_CAPTION("-------- Misc --------", 0xFF0AB9E6), //MDEF_HANDLER("Dump package1/2", dump_packages12), //MDEF_HANDLER("Fix archive bit (except Nintendo)", fix_sd_all_attr), //MDEF_HANDLER("Fix archive bit (Nintendo only)", fix_sd_nin_attr), //MDEF_HANDLER("Fix fuel gauge configuration", fix_fuel_gauge_configuration), //MDEF_HANDLER("Reset all battery cfg", reset_pmic_fuel_gauge_charger_config), //MDEF_CHGLINE(), MDEF_CAPTION("-------- Other -------", 0xFFFFDD00), MDEF_HANDLER("AutoRCM", menu_autorcm), MDEF_END() }; menu_t menu_tools = { ment_tools, "Tools", 0, 0 }; ment_t ment_top[] = { MDEF_HANDLER("Launch", launch_firmware), //MDEF_MENU("Options", &menu_options), MDEF_CAPTION("---------------", 0xFF444444), MDEF_MENU("Tools", &menu_tools), MDEF_MENU("Console info", &menu_cinfo), MDEF_CAPTION("---------------", 0xFF444444), MDEF_HANDLER("Reboot (Normal)", reboot_normal), MDEF_HANDLER("Reboot (RCM)", reboot_rcm), MDEF_HANDLER("Power off", power_off), MDEF_CAPTION("---------------", 0xFF444444), MDEF_HANDLER("About", _about), MDEF_END() }; menu_t menu_top = { ment_top, "hekate - CTCaer mod v5.3.1", 0, 0 }; extern void pivot_stack(u32 stack_top); void ipl_main() { // Do initial HW configuration. This is compatible with consecutive reruns without a reset. hw_init(); // Pivot the stack so we have enough space. pivot_stack(IPL_STACK_TOP); // Tegra/Horizon configuration goes to 0x80000000+, package2 goes to 0xA9800000, we place our heap in between. heap_init(IPL_HEAP_START); #ifdef DEBUG_UART_PORT uart_send(DEBUG_UART_PORT, (u8 *)"hekate: Hello!\r\n", 16); uart_wait_idle(DEBUG_UART_PORT, UART_TX_IDLE); #endif // Check if battery is enough. _check_low_battery(); // Set bootloader's default configuration. set_default_configuration(); sd_mount(); // Save sdram lp0 config. if (!ianos_loader("bootloader/sys/libsys_lp0.bso", DRAM_LIB, (void *)sdram_get_params_patched())) h_cfg.errors |= ERR_LIBSYS_LP0; // Train DRAM and switch to max frequency. if (minerva_init()) h_cfg.errors |= ERR_LIBSYS_MTC; display_init(); u32 *fb = display_init_framebuffer_pitch(); gfx_init_ctxt(fb, 720, 1280, 720); gfx_con_init(); display_backlight_pwm_init(); //display_backlight_brightness(h_cfg.backlight, 1000); // Overclock BPMP. bpmp_clk_rate_set(BPMP_CLK_DEFAULT_BOOST); // Check if we had a panic while in CFW. secmon_exo_check_panic(); // Check if RCM is patched and protect from a possible brick. _patched_rcm_protection(); // Load emuMMC configuration from SD. emummc_load_cfg(); // Show library errors. _show_errors(); // Load saved configuration and auto boot if enabled. _auto_launch_firmware(); minerva_change_freq(FREQ_800); while (true) tui_do_menu(&menu_top); // Halt BPMP if we managed to get out of execution. while (true) bpmp_halt(); }