Newer
Older
uwuboot / source / arm9 / main.c
@derrek derrek on 15 May 2017 7 KB reboot_safe, uiPrintInfo
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "mem_map.h"
#include "types.h"
#include "util.h"
#include "pxi.h"
#include "hid.h"
#include "arm9/loader_init.h"
#include "fatfs/ff.h"
#include "arm9/dev.h"
#include "arm9/firm.h"
#include "arm9/config.h"
#include "arm9/timer.h"
#include "arm9/menu.h"
#include "arm9/main.h"

static bool devs_init();
static bool fs_early_init();
static bool mount_fs();
static void screen_init();
static void unit_detect();
static void boot_env_detect();
static void fw_detect();
static bool loadSettings(int *mode);

int main(void)
{
	int mode;

	hardwareInit();

	hashCodeRoData();	// TODO: remove after debugging

	screen_init();
	
	uiDrawSplashScreen();

	uiInit();
	
	consoleSelect(&con_top);
	
	uiPrintInfo("\x1B[32mGood morning\nHello !\e[0m\n\n");
	
	PXI_sendWord(PXI_CMD_FORBID_POWER_OFF);
	
	uiPrintInfo("Early filesystem init...\n");
	
	/* Try to read the settings file ASAP. */
	if(fs_early_init())
	{
		uiPrintInfo("Loading settings...\n");
	
		if(loadSettings(&mode))
		{
			switch(mode)
			{
				case BootModeQuick:
					screen_init();
					/* fallthrough */
				case BootModeQuiet:
					TryLoadFirmwareFromSettings();
					if(isFirmLoaded())
						goto finish_firmlaunch;
					/* else fallthrough */
				case BootModeNormal:
					screen_init();
					break;
				default:
					panic();
			}
		}
		else screen_init();
	}
	
	uiPrintInfo("Initializing devices...\n");

	if(!devs_init()) screen_init();

	uiPrintInfo("Mounting filesystems...\n");

	if(!mount_fs()) screen_init();

	uiPrintInfo("Detecting unit...\n");

	unit_detect();

	uiPrintInfo("Detecting boot environment...\n");

	boot_env_detect();

	uiPrintInfo("Detecting firmware...\n");

	fw_detect();

	uiPrintInfo("Entering menu...\n");

	TIMER_sleep(1000);

	consoleClear();

	enter_menu(MENU_STATE_MAIN);

	
finish_firmlaunch:	

	uiPrintInfo("Unmounting FS...\n");

	unmount_fs();
	
	uiPrintInfo("Closing devices...\n");

	devs_close();

	return 0;
}

static bool devs_init()
{
	const char *res_str[2] = {"\x1B[31mFailed!", "\x1B[32mOK!"};
	bool sdRes, nandRes, wifiRes;

	uiPrintInfo(" Initializing SD card... ");
	uiPrintInfo("%s\e[0m\n", res_str[sdRes = dev_sdcard->init()]);
	uiPrintInfo(" Initializing raw NAND... ");
	uiPrintInfo("%s\e[0m\n", res_str[nandRes = dev_rawnand->init()]);
	uiPrintInfo(" Initializing Wifi flash... ");
	uiPrintInfo("%s\e[0m\n", res_str[wifiRes = dev_flash->init()]);

	bootInfo.sd_status = sdRes;
	bootInfo.nand_status = nandRes;
	bootInfo.wififlash_status = wifiRes;

	// atm only those boot options are possible, so if the init
	// for both failed, this is a fatal error.
	return sdRes && nandRes;
}

void devs_close()
{
	dev_sdcard->close();
	dev_decnand->close();
	dev_rawnand->close();
	dev_flash->close();
}

bool remount_nand_fs()
{
	bool res = true;

	res &= f_mount(&nand_twlnfs, "twln:", 1);
	res &= f_mount(&nand_twlpfs, "twlp:", 1);
	res &= f_mount(&nand_fs, "nand:", 1);

	return res;
}

void unmount_nand_fs()
{
	f_mount(NULL, "twln:", 1);
	f_mount(NULL, "twlp:", 1);
	f_mount(NULL, "nand:", 1);
}

// This only initializes and mounts SD card fs.
static bool fs_early_init()
{
	FRESULT res;
	const char *res_str[2] = {"\x1B[31mFailed!", "\x1B[32mOK!"};
	
	res = f_mount(&sd_fs, "sdmc:", 1);
	
	uiPrintInfo("%s\e[0m\n", res_str[res == FR_OK]);
	
	return res == FR_OK;
}

static bool mount_fs()
{
	FRESULT res[4];
	bool finalRes = true;
	const char *res_str[2] = {"\x1B[31mFailed!", "\x1B[32mOK!"};

	if(bootInfo.sd_status)
	{
		uiPrintInfo("Mounting SD card FAT FS... ");
		res[0] = f_mount(&sd_fs, "sdmc:", 1);
		if(res[0] == FR_OK) uiPrintInfo("%s\e[0m\n", res_str[1]);
		//else uiPrintInfo("%s ERROR 0x%d\e[0m\n", res_str[0], res[0]);
		finalRes &= res[0] == FR_OK;
	}
	else finalRes = false;

	if(bootInfo.nand_status)
	{
		uiPrintInfo("Mounting twln FS... ");
		res[1] = f_mount(&nand_twlnfs, "twln:", 1);
		if(res[1] == FR_OK) uiPrintInfo("%s\e[0m\n", res_str[1]);
		else uiPrintInfo("%s ERROR 0x%d\e[0m\n", res_str[0], res[1]);

		uiPrintInfo("Mounting twlp FS... ");
		res[2] = f_mount(&nand_twlpfs, "twlp:", 1);
		if(res[2] == FR_OK) uiPrintInfo("%s\e[0m\n", res_str[1]);
		else uiPrintInfo("%s ERROR 0x%d\e[0m\n", res_str[0], res[2]);

		uiPrintInfo("Mounting CTR NAND FAT FS... ");
		res[3] = f_mount(&nand_fs, "nand:", 1);
		if(res[3] == FR_OK) uiPrintInfo("%s\e[0m\n", res_str[1]);
		else uiPrintInfo("%s ERROR 0x%d\e[0m\n", res_str[0], res[3]);

		finalRes &= res[3] == res[2] == res[1] == FR_OK;
	}
	else finalRes = false;

	if(!finalRes)
		TIMER_sleep(2000);

	return finalRes;
}

bool ensure_mounted(const char *path)
{
	if(strncmp(path, "sdmc:", 5) == 0)
	{
		//if(f_chdrive("sdmc:") != FR_OK)
		{
			return f_mount(&sd_fs, "sdmc:", 1) == FR_OK;
		}
	}
	else if(strncmp(path, "nand:", 5) == 0)
	{
		//if(f_chdrive("nand:") != FR_OK)
		{
			return f_mount(&nand_fs, "nand:", 1) == FR_OK;
		}
	}
	
	return false;
}

void unmount_fs()
{
	f_mount(NULL, "sdmc:", 1);
	f_mount(NULL, "twln:", 1);
	f_mount(NULL, "twlp:", 1);
	f_mount(NULL, "nand:", 1);
}

static void screen_init()
{
	static bool screenInitDone;
	if(!screenInitDone)
	{
		screenInitDone = true;
		PXI_sendWord(PXI_CMD_ENABLE_LCDS);
		while(PXI_recvWord() != PXI_RPL_OK);
	}
}

#define PDN_REGS_BASE          (IO_MEM_ARM9_ARM11 + 0x40000)
#define REG_PDN_MPCORE_CLKCNT  *((vu16*)(PDN_REGS_BASE + 0x1300))

static void unit_detect()
{
	bootInfo.unit_is_new3ds = REG_PDN_MPCORE_CLKCNT != 0;

	sprintf(bootInfo.model, "%s 3DS", bootInfo.unit_is_new3ds ? "New" : "Original");
		
	uiPrintInfo("%s detected!\n", bootInfo.model);
}

#define CFG_REGS_BASE          (IO_MEM_ARM9_ONLY)
#define CFG_BOOTENV            *((vu32*)(CFG_REGS_BASE + 0x10000))
#define CFG_UNITINFO           *((vu8* )(CFG_REGS_BASE + 0x10010))

static void boot_env_detect()
{
	static const char *boot_environment [4]	=	{	"Cold boot",				// 0
													"Booted from Native FIRM",	// 1
													"Booted from <Unknown>",	// 2, etc
													"Booted from Legacy FIRM"	// 3
												};

	u32 boot_env = CFG_BOOTENV;
	if(boot_env > 3) boot_env = 2;
	
	sprintf(bootInfo.mode, "%s", CFG_UNITINFO != 0 ? "Dev" : "Retail");
	
	strcpy(bootInfo.boot_env, boot_environment[boot_env]);
}

static void fw_detect()
{
	u8 *nand_sector = (u8*)malloc(0x200);

	if(!bootInfo.nand_status)
		uiPrintInfo("\x1B[31mFailed!\e[0m\n");
	else
	{
		dev_decnand->read_sector(0x0B130000 >> 9, 1, nand_sector);
		// TODO: lookup...
		strcpy(bootInfo.fw_ver1, "Unknown");
		strcpy(bootInfo.fw_ver2, "Unknown");
	}

	free(nand_sector);
}

static bool loadSettings(int *mode)
{
	const char *res_str[2] = {"\x1B[31mFailed!", "\x1B[32mOK!"};
	
	bool success = loadConfigFile();
	uiPrintInfo("%s\e[0m\n", res_str[success]);

	if(success)
	{
		const int *mode_ptr = (const int *)configGetData(KBootMode);
		if(mode_ptr != NULL)
			 *mode = *mode_ptr;
		else *mode = BootModeNormal;
		return true;
	}

	*mode = BootModeNormal;

	return false;
}

#define PRNG_REGS_BASE  (IO_MEM_ARM9_ONLY + 0x11000)
#define REG_PRNG         ((vu32*)(PRNG_REGS_BASE))

u8 rng_get_byte()
{
	u32 tmp = REG_PRNG[0];
	return (u8)tmp;
}

void power_off_safe()
{
	uiClearConsoles();

	uiPrintInfo("Attempting to turn off...\n");

	unmount_fs();
	devs_close();
	// tell the arm11 we're done
	PXI_sendWord(PXI_CMD_POWER_OFF);

	for(;;);
}

void reboot_safe()
{
	wait(0x100000);
	
	uiClearConsoles();

	uiPrintInfo("Attempting to reboot...\n");

	unmount_fs();
	devs_close();
	
	PXI_sendWord(PXI_CMD_REBOOT);

	for(;;);
}