Newer
Older
uwuboot / source / arm9 / utils.c
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "types.h"
#include "arm9/console.h"
#include "mem_map.h"
#include "arm9/fatfs/ff.h"
#include "arm9/firm.h"
#include "arm9/sdmmc.h"
#include "arm9/crypto.h"
#include "arm9/utils.h"



bool loadFirmNand(void)
{
	u32 hash[8];
	AES_ctx aesCtx;
	u32 size = 0;
	u8 *buf = (u8*)FIRM_LOAD_ADDR;


	// Hash the NAND CID the bootrom left for us in ITCM.
	sha((void*)0x01FFCD84, 16, hash, SHA_INPUT_BIG | SHA_MODE_256, SHA_OUTPUT_LITTLE);

	// Setup AES engine.
	AES_selectKeyslot(6); // firmX:/ keyslot
	AES_setCtrIvNonce(&aesCtx, hash, AES_INPUT_LITTLE | AES_INPUT_NORMAL_ORDER | AES_MODE_CTR, 0x0B530000);
	AES_setCryptParams(&aesCtx, AES_FLUSH_READ_FIFO | AES_FLUSH_WRITE_FIFO | AES_BIT12 | AES_BIT13 | AES_OUTPUT_BIG |
						AES_INPUT_BIG | AES_OUTPUT_NORMAL_ORDER | AES_INPUT_NORMAL_ORDER | AES_MODE_CTR);

	// Read FIRM header from NAND and decrypt it.
	if(sdmmc_nand_readsectors(0x0B530000>>9, 1, buf))
	{
		printf("Failed to read FIRM header!\n");
		return false;
	}
	AES_crypt(&aesCtx, buf, buf, 512);
	buf += 512;

	// Calculate remaining size.
	firm_header *header = (firm_header*)FIRM_LOAD_ADDR;
	for(int i = 0; i < 4; i++) size += header->section[i].size;

	// Read remaining data.
	if(sdmmc_nand_readsectors(0x0B530200>>9, size>>9, buf))
	{
		printf("Failed to read FIRM sections!\n");
		return false;
	}

	// Decrypt remaining data of FIRM.
	AES_crypt(&aesCtx, buf, buf, size);

	return true;
}

bool loadFile(const char *filePath, void *address, u32 size, u32 *bytesRead)
{
	FIL file;
	u32 tmp;
	bool res = true;


	if(bytesRead == NULL) bytesRead = &tmp;
	if(f_open(&file, filePath, FA_READ) != FR_OK)
	{
		printf("Failed to open '%s'!\n", filePath);
		return false;
	}
	if(f_read(&file, address, size, (UINT*)bytesRead) != FR_OK)
	{
		printf("Failed to read from file!\n");
		res = false;
	}
	f_close(&file);

	return res;
}

bool makeWriteFile(const char *filePath, void *address, u32 size, u32 *bytesWritten)
{
	FIL file;
	u32 tmp;
	bool res = true;


	if(bytesWritten == NULL) bytesWritten = &tmp;
	if(f_open(&file, filePath, FA_CREATE_ALWAYS|FA_WRITE) != FR_OK)
	{
		printf("Failed to create '%s'!\n", filePath);
		return false;
	}
	if(f_write(&file, address, size, (UINT*)bytesWritten) != FR_OK)
	{
		printf("Failed to write to file!\n");
		res = false;
	}
	f_close(&file);

	return res;
}

bool updateNandLoader(const char *filePath)
{
	u32 hash[8];
	AES_ctx aesCtx;
	u32 size, cmpVal = 0xAAAAAAAA;
	u8 *buf = (u8*)0x20800000;
	firm_header *header = (firm_header*)0x20800000;


	// Hash the NAND CID the bootrom left for us in ITCM.
	sha((void*)0x01FFCD84, 16, hash, SHA_INPUT_BIG | SHA_MODE_256, SHA_OUTPUT_LITTLE);

	// Setup AES engine.
	AES_selectKeyslot(6); // firmX:/ keyslot
	AES_setCtrIvNonce(&aesCtx, hash, AES_INPUT_LITTLE | AES_INPUT_NORMAL_ORDER | AES_MODE_CTR, 0x0B130000);
	AES_setCryptParams(&aesCtx, AES_FLUSH_READ_FIFO | AES_FLUSH_WRITE_FIFO | AES_BIT12 | AES_BIT13 | AES_OUTPUT_BIG |
						AES_INPUT_BIG | AES_OUTPUT_NORMAL_ORDER | AES_INPUT_NORMAL_ORDER | AES_MODE_CTR);

	// Read FIRM from file.
	if(!loadFile(filePath, buf, FIRM_MAX_SIZE, &size)) return false;

	// Check for hax.
	if(memcmp(header->signature, &cmpVal, 4) == 0)
	{
		printf("Invalid FIRM!\n");
		return false;
	}

	// Delete update file.
	if(f_unlink(filePath) != FR_OK)
	{
		printf("Failed to delete '%s'!\n", filePath);
		return false;
	}

	// Encrypt FIRM.
	AES_crypt(&aesCtx, buf, buf, size);

	// Write FIRM to firm0:/.
	if(sdmmc_nand_writesectors(0x0B130000>>9, size>>9, buf))
	{
		printf("Failed to write sector 0xB130000!\n");
		return false;
	}

	printf("Loader successfully updated.\n");
	return true;
}

u64 getFreeSpace(const char *drive)
{
	FATFS *fs;
	DWORD freeClusters;

	if(f_getfree(drive, &freeClusters, &fs) != FR_OK)
	{
		printf("Failed to get free space for '%s'!\n", drive);
		return 0;
	}
	return ((u64)(freeClusters * fs->csize)) * 512;
}

bool dumpNand(void)
{
	u32 nandSectorCnt = getMMCDevice(0)->total_size;
	u32 sectorBlkSize = 0x4000, curSector = 0;
	u8 *buf = (u8*)0x20800000;
	FIL file;
	UINT bytesWritten;
	u32 blockSize;
	bool res = true;


	if(getFreeSpace("sdmc:") < nandSectorCnt<<9)
	{
		printf("Not enough space on the SD card!\n");
		return false;
	}
	if(f_open(&file, "sdmc:/NAND.bin", FA_CREATE_ALWAYS | FA_WRITE) != FR_OK)
	{
		printf("Failed to create '%s'!\n", "sdmc:/NAND.bin");
		return false;
	}

	while(curSector < nandSectorCnt)
	{
		blockSize = ((nandSectorCnt - curSector < sectorBlkSize) ? nandSectorCnt - curSector : sectorBlkSize);

		if(sdmmc_nand_readsectors(curSector, blockSize, buf))
		{
			printf("\nFailed to read sector 0x%X!\n", (unsigned int)curSector);
			res = false;
			break;
		}
		if(f_write(&file, buf, blockSize<<9, &bytesWritten) != FR_OK || bytesWritten>>9 != blockSize)
		{
			printf("\nFailed to write to file!\n");
			res = false;
			break;
		}

		curSector += blockSize;
		printf("\r%u%% (Sector 0x%X/0x%X)", (unsigned int)(curSector * 100 / nandSectorCnt), (unsigned int)curSector, (unsigned int)nandSectorCnt);
	}

	f_close(&file);
	return res;
}

bool restoreNand(void)
{
	FIL file;
	UINT bytesRead;
	u32 blockSize;
	bool res = true;
	u32 offset = 0;
	u32 bufSize = 0x800000;
	u8 *buf = (u8*)0x20800000;


	FILINFO fileStat;
	if(f_stat("sdmc:/NAND.bin", &fileStat) != FR_OK)
	{
		printf("Failed to get file status!\n");
		return false;
	}
	if(fileStat.fsize > (getMMCDevice(0)->total_size * 512))
	{
		printf("NAND file is bigger than NAND!\n");
		return false;
	}

	if(f_open(&file, "sdmc:/NAND.bin", FA_READ) != FR_OK)
	{
		printf("Failed to open '%s'!\n", "sdmc:/NAND.bin");
		return false;
	}

	u32 size = fileStat.fsize;
	while(offset < size)
	{
		blockSize = ((size - offset < bufSize) ? size - offset : bufSize);

		if(f_read(&file, buf, blockSize, &bytesRead) != FR_OK || bytesRead != blockSize)
		{
			printf("\nFailed to read from file!\n");
			res = false;
			break;
		}
		if(sdmmc_nand_writesectors(offset>>9, blockSize>>9, buf))
		{
			printf("\nFailed to write sector 0x%X!\n", (unsigned int)offset>>9);
			res = false;
			break;
		}

		offset += blockSize;
		printf("\r%d%% (Sector 0x%X/0x%X)", (unsigned int)((u64)offset * 100 / size), (unsigned int)offset>>9, (unsigned int)size>>9);
	}

	f_close(&file);
	return res;
}

void initGfx(void)
{
	static bool isInitialized = false;

	if(!isInitialized)
	{
		isInitialized = true;
		consoleInit(1, NULL);
		*((vu32*)CORE_SYNC_ID) = 1; // Tell ARM11 to turn on gfx.
		while(*((vu32*)CORE_SYNC_ID) == 1);
	}
}