* EmuNAND support (only one right now, but all I need is a menu/option for more)
* This is both normal layout (RedNAND) and gateway layout (header at back)
* Loader now has three different rel options for text, data, and ro
* Screeninit.
.PHONY: all
-all: loader svc
+all: loader svc screeninit
mkdir -p ../out/corbenik/module
mkdir -p ../out/corbenik/svc
+ mkdir -p ../out/corbenik/bits
cp loader/loader.cxi ../out/corbenik/module/loader.cxi
cp svc/7b.bin ../out/corbenik/svc/7b.bin
+ cp svc/emunand.bin ../out/corbenik/bits/emunand.bin
+ cp screeninit/build/screeninit.bin ../out/corbenik/bits/screeninit.bin
.PHONY: clean
-clean: clean_loader clean_svc
+clean: clean_loader clean_svc clean_screeninit
rm -rf ../out/corbenik/svc
rm -rf ../out/corbenik/module
loader:
make -C loader
-.PHONY: clean_loader
-clean_loader:
- make -C loader clean
-
.PHONY: svc
svc:
make -C svc
+.PHONY: screeninit
+screeninit:
+ make -C screeninit
+
+.PHONY: clean_loader
+clean_loader:
+ make -C loader clean
+
.PHONY: clean_svc
clean_svc:
make -C svc clean
+
+.PHONY: clean_screeninit
+screeninit_clean:
+ make -C screeninit clean
#ifndef __INTERP_H
#define __INTERP_H
-int execb(uint64_t tid, uint16_t ver, uint8_t *search_mem, uint32_t search_len);
+int execb(uint64_t tid, uint16_t ver,
+ uint8_t *text_mem, uint32_t text_len,
+ uint8_t *data_mem, uint32_t data_size,
+ uint8_t *ro_mem, uint32_t ro_size);
#endif
}
// Patch segments
- patch_text(progid, progver, (u8 *)shared->text_addr, shared->text_size << 12, original->text_size << 12);
- patch_data(progid, progver, (u8 *)shared->data_addr, shared->data_size << 12, original->data_size << 12);
- patch_ro(progid, progver, (u8 *)shared->ro_addr, shared->ro_size << 12, original->ro_size << 12);
+ patch_exe (progid, progver, (u8 *)shared->text_addr, shared->text_size << 12, original->text_size << 12,
+ (u8 *)shared->data_addr, shared->data_size << 12, original->data_size << 12,
+ (u8 *)shared->ro_addr, shared->ro_size << 12, original->ro_size << 12);
return 0;
}
// TODO - Implement. Needs some thought. This should allow usage of files off SD rather than RomFS.
}
-// This is only for the .data segment.
-void
-patch_data(u64 progId, u16 progver, u8 *data, u32 size, u32 orig_size)
-{
-}
-
-// This is only for the .ro segment.
-void
-patch_ro(u64 progId, u16 progver, u8 *ro, u32 size, u32 orig_size)
-{
-}
-
// This is only for the .code segment.
void
-patch_text(u64 progId, u16 progver, u8 *text, u32 size, u32 orig_size)
+patch_exe(u64 progId, u16 progver,
+ u8 *text, u32 text_size, u32 orig_text,
+ u8* data, u32 data_size, u32 orig_data,
+ u8* ro, u32 ro_size, u32 orig_ro)
{
if (progId == 0x0004013000008002LL)
- adjust_cpu_settings(progId, text, orig_size);
+ adjust_cpu_settings(progId, text, orig_text);
- execb(progId, progver, text, orig_size);
+ execb(progId, progver, text, orig_text, data, orig_data, ro, orig_ro);
- language_emu(progId, text, orig_size);
+ language_emu(progId, text, orig_text);
}
// Gets how many bytes .text must be extended by for patches to fit.
#include <3ds/types.h>
-void patch_text(u64 progId, u16 progver, u8 *text, u32 size, u32 orig_size);
-void patch_data(u64 progId, u16 progver, u8 *data, u32 size, u32 orig_size);
-void patch_ro(u64 progId, u16 progver, u8 *ro, u32 size, u32 orig_size);
+void patch_exe(u64 progId, u16 progver,
+ u8 *text, u32 text_size, u32 orig_text,
+ u8* data, u32 data_size, u32 orig_data,
+ u8* ro, u32 ro_size, u32 orig_ro);
u32 get_text_extend(u64 progId, u16 progver, u32 size_orig);
u32 get_ro_extend(u64 progId, u16 progver, u32 size_orig);
--- /dev/null
+rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2))
+
+ifeq ($(strip $(DEVKITARM)),)
+$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
+endif
+
+include $(DEVKITARM)/3ds_rules
+
+CC := arm-none-eabi-gcc
+AS := arm-none-eabi-as
+LD := arm-none-eabi-ld
+OC := arm-none-eabi-objcopy
+
+name := $(shell basename $(CURDIR))
+
+dir_source := source
+dir_build := build
+
+ASFLAGS := -mcpu=mpcore -mfloat-abi=hard
+CFLAGS := -Wall -Wextra -MMD -MP -mthumb -mthumb-interwork $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math
+LDFLAGS := -nostdlib
+
+objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
+ $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
+ $(call rwildcard, $(dir_source), *.s *.c)))
+
+.PHONY: all
+all: $(dir_build)/$(name).bin
+
+.PHONY: clean
+clean:
+ @rm -rf $(dir_build)
+
+$(dir_build)/$(name).bin: $(dir_build)/$(name).elf
+ $(OC) -S -O binary $< $@
+
+$(dir_build)/$(name).elf: $(objects)
+ $(LINK.o) -T linker.ld $(OUTPUT_OPTION) $^
+
+$(dir_build)/%.o: $(dir_source)/%.c
+ @mkdir -p "$(@D)"
+ $(COMPILE.c) $(OUTPUT_OPTION) $<
+
+$(dir_build)/%.o: $(dir_source)/%.s
+ @mkdir -p "$(@D)"
+ $(COMPILE.s) $(OUTPUT_OPTION) $<
+include $(call rwildcard, $(dir_build), *.d)
--- /dev/null
+ENTRY(_start)
+SECTIONS
+{
+ . = 0x24FFFC00;
+ .text.start : { *(.text.start) }
+ .text : { *(.text) }
+ .data : { *(.data) }
+ .bss : { *(.bss COMMON) }
+ .rodata : { *(.rodata) }
+ . = ALIGN(4);
+}
+
--- /dev/null
+#include "types.h"
+
+void main(void)
+{
+ // FIXME - We could use some serious macros here...
+
+ u32 brightnessLevel = *(vu32 *)0x24FFFC08;
+ vu32 *const arm11 = (u32 *)0x1FFFFFF8;
+
+ *(vu32 *)0x10141200 = 0x1007F;
+ *(vu32 *)0x10202014 = 0x00000001;
+ *(vu32 *)0x1020200C &= 0xFFFEFFFE;
+ *(vu32 *)0x10202240 = brightnessLevel; // Alteration; directly read brightness.
+ *(vu32 *)0x10202A40 = brightnessLevel;
+ *(vu32 *)0x10202244 = 0x1023E;
+ *(vu32 *)0x10202A44 = 0x1023E;
+
+ // Top screen
+ *(vu32 *)0x10400400 = 0x000001c2;
+ *(vu32 *)0x10400404 = 0x000000d1;
+ *(vu32 *)0x10400408 = 0x000001c1;
+ *(vu32 *)0x1040040c = 0x000001c1;
+ *(vu32 *)0x10400410 = 0x00000000;
+ *(vu32 *)0x10400414 = 0x000000cf;
+ *(vu32 *)0x10400418 = 0x000000d1;
+ *(vu32 *)0x1040041c = 0x01c501c1;
+ *(vu32 *)0x10400420 = 0x00010000;
+ *(vu32 *)0x10400424 = 0x0000019d;
+ *(vu32 *)0x10400428 = 0x00000002;
+ *(vu32 *)0x1040042c = 0x00000192;
+ *(vu32 *)0x10400430 = 0x00000192;
+ *(vu32 *)0x10400434 = 0x00000192;
+ *(vu32 *)0x10400438 = 0x00000001;
+ *(vu32 *)0x1040043c = 0x00000002;
+ *(vu32 *)0x10400440 = 0x01960192;
+ *(vu32 *)0x10400444 = 0x00000000;
+ *(vu32 *)0x10400448 = 0x00000000;
+ *(vu32 *)0x1040045C = 0x00f00190;
+ *(vu32 *)0x10400460 = 0x01c100d1;
+ *(vu32 *)0x10400464 = 0x01920002;
+ *(vu32 *)0x10400468 = 0x18300000;
+ *(vu32 *)0x10400470 = 0x80341;
+ *(vu32 *)0x10400474 = 0x00010501;
+ *(vu32 *)0x10400478 = 0;
+ *(vu32 *)0x10400490 = 0x000002D0;
+ *(vu32 *)0x1040049C = 0x00000000;
+
+ // Disco register
+ for(u32 i = 0; i < 256; i++)
+ *(vu32 *)0x10400484 = 0x10101 * i;
+
+ // Bottom screen
+ *(vu32 *)0x10400500 = 0x000001c2;
+ *(vu32 *)0x10400504 = 0x000000d1;
+ *(vu32 *)0x10400508 = 0x000001c1;
+ *(vu32 *)0x1040050c = 0x000001c1;
+ *(vu32 *)0x10400510 = 0x000000cd;
+ *(vu32 *)0x10400514 = 0x000000cf;
+ *(vu32 *)0x10400518 = 0x000000d1;
+ *(vu32 *)0x1040051c = 0x01c501c1;
+ *(vu32 *)0x10400520 = 0x00010000;
+ *(vu32 *)0x10400524 = 0x0000019d;
+ *(vu32 *)0x10400528 = 0x00000052;
+ *(vu32 *)0x1040052c = 0x00000192;
+ *(vu32 *)0x10400530 = 0x00000192;
+ *(vu32 *)0x10400534 = 0x0000004f;
+ *(vu32 *)0x10400538 = 0x00000050;
+ *(vu32 *)0x1040053c = 0x00000052;
+ *(vu32 *)0x10400540 = 0x01980194;
+ *(vu32 *)0x10400544 = 0x00000000;
+ *(vu32 *)0x10400548 = 0x00000011;
+ *(vu32 *)0x1040055C = 0x00f00140;
+ *(vu32 *)0x10400560 = 0x01c100d1;
+ *(vu32 *)0x10400564 = 0x01920052;
+ *(vu32 *)0x10400568 = 0x18300000 + 0x46500;
+ *(vu32 *)0x10400570 = 0x80301;
+ *(vu32 *)0x10400574 = 0x00010501;
+ *(vu32 *)0x10400578 = 0;
+ *(vu32 *)0x10400590 = 0x000002D0;
+ *(vu32 *)0x1040059C = 0x00000000;
+
+ // Disco register
+ for(u32 i = 0; i < 256; i++)
+ *(vu32 *)0x10400584 = 0x10101 * i;
+
+ *(vu32 *)0x10400468 = 0x18300000;
+ *(vu32 *)0x1040046c = 0x18300000;
+ *(vu32 *)0x10400494 = 0x18300000;
+ *(vu32 *)0x10400498 = 0x18300000;
+ *(vu32 *)0x10400568 = 0x18346500;
+ *(vu32 *)0x1040056c = 0x18346500;
+
+ //Set CakeBrah framebuffers
+ *((vu32 *)0x23FFFE00) = 0x18300000;
+ *((vu32 *)0x23FFFE04) = 0x18300000;
+ *((vu32 *)0x23FFFE08) = 0x18346500;
+
+ //Clear ARM11 entry offset
+ *arm11 = 0;
+
+ //Wait for the entry to be set
+ while(!*arm11);
+
+ //Jump to it
+ ((void (*)())*arm11)();
+}
--- /dev/null
+.section .text.start
+.align 4
+.global _start
+_start:
+ @ Disable interrupts
+ CPSID aif
+
+ b main
+
+ .word 0
--- /dev/null
+#pragma once
+
+#include <stdint.h>
+
+//Common data types
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+typedef volatile u8 vu8;
+typedef volatile u16 vu16;
+typedef volatile u32 vu32;
+typedef volatile u64 vu64;
\ No newline at end of file
PATH := $(PATH):$(DEVKITARM)/bin
-all: 7b.bin stub.bin
+all: 7b.bin stub.bin emunand.bin
%.o: %.s
arm-none-eabi-as -o $@ $<
--- /dev/null
+.section .text
+.global _start
+_start:
+ // Original code that still needs to be executed.
+ mov r4, r0
+ mov r5, r1
+ mov r7, r2
+ mov r6, r3
+
+main:
+ // If we're already trying to access the SD, return.
+ ldr r2, [r0, #4]
+ ldr r1, sdmmc // In armips this instruction uses pc-releative loading
+ cmp r2, r1
+ beq nand_sd_ret
+
+ str r1, [r0, #4] // Set object to be SD
+ ldr r2, [r0, #8] // Get sector to read
+ cmp r2, #0 // Gateway compat
+
+ ldr r3, nand_offset // ^ see above
+ add r2, r3 // Add the offset to the NAND in the SD.
+
+ ldreq r3, ncsd_offset // ^ see above
+ addeq r2, r3
+
+ str r2, [r0, #8] // Store sector to read
+
+ nand_sd_ret:
+ // Restore registers.
+ mov r0, r4
+ mov r1, r5
+ mov r2, r7
+ mov r3, r6
+
+ // Return 4 bytes behind where we got called,
+ // due to the offset of this function being stored there.
+ mov r0, lr
+ add r0, #4
+ bx r0
+
+sdmmc: .ascii "SDMC" // The offset of the sdmmc object.
+nand_offset: .ascii "NAND" // The starting offset of the emuNAND on the SD.
+ncsd_offset: .ascii "NCSD" // Location of the NCSD header relative to nand_offset
+
--- /dev/null
+.arm.little
+
+.create "patch1.bin", 0
+.arm
+nand_sd:
+ ; Original code that still needs to be executed.
+ mov r4, r0
+ mov r5, r1
+ mov r7, r2
+ mov r6, r3
+ ; End.
+
+ ; If we're already trying to access the SD, return.
+ ldr r2, [r0, #4]
+ ldr r1, [sdmmc]
+ cmp r2, r1
+ beq nand_sd_ret
+
+ str r1, [r0, #4] ; Set object to be SD
+ ldr r2, [r0, #8] ; Get sector to read
+ cmp r2, #0 ; For GW compatibility, see if we're trying to read the ncsd header (sector 0)
+
+ ldr r3, [nand_offset]
+ add r2, r3 ; Add the offset to the NAND in the SD.
+
+ ldreq r3, [ncsd_header_offset]
+ addeq r2, r3 ; If we're reading the ncsd header, add the offset of that sector.
+
+ str r2, [r0, #8] ; Store sector to read
+
+ nand_sd_ret:
+ ; Restore registers.
+ mov r1, r5
+ mov r2, r7
+ mov r3, r6
+
+ ; Return 4 bytes behind where we got called,
+ ; due to the offset of this function being stored there.
+ mov r0, lr
+ add r0, #4
+ bx r0
+.pool
+nand_offset: .ascii "NAND" ; The starting offset of the emuNAND on the SD.
+ncsd_header_offset: .ascii "NCSD" ; The offset of the first emuNAND sector relative to the start of the emuNAND (Used for when the first sector is placed at the end).
+sdmmc: .ascii "sdmmc" ; The offset of the sdmmc object.
+.close
+
+.create "patch2.bin", 0
+.arm
+ .word 0x360003
+ .word 0x10100000
+ .word 0x1000001
+ .word 0x360003
+ .word 0x20000000
+ .word 0x1010101
+ .word 0x200603
+ .word 0x8000000
+ .word 0x1010101
+ .word 0x1C0603
+ .word 0x8020000
+.close
+
+.create "patch3.bin", 0
+.thumb
+ ldr r4, [_nand_sd_write]
+ blx r4
+.align 4
+_nand_sd_write: .ascii "mem"
+.close
+
+.create "patch4.bin", 0
+.thumb
+ ldr r4, [_nand_sd_read]
+ blx r4
+.align 4
+_nand_sd_read: .ascii "mem"
+.close
'twl_s1' : "0F",
'twl_s2' : "10",
'twl_s3' : "11",
+
+ 'exe_text' : "12",
+ 'exe_data' : "13",
+ 'exe_ro' : "14",
}.get(x, "-1")
name = "NO NAME"
// Allow enabling patches which are marked as 'incompatible'. Chances are there's a reason.
#define IGNORE_BROKEN_SHIT 15
+// Whether to use an EmuNAND
+#define OPTION_EMUNAND 16
+
// Save log files during boot and from loader.
// This will slow things down a bit.
#define OPTION_SAVE_LOGS 253
LEAVE_FF(dj.fs, res);
}
+/*-----------------------------------------------------------------------*/
+/* Read File */
+/*-----------------------------------------------------------------------*/
+
+
+FRESULT
+f_getsector(FIL *fp, /* Pointer to the file object */
+ UINT *sec /* Pointer to uint which will contain the sector */
+ )
+{
+ FRESULT res;
+ DWORD clst = 0, sect = 0;
+ BYTE csect;
+
+ res = validate(fp); /* Check validity */
+
+ if (res != FR_OK)
+ LEAVE_FF(fp->fs, res);
+ if (fp->err) /* Check error */
+ LEAVE_FF(fp->fs, (FRESULT)fp->err);
+ if (!(fp->flag & FA_READ)) /* Check access mode */
+ LEAVE_FF(fp->fs, FR_DENIED);
+
+ if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */
+ csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */
+ if (!csect) { /* On the cluster boundary? */
+ if (fp->fptr == 0) { /* On the top of the file? */
+ clst = fp->sclust; /* Follow from the origin */
+ } else { /* Middle or end of the file */
+ clst = get_fat(fp->fs, fp->clust); /* Follow cluster chain on the FAT */
+ }
+
+ if (clst < 2)
+ ABORT(fp->fs, FR_INT_ERR);
+ if (clst == 0xFFFFFFFF)
+ ABORT(fp->fs, FR_DISK_ERR);
+ }
+
+ sect = clust2sect(fp->fs, clst); /* Get current sector */
+ if (!sect)
+ ABORT(fp->fs, FR_INT_ERR);
+ sect += csect;
+ }
+
+ *sec = sect;
+
+ return 0;
+}
+
+
/*-----------------------------------------------------------------------*/
/* Read File */
/*-----------------------------------------------------------------------*/
FRESULT f_mount(FATFS *fs, const TCHAR *path, BYTE opt); /* Mount/Unmount a logical drive */
FRESULT f_mkfs(const TCHAR *path, BYTE sfd, UINT au); /* Create a file system on the volume */
FRESULT f_fdisk(BYTE pdrv, const DWORD szt[], void *work); /* Divide a physical drive into some partitions */
+FRESULT f_getsector(FIL *fp, UINT *sec);
int f_putc(TCHAR c, FIL *fp); /* Put a character to the file */
int f_puts(const TCHAR *str, FIL *cp); /* Put a string to the file */
int f_printf(FIL *fp, const TCHAR *str, ...); /* Put a formatted string to the file */
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
+// LEGAL - Note that despite the above notice, it must be used under
+// MPL 2.0 here. GPLv2 and GPLv3 are not compatible, and his software
+// delcares specifically GPLv2. We have to treat it as MPL 2.0 to
+// not violate any licensing.
+
#include <stdint.h>
-#include <string.h>
-#include <stdlib.h>
+#include <stddef.h>
#include <inttypes.h>
-#include <malloc.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <dirent.h>
-#include <errno.h>
#include "sdmmc.h"
//#include "DrawCharacter.h"
uint8_t *memory;
uint32_t size;
};
-
-struct mode modes[19];
+struct mode modes[21];
int init_bytecode = 0;
int
modes[5].size = firm_p9_exefs->fileHeaders[0].size;
// NATIVE_FIRM Sect 0
- modes[6].memory = (uint8_t *)&firm_loc->section[0] + firm_loc->section[0].offset;
+ modes[6].memory = (uint8_t *)firm_loc + firm_loc->section[0].offset;
modes[6].size = firm_loc->section[0].size;
// NATIVE_FIRM Sect 1
- modes[7].memory = (uint8_t *)&firm_loc->section[1] + firm_loc->section[1].offset;
+ modes[7].memory = (uint8_t *)firm_loc + firm_loc->section[1].offset;
modes[7].size = firm_loc->section[1].size;
// NATIVE_FIRM Sect 2
- modes[8].memory = (uint8_t *)&firm_loc->section[2] + firm_loc->section[2].offset;
+ modes[8].memory = (uint8_t *)firm_loc + firm_loc->section[2].offset;
modes[8].size = firm_loc->section[2].size;
// NATIVE_FIRM Sect 3
- modes[9].memory = (uint8_t *)&firm_loc->section[3] + firm_loc->section[3].offset;
+ modes[9].memory = (uint8_t *)firm_loc + firm_loc->section[3].offset;
modes[9].size = firm_loc->section[3].size;
// AGB_FIRM Sect 0
- modes[10].memory = (uint8_t *)&agb_firm_loc->section[0] + agb_firm_loc->section[0].offset;
+ modes[10].memory = (uint8_t *)agb_firm_loc + agb_firm_loc->section[0].offset;
modes[10].size = agb_firm_loc->section[0].size;
// AGB_FIRM Sect 1
- modes[11].memory = (uint8_t *)&agb_firm_loc->section[1] + agb_firm_loc->section[1].offset;
+ modes[11].memory = (uint8_t *)agb_firm_loc + agb_firm_loc->section[1].offset;
modes[11].size = agb_firm_loc->section[1].size;
// AGB_FIRM Sect 2
- modes[12].memory = (uint8_t *)&agb_firm_loc->section[2] + agb_firm_loc->section[2].offset;
+ modes[12].memory = (uint8_t *)agb_firm_loc + agb_firm_loc->section[2].offset;
modes[12].size = agb_firm_loc->section[2].size;
// AGB_FIRM Sect 3
- modes[13].memory = (uint8_t *)&agb_firm_loc->section[3] + agb_firm_loc->section[3].offset;
+ modes[13].memory = (uint8_t *)agb_firm_loc + agb_firm_loc->section[3].offset;
modes[13].size = agb_firm_loc->section[3].size;
// TWL_FIRM Sect 0
- modes[14].memory = (uint8_t *)&twl_firm_loc->section[0] + twl_firm_loc->section[0].offset;
+ modes[14].memory = (uint8_t *)twl_firm_loc + twl_firm_loc->section[0].offset;
modes[14].size = twl_firm_loc->section[0].size;
// TWL_FIRM Sect 1
- modes[15].memory = (uint8_t *)&twl_firm_loc->section[1] + twl_firm_loc->section[1].offset;
+ modes[15].memory = (uint8_t *)twl_firm_loc + twl_firm_loc->section[1].offset;
modes[15].size = twl_firm_loc->section[1].size;
// TWL_FIRM Sect 2
- modes[16].memory = (uint8_t *)&twl_firm_loc->section[2] + twl_firm_loc->section[2].offset;
+ modes[16].memory = (uint8_t *)twl_firm_loc + twl_firm_loc->section[2].offset;
modes[16].size = twl_firm_loc->section[2].size;
// TWL_FIRM Sect 3
- modes[17].memory = (uint8_t *)&twl_firm_loc->section[3] + twl_firm_loc->section[3].offset;
+ modes[17].memory = (uint8_t *)twl_firm_loc + twl_firm_loc->section[3].offset;
modes[17].size = twl_firm_loc->section[3].size;
#endif
- // Loader (not valid in bootmode)
- // modes[18] = { 0, 0 };
-
init_bytecode = 1;
}
test_was_false = 0;
break;
case OP_REL: // Change relativity.
-#ifdef LOADER
- // Loader doesn't support this. Just treat it like a two-byte NOP.
- code += 2;
-#else
if (debug)
log("rel\n");
code++;
test_was_false = 0;
set_mode = *code;
code++;
-#endif
break;
case OP_FIND: // Find pattern.
if (debug)
#ifndef LOADER
set_mode = 3;
current_mode = &modes[set_mode];
+#else
+ set_mode = 18;
+ current_mode = &modes[set_mode];
#endif
offset = 0;
test_was_false = 0;
#ifdef LOADER
int
-execb(uint64_t tid, uint16_t ver, uint8_t *search_mem, uint32_t search_len)
+execb(uint64_t tid, uint16_t ver,
+ uint8_t *text_mem, uint32_t text_len,
+ uint8_t* data_mem, uint32_t data_len,
+ uint8_t* ro_mem, uint32_t ro_len)
{
#else
int
FSFILE_Close(file); // Done reading in.
// Set memory.
- modes[18].memory = search_mem;
- modes[18].size = search_len;
+ modes[18].memory = text_mem;
+ modes[18].size = text_len;
+
+ // Set memory.
+ modes[19].memory = data_mem;
+ modes[19].size = data_len;
+
+ // Set memory.
+ modes[20].memory = ro_mem;
+ modes[20].size = ro_len;
log(" exec\n");
#include "firm/firm.h"
#include "input.h"
#include "config.h"
+#include "screeninit.h"
+#include "std/abort.h"
int menu_handler();
int
main()
{
- if (fmount()) {
+ int c = fmount();
+ screen_init();
+
+ if (c) {
// Failed to mount SD. Bomb out.
- fprintf(BOTTOM_SCREEN, "%pFailed to mount SD card.\n", COLOR(RED, BLACK));
+ abort("Failed to mount SD card.\n");
}
load_config(); // Load configuration.
{ 0, "", "", not_option, 0, 0 },
+ { OPTION_EMUNAND, "EmuNAND", "Redirects NAND write/read to EmuNAND #0. Right now, only one is supported (but the work is done)", boolean_val, 0, 0 },
+
+ { 0, "", "", not_option, 0, 0 },
+
{ OPTION_AUTOBOOT, "Autoboot", "Boot the system automatically, unless the R key is held.", boolean_val, 0, 0 },
{ OPTION_SILENCE, " Silent mode", "Suppress all debug output during autoboot. You'll see the screen turn on, then off.", boolean_val, 0, 0 },
--- /dev/null
+/*
+* emunand.c
+*/
+
+#include "emunand.h"
+#include "../std/memory.h"
+#include "../std/draw.h"
+#include "../std/fs.h"
+#include "../std/abort.h"
+#include "../firm/firm.h"
+#include "../firm/fcram.h"
+#include "../fatfs/sdmmc.h"
+#include "../firm/headers.h"
+#include "../patch_format.h"
+
+uint8_t *temp = (uint8_t*)FCRAM_JUNK_LOC;
+
+void verify_loop_emunand(char* filename) {
+ // FIXME - This won't work unless the NAND file is completely contiguous on disk, sadly.
+ // Technically speaking if I were to defrag my SD this would work, I suspect.
+
+ uint32_t offset = get_file_sector(filename); // Get the sector of the file
+
+ // Check for RedNAND image on SD
+ if(!sdmmc_sdcard_readsectors(offset, 1, temp) && *(uint32_t *)(temp + 0x100) == NCSD_MAGIC) {
+ fprintf(stderr, "emunand: found NCSD magic\n");
+ } else {
+ abort("emunand: selected NAND image is not valid.\n");
+ }
+}
+
+void verify_emunand(uint32_t index, uint32_t* off, uint32_t* head) {
+ uint32_t nandSize = getMMCDevice(0)->total_size;
+
+ uint32_t offset;
+ if (nandSize > 0x200000)
+ offset = 0x400000 * index;
+ else
+ offset = 0x200000 * index;
+
+ // Check for RedNAND/Normal physical layout on SD
+ if(!sdmmc_sdcard_readsectors(offset + 1, 1, temp) && *(uint32_t *)(temp + 0x100) == NCSD_MAGIC) {
+ *off = offset + 1;
+ *head = offset + 1;
+
+ fprintf(stderr, "emunand: found NCSD magic for #%u\n", index);
+ fprintf(stderr, "emunand: layout is normal\n");
+ }
+ // Check for GW EmuNAND on SD
+ else if(!sdmmc_sdcard_readsectors(offset + nandSize, 1, temp) && *(uint32_t *)(temp + 0x100) == NCSD_MAGIC) {
+ *off = offset;
+ *head = offset + nandSize;
+
+ fprintf(stderr, "emunand: found NCSD magic for %u\n", index);
+ fprintf(stderr, "emunand: layout is gateway\n");
+ } else {
+ abort("emunand: selected NAND image is not valid.\n");
+ }
+}
+
+static void *getEmuCode(uint8_t *pos, uint32_t size)
+{
+ const uint8_t pattern[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
+
+ //Looking for the last free space before Process9
+ void* ret = memfind(pos + 0x13500, size - 0x13500, pattern, 6) + 0x455;
+
+ if (ret) {
+ fprintf(stderr, "emunand: free space @ %x\n", ret);
+ fprintf(stderr, "emunand: size is %u bytes\n", (uint8_t*)ret - pos);
+ }
+
+ return ret;
+}
+
+static uint32_t getSDMMC(uint8_t *pos, uint32_t size)
+{
+ //Look for struct code
+ const uint8_t pattern[] = {0x21, 0x20, 0x18, 0x20};
+ const uint8_t *off = memfind(pos, size, pattern, 4);
+
+ uint32_t ret = *(uint32_t *)(off + 9) + *(uint32_t *)(off + 0xD);
+
+ fprintf(stderr, "emunand: SDMMC code @ %x\n", ret);
+
+ return ret;
+}
+
+static void patchNANDRW(uint8_t *pos, uint32_t size, uint32_t branchOffset)
+{
+ const uint16_t nandRedir[2] = {0x4C00, 0x47A0};
+
+ //Look for read/write code
+ const uint8_t pattern[] = {0x1E, 0x00, 0xC8, 0x05};
+
+ uint16_t *readOffset = (uint16_t *)memfind(pos, size, pattern, 4) - 3;
+ uint16_t *writeOffset = (uint16_t *)memfind((uint8_t *)(readOffset + 5), 0x100, pattern, 4) - 3;
+
+ *readOffset = nandRedir[0];
+ readOffset[1] = nandRedir[1];
+ ((uint32_t *)readOffset)[1] = branchOffset;
+
+ *writeOffset = nandRedir[0];
+ writeOffset[1] = nandRedir[1];
+ ((uint32_t *)writeOffset)[1] = branchOffset;
+
+ fprintf(stderr, "emunand: write @ %x\n", writeOffset);
+ fprintf(stderr, "emunand: read @ %x\n", readOffset);
+}
+
+static void patchMPU(uint8_t *pos, uint32_t size)
+{
+ const uint32_t mpuPatch[3] = {0x00360003, 0x00200603, 0x001C0603};
+
+ //Look for MPU pattern
+ const uint8_t pattern[] = {0x03, 0x00, 0x24, 0x00};
+
+ uint32_t *off = (uint32_t *)memfind(pos, size, pattern, 4);
+
+ off[0] = mpuPatch[0];
+ off[6] = mpuPatch[1];
+ off[9] = mpuPatch[2];
+
+ fprintf(stderr, "emunand: mpu @ %x\n", off);
+}
+
+void patch_emunand(uint32_t index) {
+ //uint8_t *arm9Section, uint32_t arm9SectionSize, uint8_t *process9Offset, uint32_t process9Size, uint32_t emuOffset, uint32_t emuHeader, uint32_t branchAdditive)
+
+ // ARM9 section.
+ uint8_t* arm9Section = (uint8_t*)firm_loc + firm_loc->section[2].offset;
+ uint32_t arm9SectionSize = firm_loc->section[2].size;
+
+ uint8_t* process9Offset = (uint8_t *)firm_p9_exefs + sizeof(exefs_h) + firm_p9_exefs->fileHeaders[0].offset;
+ uint32_t process9Size = firm_p9_exefs->fileHeaders[0].size;
+
+ //Copy emuNAND code
+ void *emuCodeOffset = getEmuCode(arm9Section, arm9SectionSize);
+ if (!emuCodeOffset)
+ abort("emunand: code missing from arm9?\n");
+
+ FILE* f = fopen(PATH_EMUNAND_CODE, "r");
+ if (!f)
+ abort("emunand: code not found on SD.\n");
+
+ uint32_t emunand_size = fsize(f);
+ fread(emuCodeOffset, 1, emunand_size, f);
+ fclose(f);
+
+ uint32_t branchOffset = (uint32_t)emuCodeOffset - ((uint32_t)firm_loc + firm_loc->section[2].offset - firm_loc->section[2].address);
+
+ fprintf(stderr, "emunand: read in emunand code - %x\n", emuCodeOffset);
+
+ //Add the data of the found emuNAND
+ uint32_t *pos_offset = (uint32_t *)memfind(emuCodeOffset, emunand_size, "NAND", 4),
+ *pos_head = (uint32_t *)memfind(emuCodeOffset, emunand_size, "NCSD", 4),
+ *pos_sdmmc = (uint32_t *)memfind(emuCodeOffset, emunand_size, "SDMC", 4);
+
+ verify_emunand(index, pos_offset, pos_head);
+
+ fprintf(stderr, "emunand: nand is on sector %u\n", *pos_offset);
+ fprintf(stderr, "emunand: head is on sector %u\n", *pos_head);
+
+ if (!pos_offset || !pos_head || !pos_sdmmc)
+ abort("emunand: couldn't find pattern in hook?\n");
+
+ //Add emuNAND hooks
+ patchNANDRW(process9Offset, process9Size, branchOffset);
+
+ fprintf(stderr, "emunand: patched read/write calls\n");
+
+ //Find and add the SDMMC struct
+
+ *pos_sdmmc = getSDMMC(process9Offset, process9Size);
+
+ //Set MPU for emu code region
+ patchMPU(arm9Section, arm9SectionSize);
+
+ fprintf(stderr, "emunand: patched MPU settings\n");
+}
+
--- /dev/null
+#ifndef __EMUNAND_H
+#define __EMUNAND_H
+
+#include <stdint.h>
+
+void patch_emunand(uint32_t size);
+
+#endif
// additionally the root if not found.
#define PATH_EXEFS PATH_CFW "/exe" // ExeFS overrides, named like '<titleid>.exefs' - NYI
+#define PATH_BITS PATH_CFW "/bits" // Path to misc bits we need (emunand code, reboot code, etc)
+
+#define PATH_EMUNAND_CODE PATH_BITS "/emunand.bin" // Emunand hook.
+#define PATH_SCREENINIT_CODE PATH_BITS "/screeninit.bin" // Screeninit code (ARM11)
+
#define PATH_NATIVE_F PATH_FIRMWARES "/native"
#define PATH_AGB_F PATH_FIRMWARES "/agb"
#define PATH_TWL_F PATH_FIRMWARES "/twl"
#include "config.h"
#include "common.h"
#include "interp.h"
+#include "patch/emunand.h"
// TODO - Basically all this needs to move to patcher programs.
wait();
}
+ // Use loopback EmuNAND?
+ if (config.options[OPTION_EMUNAND]) {
+ // Yes.
+ patch_emunand(0);
+// patch_loop_emunand("/nand.bin");
+
+ // FIXME - NYI
+ wait();
+ }
+
return 0;
}
--- /dev/null
+#include "std/fs.h"
+#include "i2c.h"
+#include "patch_format.h"
+
+#define PDN_GPU_CNT (*(volatile uint8_t *)0x10141200)
+
+static volatile uint32_t *const a11_entry = (volatile uint32_t *)0x1FFFFFF8;
+
+void screen_init() {
+ if (PDN_GPU_CNT == 1) {
+ uint32_t* screenInitAddress = (uint32_t*)0x24FFFC00;
+
+ FILE* f = fopen(PATH_SCREENINIT_CODE, "r");
+ fread(screenInitAddress, 1, fsize(f), f); // Read in the screeninit payload.
+ fclose(f);
+
+ //Write brightness level for the stub to pick up
+ screenInitAddress[2] = 0x5F;
+ *a11_entry = (uint32_t)screenInitAddress;
+
+ while(*a11_entry);
+
+ //Turn on backlight
+ i2cWriteRegister(I2C_DEV_MCU, 0x22, 0x2A);
+ }
+}
--- /dev/null
+#ifndef __SCREENINIT_H
+#define __SCREENINIT_H
+
+// Inits the screen if needed.
+void screen_init();
+
+#endif
fseek(f, 0, SEEK_END);
for (int i = 0; i < TEXT_BOTTOM_HEIGHT; i++) {
char *text = text_buffer_bottom + (TEXT_BOTTOM_WIDTH * i);
- if (text[0] == 0)
- break;
+ for(int j = 0; j < TEXT_BOTTOM_WIDTH; j++) {
+ if (text[j] == 0)
+ text[j] = ' ';
+ }
fwrite(text, 1, strnlen(text, TEXT_BOTTOM_WIDTH), f);
fwrite("\n", 1, 1, f);
}
return read;
}
+
+// DWORD fptr; /* File read/write pointer (Zeroed on file open) */
+// DWORD fsize; /* File size */
+// DWORD sclust; /* File start cluster (0:no cluster chain, always 0 when fsize is 0) */
+// DWORD clust; /* Current cluster of fpter (not valid when fprt is 0) */
+// DWORD dsect; /* Sector number appearing in buf[] (0:invalid) */
+
+size_t get_file_sector(char* path) {
+ FILE* temp = fopen(path, "r");
+
+ if (!temp || !temp->is_open)
+ return 0;
+
+ UINT sector;
+
+ f_getsector(& temp->handle, §or);
+
+ fclose(temp);
+
+ return sector;
+}
size_t write_file(void *data, char *path, size_t size);
size_t read_file(void *data, char *path, size_t size);
+size_t get_file_sector(char* path);
+
#endif
#define __STD_TYPES_H
#include <stdint.h>
-#include <stdlib.h>
#define CFG_BOOTENV *(volatile uint32_t *)0x10010000
#define HID ~*(volatile uint32_t *)0x10146000