2012-11-03 01:14:04 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* __________ __ ___.
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
* \/ \/ \/ \/ \/
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* Copyright (C) 2012 Amaury Pouly
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
|
|
* KIND, either express or implied.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <getopt.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include "misc.h"
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <openssl/md5.h>
|
|
|
|
#include "nvp.h"
|
|
|
|
|
|
|
|
bool g_debug = false;
|
2012-11-03 13:04:27 +00:00
|
|
|
static char *g_out_prefix = NULL;
|
|
|
|
static FILE *g_in_file = NULL;
|
2012-11-03 01:14:04 +00:00
|
|
|
bool g_force = false;
|
2012-11-03 13:04:27 +00:00
|
|
|
static int g_nvp_node = -1;
|
2012-11-03 01:14:04 +00:00
|
|
|
|
|
|
|
#define let_the_force_flow(x) do { if(!g_force) return x; } while(0)
|
|
|
|
#define continue_the_force(x) if(x) let_the_force_flow(x)
|
|
|
|
|
|
|
|
#define check_field(v_exp, v_have, str_ok, str_bad) \
|
|
|
|
if((v_exp) != (v_have)) \
|
|
|
|
{ cprintf(RED, str_bad); let_the_force_flow(__LINE__); } \
|
|
|
|
else { cprintf(RED, str_ok); }
|
|
|
|
|
|
|
|
#define errorf(...) do { cprintf(GREY, __VA_ARGS__); return __LINE__; } while(0)
|
|
|
|
|
|
|
|
static void print_hex(void *p, int size, int unit)
|
|
|
|
{
|
|
|
|
uint8_t *p8 = p;
|
|
|
|
uint16_t *p16 = p;
|
|
|
|
uint32_t *p32 = p;
|
|
|
|
for(int i = 0; i < size; i += unit, p8++, p16++, p32++)
|
|
|
|
{
|
|
|
|
if(i != 0 && (i % 16) == 0)
|
|
|
|
printf("\n");
|
|
|
|
if(unit == 1)
|
|
|
|
printf(" %02x", *p8);
|
|
|
|
else if(unit == 2)
|
|
|
|
printf(" %04x", *p16);
|
|
|
|
else
|
|
|
|
printf(" %08x", *p32);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define SECTOR 512u
|
|
|
|
#define EMMC_MINIBOOT_START 0
|
|
|
|
#define EMMC_MINIBOOT_SIZE (8 * SECTOR)
|
|
|
|
#define EMMC_UBOOT_START (8 * SECTOR)
|
|
|
|
#define EMMC_FU_LINUX_START (512 * SECTOR)
|
|
|
|
#define EMMC_LINUX_START (66048 * SECTOR)
|
|
|
|
#define EMMC_NVP_START ((512 + 32768) * SECTOR)
|
|
|
|
#define EMMC_NVP_SIZE (30720 * SECTOR)
|
|
|
|
|
|
|
|
#define print_entry(begin, end, ...) \
|
|
|
|
do{ cprintf(YELLOW, " %08x %08x ", begin, end); cprintf(GREEN, __VA_ARGS__); } while(0)
|
|
|
|
|
|
|
|
static int read(uint32_t offset, uint32_t size, void *buf)
|
|
|
|
{
|
|
|
|
if(fseek(g_in_file, offset, SEEK_SET))
|
|
|
|
errorf("Cannot seek in file: %m\n");
|
|
|
|
if(fread(buf, size, 1, g_in_file) != 1)
|
|
|
|
errorf("Cannot read in file: %m\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nvp_read(uint32_t offset, uint32_t size, void *buf)
|
|
|
|
{
|
|
|
|
if(offset + size > EMMC_NVP_SIZE)
|
|
|
|
errorf("nvp read out of nvp area\n");
|
|
|
|
return read(offset + EMMC_NVP_START, size, buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns size or 0
|
|
|
|
static uint32_t do_image(uint32_t start, const char *name)
|
|
|
|
{
|
|
|
|
uint32_t size;
|
|
|
|
int ret = read(start, sizeof(size), &size);
|
|
|
|
if(ret) return ret;
|
|
|
|
/* actual uboot size contains 4 bytes for the size, 4 for the crc pad and
|
|
|
|
* must be ronded to the next sector */
|
|
|
|
size = ROUND_UP(size + 8, SECTOR);
|
|
|
|
|
|
|
|
print_entry(start, start + size, name);
|
|
|
|
|
|
|
|
/* Check U-Boot crc (must be 0) */
|
|
|
|
uint32_t crc_buffer[SECTOR / 4];
|
|
|
|
uint32_t crc = 0;
|
|
|
|
uint32_t pos = start + 4;
|
|
|
|
uint32_t rem_size = size - 4;
|
|
|
|
while(rem_size)
|
|
|
|
{
|
|
|
|
ret = read(pos, SECTOR, crc_buffer);
|
|
|
|
if(ret) return ret;
|
|
|
|
uint32_t sz = MIN(rem_size, SECTOR);
|
|
|
|
for(unsigned i = 0; i < sz / 4; i++)
|
|
|
|
crc ^= crc_buffer[i];
|
|
|
|
pos += sz;
|
|
|
|
rem_size -= sz;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(crc == 0)
|
|
|
|
{
|
|
|
|
cprintf(RED, " (CRC Ok)\n");
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cprintf(RED, " (CRC Mismatch)\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int do_emmc(void)
|
|
|
|
{
|
|
|
|
cprintf(BLUE, "eMMC map\n");
|
|
|
|
cprintf(RED, " begin end comment\n");
|
|
|
|
|
|
|
|
print_entry(EMMC_MINIBOOT_START, EMMC_MINIBOOT_START + EMMC_MINIBOOT_SIZE, "eMMC Mini Boot\n");
|
|
|
|
|
|
|
|
uint32_t uboot_size = do_image(EMMC_UBOOT_START, "U-Boot");
|
|
|
|
if(!uboot_size)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
uint32_t fulinux_start = EMMC_UBOOT_START + uboot_size;
|
|
|
|
uint32_t fulinux_size = do_image(fulinux_start, "FU Linux");
|
|
|
|
if(!fulinux_size)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
uint32_t fu_initrd_size = do_image(EMMC_FU_LINUX_START, "FU initrd");
|
|
|
|
if(!fu_initrd_size)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
print_entry(EMMC_NVP_START, EMMC_NVP_START + EMMC_NVP_SIZE, "NVP\n");
|
|
|
|
|
|
|
|
uint32_t linux_size = do_image(EMMC_LINUX_START, "Linux");
|
|
|
|
if(!linux_size)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
int ret = nvp_info();
|
|
|
|
continue_the_force(ret);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-03 13:04:27 +00:00
|
|
|
static int do_nvp_extract(void)
|
|
|
|
{
|
|
|
|
if(!g_out_prefix)
|
|
|
|
{
|
|
|
|
cprintf(GREY, "You must specify an output prefix to extract a NVP node\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if(!nvp_is_valid_node(g_nvp_node))
|
|
|
|
{
|
|
|
|
cprintf(GREY, "Invalid NVP node %d\n", g_nvp_node);
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
FILE *f = fopen(g_out_prefix, "wb");
|
|
|
|
if(!f)
|
|
|
|
{
|
|
|
|
cprintf(GREY, "Cannot open output file: %m\n");
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
int size = nvp_get_node_size(g_nvp_node);
|
|
|
|
void *buffer = malloc(size);
|
|
|
|
int ret = nvp_read_node(g_nvp_node, 0, buffer, size);
|
|
|
|
if(ret < 0)
|
|
|
|
cprintf(GREY, "NVP read error: %d\n", ret);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cprintf(YELLOW, "%d ", ret);
|
|
|
|
cprintf(GREEN, "bytes written\n");
|
|
|
|
fwrite(buffer, 1, ret, f);
|
|
|
|
}
|
|
|
|
free(buffer);
|
|
|
|
fclose(f);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-03 01:14:04 +00:00
|
|
|
static void usage(void)
|
|
|
|
{
|
|
|
|
printf("Usage: emmctool [options] img\n");
|
|
|
|
printf("Options:\n");
|
2012-11-03 13:04:27 +00:00
|
|
|
printf(" -o <prefix>\t\tSet output prefix\n");
|
|
|
|
printf(" -f/--force\t\tForce to continue on errors\n");
|
|
|
|
printf(" -?/--help\t\tDisplay this message\n");
|
|
|
|
printf(" -d/--debug\t\tDisplay debug messages\n");
|
|
|
|
printf(" -c/--no-color\t\tDisable color output\n");
|
|
|
|
printf(" -e/--nvp-ex <node>\tExtract a NVP node\n");
|
2012-11-03 01:14:04 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
while(1)
|
|
|
|
{
|
|
|
|
static struct option long_options[] =
|
|
|
|
{
|
|
|
|
{"help", no_argument, 0, '?'},
|
|
|
|
{"debug", no_argument, 0, 'd'},
|
|
|
|
{"no-color", no_argument, 0, 'c'},
|
|
|
|
{"force", no_argument, 0, 'f'},
|
2012-11-03 13:04:27 +00:00
|
|
|
{"nvp-ex", required_argument, 0, 'e'},
|
2012-11-03 01:14:04 +00:00
|
|
|
{0, 0, 0, 0}
|
|
|
|
};
|
|
|
|
|
2012-11-03 13:04:27 +00:00
|
|
|
int c = getopt_long(argc, argv, "?dcfo:e:", long_options, NULL);
|
2012-11-03 01:14:04 +00:00
|
|
|
if(c == -1)
|
|
|
|
break;
|
|
|
|
switch(c)
|
|
|
|
{
|
|
|
|
case -1:
|
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
enable_color(false);
|
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
g_debug = true;
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
g_force = true;
|
|
|
|
break;
|
|
|
|
case '?':
|
|
|
|
usage();
|
|
|
|
break;
|
|
|
|
case 'o':
|
|
|
|
g_out_prefix = optarg;
|
|
|
|
break;
|
2012-11-03 13:04:27 +00:00
|
|
|
case 'e':
|
|
|
|
g_nvp_node = strtoul(optarg, NULL, 0);
|
|
|
|
break;
|
2012-11-03 01:14:04 +00:00
|
|
|
default:
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(argc - optind != 1)
|
|
|
|
{
|
|
|
|
usage();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_in_file = fopen(argv[optind], "rb");
|
|
|
|
if(g_in_file == NULL)
|
|
|
|
{
|
|
|
|
perror("Cannot open boot file");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ret = nvp_init(EMMC_NVP_SIZE, &nvp_read, g_debug);
|
|
|
|
if(ret) return ret;
|
|
|
|
ret = do_emmc();
|
2012-11-03 13:04:27 +00:00
|
|
|
if(ret == 0 && g_nvp_node >= 0)
|
|
|
|
ret = do_nvp_extract();
|
2012-11-03 01:14:04 +00:00
|
|
|
|
|
|
|
fclose(g_in_file);
|
|
|
|
|
|
|
|
color(OFF);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|