$(call rwildcard, $(dir_source), *.s *.c)))
.PHONY: all
-all: a9lh
+all: a9lh vco
+
+.PHONY: vco
+vco:
+ make -C vco
.PHONY: a9lh
a9lh: $(dir_out)/arm9loaderhax.bin
.PHONY: clean
clean:
+ make -C vco clean
rm -rf $(dir_out) $(dir_build)
.PHONY: $(dir_out)/arm9loaderhax.bin
// This is svcBackdoor's code from earlier FIRMs
.arm.little
.create "backdoor.bin", 0
+ // Luckily, no ARM9/ARM11 specific instructions are used here.
+ // It can just be assembled via ARM9 gas.
bic r1, sp, #0xff
orr r1, r1, #0xf00
add r1, r1, #0x28
#!/bin/bash
-mount /dev/sdb1 /media/sd
-cp out/arm9loaderhax.bin /media/sd/anim/boot/a.bin
-umount /media/sd
+mount /dev/sdb1 /media/cd || exit 0
+cp out/arm9loaderhax.bin /media/cd/anim/boot/a.bin || exit 0
+cp -r out/corbenik /media/cd/ || exit 0
+umount /media/cd || exit 0
+eject /dev/sdb || exit 0
+++ /dev/null
-You're probably wondering what the heck corbenik does differently from cakes,
-considering it seems similar.
-
-Patches are actually code for whatever processor they're intended to run on.
-They're loaded to a static offset in memory, and executed.
-
-Patches should have a declaration of this sort somewhere in them:
- { 0xc0, 0x9b, 0xe5, 0x1c }
-Followed by a table large enough to fill with all usable functions.
-
-See template/ for how this works.
-
-When the loader finds that magic value, it fills the table with
-the offsets of important variables and functions for use by the patcher, much
-like how an ELF loader sets up references. In other words; patches are binaries
-that are relocated relative to a 'standard library'.
-
-There's some key differences, obviously, from running just arm9loader code.
-Namely:
- 1) Patches must not clobber the previous state.
- 2) Patches must properly return.
- 3) Patches must have a symbol table with the appropriate magic.
- 4) Patches shouldn't do anything aside from change data.
- 5) _start must be at offset 0x24400000.
-
-Basically, don't get fancy in _start. Just do a bl main; bx lr. You can implement
-shit yourself, but it's an utter waste of memory. Try not to.
+++ /dev/null
-#ifndef EXPORTED_H
-#define EXPORTED_H
-
-#define stdout (void*)0
-#define stderr (void*)1
-
-extern uint8_t* memory_offset;
-extern uint32_t* memory_len;
-
-extern int strlen(const char *string);
-extern int isprint(char c);
-extern void memcpy(void *dest, const void *src, size_t size);
-extern void memmove(void *dest, const void *src, size_t size);
-extern void memset(void *dest, const int filler, size_t size);
-extern int memcmp(const void *buf1, const void *buf2, const size_t size);
-extern void strncpy(void *dest, const void *src, const size_t size);
-extern int strncmp(const void *buf1, const void *buf2, const size_t size);
-extern int atoi(const char *str);
-extern uint8_t* memfind (uint8_t *string, uint32_t stringlen, uint8_t *pat, uint32_t patlen);
-
-extern void putc(void* buf, const int c);
-extern void puts(void* buf, const char *string);
-extern void fprintf(void* channel, const char* format, ...);
-
-#endif
+++ /dev/null
-.section .text.table
-.align 4
-
-.macro stub name
- .global \name
- \name :
- .byte 0
- .byte 0
- .byte 0
- .byte 0
-.endm
-
-.global MAGIC_START
-MAGIC_START:
- .byte 0xc0
- .byte 0x9b
- .byte 0xe5
- .byte 0x1c
-
-// Memory to patch as specified by the header. (uint8_t*)
-stub memory_offset
-
-// Size of memory offset. (uint32_t*)
-stub memory_len
-
-// Exported functions.
-
-// memory.c
-stub strlen
-stub isprint
-stub memcpy
-stub memmove
-stub memset
-stub memcmp
-stub strncpy
-stub strncmp
-stub atoi
-stub memfind
-
-// draw.c
-stub putc
-stub puts
-stub fprintf
+++ /dev/null
-#include <stdint.h>
-#include <stddef.h>
-#include "exported.h"
-
-int main() {
- //Look for signature checks
- uint8_t pat1[] = {0xC0, 0x1C, 0x76, 0xE7};
- uint8_t pat2[] = {0xB5, 0x22, 0x4D, 0x0C};
-
- uint8_t *firm_mem = (uint8_t*)memory_offset;
- uint32_t size = *(uint32_t*)memory_len;
-
- uint8_t *off = memfind(firm_mem, size, pat1, 4);
- uint8_t *off2 = memfind(firm_mem, size, pat2, 4) - 1;
-
- if (off == NULL) {
- fprintf(stderr, "Signature patch failed on P0.\n");
- return 1; // Failed to find sigpatch. Ugh.
- }
-
- if (off2 == NULL) {
- fprintf(stderr, "Signature patch failed on P1.\n");
- return 2; // Failed to find sigpatch. Ugh.
- }
-
- fprintf(stderr, "Signatures[0]: 0x%x\n", (uint32_t)off);
- fprintf(stderr, "Signatures[1]: 0x%x\n", (uint32_t)off2);
-
- uint8_t sigpatch[] = {0x00, 0x20, 0x70, 0x47};
-
- memcpy(off, sigpatch, 2);
- memcpy(off2, sigpatch, 4);
- fprintf(stderr, "Signature patch succeded.\n");
-
- return 0;
-}
+++ /dev/null
-#include <stdint.h>
-#include "std/unused.h"
-#include "std/memory.h"
-#include "firm/firm.h"
-#include "config.h"
-#include "common.h"
-
-uint32_t wait_key();
-
-// A portion of this file is inherited from Luma3DS.
-/*
-u16 *getFirmWrite(u8 *pos, u32 size) {
- //Look for FIRM writing code
- u8 *const off = memsearch(pos, "exe:", size, 4);
- const u8 pattern[] = {0x00, 0x28, 0x01, 0xDA};
-
- return (u16 *)memsearch(off - 0x100, pattern, 0x100, 4);
-}
-
-u16 *getFirmWriteSafe(u8 *pos, u32 size) {
- //Look for FIRM writing code
- const u8 pattern[] = {0x04, 0x1E, 0x1D, 0xDB};
-
- return (u16 *)memsearch(pos, pattern, size, 4);
-}
-
-u32 getLoader(u8 *pos, u32 *loaderSize) {
- u8 *off = pos;
- u32 size;
-
- while(1)
- {
- size = *(u32 *)(off + 0x104) * 0x200;
- if(*(u32 *)(off + 0x200) == 0x64616F6C) break;
- off += size;
- }
-
- *loaderSize = size;
-
- return (u32)(off - pos);
-}
-
-
-// patch_location = (void *)((uintptr_t)firm + section->offset + (version->offset - section->address));
-
-u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
-{
- u8 *off = memsearch(pos, "ess9", size, 4);
-
- *process9Size = *(u32 *)(off - 0x60) * 0x200;
- *process9MemAddr = *(u32 *)(off + 0xC);
-
- //Process9 code offset (start of NCCH + ExeFS offset + ExeFS header size)
- return off - 0x204 + (*(u32 *)(off - 0x64) * 0x200) + 0x200;
-}
-*/
-
-int patch_signatures() {
- //Look for signature checks
-
- uint8_t pat1[] = {0xC0, 0x1C, 0x76, 0xE7};
- uint8_t pat2[] = {0xB5, 0x22, 0x4D, 0x0C};
-
- uint8_t *firm_mem = (uint8_t*)firm_p9_exefs + sizeof(exefs_h) + firm_p9_exefs->fileHeaders[0].offset;
- uint32_t size = firm_p9_exefs->fileHeaders[0].size;
-
- uint8_t *off = memfind(firm_mem, size, pat1, 4);
- uint8_t *off2 = memfind(firm_mem, size, pat2, 4) - 1;
-
- if (off == NULL) {
- fprintf(stderr, "Signature patch failed on P0.\n");
- return 1; // Failed to find sigpatch. Ugh.
- }
-
- if (off2 == NULL) {
- fprintf(stderr, "Signature patch failed on P1.\n");
- return 2; // Failed to find sigpatch. Ugh.
- }
-
- fprintf(stderr, "Signatures[0]: 0x%x\n", (uint32_t)off);
- fprintf(stderr, "Signatures[1]: 0x%x\n", (uint32_t)off2);
-
- uint8_t sigpatch[] = {0x00, 0x20, 0x70, 0x47};
-
- memcpy(off, sigpatch, 2);
- memcpy(off2, sigpatch, 4);
- fprintf(stderr, "Signature patch succeded.\n");
-
- return 0;
-}
-
-int patch_firmprot() {
- uint8_t *firm_mem = (uint8_t*)firm_p9_exefs + sizeof(exefs_h) + firm_p9_exefs->fileHeaders[0].offset;
- uint32_t size = firm_p9_exefs->fileHeaders[0].size;
-
- //Look for FIRM writing code
- uint8_t* off = memfind(firm_mem, size, (uint8_t*)"exe:", 4);
-
- if(off == NULL) {
- fprintf(stderr, "Couldn't find 'exe:' string.\n");
- return 1;
- }
-
- fprintf(stderr, "Firmprot: 'exe:' string @ %x\n", (uint32_t)off);
-
- uint8_t pattern[] = {0x00, 0x28, 0x01, 0xDA};
-
- uint8_t* firmprot = memfind(off - 0x100, 0x100, pattern, 4);
-
- if(firmprot == NULL) {
- fprintf(stderr, "Couldn't find firmprot code.\n");
- return 2;
- }
-
- fprintf(stderr, "Firmprot: %x\n", (uint32_t)firmprot);
-
- uint8_t patch[] = {0x00, 0x20, 0xC0, 0x46};
- memcpy(firmprot, patch, 4);
-
- fprintf(stderr, "Applied firmprot patch.\n");
-
- return 0;
-}
-
-void wait() {
- if (config.options[OPTION_TRACE]) {
- fprintf(stderr, "Pausing because trace is on.\n");
- wait_key();
- }
-}
-
-int patch_firm_all() {
- // Use builtin signature patcher?
-
- fprintf(stderr, "Sigpatch: %s\n", ((config.options[OPTION_SIGPATCH]) ? "yes" : "no" ));
- fprintf(stderr, "Protect: %s\n", ((config.options[OPTION_FIRMPROT]) ? "yes" : "no" ));
-
- wait();
-
- if (config.options[OPTION_SIGPATCH]) {
- if(patch_signatures()) {
- abort("Fatal. Sigpatch has failed.");
- }
- }
-
- wait();
-
- if (config.options[OPTION_FIRMPROT]) {
- if(patch_firmprot()) {
- abort("Fatal. Firmprot has failed.");
- }
- }
-
- wait();
-
- // Replace loader?
- if (config.options[OPTION_LOADER]) {
- // Yes.
-
- // This requires OPTION_SIGPATCH.
- }
-
- // Use ARM9 hook thread?
- if (config.options[OPTION_ARM9THREAD]) {
- // Yes.
-
- // FIXME - NYI
- }
-
- return 0;
-}
+++ /dev/null
-.section .text.start
-.align 4
-.global _start
-_start:
- bl main
- bx lr @ return from patch.
+++ /dev/null
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "../../../source/patch_format.h"
-
-void read_file_u64(char* name, uint64_t* to) {
- FILE* hdl = fopen(name, "rb");
- fscanf(hdl, "%llu", to);
- fclose(hdl);
-}
-
-
-void read_file_u32(char* name, uint32_t* to) {
- FILE* hdl = fopen(name, "rb");
- fscanf(hdl, "%u", to);
- fclose(hdl);
-}
-
-void read_str(char* name, char* to, size_t len) {
- FILE* hdl = fopen(name, "rb");
- int r = fread(to, 1, len-1, hdl);
- fclose(hdl);
-}
-
-uint32_t size = 0;
-
-uint8_t* read_file_mem(char* name) {
- FILE* hdl = fopen(name, "rb");
-
- fseek(hdl, 0, SEEK_END);
- size = ftell(hdl);
- rewind(hdl);
-
- uint8_t* mem = malloc(size);
- memset(mem, 0, size);
-
- int r = fread(mem, 1, size, hdl);
- fclose(hdl);
-
- printf("%d\n", size);
-
- return mem;
-}
-
-int main(int c, char** v) {
- struct system_patch patch;
- int at = 0;
-
- memset(&patch, 0, sizeof(patch));
-
- // Set magic.
- patch.magic[0] = 'A';
- patch.magic[1] = 'I';
- patch.magic[2] = 'D';
- patch.magic[3] = 'A';
-
- read_file_u32("meta/patch_version", & patch.patch_ver);
- read_file_u32("meta/cfw_version", & patch.load_ver);
-
- read_file_u64("meta/uuid", & patch.patch_id);
- read_file_u64("meta/title", & patch.tid);
-
- read_str("meta/name", patch.name, sizeof(patch.name));
- read_str("meta/desc", patch.desc, sizeof(patch.desc));
-
- uint8_t* code = read_file_mem("out/patch.bin");
-
- patch.patch_size = size;
-
- FILE* out = fopen("out/patch.vco", "wb");
- fwrite(&patch, 1, sizeof(patch), out);
- fwrite(code, 1, patch.patch_size, out);
- fclose(out);
-
- free(code);
-
- printf("Ver: %u\n"
- "CFW: %u\n"
- "UUID: %llu\n"
- "TID: %llu\n"
- "Name: %s\n"
- "Desc: %s\n"
- "Size: %u\n",
- patch.patch_ver, patch.load_ver, patch.patch_id,
- patch.tid, patch.name, patch.desc, patch.patch_size);
-
- return 0;
-}
+++ /dev/null
-rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2))
-
-PATH := $(PATH):$(DEVKITARM)/bin
-
-HOST_CC := gcc
-
-CC := arm-none-eabi-gcc
-AS := arm-none-eabi-as
-LD := arm-none-eabi-ld
-OC := arm-none-eabi-objcopy
-
-dir_source := src
-dir_build := build
-dir_out := out
-
-ASFLAGS := -mlittle-endian -mcpu=arm946e-s -march=armv5te
-CFLAGS := -MMD -MP -Wall -Wextra -Werror -Os $(ASFLAGS) -fno-builtin -std=c11 -DVERSION=\"$(REVISION)\"
-FLAGS := dir_out=$(abspath $(dir_out)) --no-print-directory
-LDFLAGS := -nostdlib -Wl,-z,defs -lgcc
-
-objects_cfw = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
- $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
- $(call rwildcard, $(dir_source), *.s *.c)))
-
-.PHONY: all
-all: patchbin
-
-.PHONY: patchbin
-patchbin: tool $(dir_out)/patch.bin
- ./compile_header
-
-.PHONY: tool
-tool:
- $(HOST_CC) -o compile_header tool/compile_header.c
-
-
-.PHONY: clean
-clean:
- rm -rf $(dir_out) $(dir_build) compile_header
-
-.PHONY: $(dir_out)/patch.bin
-$(dir_out)/patch.bin: $(dir_build)/main.bin
- @mkdir -p "$(dir_out)"
- @cp -av $< $@
-
-$(dir_build)/main.bin: $(dir_build)/main.elf
- $(OC) -S -O binary $< $@
-
-$(dir_build)/main.elf: $(objects_cfw)
- $(CC) -T linker.ld $(OUTPUT_OPTION) $^ $(LDFLAGS)
-
-$(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
-/* This memory map is mainly to assist in doing stuff in code.
-MEMORY
-{
- INSTRUCTION_TCM (rw) : ORIGIN = 0x00000000, LENGTH = 0x08000000
- ARM_INTERNAL (rw) : ORIGIN = 0x08000000, LENGTH = 0x00100000
- NEW_INTERNAL (rw) : ORIGIN = 0x08100000, LENGTH = 0x00080000
- IO_MEMORY (rw) : ORIGIN = 0x10000000, LENGTH = 0x08000000
- VRAM (rw) : ORIGIN = 0x18000000, LENGTH = 0x00600000
- DSP_MEMORY (rw) : ORIGIN = 0x1FF00000, LENGTH = 0x00080000
- AXI_WRAM (rw) : ORIGIN = 0x1FF80000, LENGTH = 0x00080000
- FCRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x08000000
- NEW_FCRAM (rwx) : ORIGIN = 0x28000000, LENGTH = 0x08000000
- DATA_TCM (rw) : ORIGIN = 0xFFF00000, LENGTH = 0x00004000
- BOOTROM (rw) : ORIGIN = 0xFFFF0000, LENGTH = 0x00010000
-} */
-
-ENTRY(_start)
-SECTIONS
-{
- . = 0x24400000;
-
- START_SECTION = .;
- .text.start : {
- *(.text.start)
- }
- START_SECTION_END = .;
-
- TABLE_SECTION = .;
- .text.table : {
- *(.text.table)
- }
- TABLE_SECTION_END = .;
-
- TEXT_SECTION = .;
- .text : {
- *(.text)
- }
- TEXT_SECTION_END = .;
-
- DATA_SECTION = .;
- .data : {
- *(.data)
- }
- DATA_SECTION_END = .;
-
- BSS_SECTION = .;
- .bss : {
- *(.bss COMMON)
- }
- BSS_SECTION_END = .;
-
- RODATA_SECTION = .;
- .rodata : {
- *(.rodata)
- }
- RODATA_SECTION_END = .;
-
- . = ALIGN(4);
-}
+++ /dev/null
-.section .text.table
-.align 4
-
-.macro stub name
- .global \name
- \name :
- .byte 0
- .byte 0
- .byte 0
- .byte 0
-.endm
-
-.global MAGIC_START
-MAGIC_START:
- .byte 0xc0
- .byte 0x9b
- .byte 0xe5
- .byte 0x1c
-
-// Memory to patch as specified by the header. (uint8_t*)
-stub memory_offset
-
-// Size of memory offset. (uint32_t*)
-stub memory_len
-
-// Exported functions.
-
-// memory.c
-stub strlen
-stub isprint
-stub memcpy
-stub memmove
-stub memset
-stub memcmp
-stub strncpy
-stub strncmp
-stub atoi
-stub memfind
-
-// draw.c
-stub putc
-stub puts
-stub fprintf
+++ /dev/null
-#include <stdint.h>
-#include <stddef.h>
-#include "exported.h"
-
-int main() {
- //Look for signature checks
- uint8_t pat1[] = {0xC0, 0x1C, 0x76, 0xE7};
- uint8_t pat2[] = {0xB5, 0x22, 0x4D, 0x0C};
-
- uint8_t *firm_mem = (uint8_t*)memory_offset;
- uint32_t size = *(uint32_t*)memory_len;
-
- uint8_t *off = memfind(firm_mem, size, pat1, 4);
- uint8_t *off2 = memfind(firm_mem, size, pat2, 4) - 1;
-
- if (off == NULL) {
- fprintf(stderr, "Signature patch failed on P0.\n");
- return 1; // Failed to find sigpatch. Ugh.
- }
-
- if (off2 == NULL) {
- fprintf(stderr, "Signature patch failed on P1.\n");
- return 2; // Failed to find sigpatch. Ugh.
- }
-
- fprintf(stderr, "Signatures[0]: 0x%x\n", (uint32_t)off);
- fprintf(stderr, "Signatures[1]: 0x%x\n", (uint32_t)off2);
-
- uint8_t sigpatch[] = {0x00, 0x20, 0x70, 0x47};
-
- memcpy(off, sigpatch, 2);
- memcpy(off2, sigpatch, 4);
- fprintf(stderr, "Signature patch succeded.\n");
-
- return 0;
-}
+++ /dev/null
-.section .text.start
-.align 4
-.global _start
-_start:
- bl main
- bx lr @ return from patch.
+++ /dev/null
-rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2))
-
-PATH := $(PATH):$(DEVKITARM)/bin
-
-HOST_CC := gcc
-
-CC := arm-none-eabi-gcc
-AS := arm-none-eabi-as
-LD := arm-none-eabi-ld
-OC := arm-none-eabi-objcopy
-
-dir_source := src
-dir_build := build
-dir_out := out
-
-ASFLAGS := -mlittle-endian -mcpu=arm946e-s -march=armv5te
-CFLAGS := -MMD -MP -Wall -Wextra -Werror -Os $(ASFLAGS) -fno-builtin -std=c11 -DVERSION=\"$(REVISION)\"
-FLAGS := dir_out=$(abspath $(dir_out)) --no-print-directory
-LDFLAGS := -nostdlib -Wl,-z,defs
-
-objects_cfw = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
- $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
- $(call rwildcard, $(dir_source), *.s *.c)))
-
-.PHONY: all
-all: patchbin
-
-.PHONY: patchbin
-patchbin: tool $(dir_out)/patch.bin
- ./compile_header
-
-.PHONY: tool
-tool:
- $(HOST_CC) -o compile_header tool/compile_header.c
-
-
-.PHONY: clean
-clean:
- rm -rf $(dir_out) $(dir_build) compile_header
-
-.PHONY: $(dir_out)/patch.bin
-$(dir_out)/patch.bin: $(dir_build)/main.bin
- @mkdir -p "$(dir_out)"
- @cp -av $< $@
-
-$(dir_build)/main.bin: $(dir_build)/main.elf
- $(OC) -S -O binary $< $@
-
-$(dir_build)/main.elf: $(objects_cfw)
- $(CC) -T linker.ld $(OUTPUT_OPTION) $^ $(LDFLAGS)
-
-$(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
-/* This memory map is mainly to assist in doing stuff in code.
-MEMORY
-{
- INSTRUCTION_TCM (rw) : ORIGIN = 0x00000000, LENGTH = 0x08000000
- ARM_INTERNAL (rw) : ORIGIN = 0x08000000, LENGTH = 0x00100000
- NEW_INTERNAL (rw) : ORIGIN = 0x08100000, LENGTH = 0x00080000
- IO_MEMORY (rw) : ORIGIN = 0x10000000, LENGTH = 0x08000000
- VRAM (rw) : ORIGIN = 0x18000000, LENGTH = 0x00600000
- DSP_MEMORY (rw) : ORIGIN = 0x1FF00000, LENGTH = 0x00080000
- AXI_WRAM (rw) : ORIGIN = 0x1FF80000, LENGTH = 0x00080000
- FCRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x08000000
- NEW_FCRAM (rwx) : ORIGIN = 0x28000000, LENGTH = 0x08000000
- DATA_TCM (rw) : ORIGIN = 0xFFF00000, LENGTH = 0x00004000
- BOOTROM (rw) : ORIGIN = 0xFFFF0000, LENGTH = 0x00010000
-} */
-
-ENTRY(_start)
-SECTIONS
-{
- . = 0x24400000;
-
- START_SECTION = .;
- .text.start : {
- *(.text.start)
- }
- START_SECTION_END = .;
-
- TABLE_SECTION = .;
- .text.table : {
- *(.text.table)
- }
- TABLE_SECTION_END = .;
-
- TEXT_SECTION = .;
- .text : {
- *(.text)
- }
- TEXT_SECTION_END = .;
-
- DATA_SECTION = .;
- .data : {
- *(.data)
- }
- DATA_SECTION_END = .;
-
- BSS_SECTION = .;
- .bss : {
- *(.bss COMMON)
- }
- BSS_SECTION_END = .;
-
- RODATA_SECTION = .;
- .rodata : {
- *(.rodata)
- }
- RODATA_SECTION_END = .;
-
- . = ALIGN(4);
-}
+++ /dev/null
-Prints "Hello World!"
+++ /dev/null
-Example Patch
+++ /dev/null
-#ifndef EXPORTED_H
-#define EXPORTED_H
-
-#define stdout (void*)0
-#define stderr (void*)1
-
-extern uint8_t* memory_offset;
-extern uint32_t* memory_len;
-
-extern int strlen(const char *string);
-extern int isprint(char c);
-extern void memcpy(void *dest, const void *src, size_t size);
-extern void memmove(void *dest, const void *src, size_t size);
-extern void memset(void *dest, const int filler, size_t size);
-extern int memcmp(const void *buf1, const void *buf2, const size_t size);
-extern void strncpy(void *dest, const void *src, const size_t size);
-extern int strncmp(const void *buf1, const void *buf2, const size_t size);
-extern int atoi(const char *str);
-extern uint8_t* memfind (uint8_t *string, uint32_t stringlen, uint8_t *pat, uint32_t patlen);
-
-extern void putc(void* buf, const int c);
-extern void puts(void* buf, const char *string);
-extern void fprintf(void* channel, const char* format, ...);
-
-#endif
+++ /dev/null
-.section .text.table
-.align 4
-
-.macro stub name
- .global \name
- \name :
- ldr pc, [pc, #-4] // Load the data after this to the PC. return will be before this call.
- .byte 0 // Read; pc relative in arm mode is pc+8, so pc-4 is the correct offset
- .byte 0
- .byte 0
- .byte 0
-.endm
-
-.macro ref name
- .global \name
- \name :
- .byte 0
- .byte 0
- .byte 0
- .byte 0
-.endm
-
-.global MAGIC_START
-MAGIC_START:
- .byte 0xc0
- .byte 0x9b
- .byte 0xe5
- .byte 0x1c
-
-// Memory to patch as specified by the header. (uint8_t*)
-ref memory_offset
-
-// Size of memory offset. (uint32_t*)
-ref memory_len
-
-// Exported functions.
-
-// memory.c
-stub strlen
-stub isprint
-stub memcpy
-stub memmove
-stub memset
-stub memcmp
-stub strncpy
-stub strncmp
-stub atoi
-stub memfind
-
-// draw.c
-stub putc
-stub puts
-stub fprintf
+++ /dev/null
-.section .text.start
-.align 4
-.global _start
-_start:
- stmdb sp!, {lr}
- bl main
- ldmia sp!, {lr}
- bx lr // return from patch.
+++ /dev/null
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "../../../source/patch_format.h"
-
-void read_file_u64(char* name, uint64_t* to) {
- FILE* hdl = fopen(name, "rb");
- fscanf(hdl, "%llu", to);
- fclose(hdl);
-}
-
-
-void read_file_u32(char* name, uint32_t* to) {
- FILE* hdl = fopen(name, "rb");
- fscanf(hdl, "%u", to);
- fclose(hdl);
-}
-
-void read_str(char* name, char* to, size_t len) {
- FILE* hdl = fopen(name, "rb");
- int r = fread(to, 1, len-1, hdl);
- fclose(hdl);
-}
-
-uint32_t size = 0;
-
-uint8_t* read_file_mem(char* name) {
- FILE* hdl = fopen(name, "rb");
-
- fseek(hdl, 0, SEEK_END);
- size = ftell(hdl);
- rewind(hdl);
-
- uint8_t* mem = malloc(size);
- memset(mem, 0, size);
-
- int r = fread(mem, 1, size, hdl);
- fclose(hdl);
-
- printf("%d\n", size);
-
- return mem;
-}
-
-int main(int c, char** v) {
- struct system_patch patch;
- int at = 0;
-
- memset(&patch, 0, sizeof(patch));
-
- // Set magic.
- patch.magic[0] = 'A';
- patch.magic[1] = 'I';
- patch.magic[2] = 'D';
- patch.magic[3] = 'A';
-
- read_file_u32("meta/patch_version", & patch.patch_ver);
- read_file_u32("meta/cfw_version", & patch.load_ver);
-
- read_file_u64("meta/uuid", & patch.patch_id);
- read_file_u64("meta/title", & patch.tid);
-
- read_str("meta/name", patch.name, sizeof(patch.name));
- read_str("meta/desc", patch.desc, sizeof(patch.desc));
-
- uint8_t* code = read_file_mem("out/patch.bin");
-
- patch.patch_size = size;
-
- FILE* out = fopen("out/patch.vco", "wb");
- fwrite(&patch, 1, sizeof(patch), out);
- fwrite(code, 1, patch.patch_size, out);
- fclose(out);
-
- free(code);
-
- printf("Ver: %u\n"
- "CFW: %u\n"
- "UUID: %llu\n"
- "TID: %llu\n"
- "Name: %s\n"
- "Desc: %s\n"
- "Size: %u\n",
- patch.patch_ver, patch.load_ver, patch.patch_id,
- patch.tid, patch.name, patch.desc, patch.patch_size);
-
- return 0;
-}
int firmware_changed = 0;
if (read_file(dest, path, *size)) {
- fprintf(BOTTOM_SCREEN, "Failed to read FIRM from SD\n");
+ fprintf(BOTTOM_SCREEN, " Failed to read FIRM.\n");
// Only whine about this if it's NATIVE_FIRM, which is important.
if (firm_title == NATIVE_FIRM_TITLEID) {
if (firm_title == NATIVE_FIRM_TITLEID) {
fprintf(BOTTOM_SCREEN, "Failed to decrypt firmware.\n"
"This is fatal. Aborting.\n");
+ status = 1;
+ goto exit_error;
}
- goto exit_error;
}
firmware_changed = 1; // Decryption performed.
} else {
- fprintf(BOTTOM_SCREEN, "FIRM not encrypted\n");
+ fprintf(BOTTOM_SCREEN, " FIRM not encrypted\n");
}
struct firm_signature* fsig = get_firm_info(dest);
- fprintf(BOTTOM_SCREEN, "FIRM version: %s\n", fsig->version_string);
+ fprintf(BOTTOM_SCREEN, " FIRM version: %s\n", fsig->version_string);
// The N3DS firm has an additional encryption layer for ARM9
if (fsig->console == console_n3ds) {
}
firmware_changed = 1; // Decryption of arm9bin performed.
} else {
- fprintf(BOTTOM_SCREEN, "ARM9 FIRM binary not encrypted\n");
+ fprintf(BOTTOM_SCREEN, " ARM9 FIRM binary not encrypted\n");
if (firm_title == NATIVE_FIRM_TITLEID && fsig->version > 0x0F) {
slot0x11key96_init(); // This has to be loaded regardless, otherwise boot will fail.
}
// Save firmware.bin if decryption was done.
if (firmware_changed) {
- fprintf(BOTTOM_SCREEN, "Saving decrypted FIRM\n");
+ fprintf(BOTTOM_SCREEN, " Saving decrypted FIRM\n");
write_file(dest, path, *size);
}
- if (fsig->console == console_n3ds)
- {
- fprintf(BOTTOM_SCREEN, "Fixing arm9 entrypoint\n");
+ if (fsig->console == console_n3ds) {
+ fprintf(BOTTOM_SCREEN, " Fixing arm9 entrypoint\n");
// Patch the entrypoint to skip arm9loader
if (firm_title == NATIVE_FIRM_TITLEID) {
fprintf(BOTTOM_SCREEN, "Updated keyX keyslots\n");
}
+ fumount(); // Unmount SD. No longer needed.
+ fprintf(BOTTOM_SCREEN, "SD Unmounted.\n");
+
for (firm_section_h *section = firm_loc->section;
section < firm_loc->section + 4 && section->address != 0; section++) {
memcpy((void *)section->address, (void *)firm_loc + section->offset, section->size);
process9->address = p9exheader->sci.textCodeSet.address;
process9->size = (*p9exefs)->fileHeaders[0].size;
process9->offset = (void*)((*p9exefs) + 1) - (void*)firm;
- fprintf(BOTTOM_SCREEN, "Found Process9 for FIRM.\n");
+ fprintf(BOTTOM_SCREEN, " Found Process9 for FIRM.\n");
return 0;
}
}
}
}
}
- fprintf(BOTTOM_SCREEN, "Couldn't find Process9 for FIRM?\n");
+ fprintf(BOTTOM_SCREEN, " Couldn't find Process9?\n");
return 1;
}
int load_firms() {
- fprintf(TOP_SCREEN, "[Loading FIRM]");
+ fprintf(TOP_SCREEN, "[Loading FIRM]\n");
fprintf(BOTTOM_SCREEN, "Loading NATIVE_FIRM\n");
if (load_firm(firm_loc, PATH_NATIVE_F, PATH_NATIVE_FIRMKEY, &firm_size, NATIVE_FIRM_TITLEID) != 0)
fprintf(BOTTOM_SCREEN, "Loading TWL_FIRM\n");
if(load_firm(twl_firm_loc, PATH_TWL_F, PATH_TWL_FIRMKEY, &twl_firm_size, TWL_FIRM_TITLEID))
- fprintf(BOTTOM_SCREEN, "TWL_FIRM failed to load.\n");
+ fprintf(BOTTOM_SCREEN, " TWL_FIRM failed to load.\n");
else
find_proc9(twl_firm_loc, &twl_firm_proc9, &twl_firm_p9_exefs);
fprintf(BOTTOM_SCREEN, "Loading AGB_FIRM\n");
if(load_firm(agb_firm_loc, PATH_AGB_F, PATH_AGB_FIRMKEY, &agb_firm_size, AGB_FIRM_TITLEID))
- fprintf(BOTTOM_SCREEN, "AGB_FIRM failed to load.\n");
+ fprintf(BOTTOM_SCREEN, " AGB_FIRM failed to load.\n");
else
find_proc9(agb_firm_loc, &agb_firm_proc9, &agb_firm_p9_exefs);
}
void boot_cfw() {
- fprintf(TOP_SCREEN, "[Patching]");
+ fprintf(TOP_SCREEN, "Applying patches...\n");
if (patch_firm_all() != 0)
return;
#include "common.h"
#include "firm/fcram.h"
+#include "firm/firm.h"
-int link_and_load_patchbin(char* path) {
- fprintf(stderr, "Prep to relocate and execute patch.\n");
+extern void flush_cache();
- struct system_patch patch;
+extern firm_h *firm_loc;
+extern exefs_h *firm_p9_exefs;
+
+extern firm_h *twl_firm_loc;
+extern exefs_h *twl_firm_p9_exefs;
+
+extern firm_h *agb_firm_loc;
+extern exefs_h *agb_firm_p9_exefs;
+
+firm_h* __attribute__((optimize("O0"))) get_firm() {
+ return firm_loc;
+}
+
+exefs_h* __attribute__((optimize("O0"))) get_firm_proc9_exefs() {
+ return firm_p9_exefs;
+}
- fprintf(stderr, "Loading patch vco.\n");
+firm_h* __attribute__((optimize("O0"))) get_agb() {
+ return agb_firm_loc;
+}
+
+exefs_h* __attribute__((optimize("O0"))) get_agb_proc9_exefs() {
+ return agb_firm_p9_exefs;
+}
+
+firm_h* __attribute__((optimize("O0"))) get_twl() {
+ return twl_firm_loc;
+}
+
+exefs_h* __attribute__((optimize("O0"))) get_twl_proc9_exefs() {
+ return twl_firm_p9_exefs;
+}
+
+// Yes, this is EXACTLY what it looks like. We dynamically link and
+// load patches as binaries; they use functions from corbenik to do
+// the work, and therefore must have a function table in them.
+
+// See vco/template for how this magic works.
+
+// This ensures relatively small patches while also having incredible
+// flexibility unlike a 'patch format'.
+
+int execp(char* path) {
+ int basename = 0;
+ for(basename=strlen(path); basename > 0; basename--)
+ if (path[basename] == '/')
+ break;
+ basename++;
+
+ fprintf(stderr, "Executing patchbin: %s\n", &path[basename]);
+
+ struct system_patch patch;
// Load patch from path.
FILE* f = fopen(path, "r");
fread(&patch, 1, sizeof(patch), f);
- fprintf(stderr, "Patch payload is %d bytes.\n", patch.patch_size);
+ fprintf(stderr, " [h]");
fread((uint8_t*)FCRAM_PATCHBIN_EXEC_LOC, 1, patch.patch_size, f);
+ fprintf(stderr, "[x]");
+
fclose(f);
- fprintf(stderr, "Finding relocation table.\n");
+ fprintf(stderr, "[s]");
// Now then...find the magical number.
uint8_t magic[] = { 0xc0, 0x9b, 0xe5, 0x1c };
- uint8_t* off = memfind((uint8_t*)FCRAM_PATCHBIN_EXEC_LOC, patch.patch_size, magic, 4);
+ uint32_t* link_table = (uint32_t*)memfind((uint8_t*)FCRAM_PATCHBIN_EXEC_LOC, patch.patch_size, magic, 4);
- if (off == NULL) {
- fprintf(stderr, "Relocation table missing. Abort.\n");
+ if (link_table == NULL) {
+ fprintf(stderr, "\n Table missing. Abort.\n");
return 1;
}
- fprintf(stderr, "Relocation table is at %x\n", (uint32_t)off);
+ fprintf(stderr, "[r]");
- uint32_t* link_table = (uint32_t*)(off + 4);
+ // 0 - magic
// memory.c
- link_table[3] = (uint32_t)strlen;
- link_table[5] = (uint32_t)isprint;
- link_table[7] = (uint32_t)memcpy;
- link_table[9] = (uint32_t)memmove;
- link_table[11] = (uint32_t)memset;
- link_table[13] = (uint32_t)memcmp;
- link_table[15] = (uint32_t)strncpy;
- link_table[17] = (uint32_t)strncmp;
- link_table[19] = (uint32_t)atoi;
- link_table[21] = (uint32_t)memfind;
+ link_table[2] = (uint32_t)strlen;
+ link_table[4] = (uint32_t)isprint;
+ link_table[6] = (uint32_t)memcpy;
+ link_table[8] = (uint32_t)memmove;
+ link_table[10] = (uint32_t)memset;
+ link_table[12] = (uint32_t)memcmp;
+ link_table[14] = (uint32_t)strncpy;
+ link_table[16] = (uint32_t)strncmp;
+ link_table[18] = (uint32_t)atoi;
+ link_table[20] = (uint32_t)memfind;
// draw.c
- link_table[23] = (uint32_t)putc;
- link_table[25] = (uint32_t)puts;
- link_table[27] = (uint32_t)fprintf;
+ link_table[22] = (uint32_t)putc;
+ link_table[24] = (uint32_t)puts;
+ link_table[26] = (uint32_t)fprintf;
+
+ // Get functions
+ link_table[28] = (uint32_t)get_firm;
+ link_table[30] = (uint32_t)get_firm_proc9_exefs;
+
+ link_table[32] = (uint32_t)get_agb;
+ link_table[34] = (uint32_t)get_agb_proc9_exefs;
+
+ link_table[36] = (uint32_t)get_twl;
+ link_table[38] = (uint32_t)get_twl_proc9_exefs;
+
+ fprintf(stderr, "[b]\n");
+
+ int (*patch_loc)() = (void*)FCRAM_PATCHBIN_EXEC_LOC ;
- fprintf(stderr, "Copied relocations. Running binary.\n");
+ int ret = (*patch_loc)();
- int (*exec)() = (void*)FCRAM_PATCHBIN_EXEC_LOC ;
+ fprintf(stderr, " Exit: %d\n", ret);
- return (*exec)();
+ return ret;
}
#define PATH_LOCEMU PATH_CFW "/locale.conf" // Locale emulation config
#define PATH_CPU_CFG PATH_CFW "/cpu.conf" // CPU settings config
-#define PATH_FIRMWARES PATH_CFW "/firmware" // Firmware folder.
-#define PATH_PATCHES PATH_CFW "/patches" // Patches folder.
+#define PATH_FIRMWARES PATH_CFW "/firm" // Firmware folder.
+#define PATH_PATCHES PATH_CFW "/bin" // Patch binary folder.
#define PATH_TEMP PATH_CFW "/temp" // Files that are transient (user can delete them and they will be regenerated)
#define PATH_KEYS PATH_CFW "/keys" // Keyfiles will be loaded from this dir, and additionally the root if not found.
#define PATH_EXEFS PATH_CFW "/exefs" // ExeFS overrides, named like '<titleid>.exefs'
#define PATH_AGB_CETK PATH_KEYS "/agb.cetk"
#define PATH_AGB_FIRMKEY PATH_KEYS "/agb.key"
-#define PATH_SLOT0X11KEY96 PATH_KEYS "/0x11.key"
+#define PATH_SLOT0X11KEY96 PATH_KEYS "/11.key"
#define PATH_ALT_SLOT0X11KEY96 "/slot0x11key96.bin" // Hey, your perrogative, buddy. I like cleaned up paths.
#include "config.h"
#include "common.h"
+// TODO - Basically all this needs to move to patcher programs.
+
uint32_t wait_key();
-int link_and_load_patchbin(char* path);
+int execp(char* path);
// A portion of this file is inherited from Luma3DS.
-/*
-u16 *getFirmWrite(u8 *pos, u32 size) {
- //Look for FIRM writing code
- u8 *const off = memsearch(pos, "exe:", size, 4);
- const u8 pattern[] = {0x00, 0x28, 0x01, 0xDA};
-
- return (u16 *)memsearch(off - 0x100, pattern, 0x100, 4);
-}
-
-u16 *getFirmWriteSafe(u8 *pos, u32 size) {
- //Look for FIRM writing code
- const u8 pattern[] = {0x04, 0x1E, 0x1D, 0xDB};
-
- return (u16 *)memsearch(pos, pattern, size, 4);
-}
-
-u32 getLoader(u8 *pos, u32 *loaderSize) {
+/*u32 getLoader(u8 *pos, u32 *loaderSize) {
u8 *off = pos;
u32 size;
return (u32)(off - pos);
}
-
-
-// patch_location = (void *)((uintptr_t)firm + section->offset + (version->offset - section->address));
-
-u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
-{
- u8 *off = memsearch(pos, "ess9", size, 4);
-
- *process9Size = *(u32 *)(off - 0x60) * 0x200;
- *process9MemAddr = *(u32 *)(off + 0xC);
-
- //Process9 code offset (start of NCCH + ExeFS offset + ExeFS header size)
- return off - 0x204 + (*(u32 *)(off - 0x64) * 0x200) + 0x200;
-}
*/
-int patch_signatures() {
+/* int patch_signatures() {
//Look for signature checks
uint8_t pat1[] = {0xC0, 0x1C, 0x76, 0xE7};
uint8_t pat2[] = {0xB5, 0x22, 0x4D, 0x0C};
+ // The code segment.
uint8_t *firm_mem = (uint8_t*)firm_p9_exefs + sizeof(exefs_h) + firm_p9_exefs->fileHeaders[0].offset;
uint32_t size = firm_p9_exefs->fileHeaders[0].size;
uint8_t *off = memfind(firm_mem, size, pat1, 4);
+
+ // We're subbing one because the code goes back 1.
+ // Unique patterns, etc.
uint8_t *off2 = memfind(firm_mem, size, pat2, 4) - 1;
if (off == NULL) {
fprintf(stderr, "Signatures[0]: 0x%x\n", (uint32_t)off);
fprintf(stderr, "Signatures[1]: 0x%x\n", (uint32_t)off2);
+ // See asm/sigpatches.s for the code here
uint8_t sigpatch[] = {0x00, 0x20, 0x70, 0x47};
memcpy(off, sigpatch, 2);
memcpy(off2, sigpatch, 4);
+
fprintf(stderr, "Signature patch succeded.\n");
return 0;
-}
+} */
int patch_firmprot() {
uint8_t *firm_mem = (uint8_t*)firm_p9_exefs + sizeof(exefs_h) + firm_p9_exefs->fileHeaders[0].offset;
uint32_t size = firm_p9_exefs->fileHeaders[0].size;
- //Look for FIRM writing code
+ // We look for 'exe:' first; this string is close to what we patch
uint8_t* off = memfind(firm_mem, size, (uint8_t*)"exe:", 4);
if(off == NULL) {
void wait() {
if (config.options[OPTION_TRACE]) {
- fprintf(stderr, "Pausing because trace is on.\n");
+ fprintf(stderr, "[press key]\n");
wait_key();
}
}
int patch_firm_all() {
- link_and_load_patchbin(PATH_PATCHES "/patch.vco");
+ // FIXME - Linker is bork at the moment.
+ execp(PATH_PATCHES "/example.vco");
wait();
// Use builtin signature patcher?
+ // TODO - Obviously these get moved to external patchers.
fprintf(stderr, "Sigpatch: %s\n", ((config.options[OPTION_SIGPATCH]) ? "yes" : "no" ));
fprintf(stderr, "Protect: %s\n", ((config.options[OPTION_FIRMPROT]) ? "yes" : "no" ));
wait();
if (config.options[OPTION_SIGPATCH]) {
- if(patch_signatures()) {
+ // TODO - Patch menu. This is okay-ish for now.
+ if(execp(PATH_PATCHES "/signatures.vco")) {
abort("Fatal. Sigpatch has failed.");
}
}
.align 4
.global _start
_start:
- @ Change the stack pointer
+ // Change the stack pointer
mov sp, #0x27000000
- @ Give read/write access to all the memory regions
+ // Give read/write access to all the memory regions
ldr r5, =0x33333333
mcr p15, 0, r5, c5, c0, 2 @ write data access
mcr p15, 0, r5, c5, c0, 3 @ write instruction access
- @ Sets MPU permissions and cache settings
+ // Sets MPU permissions and cache settings
ldr r0, =0xFFFF001D @ ffff0000 32k
ldr r1, =0x01FF801D @ 01ff8000 32k
ldr r2, =0x08000027 @ 08000000 1M
mcr p15, 0, r5, c6, c5, 0
mcr p15, 0, r6, c6, c6, 0
mcr p15, 0, r7, c6, c7, 0
- mcr p15, 0, r10, c3, c0, 0 @ Write bufferable 0, 2, 5
- mcr p15, 0, r11, c2, c0, 0 @ Data cacheable 0, 2, 5
- mcr p15, 0, r12, c2, c0, 1 @ Inst cacheable 0, 2, 5
- @ Enable caches
- mrc p15, 0, r4, c1, c0, 0 @ read control register
- orr r4, r4, #(1<<12) @ - instruction cache enable
- orr r4, r4, #(1<<2) @ - data cache enable
- orr r4, r4, #(1<<0) @ - mpu enable
- mcr p15, 0, r4, c1, c0, 0 @ write control register
+ // Yes, you're reading correctly. We are NOT enabling instruction caching.
+ // It causes issues with the linker, which is why it is left off.
- @ Flush caches
- mov r5, #0
- mcr p15, 0, r5, c7, c5, 0 @ flush I-cache
- mcr p15, 0, r5, c7, c6, 0 @ flush D-cache
- mcr p15, 0, r5, c7, c10, 4 @ drain write buffer
-
- @ Fixes mounting of SDMC
+ // Fixes mounting of SDMC
ldr r0, =0x10000020
mov r1, #0x340
str r1, [r0]
static unsigned int top_cursor_x = 0, top_cursor_y = 0;
static unsigned int bottom_cursor_x = 0, bottom_cursor_y = 0;
-static char text_buffer_top [TEXT_TOP_SIZE];
-static char text_buffer_bottom [TEXT_BOTTOM_SIZE];
+static char text_buffer_top [TEXT_TOP_HEIGHT*TEXT_TOP_WIDTH+1];
+static char text_buffer_bottom [TEXT_BOTTOM_HEIGHT*TEXT_BOTTOM_WIDTH+1];
-static char color_buffer_top [TEXT_TOP_SIZE];
-static char color_buffer_bottom [TEXT_BOTTOM_SIZE];
+static char color_buffer_top [TEXT_TOP_HEIGHT*TEXT_TOP_WIDTH+1];
+static char color_buffer_bottom [TEXT_BOTTOM_HEIGHT*TEXT_BOTTOM_WIDTH+1];
static uint32_t colors[16] = {
0x000000, // Black
0xffffff // White
};
-void clear_screen(uint8_t* screen) {
- uint32_t size = 0;
- char* buffer = 0;
- uint32_t buffer_size = 0;
+void clear_disp(uint8_t* screen) {
+ if (screen == TOP_SCREEN)
+ screen = framebuffers->top_left;
+ else if (screen == BOTTOM_SCREEN)
+ screen = framebuffers->bottom;
+
+ if(screen == framebuffers->top_left ||
+ screen == framebuffers->top_right) {
+ memset(screen, 0, SCREEN_TOP_SIZE);
+ } else if(screen == framebuffers->bottom) {
+ memset(screen, 0, SCREEN_BOTTOM_SIZE);
+ }
+}
+void clear_text(uint8_t* screen) {
if (screen == TOP_SCREEN)
screen = framebuffers->top_left;
else if (screen == BOTTOM_SCREEN)
if(screen == framebuffers->top_left ||
screen == framebuffers->top_right) {
- size = SCREEN_TOP_SIZE;
- buffer = text_buffer_top;
- buffer_size = TEXT_TOP_SIZE;
- top_cursor_x = 0;
- top_cursor_y = 0;
+ for(int i=0; i < TEXT_TOP_HEIGHT; i++) {
+ text_buffer_top[i*TEXT_TOP_WIDTH] = 0;
+ color_buffer_top[i*TEXT_TOP_WIDTH] = 0;
+ }
} else if(screen == framebuffers->bottom) {
- size = SCREEN_BOTTOM_SIZE;
- buffer = text_buffer_bottom;
- buffer_size = TEXT_BOTTOM_SIZE;
- bottom_cursor_x = 0;
- bottom_cursor_y = 0;
- } else {
- return; // Invalid buffer.
+ for(int i=0; i < TEXT_BOTTOM_HEIGHT; i++) {
+ text_buffer_bottom[i*TEXT_BOTTOM_WIDTH] = 0;
+ color_buffer_bottom[i*TEXT_BOTTOM_WIDTH] = 0;
+ }
}
+}
- memset(screen, 0, size);
- memset(buffer, 0, buffer_size);
+void clear_screen(uint8_t* screen) {
+ clear_disp(screen);
+ clear_text(screen);
}
void set_cursor(void* channel, unsigned int x, unsigned int y) {
if (buf == stdout || buf == stderr) {
unsigned int width = 0;
_UNUSED unsigned int height = 0;
- unsigned int size = 0;
- unsigned int cursor_x;
- unsigned int cursor_y;
- char* colorbuf;
- char* strbuf;
+ unsigned int *cursor_x;
+ unsigned int *cursor_y;
+ char *colorbuf;
+ char *strbuf;
unsigned char* color = NULL;
if (buf == TOP_SCREEN) {
width = TEXT_TOP_WIDTH;
height = TEXT_TOP_HEIGHT;
- size = TEXT_TOP_SIZE;
colorbuf = color_buffer_top;
strbuf = text_buffer_top;
- cursor_x = top_cursor_x;
- cursor_y = top_cursor_y;
+ cursor_x = &top_cursor_x;
+ cursor_y = &top_cursor_y;
color = &color_top;
} else if (buf == BOTTOM_SCREEN) {
width = TEXT_BOTTOM_WIDTH;
height = TEXT_BOTTOM_HEIGHT;
- size = TEXT_BOTTOM_SIZE;
colorbuf = color_buffer_bottom;
strbuf = text_buffer_bottom;
- cursor_x = bottom_cursor_x;
- cursor_y = bottom_cursor_y;
+ cursor_x = &bottom_cursor_x;
+ cursor_y = &bottom_cursor_y;
color = &color_bottom;
}
- unsigned int offset = width * cursor_y + cursor_x;
+ if (cursor_x[0] >= width) {
+ cursor_x[0] = 0;
+ cursor_y[0]++;
+ }
- if (offset >= size) {
- // Scroll a line back. This involves memcpy.
- // Yes, memcpy overwrites part of the buffer it is reading.
- memcpy(strbuf, strbuf+width, size-width);
- memcpy(colorbuf, colorbuf+width, size-width);
- cursor_y -= 1;
- offset = width * cursor_y + cursor_x;
- }
+ while (cursor_y[0] >= height) {
+ // Scroll.
+ for(unsigned int y=0; y < height-1; y++) {
+ memset(&strbuf[y*width], 0, width);
+ memset(&colorbuf[y*width], 0, width);
+ strncpy(&strbuf[y*width], &strbuf[(y+1)*width], width);
+ strncpy(&colorbuf[y*width], &colorbuf[(y+1)*width], width);
+ }
+ memset(&strbuf[(height-1)*width], 0, width);
+ memset(&colorbuf[(height-1)*width], 0, width);
- if (offset >= size) {
- // So if we're being real, this won't ever happen.
- return;
- }
+ cursor_y[0]--;
+
+ clear_disp(buf); // Clear screen.
+ }
switch(c) {
case '\n':
- cursor_y++; // Increment line.
- cursor_x = 0;
- break;
+ strbuf[cursor_y[0]*width+cursor_x[0]] = 0;
+ colorbuf[cursor_y[0]*width+cursor_x[0]] = 0;
+ cursor_y[0]++;
+ // Fall through intentional.
case '\r':
- cursor_x = 0; // Reset to beginning of line.
+ cursor_x[0] = 0; // Reset to beginning of line.
break;
default:
- strbuf[offset] = c;
- colorbuf[offset] = *color; // White on black.
- cursor_x++;
- if (cursor_x >= width) {
- cursor_y++;
- cursor_x = 0;
- }
- break;
- }
+ strbuf[cursor_y[0]*width+cursor_x[0]] = c;
+ colorbuf[cursor_y[0]*width+cursor_x[0]] = *color;
+
+ if (cursor_x[0] + 1 < width) {
+ strbuf[cursor_y[0]*width+cursor_x[0]+1] = 0; // Terminate.
+ colorbuf[cursor_y[0]*width+cursor_x[0]+1] = 0;
+ }
+
+ cursor_x[0]++;
- if (buf == TOP_SCREEN) {
- top_cursor_x = cursor_x;
- top_cursor_y = cursor_y;
- } else if (buf == BOTTOM_SCREEN) {
- bottom_cursor_x = cursor_x;
- bottom_cursor_y = cursor_y;
+ break;
}
} else {
// FILE*, not stdin or stdout.
void fflush(void* channel) {
if (channel == TOP_SCREEN) {
- for(int x=0; x < TEXT_TOP_WIDTH; x++) {
- for(int y=0; y < TEXT_TOP_HEIGHT; y++) {
+ for(int y=0; y < TEXT_TOP_HEIGHT; y++) {
+ for(int x=0; x < TEXT_TOP_WIDTH; x++) {
char c = text_buffer_top[y*TEXT_TOP_WIDTH+x];
+ if (c == 0)
+ break;
uint32_t color_fg = colors[((color_buffer_top[y*TEXT_TOP_WIDTH+x] >> 4) & 0x0f)];
uint32_t color_bg = colors[(color_buffer_top[y*TEXT_TOP_WIDTH+x] & 0x0f)];
draw_character(framebuffers->top_left, c, x, y, color_fg, color_bg);
}
}
} else if (channel == BOTTOM_SCREEN) {
- for(int x=0; x < TEXT_BOTTOM_WIDTH; x++) {
- for(int y=0; y < TEXT_BOTTOM_HEIGHT; y++) {
+ for(int y=0; y < TEXT_BOTTOM_HEIGHT; y++) {
+ for(int x=0; x < TEXT_BOTTOM_WIDTH; x++) {
char c = text_buffer_bottom[y*TEXT_BOTTOM_WIDTH+x];
+ if (c == 0)
+ break;
uint32_t color_fg = colors[((color_buffer_bottom[y*TEXT_BOTTOM_WIDTH+x] >> 4) & 0x0f)];
uint32_t color_bg = colors[(color_buffer_bottom[y*TEXT_BOTTOM_WIDTH+x] & 0x0f)];
draw_character(framebuffers->bottom, c, x, y, color_fg, color_bg);
--- /dev/null
+.PHONY: all copyout
+all: template signatures
+ mkdir -p ../out/corbenik/bin
+ cp template/out/patch.vco ../out/corbenik/bin/example.vco
+ cp signatures/out/patch.vco ../out/corbenik/bin/signatures.vco
+
+.PHONY: clean
+clean: clean_template clean_signatures
+ rm -rf ../out/corbenik/bin
+
+.PHONY: template
+template:
+ make -C template
+
+.PHONY: signatures
+signatures:
+ make -C signatures
+
+.PHONY: clean_template
+clean_template:
+ make -C template clean
+
+.PHONY: clean_signatures
+clean_signatures:
+ make -C signatures clean
--- /dev/null
+Corbenik patch binaries
+----------------------------------
+
+This directory contains the source for vco files - the patcher executables.
+
+You're probably wondering what the heck corbenik does differently from cakes,
+considering it seems similar in many ways.
+
+Patches are actually code for whatever processor they're intended to run on,
+be it ARM9 or ARM11. They're loaded to a static offset in memory, and executed
+from there with relocations to corbenik's internal functions. This keeps patches
+relatively small, and allows complete control over behavior.
+
+Patches should have a declaration of this sort somewhere in them:
+ { 0xc0, 0x9b, 0xe5, 0x1c }
+Followed by a table large enough to fill with all usable functions.
+
+The loader is subject to change at any moment's notice; the ABI is not yet
+stable. It may become an ELF loader at some point. I don't know.
+
+You may want to consult src/loader.c to see what functions are exported, or
+simply base your code on the generic example in the 'template' folder instead.
+
+There's some key differences here, obviously, from running just arm9loader code. Namely:
+
+ 1) Patches must not clobber the previous state. Meaning; start does nothing but
+ chain to main.
+
+ 2) Patches must properly return, and also return a value. Return code 0 is
+ success; keep this in mind. Corbenik will attempt to reset after a non-
+ zero return code. If you don't know how to return; you're looking for
+ 'bx lr'.
+
+ 3) Patches must have a symbol table with the appropriate magic.
+ No symbol table? No load. This might be relaxed in future versions
+ to allow patches to be marked static, but IDK.
+
+ 4) Don't code a patch that does too fancy stuff. Patches are not intended to
+ be 65k binaries. Seriously.
+
+ 5) _start must be at offset 0x24400000. This is where you are in memory.
+
+You can implement shit yourself, but it's an utter waste of memory. Try to use
+the linker exports unless you have a good reason not to.
--- /dev/null
+../template/Makefile
\ No newline at end of file
--- /dev/null
+../template/linker.ld
\ No newline at end of file
--- /dev/null
+../../template/src/exported.h
\ No newline at end of file
--- /dev/null
+../../template/src/headers.h
\ No newline at end of file
--- /dev/null
+../../template/src/link_table.s
\ No newline at end of file
--- /dev/null
+#include <stdint.h>
+#include <stddef.h>
+#include "exported.h"
+
+uint8_t pat1[] = {0xC0, 0x1C, 0x76, 0xE7};
+uint8_t pat2[] = {0xB5, 0x22, 0x4D, 0x0C};
+
+// See asm/sigpatches.s for the code here
+uint8_t sigpatch[] = {0x00, 0x20, 0x70, 0x47};
+
+uint8_t *firm_mem, *off, *off2;
+uint32_t size = 0;
+exefs_h* firm_p9_exefs;
+
+int main() {
+ //Look for signature checks
+ fprintf(stderr, "Applying signature patch\n");
+
+ firm_p9_exefs = get_firm_proc9_exefs();
+
+ fprintf(stderr, "ExeFS: 0x%x\n", firm_p9_exefs);
+
+ // The code segment.
+ firm_mem = (uint8_t*)firm_p9_exefs + sizeof(exefs_h) + firm_p9_exefs->fileHeaders[0].offset;
+ size = firm_p9_exefs->fileHeaders[0].size;
+
+ fprintf(stderr, "Search, P0.\n");
+ off = memfind(firm_mem, size, pat1, 4);
+
+ if (off == NULL) {
+ fprintf(stderr, "Failed, P0.\n");
+ return 1; // Failed to find sigpatch. Ugh.
+ }
+
+ // We're subbing one because the code goes back 1.
+ // Unique patterns, etc.
+ fprintf(stderr, "Search, P1.\n");
+ off2 = memfind(firm_mem, size, pat2, 4) - 1;
+
+ if (off2 == NULL) {
+ fprintf(stderr, "Failed, P1.\n");
+ return 2; // Failed to find sigpatch. Ugh.
+ }
+
+ fprintf(stderr, "Signatures, P0, 0x%x\n", (uint32_t)off);
+ memcpy(off, sigpatch, 2);
+
+ fprintf(stderr, "Signatures, P1, 0x%x\n", (uint32_t)off2);
+ memcpy(off2, sigpatch, 4);
+
+ fprintf(stderr, "Succeded.\n");
+
+ return 0;
+}
--- /dev/null
+../../template/src/start.s
\ No newline at end of file
--- /dev/null
+../template/tool/
\ No newline at end of file
dir_out := out
ASFLAGS := -mlittle-endian -mcpu=arm946e-s -march=armv5te
-CFLAGS := -MMD -MP -Wall -Wextra -Werror -O0 $(ASFLAGS) -fno-builtin -std=c11 -DVERSION=\"$(REVISION)\"
+CFLAGS := -MMD -MP -Wall -Wextra -Werror -O0 $(ASFLAGS) -fno-builtin -std=c11 -marm
FLAGS := dir_out=$(abspath $(dir_out)) --no-print-directory
LDFLAGS := -nostdlib -Wl,-z,defs -lgcc
#ifndef EXPORTED_H
#define EXPORTED_H
-#define stdout (void*)0
-#define stderr (void*)1
+#include "headers.h"
-extern uint8_t* memory_offset;
-extern uint32_t* memory_len;
+#define stdout ((void*)0)
+#define stderr ((void*)2)
extern int strlen(const char *string);
extern int isprint(char c);
extern void puts(void* buf, const char *string);
extern void fprintf(void* channel, const char* format, ...);
+extern firm_h* get_firm();
+extern exefs_h* get_firm_proc9_exefs();
+
+extern firm_h* get_agb();
+extern exefs_h* get_agb_proc9_exefs();
+
+extern firm_h* get_twl();
+extern exefs_h* get_twl_proc9_exefs();
+
#endif
--- /dev/null
+../../../source/firm/headers.h
\ No newline at end of file
--- /dev/null
+.section .text.table
+.align 4
+
+.macro stub name
+ .global \name
+ \name :
+ ldr pc, [pc, #-4] // Load the data after this to the PC. return will be before this call.
+ bx lr // Fall through in case of error.
+.endm
+
+// (int) [0]
+.global MAGIC_START
+MAGIC_START:
+ .byte 0xc0
+ .byte 0x9b
+ .byte 0xe5
+ .byte 0x1c
+
+// Exported functions.
+
+// memory.c
+// (int) [3]
+stub strlen
+// (int) [5]
+stub isprint
+// (int) [7]
+stub memcpy
+// (int) [9]
+stub memmove
+// (int) [11]
+stub memset
+// (int) [13]
+stub memcmp
+// (int) [15]
+stub strncpy
+// (int) [17]
+stub strncmp
+// (int) [19]
+stub atoi
+// (int) [21]
+stub memfind
+
+// draw.c
+// (int) [23]
+stub putc
+// (int) [25]
+stub puts
+// (int) [27]
+stub fprintf
+
+// Wrappers to get shit.
+
+// Gets NATIVE_FIRM memory offset as (firm_h*)
+stub get_firm
+
+// Get NATIVE_FIRM process9
+stub get_firm_proc9_exefs
+
+// Gets AGB_FIRM.
+stub get_agb
+
+// Get AGB_FIRM process9 exefs
+stub get_agb_proc9_exefs
+
+// Gets TWL_FIRM.
+stub get_twl
+
+// Get TWL_FIRM process9 exefs
+stub get_twl_proc9_exefs
--- /dev/null
+.section .text.start
+.align 4
+.global _start
+_start:
+ b main
+_die:
+ b _die // I have no clue how one would end up here.
FILE* hdl = fopen(name, "rb");
int r = fread(to, 1, len-1, hdl);
fclose(hdl);
+
+ for(int i=len-1; i >= 0; i--) {
+ if (to[i] == '\n') {
+ to[i] = 0;
+ break;
+ }
+ }
}
uint32_t size = 0;
int r = fread(mem, 1, size, hdl);
fclose(hdl);
- printf("%d\n", size);
-
return mem;
}