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);