Newer
Older
uwuboot / source / arm9 / main.c
/*
 *   This file is part of fastboot 3DS
 *   Copyright (C) 2017 derrek, profi200
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that 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 <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "mem_map.h"
#include "types.h"
#include "util.h"
#include "pxi.h"
#include "arm9/hid.h"
#include "arm9/hardware.h"
#include "arm9/interrupt.h"
#include "fatfs/ff.h"
#include "arm9/dev.h"
#include "arm9/fsutils.h"
#include "arm9/firm.h"
#include "arm9/config.h"
#include "arm9/timer.h"
#include "arm9/menu.h"
#include "arm9/main.h"
#include "arm9/start.h"
#include "cache.h"

static void initWifiFlash(void);
static u32  mount_fs(void);
static void screen_init();
static void unit_detect();
static void boot_env_detect();
static void fw_detect();
static bool loadSettings(int *mode);
static void checkSetVerboseMode();

int main(void)
{
	int mode = BootModeNormal;

	hardwareInit();

	debugHashCodeRoData();
	
	uiInit();
	
	checkSetVerboseMode();
	
	consoleSelect(&con_bottom);
	
	uiPrintIfVerbose("\x1B[32mGood morning\nHello !\e[0m\n\n");
	
	PXI_sendWord(PXI_CMD_FORBID_POWER_OFF);
	
	uiPrintIfVerbose("Detecting unit...\n");

	unit_detect();

	initWifiFlash();

	uiPrintIfVerbose("Filesystem init...\n");
	
	/* Try to read the settings file ASAP. */
	if(mount_fs() > 0) // We got at least 1 drive mounted
	{
		uiPrintIfVerbose("Loading settings...\n");
	
		if(loadSettings(&mode))
		{
			uiPrintIfVerbose("Trying to launch FIRM from settings...\n");
		
			switch(mode)
			{
				case BootModeQuick:
					screen_init();
					uiDrawSplashScreen();
					/* fallthrough */
				case BootModeQuiet:
					tryLoadFirmwareFromSettings(false);
					if(isFirmLoaded())
						goto finish_firmlaunch;
					/* else fallthrough */
				case BootModeNormal:
					screen_init();
					break;
				default:
					panic();
			}
		}
		else screen_init();
	}
	
	if(mode != BootModeQuick)
		uiDrawSplashScreen();

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

	boot_env_detect();

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

	fw_detect();

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

	TIMER_sleep(600);

	consoleClear();

	enter_menu(MENU_STATE_MAIN);

	
finish_firmlaunch:

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

	fsUnmountAll();
	
	uiPrintIfVerbose("Closing devices...\n");

	devs_close();

	hardwareDeinit();

	// TODO: Proper argc/v passing needs to be implemented.
	firm_launch(1, (const char**)(ITCM_KERNEL_MIRROR + 0x7470));

	return 0;
}

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

static void initWifiFlash(void)
{
	const char *const res_str[2] = {"\x1B[31m Failed!", "\x1B[32m OK!"};

	uiPrintIfVerbose(" Initializing Wifi flash...");
	bool flashRes = dev_flash->init();
	uiPrintIfVerbose("%s\x1B[0m\n", res_str[flashRes]);
	bootInfo.wififlash_status = flashRes;
}

static u32 mount_fs(void)
{
	FRESULT tmp;
	u32 res = 0;
	const char *const res_str[2] = {"\x1B[31m Failed!", "\x1B[32m OK!"};


	uiPrintIfVerbose(" Mounting SD card FAT FS...");
	tmp = f_mount(&sd_fs, "sdmc:", 1);
	bootInfo.sd_status = (tmp ? 0 : 2u);
	uiPrintIfVerbose("%s %d\x1B[0m\n", res_str[tmp == FR_OK], tmp);
	res |= (tmp ? 0 : 1u);


	uiPrintIfVerbose(" Mounting twln FS...");
	tmp = f_mount(&nand_twlnfs, "twln:", 1);
	uiPrintIfVerbose("%s %d\x1B[0m\n", res_str[tmp == FR_OK], tmp);
	res |= (tmp ? 0 : 1u<<1);

	uiPrintIfVerbose(" Mounting twlp FS...");
	tmp = f_mount(&nand_twlpfs, "twlp:", 1);
	uiPrintIfVerbose("%s %d\x1B[0m\n", res_str[tmp == FR_OK], tmp);
	res |= (tmp ? 0 : 1u<<2);

	uiPrintIfVerbose(" Mounting CTR NAND FS...");
	tmp = f_mount(&nand_fs, "nand:", 1);
	uiPrintIfVerbose("%s %d\x1B[0m\n", res_str[tmp == FR_OK], tmp);
	res |= (tmp ? 0 : 1u<<3);

	bootInfo.nand_status = res>>1;


	if(res != 0xFu && uiGetVerboseMode())
	{
		screen_init();
		TIMER_sleep(2000);
	}

	return res;
}

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

static void unit_detect()
{
	bootInfo.unitIsNew3DS = REG_PDN_MPCORE_CFG & 2;

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

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.bootEnv, boot_environment[boot_env]);
}

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

	if(!bootInfo.nand_status)
		uiPrintIfVerbose("\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();
	uiPrintIfVerbose("%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;
}

static void checkSetVerboseMode()
{
	u32 keys;
	
	hidScanInput();
	keys = hidKeysDown() & HID_KEY_MASK_ALL;
	if(keys == HID_VERBOSE_MODE_BUTTONS)
		uiSetVerboseMode(true);
}

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

noreturn void power_off_safe()
{
	uiClearConsoles();
	uiPrintIfVerbose("Attempting to turn off...\n");

	fsUnmountAll();
	devs_close();

	flushDCache();
	__asm__ __volatile__("msr cpsr_c, #0xDF" : :); // System mode, interrupts disabled

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

	while(1) waitForInterrupt();
}

noreturn void reboot_safe()
{
	uiClearConsoles();
	uiPrintIfVerbose("Attempting to reboot...\n");

	fsUnmountAll();
	devs_close();

	flushDCache();
	__asm__ __volatile__("msr cpsr_c, #0xDF" : :); // System mode, interrupts disabled

	PXI_sendWord(PXI_CMD_REBOOT);

	while(1) waitForInterrupt();
}