From e8e0851ebe1d99f1d25a49f6abb2445ceaeebed6 Mon Sep 17 00:00:00 2001 From: marcan Date: Wed, 18 Mar 2009 18:21:00 +0100 Subject: [PATCH] New boot2 load code, fix up IPC interaction --- boot2.c | 334 ++++++++++++++++++++++++++++++++++++++++++++++---------- ipc.c | 11 +- ipc.h | 1 + 3 files changed, 288 insertions(+), 58 deletions(-) diff --git a/boot2.c b/boot2.c index dbb5d51..69c4a00 100644 --- a/boot2.c +++ b/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; blocklen != 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); diff --git a/ipc.c b/ipc.c index 5c03ff5..9d46f21 100644 --- a/ipc.c +++ b/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); diff --git a/ipc.h b/ipc.h index c1f81dc..e6b47f3 100644 --- a/ipc.h +++ b/ipc.h @@ -88,6 +88,7 @@ void ipc_irq(void); void ipc_initialize(void); void ipc_shutdown(void); void ipc_post(u32 code, u32 tag, u32 num_args, ...); +void ipc_flush(void); void ipc_process_slow(void); #endif