mirror of
https://github.com/fail0verflow/mini.git
synced 2024-11-28 06:04:19 +01:00
New boot2 load code, fix up IPC interaction
This commit is contained in:
parent
e1ca14840e
commit
e8e0851ebe
334
boot2.c
334
boot2.c
@ -5,23 +5,28 @@
|
||||
#include "string.h"
|
||||
#include "gecko.h"
|
||||
#include "powerpc.h"
|
||||
#include "utils.h"
|
||||
|
||||
static u8 boot2[256 << 11] MEM2_BSS ALIGNED(64);
|
||||
static u8 key[32] ALIGNED(64);
|
||||
static u8 ecc[128] ALIGNED(128);
|
||||
static u8 boot2[0x80000] MEM2_BSS ALIGNED(64);
|
||||
static u8 boot2_key[32] MEM2_BSS ALIGNED(32);
|
||||
static u8 boot2_iv[32] MEM2_BSS ALIGNED(32);
|
||||
static u8 sector_buf[PAGE_SIZE] MEM2_BSS ALIGNED(64);
|
||||
static u8 ecc_buf[ECC_BUFFER_ALLOC] MEM2_BSS ALIGNED(128);
|
||||
static u8 boot2_initialized = 0;
|
||||
static u8 boot2_copy;
|
||||
static u8 pages_read;
|
||||
static u8 *page_ptr;
|
||||
extern void *vector;
|
||||
|
||||
struct wadheader {
|
||||
typedef struct {
|
||||
u32 len;
|
||||
u32 type;
|
||||
u32 data_offset;
|
||||
u32 certs_len;
|
||||
u32 tik_len;
|
||||
u32 tmd_len;
|
||||
u32 reserved; // ??
|
||||
u32 data_len; // ??
|
||||
u32 footer_len; // ??
|
||||
};
|
||||
u32 padding[3];
|
||||
} boot2header;
|
||||
|
||||
typedef struct {
|
||||
u32 hdrsize;
|
||||
u32 loadersize;
|
||||
@ -29,53 +34,266 @@ typedef struct {
|
||||
u32 argument;
|
||||
} ioshdr;
|
||||
|
||||
void boot2_init() {
|
||||
int i;
|
||||
void *ptr = boot2;
|
||||
u8 *tikptr = NULL;
|
||||
u8 *tmdptr = NULL;
|
||||
u8 *cntptr = NULL;
|
||||
u8 iv[16];
|
||||
struct wadheader *hdr = (struct wadheader *)boot2;
|
||||
u32 datalen;
|
||||
typedef struct {
|
||||
u64 signature;
|
||||
u32 generation;
|
||||
u8 blocks[0x40];
|
||||
} __attribute__((packed)) boot2blockmap;
|
||||
|
||||
for (i = 0x40; i < 0x140; i++, ptr += 2048) {
|
||||
nand_read_page(i, ptr, ecc);
|
||||
typedef struct {
|
||||
u32 cid;
|
||||
u16 index;
|
||||
u16 type;
|
||||
u64 size;
|
||||
u8 hash[20];
|
||||
} __attribute__((packed)) tmd_content;
|
||||
|
||||
typedef struct {
|
||||
u32 type;
|
||||
u8 sig[256];
|
||||
u8 fill[60];
|
||||
} __attribute__((packed)) sig_rsa2048;
|
||||
|
||||
typedef struct {
|
||||
sig_rsa2048 signature;
|
||||
char issuer[0x40];
|
||||
u8 version;
|
||||
u8 ca_crl_version;
|
||||
u8 signer_crl_version;
|
||||
u8 fill2;
|
||||
u64 sys_version;
|
||||
u64 title_id;
|
||||
u32 title_type;
|
||||
u16 group_id;
|
||||
u16 zero;
|
||||
u16 region;
|
||||
u8 ratings[16];
|
||||
u8 reserved[42];
|
||||
u32 access_rights;
|
||||
u16 title_version;
|
||||
u16 num_contents;
|
||||
u16 boot_index;
|
||||
u16 fill3;
|
||||
tmd_content boot_content;
|
||||
} __attribute__((packed)) tmd;
|
||||
|
||||
typedef struct _tik {
|
||||
sig_rsa2048 signature;
|
||||
char issuer[0x40];
|
||||
u8 fill[63];
|
||||
u8 cipher_title_key[16];
|
||||
u8 fill2;
|
||||
u64 ticketid;
|
||||
u32 devicetype;
|
||||
u64 titleid;
|
||||
u16 access_mask;
|
||||
u8 reserved[0x3c];
|
||||
u8 cidx_mask[0x40];
|
||||
u16 padding;
|
||||
u32 limits[16];
|
||||
} __attribute__((packed)) tik;
|
||||
|
||||
static boot2blockmap good_blockmap MEM2_BSS;
|
||||
|
||||
#define BLOCKMAP_SIGNATURE 0x26f29a401ee684cfULL
|
||||
|
||||
#define BOOT2_START 1
|
||||
#define BOOT2_END 7
|
||||
|
||||
static u8 boot2_blocks[BOOT2_END - BOOT2_START + 1];
|
||||
static u32 valid_blocks;
|
||||
|
||||
static tmd boot2_tmd MEM2_BSS;
|
||||
static tik boot2_tik MEM2_BSS;
|
||||
static u8 *boot2_content;
|
||||
static u32 boot2_content_size;
|
||||
|
||||
// find two equal valid blockmaps from a set of three, return one of them
|
||||
static int find_valid_map(const boot2blockmap *maps)
|
||||
{
|
||||
if(maps[0].signature == BLOCKMAP_SIGNATURE) {
|
||||
if(!memcmp(&maps[0],&maps[1],sizeof(boot2blockmap)))
|
||||
return 0;
|
||||
if(!memcmp(&maps[0],&maps[2],sizeof(boot2blockmap)))
|
||||
return 0;
|
||||
}
|
||||
if(maps[1].signature == BLOCKMAP_SIGNATURE) {
|
||||
if(!memcmp(&maps[1],&maps[2],sizeof(boot2blockmap)))
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// translate a page offset into boot2 to a real NAND page number using blockmap
|
||||
static inline u32 boot2_page_translate(u32 page)
|
||||
{
|
||||
u32 subpage = page % BLOCK_SIZE;
|
||||
u32 block = page / BLOCK_SIZE;
|
||||
|
||||
return boot2_blocks[block] * BLOCK_SIZE + subpage;
|
||||
}
|
||||
|
||||
// read boot2 up to the specified number of bytes (aligned to the next page)
|
||||
static int read_to(u32 bytes)
|
||||
{
|
||||
if(bytes > (valid_blocks * BLOCK_SIZE * PAGE_SIZE)) {
|
||||
gecko_printf("tried to read %d boot2 bytes (%d pages), but only %d blocks (%d pages) are valid!\n", bytes, (bytes+(PAGE_SIZE-1)) / PAGE_SIZE, valid_blocks, valid_blocks * BLOCK_SIZE);
|
||||
return -1;
|
||||
}
|
||||
while(bytes > pages_read * PAGE_SIZE) {
|
||||
u32 page = boot2_page_translate(pages_read);
|
||||
nand_read_page(page, page_ptr, ecc_buf);
|
||||
nand_wait();
|
||||
if(nand_correct(i, ptr, ecc) < 0) {
|
||||
gecko_printf("boot2 is corrupted, cannot load!\n");
|
||||
return;
|
||||
if(nand_correct(page, page_ptr, ecc_buf) < 0) {
|
||||
gecko_printf("boot2 page %d (NAND 0x%x) is uncorrectable\n", pages_read, page);
|
||||
return -1;
|
||||
}
|
||||
page_ptr += PAGE_SIZE;
|
||||
pages_read++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int boot2_load(int copy)
|
||||
{
|
||||
boot2blockmap *maps = (boot2blockmap*)sector_buf;
|
||||
u32 block;
|
||||
u32 page;
|
||||
int mapno;
|
||||
u32 found = 0;
|
||||
boot2header *hdr;
|
||||
u8 iv[16];
|
||||
|
||||
boot2_content = NULL;
|
||||
boot2_content_size = 0;
|
||||
pages_read = 0;
|
||||
memset(&good_blockmap, 0, sizeof(boot2blockmap));
|
||||
valid_blocks = 0;
|
||||
|
||||
// find the best blockmap
|
||||
for(block=BOOT2_START; block<=BOOT2_END; block++) {
|
||||
page = (block+1)*BLOCK_SIZE - 1;
|
||||
nand_read_page(page, sector_buf, ecc_buf);
|
||||
nand_wait();
|
||||
// boot1 doesn't actually do this, but it's probably a good idea to try to correct 1-bit errors anyway
|
||||
if(nand_correct(page, sector_buf, ecc_buf) < 0) {
|
||||
gecko_printf("boot2 map candidate page %d is uncorrectable, trying anyway\n", page);
|
||||
}
|
||||
mapno = find_valid_map(maps);
|
||||
if(mapno >= 0) {
|
||||
gecko_printf("found valid boot2 blockmap at page 0x%x, submap %d, generation %d\n", page, mapno, maps[mapno].generation);
|
||||
if(maps[mapno].generation >= good_blockmap.generation) {
|
||||
memcpy(&good_blockmap, &maps[mapno], sizeof(boot2blockmap));
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hdr->len != sizeof(struct wadheader))
|
||||
return;
|
||||
if(!found) {
|
||||
gecko_printf("no valid boot2 blockmap found!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
tikptr = boot2 + hdr->len + hdr->certs_len;
|
||||
tmdptr = tikptr + hdr->tik_len;
|
||||
cntptr = ALIGN_FORWARD(tmdptr + hdr->tmd_len, 0x40);
|
||||
// traverse the blockmap and make a list of the actual boot2 blocks, in order
|
||||
if(copy == 0) {
|
||||
for(block=BOOT2_START; block<=BOOT2_END; block++) {
|
||||
if(good_blockmap.blocks[block] == 0x00) {
|
||||
boot2_blocks[valid_blocks++] = block;
|
||||
}
|
||||
}
|
||||
} else if(copy == 1) {
|
||||
for(block=BOOT2_END; block>=BOOT2_START; block--) {
|
||||
if(good_blockmap.blocks[block] == 0x00) {
|
||||
boot2_blocks[valid_blocks++] = block;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
gecko_printf("invalid boot2 copy %d\n", copy);
|
||||
return -1;
|
||||
}
|
||||
|
||||
gecko_printf("boot2 blocks:");
|
||||
for(block=0; block<valid_blocks; block++)
|
||||
gecko_printf(" %02x", boot2_blocks[block]);
|
||||
gecko_printf("\n");
|
||||
|
||||
// read boot2 header
|
||||
page_ptr = boot2;
|
||||
if(read_to(sizeof(boot2header)) < 0) {
|
||||
gecko_printf("error while reading boot2 header");
|
||||
return -1;
|
||||
}
|
||||
|
||||
hdr = (boot2header *)boot2;
|
||||
|
||||
if(hdr->len != sizeof(boot2header)) {
|
||||
gecko_printf("invalid boot2 header size 0x%x\n", hdr->len);
|
||||
return -1;
|
||||
}
|
||||
if(hdr->tmd_len != sizeof(tmd)) {
|
||||
gecko_printf("boot2 tmd size mismatch: expected 0x%x, got 0x%x (more than one content?)\n", sizeof(tmd), hdr->tmd_len);
|
||||
return -1;
|
||||
}
|
||||
if(hdr->tik_len != sizeof(tik)) {
|
||||
gecko_printf("boot2 tik size mismatch: expected 0x%x, got 0x%x\n", sizeof(tik), hdr->tik_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// read tmd, tik, certs
|
||||
if(read_to(hdr->data_offset) < 0) {
|
||||
gecko_printf("error while reading boot2 certs/tmd/ticket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(&boot2_tik, &boot2[hdr->len + hdr->certs_len], sizeof(tik));
|
||||
memcpy(&boot2_tmd, &boot2[hdr->len + hdr->certs_len + hdr->tik_len], sizeof(tmd));
|
||||
|
||||
memset(iv, 0, 16);
|
||||
memcpy(iv, tikptr + 0x1dc, 8);
|
||||
memcpy(iv, &boot2_tik.titleid, 8);
|
||||
|
||||
aes_reset();
|
||||
aes_set_iv(iv);
|
||||
aes_set_key(otp.common_key);
|
||||
memcpy(key, tikptr+0x1bf, 16);
|
||||
aes_decrypt(key, key, 1, 0);
|
||||
memcpy(boot2_key, &boot2_tik.cipher_title_key, 16);
|
||||
|
||||
memcpy(&datalen, tmdptr+0x1e4+8+4, 4);
|
||||
memset(iv, 0, 16);
|
||||
memcpy(iv, tmdptr + 0x1e4 + 4, 2);
|
||||
aes_decrypt(boot2_key, boot2_key, 1, 0);
|
||||
|
||||
aes_reset();
|
||||
aes_set_iv(iv);
|
||||
aes_set_key(key);
|
||||
aes_decrypt(cntptr, cntptr, ALIGN_FORWARD(datalen, 16)/16, 0);
|
||||
memset(boot2_iv, 0, 16);
|
||||
memcpy(boot2_iv, &boot2_tmd.boot_content.index, 2); //just zero anyway...
|
||||
|
||||
memcpy(boot2, cntptr, datalen);
|
||||
u32 *kp = (u32*)boot2_key;
|
||||
gecko_printf("boot2 title key: %08x%08x%08x%08x\n", kp[0], kp[1], kp[2], kp[3]);
|
||||
|
||||
boot2_content_size = (boot2_tmd.boot_content.size + 15) & ~15;
|
||||
gecko_printf("boot2 content size: 0x%x (padded: 0x%x)\n", (u32)boot2_tmd.boot_content.size, boot2_content_size);
|
||||
|
||||
// read content
|
||||
if(read_to(hdr->data_offset + boot2_content_size) < 0) {
|
||||
gecko_printf("error while reading boot2 content");
|
||||
return -1;
|
||||
}
|
||||
|
||||
boot2_content = &boot2[hdr->data_offset];
|
||||
|
||||
boot2_copy = copy;
|
||||
gecko_printf("boot2 copy %d loaded to %p\n", copy, boot2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void boot2_init() {
|
||||
boot2_copy = -1;
|
||||
boot2_initialized = 0;
|
||||
if(boot2_load(0) < 0) {
|
||||
gecko_printf("failed to load boot2 copy 0, trying copy 1...\n");
|
||||
if(boot2_load(1) < 0) {
|
||||
gecko_printf("failed to load boot2 copy 1!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// boot2 content flush would flush entire cache anyway so just do it all
|
||||
dc_flushall();
|
||||
boot2_initialized = 1;
|
||||
gecko_printf("boot2 loaded\n");
|
||||
}
|
||||
|
||||
static u32 match[] = {
|
||||
@ -92,21 +310,22 @@ static u32 patch[] = {
|
||||
0x48415858,
|
||||
};
|
||||
|
||||
int boot2_run(u32 tid_hi, u32 tid_lo) {
|
||||
void *ptr;
|
||||
void boot2_run(u32 tid_hi, u32 tid_lo) {
|
||||
u8 *ptr;
|
||||
int i;
|
||||
ioshdr *hdr = (ioshdr *)boot2;
|
||||
|
||||
if (boot2_initialized != 1)
|
||||
return 0;
|
||||
ioshdr *hdr;
|
||||
|
||||
patch[2] = tid_hi;
|
||||
patch[3] = tid_lo;
|
||||
|
||||
gecko_printf("booting boot2 with title 0x%08x, 0x%08x\n", tid_hi, tid_lo);
|
||||
gecko_printf("booting boot2 with title %08x-%08x\n", tid_hi, tid_lo);
|
||||
mem_protect(1, (void *)0x11000000, (void *)0x13FFFFFF);
|
||||
memcpy((void *)0x11000000, boot2, sizeof boot2);
|
||||
ptr = (void *)0x11000000 + hdr->hdrsize + hdr->loadersize;
|
||||
aes_reset();
|
||||
aes_set_iv(boot2_iv);
|
||||
aes_set_key(boot2_key);
|
||||
aes_decrypt(boot2_content, (void *)0x11000000, boot2_content_size / 16, 0);
|
||||
hdr = (ioshdr *)0x11000000;
|
||||
ptr = (u8 *)0x11000000 + hdr->hdrsize + hdr->loadersize;
|
||||
for (i = 0; i < sizeof(boot2); i += 1) {
|
||||
if (memcmp(ptr+i, match, sizeof(match)) == 0) {
|
||||
memcpy(ptr+i, patch, sizeof(patch));
|
||||
@ -114,21 +333,24 @@ int boot2_run(u32 tid_hi, u32 tid_lo) {
|
||||
}
|
||||
}
|
||||
|
||||
hdr = (ioshdr *)0x11000000;
|
||||
hdr->argument = 0x42;
|
||||
vector = (void *)0x11000000 + hdr->hdrsize;
|
||||
vector = (u8 *)0x11000000 + hdr->hdrsize;
|
||||
gecko_printf("boot2 is at %p\n", vector);
|
||||
return 1;
|
||||
return;
|
||||
}
|
||||
|
||||
void boot2_ipc(volatile ipc_request *req)
|
||||
{
|
||||
u32 ret = 0;
|
||||
|
||||
switch (req->req) {
|
||||
case IPC_BOOT2_RUN:
|
||||
ret = boot2_run((u32)req->args[0], (u32)req->args[1]);
|
||||
ipc_post(req->code, req->tag, 1, ret);
|
||||
if(boot2_initialized) {
|
||||
// post first so that the memory protection doesn't kill IPC for the PowerPC
|
||||
ipc_post(req->code, req->tag, 1, boot2_copy);
|
||||
ipc_flush();
|
||||
boot2_run((u32)req->args[0], (u32)req->args[1]);
|
||||
} else {
|
||||
ipc_post(req->code, req->tag, 1, -1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
gecko_printf("IPC: unknown SLOW BOOT2 request %04X\n", req->req);
|
||||
|
11
ipc.c
11
ipc.c
@ -98,6 +98,11 @@ void ipc_post(u32 code, u32 tag, u32 num_args, ...)
|
||||
irq_restore(cookie);
|
||||
}
|
||||
|
||||
void ipc_flush(void)
|
||||
{
|
||||
while(peek_outhead() != out_tail);
|
||||
}
|
||||
|
||||
static int process_slow(volatile ipc_request *req)
|
||||
{
|
||||
//gecko_printf("IPC: process slow_queue @ %p\n",req);
|
||||
@ -252,8 +257,10 @@ void ipc_initialize(void)
|
||||
|
||||
void ipc_shutdown(void)
|
||||
{
|
||||
write32(HW_IPC_ARMMSG, 0);
|
||||
write32(HW_IPC_PPCMSG, 0);
|
||||
// Don't kill message registers so our PPC side doesn't get confused
|
||||
//write32(HW_IPC_ARMMSG, 0);
|
||||
//write32(HW_IPC_PPCMSG, 0);
|
||||
// Do kill flags so Nintendo's SDK doesn't get confused
|
||||
write32(HW_IPC_PPCCTRL, IPC_CTRL_RESET);
|
||||
write32(HW_IPC_ARMCTRL, IPC_CTRL_RESET);
|
||||
irq_disable(IRQ_IPC);
|
||||
|
Loading…
Reference in New Issue
Block a user