diff --git a/Makefile b/Makefile index b0cec20..09130f8 100644 --- a/Makefile +++ b/Makefile @@ -7,10 +7,10 @@ export TARGET := $(notdir $(CURDIR)) ENTRY9 := 0x08000054 -ENTRY11 := 0x1FF85040 +ENTRY11 := 0x1FF89040 SECTION0_ADR := 0x08000040 SECTION0_TYPE := 0 -SECTION1_ADR := 0x1FF85000 +SECTION1_ADR := 0x1FF89000 SECTION1_TYPE := 1 diff --git a/arm11/arm11.ld b/arm11/arm11.ld index 1b0b2dd..6612651 100644 --- a/arm11/arm11.ld +++ b/arm11/arm11.ld @@ -13,7 +13,7 @@ { /* =========== CODE section =========== */ - . = 0x1FF85000; + . = 0x1FF89000; __start__ = . ; .text ALIGN(4) : diff --git a/include/arm11/firm.h b/include/arm11/firm.h index e0b021a..ca4aed8 100644 --- a/include/arm11/firm.h +++ b/include/arm11/firm.h @@ -22,4 +22,5 @@ -noreturn void firm_launch(void); +s32 loadVerifyFirm(const char *const path, bool skipHashCheck); +noreturn void firmLaunch(void); diff --git a/include/arm11/fsutils.h b/include/arm11/fsutils.h deleted file mode 100644 index a67c8fa..0000000 --- a/include/arm11/fsutils.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -/* - * 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 . - */ - -#include "types.h" - - - -bool fsEnsureMounted(const char *path); -void fsUnmountAll(); -u32 fsMountNandFilesystems(); -void fsUnmountNandFilesystems(); -bool fsMountSdmc(); -bool fsCreateFileWithPath(const char *filepath); diff --git a/include/arm11/lz11.h b/include/arm11/lz11.h new file mode 100644 index 0000000..095a623 --- /dev/null +++ b/include/arm11/lz11.h @@ -0,0 +1,25 @@ +#pragma once + +/* + * 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 . + */ + +#include "types.h" + + + +void lz11Decompress(const void *in, void *out, u32 size); diff --git a/include/arm11/menu/menu.h b/include/arm11/menu/menu.h index da745ec..fdb4d9d 100644 --- a/include/arm11/menu/menu.h +++ b/include/arm11/menu/menu.h @@ -24,7 +24,6 @@ #define MENU_MAX_ENTRIES 8 #define MENU_MAX_DEPTH 4 -#define MENU_DISP_Y -2 #define MENU_OFFSET_TITLE 5 #define MENU_OFFSET_BUTTONS 18 #define MENU_WIDTH 28 diff --git a/include/arm11/menu/menu_fb3ds.h b/include/arm11/menu/menu_fb3ds.h index 0f80a34..6da175d 100644 --- a/include/arm11/menu/menu_fb3ds.h +++ b/include/arm11/menu/menu_fb3ds.h @@ -85,11 +85,11 @@ } }, { // 2 - "Boot Slot Setup", 3, NULL, + "Boot Slot Setup", 3, &menuPresetBootSlot, { - { "Setup [slot 1]...", DESC_SET_SLOT_1, &DummyFunc, 0 }, - { "Setup [slot 2]...", DESC_SET_SLOT_2, &DummyFunc, 1 }, - { "Setup [slot 3]...", DESC_SET_SLOT_3, &DummyFunc, 2 } + { "Setup [slot 1]...", DESC_SET_SLOT_1, &menuSetupBootSlot, 0 }, + { "Setup [slot 2]...", DESC_SET_SLOT_2, &menuSetupBootSlot, 1 }, + { "Setup [slot 3]...", DESC_SET_SLOT_3, &menuSetupBootSlot, 2 } } }, { // 3 diff --git a/include/arm11/menu/menu_fsel.h b/include/arm11/menu/menu_fsel.h index 883ccf4..be23bec 100644 --- a/include/arm11/menu/menu_fsel.h +++ b/include/arm11/menu/menu_fsel.h @@ -19,5 +19,11 @@ */ #include "types.h" +#include "arm11/console.h" -u32 menuFileSelector(char* result, const char* start, const char* pattern); +#define BRWS_MAX_ENTRIES 14 +#define BRWS_OFFSET_TITLE 3 +#define BRWS_OFFSET_BUTTONS 20 +#define BRWS_WIDTH 40 + +bool menuFileSelector(char* res_path, PrintConsole* menu_con, const char* start, const char* pattern); diff --git a/include/arm11/menu/menu_func.h b/include/arm11/menu/menu_func.h index 3a159da..e17aaac 100644 --- a/include/arm11/menu/menu_func.h +++ b/include/arm11/menu/menu_func.h @@ -21,7 +21,9 @@ #include "types.h" u32 menuPresetBootMode(void); +u32 menuPresetBootSlot(void); u32 menuSetBootMode(PrintConsole* con, u32 param); +u32 menuSetupBootSlot(PrintConsole* con, u32 param); u32 DummyFunc(PrintConsole* con, u32 param); u32 SetView(PrintConsole* con, u32 param); u32 ShowCredits(PrintConsole* con, u32 param); diff --git a/include/arm9/config.h b/include/arm9/config.h deleted file mode 100644 index df8fa95..0000000 --- a/include/arm9/config.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -/* - * 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 . - */ - - -/*enum Keys { - KBootOption1 = 0, - KBootOption2, - KBootOption3, - KBootOption1NandImage, - KBootOption2NandImage, - KBootOption3NandImage, - KBootOption1Buttons, - KBootOption2Buttons, - KBootOption3Buttons, - KBootMode, - KDevMode, - // ... - KLast -}; - -enum BootModes { - BootModeNormal = 0, - BootModeQuick, - BootModeQuiet -}; - -#define numKeys KLast - -bool loadConfigFile(); -bool writeConfigFile(); -void *configCopyText(int key); -const void *configGetData(int key); -bool configDataExist(int key); -const char *configGetKeyText(int key); -bool configSetKeyData(int key, const void *data); -void configRestoreDefaults(); -bool configDevModeEnabled();*/ diff --git a/include/arm9/firm.h b/include/arm9/firm.h index dbe8f76..1ef37a4 100644 --- a/include/arm9/firm.h +++ b/include/arm9/firm.h @@ -47,6 +47,5 @@ -bool firm_size(size_t *size); -bool firm_verify(u32 fwSize, bool skipHashCheck, bool printInfo); -noreturn void firm_launch(int argc, const char **argv); +s32 loadVerifyFirm(const char *const path, bool skipHashCheck); +noreturn void firmLaunch(int argc, const char **argv); diff --git a/include/arm9/fsutils.h b/include/arm9/fsutils.h deleted file mode 100644 index 2919191..0000000 --- a/include/arm9/fsutils.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -/* - * 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 . - */ - -#include "types.h" -#include "fatfs/ff.h" - - -// SD card FAT fs instance -FATFS sd_fs; -// same for all NAND filesystems -FATFS nand_twlnfs, nand_twlpfs, nand_fs; - - - -bool fsGetFreeSpaceOnDrive(const char *drive, u64 *freeSpace); -bool fsEnsureMounted(const char *path); -void fsUnmountAll(); -u32 fsMountNandFilesystems(); -void fsUnmountNandFilesystems(); -u32 fsRemountNandFilesystems(); -bool fsMountSdmc(); -bool fsCreateFileWithPath(const char *filepath); diff --git a/include/fsutils.h b/include/fsutils.h new file mode 100644 index 0000000..a67c8fa --- /dev/null +++ b/include/fsutils.h @@ -0,0 +1,30 @@ +#pragma once + +/* + * 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 . + */ + +#include "types.h" + + + +bool fsEnsureMounted(const char *path); +void fsUnmountAll(); +u32 fsMountNandFilesystems(); +void fsUnmountNandFilesystems(); +bool fsMountSdmc(); +bool fsCreateFileWithPath(const char *filepath); diff --git a/include/ipc_handler.h b/include/ipc_handler.h index b213d7b..46d2c97 100644 --- a/include/ipc_handler.h +++ b/include/ipc_handler.h @@ -58,8 +58,8 @@ IPC_CMD9_WRITE_SECTORS = CMD_ID(20) | CMD_IN_BUFS(15) | CMD_OUT_BUFS(15) | CMD_PARAMS(15), IPC_CMD9_MALLOC = CMD_ID(21) | CMD_IN_BUFS(15) | CMD_OUT_BUFS(15) | CMD_PARAMS(15), IPC_CMD9_FREE = CMD_ID(22) | CMD_IN_BUFS(15) | CMD_OUT_BUFS(15) | CMD_PARAMS(15), - IPC_CMD9_LOAD_VERIFY_FIRM = CMD_ID(23) | CMD_IN_BUFS(15) | CMD_OUT_BUFS(15) | CMD_PARAMS(15), - IPC_CMD9_FIRM_LAUNCH = CMD_ID(24) | CMD_IN_BUFS(15) | CMD_OUT_BUFS(15) | CMD_PARAMS(15), + IPC_CMD9_LOAD_VERIFY_FIRM = CMD_ID(23) | CMD_IN_BUFS(1) | CMD_OUT_BUFS(0) | CMD_PARAMS(1), + IPC_CMD9_FIRM_LAUNCH = CMD_ID(24) | CMD_IN_BUFS(0) | CMD_OUT_BUFS(0) | CMD_PARAMS(0), IPC_CMD9_PREPA_POWER = CMD_ID(25) | CMD_IN_BUFS(15) | CMD_OUT_BUFS(15) | CMD_PARAMS(15), IPC_CMD9_PANIC = CMD_ID(26) | CMD_IN_BUFS(15) | CMD_OUT_BUFS(15) | CMD_PARAMS(15), IPC_CMD9_EXCEPTION = CMD_ID(27) | CMD_IN_BUFS(15) | CMD_OUT_BUFS(15) | CMD_PARAMS(15) diff --git a/include/mem_map.h b/include/mem_map.h index bce0eca..f0d173b 100644 --- a/include/mem_map.h +++ b/include/mem_map.h @@ -135,15 +135,17 @@ #ifdef ARM11 -#define A11_MMU_TABLES_BASE (AXIWRAM_BASE) +#define A11_C0_STACK_START (AXIWRAM_BASE) // Core 0 stack +#define A11_C0_STACK_END (A11_C0_STACK_START + 0x2000) +#define A11_C1_STACK_START (A11_C0_STACK_END) // Core 1 stack +#define A11_C1_STACK_END (A11_C1_STACK_START + 0x2000) +#define A11_EXC_STACK_START (VRAM_BASE + VRAM_SIZE - 0x200000) +#define A11_EXC_STACK_END (VRAM_BASE + VRAM_SIZE - 0x100000) +#define A11_MMU_TABLES_BASE (A11_C1_STACK_END) #define A11_VECTORS_START (AXIWRAM_BASE + AXIWRAM_SIZE - 0x60) #define A11_VECTORS_SIZE (0x60) #define A11_FALLBACK_ENTRY (AXIWRAM_BASE + AXIWRAM_SIZE - 0x4) #define A11_STUB_ENTRY (AXIWRAM_BASE + AXIWRAM_SIZE - 0x200) #define A11_STUB_SIZE (0x1A0) // Don't overwrite the vectors #define A11_HEAP_END (AXIWRAM_BASE + AXIWRAM_SIZE) -#define A11_STACK_START (A11_STUB_ENTRY - 0xE00) -#define A11_STACK_END (A11_STUB_ENTRY) -#define A11_EXC_STACK_START (VRAM_BASE + VRAM_SIZE - 0x200000) -#define A11_EXC_STACK_END (VRAM_BASE + VRAM_SIZE - 0x100000) #endif diff --git a/source/arm11/config.c b/source/arm11/config.c index 0d0de2b..cab2f2d 100644 --- a/source/arm11/config.c +++ b/source/arm11/config.c @@ -27,7 +27,7 @@ #include "fs.h" #include "util.h" // #include "arm9/hardware/interrupt.h" -#include "arm11/fsutils.h" +#include "fsutils.h" #include "arm11/hardware/hid.h" #include "arm11/debug.h" #include "arm11/config.h" diff --git a/source/arm11/firm.c b/source/arm11/firm.c index 916f9ec..ceb6223 100644 --- a/source/arm11/firm.c +++ b/source/arm11/firm.c @@ -21,26 +21,27 @@ #include "mem_map.h" #include "arm11/start.h" #include "hardware/pxi.h" +#include "ipc_handler.h" -/*void NAKED firmLaunchStub(void) +void NAKED firmLaunchStub(void) { *((vu32*)A11_FALLBACK_ENTRY) = 0; - // Answer ARM0 - REG_PXI_SYNC11 = 0; // Disable all IRQs - while(REG_PXI_CNT11 & PXI_SEND_FIFO_FULL); - REG_PXI_SEND11 = PXI_RPL_OK; + REG_PXI_SYNC = 0; // Disable all IRQs + // Tell ARM9 we are ready + while(REG_PXI_CNT & PXI_SEND_FIFO_FULL); + REG_PXI_SEND = 0xA8E4u; // Wait for entry address - while(REG_PXI_CNT11 & PXI_RECV_FIFO_EMPTY); - u32 entry = REG_PXI_RECV11; + while(REG_PXI_CNT & PXI_RECV_FIFO_EMPTY); + u32 entry = REG_PXI_RECV; // Tell ARM9 we got the entry - while(REG_PXI_CNT11 & PXI_SEND_FIFO_FULL); - REG_PXI_SEND11 = PXI_RPL_FIRM_LAUNCH_READY; - REG_PXI_CNT11 = 0; // Disable PXI + while(REG_PXI_CNT & PXI_SEND_FIFO_FULL); + REG_PXI_SEND = 0x94C6u; + REG_PXI_CNT = 0; // Disable PXI if(!entry) { @@ -49,15 +50,29 @@ } ((void (*)(void))entry)(); -}*/ +} -noreturn void firm_launch(void) +s32 loadVerifyFirm(const char *const path, bool skipHashCheck) { + u32 cmdBuf[3]; + cmdBuf[0] = (u32)path; + cmdBuf[1] = strlen(path) + 1; + cmdBuf[2] = skipHashCheck; + + return PXI_sendCmd(IPC_CMD9_LOAD_VERIFY_FIRM, cmdBuf, 3); +} + +noreturn void firmLaunch(void) +{ + PXI_sendCmd(IPC_CMD9_FIRM_LAUNCH, NULL, 0); + // Relocate ARM11 stub - /*memcpy((void*)A11_STUB_ENTRY, (const void*)firmLaunchStub, A11_STUB_SIZE); + memcpy((void*)A11_STUB_ENTRY, (const void*)firmLaunchStub, A11_STUB_SIZE); deinitCpu(); - ((void (*)(void))A11_STUB_ENTRY)();*/ + // Change sp to a safe location + __asm__ __volatile__("mov sp, %0" : : "r" (A11_STUB_ENTRY) : "sp"); + ((void (*)(void))A11_STUB_ENTRY)(); while(1); } diff --git a/source/arm11/fsutils.c b/source/arm11/fsutils.c deleted file mode 100644 index 8e44f98..0000000 --- a/source/arm11/fsutils.c +++ /dev/null @@ -1,129 +0,0 @@ -/* - * 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 . - */ - -#include -#include -#include -// #include "arm11/dev.h" -#include "arm11/fsutils.h" -#include "util.h" -#include "fs.h" - -bool fsEnsureMounted(const char *path) -{ - if(strncmp(path, "sdmc:", 5) == 0) - { - return (fMount(FS_DRIVE_SDMC) == 0); - } - else if(strncmp(path, "nand:", 5) == 0) - { - return (fMount(FS_DRIVE_NAND) == 0); - } - else if(strncmp(path, "twln:", 5) == 0) - { - return (fMount(FS_DRIVE_TWLN) == 0); - } - else if(strncmp(path, "twlp:", 5) == 0) - { - return (fMount(FS_DRIVE_TWLP) == 0); - } - - return false; -} - -void fsUnmountAll() -{ - fUnmount(FS_DRIVE_SDMC); - fUnmount(FS_DRIVE_NAND); - fUnmount(FS_DRIVE_TWLN); - fUnmount(FS_DRIVE_TWLP); -} - -u32 fsMountNandFilesystems() -{ - u32 res = 0; - - res |= (fMount(FS_DRIVE_TWLN) ? 0 : 1u); - res |= (fMount(FS_DRIVE_TWLP) ? 0 : 1u<<1); - res |= (fMount(FS_DRIVE_NAND) ? 0 : 1u<<2); - - return res; -} - -void fsUnmountNandFilesystems() -{ - fUnmount(FS_DRIVE_NAND); - fUnmount(FS_DRIVE_TWLN); - fUnmount(FS_DRIVE_TWLP); - // dev_decnand->close(); // ? -} - -bool fsMountSdmc() -{ - return (fMount(FS_DRIVE_SDMC) == 0); -} - -bool fsCreateFileWithPath(const char *filepath) -{ - const size_t maxBufSize = 0x100; - char *tempBuf = (char *) malloc(maxBufSize); - char *tempPtr = tempBuf; - s32 fhandle; - FILINFO fno; - - /* memory check */ - if (!tempBuf) - { - return false; // out of memory - } - - /* create directories */ - for (char *p = (char*) filepath; *p; p++) - { - if ((*p == '/') || (*p == '\\')) - { - *tempPtr = '\0'; - s32 dhandle = fOpenDir(tempBuf); - if (dhandle >= 0) fCloseDir(dhandle); - else if (fMkdir(tempBuf) != 0) goto fail; - } - *(tempPtr++) = *p; - } - *tempPtr = '\0'; - - /* touch file */ - if (fStat(tempBuf, &fno) == 0) - { - if (fno.fattrib & AM_DIR) goto fail; - } - - fhandle = fOpen(tempBuf, FS_CREATE_ALWAYS); - if (fhandle < 0) goto fail; - fClose(fhandle); - - free(tempBuf); - - return true; - -fail: - - free(tempBuf); - - return false; -} - diff --git a/source/arm11/hardware/cache.s b/source/arm11/hardware/cache.s index 82dadf4..5c4b81c 100644 --- a/source/arm11/hardware/cache.s +++ b/source/arm11/hardware/cache.s @@ -113,3 +113,6 @@ blt invalidateDCacheRange_lp mcr p15, 0, r2, c7, c10, 4 @ Data Synchronization Barrier bx lr + + +.pool diff --git a/source/arm11/hardware/exception.s b/source/arm11/hardware/exception.s index d24a2a7..4cddfd3 100644 --- a/source/arm11/hardware/exception.s +++ b/source/arm11/hardware/exception.s @@ -90,3 +90,6 @@ str r0, [r12, #0x110] @ REG_CPU_II_EOI ldmfd sp!, {r0-r3, r12, lr} rfefd sp! @ Restore lr (pc) and spsr (cpsr) + + +.pool diff --git a/source/arm11/hardware/mmu.c b/source/arm11/hardware/mmu.c index 5029a96..2ce31db 100644 --- a/source/arm11/hardware/mmu.c +++ b/source/arm11/hardware/mmu.c @@ -143,17 +143,22 @@ // PERM_PRIV_RW_USR_NO_ACC, 1, true, // L1_TO_L2(MAKE_CUSTOM_NORM_ATTR(POLICY_WRITE_BACK_ALLOC_BUFFERED, POLICY_WRITE_BACK_ALLOC_BUFFERED))); + // AXIWRAM core 0/1 stack mapping + mmuMapPages(A11_C0_STACK_START, A11_C0_STACK_START, 4, (u32*)(A11_MMU_TABLES_BASE + 0x4400u), + true, PERM_PRIV_RW_USR_NO_ACC, 1, true, + L1_TO_L2(MAKE_CUSTOM_NORM_ATTR(POLICY_WRITE_BACK_ALLOC_BUFFERED, POLICY_WRITE_BACK_ALLOC_BUFFERED))); + // AXIWRAM MMU table mapping mmuMapPages(A11_MMU_TABLES_BASE, A11_MMU_TABLES_BASE, 5, (u32*)(A11_MMU_TABLES_BASE + 0x4400u), true, PERM_PRIV_RO_USR_NO_ACC, 1, true, L1_TO_L2(ATTR_NORM_NONCACHABLE)); - // Remaining AXIWRAM pages - mmuMapPages(AXIWRAM_BASE + 0x5000u, AXIWRAM_BASE + 0x5000u, 123, - (u32*)(A11_MMU_TABLES_BASE + 0x4400u), true, PERM_PRIV_RW_USR_NO_ACC, 1, false, - L1_TO_L2(MAKE_CUSTOM_NORM_ATTR(POLICY_WRITE_BACK_ALLOC_BUFFERED, POLICY_WRITE_BACK_ALLOC_BUFFERED))); - extern u32 __start__[]; + // Remaining AXIWRAM pages + mmuMapPages((u32)__start__, (u32)__start__, 119, (u32*)(A11_MMU_TABLES_BASE + 0x4400u), + true, PERM_PRIV_RW_USR_NO_ACC, 1, false, + L1_TO_L2(MAKE_CUSTOM_NORM_ATTR(POLICY_WRITE_BACK_ALLOC_BUFFERED, POLICY_WRITE_BACK_ALLOC_BUFFERED))); + // Map fastboot executable start to boot11 mirror (exception vectors) mmuMapPages(BOOT11_MIRROR2, (u32)__start__, 1, (u32*)(A11_MMU_TABLES_BASE + 0x4800u), true, PERM_PRIV_RO_USR_NO_ACC, 1, false, diff --git a/source/arm11/lz11.s b/source/arm11/lz11.s new file mode 100644 index 0000000..e580b3b --- /dev/null +++ b/source/arm11/lz11.s @@ -0,0 +1,93 @@ +@ Code by mtheall + +#include "asmfunc.h" + +.arm +.cpu mpcore +.fpu vfpv2 + + + +ASM_FUNC lz11Decompress + push {r4-r6} + ldr r3, =(0x01010101) + +.Lloop: + cmp r2, #0 @ if(size <= 0) + pople {r4-r6} @ pop stack + bxle lr @ return + + rors r3, r3, #1 @ r3 = (r3<<31) | (r3>>1) + ldrcsb r12, [r0], #1 @ if(r3 & (1<<31)) flags = *in++ + tst r12, r3 @ if(flags & r3 == 0) + beq .Lcopy_uncompressed @ goto copy_uncompressed + + ldrb r4, [r0], #1 @ r4 = *in++ + and r5, r4, #0xF0 @ r5 = r4 & 0xF0 + cmp r5, #0x00 @ if(r5 == 0x00) + beq .Lextended @ goto extended + cmp r5, #0x10 @ if(r5 == 0x10) + beq .LXextended @ goto Xextended + + lsr r6, r4, #4 @ len = r4>>4 + add r6, r6, #1 @ len++ + b .L1 + +.Lextended: + lsl r6, r4, #4 @ len = r4<<4 + ldrb r4, [r0], #1 @ r4 = *in++ + orr r6, r6, r4, lsr #4 @ len = len | (r4>>4) + add r6, r6, #0x11 @ len += 0x11 + b .L1 + +.LXextended: + and r4, r4, #0x0F @ r4 &= 0x0F + lsl r6, r4, #12 @ len = r4<<12 + ldrb r4, [r0], #1 @ r4 = *in++ + orr r6, r6, r4, lsl #4 @ len |= r4<<4 + ldrb r4, [r0], #1 @ r4 = *in++ + orr r6, r6, r4, lsr #4 @ len |= r4>>4 + add r6, r6, #0x011 @ len += 0x011 + add r6, r6, #0x100 @ len += 0x100 + +.L1: + and r5, r4, #0x0F @ disp = r4 & 0x0F note: disp is in r5 + ldrb r4, [r0], #1 @ r4 = *in++ + orr r4, r4, r5, lsl #8 @ disp = r4 | (disp<<8) note: disp changes to r4 + add r4, r4, #1 @ disp++ + sub r2, r2, r6 @ size -= len + tst r4, #1 @ if(r4 & 1 == 0) // aligned displacement + beq .Lcopy_aligned @ goto copy_aligned + +.Lcopy_compressed: + ldrb r5, [r1, -r4] @ r5 = *(out - disp) + subs r6, r6, #1 @ if(len-- == 1) + strb r5, [r1], #1 @ *out++ = r5 + bne .Lcopy_compressed @ goto copy_compressed + b .Lloop @ goto loop + +.Lcopy_aligned: + tst r1, #0x1 @ if(r1 & 0x1 == 0) // src/dst is aligned + beq .Lcopy_hwords @ goto copy_hwords + ldrb r5, [r1, -r4] @ r5 = *(out - disp) // read a byte to align + strb r5, [r1], #1 @ *out++ = r5 + sub r6, r6, #1 @ len-- +.Lcopy_hwords: + subs r6, r6, #2 @ len -= 2 + ldrgeh r5, [r1, -r4] @ if(len >= 0) r5 = *(short*)(out - disp) + strgeh r5, [r1], #2 @ if(len >= 0) *(short*)out++ = r5 + beq .Lloop @ if(len == 0) goto loop + bgt .Lcopy_hwords @ if(len > 0) goto copy_hwords +@ extra byte + ldrb r5, [r1, -r4] @ r5 = *(out - disp) + strb r5, [r1], #1 @ *out++ = r5 + b .Lloop @ goto loop + +.Lcopy_uncompressed: + ldrb r4, [r0], #1 @ r4 = *in++ + sub r2, r2, #1 @ size-- + strb r4, [r1], #1 @ *out++ = r4 + b .Lloop @ goto loop + + +.pool diff --git a/source/arm11/main.c b/source/arm11/main.c index bb849c4..7624767 100644 --- a/source/arm11/main.c +++ b/source/arm11/main.c @@ -29,7 +29,7 @@ #include "arm11/firm.h" #include "arm11/console.h" #include "arm11/fmt.h" -#include "arm11/fsutils.h" +#include "fsutils.h" #include "arm11/config.h" @@ -64,7 +64,7 @@ // take over any changes to the config writeConfigFile(); - + // deinit filestesystem fsUnmountAll(); diff --git a/source/arm11/menu/menu.c b/source/arm11/menu/menu.c index 08e43eb..f0fd5ab 100644 --- a/source/arm11/menu/menu.c +++ b/source/arm11/menu/menu.c @@ -150,7 +150,7 @@ consoleSetCursor(menu_con, menu_x, menu_y++); ee_printf(is_selected ? "\x1b[47;30m%.3s %-*.*s\x1b[0m" : "%.3s %-*.*s", - ((menu_preset >> i) & 0x1) ? "[X]" : "[ ]", MENU_WIDTH-3, MENU_WIDTH-3, name); + ((menu_preset >> i) & 0x1) ? "[X]" : "[ ]", MENU_WIDTH-4, MENU_WIDTH-4, name); } // button instructions diff --git a/source/arm11/menu/menu_fsel.c b/source/arm11/menu/menu_fsel.c index 76f1da6..94fcc99 100644 --- a/source/arm11/menu/menu_fsel.c +++ b/source/arm11/menu/menu_fsel.c @@ -23,14 +23,17 @@ #include "types.h" #include "fs.h" #include "util.h" +#include "fsutils.h" +#include "arm11/menu/menu_fsel.h" #include "arm11/hardware/hid.h" -#include "arm11/fsutils.h" +#include "arm11/hardware/timer.h" #include "arm11/console.h" +#include "arm11/power.h" #include "arm11/debug.h" #include "arm11/fmt.h" -#define MAX_DIR_ENTRIES 0x200 // 512 -> worst case, approx 128kiB (yes, this is limited) -#define N_DIR_READ 0x10 // 16 at a time +#define MAX_DIR_ENTRIES 0x100 // 256 -> worst case approx 64kiB (yes, this is limited) +#define N_DIR_READ 0x10 // 16 at a time typedef struct { u32 fsize; // size of the file @@ -49,49 +52,95 @@ } +void truncateString(char* dest, const char* orig, int nsize, int tpos) +{ + int osize = strlen(orig); + + if (nsize < 0) + { + return; + } else if (nsize <= 3) + { + ee_snprintf(dest, nsize, orig); + } else if (nsize >= osize) + { + ee_snprintf(dest, nsize + 1, orig); + } else + { + if (tpos + 3 > nsize) tpos = nsize - 3; + ee_snprintf(dest, nsize + 1, "%-.*s...%-.*s", tpos, orig, nsize - (3 + tpos), orig + osize - (nsize - (3 + tpos))); + } +} + + +void formatBytes(char* str, u64 bytes) +{ + // str should be 32 byte in size, just to be safe + const char* units[] = {" Byte", " kiB", " MiB", " GiB"}; + + if (bytes < 1024) + { + ee_snprintf(str, 32, "%llu%s", bytes, units[0]); + } + else + { + u32 scale = 1; + u64 bytes100 = (bytes * 100) >> 10; + for(; (bytes100 >= 1024*100) && (scale < 3); scale++, bytes100 >>= 10); + ee_snprintf(str, 32, "%llu.%llu%s", bytes100 / 100, (bytes100 % 100) / 10, units[scale]); + } +} + + // inspired by http://www.geeksforgeeks.org/wildcard-character-matching/ static bool matchName(const char* path, const char* pattern) { - // handling non asterisk chars - for (; *pattern != '*'; pattern++, path++) + // handling non asterisk chars + for (; *pattern != '*'; pattern++, path++) { - if ((*pattern == '\0') && (*path == '\0')) + if ((*pattern == '\0') && (*path == '\0')) { - return true; // end reached simultaneously, match found - } + // end reached simultaneously, match found + return true; + } else if ((*pattern == '\0') || (*path == '\0')) { - return false; // end reached on only one, failure - } + // end reached on only one, failure + return false; + } else if ((*pattern != '?') && (tolower(*pattern) != tolower(*path))) { - return false; // chars don't match, failure - } - } + // chars don't match, failure + return false; + } + } - // handling the asterisk (matches one or more chars in path) - if ((*(pattern+1) == '?') || (*(pattern+1) == '*')) + // handling the asterisk (matches one or more chars in path) + if ((*(pattern+1) == '?') || (*(pattern+1) == '*')) { - return false; // stupid user/dev shenanigans, failure - } + // stupid user/dev shenanigans, failure + return false; + } else if (*path == '\0') { - return false; // asterisk, but end reached on path, failure - } + // asterisk, but end reached on path, failure + return false; + } else if (*(pattern+1) == '\0') { - return true; // nothing after the asterisk, match found - } + // nothing after the asterisk, match found + return true; + } else { // we couldn't really go without recursion here - for (path++; *path != '\0'; path++) + for (path++; *path != '\0'; path++) { - if (matchName(path, pattern + 1) == true) return true; - } - } - - return false; + if (matchName(path, pattern + 1) == true) return true; + } + } + + return false; } @@ -111,58 +160,81 @@ { for(s32 s = 0; s < n_entries; s++) { - DirBufferEntry* cmp0 = &(dir_buffer[s]); - DirBufferEntry* min0 = cmp0; + DirBufferEntry* cmp0 = &(dir_buffer[s]); + DirBufferEntry* min0 = cmp0; - for(s32 c = s + 1; c < n_entries; c++) + for(s32 c = s + 1; c < n_entries; c++) { - DirBufferEntry* cmp1 = &(dir_buffer[c]); - if(min0->is_dir != cmp1->is_dir) + DirBufferEntry* cmp1 = &(dir_buffer[c]); + if(min0->is_dir != cmp1->is_dir) { - if (min0->is_dir) - min0 = cmp1; - continue; - } - if(strnicmp(min0->fname, cmp1->fname, 256) > 0) - { - min0 = cmp1; + if (cmp1->is_dir) + min0 = cmp1; + continue; } - } + if(strnicmp(min0->fname, cmp1->fname, 256) > 0) + { + min0 = cmp1; + } + } - if(min0 != cmp0) + if(min0 != cmp0) { - DirBufferEntry swap; // swap entries - memcpy(&swap, cmp0, sizeof(DirBufferEntry)); - memcpy(cmp0, min0, sizeof(DirBufferEntry)); - memcpy(min0, &swap, sizeof(DirBufferEntry)); - } - } + DirBufferEntry swap; // swap entries + memcpy(&swap, cmp0, sizeof(DirBufferEntry)); + memcpy(cmp0, min0, sizeof(DirBufferEntry)); + memcpy(min0, &swap, sizeof(DirBufferEntry)); + } + } } static s32 readDirToBuffer(DirBufferEntry* dir_buffer, const char* path, const char* pattern) { - FsFileInfo finfo[N_DIR_READ]; + FsFileInfo* finfo = (FsFileInfo*) malloc(N_DIR_READ * sizeof(FsFileInfo)); s32 n_entries = 0; s32 dhandle; + if (!finfo) // out of memory + return -1; + // set dir_buffer to all zeroes memset(dir_buffer, 0, sizeof(DirBufferEntry) * MAX_DIR_ENTRIES); + + // special handling when in root + if(!*path) + { + const char* root_paths[] = { "sdmc:", "nand:", "twln:", "twlp:" }; + n_entries = sizeof(root_paths) / sizeof(const char*); + + for(s32 i = 0; i < n_entries; i++) + { + dir_buffer[i].fsize = 0; + dir_buffer[i].is_dir = 1; + dir_buffer[i].fname = mallocpyString(root_paths[i]); + } + + return n_entries; + } // open directory dhandle = fOpenDir(path); - if(dhandle < 0) return -1; + if(dhandle < 0) + { + free(finfo); + return -1; + } s32 n_read = 0; while((n_read = fReadDir(dhandle, finfo, N_DIR_READ)) > 0) { if (n_read < 0) // error reading dir - return -1; + goto fail; for(s32 i = 0; i < n_read; i++) { - if(pattern && !matchName(finfo[i].fname, pattern)) - continue; // not a match with the provided pattern + if(!(finfo[i].fattrib & AM_DIR) && pattern && !matchName(finfo[i].fname, pattern)) + continue; // not a match with the provided pattern and not a dir // take over data dir_buffer[n_entries].fsize = finfo[i].fsize; @@ -171,7 +243,7 @@ // check for out of memory if (!dir_buffer[n_entries].fname) - return -1; + goto fail; // max dir buffer size reached? if (++n_entries >= MAX_DIR_ENTRIES) @@ -183,18 +255,244 @@ break; // WARNING: no errors here - list just isn't complete } + fCloseDir(dhandle); + free(finfo); + + if (n_entries > 0) + sortDirBuffer(dir_buffer, n_entries); + return n_entries; + + fail: + + fCloseDir(dhandle); + free(finfo); + + return -1; } -u32 menuFileSelector(char* path, const char* start, const char* pattern) +/** + * @brief Draws the file listing to the given console. + * @param curr_path Current path displayed on screen. + * @param dir_buffer An array of structs containing the file listing. + * @param n_entries Number of elements in the above struct. + * @param menu_con Console that the file browser is displayed on. + * @param index Current placement of the cursor. + * @param scroll Current scroll offset, will be written to by this function. + */ +void browserDraw(const char* curr_path, DirBufferEntry* dir_buffer, s32 n_entries, PrintConsole* menu_con, s32 index, s32* scroll) +{ + int brws_x = (menu_con->consoleWidth - BRWS_WIDTH) >> 1; + int brws_y = BRWS_OFFSET_TITLE; + char temp_str[BRWS_WIDTH + 1]; + char byte_str[32]; + + // fix scroll (if required) + if (index < *scroll) + { + *scroll = index; + } else if (*scroll + BRWS_MAX_ENTRIES <= index) + { + *scroll = index - (BRWS_MAX_ENTRIES - 1); + } + + // select menu console + consoleSelect(menu_con); + consoleClear(); + + // browser title + consoleSetCursor(menu_con, brws_x, brws_y++); + truncateString(temp_str, curr_path, BRWS_WIDTH, 8); + if (!*temp_str) strncpy(temp_str, "[root]", BRWS_WIDTH); + ee_printf(temp_str); + brws_y++; + + // menu entries + for (s32 i = 0; i < BRWS_MAX_ENTRIES; i++) + { + s32 pos = i + *scroll; + if (pos >= n_entries) + break; + + DirBufferEntry* entry = &(dir_buffer[pos]); + bool is_selected = (pos == index); + + if(entry->is_dir) + strncpy(byte_str, *curr_path ? "(DIR)" : "(DRV)", 31); + else + formatBytes(byte_str, entry->fsize); + + consoleSetCursor(menu_con, brws_x, brws_y++); + truncateString(temp_str, entry->fname, BRWS_WIDTH-13, 8); + ee_printf(is_selected ? "\x1b[47;30m %-*.*s %10.10s \x1b[0m" : " %-*.*s %10.10s ", + BRWS_WIDTH-13, BRWS_WIDTH-13, temp_str, byte_str); + } + + // button instructions + brws_y = BRWS_OFFSET_BUTTONS; + consoleSetCursor(menu_con, brws_x, brws_y++); + ee_printf("%-*.*s", BRWS_WIDTH, BRWS_WIDTH, "[A]:Choose [B]:Back"); +} + + +/** + * @brief Shows a file select dialogue. + * @param res_path The selected path will end up here. + * @param menu_con Console that the file browser is displayed on. + * @param start Starting path (must be a dir). + * @param patter Only files matching this wildcard pattern will be displayed. + */ +bool menuFileSelector(char* res_path, PrintConsole* menu_con, const char* start, const char* pattern) { DirBufferEntry* dir_buffer; - bool result = false; + bool result = true; // <--- should be handled differently dir_buffer = (DirBufferEntry*) malloc(MAX_DIR_ENTRIES * sizeof(DirBufferEntry)); if (!dir_buffer) return false; + // res_path has to be at least 256 byte long (including '\0') and + // is also used as temporary buffer + *res_path = '\0'; // root dir if start is NULL + if(start) strncpy(res_path, start, 256); + + // check res_path, fix if required + char* lastname = NULL; + FsFileInfo fno; + if (fStat(res_path, &fno) != FR_OK) + { + *res_path = '\0'; + } + // not a dir -> must be a file + else if (!(fno.fattrib & AM_DIR)) + { + lastname = strrchr(res_path, '/'); + if (!lastname) + panicMsg("Invalid path"); + *(lastname++) = '\0'; + + s32 dhandle = fOpenDir(res_path); + if (dhandle < 0) + panicMsg("Filesystem error"); + fCloseDir(dhandle); + } + + bool is_dir = true; // we are not finished while we have a dir in res_path + while(is_dir) + { + s32 n_entries = readDirToBuffer(dir_buffer, res_path, pattern); + s32 last_index = (u32) -1; + s32 scroll = 0; + s32 index = 0; + + if(n_entries < 0) + panicMsg("Error reading dir!"); + + // find lastname in listing + if (lastname) + { + for (s32 i = 0; i < n_entries; i++) + { + if (strncmp(dir_buffer[i].fname, lastname, 256) == 0) + { + index = i; + break; + } + } + lastname = NULL; + } + + u32 dbutton_cooldown = 0; + while(true) + { + // update file browser (on demand) + if (index != last_index) { + browserDraw(res_path, dir_buffer, n_entries, menu_con, index, &scroll); + last_index = index; + + GX_textureCopy((u64*)RENDERBUF_TOP, 0, (u64*)GFX_getFramebuffer(SCREEN_TOP), + 0, SCREEN_SIZE_TOP + SCREEN_SIZE_SUB); + GFX_swapFramebufs(); + } + GFX_waitForEvent(GFX_EVENT_PDC0, true); // VBlank + + // directional button cooldown + for (u32 i = dbutton_cooldown; i > 0; i--) + { + hidScanInput(); + GFX_waitForEvent(GFX_EVENT_PDC0, true); // VBlank + if (!(hidKeysHeld() & (KEY_DDOWN|KEY_DUP|KEY_DLEFT|KEY_DRIGHT))) + break; + } + dbutton_cooldown = 0; + + if(hidGetPowerButton(true)) // handle power button + { + power_off(); + } + + hidScanInput(); + const u32 kDown = hidKeysDown(); + const u32 kHeld = hidKeysHeld(); + + if ((kDown & KEY_A) && n_entries) + { + // build new res_path + char* name = &(res_path[strlen(res_path)]); + if (name > res_path) *(name++) = '/'; + strncpy(name, dir_buffer[index].fname, (256 - (name - res_path))); + + // is this a dir? + is_dir = dir_buffer[index].is_dir; + + lastname = NULL; + break; + } + else if ((kDown & KEY_B) && *res_path) + { + // return to previous path + // (the name of this path will be stored in lastname) + lastname = strrchr(res_path, '/'); + + if (lastname) + *(lastname++) = '\0'; + else + *res_path = '\0'; + break; + } + else if (kHeld & (KEY_DDOWN|KEY_DUP|KEY_DLEFT|KEY_DRIGHT) && n_entries) + { + if (kHeld & KEY_DDOWN) + { + // cursor down + index = (index == n_entries - 1) ? 0 : index + 1; + } + else if (kHeld & KEY_DUP) + { + // cursor up + index = (index == 0) ? n_entries - 1 : index - 1; + } + else if (kHeld & KEY_DRIGHT) + { + // cursor down a page + index += BRWS_MAX_ENTRIES; + if (index >= n_entries) index = n_entries - 1; + } + else if (kHeld & KEY_DLEFT) + { + // cursor up a page + index -= BRWS_MAX_ENTRIES; + if (index < 0) index = 0; + } + + // set directional button cooldown + dbutton_cooldown = 10; + } + } + + freeDirBufferContent(dir_buffer, n_entries); + } + free(dir_buffer); return result; diff --git a/source/arm11/menu/menu_func.c b/source/arm11/menu/menu_func.c index cb1d934..4fc49f7 100644 --- a/source/arm11/menu/menu_func.c +++ b/source/arm11/menu/menu_func.c Binary files differ diff --git a/source/arm11/start.s b/source/arm11/start.s index f6d3fa9..a050752 100644 --- a/source/arm11/start.s +++ b/source/arm11/start.s @@ -156,13 +156,6 @@ _start_lp: wfi b _start_lp -.pool -_sysmode_stacks: - .word A11_STACK_END - .word (VRAM_BASE + VRAM_SIZE) @ Stack for core 1 for now. - @ Since we don't use core 1 this should be ok - .word 0 - .word 0 #define MAKE_BRANCH(src, dst) (0xEA000000 | (((((dst) - (src)) >> 2) - 2) & 0xFFFFFF)) @@ -176,7 +169,6 @@ subs r1, r1, #1 bne stubExceptionVectors_lp bx lr -.pool @ void clearMem(u32 *adr, u32 size) @@ -251,4 +243,11 @@ mcr p15, 0, r2, c7, c7, 0 @ Invalidate Both Caches. Also flushes the branch target cache mcr p15, 0, r2, c7, c10, 4 @ Data Synchronization Barrier bx r3 + + .pool +_sysmode_stacks: + .word A11_C0_STACK_END @ Stack for core 0 + .word A11_C1_STACK_END @ Stack for core 1 + .word 0 @ New 3DS cores are not used at all + .word 0 diff --git a/source/arm9/config.c b/source/arm9/config.c deleted file mode 100644 index ecfa5e4..0000000 --- a/source/arm9/config.c +++ /dev/null @@ -1,835 +0,0 @@ -/* - * 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 . - */ - -/* This is a parser/writer for the fastbootcfg.txt file. It's not vulnerable, I swear. */ - -/*#include -#include -#include -#include -#include "types.h" -#include "mem_map.h" -#include "fatfs/ff.h" -#include "arm9/fsutils.h" -#include "arm9/hardware/interrupt.h" -#include "util.h" -#include "arm9/hardware/hid.h" -#include "arm9/debug.h" -#include "arm9/config.h" - -#define MAX_FILE_SIZE 0x4000 - 1 - -static const char *SdmcFilepath = "sdmc:/3ds/fastbootcfg.txt"; -static const char *NandFilepath = "nand:/fastboot3DS/fastbootcfg.txt"; - -static const char *filepath; - -static FIL file; - -typedef struct { - char *textData; // ptr to the first char of an attribute's data inside filebuf - u32 textLength; // length of the above data, without '\0' ofc - void *data; // used to store the final native data -} AttributeEntryType; - -typedef struct { - // function to convert the textData to arbitrary native data - bool (*parse)(AttributeEntryType *attr); - // opposite of parse(), used to update/create an entry in the file - bool (*write)(AttributeEntryType *attr, const void *newData, int key); -} FunctionsEntryType; - -static void unloadConfigFile(); -static bool createConfigFile(); -static bool parseConfigFile(); -static bool parseBootOption(AttributeEntryType *attr); -static bool writeBootOption(AttributeEntryType *attr, const void *newData, int key); -static bool parseBootOptionPad(AttributeEntryType *attr); -static bool writeBootOptionPad(AttributeEntryType *attr, const void *newData, int key); -static bool parseBootMode(AttributeEntryType *attr); -static bool writeBootMode(AttributeEntryType *attr, const void *newData, int key); -static bool parseDevMode(AttributeEntryType *attr); -static bool writeDevMode(AttributeEntryType *attr, const void *newData, int key); - -static const char *keyStrings[] = { - "BOOT_OPTION1", - "BOOT_OPTION2", - "BOOT_OPTION3", - "BOOT_OPTION1_NAND_IMAGE", - "BOOT_OPTION2_NAND_IMAGE", - "BOOT_OPTION3_NAND_IMAGE", - "BOOT_OPTION1_BUTTONS", - "BOOT_OPTION2_BUTTONS", - "BOOT_OPTION3_BUTTONS", - "BOOT_MODE", - "DEV_MODE" -}; - -static FunctionsEntryType keyFunctions[] = { - { parseBootOption, writeBootOption }, - { parseBootOption, writeBootOption }, - { parseBootOption, writeBootOption },*/ - /* use the same functions for nand image option */ - /*{ parseBootOption, writeBootOption }, - { parseBootOption, writeBootOption }, - { parseBootOption, writeBootOption }, - { parseBootOptionPad, writeBootOptionPad }, - { parseBootOptionPad, writeBootOptionPad }, - { parseBootOptionPad, writeBootOptionPad }, - { parseBootMode, writeBootMode }, - { parseDevMode, writeDevMode } -}; - -static AttributeEntryType attributes[numKeys]; - -static char *filebuf = NULL; - -static bool configLoaded = false;*/ - -/* This loads the config file from SD card or eMMC and parses it */ -/*bool loadConfigFile() -{ - FILINFO fileStat; - u32 fileSize; - unsigned bytesRead; - bool createFile = false; - - if(configLoaded) - unloadConfigFile(); - - // first, try SD card fatfs - if(bootInfo.sd_status == 2) - { - filepath = SdmcFilepath; - - // does the config file exist? - if(f_stat(filepath, &fileStat) != FR_OK) - { - createFile = true; - } - } - else - { - filepath = NandFilepath; - - if(f_stat(filepath, &fileStat) != FR_OK) - { - createFile = true; - } - } - - if(createFile) - { - // try to create a file - if(!createConfigFile()) - return false; - - // does it work now? - if(f_stat(filepath, &fileStat) != FR_OK) - return false; - } - - fileSize = fileStat.fsize; - - if(fileSize > MAX_FILE_SIZE) - { - uiPrintIfVerbose("Invalid config-file size!\n"); - goto fail; - } - - filebuf = (char *) malloc(MAX_FILE_SIZE + 1); - - if(!filebuf) - { - uiPrintIfVerbose("Out of memory!\n"); - goto fail; - } - - if(f_open(&file, filepath, FA_READ) != FR_OK) - { - uiPrintIfVerbose("Failed to open config-file for reading!\n"); - goto fail; - } - - if(f_read(&file, filebuf, fileSize, &bytesRead) != FR_OK || bytesRead != fileSize) - { - uiPrintIfVerbose("Failed to read from config-file!\n"); - f_close(&file); - goto fail; - } - - f_close(&file); - - // terminate string buf - filebuf[fileSize] = '\0'; - - return parseConfigFile(); - -fail: - - // something didn't work, clean everything up - unloadConfigFile(); - - return false; -} - -static void unloadConfigFile() -{ - configLoaded = false; - AttributeEntryType *curAttr;*/ - - /* Free all data */ - /*for(u32 i=0; itextData = NULL; - curAttr->textLength = 0; - - if(curAttr->data) - { - free(curAttr->data); - curAttr->data = NULL; - } - } - - if(filebuf) - { - free(filebuf); - filebuf = NULL; - } -} - -bool writeConfigFile() -{ - if(!filebuf) - goto fail; - - const u32 fileSize = strlen(filebuf); - unsigned int bytesWritten; - - if(fileSize > MAX_FILE_SIZE) - panicMsg("fileSize too large!"); - - if(!fsCreateFileWithPath(filepath)) - { - goto fail; - } - - if(f_open(&file, filepath, FA_WRITE) != FR_OK) - { - goto fail; - } - - if(fileSize) - { - if((f_write(&file, filebuf, fileSize, &bytesWritten) != FR_OK) || (bytesWritten != fileSize)) - { - f_close(&file); - goto fail; - } - } - - // Make sure changes are written to disk in case - // the SD card is removed later. - if(f_sync(&file) != FR_OK) - { - f_close(&file); - goto fail; - } - - f_close(&file); - - return true; - -fail: - - return false; -} - -static bool createConfigFile() -{ - char temp = '\0'; - bool ret; - - if(filebuf) - panicMsg("config: internal error"); - - filebuf = &temp; - - ret = writeConfigFile(); - - filebuf = NULL; - - return ret; -}*/ - -/* returns the start of an attribute's data. */ -/*static char *findDefinition(const char *attrName) -{ - char *start; - char c; - - start = strstr(filebuf, attrName); - - if(start) - {*/ - /* verify definition char */ - /*start = start + strlen(attrName); - - while(*start == ' ') start++; - - c = *start; - if(c != '=') - start = NULL; - else - { - start ++; - while(*start == ' ') start++; - } - } - - return start; -}*/ - -/* returns the length of an attribute's data. */ -/*static u32 parseDefinition(char *attrData) -{ - static const char LF = 0x0A, CR = 0x0D, NEL = 0x15; - u32 len = 0; - char *cur = attrData; - char c; - - if(!attrData) - return 0; - - // look for linebreaks that mark the end of a definition - for(; (c = *cur) != '\0'; cur++) - { - if(c == LF || c == CR || c == NEL) - break; - len ++; - c = *cur; - } - - return len; -} - -static bool parseConfigFile() -{ - AttributeEntryType *curAttr; - u32 len; - char *text; - - // pass #1: look for definitions and populate our lookup table. - for(u32 i=0; itextData = text; - if(!text) - { - curAttr->textLength = 0; - continue; - } - len = parseDefinition(text); - curAttr->textLength = len; - } - - // pass #2: parse text-data and convert it into a native form - for(u32 i=0; itextData) - { - curAttr->data = NULL; - continue; - } - else - { // TODO: what to do if ret == false ? - //uiPrintIfVerbose("Parsing attr text with size %i\n%s\n", curAttr->textLength, curAttr->textData); - keyFunctions[i].parse(curAttr); - } - } - - configLoaded = true; - - return true; -} - -// Add a new definition in our file -static char *writeAddDefinitionText(const char *keyName, const char *textData) -{ - const char *def = " = "; - u32 curLen = strlen(filebuf); - u32 remainingLen = MAX_FILE_SIZE - curLen; - u32 totalLen = strlen(textData); - u32 defLen = strlen(def); - u32 keyLen = strlen(keyName); - - if(totalLen > remainingLen) - return NULL; - - totalLen += keyLen; - totalLen += defLen; - - if(curLen != 0) - totalLen += 2; // new line encoding - - if(totalLen > remainingLen) - return NULL; - - // insert line break if we already have any content - if(curLen != 0) - { - filebuf[curLen++] = 0x0d; - filebuf[curLen++] = 0x0a; - } - - ee_sprintf(&filebuf[curLen], "%s%s%s", keyName, def, textData); - - return &filebuf[curLen + keyLen + defLen]; -} - -static u32 writeUpdateDefinitionText(char *textData, u32 curTextLen, const char *newText) -{ - u32 diff; - u32 newLen = strlen(newText); - u32 curLen = strlen(filebuf); - u32 remainingLen = MAX_FILE_SIZE - curLen; - - if(newLen > remainingLen) - return 0; - - diff = newLen - curTextLen; - - if(diff) - { - if(newLen > curTextLen) - { - size_t backupLen = curLen - (textData - filebuf) - curTextLen + 1; - char *tempBuf = (char *) malloc(backupLen); - if(!tempBuf) - return 0; - memcpy(tempBuf, textData + curTextLen, backupLen); - - memcpy_s(filebuf, MAX_FILE_SIZE + 1, textData - filebuf + newLen, - tempBuf, backupLen, 0, false); - } - else - { - memcpy_s(filebuf, MAX_FILE_SIZE + 1, textData - filebuf + newLen, - filebuf, MAX_FILE_SIZE + 1, textData - filebuf + curTextLen, false); - } - - for(u32 key=0; key textData) - { - attributes[key].textData += diff; - } - } - } - - memcpy(textData, newText, newLen); - - return newLen; -} - -static void writeAttributeText(AttributeEntryType *attr, const char *newText, int key) -{ - // text data doesn't exist yet? - if(!attr->textData) - { - attr->textData = writeAddDefinitionText(configGetKeyText(key), newText); - attr->textLength = strlen(newText); - } - else // update definition - { - attr->textLength = writeUpdateDefinitionText(attr->textData, attr->textLength, newText); - } -} - -static bool isValidPath(const char *path) -{ - char c; - bool gotMountpoint = false; - - for(; (c = *path) != '\0'; path++) - { - // no multiple mountpoints - if(c == ':') - { - if(gotMountpoint) - return false; - gotMountpoint = true; - } - - // no dir-return - if(c == '.' && path[1] == '.') - return false; - - // no space after dir slash - if((c == '\\' || c == '/') && path[1] == ' ') - return false; - - // no non ASCII chars - if(!((unsigned char)c <= 127)) - return false; - } - - if(!gotMountpoint) - return false; - - return true; -} - -static bool parseBootOption(AttributeEntryType *attr) -{ - attr->data = NULL; - - char *buf = (char *) malloc(attr->textLength + 1); - if(!buf) return false; - - memcpy(buf, attr->textData, attr->textLength); - buf[attr->textLength] = '\0'; - - if(!isValidPath(buf)) - { - free(buf); - return false; - } - - attr->data = buf; - - return true; -} - -static bool writeBootOption(AttributeEntryType *attr, const void *newData, int key) -{ - u32 len; - char *buf; - const char *path = (const char *) newData; - - if(!path) - return false; - - if(!isValidPath(path)) - return false; - - len = strlen(path); - - if(!attr->data) - { - buf = (char *) malloc(len + 1); - if(!buf) - return false; - attr->data = buf; - } - else if(len != attr->textLength) - { - free(attr->data); - buf = (char *) malloc(len + 1); - if(!buf) - return false; - attr->data = buf; - } - else buf = attr->data; - - memcpy(buf, path, len); - buf[len] = '\0'; - - writeAttributeText(attr, path, key); - - return true; -} - -static const char * convTable[] = { - "A", "B", "SELECT", "START", "RIGHT", "LEFT", - "UP", "DOWN", "R", "L", "X", "Y" -}; - -static bool parseBootOptionPad(AttributeEntryType *attr) -{ - char c; - char *textData = attr->textData; - u32 padValue = 0; - u32 i; - - for(; (c = *textData) != '\0' && textData < attr->textData + attr->textLength;) - { - for(i=0; idata = (u32 *) malloc(sizeof(u32)); - if(!attr->data) - return false; - - *(u32 *)attr->data = padValue; - return true; -} - -static bool writeBootOptionPad(AttributeEntryType *attr, const void *newData, int key) -{ - char tempbuf[0x40] = { 0 }; - u32 padValue; - u32 i; - bool keysAdded = false; - - if(!newData) - return false; - - padValue = *(const u32 *)newData; - - padValue &= HID_KEY_MASK_ALL; - - if(!attr->data) - { - attr->data = (u32 *) malloc(sizeof(u32)); - if(!attr->data) - return false; - } - - *(u32 *)attr->data = padValue; - - for(i=0; i> i) & 1) - { - if(keysAdded) - strcat(tempbuf, " + "); - strcat(tempbuf, convTable[i]); - keysAdded = true; - } - } - - writeAttributeText(attr, tempbuf, key); - - return true; -} - -static const char * modeTable[] = { - "Normal", "Quick", "Quiet" -}; - -static bool parseBootMode(AttributeEntryType *attr) -{ - char *textData = attr->textData; - u32 i; - - for(i=0; i<3; i++) - { - if(strnicmp(textData, modeTable[i], strlen(modeTable[i])) == 0) - break; - } - - if(i >= 3) - { - attr->data = NULL; - return false; - } - - attr->data = (u32 *) malloc(sizeof(u32)); - if(!attr->data) - return false; - - *(u32 *)attr->data = i; - return true; -} - -static bool writeBootMode(AttributeEntryType *attr, const void *newData, int key) -{ - u32 mode; - - if(!newData) - return false; - - mode = *(const u32 *)newData; - - if(mode > BootModeQuiet) - return false; - - if(!attr->data) - { - attr->data = (u32 *) malloc(sizeof(u32)); - if(!attr->data) - return false; - } - - *(u32 *)attr->data = mode; - - const char *textData = modeTable[mode]; - - writeAttributeText(attr, textData, key); - - return true; -} - -static const char * devModeStates[] = { - "Enabled", "Disabled" -}; - -static bool parseDevMode(AttributeEntryType *attr) -{ - char *textData = attr->textData; - bool enabled; - - if(strcmp(textData, devModeStates[0]) == 0) - enabled = true; - else if(strcmp(textData, devModeStates[1]) == 0) - enabled = false; - else - { - attr->data = NULL; - return false; - } - - attr->data = (bool *) malloc(sizeof(bool)); - if(!attr->data) - return false; - - *(bool *)attr->data = enabled; - return true; -} - -static bool writeDevMode(AttributeEntryType *attr, const void *newData, int key) -{ - bool enabled; - - if(!newData) - return false; - - enabled = *(const bool *)newData; - - if(!attr->data) - { - attr->data = (bool *) malloc(sizeof(bool)); - if(!attr->data) - return false; - } - - *(bool *)attr->data = enabled; - - const char *textData = modeTable[enabled ? 0 : 1]; - - writeAttributeText(attr, textData, key); - - return true; -} - -void *configCopyText(int key) -{ - if(!configLoaded) - return NULL; - - if(key < 0 || key >= KLast) - return NULL; - - if(!attributes[key].textData) - return NULL; - - char *buf = (char *) malloc(attributes[key].textLength + 1); - if(!buf) - return NULL; - - memcpy(buf, attributes[key].textData, attributes[key].textLength); - buf[attributes[key].textLength] = '\0'; - - return buf; -} - -const void *configGetData(int key) -{ - if(!configLoaded) - return NULL; - - if(key < 0 || key >= KLast) - return NULL; - - return attributes[key].data; -} - -bool configDataExist(int key) -{ - if(!configLoaded) - return false; - - return configGetData(key) != NULL; -} - -const char *configGetKeyText(int key) -{ - if(key < 0 || key >= KLast) - return NULL; - - return keyStrings[key]; -} - -bool configSetKeyData(int key, const void *data) -{ - AttributeEntryType *attr; - - if(!configLoaded) - return false; - - if(key < 0 || key >= KLast) - return NULL; - - attr = &attributes[key]; - - if(!keyFunctions[key].write) - panicMsg("Unimplemented key function!"); - - return keyFunctions[key].write(attr, data, key); -} - -void configRestoreDefaults() -{ - if(!configLoaded) - return; - - for(int key=0; key. */ -/*#include -#include #include #include "types.h" // we need the arm11 mem map information @@ -26,60 +24,50 @@ #undef ARM11 #include "arm9/firm.h" #include "arm9/start.h" +#include "util.h" #include "arm9/hardware/crypto.h" #include "arm9/hardware/ndma.h" -#include "arm9/gui/ui.h" -#include "hardware/cache.h" -#include "util.h" -#include "hardware/pxi.h"*/ +#include "hardware/pxi.h" +#include "arm9/partitions.h" +#include "arm9/dev.h" +#include "fs.h" - -/* We don't want the FIRM to do hacky stuff with our loader */ - -/*typedef struct +static const struct { u32 addr; u32 size; -} firmProtectedArea; +} sectionWhitelist[] = +{ + { // Unused ITCM data + ITCM_KERNEL_MIRROR, ITCM_SIZE - 0x4800 + }, + { // ARM9 memory excluding exception vectors + A9_RAM_BASE + 0x40, A9_RAM_SIZE + A9_RAM_N3DS_EXT_SIZE - 0x40 + }, + { // VRAM excluding the FIRM buffer + VRAM_BASE, VRAM_SIZE - 0x400000 + }, + { // DSP memory + AXIWRAM excluding stack and FIRM launch stub + DSP_MEM_BASE, DSP_MEM_SIZE + AXIWRAM_SIZE - 0x220 + }, + { // FCRAM + FCRAM_BASE, FCRAM_SIZE + FCRAM_N3DS_EXT_SIZE + } +}; -static const firmProtectedArea firmProtectedAreas[] = { - { // FIRM buffer - FIRM_LOAD_ADDR, FIRM_MAX_SIZE - }, - { // io regs - IO_MEM_BASE, VRAM_BASE - IO_MEM_BASE - }, - { // arm9 exception vector table - A9_VECTORS_START, A9_VECTORS_SIZE - }, - { // arm9 stack - A9_STACK_START, A9_STACK_END - A9_STACK_START - }, - { // Console unique data + ARM9 FIRM launch stub + argv data - ITCM_BASE + 0x3800, ITCM_SIZE - 0x3800 - }, - { // arm11 exception vector table - A11_VECTORS_START, A11_VECTORS_SIZE - }, - { // arm11 stack - A11_STACK_START, A11_STACK_END - A11_STACK_START - }, - { // arm11 relocated firm launch stub - A11_STUB_ENTRY, A11_STUB_SIZE - } -};*/ + /* Calculates the actual firm partition size by using its header */ -/*bool firm_size(size_t *size) +bool firm_size(size_t *size) { firm_header *firm_hdr = (firm_header*)FIRM_LOAD_ADDR; u32 curLen = sizeof(firm_header); u32 curOffset = 0; - *size = 0;*/ + *size = 0; /* scan sections in reverse order */ - /*for(int i=3; i>=0; i--) + for(int i=3; i>=0; i--) { firm_sectionheader *section = &firm_hdr->section[i]; @@ -114,6 +102,14 @@ u32 entry11 = firm_hdr->entrypointarm11; + REG_PXI_SYNC = 0; // Disable all IRQs + while(1) + { + // Wait for the ARM11 to be ready before copying sections + while(REG_PXI_CNT & PXI_RECV_FIFO_EMPTY); + if(REG_PXI_RECV == 0xA8E4u) break; + } + for(u32 i = 0; i < 4; i++) { firm_sectionheader *section = &firm_hdr->section[i]; @@ -149,137 +145,124 @@ REG_NDMA2_CNT & NDMA_ENABLE || REG_NDMA3_CNT & NDMA_ENABLE); // Tell ARM11 its entrypoint - REG_PXI_SYNC9 = 0; // Disable all IRQs - while(REG_PXI_CNT9 & PXI_SEND_FIFO_FULL); - REG_PXI_SEND9 = entry11; + while(REG_PXI_CNT & PXI_SEND_FIFO_FULL); + REG_PXI_SEND = entry11; - // Wait for ARM11... while(1) { - while(REG_PXI_CNT9 & PXI_RECV_FIFO_EMPTY); - if(REG_PXI_RECV9 == PXI_RPL_FIRM_LAUNCH_READY) - break; + // Wait for the ARM111 to confirm it received the entrypoint + while(REG_PXI_CNT & PXI_RECV_FIFO_EMPTY); + if(REG_PXI_RECV == 0x94C6u) break; } - REG_PXI_CNT9 = 0; // Disable PXI + + REG_PXI_CNT = 0; // Disable PXI // go for it! - entry9(argc, argv, 0x2BEEFu); + entry9(argc, argv, 0x3BEEFu); } -bool firm_verify(u32 fwSize, bool skipHashCheck, bool printInfo) +s32 loadVerifyFirm(const char *const path, bool skipHashCheck) { - firm_header *firm_hdr = (firm_header*)FIRM_LOAD_ADDR; - const char *const res[2] = {"\x1B[31mBAD", "\x1B[32mGOOD"}; - bool isValid; - bool retval = true; - u32 hash[8]; - - if(fwSize > FIRM_MAX_SIZE) - return false; - - if(fwSize <= sizeof(firm_header)) - return false; - - if(memcmp(&firm_hdr->magic, "FIRM", 4) != 0) - return false; + u32 firmSize; + const firm_header *const firmHdr = (firm_header*)FIRM_LOAD_ADDR; - if(firm_hdr->entrypointarm9 == 0) + + if(memcmp(path, "firm", 4) == 0) { - if(printInfo) uiPrintError("Bad ARM9 entrypoint!\n"); - return false; + if(!dev_decnand->is_active()) return -1; + + size_t partInd, sector; + if(!partitionGetIndex(path, &partInd)) return -2; + if(!partitionGetSectorOffset(partInd, §or)) return -3; + + if(!dev_decnand->read_sector(sector, 1, (void*)FIRM_LOAD_ADDR)) return -4; + if(!firm_size((size_t*)&firmSize)) return -5; + sector++; + if(!dev_decnand->read_sector(sector, (firmSize>>9) - 1, (void*)(FIRM_LOAD_ADDR + 0x200))) return -4; + } + else + { + const s32 f = fOpen(path, FS_OPEN_EXISTING | FS_OPEN_READ); + if(f < 0) return -6; + + firmSize = fSize(f); + if(firmSize > FIRM_MAX_SIZE) + { + fClose(f); + return -7; + } + if(fRead(f, (void*)FIRM_LOAD_ADDR, firmSize) < 0) + { + fClose(f); + return -8; + } + + fClose(f); } - if(printInfo) - { - uiPrintInfo("\nARM9 entry: 0x%" PRIX32 "\n", firm_hdr->entrypointarm9); - uiPrintInfo("ARM11 entry: 0x%" PRIX32 "\n\n", firm_hdr->entrypointarm11); - } - - for(u32 i=0; i<4; i++) - { - firm_sectionheader *section = &firm_hdr->section[i]; - if(section->size == 0) - continue; + // Check if <= FIRM header size + if(firmSize <= sizeof(firm_header)) return -9; - if(printInfo) - uiPrintInfo("Section %" PRIu32 ":\n Offset: 0x%" PRIX32 "\n Addr: 0x%" PRIX32 "\n Size: 0x%" PRIX32 "\n", - i, section->offset, section->address, section->size); - - if(section->offset >= fwSize || section->offset < sizeof(firm_header)) + // Check magic + if(memcmp(&firmHdr->magic, "FIRM", 4) != 0) return -10; + + // ARM9 entrypoint must not be 0 + if(firmHdr->entrypointarm9 == 0) return -11; + + for(u32 i = 0; i < 4; i++) + { + const firm_sectionheader *const section = &firmHdr->section[i]; + const u32 secSize = section->size; + + if(!secSize) continue; + + const u32 secOffset = section->offset; + // Check section offset + if(secOffset >= firmSize || secOffset < sizeof(firm_header)) return -12; + + // Check section size + if(secSize >= firmSize || (secSize + secOffset > firmSize)) return -13; + + const u32 secAddr = section->address; + bool allowed = false; + for(u32 n = 0; n < arrayEntries(sectionWhitelist); n++) { - if(printInfo) - uiPrintError("Bad section offset!\n"); - return false; - } - - if((section->size >= fwSize) || (section->size + section->offset > fwSize)) - { - if(printInfo) - uiPrintError("Bad section size!\n"); - return false; - } - - // check for bad sections - const u32 numEntries = arrayEntries(firmProtectedAreas); - - for(u32 j=0; jaddress; - u32 end = start + section->size; - - isValid = true; - - if(start >= addr && start < addr + size) isValid = false; - - else if(end > addr && end <= addr + size) isValid = false; - - else if(start < addr && end > addr + size) isValid = false; - - if(!isValid) + const u32 addr = sectionWhitelist[n].addr; + const u32 size = sectionWhitelist[n].size; + + // Overflow check + if(secAddr > ~secSize) return -14; + + // Range check + if(secAddr >= addr && secAddr + secSize <= addr + size) { - if(printInfo) - uiPrintError("Unallowed section:\n0x%" PRIX32 " - 0x%" PRIX32 "\n", start, end); - retval = false; + allowed = true; break; } } - + if(!allowed) return -15; + if(!skipHashCheck) { - sha((u32*)(FIRM_LOAD_ADDR + section->offset), section->size, hash, + u32 hash[8]; + sha((u32*)(FIRM_LOAD_ADDR + secOffset), secSize, hash, SHA_INPUT_BIG | SHA_MODE_256, SHA_OUTPUT_BIG); - isValid = memcmp(hash, section->hash, 32) == 0; - - if(printInfo) - uiPrintInfo(" Hash: %s\x1B[0m\n", res[isValid]); - - retval &= isValid; + if(memcmp(section->hash, hash, 32) != 0) return -16; } } - - return retval; + + // TODO: argc/v stuff + + return 0; } -noreturn void firm_launch(int argc, const char **argv) +noreturn void firmLaunch(int argc, const char **argv) { - //ee_printf("Sending PXI_CMD_FIRM_LAUNCH\n"); - PXI_sendWord(PXI_CMD_FIRM_LAUNCH); - - //ee_printf("Waiting for ARM11...\n"); - while(PXI_recvWord() != PXI_RPL_OK); - - //ee_printf("Relocating FIRM launch stub...\n"); memcpy((void*)A9_STUB_ENTRY, (const void*)firmLaunchStub, A9_STUB_SIZE); deinitCpu(); - //ee_printf("Starting firm launch...\n"); ((void (*)(int, const char**))A9_STUB_ENTRY)(argc, argv); while(1); -}*/ +} diff --git a/source/arm9/fsutils.c b/source/arm9/fsutils.c deleted file mode 100644 index 8bd0b04..0000000 --- a/source/arm9/fsutils.c +++ /dev/null @@ -1,187 +0,0 @@ -/* - * 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 . - */ - -#include -#include -#include -#include "arm9/dev.h" -#include "arm9/fsutils.h" -#include "util.h" - -bool fsGetFreeSpaceOnDrive(const char *drive, u64 *freeSpace) -{ - FATFS *fs; - DWORD freeClusters; - - if(f_getfree(drive, &freeClusters, &fs) != FR_OK) - { - return false; - } - - *freeSpace = ((u64)(freeClusters * fs->csize)) * 512; - - return true; -} - -bool fsEnsureMounted(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 fsUnmountAll() -{ - f_mount(NULL, "sdmc:", 1); - f_mount(NULL, "twln:", 1); - f_mount(NULL, "twlp:", 1); - f_mount(NULL, "nand:", 1); -} - -u32 fsMountNandFilesystems() -{ - u32 res = 0; - - res |= (f_mount(&nand_twlnfs, "twln:", 1) ? 0 : 1u); - res |= (f_mount(&nand_twlpfs, "twlp:", 1) ? 0 : 1u<<1); - res |= (f_mount(&nand_fs, "nand:", 1) ? 0 : 1u<<2); - - return res; -} - -void fsUnmountNandFilesystems() -{ - f_mount(NULL, "twln:", 1); - f_mount(NULL, "twlp:", 1); - f_mount(NULL, "nand:", 1); - dev_decnand->close(); -} - -u32 fsRemountNandFilesystems() -{ - u32 res = 0; - - res |= (f_mount(&nand_twlnfs, "twln:", 1) ? 0 : 1u); - res |= (f_mount(&nand_twlpfs, "twlp:", 1) ? 0 : 1u<<1); - res |= (f_mount(&nand_fs, "nand:", 1) ? 0 : 1u<<2); - - return res; -} - -bool fsMountSdmc() -{ - return f_mount(&sd_fs, "sdmc:", 1) == FR_OK; -} - -bool fsCreateFileWithPath(const char *filepath) -{ - const char *p = filepath; - const char *subdir; - const size_t maxBufSize = 0x1000; - char *tempBuf = (char *) malloc(maxBufSize); - char *oldPath = (char *) malloc(maxBufSize); - FRESULT res; - FIL tempFile; - size_t copyLen; - - if(!tempBuf || !oldPath) - return false; - - memset(oldPath, 0, maxBufSize); - - if(f_getcwd(oldPath, maxBufSize - 1) != FR_OK) - goto fail_early; - - while(*p && *p != '/' && *p != '\\') - p++; - - if(!*p) - goto fail; - - p++; - - /* create directories */ - for(;;) - { - subdir = p; - - while(*p && *p != '/' && *p != '\\') - p++; - - if(!*p) // not a directory or error - break; - - copyLen = min((size_t)p - (size_t)subdir, maxBufSize - 1); - memcpy(tempBuf, subdir, copyLen); - tempBuf[copyLen] = '\0'; - - res = f_mkdir(tempBuf); - if(res != FR_OK && res != FR_EXIST) - goto fail; - - if(f_chdir(tempBuf) != FR_OK) - goto fail; - - p++; - } - - if(p == subdir + 1) - goto fail; - - /* touch file */ - res = f_open(&tempFile, filepath, FA_CREATE_ALWAYS); - if(res != FR_OK && res != FR_EXIST) - goto fail; - - f_close(&tempFile); - - - if(f_chdir(oldPath) != FR_OK) - goto fail; - - free(tempBuf); - free(oldPath); - - return true; - -fail: - - // This should never fail - f_chdir(oldPath); - -fail_early: - - free(tempBuf); - free(oldPath); - - return false; -} - diff --git a/source/arm9/hardware/cache.s b/source/arm9/hardware/cache.s index 594cfdf..3667e24 100644 --- a/source/arm9/hardware/cache.s +++ b/source/arm9/hardware/cache.s @@ -124,3 +124,6 @@ cmp r0, r1 blt invalidateDCacheRange_lp bx lr + + +.pool diff --git a/source/arm9/hardware/exception.s b/source/arm9/hardware/exception.s index 37215b7..84cde51 100644 --- a/source/arm9/hardware/exception.s +++ b/source/arm9/hardware/exception.s @@ -92,3 +92,6 @@ msr spsr_fsxc, r0 irqHandler_skip_processing: ldmfd sp!, {r0-r3, r12, pc}^ + + +.pool diff --git a/source/arm9/ipc_handler.c b/source/arm9/ipc_handler.c index b3ca286..308fd09 100644 --- a/source/arm9/ipc_handler.c +++ b/source/arm9/ipc_handler.c @@ -22,6 +22,7 @@ #include "hardware/cache.h" #include "arm9/debug.h" #include "fs.h" +#include "arm9/firm.h" @@ -102,8 +103,13 @@ case IPC_CMD_ID_MASK(IPC_CMD9_FREE): break; case IPC_CMD_ID_MASK(IPC_CMD9_LOAD_VERIFY_FIRM): + result = loadVerifyFirm((const char *const)buf[0], buf[2]); break; case IPC_CMD_ID_MASK(IPC_CMD9_FIRM_LAUNCH): + { + extern volatile bool g_startFirmLaunch; + g_startFirmLaunch = true; + } break; case IPC_CMD_ID_MASK(IPC_CMD9_PREPA_POWER): case IPC_CMD_ID_MASK(IPC_CMD9_PANIC): diff --git a/source/arm9/lz11.s b/source/arm9/lz11.s deleted file mode 100644 index 3b82f12..0000000 --- a/source/arm9/lz11.s +++ /dev/null @@ -1,90 +0,0 @@ -@ Code by mtheall - -#include "asmfunc.h" - -.arm -.cpu arm946e-s -.fpu softvfp - - - -ASM_FUNC lz11Decompress - push {r4-r6} - ldr r3, =(0x01010101) - -.Lloop: - cmp r2, #0 @ if(size <= 0) - pople {r4-r6} @ pop stack - bxle lr @ return - - rors r3, r3, #1 @ r3 = (r3<<31) | (r3>>1) - ldrcsb r12, [r0], #1 @ if(r3 & (1<<31)) flags = *in++ - tst r12, r3 @ if(flags & r3 == 0) - beq .Lcopy_uncompressed @ goto copy_uncompressed - - ldrb r4, [r0], #1 @ r4 = *in++ - and r5, r4, #0xF0 @ r5 = r4 & 0xF0 - cmp r5, #0x00 @ if(r5 == 0x00) - beq .Lextended @ goto extended - cmp r5, #0x10 @ if(r5 == 0x10) - beq .LXextended @ goto Xextended - - lsr r6, r4, #4 @ len = r4>>4 - add r6, r6, #1 @ len++ - b .L1 - -.Lextended: - lsl r6, r4, #4 @ len = r4<<4 - ldrb r4, [r0], #1 @ r4 = *in++ - orr r6, r6, r4, lsr #4 @ len = len | (r4>>4) - add r6, r6, #0x11 @ len += 0x11 - b .L1 - -.LXextended: - and r4, r4, #0x0F @ r4 &= 0x0F - lsl r6, r4, #12 @ len = r4<<12 - ldrb r4, [r0], #1 @ r4 = *in++ - orr r6, r6, r4, lsl #4 @ len |= r4<<4 - ldrb r4, [r0], #1 @ r4 = *in++ - orr r6, r6, r4, lsr #4 @ len |= r4>>4 - add r6, r6, #0x011 @ len += 0x011 - add r6, r6, #0x100 @ len += 0x100 - -.L1: - and r5, r4, #0x0F @ disp = r4 & 0x0F note: disp is in r5 - ldrb r4, [r0], #1 @ r4 = *in++ - orr r4, r4, r5, lsl #8 @ disp = r4 | (disp<<8) note: disp changes to r4 - add r4, r4, #1 @ disp++ - sub r2, r2, r6 @ size -= len - tst r4, #1 @ if(r4 & 1 == 0) // aligned displacement - beq .Lcopy_aligned @ goto copy_aligned - -.Lcopy_compressed: - ldrb r5, [r1, -r4] @ r5 = *(out - disp) - subs r6, r6, #1 @ if(len-- == 1) - strb r5, [r1], #1 @ *out++ = r5 - bne .Lcopy_compressed @ goto copy_compressed - b .Lloop @ goto loop - -.Lcopy_aligned: - tst r1, #0x1 @ if(r1 & 0x1 == 0) // src/dst is aligned - beq .Lcopy_hwords @ goto copy_hwords - ldrb r5, [r1, -r4] @ r5 = *(out - disp) // read a byte to align - strb r5, [r1], #1 @ *out++ = r5 - sub r6, r6, #1 @ len-- -.Lcopy_hwords: - subs r6, r6, #2 @ len -= 2 - ldrgeh r5, [r1, -r4] @ if(len >= 0) r5 = *(short*)(out - disp) - strgeh r5, [r1], #2 @ if(len >= 0) *(short*)out++ = r5 - beq .Lloop @ if(len == 0) goto loop - bgt .Lcopy_hwords @ if(len > 0) goto copy_hwords -@ extra byte - ldrb r5, [r1, -r4] @ r5 = *(out - disp) - strb r5, [r1], #1 @ *out++ = r5 - b .Lloop @ goto loop - -.Lcopy_uncompressed: - ldrb r4, [r0], #1 @ r4 = *in++ - sub r2, r2, #1 @ size-- - strb r4, [r1], #1 @ *out++ = r4 - b .Lloop @ goto loop diff --git a/source/arm9/main.c b/source/arm9/main.c index 0f39578..2ca1526 100644 --- a/source/arm9/main.c +++ b/source/arm9/main.c @@ -16,120 +16,28 @@ * along with this program. If not, see . */ -#include -#include -#include #include "types.h" #include "mem_map.h" -#include "util.h" -#include "hardware/pxi.h" -#include "arm9/hardware/hid.h" #include "arm9/hardware/hardware.h" #include "arm9/debug.h" #include "arm9/hardware/interrupt.h" -#include "fatfs/ff.h" -#include "arm9/dev.h" -#include "arm9/fsutils.h" #include "arm9/firm.h" -#include "arm9/config.h" -#include "arm9/hardware/timer.h" -#include "arm9/start.h" -#include "hardware/cache.h" + + +volatile bool g_startFirmLaunch = false; 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_ALLOW_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");*/ - - /* In verbose mode wait before we clear all debug messages */ - /*if(uiGetVerboseMode()) - TIMER_sleep(600); - - consoleClear(); - - enter_menu(MENU_STATE_MAIN); - - -finish_firmlaunch: - - uiPrintIfVerbose("Unmounting FS...\n"); - - fsUnmountAll(); - - uiPrintIfVerbose("Closing devices...\n"); - - devs_close(); + while(!g_startFirmLaunch) __wfi(); hardwareDeinit(); - // TODO: Proper argc/v passing needs to be implemented. - firm_launch(1, (const char**)(ITCM_KERNEL_MIRROR + 0x7470));*/ + firmLaunch(1, (const char**)(ITCM_KERNEL_MIRROR + 0x7470)); return 0; } diff --git a/source/arm9/start.s b/source/arm9/start.s index c12ee07..c13c529 100644 --- a/source/arm9/start.s +++ b/source/arm9/start.s @@ -114,20 +114,19 @@ @ Wait for interrupt mcr p15, 0, r0, c7, c0, 4 b _start_lp -.pool #define MAKE_BRANCH(src, dst) (0xEA000000 | (((((dst) - (src)) >> 2) - 2) & 0xFFFFFF)) setupExceptionVectors: - adr r0, vectorStubs + adr r0, _vectorStubs mov r1, #A9_VECTORS_START ldmia r0!, {r2-r9} stmia r1!, {r2-r9} ldm r0, {r2-r5} stm r1, {r2-r5} bx lr -vectorStubs: +_vectorStubs: ldr pc, irqHandlerPtr irqHandlerPtr: .word irqHandler ldr pc, fiqHandlerPtr @@ -152,7 +151,6 @@ orr r0, r0, #0x50000 @ Enable DTCM and ITCM mcr p15, 0, r0, c1, c0, 0 @ Write control register bx lr -.pool @ void clearMem(u32 *adr, u32 size) @@ -313,7 +311,6 @@ orr r0, r0, r1 @ Enable MPU, D-Cache and I-Cache mcr p15, 0, r0, c1, c0, 0 @ Write control register bx lr -.pool @ Needed by libc @@ -345,4 +342,6 @@ mcr p15, 0, r2, c7, c6, 0 @ Invalidate D-Cache mcr p15, 0, r2, c7, c10, 4 @ Drain write buffer bx r3 + + .pool diff --git a/source/fb_assert.c b/source/fb_assert.c index 40fa3f3..e80b38c 100644 --- a/source/fb_assert.c +++ b/source/fb_assert.c @@ -36,5 +36,6 @@ ee_printf("Assertion failed: %s:%" PRIu32, str, line); #endif + enterCriticalSection(); while(1) __wfi(); } diff --git a/source/fsutils.c b/source/fsutils.c new file mode 100644 index 0000000..38acead --- /dev/null +++ b/source/fsutils.c @@ -0,0 +1,129 @@ +/* + * 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 . + */ + +#include +#include +#include +// #include "arm11/dev.h" +#include "fsutils.h" +#include "util.h" +#include "fs.h" + +bool fsEnsureMounted(const char *path) +{ + if(strncmp(path, "sdmc:", 5) == 0) + { + return (fMount(FS_DRIVE_SDMC) == 0); + } + else if(strncmp(path, "nand:", 5) == 0) + { + return (fMount(FS_DRIVE_NAND) == 0); + } + else if(strncmp(path, "twln:", 5) == 0) + { + return (fMount(FS_DRIVE_TWLN) == 0); + } + else if(strncmp(path, "twlp:", 5) == 0) + { + return (fMount(FS_DRIVE_TWLP) == 0); + } + + return false; +} + +void fsUnmountAll() +{ + fUnmount(FS_DRIVE_SDMC); + fUnmount(FS_DRIVE_NAND); + fUnmount(FS_DRIVE_TWLN); + fUnmount(FS_DRIVE_TWLP); +} + +u32 fsMountNandFilesystems() +{ + u32 res = 0; + + res |= (fMount(FS_DRIVE_TWLN) ? 0 : 1u); + res |= (fMount(FS_DRIVE_TWLP) ? 0 : 1u<<1); + res |= (fMount(FS_DRIVE_NAND) ? 0 : 1u<<2); + + return res; +} + +void fsUnmountNandFilesystems() +{ + fUnmount(FS_DRIVE_NAND); + fUnmount(FS_DRIVE_TWLN); + fUnmount(FS_DRIVE_TWLP); + // dev_decnand->close(); // ? +} + +bool fsMountSdmc() +{ + return (fMount(FS_DRIVE_SDMC) == 0); +} + +bool fsCreateFileWithPath(const char *filepath) +{ + const size_t maxBufSize = 0x100; + char *tempBuf = (char *) malloc(maxBufSize); + char *tempPtr = tempBuf; + s32 fhandle; + FILINFO fno; + + /* memory check */ + if (!tempBuf) + { + return false; // out of memory + } + + /* create directories */ + for (char *p = (char*) filepath; *p; p++) + { + if ((*p == '/') || (*p == '\\')) + { + *tempPtr = '\0'; + s32 dhandle = fOpenDir(tempBuf); + if (dhandle >= 0) fCloseDir(dhandle); + else if (fMkdir(tempBuf) != 0) goto fail; + } + *(tempPtr++) = *p; + } + *tempPtr = '\0'; + + /* touch file */ + if (fStat(tempBuf, &fno) == 0) + { + if (fno.fattrib & AM_DIR) goto fail; + } + + fhandle = fOpen(tempBuf, FS_CREATE_ALWAYS); + if (fhandle < 0) goto fail; + fClose(fhandle); + + free(tempBuf); + + return true; + +fail: + + free(tempBuf); + + return false; +} + diff --git a/source/hardware/pxi.c b/source/hardware/pxi.c index 1d63914..3f4fe65 100644 --- a/source/hardware/pxi.c +++ b/source/hardware/pxi.c @@ -72,7 +72,6 @@ u32 PXI_sendCmd(u32 cmd, const u32 *const buf, u32 words) { - fb_assert(buf != NULL); fb_assert(words <= IPC_MAX_PARAMS);