rockbox/utils/rknanoutils/rkboottool/rkboottool.c
Amaury Pouly 89254cb612 rknanoutils: add raw encode mode, add header fields
The raw encode mode allows to use the raw encode_page routine on
any file which proved to be useful. The guessed fields of the
headers are based on some rk2918 headers which leaked. They are
mainly informative though (date, version, chip).

Change-Id: I139ea0c40f76b6dde041c448bbf3e7ecf9cab24a
2012-10-18 10:41:14 +02:00

869 lines
25 KiB
C

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <stdarg.h>
#include "misc.h"
#include "elf.h"
#define cprintf(col, ...) do {color(col); printf(__VA_ARGS__); }while(0)
bool g_debug = false;
typedef uint8_t packed_bcd_uint8_t;
typedef uint16_t packed_bcd_uint16_t;
struct rknano_date_t
{
packed_bcd_uint16_t year;
packed_bcd_uint8_t mday;
packed_bcd_uint8_t month;
};
struct rknano_version_t
{
packed_bcd_uint16_t major;
packed_bcd_uint16_t minor;
packed_bcd_uint16_t rev;
};
struct rknano_image_t
{
uint16_t width;
uint16_t height;
uint8_t data[0];
};
struct rknano_blob_t
{
uint32_t offset;
uint32_t size;
};
#define VENDOR_NAME_SIZE 32
#define MODEL_NAME_SIZE 32
#define MAX_NR_STAGES 4
#define MAX_NR_FONTS 10
#define MAX_NR_GBK 5
#define MAX_NR_STRTBL 10
#define MAX_NR_IMAGERES 10
#define MAX_NR_UNK 10
#define MAGIC_RKNANOFW "RKnanoFW"
#define MAGIC_RKNANOFW_SIZE 8
struct rknano_header_t
{
struct rknano_date_t date;
struct rknano_version_t version;
uint8_t unk6[6];
char vendor[VENDOR_NAME_SIZE];
char model[MODEL_NAME_SIZE];
uint32_t nr_stages;
struct rknano_blob_t stage[MAX_NR_STAGES];
uint32_t nr_fonts;
struct rknano_blob_t font[MAX_NR_FONTS];
uint32_t nr_gbk;
struct rknano_blob_t gbk[MAX_NR_GBK];
uint32_t nr_strtbl;
struct rknano_blob_t strtbl[MAX_NR_STRTBL];
uint32_t nr_imageres;
struct rknano_blob_t imageres[MAX_NR_IMAGERES];
uint32_t nr_unk;
struct rknano_blob_t unk[MAX_NR_UNK];
uint32_t pad;
uint32_t size;
char magic[MAGIC_RKNANOFW_SIZE];
};
char *g_out_prefix = NULL;
static void encode_page(uint8_t *inpg, uint8_t *outpg, const int size)
{
uint8_t key[] = {
0x7C, 0x4E, 0x03, 0x04,
0x55, 0x05, 0x09, 0x07,
0x2D, 0x2C, 0x7B, 0x38,
0x17, 0x0D, 0x17, 0x11
};
int i, i3, x, val, idx;
uint8_t key1[0x100];
uint8_t key2[0x100];
for (i=0; i<0x100; i++) {
key1[i] = i;
key2[i] = key[i&0xf];
}
i3 = 0;
for (i=0; i<0x100; i++) {
x = key1[i];
i3 = key1[i] + i3;
i3 += key2[i];
i3 &= 0xff;
key1[i] = key1[i3];
key1[i3] = x;
}
idx = 0;
for (i=0; i<size; i++) {
x = key1[(i+1) & 0xff];
val = x;
idx = (x + idx) & 0xff;
key1[(i+1) & 0xff] = key1[idx];
key1[idx] = (x & 0xff);
val = (key1[(i+1)&0xff] + x) & 0xff;
val = key1[val];
outpg[i] = val ^ inpg[i];
}
}
static uint16_t crc(uint8_t *buf, int size)
{
uint16_t result = 65535;
for(; size; buf++, size--)
{
for(int bit = 128; bit; bit >>= 1)
{
if(result & 0x80)
result = (2 * result) ^ 0x1021;
else
result *= 2;
if(*buf & bit)
result ^= 0x1021;
}
}
return result;
}
/* scramble mode */
enum {
NO_ENC,
CONTINOUS_ENC, /* scramble whole block at once */
PAGE_ENC /* nand bootloader is scrambled in 0x200 chunks */
};
static void save_blob(const struct rknano_blob_t *b, void *buf, uint32_t size,
char *name, int suffix, int enc_mode)
{
if(g_out_prefix == NULL || b->size == 0 || b->offset + b->size > size)
return;
char *path = malloc(strlen(g_out_prefix) + strlen(name) + 32);
if(suffix >= 0)
sprintf(path, "%s%s%d.bin", g_out_prefix, name, suffix);
else
sprintf(path, "%s%s.bin", g_out_prefix, name);
FILE *f = fopen(path, "wb");
uint8_t *ptr = buf + b->offset;
if(enc_mode != NO_ENC)
{
ptr = malloc(b->size);
int len = b->size;
uint8_t *buff_ptr = buf + b->offset;
uint8_t *out_ptr = ptr;
if(enc_mode == PAGE_ENC)
{
while(len >= 0x200)
{
encode_page(buff_ptr, out_ptr, 0x200);
buff_ptr += 0x200;
out_ptr += 0x200;
len -= 0x200;
}
}
encode_page(buff_ptr, out_ptr, len);
}
if(f)
{
fwrite(ptr, b->size, 1, f);
fclose(f);
}
if(enc_mode != NO_ENC)
free(ptr);
}
static void print_blob_interval(const struct rknano_blob_t *b)
{
cprintf(YELLOW, "%#x -> %#x", b->offset, b->offset + b->size);
}
static int do_nanofw_image(uint8_t *buf, unsigned long size)
{
if(size < sizeof(struct rknano_header_t))
return 1;
struct rknano_header_t *hdr = (void *)buf;
if(size < hdr->size)
return 1;
if(strncmp(hdr->magic, MAGIC_RKNANOFW, MAGIC_RKNANOFW_SIZE))
return 1;
cprintf(BLUE, "Header\n");
cprintf(GREEN, " Date: ");
cprintf(YELLOW, "%x/%x/%x\n", hdr->date.mday, hdr->date.month, hdr->date.year);
cprintf(GREEN, " Version: ");
cprintf(YELLOW, "%x.%x.%x\n", hdr->version.major, hdr->version.minor, hdr->version.rev);
cprintf(GREEN, " Vendor: ");
cprintf(YELLOW, "%s\n", hdr->vendor);
cprintf(GREEN, " Model: ");
cprintf(YELLOW, "%s\n", hdr->model);
cprintf(GREEN, " Pad: ");
for(int i = 0; i < 6; i++)
cprintf(YELLOW, " %02x", hdr->unk6[i]);
cprintf(YELLOW, "\n");
cprintf(BLUE, "Stages\n");
for(unsigned i = 0; i < hdr->nr_stages; i++)
{
cprintf(GREEN, " %i: ", i);
print_blob_interval(&hdr->stage[i]);
cprintf(OFF, "\n");
save_blob(&hdr->stage[i], buf, size, "stage", i, NO_ENC);
}
cprintf(BLUE, "Fonts\n");
for(unsigned i = 0; i < hdr->nr_fonts; i++)
{
cprintf(GREEN, " %i: ", i);
print_blob_interval(&hdr->font[i]);
cprintf(OFF, "\n");
save_blob(&hdr->font[i], buf, size, "font", i, NO_ENC);
}
cprintf(BLUE, "GBK\n");
for(unsigned i = 0; i < hdr->nr_gbk; i++)
{
cprintf(GREEN, " %i: ", i);
print_blob_interval(&hdr->gbk[i]);
cprintf(OFF, "\n");
save_blob(&hdr->gbk[i], buf, size, "gbk", i, NO_ENC);
}
cprintf(BLUE, "String Tables\n");
for(unsigned i = 0; i < hdr->nr_strtbl; i++)
{
cprintf(GREEN, " %i: ", i);
print_blob_interval(&hdr->strtbl[i]);
cprintf(OFF, "\n");
save_blob(&hdr->strtbl[i], buf, size, "strtbl", i, NO_ENC);
}
cprintf(BLUE, "Image Resources\n");
for(unsigned i = 0; i < hdr->nr_imageres; i++)
{
cprintf(GREEN, " %i: ", i);
print_blob_interval(&hdr->imageres[i]);
cprintf(OFF, "\n");
save_blob(&hdr->imageres[i], buf, size, "imgres", i, NO_ENC);
}
cprintf(BLUE, "Unknown\n");
for(unsigned i = 0; i < hdr->nr_unk; i++)
{
cprintf(GREEN, " %i: ", i);
print_blob_interval(&hdr->unk[i]);
cprintf(OFF, "\n");
save_blob(&hdr->unk[i], buf, size, "unk", i, NO_ENC);
}
cprintf(BLUE, "Other\n");
cprintf(GREEN, " Size: ");
cprintf(YELLOW, "%#x\n", hdr->size);
cprintf(GREEN, " Magic: ");
cprintf(YELLOW, "%." STR(MAGIC_RKNANOFW_SIZE) "s ", hdr->magic);
if(strncmp(hdr->magic, MAGIC_RKNANOFW, MAGIC_RKNANOFW_SIZE) == 0)
cprintf(RED, "OK\n");
else
cprintf(RED, "Mismatch\n");
return 0;
}
struct rknano_stage_header_t
{
uint32_t addr;
uint32_t count;
} __attribute__((packed));
/*
* The [code_pa,code_pa+code_sz[ and [data_pa,data_pa+data_sz[ ranges
* are consistent: they never overlap and have no gaps and fill the
* entire space. Furthermore they match the code sequences so it's
* reasonable to assume these fields are correct.
* The other fields are still quite unsure. */
struct rknano_stage_section_t
{
uint32_t code_pa;
uint32_t code_va;
uint32_t code_sz;
uint32_t data_pa;
uint32_t data_va;
uint32_t data_sz;
uint32_t bss_va;
uint32_t bss_sz;
} __attribute__((packed));
static void elf_printf(void *user, bool error, const char *fmt, ...)
{
if(!g_debug && !error)
return;
(void) user;
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
}
static void elf_write(void *user, uint32_t addr, const void *buf, size_t count)
{
FILE *f = user;
fseek(f, addr, SEEK_SET);
fwrite(buf, count, 1, f);
}
static void extract_elf_section(struct elf_params_t *elf, int count)
{
if(g_out_prefix == NULL)
return;
char *filename = xmalloc(strlen(g_out_prefix) + 32);
sprintf(filename, "%s%d.elf", g_out_prefix, count);
if(g_debug)
printf("Write entry %d to %s\n", count, filename);
FILE *fd = fopen(filename, "wb");
free(filename);
if(fd == NULL)
return ;
elf_write_file(elf, elf_write, elf_printf, fd);
fclose(fd);
}
static int do_nanostage_image(uint8_t *buf, unsigned long size)
{
if(size < sizeof(struct rknano_stage_section_t))
return 1;
struct rknano_stage_header_t *hdr = (void *)buf;
cprintf(BLUE, "Header\n");
cprintf(GREEN, " Base Address: ");
cprintf(YELLOW, "%#08x\n", hdr->addr);
cprintf(GREEN, " Load count: ");
cprintf(YELLOW, "%d\n", hdr->count);
struct rknano_stage_section_t *sec = (void *)(hdr + 1);
for(unsigned i = 0; i < hdr->count; i++, sec++)
{
cprintf(BLUE, "Section %d\n", i);
cprintf(GREEN, " Code: ");
cprintf(YELLOW, "0x%08x", sec->code_pa);
cprintf(RED, "-(txt)-");
cprintf(YELLOW, "0x%08x", sec->code_pa + sec->code_sz);
cprintf(BLUE, " |--> ");
cprintf(YELLOW, "0x%08x", sec->code_va);
cprintf(RED, "-(txt)-");
cprintf(YELLOW, "0x%08x\n", sec->code_va + sec->code_sz);
cprintf(GREEN, " Data: ");
cprintf(YELLOW, "0x%08x", sec->data_pa);
cprintf(RED, "-(dat)-");
cprintf(YELLOW, "0x%08x", sec->data_pa + sec->data_sz);
cprintf(BLUE, " |--> ");
cprintf(YELLOW, "0x%08x", sec->data_va);
cprintf(RED, "-(dat)-");
cprintf(YELLOW, "0x%08x\n", sec->data_va + sec->data_sz);
cprintf(GREEN, " Data: ");
cprintf(RED, " ");
cprintf(BLUE, " |--> ");
cprintf(YELLOW, "0x%08x", sec->bss_va);
cprintf(RED, "-(bss)-");
cprintf(YELLOW, "0x%08x\n", sec->bss_va + sec->bss_sz);
#if 0
struct rknano_blob_t blob;
blob.offset = sec->code_pa - hdr->addr;
blob.size = sec->code_sz;
save_blob(&blob, buf, size, "entry.", i, NO_ENC);
#else
struct elf_params_t elf;
elf_init(&elf);
elf_add_load_section(&elf, sec->code_va, sec->code_sz, buf + sec->code_pa - hdr->addr);
elf_add_load_section(&elf, sec->data_va, sec->data_sz, buf + sec->data_pa - hdr->addr);
elf_add_fill_section(&elf, sec->bss_va, sec->bss_sz, 0);
extract_elf_section(&elf, i);
elf_release(&elf);
#endif
}
return 0;
}
#define MAGIC_BOOT "BOOT"
#define MAGIC_BOOT_SIZE 4
struct rknano_boot_desc_t
{
uint8_t count;
uint32_t offset;
uint8_t stride;
} __attribute__((packed));
struct rknano_boot_header_t
{
char magic[MAGIC_BOOT_SIZE];
uint16_t hdr_size;
uint32_t version;
uint32_t unk;
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
uint32_t chip;
struct rknano_boot_desc_t desc_1;
struct rknano_boot_desc_t desc_2;
struct rknano_boot_desc_t desc_4;
uint8_t field_2B[9];
uint32_t field_34;
} __attribute__((packed));
struct rknano_boot_entry_t
{
uint8_t entry_size; // unsure
uint32_t unk;
uint16_t name[20];
uint32_t offset;
uint32_t size;
uint32_t sthg2;
} __attribute__((packed));
uint32_t boot_crc_table[256] =
{
0x00000000, 0x04C10DB7, 0x09821B6E, 0x0D4316D9,
0x130436DC, 0x17C53B6B, 0x1A862DB2, 0x1E472005,
0x26086DB8, 0x22C9600F, 0x2F8A76D6, 0x2B4B7B61,
0x350C5B64, 0x31CD56D3, 0x3C8E400A, 0x384F4DBD,
0x4C10DB70, 0x48D1D6C7, 0x4592C01E, 0x4153CDA9,
0x5F14EDAC, 0x5BD5E01B, 0x5696F6C2, 0x5257FB75,
0x6A18B6C8, 0x6ED9BB7F, 0x639AADA6, 0x675BA011,
0x791C8014, 0x7DDD8DA3, 0x709E9B7A, 0x745F96CD,
0x9821B6E0, 0x9CE0BB57, 0x91A3AD8E, 0x9562A039,
0x8B25803C, 0x8FE48D8B, 0x82A79B52, 0x866696E5,
0xBE29DB58, 0xBAE8D6EF, 0xB7ABC036, 0xB36ACD81,
0xAD2DED84, 0xA9ECE033, 0xA4AFF6EA, 0xA06EFB5D,
0xD4316D90, 0xD0F06027, 0xDDB376FE, 0xD9727B49,
0xC7355B4C, 0xC3F456FB, 0xCEB74022, 0xCA764D95,
0xF2390028, 0xF6F80D9F, 0xFBBB1B46, 0xFF7A16F1,
0xE13D36F4, 0xE5FC3B43, 0xE8BF2D9A, 0xEC7E202D,
0x34826077, 0x30436DC0, 0x3D007B19, 0x39C176AE,
0x278656AB, 0x23475B1C, 0x2E044DC5, 0x2AC54072,
0x128A0DCF, 0x164B0078, 0x1B0816A1, 0x1FC91B16,
0x018E3B13, 0x054F36A4, 0x080C207D, 0x0CCD2DCA,
0x7892BB07, 0x7C53B6B0, 0x7110A069, 0x75D1ADDE,
0x6B968DDB, 0x6F57806C, 0x621496B5, 0x66D59B02,
0x5E9AD6BF, 0x5A5BDB08, 0x5718CDD1, 0x53D9C066,
0x4D9EE063, 0x495FEDD4, 0x441CFB0D, 0x40DDF6BA,
0xACA3D697, 0xA862DB20, 0xA521CDF9, 0xA1E0C04E,
0xBFA7E04B, 0xBB66EDFC, 0xB625FB25, 0xB2E4F692,
0x8AABBB2F, 0x8E6AB698, 0x8329A041, 0x87E8ADF6,
0x99AF8DF3, 0x9D6E8044, 0x902D969D, 0x94EC9B2A,
0xE0B30DE7, 0xE4720050, 0xE9311689, 0xEDF01B3E,
0xF3B73B3B, 0xF776368C, 0xFA352055, 0xFEF42DE2,
0xC6BB605F, 0xC27A6DE8, 0xCF397B31, 0xCBF87686,
0xD5BF5683, 0xD17E5B34, 0xDC3D4DED, 0xD8FC405A,
0x6904C0EE, 0x6DC5CD59, 0x6086DB80, 0x6447D637,
0x7A00F632, 0x7EC1FB85, 0x7382ED5C, 0x7743E0EB,
0x4F0CAD56, 0x4BCDA0E1, 0x468EB638, 0x424FBB8F,
0x5C089B8A, 0x58C9963D, 0x558A80E4, 0x514B8D53,
0x25141B9E, 0x21D51629, 0x2C9600F0, 0x28570D47,
0x36102D42, 0x32D120F5, 0x3F92362C, 0x3B533B9B,
0x031C7626, 0x07DD7B91, 0x0A9E6D48, 0x0E5F60FF,
0x101840FA, 0x14D94D4D, 0x199A5B94, 0x1D5B5623,
0xF125760E, 0xF5E47BB9, 0xF8A76D60, 0xFC6660D7,
0xE22140D2, 0xE6E04D65, 0xEBA35BBC, 0xEF62560B,
0xD72D1BB6, 0xD3EC1601, 0xDEAF00D8, 0xDA6E0D6F,
0xC4292D6A, 0xC0E820DD, 0xCDAB3604, 0xC96A3BB3,
0xBD35AD7E, 0xB9F4A0C9, 0xB4B7B610, 0xB076BBA7,
0xAE319BA2, 0xAAF09615, 0xA7B380CC, 0xA3728D7B,
0x9B3DC0C6, 0x9FFCCD71, 0x92BFDBA8, 0x967ED61F,
0x8839F61A, 0x8CF8FBAD, 0x81BBED74, 0x857AE0C3,
0x5D86A099, 0x5947AD2E, 0x5404BBF7, 0x50C5B640,
0x4E829645, 0x4A439BF2, 0x47008D2B, 0x43C1809C,
0x7B8ECD21, 0x7F4FC096, 0x720CD64F, 0x76CDDBF8,
0x688AFBFD, 0x6C4BF64A, 0x6108E093, 0x65C9ED24,
0x11967BE9, 0x1557765E, 0x18146087, 0x1CD56D30,
0x02924D35, 0x06534082, 0x0B10565B, 0x0FD15BEC,
0x379E1651, 0x335F1BE6, 0x3E1C0D3F, 0x3ADD0088,
0x249A208D, 0x205B2D3A, 0x2D183BE3, 0x29D93654,
0xC5A71679, 0xC1661BCE, 0xCC250D17, 0xC8E400A0,
0xD6A320A5, 0xD2622D12, 0xDF213BCB, 0xDBE0367C,
0xE3AF7BC1, 0xE76E7676, 0xEA2D60AF, 0xEEEC6D18,
0xF0AB4D1D, 0xF46A40AA, 0xF9295673, 0xFDE85BC4,
0x89B7CD09, 0x8D76C0BE, 0x8035D667, 0x84F4DBD0,
0x9AB3FBD5, 0x9E72F662, 0x9331E0BB, 0x97F0ED0C,
0xAFBFA0B1, 0xAB7EAD06, 0xA63DBBDF, 0xA2FCB668,
0xBCBB966D, 0xB87A9BDA, 0xB5398D03, 0xB1F880B4,
};
static uint32_t boot_crc(uint8_t *buf, int size)
{
uint32_t crc = 0;
for(int i = 0; i < size; i++)
crc = boot_crc_table[buf[i] ^ (crc >> 24)] ^ (crc << 8);
return crc;
}
wchar_t *from_uni16(uint16_t *str)
{
static wchar_t buffer[64];
int i = 0;
while(str[i])
{
buffer[i] = str[i];
i++;
}
return buffer;
}
static int do_boot_desc(uint8_t *buf, unsigned long size,
struct rknano_boot_desc_t *desc, int desc_idx)
{
(void) buf;
(void) size;
cprintf(BLUE, "Desc %d\n", desc_idx);
cprintf(GREEN, " Count: "); cprintf(YELLOW, "%d\n", desc->count);
cprintf(GREEN, " Offset: "); cprintf(YELLOW, "%#x\n", desc->offset);
cprintf(GREEN, " Stride: "); cprintf(YELLOW, "%#x ", desc->stride);
if(desc->stride < sizeof(struct rknano_boot_entry_t))
cprintf(RED, "(too small <%#lx)\n", sizeof(struct rknano_boot_entry_t));
else
cprintf(RED, "(OK >=%#lx)\n", sizeof(struct rknano_boot_entry_t));
for(int i = 0; i < desc->count; i++)
{
struct rknano_boot_entry_t *entry = (void *)(buf + desc->offset + i * desc->stride);
cprintf(BLUE, " Entry %d\n", i);
cprintf(GREEN, " Entry size: "); cprintf(YELLOW, "%#x ", entry->entry_size);
if(desc->stride < sizeof(struct rknano_boot_entry_t))
cprintf(RED, "(too small <%#lx)\n", sizeof(struct rknano_boot_entry_t));
else
cprintf(RED, "(OK >=%#lx)\n", sizeof(struct rknano_boot_entry_t));
cprintf(GREEN, " Unk: "); cprintf(YELLOW, "%#x\n", entry->unk);
cprintf(GREEN, " Name: "); cprintf(YELLOW, "%S\n", from_uni16(entry->name));
cprintf(GREEN, " Offset: "); cprintf(YELLOW, "%#x\n", entry->offset);
cprintf(GREEN, " Size: "); cprintf(YELLOW, "%#x\n", entry->size);
cprintf(GREEN, " Sthg 2: "); cprintf(YELLOW, "%#x\n", entry->sthg2);
struct rknano_blob_t blob;
blob.offset = entry->offset;
blob.size = entry->size;
char name[128];
sprintf(name, "%d.%S", desc_idx, from_uni16(entry->name));
save_blob(&blob, buf, size, name, -1, PAGE_ENC);
}
return 0;
}
static int do_boot_image(uint8_t *buf, unsigned long size)
{
if(size < sizeof(struct rknano_boot_header_t))
return 1;
struct rknano_boot_header_t *hdr = (void *)buf;
if(strncmp(hdr->magic, MAGIC_BOOT, MAGIC_BOOT_SIZE))
return 1;
cprintf(BLUE, "Header\n");
cprintf(GREEN, " Magic: ");
cprintf(YELLOW, "%." STR(MAGIC_BOOT_SIZE) "s ", hdr->magic);
if(strncmp(hdr->magic, MAGIC_BOOT, MAGIC_BOOT_SIZE) == 0)
cprintf(RED, "OK\n");
else
cprintf(RED, "Mismatch\n");
cprintf(GREEN, " Header Size: ");
cprintf(YELLOW, "%#x ", hdr->hdr_size);
if(hdr->hdr_size >= sizeof(struct rknano_boot_header_t))
cprintf(RED, "OK\n");
else
cprintf(RED, "Mismatch\n");
#define print(str, name) cprintf(GREEN, " "str": ");cprintf(YELLOW, "%#x\n", (unsigned)hdr->name)
#define print_arr(str, name, sz) \
cprintf(GREEN, " "str":");for(int i = 0; i < sz; i++)cprintf(YELLOW, " %#x", (unsigned)hdr->name[i]);printf("\n")
cprintf(GREEN, " Version: ");
cprintf(YELLOW, "%x.%x.%x\n", (hdr->version >> 24) & 0xff,
(hdr->version >> 16) & 0xff, hdr->version & 0xffff);
cprintf(GREEN, " Date: ");
cprintf(YELLOW, "%d/%d/%d %02d:%02d:%02d\n", hdr->day, hdr->month, hdr->year,
hdr->hour, hdr->minute, hdr->second);
cprintf(GREEN, " Chip: ");
cprintf(YELLOW, "%#x\n", hdr->chip);
print_arr("field_2A", field_2B, 9);
print("field_34", field_34);
do_boot_desc(buf, size, &hdr->desc_1, 1);
do_boot_desc(buf, size, &hdr->desc_2, 2);
do_boot_desc(buf, size, &hdr->desc_4, 4);
cprintf(BLUE, "Variable Header:\n");
cprintf(GREEN, " Value: ");
cprintf(YELLOW, "%#lx\n", *(unsigned long *)((uint8_t *)hdr + hdr->field_34 - 10));
/* The last 4 bytes are a 32-bit CRC */
cprintf(BLUE, "Post Header:\n");
cprintf(GREEN, " CRC: ");
uint32_t crc = *(uint32_t *)(buf + size - 4);
uint32_t ccrc = boot_crc(buf, size - 4);
cprintf(YELLOW, "%08x ", crc);
if(crc == ccrc)
cprintf(RED, "OK\n");
else
cprintf(RED, "Mismatch\n");
return 0;
}
typedef struct rknano_blob_t rkfw_blob_t;
#define MAGIC_RKFW "RKFW"
#define MAGIC_RKFW_SIZE 4
struct rkfw_header_t
{
char magic[MAGIC_RKFW_SIZE];
uint16_t hdr_size; // UNSURE
uint32_t version;
uint32_t code;
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
uint32_t chip;
rkfw_blob_t loader;
rkfw_blob_t update;
uint8_t pad[61];
} __attribute__((packed));
static int do_rkfw_image(uint8_t *buf, unsigned long size)
{
if(size < sizeof(struct rkfw_header_t))
return 1;
struct rkfw_header_t *hdr = (void *)buf;
if(strncmp(hdr->magic, MAGIC_RKFW, MAGIC_RKFW_SIZE))
return 1;
cprintf(BLUE, "Header\n");
cprintf(GREEN, " Magic: ");
cprintf(YELLOW, "%." STR(MAGIC_RKFW_SIZE) "s ", hdr->magic);
if(strncmp(hdr->magic, MAGIC_RKFW, MAGIC_RKFW_SIZE) == 0)
cprintf(RED, "OK\n");
else
cprintf(RED, "Mismatch\n");
cprintf(GREEN, " Header size: ");
cprintf(YELLOW, " %#x ", hdr->hdr_size);
if(hdr->hdr_size == sizeof(struct rkfw_header_t))
cprintf(RED, "OK\n");
else
cprintf(RED, "Mismatch\n");
cprintf(GREEN, " Version: ");
cprintf(YELLOW, "%x.%x.%x\n", (hdr->version >> 24) & 0xff,
(hdr->version >> 16) & 0xff, hdr->version & 0xffff);
cprintf(GREEN, " Code: ");
cprintf(YELLOW, "%#x\n", hdr->code);
cprintf(GREEN, " Date: ");
cprintf(YELLOW, "%d/%d/%d %02d:%02d:%02d\n", hdr->day, hdr->month, hdr->year,
hdr->hour, hdr->minute, hdr->second);
cprintf(GREEN, " Chip: ");
cprintf(YELLOW, "%#x\n", hdr->chip);
cprintf(GREEN, " Loader: ");
print_blob_interval(&hdr->loader);
cprintf(OFF, "\n");
save_blob(&hdr->loader, buf, size, "loader", 0, NO_ENC);
cprintf(GREEN, " Update: ");
print_blob_interval(&hdr->update);
cprintf(OFF, "\n");
save_blob(&hdr->update, buf, size, "update", 0, NO_ENC);
print_arr("pad", pad, 61);
return 0;
}
static int do_rkencode_image(uint8_t *buf, unsigned long size)
{
void *ptr = malloc(size);
int len = size;
uint8_t *buff_ptr = buf;
uint8_t *out_ptr = ptr;
int enc_mode = PAGE_ENC;
if(enc_mode == PAGE_ENC)
{
while(len >= 0x200)
{
encode_page(buff_ptr, out_ptr, 0x200);
buff_ptr += 0x200;
out_ptr += 0x200;
len -= 0x200;
}
}
encode_page(buff_ptr, out_ptr, len);
if(g_out_prefix)
{
FILE *f = fopen(g_out_prefix, "wb");
if(f)
{
fwrite(buff_ptr, 1, size, f);
fclose(f);
}
else
printf("Cannot open output file: %m\n");
}
free(ptr);
return 0;
}
static void usage(void)
{
printf("Usage: rkboottool [options] rknanoboot.bin\n");
printf("Options:\n");
printf(" --rkfw\tUnpack a rkfw file\n");
printf(" --rknanofw\tUnpack a regular RknanoFW file\n");
printf(" --rkboot\tUnpack a BOOT file\n");
printf(" --rknanostage\tUnpack a RknanoFW stage file\n");
printf(" --rkencode\tEncode a raw file\n");
printf(" -o <prefix>\tSet output prefix\n");
printf("The default is to try to guess the format.\n");
printf("If several formats are specified, all are tried.\n");
exit(1);
}
int main(int argc, char **argv)
{
bool try_nanofw = false;
bool try_rkfw = false;
bool try_boot = false;
bool try_nanostage = false;
bool try_rkencode = false;
while(1)
{
static struct option long_options[] =
{
{"help", no_argument, 0, '?'},
{"debug", no_argument, 0, 'd'},
{"rkfw", no_argument, 0, '9'},
{"rknanofw", no_argument, 0, 'n'},
{"rknanostage", no_argument, 0, 's'},
{"rkencode", no_argument, 0, 'e'},
{"rkboot", no_argument, 0, 'b'},
{"no-color", no_argument, 0, 'c'},
{0, 0, 0, 0}
};
int c = getopt_long(argc, argv, "?d9nscbeo:", long_options, NULL);
if(c == -1)
break;
switch(c)
{
case -1:
break;
case 'c':
enable_color(false);
break;
case 'b':
try_boot = true;
break;
case 'n':
try_nanofw = true;
break;
case 'd':
g_debug = true;
break;
case '?':
usage();
break;
case 'o':
g_out_prefix = optarg;
break;
case '9':
try_rkfw = true;
break;
case 's':
try_nanostage = true;
break;
case 'e':
try_rkencode = true;
break;
default:
printf("Invalid argument '%c'\n", c);
abort();
}
}
if(argc - optind != 1)
{
usage();
return 1;
}
if(!try_nanostage && !try_rkfw && !try_nanofw && !try_boot && !try_rkencode)
try_nanostage = try_rkfw = try_nanofw = try_boot = true;
FILE *fin = fopen(argv[optind], "r");
if(fin == NULL)
{
perror("Cannot open boot file");
return 1;
}
fseek(fin, 0, SEEK_END);
long size = ftell(fin);
fseek(fin, 0, SEEK_SET);
void *buf = malloc(size);
if(buf == NULL)
{
perror("Cannot allocate memory");
return 1;
}
if(fread(buf, size, 1, fin) != 1)
{
perror("Cannot read file");
return 1;
}
fclose(fin);
if(try_nanofw && !do_nanofw_image(buf, size))
goto Lsuccess;
if(try_rkfw && !do_rkfw_image(buf, size))
goto Lsuccess;
if(try_boot && !do_boot_image(buf, size))
goto Lsuccess;
if(try_nanostage && !do_nanostage_image(buf, size))
goto Lsuccess;
if(try_rkencode && !do_rkencode_image(buf, size))
goto Lsuccess;
cprintf(GREY, "No valid format found!\n");
Lsuccess:
free(buf);
return 0;
}