diff --git a/Makefile b/Makefile index 72e5e7b..e34a077 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ TARGET = miniios.bin ELF = miniios.elf OBJECTS = start.o ipcstruct.o main.o ipc.o vsprintf.o string.o gecko.o memory.o memory_asm.o \ utils_asm.o utils.o ff.o diskio.o sdhc.o powerpc_elf.o powerpc.o panic.o irq.o irq_asm.o \ - exception.o exception_asm.o + exception.o exception_asm.o crypto.o nand.o nandfs.o $(TARGET) : $(ELF) $(ELFLOADER) @echo "MAKEBIN $@" diff --git a/crypto.c b/crypto.c new file mode 100644 index 0000000..cbecef8 --- /dev/null +++ b/crypto.c @@ -0,0 +1,102 @@ +#include "crypto.h" +#include "hollywood.h" +#include "utils.h" +#include "memory.h" +#include "irq.h" + + +#define AES_CMD_RESET 0 +#define AES_CMD_DECRYPT 0x9800 + +otp_t otp __attribute__((aligned(4))); // FIXME/WTF: why does this crash without the align?! + +void crypto_read_otp(void) +{ + u32 *otpd = (u32*)&otp; + int i; + for (i=0; i< 0x20; i++) { + write32(HW_OTPCMD,0x80000000|i); + *otpd++ = read32(HW_OTPDATA); + } +} + + +void crypto_initialize() +{ + crypto_read_otp(); + write32(AES_CMD, 0); + while (read32(AES_CMD) != 0); + irq_enable(IRQ_AES); +} + + +static int _aes_irq = 0; + +void aes_irq() +{ + _aes_irq = 1; +} + +static inline void aes_command(u16 cmd, u8 iv_keep, u32 blocks) +{ + if (blocks != 0) + blocks--; + _aes_irq = 0; + write32(AES_CMD, (cmd << 16) | (iv_keep ? 0x1000 : 0) | (blocks&0x7f)); + while (read32(AES_CMD) & 0x80000000); +} + +void aes_reset(void) +{ + write32(AES_CMD, 0); + while (read32(AES_CMD) != 0); +} + +void aes_set_iv(u8 *iv) +{ + int i; + for(i = 0; i < 4; i++) { + write32(AES_IV, *(u32 *)iv); + iv += 4; + } +} + +void aes_empty_iv() +{ + int i; + for(i = 0; i < 4; i++) + write32(AES_IV, 0); +} + +void aes_set_key(u8 *key) +{ + int i; + for(i = 0; i < 4; i++) { + write32(AES_KEY, *(u32 *)key); + key += 4; + } +} + +void aes_decrypt(u8 *src, u8 *dst, u32 blocks, u8 keep_iv) +{ + int this_blocks = 0; + while(blocks > 0) { + this_blocks = blocks; + if (this_blocks > 0x80) + this_blocks = 0x80; + + dc_flushrange(src, this_blocks<<4); + dc_invalidaterange(dst, this_blocks<<4); + write32(AES_SRC, (u32)src); + write32(AES_DEST, (u32)dst); + aes_command(AES_CMD_DECRYPT, keep_iv, this_blocks); + + blocks -= this_blocks; + src += this_blocks<<4; + dst += this_blocks<<4; + keep_iv = 1; + } + +} + + diff --git a/crypto.h b/crypto.h new file mode 100644 index 0000000..86017c3 --- /dev/null +++ b/crypto.h @@ -0,0 +1,54 @@ +#ifndef __CRYPTO_H__ +#define __CRYPTO_H__ 1 + +#include "types.h" + +typedef struct +{ + u8 boot1_hash[20]; + u8 common_key[16]; + u32 ng_id; + union { + struct { + u8 ng_priv[30]; + u8 _wtf1[18]; + }; + struct { + u8 _wtf2[28]; + u8 nand_hmac[20]; + }; + }; + u8 nand_key[16]; + u8 rng_key[16]; + u32 unk1; + u32 unk2; +} __attribute__((packed)) otp_t; + +typedef struct +{ + union { + struct { + u32 dunno0; // 0x2 = MS + u32 dunno1; // 0x1 = CA + u32 ng_key_id; + u8 ng_sig[60]; + u8 fill[0x2c]; + u8 korean_key[16]; + }; + u8 data[512]; + }; +} __attribute__((packed)) seeprom_t; + +extern otp_t otp; + +void crypto_read_otp(); + +void crypto_initialize(); + +void aes_reset(void); +void aes_set_iv(u8 *iv); +void aes_empty_iv(); +void aes_set_key(u8 *key); +void aes_decrypt(u8 *src, u8 *dst, u32 blocks, u8 keep_iv); + +#endif diff --git a/irq.c b/irq.c index c1a73b6..5d796f6 100644 --- a/irq.c +++ b/irq.c @@ -3,6 +3,7 @@ #include "gecko.h" #include "utils.h" #include "ipc.h" +#include "crypto.h" void irq_setup_stack(void); @@ -45,6 +46,7 @@ void irq_handler(void) gecko_printf("IRQ: NAND\n"); write32(NAND_CMD, 0x7fffffff); // shut it up write32(HW_IRQFLAG, IRQF_NAND); + nand_irq(); } if(flags & IRQF_GPIO1B) { gecko_printf("IRQ: GPIO1B\n"); @@ -65,6 +67,10 @@ void irq_handler(void) ipc_irq(); write32(HW_IRQFLAG, IRQF_IPC); } + if(flags & IRQF_AES) { + gecko_printf("IRQ: AES\n"); + write32(HW_IRQFLAG, IRQF_AES); + } flags &= ~IRQF_ALL; if(flags) { gecko_printf("IRQ: unknown 0x%08x\n", flags); diff --git a/irq.h b/irq.h index 6ad2eb8..6d55ece 100644 --- a/irq.h +++ b/irq.h @@ -3,6 +3,7 @@ #define IRQ_TIMER 0 #define IRQ_NAND 1 +#define IRQ_AES 2 // not sure on this one #define IRQ_GPIO1B 10 #define IRQ_GPIO1 11 #define IRQ_RESET 17 @@ -10,6 +11,7 @@ #define IRQF_TIMER (1< 0 ? ((alignment) - \ +((sizeof(type)*(cnt))%(alignment))) : 0))]; \ +type *name = (type*)(((u32)(_al__##name)) + ((alignment) - (( \ +(u32)(_al__##name))&((alignment)-1)))) + + +#define NAND_RESET 0xff +#define NAND_CHIPID 0x90 +#define NAND_GETSTATUS 0x70 +#define NAND_ERASE_PRE 0x60 +#define NAND_ERASE_POST 0xd0 +#define NAND_READ_PRE 0x00 +#define NAND_READ_POST 0x30 +#define NAND_WRITE_PRE 0x80 +#define NAND_WRITE_POST 0x10 + +#define NAND_BUSY_MASK 0x80000000 + +#define PAGE_SIZE 2048 +#define PAGE_SPARE_SIZE 64 + +static int irq = 0; + +void nand_irq(void) +{ + irq = 1; +} + + +static inline u32 __nand_read32(u32 addr) +{ + return read32(addr); +} + +inline void __nand_write32(u32 addr, u32 data) +{ + write32(addr, data); +} + +inline void __nand_wait(void) { + while(__nand_read32(NAND_CMD) & NAND_BUSY_MASK); +} + +void nand_send_command(u32 command, u32 bitmask, u32 flags, u32 num_bytes) { + u32 cmd = NAND_BUSY_MASK | (bitmask << 24) | (command << 16) | flags | num_bytes; + + NAND_debug("nand_send_command(%x, %x, %x, %x) -> %x\n", + command, bitmask, flags, num_bytes, cmd); + + __nand_write32(NAND_CMD, 0x7fffffff); + __nand_write32(NAND_CMD, 0); + __nand_write32(NAND_CMD, cmd); + __nand_wait(); +} + +void __nand_set_address(s32 page_off, s32 pageno) { + NAND_debug("nand_set_address: %d, %d\n", page_off, pageno); + if (page_off != -1) __nand_write32(NAND_ADDR0, page_off); + if (pageno != -1) __nand_write32(NAND_ADDR1, pageno); +} + +void __nand_setup_dma(u8 *data, u8 *spare) { + NAND_debug("nand_setup_dma: %p, %p\n", data, spare); + if (((s32)data) != -1) { + dc_invalidaterange(data, 0x800); + __nand_write32(NAND_DATA, (s32)data); + } + if (((s32)spare) != -1) { + dc_invalidaterange(spare, 0x50); // +0x10 for calculated syndrome? + __nand_write32(NAND_ECC, (s32)spare); + } +} + +int nand_reset(void) +{ + NAND_debug("nand_reset()\n"); + nand_send_command(NAND_RESET, 0, 0x8000, 0); +// yay cargo cult + __nand_write32(NAND_CONF, 0x8000000); + __nand_write32(NAND_CONF, 0x4b3e0e7f); + return 0; +} + +u32 nand_get_id(void) { + STACK_ALIGN(u8, idbuf, 0x40, 64); + + __nand_wait(); + + NAND_debug("nand_get_id(%p) (pre)\n", idbuf); + memset(idbuf, 0x42, 4); + dc_flushrange(idbuf, 0x40); + __nand_set_address(0,0); + + NAND_debug("id = %02hx%02hx%02hx%02hx\n", idbuf[0], idbuf[1], idbuf[2], idbuf[3]); + + __nand_setup_dma(idbuf, (u8 *)-1); + nand_send_command(NAND_CHIPID, 1, 0x2000, 0x40); + NAND_debug("id = %02hx%02hx%02hx%02hx (post)\n", idbuf[0], idbuf[1], idbuf[2], idbuf[3]); + + return idbuf[0] << 24 | idbuf[1] << 16 | idbuf[2] << 8 | idbuf[3]; +} + +u32 nand_get_status(void) { + STACK_ALIGN(u8, status_buf, 0x40, 64); + status_buf[0]=0; + dc_flushrange(status_buf, 0x40); + __nand_setup_dma(status_buf, (u8 *)-1); + nand_send_command(NAND_GETSTATUS, 0, 0x2000, 0x40); + dc_invalidaterange(status_buf, 0x40); + return status_buf[0]; +} + +inline void __nand_wait_irq(void) { + while(irq == 0); +} + +void nand_read_page(u32 pageno, void *data, void *ecc) { + NAND_debug("nand_read_page(%u, %p, %p)\n", pageno, data, ecc); +#if 1 + irq = 0; + __nand_set_address(0, pageno); + nand_send_command(NAND_READ_PRE, 0x1f, 0, 0); + + dc_invalidaterange(data, 0x800); + dc_invalidaterange(ecc, 0x50); + + __nand_setup_dma(data, ecc); + nand_send_command(NAND_READ_POST, 0, 0x4000b000, 0x840); + + __nand_wait_irq(); +#else + int i; + unsigned char *c_data = (unsigned char *)data; + unsigned char *c_ecc = (unsigned char *)ecc; + for(i=0; i<0x800; i++) c_data[i] = pageno % 256; + for(i=0; i<0x40; i++) c_ecc = pageno % 256; +#endif +} + + +// FIXME: do we really need to read the ECC data? +static u8 ecc[64] __attribute__((aligned(32))) MEM2_BSS; +void nand_read_cluster(u32 clusterno, void *data) { + int i; + for(i = 0; i < 8; i++) + nand_read_page((clusterno*8) + i, data + (i * PAGE_SIZE), ecc); +} + +void nand_read_decrypted_cluster(u32 clusterno, void *data) { + nand_read_cluster(clusterno, data); + aes_reset(); + aes_set_key(otp.nand_key); + aes_empty_iv(); + aes_decrypt(data, data, 0x400, 0); +} + +#ifdef NAND_SUPPORT_WRITE +void nand_write_page(u32 pageno, void *data, void *ecc) { + NAND_debug("nand_write_page(%u, %p, %p)\n", pageno, data, ecc); + dc_flushrange(data, 0x800); + dc_flushrange(ecc, 0x40); + __nand_set_address(0, pageno); + __nand_setup_dma(data, ecc); + nand_send_command(NAND_WRITE_PRE, 0x1f, 0x40005000, 0x840); + + nand_send_command(NAND_WRITE_POST, 0, 0x40008000, 0x0); +} +#endif + +#ifdef NAND_SUPPORT_ERASE +void nand_erase_block(u32 pageno) { + NAND_debug("nand_erase_block(%d)\n", pageno); + __nand_set_address(0, pageno); + nand_send_command(NAND_ERASE_PRE, 0x1c, 0, 0); + nand_send_command(NAND_ERASE_POST, 0, 0x40008000, 0x0); +} +#endif + +void nand_initialize(void) +{ + irq_enable(IRQ_NAND); + irq = 0; + nand_reset(); +} diff --git a/nand.h b/nand.h new file mode 100644 index 0000000..8103d13 --- /dev/null +++ b/nand.h @@ -0,0 +1,21 @@ +#ifndef __NAND_H__ +#define __NAND_H__ + +#include "types.h" + +void nand_irq(void); + +void nand_send_command(u32 command, u32 bitmask, u32 flags, u32 num_bytes); +int nand_reset(void); +u32 nand_get_id(void); +u32 nand_get_status(void); +void nand_read_page(u32 pageno, void *data, void *ecc); +void nand_write_page(u32 pageno, void *data, void *ecc); +void nand_erase_block(u32 pageno); + +void nand_read_cluster(u32 clusterno, void *data); +void nand_read_decrypted_cluster(u32 clusterno, void *data); + +void nand_initialize(); + +#endif diff --git a/nandfs.c b/nandfs.c new file mode 100644 index 0000000..ee24111 --- /dev/null +++ b/nandfs.c @@ -0,0 +1,214 @@ +#include "nandfs.h" +#include "nand.h" +#include "gecko.h" +#include "string.h" + +#define PAGE_SIZE 2048 + +struct _nandfs_file_node { + u8 name[NANDFS_NAME_LEN]; + u8 attr; + u8 wtf; + union { + u16 first_child; + u16 first_cluster; + }; + u16 sibling; + u32 size; + u32 uid; + u16 gid; + u32 dummy; +} __attribute__((packed)); + +struct _nandfs_sffs { + u8 magic[4]; + u32 version; + u32 dummy; + + s16 cluster_table[32768]; + struct _nandfs_file_node files[6143]; +} __attribute__((packed)); + +static struct _nandfs_sffs sffs __attribute__((aligned(32))) MEM2_BSS; +static u8 buffer[8*2048] __attribute__((aligned(32))) MEM2_BSS; +static s32 initialized = 0; + +s32 nandfs_initialize() +{ + u32 i; + u32 supercluster = 0; + u32 supercluster_version = 0; + + nand_reset(); + + for(i = 0x7F00; i < 0x7fff; i++) { + nand_read_page(i*8, (void *)&sffs, NULL); + if(memcmp(sffs.magic, "SFFS", 4) != 0) + continue; + if(supercluster == 0 || sffs.version > supercluster_version) { + supercluster = i; + supercluster_version = sffs.version; + } + + } + + if(supercluster == 0) { + gecko_printf("no supercluster found. " + " your nand filesystem is seriously broken...\n"); + return -1; + } + + gecko_printf("using supercluster starting at page %08x\n", + supercluster); + + for(i = 0; i < sizeof(struct _nandfs_sffs)/(PAGE_SIZE*8); + i++) { + nand_read_cluster(supercluster + i, + ((u8 *)&sffs) + (i * PAGE_SIZE * 8)); + } + + initialized = 1; + return 0; +} + +s32 nandfs_open(struct nandfs_fp *fp, const char *path) +{ + char *ptr, *ptr2; + u32 len; + struct _nandfs_file_node *cur = sffs.files; + + if (initialized != 1) + return -1; + + memset(fp, 0, sizeof(*fp)); + + if(strcmp(cur->name, "/") != 0) { + gecko_printf("your nandfs is corrupted. fixit!\n"); + return -1; + } + + cur = &sffs.files[cur->first_child]; + + ptr = (char *)path; + do { + ptr++; + ptr2 = strchr(ptr, '/'); + if (ptr2 == NULL) + len = strlen(ptr); + else { + ptr2++; + len = ptr2 - ptr - 1; + } + if (len > 12) + { + gecko_printf("invalid length: %s %s %s [%d]\n", + ptr, ptr2, path, len); + return -1; + } + + gecko_printf("length: %d\n", len); + + for (;;) { + int res = strncmp(cur->name, ptr, len); + if(ptr2 != NULL && strncmp(cur->name, ptr, len) == 0 + && strnlen(cur->name, 12) == len + && (cur->attr&3) == 2 + && (cur->first_child&0xffff) != (s16)0xffff) { + cur = &sffs.files[cur->first_child]; + ptr = ptr2-1; + break; + } else if(ptr2 == NULL && + strncmp(cur->name, ptr, len) == 0 && + strnlen(cur->name, 12) == len && + (cur->attr&3) == 1) { + break; + } else if((cur->sibling&0xffff) != 0xffff) { + cur = &sffs.files[cur->sibling]; + } else { + gecko_printf("unable to find %s (%s)\n", ptr, + path); + return -1; + } + } + + } while(ptr2 != NULL); + + fp->first_cluster = cur->first_cluster; + fp->cur_cluster = fp->first_cluster; + fp->offset = 0; + fp->size = cur->size; + return 0; +} + +s32 nandfs_read(void *ptr, u32 size, u32 nmemb, struct nandfs_fp *fp) +{ + u32 total = size*nmemb; + u32 copy_offset, copy_len; + + if (initialized != 1) + return -1; + + if (fp->offset + total > fp->size) + total = fp->size - fp->offset; + + if (total == 0) + return 0; + + while(total > 0) { + nand_read_decrypted_cluster(fp->cur_cluster, buffer); + copy_offset = fp->offset % (PAGE_SIZE * 8); + copy_len = (PAGE_SIZE * 8) - copy_offset; + if(copy_len > total) + copy_len = total; + memcpy(ptr, buffer + copy_offset, copy_len); + total -= copy_len; + fp->offset += copy_len; + + if ((copy_offset + copy_len) >= (PAGE_SIZE * 8)) + fp->cur_cluster = sffs.cluster_table[fp->cur_cluster]; + } + + return size*nmemb; +} + +s32 nandfs_seek(struct nandfs_fp *fp, s32 offset, u32 whence) +{ + if (initialized != 1) + return -1; + + switch (whence) { + case NANDFS_SEEK_SET: + if (offset > fp->size) + return -1; + if (offset > 0) + return -1; + + fp->offset = offset; + break; + + case NANDFS_SEEK_CUR: + if ((fp->offset + offset) > fp->size || + (fp->offset + offset) < 0) + return -1; + fp->offset += offset; + break; + + case NANDFS_SEEK_END: + default: + if ((fp->size + offset) > fp->size || + (fp->size + offset) < 0) + return -1; + fp->offset = fp->size + offset; + break; + } + + int skip = fp->offset; + fp->cur_cluster = fp->first_cluster; + while (skip > (2048*8)) { + fp->cur_cluster = sffs.cluster_table[fp->cur_cluster]; + skip -= 2048*8; + } + + return 0; +} + diff --git a/nandfs.h b/nandfs.h new file mode 100644 index 0000000..a34bec1 --- /dev/null +++ b/nandfs.h @@ -0,0 +1,25 @@ +#ifndef __NANDFS_H__ +#define __NANDFS_H__ + +#include "types.h" + +#define NANDFS_NAME_LEN 12 + +#define NANDFS_SEEK_SET 0 +#define NANDFS_SEEK_CUR 1 +#define NANDFS_SEEK_END 2 + +struct nandfs_fp { + s16 first_cluster; + s32 cur_cluster; + u32 size; + u32 offset; +}; + +s32 nandfs_initialize(); + +s32 nandfs_open(struct nandfs_fp *fp, const char *path); +s32 nandfs_read(void *ptr, u32 size, u32 nmemb, struct nandfs_fp *fp); +s32 nandfs_seek(struct nandfs_fp *fp, s32 offset, u32 whence); + +#endif diff --git a/string.c b/string.c index 7d5481c..9b21d3e 100644 --- a/string.c +++ b/string.c @@ -58,6 +58,18 @@ int strcmp(const char *p, const char *q) } } +int strncmp(const char *p, const char *q, size_t n) +{ + while (n-- != 0) { + unsigned char a, b; + a = *p++; + b = *q++; + if (a == 0 || a != b) + return a - b; + } + return 0; +} + void *memset(void *dst, int x, size_t n) { unsigned char *p; @@ -92,3 +104,11 @@ int memcmp(const void *s1, const void *s2, size_t n) return 0; } +char *strchr(const char *s, int c) +{ + do { + if(*s == c) + return (char *)s; + } while(*s++ != 0); + return NULL; +} diff --git a/string.h b/string.h index 8a1f13e..d016af4 100644 --- a/string.h +++ b/string.h @@ -6,8 +6,10 @@ char *strcpy(char *, const char *); char *strncpy(char *, const char *, size_t); int strcmp(const char *, const char *); +int strncmp(const char *p, const char *q, size_t n); size_t strlen(const char *); size_t strnlen(const char *, size_t); +char *strchr(const char *s, int c); void *memset(void *, int, size_t); void *memcpy(void *, const void *, size_t); int memcmp(const void *s1, const void *s2, size_t n);