I can confirm that we now get into corbenik's payload on firmlaunch (the good old power off on button approach) but some step of initialization is resulting in a hang. Not there yet, sadly.
all: $(OBJECTS)
%.o: %.s
- arm-none-eabi-as -o $@ $<
+ arm-none-eabi-as -I$(shell pwd)/../include -o $@ $<
%.elf: %.o
arm-none-eabi-ld -T link.ld -o $@ $<
+++ /dev/null
-.set firm_addr, 0x24000000 // Temporary location where we'll load the FIRM to
-.set firm_maxsize, 0x200000 // Random value that's bigger than any of the currently known firm's sizes.
-
-.section .text
-.global _start
-_start:
-
- // Set MPU settings
- mrc p15, 0, r0, c2, c0, 0 // dcacheable
- mrc p15, 0, r12, c2, c0, 1 // icacheable
- mrc p15, 0, r1, c3, c0, 0 // write bufferable
- mrc p15, 0, r2, c5, c0, 2 // daccess
- mrc p15, 0, r3, c5, c0, 3 // iaccess
- ldr r4, =0x18000035 // 0x18000000 128M
- bic r2, r2, #0xF0000 // unprotect region 4
- bic r3, r3, #0xF0000 // unprotect region 4
- orr r0, r0, #0x10 // dcacheable region 4
- orr r2, r2, #0x30000 // region 4 r/w
- orr r3, r3, #0x30000 // region 4 r/w
- orr r12, r12, #0x10 // icacheable region 4
- orr r1, r1, #0x10 // write bufferable region 4
- mcr p15, 0, r0, c2, c0, 0
- mcr p15, 0, r12, c2, c0, 1
- mcr p15, 0, r1, c3, c0, 0 // write bufferable
- mcr p15, 0, r2, c5, c0, 2 // daccess
- mcr p15, 0, r3, c5, c0, 3 // iaccess
- mcr p15, 0, r4, c6, c4, 0 // region 4 (hmmm)
-
- mrc p15, 0, r0, c2, c0, 0 // dcacheable
- mrc p15, 0, r1, c2, c0, 1 // icacheable
- mrc p15, 0, r2, c3, c0, 0 // write bufferable
- orr r0, r0, #0x20 // dcacheable region 5
- orr r1, r1, #0x20 // icacheable region 5
- orr r2, r2, #0x20 // write bufferable region 5
- mcr p15, 0, r0, c2, c0, 0 // dcacheable
- mcr p15, 0, r1, c2, c0, 1 // icacheable
- mcr p15, 0, r2, c3, c0, 0 // write bufferable
-
- // Copy the firmware
- mov r4, #firm_addr
- add r5, r4, #0x40 // Start of loop
- add r6, r5, #0x30 * 3 // End of loop (scan 4 entries)
-
- copy_firm_loop:
- ldr r0, [r5]
- cmp r0, #0
- addne r0, r4 // src
- ldrne r1, [r5, #4] // dest
- ldrne r2, [r5, #8] // size
- blne memcpy32
-
- cmp r5, r6
- addlo r5, #0x30
- blo copy_firm_loop
-
- // Flush cache
- mov r2, #0
- mov r1, r2
- flush_cache:
- mov r0, #0
- mov r3, r2, lsl #30
- flush_cache_inner_loop:
- orr r12, r3, r0, lsl#5
- mcr p15, 0, r1, c7, c10, 4 // drain write buffer
- mcr p15, 0, r12, c7, c14, 2 // clean and flush dcache entry (index and segment)
- add r0, #1
- cmp r0, #0x20
- bcc flush_cache_inner_loop
- add r2, #1
- cmp r2, #4
- bcc flush_cache
-
- // Enable MPU
- ldr r0, =0x42078 // alt vector select, enable itcm
- mcr p15, 0, r0, c1, c0, 0
- mcr p15, 0, r1, c7, c5, 0 // flush dcache
- mcr p15, 0, r1, c7, c6, 0 // flush icache
- mcr p15, 0, r1, c7, c10, 4 // drain write buffer
- mov r0, #firm_addr
-
- // Boot FIRM
- mov r1, #0x1FFFFFFC
- ldr r2, [r0, #8] // arm11 entry
- str r2, [r1]
- ldr r0, [r0, #0xC] // arm9 entry
- bx r0
-.pool
-
-memcpy32: // memcpy32(void *src, void *dst, unsigned int size)
- add r2, r0
- memcpy32_loop:
- ldmia r0!, {r3}
- stmia r1!, {r3}
- cmp r0, r2
- blo memcpy32_loop
- bx lr
-.set firm_addr, 0x24000000 // Temporary location where we'll load the FIRM to
-.set firm_maxsize, 0x200000 // Random value that's bigger than any of the currently known firm's sizes.
+.set load_addr, 0x24F00000
+.set load_maxsize, 0x200000 // Random value that's bigger than corbenik
.section .text
.global _start
cmp r0, r2
bne pxi_wait_recv
- // Convert 2 bytes of the path string
- // This will be the method of getting the lower 2 bytes of the title ID
- // until someone bothers figuring out where the value is derived from.
- mov r0, #0 // Result
- add r1, sp, #0x3A8 - 0x70
- add r1, #0x22 // The significant bytes
- mov r2, #4 // Maximum loops (amount of bytes * 2)
-
- hex_string_to_int_loop:
- ldr r3, [r1], #2 // 2 because it's a utf-16 string.
- and r3, #0xFF
-
- // Check if it"s a number
- cmp r3, #'0'
- blo hex_string_to_int_end
- sub r3, #'0'
- cmp r3, #9
- bls hex_string_to_int_calc
-
- // Check if it"s a capital letter
- cmp r3, #'A' - '0'
- blo hex_string_to_int_end
- sub r3, #'A' - '0' - 0xA // Make the correct value: 0xF >= al >= 0xA
- cmp r3, #0xF
- bls hex_string_to_int_calc
-
- // Incorrect value: x > "A"
- bhi hex_string_to_int_end
-
- hex_string_to_int_calc:
- orr r0, r3, r0, lsl #4
- subs r2, #1
- bne hex_string_to_int_loop
- hex_string_to_int_end:
-
- // Get the FIRM path
- cmp r0, #0x0002 // NATIVE_FIRM
- ldreq r1, firm_fname
- beq check_fname
-
- ldr r5, =0x0102 // TWL_FIRM
- cmp r0, r5
- ldreq r1, twl_firm_fname
- beq check_fname
-
- ldr r5, =0x0202 // AGB_FIRM
- cmp r0, r5
- ldreq r1, agb_firm_fname
- beq check_fname
-
- fallback:
- // Fallback: Load specified FIRM from exefs
- add r1, sp, #0x3A8-0x70 // Location of exefs string.
- b load_firm
-
- check_fname:
- // Check the given string offset
- cmp r1, #0
- beq fallback
-
- load_firm:
+ load_file:
// Open file
add r0, r7, #8
+ adr r1, boot_fname
mov r2, #1
ldr r6, fopen
orr r6, #1
blx r6
cmp r0, #0 // Check if we were able to load the FIRM
- bne fallback // Otherwise, try again with the FIRM from exefs.
- // This will loop indefinitely if the exefs FIRM fails to load, but whatever.
+ bne die // Otherwise, hang.
- // Read file
+ read_file:
+ // Read file to the proper base.
mov r0, r7
adr r1, bytes_read
- mov r2, #firm_addr
- mov r3, #firm_maxsize
- ldr r6, [sp, #0x3A8-0x198]
+ ldr r2, =load_addr
+ ldr r3, =load_maxsize
+ ldr r6, [r7]
ldr r6, [r6, #0x28]
blx r6
mov r3, #0
swi 0x7C
- // Jump to reboot code
- ldr r0, reboot_code
- swi 0x7B
+ jump_to_kernel:
+ // Jump to reboot code in kernel mode
+ ldr r0, koffset_base
+ add r0, pc
+ swi 0x7B
die:
b die
-.align 4
-bytes_read: .word 0
-fopen: .ascii "open"
-reboot_code: .ascii "rebc"
+bytes_read: .word 0
+fopen: .ascii "OPEN"
+koffset_base: .word kernel_code-jump_to_kernel-12
+.pool
+
+kernel_code:
+ // Disable MPU
+ ldr r0, =0x42078
+ mcr p15, 0, r0, c1, c0, 0
+
+ // Flush cache
+ mov r2, #0
+ mov r1, r2
+ flush_cache:
+ mov r0, #0
+ mov r3, r2, lsl #30
+ flush_cache_inner_loop:
+ orr r12, r3, r0, lsl#5
+ mcr p15, 0, r12, c7, c14, 2 // clean and flush dcache entry (index and segment)
+ add r0, #1
+ cmp r0, #0x20
+ bcc flush_cache_inner_loop
+ add r2, #1
+ cmp r2, #4
+ bcc flush_cache
+
+ mcr p15, 0, r1, c7, c10, 4 // drain write buffer
+
+ mcr p15, 0, r1, c7, c5, 0 // Flush icache
+
+ ldr r0, =load_addr
+ bx r0
+
.pool
-firm_fname: .ascii "NATF"
-twl_firm_fname: .ascii "TWLF"
-agb_firm_fname: .ascii "AGBF"
+boot_fname: // This gets appended at insert-time
#include <ctr9/ctr_cache.h> // for ctr_cache_clean_and_flush_all
#include <ctr9/i2c.h> // for i2cWriteRegister, I2C_DEV_MCU
#include <malloc.h> // for memalign
-#include <option.h> // for get_opt_u32, OPTION_BRIGHTNESS
#include <std/draw.h> // for framebuffers
struct framebuffers *framebuffers;
}
}
-void screen_mode(uint32_t mode) {
+void screen_mode(uint32_t mode, uint32_t bright_level) {
static uint32_t stride, init_top, init_bottom, bright;
- bright = brightness[get_opt_u32(OPTION_BRIGHTNESS)];
+ bright = brightness[bright_level];
stride = 240;
switch(mode) {
#include <std/fs.h> // for crclose, cropen, crread, crsize, recur...
#include <std/memory.h> // for memfind, strdup_self
#include <structures.h> // for PATH_BITS, PATH_CHAINS
+#include <option.h> // for get_opt_u32
#include "ctr9/io/fatfs/ff.h" // for FILINFO, f_stat, ::FR_OK, AM_DIR
uint32_t current_chain_index = 0;
fprintf(stderr, "Changing display mode and chainloading...\n");
- screen_mode(1); // Because RGBA8 screeninit is non-standard...ugh
+ screen_mode(1, get_opt_u32(OPTION_BRIGHTNESS)); // Because RGBA8 screeninit is non-standard...ugh
// Copy CakeHax struct where it is expected (at 0x23FFFE00)
// It's very very likely we'll corrupt memory with this, but we aren't coming back anyways as of the
#include <stdlib.h>
#include <arm11.h> // for screen_mode, RGBA8, installArm11Stub, set_...
#include <ctr9/ctr_hid.h> // for ctr_hid_get_buttons, CTR_HID_RT
+#include <ctr9/ctr_system.h> // for ctr_system_poweroff
#include <firm/headers.h> // for prepatch_firm, boot_firm
#include <firm/firm.h> // for prepatch_firm, boot_firm
#include <input.h> // for wait
int
main(int argc, char** argv)
{
+ if (ctr_hid_get_buttons() & CTR_HID_LT)
+ ctr_system_poweroff();
+
int have_bg = 0;
int si = 0;
std_init();
if (crmount())
- poweroff(); // Failed to mount SD. Bomb out.
+ ctr_system_poweroff(); // We can't use poweroff here, since that does n/a cleanup that will cause a hang.
load_config(); // Load configuration.
shut_up(); // This does exactly what it sounds like.
if (have_bg && !si) {
- screen_mode(RGBA8); // Use RGBA8 mode.
+ screen_mode(RGBA8, get_opt_u32(OPTION_BRIGHTNESS)); // Use RGBA8 mode.
si = 1;
clear_disp(TOP_SCREEN);
}
} else {
if (!si) {
- screen_mode(RGBA8); // Use RGBA8 mode.
+ screen_mode(RGBA8, get_opt_u32(OPTION_BRIGHTNESS)); // Use RGBA8 mode.
si = 1;
clear_disp(TOP_SCREEN);
uint8_t*
getProcess9(uint8_t *pos, uint32_t size, uint32_t *process9Size, uint32_t *process9MemAddr)
{
- uint8_t *off = memfind(pos, size, "ess9", 4);
- *process9Size = *(uint32_t *)(off - 0x60) * 0x200;
- *process9MemAddr = *(uint32_t *)(off + 0xC);
+ uint8_t *magic = memfind(pos, size, "NCCH", 4);
+ cxi_h* cxi = (cxi_h*)(magic - 0x100);
+
+ *process9Size = (cxi->ncch.exeFSSize - 1) * 0x200;
+ *process9MemAddr = cxi->exheader.sci.textCodeSet.address;
+
// Process9 code offset (start of NCCH + ExeFS offset + ExeFS header size)
- return off - 0x204 + (*(uint32_t *)(off - 0x64) * 0x200) + 0x200;
+ return (uint8_t*)cxi + (cxi->ncch.exeFSOffset + 1) * 0x200;
}
int
uint32_t process9Size, process9MemAddr;
uint8_t *process9Offset =
- getProcess9((uint8_t *)firm_loc + firm_loc->section[2].offset + 0x15000, firm_loc->section[2].size - 0x15000, &process9Size, &process9MemAddr);
+ getProcess9((uint8_t *)firm_loc + firm_loc->section[2].offset, firm_loc->section[2].size, &process9Size, &process9MemAddr);
fprintf(stderr, "reboot: proc9 mem @ %lx\n", (uint32_t)process9MemAddr);
+ fprintf(stderr, "reboot: proc9 off @ %lx\n", (uint32_t)process9Offset);
wait();
// Firmlaunch function offset - offset in BLX opcode (A4-16 - ARM DDI 0100E) + 1
uint32_t fOpenOffset = (uint32_t)(off + 9 - (-((*(uint32_t *)off & 0x00FFFFFF) << 2) & (0xFFFFFF << 2)) - (uint32_t)process9Offset + process9MemAddr);
- fprintf(stderr, "reboot: cropen @ %lx\n", fOpenOffset);
+ fprintf(stderr, "reboot: fopen @ %lx\n", fOpenOffset);
wait();
crclose(f);
// Put the fOpen offset in the right location
- uint32_t *pos_cropen = (uint32_t *)memfind(off, size, "open", 4);
- if (!pos_cropen)
+ uint32_t *pos_fopen = (uint32_t *)memfind(off, size, "OPEN", 4);
+ if (!pos_fopen)
return 1;
- *pos_cropen = fOpenOffset;
+ *pos_fopen = fOpenOffset;
- uint32_t *pos_native = (uint32_t *)memfind(off, size, "NATF", 4);
- uint32_t *pos_twl = (uint32_t *)memfind(off, size, "TWLF", 4);
- uint32_t *pos_agb = (uint32_t *)memfind(off, size, "AGBF", 4);
-
- if (!pos_native && !pos_twl && !pos_agb)
- return 1;
+ // The path is positioned at the end of our assembly to avoid reservation of space.
+ // We append it here.
+ uint8_t path[] = "sdmc:" PATH_BITS "/corbenik.bin";
- fprintf(stderr, "reboot: NATF @ %lx\n", (uint32_t)pos_native);
- fprintf(stderr, "reboot: TWLF @ %lx\n", (uint32_t)pos_twl);
- fprintf(stderr, "reboot: AGBF @ %lx\n", (uint32_t)pos_agb);
-
- uint8_t *mem = (uint8_t *)0x01FF8000; // 0x8000 space that will be resident. This is AXI WRAM. We have about 0x3700 bytes here.
- // According to 3dbrew, this space's props from userland:
- // [L2L] VA fff00000..fff20000 -> PA 1ff80000..1ffa0000 [ X ] [ Priv: R-, User: -- ]
- // It can be executed by the system but not written (and can only be executed with privs)
- // This seems to be the perfect place to stick some code to be resident in memory. Beyond this,
- // it might be needed to replace a svc call to access it. I'm rather sure that NTR does this.
-
- *pos_native = (uint32_t)mem;
- memcpy(mem, u"sdmc:", 10);
- mem += 10;
- for (size_t i = 0; i < sizeof(PATH_NATIVE_P); i++, mem += 2) {
- *mem = PATH_NATIVE_P[i];
- *(mem + 1) = 0;
- }
-
- *pos_twl = (uint32_t)mem;
- memcpy(mem, u"sdmc:", 10);
- mem += 10;
- for (size_t i = 0; i < sizeof(PATH_TWL_P); i++, mem += 2) {
- *mem = PATH_TWL_P[i];
- *(mem + 1) = 0;
- }
+ uint16_t *pos = (uint16_t*)(off + size);
- *pos_agb = (uint32_t)mem;
- memcpy(mem, u"sdmc:", 10);
- mem += 10;
- for (size_t i = 0; i < sizeof(PATH_AGB_P); i++, mem += 2) {
- *mem = PATH_AGB_P[i];
- *(mem + 1) = 0;
+ for(int i=0; path[i] != 0; i++) {
+ pos[0] = path[i];
+ pos++;
}
-
- uint32_t *pos_rebc = (uint32_t *)memfind(off, size, "rebc", 4);
- *pos_rebc = (uint32_t)mem;
-
- fprintf(stderr, "reboot: rebc @ %lx\n", (uint32_t)pos_rebc);
-
- f = cropen(PATH_REBOOT_CODE, "r");
- if (!f)
- return 1;
-
- crread(mem, 1, crsize(f), f);
- crclose(f);
+ pos[0] = 0;
return 0;
}
#include <stdlib.h> // for NULL
#include <string.h> // for memcpy, strlen, strncpy
+#include <ctr9/ctr_system.h>
+#include <ctr9/ctr_cache.h>
+
#include <structures.h>
#include <std/fs.h>
#include <std/memory.h>
__attribute__((noreturn))
void panic() {
- memset(*(uint8_t**)(0x23FFFE00), 0xFF, 320*240*3);
+ ctr_system_poweroff();
while(1);
}
memmove(bootstrap, memory, size); // Memory is now clobbered. Any memory allocation is unsafe past this point.
// Set args = 2, { PATH_BITS "/corbenik.bin", "-native" }
+ ctr_cache_clean_and_flush_all();
- ((int(*)(int, char**))bootstrap)(0, 0);
+ ((int(*)(int, char**))bootstrap)(0, NULL);
panic();
}
*
* \param mode Screen mode to initialize in, one of RGBA8, BGR8, RGB565_OES, RGB5_A1_OES, or RGBA4_OES
*/
-void screen_mode(uint32_t mode);
+void screen_mode(uint32_t mode, uint32_t bright_level);
/* Invokes a bare ARM11 function. For usage, see arm11.c.
*