#include <string.h>
#include <stdlib.h>
#include <bdk.h>
#include "hos.h"
#include "../config.h"
#include <libs/fatfs/ff.h>
#include "../storage/emummc.h"
extern hekate_config h_cfg;
enum emuMMC_Type
{
emuMMC_None = 0,
emuMMC_Partition,
emuMMC_File,
emuMMC_MAX
};
#define EMUMMC_MAGIC 0x30534645
#define EMUMMC_FILE_PATH_MAX 0x80
typedef struct
{
u32 magic;
u32 type;
u32 id;
u32 fs_ver;
} emummc_base_config_t;
typedef struct
{
u64 start_sector;
} emummc_partition_config_t;
typedef struct
{
char path[EMUMMC_FILE_PATH_MAX];
} emummc_file_config_t;
typedef struct
{
emummc_base_config_t base_cfg;
union
{
emummc_partition_config_t partition_cfg;
emummc_file_config_t file_cfg;
};
char nintendo_path[EMUMMC_FILE_PATH_MAX];
} exo_emummc_config_t;
typedef struct _exo_cfg_t
{
u32 magic;
u32 fwno;
u32 flags[2];
u16 display_id;
u8 uart_port;
u8 uart_invert;
u32 uart_baudrate;
u32 rsvd1[2];
exo_emummc_config_t emummc_cfg;
} exo_cfg_t;
typedef struct _atm_meta_t
{
u32 magic;
u32 fwno;
} wb_cfg_t;
typedef struct _atm_fatal_error_ctx
{
u32 magic;
u32 error_desc;
u64 title_id;
union
{
u64 gprs[32];
struct
{
u64 _gprs[29];
u64 fp;
u64 lr;
u64 sp;
};
};
u64 pc;
u64 module_base;
u32 pstate;
u32 afsr0;
u32 afsr1;
u32 esr;
u64 far;
u64 report_identifier;
u64 stack_trace_size;
u64 stack_dump_size;
u64 stack_trace[0x20];
u8 stack_dump[0x100];
u8 tls[0x100];
} atm_fatal_error_ctx;
#define ATM_FATAL_ERR_CTX_ADDR 0x4003E000
#define ATM_FATAL_MAGIC 0x30454641
#define ATM_EXO_FATAL_ADDR 0x80020000
#define ATM_EXO_FATAL_SIZE SZ_128K
#define ATM_WB_HEADER_OFF 0x244
#define ATM_WB_MAGIC 0x30544257
#define EXO_CFG_ADDR 0x8000F000
#define EXO_MAGIC_VAL 0x304F5845
#define EXO_FLAG_DBG_PRIV BIT(1)
#define EXO_FLAG_DBG_USER BIT(2)
#define EXO_FLAG_NO_USER_EXC BIT(3)
#define EXO_FLAG_USER_PMU BIT(4)
#define EXO_FLAG_CAL0_BLANKING BIT(5)
#define EXO_FLAG_CAL0_WRITES_SYS BIT(6)
#define EXO_FLAG_ENABLE_USB3 BIT(7)
#define EXO_FW_VER(mj, mn) (((mj) << 24) | ((mn) << 16))
void config_exosphere(launch_ctxt_t *ctxt, u32 warmboot_base)
{
u32 exo_fw_no;
u32 exo_flags = 0;
bool usb3_force = false;
bool user_debug = false;
bool cal0_blanking = false;
bool cal0_allow_writes_sys = false;
memset((exo_cfg_t *)EXO_CFG_ADDR, 0, sizeof(exo_cfg_t));
volatile exo_cfg_t *exo_cfg = (exo_cfg_t *)EXO_CFG_ADDR;
if (ctxt->pkg1_id->kb >= HOS_KB_VERSION_1210)
exo_fw_no = ctxt->pkg1_id->kb + 4;
else if (ctxt->pkg1_id->fuses <= 3 || ctxt->pkg1_id->fuses >= 10)
exo_fw_no = ctxt->pkg1_id->fuses;
else
exo_fw_no = ctxt->pkg1_id->fuses - 1;
if (!memcmp(ctxt->pkg1_id->id, "20190314", 8) ||
!memcmp(ctxt->pkg1_id->id, "20210129", 8)
)
exo_fw_no++;
if (ctxt->pkg1_id->kb == HOS_KB_VERSION_1210)
ctxt->exo_ctx.hos_revision = 1;
switch (exo_fw_no)
{
case 1 ... 4:
case 6:
exo_fw_no = EXO_FW_VER(exo_fw_no, 0);
break;
case 5:
exo_fw_no = EXO_FW_VER(5, ctxt->exo_ctx.hos_revision);
break;
case 7:
exo_fw_no = EXO_FW_VER(6, 2);
break;
case 8 ... 9:
exo_fw_no = EXO_FW_VER(exo_fw_no - 1, 0);
break;
case 10:
exo_fw_no = EXO_FW_VER(8, 1);
break;
case 11:
exo_fw_no = EXO_FW_VER(9, 0);
break;
case 12:
exo_fw_no = EXO_FW_VER(9, 1);
break;
case 13 ... 23:
exo_fw_no = EXO_FW_VER(exo_fw_no - 3, ctxt->exo_ctx.hos_revision);
break;
}
if (!ctxt->stock)
{
LIST_INIT(ini_exo_sections);
if (ini_parse(&ini_exo_sections, "exosphere.ini", false))
{
LIST_FOREACH_ENTRY(ini_sec_t, ini_sec, &ini_exo_sections, link)
{
if (!(ini_sec->type == INI_CHOICE) || strcmp(ini_sec->name, "exosphere"))
continue;
LIST_FOREACH_ENTRY(ini_kv_t, kv, &ini_sec->kvs, link)
{
if (!strcmp("debugmode_user", kv->key))
user_debug = atoi(kv->val);
else if (!strcmp("log_port", kv->key))
exo_cfg->uart_port = atoi(kv->val);
else if (!strcmp("log_inverted", kv->key))
exo_cfg->uart_invert = atoi(kv->val);
else if (!strcmp("log_baud_rate", kv->key))
exo_cfg->uart_baudrate = atoi(kv->val);
else if (emu_cfg.enabled && !h_cfg.emummc_force_disable)
{
if (!strcmp("blank_prodinfo_emummc", kv->key))
cal0_blanking = atoi(kv->val);
}
else
{
if (!strcmp("blank_prodinfo_sysmmc", kv->key))
cal0_blanking = atoi(kv->val);
else if (!strcmp("allow_writing_to_cal_sysmmc", kv->key))
cal0_allow_writes_sys = atoi(kv->val);
}
}
break;
}
}
if (!ctxt->exo_ctx.usb3_force)
{
LIST_INIT(ini_sys_sections);
if (ini_parse(&ini_sys_sections, "atmosphere/config/system_settings.ini", false))
{
LIST_FOREACH_ENTRY(ini_sec_t, ini_sec, &ini_sys_sections, link)
{
if (!(ini_sec->type == INI_CHOICE) || strcmp(ini_sec->name, "usb"))
continue;
LIST_FOREACH_ENTRY(ini_kv_t, kv, &ini_sec->kvs, link)
{
if (!strcmp("usb30_force_enabled", kv->key))
{
usb3_force = !strcmp("u8!0x1", kv->val);
break;
}
}
break;
}
}
}
}
if (!ctxt->stock)
exo_flags |= EXO_FLAG_DBG_PRIV;
if (user_debug)
exo_flags |= EXO_FLAG_DBG_USER;
if (ctxt->exo_ctx.no_user_exceptions)
exo_flags |= EXO_FLAG_NO_USER_EXC;
if (ctxt->exo_ctx.user_pmu)
exo_flags |= EXO_FLAG_USER_PMU;
if ((ctxt->exo_ctx.usb3_force && *ctxt->exo_ctx.usb3_force)
|| (!ctxt->exo_ctx.usb3_force && usb3_force))
exo_flags |= EXO_FLAG_ENABLE_USB3;
if ((ctxt->exo_ctx.cal0_blank && *ctxt->exo_ctx.cal0_blank)
|| (!ctxt->exo_ctx.cal0_blank && cal0_blanking))
exo_flags |= EXO_FLAG_CAL0_BLANKING;
if ((ctxt->exo_ctx.cal0_allow_writes_sys && *ctxt->exo_ctx.cal0_allow_writes_sys)
|| (!ctxt->exo_ctx.cal0_allow_writes_sys && cal0_allow_writes_sys))
exo_flags |= EXO_FLAG_CAL0_WRITES_SYS;
exo_cfg->magic = EXO_MAGIC_VAL;
exo_cfg->fwno = exo_fw_no;
exo_cfg->flags[0] = exo_flags;
volatile wb_cfg_t *wb_cfg = (wb_cfg_t *)(warmboot_base + ATM_WB_HEADER_OFF);
if (wb_cfg->magic == ATM_WB_MAGIC)
{
wb_cfg->fwno = exo_fw_no;
pkg1_warmboot_rsa_mod(warmboot_base);
}
if (emu_cfg.enabled && !h_cfg.emummc_force_disable)
{
exo_cfg->emummc_cfg.base_cfg.magic = EMUMMC_MAGIC;
exo_cfg->emummc_cfg.base_cfg.type = emu_cfg.sector ? emuMMC_Partition : emuMMC_File;
exo_cfg->emummc_cfg.base_cfg.fs_ver = emu_cfg.fs_ver;
exo_cfg->emummc_cfg.base_cfg.id = emu_cfg.id;
if (emu_cfg.sector)
exo_cfg->emummc_cfg.partition_cfg.start_sector = emu_cfg.sector;
else
strcpy((char *)exo_cfg->emummc_cfg.file_cfg.path, emu_cfg.path);
if (!ctxt->stock && emu_cfg.nintendo_path && emu_cfg.nintendo_path[0])
strcpy((char *)exo_cfg->emummc_cfg.nintendo_path, emu_cfg.nintendo_path);
else
strcpy((char *)exo_cfg->emummc_cfg.nintendo_path, "Nintendo");
}
if (h_cfg.t210b01)
{
memset((void *)ATM_EXO_FATAL_ADDR, 0, ATM_EXO_FATAL_SIZE);
if (ctxt->exofatal)
memcpy((void *)ATM_EXO_FATAL_ADDR, ctxt->exofatal, ctxt->exofatal_size);
exo_cfg->display_id = display_get_decoded_panel_id();
}
#ifdef DEBUG_UART_PORT
if (!ctxt->stock)
{
exo_cfg->uart_port = DEBUG_UART_PORT;
exo_cfg->uart_invert = DEBUG_UART_INVERT;
exo_cfg->uart_baudrate = DEBUG_UART_BAUDRATE;
}
#endif
}
static const char *get_error_desc(u32 error_desc)
{
switch (error_desc)
{
case 0x100:
return "IABRT";
case 0x101:
return "DABRT";
case 0x102:
return "IUA";
case 0x103:
return "DUA";
case 0x104:
return "UDF";
case 0x106:
return "SYS";
case 0x301:
return "SVC";
case 0xF00:
return "KRNL";
case 0xFFD:
return "SO";
case 0xFFE:
return "std::abort";
default:
return "UNK";
}
}
#define HOS_PID_BOOT2 0x8
void secmon_exo_check_panic()
{
volatile atm_fatal_error_ctx *rpt = (atm_fatal_error_ctx *)ATM_FATAL_ERR_CTX_ADDR;
if ((rpt->magic & 0xF0FFFFFF) != ATM_FATAL_MAGIC)
return;
gfx_clear_grey(0x1B);
gfx_con_setpos(0, 0);
WPRINTF("Panic occurred while running Atmosphere.\n\n");
WPRINTFARGS("Title ID: %08X%08X", (u32)((u64)rpt->title_id >> 32), (u32)rpt->title_id);
WPRINTFARGS("Error: %s (0x%x)\n", get_error_desc(rpt->error_desc), rpt->error_desc);
if ((u32)rpt->title_id == HOS_PID_BOOT2)
WPRINTF("Mismatched Atmosphere files?\n");
char filepath[0x40];
f_mkdir("atmosphere/fatal_errors");
strcpy(filepath, "atmosphere/fatal_errors/report_");
itoa((u32)((u64)rpt->report_identifier >> 32), filepath + strlen(filepath), 16);
itoa((u32)(rpt->report_identifier), filepath + strlen(filepath), 16);
strcat(filepath, ".bin");
if (!sd_save_to_file((void *)rpt, sizeof(atm_fatal_error_ctx), filepath))
{
gfx_con.fntsz = 8;
WPRINTFARGS("Report saved to %s\n", filepath);
gfx_con.fntsz = 16;
}
rpt->magic = 0;
gfx_printf("\n\nPress POWER to continue.\n");
display_backlight_brightness(100, 1000);
msleep(1000);
while (!(btn_wait() & BTN_POWER))
;
display_backlight_brightness(0, 1000);
gfx_con_setpos(0, 0);
}