/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include hekate_config h_cfg; boot_cfg_t __attribute__((section ("._boot_cfg"))) b_cfg; volatile nyx_storage_t *nyx_str = (nyx_storage_t *)NYX_STORAGE_ADDR; // 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; } } int launch_payload(char *path, bool update) { 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); 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; } #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_SD_BOOT_EN) WPRINTF("Failed to init SD!\n"); 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); } 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_default_configuration(); // Mount SD Card. h_cfg.errors |= !sd_mount() ? ERR_SD_BOOT_EN : 0; // 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); // Show library errors. _show_errors(); // Failed to launch Nyx, unmount SD Card. sd_end(); minerva_change_freq(FREQ_800); if (sd_mount()) { if (f_stat("config/aio-switch-updater/atmosphere/fusee-secondary.bin", NULL) == FR_OK && f_unlink("atmosphere/fusee-secondary.bin") == FR_OK) { f_rename("config/aio-switch-updater/atmosphere/fusee-secondary.bin", "atmosphere/fusee-secondary.bin"); } if (f_stat("config/aio-switch-updater/sept/payload.bin", NULL) == FR_OK && f_unlink("sept/payload.bin") == FR_OK) { f_rename("config/aio-switch-updater/sept/payload.bin", "sept/payload.bin"); } } if (f_stat("bootloader/update.bin", NULL) == FR_OK) launch_payload("bootloader/update.bin", false); if (f_stat("atmosphere/reboot_payload.bin", NULL) == FR_OK) launch_payload("atmosphere/reboot_payload.bin", false); EPRINTF("Failed to launch payload."); // Halt BPMP if we managed to get out of execution. while (true) bpmp_halt(); }