forked from Mirror/GodMode9
160 lines
4.9 KiB
C
160 lines
4.9 KiB
C
/*
|
|
Written by Wolfvak, specially sublicensed under the GPLv2
|
|
Read LICENSE for more details
|
|
*/
|
|
|
|
#include "common.h"
|
|
#include "fsinit.h"
|
|
#include "fsutil.h"
|
|
#include "language.h"
|
|
#include "qrcodegen.h"
|
|
#include "power.h"
|
|
#include "rtc.h"
|
|
#include "hid.h"
|
|
#include "ui.h"
|
|
#include "memmap.h"
|
|
|
|
#include <arm.h>
|
|
|
|
#define PC_DUMPRAD (0x10)
|
|
#define SP_DUMPLEN (0x80)
|
|
extern u32 __text_s, __text_e;
|
|
|
|
static bool sp_dumpable(u32 sp, u32 *sp_lower, u32 *sp_upper)
|
|
{
|
|
if ((sp >= __STACK_TOP) || (sp < (__STACK_TOP - __STACK_LEN)))
|
|
return false;
|
|
*sp_lower = sp;
|
|
*sp_upper = min(sp + SP_DUMPLEN, __STACK_TOP);
|
|
return true;
|
|
}
|
|
|
|
static bool pc_dumpable(u32 pc, u32 *pc_lower, u32 *pc_upper)
|
|
{
|
|
u32 code_start = (u32)(&__text_s), code_end = (u32)(&__text_e);
|
|
|
|
if ((pc >= code_end) || (pc < code_start))
|
|
return false;
|
|
|
|
*pc_lower = max(pc - PC_DUMPRAD, code_start);
|
|
*pc_upper = min(pc + PC_DUMPRAD, code_end);
|
|
return true;
|
|
}
|
|
|
|
#define XRQ_DUMPDATAFUNC(type, size) \
|
|
static unsigned XRQ_DumpData_##type(char *b, u32 s, u32 e) \
|
|
{ \
|
|
char *c = b; \
|
|
while(s<e) { \
|
|
b+=sprintf(b, "%08lX: ",s); \
|
|
type *dl = (type*)s; \
|
|
for (u32 i = 0; i < (16 / sizeof(type)); i++) { \
|
|
b+=sprintf(b, "%0" #size "lX ", (u32)dl[i]); \
|
|
} \
|
|
b+=sprintf(b, "\n"); \
|
|
s+=16; \
|
|
} \
|
|
return (unsigned)(b-c); \
|
|
}
|
|
XRQ_DUMPDATAFUNC(u8, 2)
|
|
XRQ_DUMPDATAFUNC(u16, 4)
|
|
XRQ_DUMPDATAFUNC(u32, 8)
|
|
|
|
|
|
static const char *XRQ_Name[] = {
|
|
"Reset", "Undefined", "SWI", "Prefetch Abort",
|
|
"Data Abort", "Reserved", "IRQ", "FIQ"
|
|
};
|
|
|
|
|
|
void XRQ_DumpRegisters(u32 xrq, u32 *regs)
|
|
{
|
|
u32 sp, sp_lower, sp_upper, pc, pc_lower, pc_upper;
|
|
char dumpstr[2048], *wstr = dumpstr;
|
|
|
|
DsTime dstime;
|
|
get_dstime(&dstime);
|
|
|
|
/* Dump registers */
|
|
wstr += sprintf(wstr, "Exception: %s (%lu)\n", XRQ_Name[xrq&7], xrq);
|
|
wstr += sprintf(wstr, FLAVOR " " VERSION "\n");
|
|
wstr += sprintf(wstr, "20%02lX-%02lX-%02lX %02lX:%02lX:%02lX\n \n",
|
|
(u32) dstime.bcd_Y, (u32) dstime.bcd_M, (u32) dstime.bcd_D,
|
|
(u32) dstime.bcd_h, (u32) dstime.bcd_m, (u32) dstime.bcd_s);
|
|
for (int i = 0; i < 16; i += 2) {
|
|
wstr += sprintf(wstr,
|
|
"R%02d: %08lX | R%02d: %08lX\n", i, regs[i], i+1, regs[i+1]);
|
|
}
|
|
wstr += sprintf(wstr, "CPSR: %08lX\n\n", regs[16]);
|
|
|
|
/* Output registers to main screen */
|
|
u32 draw_width = GetDrawStringWidth(dumpstr);
|
|
u32 draw_height = GetDrawStringHeight(dumpstr);
|
|
u32 draw_x = (SCREEN_WIDTH_MAIN - draw_width) / 2;
|
|
u32 draw_y = (SCREEN_HEIGHT - draw_height) / 2;
|
|
u32 draw_y_upd = draw_y + draw_height - 10;
|
|
|
|
ClearScreen(MAIN_SCREEN, COLOR_STD_BG);
|
|
DrawStringF(MAIN_SCREEN, draw_x, draw_y, COLOR_STD_FONT, COLOR_STD_BG, "%s", dumpstr);
|
|
|
|
/* Dump STACK */
|
|
sp = regs[13] & ~0xF;
|
|
if (sp_dumpable(sp, &sp_lower, &sp_upper)) {
|
|
wstr += sprintf(wstr, "Stack:\n");
|
|
wstr += XRQ_DumpData_u8(wstr, sp_lower, sp_upper);
|
|
wstr += sprintf(wstr, "\n");
|
|
}
|
|
|
|
/* Dump CODE */
|
|
pc = regs[15] & ~0xF;
|
|
if (pc_dumpable(pc, &pc_lower, &pc_upper)) {
|
|
wstr += sprintf(wstr, "Code:\n");
|
|
if (regs[16] & SR_THUMB) { // need to take Thumb code into account
|
|
wstr += XRQ_DumpData_u16(wstr, pc_lower, pc_upper);
|
|
} else {
|
|
wstr += XRQ_DumpData_u32(wstr, pc_lower, pc_upper);
|
|
}
|
|
}
|
|
|
|
/* Draw QR Code */
|
|
u8 qrcode[qrcodegen_BUFFER_LEN_MAX];
|
|
u8 temp[qrcodegen_BUFFER_LEN_MAX];
|
|
char tempstr[UTF_BUFFER_BYTESIZE(29)];
|
|
ResizeString(tempstr, STR_GENERATING_QR_CODE, 29, 29, false);
|
|
DrawString(MAIN_SCREEN, tempstr, draw_x, draw_y_upd, COLOR_STD_FONT, COLOR_STD_BG);
|
|
if (qrcodegen_encodeText(dumpstr, temp, qrcode, qrcodegen_Ecc_LOW,
|
|
qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true)) {
|
|
DrawQrCode(ALT_SCREEN, qrcode);
|
|
}
|
|
|
|
/* Reinitialize SD */
|
|
ResizeString(tempstr, STR_REINITIALIZING_SD_CARD, 29, 29, false);
|
|
DrawString(MAIN_SCREEN, tempstr, draw_x, draw_y_upd, COLOR_STD_FONT, COLOR_STD_BG);
|
|
while (!InitSDCardFS()) {
|
|
if (InputWait(1) & BUTTON_POWER) PowerOff();
|
|
DeinitSDCardFS();
|
|
}
|
|
|
|
|
|
/* Dump to SD */
|
|
char path[64];
|
|
snprintf(path, 64, "%s/exception_dump_%02lX%02lX%02lX%02lX%02lX%02lX.txt", OUTPUT_PATH,
|
|
(u32) dstime.bcd_Y, (u32) dstime.bcd_M, (u32) dstime.bcd_D,
|
|
(u32) dstime.bcd_h, (u32) dstime.bcd_m, (u32) dstime.bcd_s);
|
|
ResizeString(tempstr, STR_DUMPING_STATE_TO_SD_CARD, 29, 29, false);
|
|
DrawString(MAIN_SCREEN, tempstr, draw_x, draw_y_upd, COLOR_STD_FONT, COLOR_STD_BG);
|
|
FileSetData(path, dumpstr, wstr - dumpstr, 0, true);
|
|
|
|
/* Deinit SD */
|
|
DeinitSDCardFS();
|
|
|
|
/* Done, wait for user power off */
|
|
ResizeString(tempstr, STR_PRESS_POWER_TO_TURN_OFF, 29, 29, false);
|
|
DrawString(MAIN_SCREEN, tempstr, draw_x, draw_y_upd, COLOR_STD_FONT, COLOR_STD_BG);
|
|
while (!(InputWait(0) & BUTTON_POWER));
|
|
PowerOff();
|
|
|
|
/* We will not return */
|
|
return;
|
|
}
|