/* * Copyright (c) 2018 naehrwert * * Copyright (c) 2018-2022 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 "hos/hos.h" #include #include #include #include "frontend/fe_emmc_tools.h" #include "frontend/gui.h" #ifdef MENU_LOGO_ENABLE u8 *Kc_MENU_LOGO; #endif //MENU_LOGO_ENABLE nyx_config n_cfg; hekate_config h_cfg; const volatile ipl_ver_meta_t __attribute__((section ("._ipl_version"))) ipl_ver = { .magic = NYX_MAGIC, .version = (NYX_VER_MJ + '0') | ((NYX_VER_MN + '0') << 8) | ((NYX_VER_HF + '0') << 16), .rsvd0 = 0, .rsvd1 = 0 }; volatile nyx_storage_t *nyx_str = (nyx_storage_t *)NYX_STORAGE_ADDR; volatile boot_cfg_t *b_cfg; char *emmcsn_path_impl(char *path, char *sub_dir, char *filename, sdmmc_storage_t *storage) { static char emmc_sn[9] = {0}; // Check if eMMC S/N storage has valid data and skip parsing in that case. if (emmc_sn[0] && strcmp(emmc_sn, "00000000")) goto create_dir; // Get actual eMMC S/N. if (!storage) { if (!emmc_initialize(false)) strcpy(emmc_sn, "00000000"); else { itoa(emmc_storage.cid.serial, emmc_sn, 16); sdmmc_storage_end(&emmc_storage); } } else itoa(storage->cid.serial, emmc_sn, 16); create_dir: // Check if only eMMC S/N was requested. if (!path) return emmc_sn; // Create main folder. strcpy(path, "backup"); f_mkdir(path); // Create eMMC S/N folder. strcat(path, "/"); strcat(path, emmc_sn); f_mkdir(path); // Create sub folder if defined. Dir slash must be included. strcat(path, sub_dir); // Can be a null-terminator. if (strlen(sub_dir)) f_mkdir(path); // Add filename. strcat(path, "/"); strcat(path, filename); // Can be a null-terminator. return emmc_sn; } // 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 *)nyx_str->hekate, 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; } } lv_res_t launch_payload(lv_obj_t *list) { const char *filename = lv_list_get_btn_text(list); if (!filename || !filename[0]) goto out; char path[128]; strcpy(path,"bootloader/payloads/"); strcat(path, filename); if (!sd_mount()) goto out; FIL fp; if (f_open(&fp, path, FA_READ)) { 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 (h_cfg.t210b01) { f_close(&fp); EPRINTF("Coreboot not allowed on Mariko!"); goto out; } } if (f_read(&fp, buf, size, NULL)) { f_close(&fp); goto out; } f_close(&fp); sd_end(); if (size < 0x30000) { 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); } void (*ext_payload_ptr)() = (void *)EXT_PAYLOAD_ADDR; // Some cards (Sandisk U1), do not like a fast power cycle. Wait min 100ms. sdmmc_storage_init_wait_sd(); // Launch our payload. (*ext_payload_ptr)(); out: sd_unmount(); return LV_RES_OK; } void load_saved_configuration() { LIST_INIT(ini_sections); LIST_INIT(ini_nyx_sections); if (!ini_parse(&ini_sections, "bootloader/hekate_ipl.ini", false)) goto skip_main_cfg_parse; // Load hekate configuration. LIST_FOREACH_ENTRY(ini_sec_t, ini_sec, &ini_sections, link) { // Only parse config section. if (ini_sec->type == INI_CHOICE && !strcmp(ini_sec->name, "config")) { 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); if (h_cfg.backlight <= 20) h_cfg.backlight = 30; } 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("bootprotect", kv->key)) h_cfg.bootprotect = atoi(kv->val); } break; } } skip_main_cfg_parse: if (!ini_parse(&ini_nyx_sections, "bootloader/nyx.ini", false)) return; // Load Nyx configuration. LIST_FOREACH_ENTRY(ini_sec_t, ini_sec, &ini_nyx_sections, link) { // Only parse config section. if (ini_sec->type == INI_CHOICE && !strcmp(ini_sec->name, "config")) { LIST_FOREACH_ENTRY(ini_kv_t, kv, &ini_sec->kvs, link) { if (!strcmp("themecolor", kv->key)) n_cfg.theme_color = atoi(kv->val); else if (!strcmp("entries5col", kv->key)) n_cfg.entries_5_columns = atoi(kv->val) == 1; else if (!strcmp("timeoff", kv->key)) n_cfg.timeoff = strtol(kv->val, NULL, 16); else if (!strcmp("homescreen", kv->key)) n_cfg.home_screen = atoi(kv->val); else if (!strcmp("verification", kv->key)) n_cfg.verification = atoi(kv->val); else if (!strcmp("umsemmcrw", kv->key)) n_cfg.ums_emmc_rw = atoi(kv->val) == 1; else if (!strcmp("jcdisable", kv->key)) n_cfg.jc_disable = atoi(kv->val) == 1; else if (!strcmp("jcforceright", kv->key)) n_cfg.jc_force_right = atoi(kv->val) == 1; else if (!strcmp("bpmpclock", kv->key)) n_cfg.bpmp_clock = strtol(kv->val, NULL, 10); } break; } } } #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) { gfx_clear_grey(0); gfx_con_setpos(0, 0); display_backlight_brightness(150, 1000); display_activate_console(); WPRINTFARGS("Nyx exception occurred (LR %08X):\n", *excp_lr); switch (*excp_type) { case EXCP_TYPE_RESET: WPRINTF("RESET"); break; case EXCP_TYPE_UNDEF: WPRINTF("UNDEF"); break; case EXCP_TYPE_PABRT: WPRINTF("PABRT"); break; case EXCP_TYPE_DABRT: WPRINTF("DABRT"); break; } gfx_puts("\n"); // Clear the exception. *excp_lr = 0; *excp_type = 0; *excp_enabled = 0; WPRINTF("Press any key..."); msleep(1500); btn_wait(); reload_nyx(); } } void nyx_init_load_res() { bpmp_mmu_enable(); bpmp_clk_rate_get(); // Set a modest clock for init. It will be restored later if possible. bpmp_clk_rate_set(BPMP_CLK_LOWER_BOOST); // Set bootloader's default configuration. set_default_configuration(); set_nyx_default_configuration(); // Reset new info if magic not correct. if (nyx_str->info.magic != NYX_NEW_INFO) { nyx_str->info.sd_init = 0; for (u32 i = 0; i < 3; i++) nyx_str->info.sd_errors[i] = 0; } // Clear info magic. nyx_str->info.magic = 0; // Set display id from previous initialization. display_set_decoded_panel_id(nyx_str->info.disp_id); // Initialize gfx console. gfx_init_ctxt((u32 *)LOG_FB_ADDRESS, 1280, 656, 656); gfx_con_init(); // Show exception errors if any. _show_errors(); sd_mount(); // Train DRAM and switch to max frequency. minerva_init(); load_saved_configuration(); // Initialize nyx cfg to lower clock for T210. // In case of lower binned SoC, this can help with hangs. if (!n_cfg.bpmp_clock) { n_cfg.bpmp_clock = h_cfg.t210b01 ? 1 : 2; // Set lower clock for T210. create_nyx_config_entry(false); n_cfg.bpmp_clock = h_cfg.t210b01 ? 1 : 0; // Restore for T210 and keep for T210B01. } // Restore clock to max. if (n_cfg.bpmp_clock < 2) bpmp_clk_rate_set(BPMP_CLK_DEFAULT_BOOST); FIL fp; if (!f_open(&fp, "bootloader/sys/res.pak", FA_READ)) { f_read(&fp, (void *)NYX_RES_ADDR, f_size(&fp), NULL); f_close(&fp); } // If no custom switch icon exists, load normal. if (f_stat("bootloader/res/icon_switch_custom.bmp", NULL)) icon_switch = bmp_to_lvimg_obj("bootloader/res/icon_switch.bmp"); else icon_switch = bmp_to_lvimg_obj("bootloader/res/icon_switch_custom.bmp"); // If no custom payload icon exists, load normal. if (f_stat("bootloader/res/icon_payload_custom.bmp", NULL)) icon_payload = bmp_to_lvimg_obj("bootloader/res/icon_payload.bmp"); else icon_payload = bmp_to_lvimg_obj("bootloader/res/icon_payload_custom.bmp"); // Load background resource if any. hekate_bg = bmp_to_lvimg_obj("bootloader/res/background.bmp"); sd_unmount(); } void ipl_main() { //Tegra/Horizon configuration goes to 0x80000000+, package2 goes to 0xA9800000, we place our heap in between. heap_init((void *)IPL_HEAP_START); b_cfg = (boot_cfg_t *)(nyx_str->hekate + 0x94); // Important: Preserve version header! __asm__ ("" : : "" (ipl_ver)); #ifdef DEBUG_UART_PORT #if DEBUG_UART_PORT == UART_B gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_SPIO); gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_GPIO); #endif #if DEBUG_UART_PORT == UART_C gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_GPIO); gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_SPIO); #endif pinmux_config_uart(DEBUG_UART_PORT); clock_enable_uart(DEBUG_UART_PORT); uart_init(DEBUG_UART_PORT, DEBUG_UART_BAUDRATE, UART_AO_TX_AO_RX); uart_invert(DEBUG_UART_PORT, DEBUG_UART_INVERT, UART_INVERT_TXD); uart_send(DEBUG_UART_PORT, (u8 *)"hekate-NYX: Hello!\r\n", 20); uart_wait_xfer(DEBUG_UART_PORT, UART_TX_IDLE); #endif // Initialize the rest of hw and load nyx's resources. nyx_init_load_res(); nyx_load_and_run(); // Halt BPMP if we managed to get out of execution. while (true) bpmp_halt(); }