REVISION := r$(shell git rev-list --count HEAD):$(shell git rev-parse HEAD | head -c8)
ASFLAGS := -mlittle-endian -mcpu=arm946e-s -march=armv5te
-CFLAGS := -MMD -MP -Wall -Wextra -Werror -fno-omit-frame-pointer -Os $(ASFLAGS) -fno-builtin -std=c11 -DVERSION=\"$(REVISION)\" -DBUFFER=1
+CFLAGS := -MMD -MP -Wall -Wextra -Werror -fno-omit-frame-pointer -Os $(ASFLAGS) -fno-builtin -std=c11 -DVERSION=\"$(REVISION)\"
FLAGS := dir_out=$(abspath $(dir_out)) --no-print-directory
LDFLAGS := -nostdlib -Wl,-z,defs -lgcc -Wl,-Map,$(dir_build)/link.map
This is (yet another) CFW for the 3DS. Unlike other CFWs, this was mostly written from scratch and for fun. I'm a control freak, and this carries quite a bit of my mindset being a LFS/Gentoo user.
-Some parts are inherited from other CFWs - e.g. the firmware loading code in src/firm is mostly based on Cakes, and the signature patch bytecode is roughly based on the implementation in Luma3DS.
-
-Out of the bunch, corbenik is most similar to cakes of the bunch, in that it uses external patches. Unlike cakes, patches consist of a headered bytecode file. See `doc/bytecode.md`, `host/bytecode_asm.py` and `patch/*` for more on this.
-
-## Oh god yet another rebrand of--
-
-No. This is >75% original code. If you notice, there's a large amount of history because I didn't just dump the code on github; it's been in a repo all along while I worked on it and before it was made public.
-
-I also am not claiming to have written everything. See `doc/credits.md`.
-
-It shares close to zero of the actual architecture with other CFWs, only trivially implemented parts that look the same no matter who coded them.
+Some parts are inherited from other CFWs - e.g. the firmware loading code in src/firm is mostly based on Cakes, and the patch bytecode is based on Luma3DS' implementation in C (though, it isn't really derived from it)
+Out of the bunch of CFWs in existence, Corbenik is most similar to cakes of the bunch, in that it uses external patches. External patches are headered, can have dependencies, and consist of a lightweight and specialized bytecode/assembly which is intended for patching. See `doc/bytecode.md`, `host/bytecode_asm.py` and `patch/*` for more on this. The assembler is a bit crappy at the moment, and I *do* plan to improve it. However, it outputs the correct code which gets the job done.
## Rationale
I was initially going to make dynamic cakes, but I quickly realized a fatal flaw in any "patch" format: what you can do from a patch is limited to what the parser handles. This exact problem is why sempatches are used sometimes instead of classic diffs on the LKML, and it isn't a problem that will be solved without code. In my opinion, the best way to fix it is to simply externalize patches as programs. I also had a number of mad science experiments which would be very hard to perform in the context of ReiNAND based firmwares, and Cakes wouldn't make it easy either.
--- /dev/null
+// 'Tis not ready for the world at large yet.
+// I don't want to delete it since I'm working on it though,
+// so it's temporarliy #if'd 0.
+#if 0
+
+#define LOADER 1
+
+#include <3ds.h>
+#include "patcher.h"
+#include "fsldr.h"
+#include "internal.h"
+#include "memory.h"
+#include "logger.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 255
+#define _MAX_LFN 255
+#endif
+#include "config.h"
+#include "../../../source/patch_format.h"
+
+#include "patch/patch.h"
+
+// Yes, we're including a C file. Problem?
+#include "../../../source/interp.c"
+
+#endif
+++ /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);
-
- for (int i = len - 1; i >= 0; i--) {
- if (to[i] == '\n') {
- to[i] = 0;
- break;
- }
- }
-}
-
-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);
-
- 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;
-}
#include <stdint.h>
-#include "std/unused.h"
-#include "std/memory.h"
-#include "firm/firm.h"
-#include "config.h"
-#include "common.h"
+#include <stddef.h>
+#ifndef LOADER
+ #include "std/unused.h"
+ #include "std/memory.h"
+ #include "firm/firm.h"
+ #include "config.h"
+ #include "common.h"
+#endif
#define OP_NOP 0x00
#define OP_REL 0x01
#define OP_AND 0x09
#define OP_TITLE 0x0A
+#ifdef LOADER
+ #define fprintf(a...)
+ #define abort(a...)
+#endif
+
struct mode {
uint8_t* memory;
uint32_t size;
int exec_bytecode(uint8_t* bytecode, uint32_t len, int debug) {
if (!init_bytecode) {
+#ifndef LOADER
modes[0].memory = (uint8_t*)firm_loc;
modes[0].size = FCRAM_SPACING; // NATIVE_FIRM
// TWL_FIRM Sect 3
modes[17].memory = (uint8_t*)&twl_firm_loc->section[3] + 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;
}
- struct mode* current_mode = &modes[3];
+#ifdef LOADER
+ uint32_t set_mode = 18;
+#else
+ uint32_t set_mode = 3;
+#endif
+ struct mode* current_mode = &modes[set_mode];
+
uint32_t offset = 0;
uint8_t test_was_false = 0;
- uint32_t set_mode = 0;
-
uint32_t i;
uint8_t* code = bytecode;
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)
fprintf(stderr, "rel\n");
code++;
test_was_false = 0;
set_mode = *code;
code++;
+#endif
break;
case OP_FIND: // Find pattern.
if (debug)
code += 2;
if(memcmp(current_mode->memory+offset, code, *(code-1))) {
test_was_false = 1;
- fprintf(stderr, "false\n");
- } else {
+ if (debug)
+ fprintf(stderr, "false\n");
+ } else if (debug) {
fprintf(stderr, "true\n");
}
code += *(code-1);
fprintf(stderr, "jmp\n");
code++;
if (!test_was_false) {
- fprintf(stderr, "jmp to %hu,%hu\n", code[0], code[1]);
+ if (debug)
+ fprintf(stderr, "jmp to %hu,%hu\n", code[0], code[1]);
code = bytecode + code[1] + ( code[0] * 0x100 );
} else {
code += 2;
return 0;
}
+#ifdef LOADER
+int execb(char* filename, uint64_t tid, uint8_t* search_mem, uint32_t search_len) {
+#else
int execb(char* filename) {
+#endif
+ uint8_t* patch_mem;
+ uint32_t patch_len;
+ struct system_patch* patch;
+#ifdef LOADER
+ // Loader can use actual allocation functions, so this isn't as much of an issue
+
+ // Set memory.
+ modes[18].memory = search_mem;
+ modes[18].size = search_len;
+
+ // Load patch. We need memory, so...
+ // svcControlMemory(&tmp, __loader_prog_heap, 0x0, __ctru_heap_size, MEMOP_ALLOC, MEMPERM_READ | MEMPERM_WRITE);
+
+ // Check magic.
+
+ // Check TID supported.
+
+#else
+ // Read patch to scrap memory.
+
FILE* f = fopen(filename, "r");
size_t len = fsize(f);
fread((uint8_t*)FCRAM_PATCH_LOC, 1, len, f);
fclose(f);
- struct system_patch* patch = (struct system_patch*)FCRAM_PATCH_LOC;
- fprintf(stderr, "Name: %s\nDesc: %s\n", patch->name, patch->desc);
- uint8_t* patch_mem = (uint8_t*)patch + sizeof(struct system_patch) + (patch->depends * 8) + (patch->titles * 8);
- uint32_t patch_len = patch->size;
+ patch = (struct system_patch*)FCRAM_PATCH_LOC;
+
+ // Make sure various bits are correct.
+ if (memcmp(patch->magic, "AIDA", 4)) {
+ // Incorrect magic.
+ return 1;
+ }
+
+ if (patch->titles != 0) {
+ // Not an error, per se, but it means this patch is meant for loader, not us.
+ // Patches intended for use during boot will always be applied to zero titles.
+ return 0;
+ }
+
+ fprintf(stderr, "Patch: %s\n", patch->name);
- return exec_bytecode(patch_mem, patch_len, 1);
+ patch_mem = (uint8_t*)patch + sizeof(struct system_patch) + (patch->depends * 8) + (patch->titles * 8);
+ patch_len = patch->size;
+#endif
+ return exec_bytecode(patch_mem, patch_len, 0);
}