Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
CTCaer
GitHub Repository: CTCaer/hekate
Path: blob/master/bootloader/hos/secmon_exo.c
3694 views
1
/*
2
* Copyright (c) 2018-2026 CTCaer
3
* Copyright (c) 2019 Atmosphère-NX
4
*
5
* This program is free software; you can redistribute it and/or modify it
6
* under the terms and conditions of the GNU General Public License,
7
* version 2, as published by the Free Software Foundation.
8
*
9
* This program is distributed in the hope it will be useful, but WITHOUT
10
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12
* more details.
13
*
14
* You should have received a copy of the GNU General Public License
15
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16
*/
17
18
#include <string.h>
19
#include <stdlib.h>
20
21
#include <bdk.h>
22
23
#include "hos.h"
24
#include "../config.h"
25
#include <libs/fatfs/ff.h>
26
#include "../storage/emummc.h"
27
28
enum emuMMC_Type
29
{
30
emuMMC_None = 0,
31
emuMMC_Partition,
32
emuMMC_File,
33
emuMMC_MAX
34
};
35
36
/* "EFS0" */
37
#define EMUMMC_MAGIC 0x30534645
38
#define EMUMMC_FILE_PATH_MAX 0x80
39
40
typedef struct
41
{
42
u32 magic;
43
u32 type;
44
u32 id;
45
u32 fs_ver;
46
} emummc_base_config_t;
47
48
typedef struct
49
{
50
u64 start_sector;
51
} emummc_partition_config_t;
52
53
typedef struct
54
{
55
char path[EMUMMC_FILE_PATH_MAX];
56
} emummc_file_config_t;
57
58
typedef struct
59
{
60
emummc_base_config_t base_cfg;
61
union
62
{
63
emummc_partition_config_t partition_cfg;
64
emummc_file_config_t file_cfg;
65
};
66
char nintendo_path[EMUMMC_FILE_PATH_MAX];
67
} exo_emummc_config_t;
68
69
typedef struct _exo_cfg_t
70
{
71
u32 magic;
72
u32 fwno;
73
u32 flags[2];
74
u16 display_id;
75
u8 uart_port;
76
u8 uart_invert;
77
u32 uart_baudrate;
78
u32 rsvd1[2];
79
exo_emummc_config_t emummc_cfg;
80
} exo_cfg_t;
81
82
typedef struct _atm_meta_t
83
{
84
u32 magic;
85
u32 fwno;
86
} wb_cfg_t;
87
88
// Atmosphère reboot-to-fatal-error.
89
typedef struct _atm_fatal_error_ctx
90
{
91
u32 magic;
92
u32 error_desc;
93
u64 title_id;
94
union
95
{
96
u64 gprs[32];
97
struct
98
{
99
u64 _gprs[29];
100
u64 fp;
101
u64 lr;
102
u64 sp;
103
};
104
};
105
u64 pc;
106
u64 module_base;
107
u32 pstate;
108
u32 afsr0;
109
u32 afsr1;
110
u32 esr;
111
u64 far;
112
u64 report_identifier; // Normally just system tick.
113
u64 stack_trace_size;
114
u64 stack_dump_size;
115
u64 stack_trace[0x20];
116
u8 stack_dump[0x100];
117
u8 tls[0x100];
118
} atm_fatal_error_ctx;
119
120
#define ATM_FATAL_ERR_CTX_ADDR 0x4003E000
121
#define ATM_FATAL_MAGIC 0x30454641 // AFE0
122
123
#define ATM_EXO_FATAL_ADDR 0x80020000
124
#define ATM_EXO_FATAL_SIZE SZ_128K
125
126
#define ATM_WB_HEADER_OFF 0x244
127
#define ATM_WB_MAGIC 0x30544257 // WBT0
128
129
// Exosphère mailbox defines.
130
#define EXO_CFG_ADDR 0x8000F000
131
#define EXO_MAGIC_VAL 0x304F5845
132
#define EXO_FLAG_DBG_PRIV BIT(1)
133
#define EXO_FLAG_DBG_USER BIT(2)
134
#define EXO_FLAG_NO_USER_EXC BIT(3)
135
#define EXO_FLAG_USER_PMU BIT(4)
136
#define EXO_FLAG_CAL0_BLANKING BIT(5)
137
#define EXO_FLAG_CAL0_WRITES_SYS BIT(6)
138
#define EXO_FLAG_ENABLE_USB3 BIT(7)
139
#define EXO_FLAG_BC_MEM_MODE BIT(8)
140
141
#define EXO_FW_VER(mj, mn) (((mj) << 24) | ((mn) << 16))
142
143
void config_exosphere(launch_ctxt_t *ctxt, u32 warmboot_base)
144
{
145
u32 exo_fw_no;
146
u32 exo_flags = 0;
147
bool usb3_force = false;
148
bool user_debug = false;
149
bool bc_mem_mode = false;
150
bool cal0_blanking = false;
151
bool cal0_allow_writes_sys = false;
152
153
memset((exo_cfg_t *)EXO_CFG_ADDR, 0, sizeof(exo_cfg_t));
154
155
volatile exo_cfg_t *exo_cfg = (exo_cfg_t *)EXO_CFG_ADDR;
156
157
//! TODO: Replace current HOS version decoding (as it's bound to break in the future).
158
159
// Old exosphere target versioning.
160
if (ctxt->pkg1_id->mkey >= HOS_MKEY_VER_1210) // 12.1.0+
161
exo_fw_no = ctxt->pkg1_id->mkey + 4;
162
else if (ctxt->pkg1_id->fuses <= 3 || ctxt->pkg1_id->fuses >= 10) // 1.0.0 - 3.0.0, 8.1.0 - 12.0.3.
163
exo_fw_no = ctxt->pkg1_id->fuses;
164
else
165
exo_fw_no = ctxt->pkg1_id->fuses - 1; // 3.0.1 - 7.0.1, 8.0.x.
166
167
// Handle versions that change API and do not burn new fuse.
168
if (!memcmp(ctxt->pkg1_id->id, "20190314", 8) || // 8.0.x, same fuses with 7.0.1.
169
!memcmp(ctxt->pkg1_id->id, "20210129", 8) // 12.0.0, same fuses with 11.0.0.
170
)
171
exo_fw_no++;
172
173
// Set 12.1.0 specific revision.
174
if (ctxt->pkg1_id->mkey == HOS_MKEY_VER_1210)
175
ctxt->exo_ctx.hos_revision = 1;
176
177
// Feed old exosphere target versioning to new.
178
switch (exo_fw_no)
179
{
180
case 1 ... 4:
181
case 6:
182
exo_fw_no = EXO_FW_VER(exo_fw_no, 0);
183
break;
184
case 5:
185
exo_fw_no = EXO_FW_VER(5, ctxt->exo_ctx.hos_revision);
186
break;
187
case 7:
188
exo_fw_no = EXO_FW_VER(6, 2);
189
break;
190
case 8 ... 9:
191
exo_fw_no = EXO_FW_VER(exo_fw_no - 1, 0);
192
break;
193
case 10:
194
exo_fw_no = EXO_FW_VER(8, 1);
195
break;
196
case 11:
197
exo_fw_no = EXO_FW_VER(9, 0);
198
break;
199
case 12:
200
exo_fw_no = EXO_FW_VER(9, 1);
201
break;
202
case 13 ... 25: //!TODO: Update on API changes. 25: 22.0.0.
203
exo_fw_no = EXO_FW_VER(exo_fw_no - 3, ctxt->exo_ctx.hos_revision);
204
break;
205
}
206
207
// Parse exosphere.ini.
208
if (!ctxt->stock)
209
{
210
LIST_INIT(ini_exo_sections);
211
if (!ini_parse(&ini_exo_sections, "exosphere.ini", false))
212
{
213
LIST_FOREACH_ENTRY(ini_sec_t, ini_sec, &ini_exo_sections, link)
214
{
215
// Only parse exosphere section.
216
if (!(ini_sec->type == INI_CHOICE) || strcmp(ini_sec->name, "exosphere"))
217
continue;
218
219
LIST_FOREACH_ENTRY(ini_kv_t, kv, &ini_sec->kvs, link)
220
{
221
if (!strcmp("debugmode_user", kv->key))
222
user_debug = atoi(kv->val);
223
else if (!strcmp("log_port", kv->key))
224
exo_cfg->uart_port = atoi(kv->val);
225
else if (!strcmp("log_inverted", kv->key))
226
exo_cfg->uart_invert = atoi(kv->val);
227
else if (!strcmp("log_baud_rate", kv->key))
228
exo_cfg->uart_baudrate = atoi(kv->val);
229
else if (!strcmp("enable_mem_mode", kv->key))
230
bc_mem_mode = atoi(kv->val);
231
else if (emu_cfg.enabled && !h_cfg.emummc_force_disable)
232
{
233
if (!strcmp("blank_prodinfo_emummc", kv->key))
234
cal0_blanking = atoi(kv->val);
235
}
236
else
237
{
238
if (!strcmp("blank_prodinfo_sysmmc", kv->key))
239
cal0_blanking = atoi(kv->val);
240
else if (!strcmp("allow_writing_to_cal_sysmmc", kv->key))
241
cal0_allow_writes_sys = atoi(kv->val);
242
}
243
}
244
break;
245
}
246
}
247
248
// Parse usb mtim settings. Avoid parsing if it's overridden.
249
if (!ctxt->exo_ctx.usb3_force)
250
{
251
LIST_INIT(ini_sys_sections);
252
if (!ini_parse(&ini_sys_sections, "atmosphere/config/system_settings.ini", false))
253
{
254
LIST_FOREACH_ENTRY(ini_sec_t, ini_sec, &ini_sys_sections, link)
255
{
256
// Only parse usb section.
257
if (!(ini_sec->type == INI_CHOICE) || strcmp(ini_sec->name, "usb"))
258
continue;
259
260
LIST_FOREACH_ENTRY(ini_kv_t, kv, &ini_sec->kvs, link)
261
{
262
if (!strcmp("usb30_force_enabled", kv->key))
263
{
264
usb3_force = !strcmp("u8!0x1", kv->val);
265
break; // Only parse usb30_force_enabled key.
266
}
267
}
268
break;
269
}
270
}
271
}
272
}
273
274
// Private debug mode always on for CFW mode.
275
if (!ctxt->stock)
276
exo_flags |= EXO_FLAG_DBG_PRIV;
277
278
// Enable user debug.
279
if (user_debug)
280
exo_flags |= EXO_FLAG_DBG_USER;
281
282
// Disable proper failure handling.
283
if (ctxt->exo_ctx.no_user_exceptions)
284
exo_flags |= EXO_FLAG_NO_USER_EXC;
285
286
// Enable user access to PMU.
287
if (ctxt->exo_ctx.user_pmu)
288
exo_flags |= EXO_FLAG_USER_PMU;
289
290
// Enable Boot Config Memory Mode. Check if system_settings ini value is overridden. If not, check if enabled in ini.
291
if ((ctxt->exo_ctx.force_mem_mode && *ctxt->exo_ctx.force_mem_mode)
292
|| (!ctxt->exo_ctx.force_mem_mode && bc_mem_mode))
293
exo_flags |= EXO_FLAG_BC_MEM_MODE;
294
295
// Enable USB 3.0. Check if system_settings ini value is overridden. If not, check if enabled in ini.
296
if ((ctxt->exo_ctx.usb3_force && *ctxt->exo_ctx.usb3_force)
297
|| (!ctxt->exo_ctx.usb3_force && usb3_force))
298
exo_flags |= EXO_FLAG_ENABLE_USB3;
299
300
// Enable prodinfo blanking. Check if exo ini value is overridden. If not, check if enabled in exo ini.
301
if ((ctxt->exo_ctx.cal0_blank && *ctxt->exo_ctx.cal0_blank)
302
|| (!ctxt->exo_ctx.cal0_blank && cal0_blanking))
303
exo_flags |= EXO_FLAG_CAL0_BLANKING;
304
305
// Allow prodinfo writes. Check if exo ini value is overridden. If not, check if enabled in exo ini.
306
if ((ctxt->exo_ctx.cal0_allow_writes_sys && *ctxt->exo_ctx.cal0_allow_writes_sys)
307
|| (!ctxt->exo_ctx.cal0_allow_writes_sys && cal0_allow_writes_sys))
308
exo_flags |= EXO_FLAG_CAL0_WRITES_SYS;
309
310
// Set mailbox values.
311
exo_cfg->magic = EXO_MAGIC_VAL;
312
exo_cfg->fwno = exo_fw_no;
313
exo_cfg->flags[0] = exo_flags;
314
315
// If warmboot is lp0fw, add in RSA modulus.
316
volatile wb_cfg_t *wb_cfg = (wb_cfg_t *)(warmboot_base + ATM_WB_HEADER_OFF);
317
318
if (wb_cfg->magic == ATM_WB_MAGIC)
319
{
320
wb_cfg->fwno = exo_fw_no;
321
322
// Set warmboot binary rsa modulus.
323
pkg1_warmboot_rsa_mod(warmboot_base);
324
}
325
326
if (emu_cfg.enabled && !h_cfg.emummc_force_disable)
327
{
328
exo_cfg->emummc_cfg.base_cfg.magic = EMUMMC_MAGIC;
329
exo_cfg->emummc_cfg.base_cfg.type = emu_cfg.sector ? emuMMC_Partition : emuMMC_File;
330
exo_cfg->emummc_cfg.base_cfg.fs_ver = emu_cfg.fs_ver;
331
exo_cfg->emummc_cfg.base_cfg.id = emu_cfg.id;
332
333
if (emu_cfg.sector)
334
exo_cfg->emummc_cfg.partition_cfg.start_sector = emu_cfg.sector;
335
else
336
strcpy((char *)exo_cfg->emummc_cfg.file_cfg.path, emu_cfg.path);
337
338
if (!ctxt->stock && emu_cfg.nintendo_path && emu_cfg.nintendo_path[0])
339
strcpy((char *)exo_cfg->emummc_cfg.nintendo_path, emu_cfg.nintendo_path);
340
else
341
strcpy((char *)exo_cfg->emummc_cfg.nintendo_path, "Nintendo");
342
}
343
344
// Copy over exosphere fatal for Mariko.
345
if (h_cfg.t210b01)
346
{
347
memset((void *)ATM_EXO_FATAL_ADDR, 0, ATM_EXO_FATAL_SIZE);
348
if (ctxt->exofatal)
349
memcpy((void *)ATM_EXO_FATAL_ADDR, ctxt->exofatal, ctxt->exofatal_size);
350
351
// Set display id.
352
exo_cfg->display_id = display_get_decoded_panel_id();
353
}
354
355
#ifdef DEBUG_UART_PORT
356
// Ovverride logging parameters if set in compile time.
357
if (!ctxt->stock)
358
{
359
exo_cfg->uart_port = DEBUG_UART_PORT;
360
exo_cfg->uart_invert = DEBUG_UART_INVERT;
361
exo_cfg->uart_baudrate = DEBUG_UART_BAUDRATE;
362
}
363
#endif
364
}
365
366
static const char *get_error_desc(u32 error_desc)
367
{
368
switch (error_desc)
369
{
370
case 0x100:
371
return "IABRT"; // Instruction Abort.
372
case 0x101:
373
return "DABRT"; // Data Abort.
374
case 0x102:
375
return "IUA"; // Instruction Unaligned Access.
376
case 0x103:
377
return "DUA"; // Data Unaligned Access.
378
case 0x104:
379
return "UDF"; // Undefined Instruction.
380
case 0x106:
381
return "SYS"; // System Error.
382
case 0x301:
383
return "SVC"; // Bad arguments or unimplemented SVC.
384
case 0xF00:
385
return "KRNL"; // Kernel panic.
386
case 0xFFD:
387
return "SO"; // Stack Overflow.
388
case 0xFFE:
389
return "std::abort";
390
default:
391
return "UNK";
392
}
393
}
394
395
void secmon_exo_check_panic()
396
{
397
volatile atm_fatal_error_ctx *rpt = (atm_fatal_error_ctx *)ATM_FATAL_ERR_CTX_ADDR;
398
399
// Mask magic to maintain compatibility with any AFE version, thanks to additive struct members.
400
if ((rpt->magic & 0xF0FFFFFF) != ATM_FATAL_MAGIC)
401
return;
402
403
gfx_clear_grey(0x1B);
404
gfx_con_setpos(0, 0);
405
406
WPRINTF("Atmosphere panic occurred!\n\n");
407
WPRINTFARGS("Title ID: %08X%08X", (u32)((u64)rpt->title_id >> 32), (u32)rpt->title_id);
408
WPRINTFARGS("Error: %s (0x%x)\n", get_error_desc(rpt->error_desc), rpt->error_desc);
409
410
// Save context to the SD card.
411
char filepath[0x40];
412
f_mkdir("atmosphere/fatal_errors");
413
strcpy(filepath, "atmosphere/fatal_errors/report_");
414
itoa((u32)((u64)rpt->report_identifier >> 32), filepath + strlen(filepath), 16);
415
itoa((u32)(rpt->report_identifier), filepath + strlen(filepath), 16);
416
strcat(filepath, ".bin");
417
418
if (!sd_save_to_file((void *)rpt, sizeof(atm_fatal_error_ctx), filepath))
419
{
420
gfx_con.fntsz = 8;
421
WPRINTFARGS("Report saved to %s\n", filepath);
422
gfx_con.fntsz = 16;
423
}
424
425
// Change magic to invalid, to prevent double-display of error/bootlooping.
426
rpt->magic = 0;
427
428
gfx_printf("\n\nPress POWER to continue.\n");
429
gfx_con_setpos(0, 0);
430
431
display_backlight_brightness(150, 1000);
432
msleep(1000);
433
434
while (!(btn_wait() & BTN_POWER))
435
;
436
}
437
438