From 8c1a9f508277ee3c3f36665ff9f60bc091529891 Mon Sep 17 00:00:00 2001 From: Amaury Pouly Date: Sat, 3 Nov 2012 02:14:04 +0100 Subject: [PATCH] Introduce emmctools for the sony nwz players. This tool is specific to the em1/mp200 sony based players. In deals with raw emmc images (which is possible but hard to get). This tool is also useful as a documentation of the underlying emmc format used for a future port. Change-Id: I66c9b0e47351e5d89f6a404aa62038e00fdc1093 --- utils/nwztools/emmctools/Makefile | 20 ++ utils/nwztools/emmctools/emmctool.c | 242 ++++++++++++++++ utils/nwztools/emmctools/misc.c | 53 ++++ utils/nwztools/emmctools/misc.h | 50 ++++ utils/nwztools/emmctools/nvp.c | 546 ++++++++++++++++++++++++++++++++++++ utils/nwztools/emmctools/nvp.h | 93 ++++++ 6 files changed, 1004 insertions(+) create mode 100644 utils/nwztools/emmctools/Makefile create mode 100644 utils/nwztools/emmctools/emmctool.c create mode 100644 utils/nwztools/emmctools/misc.c create mode 100644 utils/nwztools/emmctools/misc.h create mode 100644 utils/nwztools/emmctools/nvp.c create mode 100644 utils/nwztools/emmctools/nvp.h diff --git a/utils/nwztools/emmctools/Makefile b/utils/nwztools/emmctools/Makefile new file mode 100644 index 0000000000..953757fdab --- /dev/null +++ b/utils/nwztools/emmctools/Makefile @@ -0,0 +1,20 @@ +DEFINES= +CC=gcc +LD=gcc +CFLAGS=-g -std=c99 -W -Wall $(DEFINES) +LDFLAGS= +BINS=emmctool + +all: $(BINS) + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + +emmctool: emmctool.o misc.o nvp.o + $(LD) -o $@ $^ $(LDFLAGS) + +clean: + rm -fr *.o + +veryclean: + rm -rf $(BINS) diff --git a/utils/nwztools/emmctools/emmctool.c b/utils/nwztools/emmctools/emmctool.c new file mode 100644 index 0000000000..8fa7b0907b --- /dev/null +++ b/utils/nwztools/emmctools/emmctool.c @@ -0,0 +1,242 @@ +/*************************************************************************** + * __________ __ ___. + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "misc.h" +#include +#include +#include "nvp.h" + +bool g_debug = false; +char *g_out_prefix = NULL; +FILE *g_in_file = NULL; +bool g_force = false; + +#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; +} + +static void usage(void) +{ + printf("Usage: emmctool [options] img\n"); + printf("Options:\n"); + printf(" -o \tSet output prefix\n"); + printf(" -f/--force\tForce to continue on errors\n"); + printf(" -?/--help\tDisplay this message\n"); + printf(" -d/--debug\tDisplay debug messages\n"); + printf(" -c/--no-color\tDisable color output\n"); + 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'}, + {0, 0, 0, 0} + }; + + int c = getopt_long(argc, argv, "?dcfo:", long_options, NULL); + 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; + 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(); + + fclose(g_in_file); + + color(OFF); + + return ret; +} + diff --git a/utils/nwztools/emmctools/misc.c b/utils/nwztools/emmctools/misc.c new file mode 100644 index 0000000000..108235e7fd --- /dev/null +++ b/utils/nwztools/emmctools/misc.c @@ -0,0 +1,53 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 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 +#include +#include +#include +#include "misc.h" + +char OFF[] = { 0x1b, 0x5b, 0x31, 0x3b, '0', '0', 0x6d, '\0' }; + +char GREY[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '0', 0x6d, '\0' }; +char RED[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '1', 0x6d, '\0' }; +char GREEN[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '2', 0x6d, '\0' }; +char YELLOW[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '3', 0x6d, '\0' }; +char BLUE[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '4', 0x6d, '\0' }; + +static bool g_color_enable = true; + +void *xmalloc(size_t s) +{ + void * r = malloc(s); + if(!r) bugp("malloc"); + return r; +} + +void enable_color(bool enable) +{ + g_color_enable = enable; +} + +void color(color_t c) +{ + if(g_color_enable) + printf("%s", (char *)c); +} diff --git a/utils/nwztools/emmctools/misc.h b/utils/nwztools/emmctools/misc.h new file mode 100644 index 0000000000..035b0ef8c1 --- /dev/null +++ b/utils/nwztools/emmctools/misc.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 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. + * + ****************************************************************************/ +#ifndef __MISC_H__ +#define __MISC_H__ + +#include +#include + +#define _STR(a) #a +#define STR(a) _STR(a) + +#define bug(...) do { fprintf(stderr,"["__FILE__":"STR(__LINE__)"]ERROR: "__VA_ARGS__); exit(1); } while(0) +#define bugp(...) do { fprintf(stderr, __VA_ARGS__); perror(" "); exit(1); } while(0) + +#define ROUND_UP(val, round) ((((val) + (round) - 1) / (round)) * (round)) + +typedef char color_t[]; + +extern color_t OFF, GREY, RED, GREEN, YELLOW, BLUE; +void *xmalloc(size_t s); +void color(color_t c); +void enable_color(bool enable); + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#define cprintf(col, ...) do {color(col); printf(__VA_ARGS__); }while(0) + +#define cprintf_field(str1, ...) do{ cprintf(GREEN, str1); cprintf(YELLOW, __VA_ARGS__); }while(0) + +#endif /* __MISC_H__ */ diff --git a/utils/nwztools/emmctools/nvp.c b/utils/nwztools/emmctools/nvp.c new file mode 100644 index 0000000000..46515f74db --- /dev/null +++ b/utils/nwztools/emmctools/nvp.c @@ -0,0 +1,546 @@ +/*************************************************************************** + * __________ __ ___. + * 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 "nvp.h" +#include +#include +#include + +static struct nvp_zone_info_entry_t nvp_zone_ubt[] = +{ + {0x18, 0, 1, 4, 0, 0, 0, 0, "system information"}, + {0x17, 1, 1, 0x20, 0, 0, 0, 0, "u-boot password"}, + {9, 2, 1, 4, 0, 0, 0, 0, "firmware update flag"}, + {0xA, 3, 1, 4, 0, 0, 0, 0, "beep ok flag"}, + {0x22, 4, 1, 0x10, 0, 0, 0, 0, "rtc alarm"}, + {0x50, 5, 1, 4, 0, 0, 0, 0, "hold mode"} +}; + +static struct nvp_zone_info_entry_t nvp_zone_sys[] = +{ + {0x10, 1, 1, 0x40, 0, 0, 0, 0, "model id"}, + {4, 2, 1, 0x10, 0, 0, 0, 0, "serial number"}, + {0xB, 3, 1, 0x20, 0, 0, 0, 0, "ship information"}, + {0x44, 4, 1, 4, 0, 0, 0, 0, "color variation"}, + {0x1A, 5, 1, 5, 0, 0, 0, 0, "product code"}, + {0x1D, 6, 1, 8, 0, 0, 0, 0, "update file name"}, + {0x20, 7, 1, 0x40, 0, 0, 0, 0, "key and signature"}, + {0x11, 8, 1, 4, 0, 0, 0, 0, "test mode flag"}, + {0x12, 9, 1, 4, 0, 0, 0, 0, "getty mode flag"}, + {0x46, 0xA, 1, 4, 0, 0, 0, 0, "disable iptable flag"}, + {0x1E, 0xB, 1, 0x40, 0, 0, 0, 0, "sound driver parameter"}, + {0x1F, 0xC, 1, 0x40, 0, 0, 0, 0, "noise cancel driver parameter"}, + {0x4D, 0xD, 1, 6, 0, 0, 0, 0, "wifi mac address"}, + {0x4B, 0xE, 1, 4, 0, 0, 0, 0, "wifi protected setup"}, + {0x52, 0xF, 1, 0x10, 0, 0, 0, 0, "fm parameter"}, + {0x53, 0x10, 1, 4, 0, 0, 0, 0, "speaker ship info"}, + {0x54, 0x11, 1, 4, 0, 0, 0, 0, "mass storage class mode"}, + {0x19, 0x12, 1, 4, 0, 0, 0, 0, "exception monitor mode"}, + {0x1B, 0x13, 1, 4, 0, 0, 0, 0, "battery calibration"}, + {0x56, 0x14, 1, 0x200, 0, 0, 0, 0, "bluetooth pskey"} +}; + +static struct nvp_zone_info_entry_t nvp_zone_app[] = +{ + {5, 0, 8, 0x1000, 0, 0, 0, 0, "application parameter"}, + {7, 0x40, 1, 0x14, 0, 0, 0, 0, "secure clock"}, + {0xC, 0x41, 1, 0xA0, 0, 0, 0, 0, "aad icv"}, + {0xD, 0x42, 2, 0x208, 0, 0, 0, 0, "empr key"}, + {0x4C, 0x44, 1, 0x10, 0, 0, 0, 0, "slacker time"}, + {0x15, 0x45, 1, 4, 0, 0, 0, 0, "key mode (debug/release)"}, + {0x47, 0x46, 1, 0x40, 0, 0, 0, 0, "marlin time"}, + {0x48, 0x47, 0x20, 0x4000, 0, 0, 0, 0, "marlin crl"}, + {0x59, 0x76, 1, 0x200, 0, 0, 0, 0, "btmw factory pair info"}, + {0x58, 0x77, 1, 0x200, 0, 0, 0, 0, "btmw factory scdb"}, + {0x57, 0x78, 1, 4, 0, 0, 0, 0, "btmw log mode flag"}, + {0x55, 0x79, 1, 4, 0, 0, 0, 0, "europe vol regulation flag"}, + {8, 0x7A, 1, 8, 0, 0, 0, 0, "middleware parameter"}, + {0x16, 0x7B, 1, 4, 0, 0, 0, 0, "quick shutdown flag"}, + {0x45, 0x7C, 1, 4, 0, 0, 0, 0, "time out to sleep"}, + {0x4E, 0x7D, 1, 4, 0, 0, 0, 0, "application debug mode flag"}, + {0x4F, 0x7E, 1, 4, 0, 0, 0, 0, "browser log mode flag"} +}; + +static struct nvp_zone_info_entry_t nvp_zone_drm[] = +{ + {3, 1, 2, 0x2C0, 0, 0, 0, 0, "aad key"}, + {0x1C, 6, 1, 0x40, 0, 0, 0, 0, "wmt key"}, + {0x51, 9, 0x11, 0x2020, 0, 0, 0, 0, "slacker id file"}, + {0x49, 0x1A, 0x41, 0x8100, 0, 0, 0, 0, "marlin device key"}, + {0x21, 0x5B, 1, 0x40, 0, 0, 0, 0, "starfish id"}, + {0x23, 0x5C, 4, 0x800, 0, 0, 0, 0, "bluetooth address"} +}; + +static struct nvp_zone_info_entry_t nvp_zone_ekb[] = +{ + {0xE, 0, 0x20, 0x4000, 0, 0, 0, 0, "EKB 0"}, + {0xF, 0x20, 0x20, 0x4000, 0, 0, 0, 0, "EKB 1"}, + {0x4A, 0x40, 0x30, 0x6000, 0, 0, 0, 0, "marlin user key"} +}; + +static struct nvp_zone_info_entry_t nvp_zone_emp[] = +{ + {0x24, 0, 2, 0x400, 0, 0, 0, 0, "EMPR 0"}, + {0x25, 2, 2, 0x400, 0, 0, 0, 0, "EMPR 1"}, + {0x26, 4, 2, 0x400, 0, 0, 0, 0, "EMPR 2"}, + {0x27, 6, 2, 0x400, 0, 0, 0, 0, "EMPR 3"}, + {0x28, 8, 2, 0x400, 0, 0, 0, 0, "EMPR 4"}, + {0x29, 0xA, 2, 0x400, 0, 0, 0, 0, "EMPR 5"}, + {0x2A, 0xC, 2, 0x400, 0, 0, 0, 0, "EMPR 6"}, + {0x2B, 0xE, 2, 0x400, 0, 0, 0, 0, "EMPR 7"}, + {0x2C, 0x10, 2, 0x400, 0, 0, 0, 0, "EMPR 8"}, + {0x2D, 0x12, 2, 0x400, 0, 0, 0, 0, "EMPR 9"}, + {0x2E, 0x14, 2, 0x400, 0, 0, 0, 0, "EMPR 10"}, + {0x2F, 0x16, 2, 0x400, 0, 0, 0, 0, "EMPR 11"}, + {0x30, 0x18, 2, 0x400, 0, 0, 0, 0, "EMPR 12"}, + {0x31, 0x1A, 2, 0x400, 0, 0, 0, 0, "EMPR 13"}, + {0x32, 0x1C, 2, 0x400, 0, 0, 0, 0, "EMPR 14"}, + {0x33, 0x1E, 2, 0x400, 0, 0, 0, 0, "EMPR 15"}, + {0x34, 0x20, 2, 0x400, 0, 0, 0, 0, "EMPR 16"}, + {0x35, 0x22, 2, 0x400, 0, 0, 0, 0, "EMPR 17"}, + {0x36, 0x24, 2, 0x400, 0, 0, 0, 0, "EMPR 18"}, + {0x37, 0x26, 2, 0x400, 0, 0, 0, 0, "EMPR 19"}, + {0x38, 0x28, 2, 0x400, 0, 0, 0, 0, "EMPR 20"}, + {0x39, 0x2A, 2, 0x400, 0, 0, 0, 0, "EMPR 21"}, + {0x3A, 0x2C, 2, 0x400, 0, 0, 0, 0, "EMPR 22"}, + {0x3B, 0x2E, 2, 0x400, 0, 0, 0, 0, "EMPR 23"}, + {0x3C, 0x30, 2, 0x400, 0, 0, 0, 0, "EMPR 24"}, + {0x3D, 0x32, 2, 0x400, 0, 0, 0, 0, "EMPR 25"}, + {0x3E, 0x34, 2, 0x400, 0, 0, 0, 0, "EMPR 26"}, + {0x3F, 0x36, 2, 0x400, 0, 0, 0, 0, "EMPR 27"}, + {0x40, 0x38, 2, 0x400, 0, 0, 0, 0, "EMPR 28"}, + {0x41, 0x3A, 2, 0x400, 0, 0, 0, 0, "EMPR 29"}, + {0x42, 0x3C, 2, 0x400, 0, 0, 0, 0, "EMPR 30"}, + {0x43, 0x3E, 2, 0x400, 0, 0, 0, 0, "EMPR 31"} +}; + +static struct nvp_zone_info_entry_t nvp_zone_bti[] = +{ + {1, 0, 0x20, 0x40000, 0, 0, 0, 0, "boot image"} +}; + +static struct nvp_zone_info_entry_t nvp_zone_hdi[] = +{ + {2, 0, 0x20, 0x40000, 0, 0, 0, 0, "hold image"} +}; + +static struct nvp_zone_info_entry_t nvp_zone_lbi[] = +{ + {0x14, 0, 0x20, 0x40000, 0, 0, 0, 0, "low battery image"} +}; + +static struct nvp_zone_info_entry_t nvp_zone_upi[] = +{ + {0x13, 0, 0x20, 0x40000, 0, 0, 0, 0, "update image"} +}; + +static struct nvp_zone_info_entry_t nvp_zone_eri[] = +{ + {6, 0, 0x20, 0x40000, 0, 0, 0, 0, "update error image"} +}; + +struct nvp_area_info_entry_t nvp_area_info[NVP_NR_AREAS] = +{ + {2, nvp_zone_ubt, 6, 0, 0, 0, 0, "u-boot parameter"}, + {2, nvp_zone_sys, 0x14, 0, 0, 0, 0, "system parameter"}, + {2, nvp_zone_app, 0x11, 0, 0, 0, 0, "application parameter"}, + {2, nvp_zone_drm, 6, 0, 0, 0, 0, "drm data"}, + {2, nvp_zone_ekb, 3, 0, 0, 0, 0, "ekb data"}, + {2, nvp_zone_emp, 0x20, 0, 0, 0, 0, "empr data"}, + {2, 0, 0, 0, 0, 0, 0, "reserved"}, + {2, 0, 0, 0, 0, 0, 0, "reserved"}, + {1, nvp_zone_bti, 1, 0, 0, 0, 0, "boot image"}, + {1, nvp_zone_hdi, 1, 0, 0, 0, 0, "hold image"}, + {1, nvp_zone_lbi, 1, 0, 0, 0, 0, "low battery image"}, + {1, nvp_zone_upi, 1, 0, 0, 0, 0, "update image"}, + {1, nvp_zone_eri, 1, 0, 0, 0, 0, "update error image"}, + {1, 0, 0, 0, 0, 0, 0, "reserved"}, + {1, 0, 0, 0, 0, 0, 0, "reserved"}, + {1, 0, 0, 0, 0, 0, 0, "reserved"} +}; + +static int nr_nodes; +static struct nvp_node_info_t *node_info; +static nvp_read_fn_t nvp_read; +static int nvp_size; +static int nr_sectors; +static int nr_clusters; +static uint8_t *nvp_table; +static uint8_t *nvp_shadow; +static uint16_t *nvp_bitmap; + +int nvp_get_cluster_status(int cluster) +{ + if(cluster <= 3 || cluster >= nr_clusters) + { + cprintf(GREY, "invalid cluster number: cluster=%d\n", cluster); + return -1; + } + return nvp_bitmap[cluster]; +} + +int nvp_set_cluster_status(int cluster, int status) +{ + if(cluster <= 3 || cluster >= nr_clusters) + { + cprintf(GREY, "invalid cluster number: cluster=%d\n", cluster); + return -1; + } + nvp_bitmap[cluster] = status; + return 0; +} + +int nvp_get_sector_status(int sector) +{ + if(sector <= 3 || sector >= nr_sectors) + { + cprintf(GREY, "invalid sector number: sector=%d\n", sector); + return -1; + } + return (nvp_bitmap[sector >> 4] >> (sector & 0xf)) & 0x1; +} + +int nvp_set_sector_status(int sector, int status) +{ + if(sector <= 3 || sector >= nr_sectors) + { + cprintf(GREY, "invalid sector number: sector=%d\n", sector); + return -1; + } + if(status) + nvp_bitmap[sector >> 4] |= 1 << (sector & 0xf); + else + nvp_bitmap[sector >> 4] &= ~(1 << (sector & 0xf)); + return 0; +} + +int nvp_get_cluster_number(int shadow, int area, int zone, int index) +{ + int start = nvp_area_info[area].zone_info[zone].start; + int count = nvp_area_info[area].zone_info[zone].count; + if(index >= count) + { + cprintf(GREY, "invalid index: index=%d\n", index); + return -1; + } + uint8_t *ptr = shadow ? nvp_shadow : nvp_table; + uint16_t cluster = *(uint16_t *)&ptr[area * NVP_AREA_TABLE_SIZE + (start + index) * 2]; + if(cluster == 0) + return 0; + if(cluster <= 3 || cluster >= nr_clusters) + { + cprintf(GREY, "invalid cluster: shadow=%d area=%d zone=%d index=%d cluster=%d\n", + shadow, area, zone, index, cluster); + return -1; + } + return cluster; +} + +int nvp_get_sector_number(int shadow, int area, int zone, int index) +{ + int start = nvp_area_info[area].zone_info[zone].start; + int count = nvp_area_info[area].zone_info[zone].count; + if(index >= count) + { + cprintf(GREY, "invalid index: index=%d\n", index); + return -1; + } + uint8_t *ptr = shadow ? nvp_shadow : nvp_table; + uint32_t sector = *(uint32_t *)&ptr[area * NVP_AREA_TABLE_SIZE + (start + index) * 4]; + if(sector == 0) + return 0; + if(sector <= 0x3f || sector >= (unsigned)nr_sectors) + { + cprintf(GREY, "invalid sector: shadow=%d area=%d zone=%d index=%d sector=%d\n", + shadow, area, zone, index, sector); + return -1; + } + return sector; +} + +int nvp_read_data(int shadow, int area, int zone, int offset, void *buf, int size) +{ + int large = nvp_area_info[area].kind == NVP_AREA_LARGE_KIND; + int unit_size = large ? NVP_LARGE_AREA_SIZE : NVP_SMALL_AREA_SIZE; + + while(size > 0) + { + int index = offset / unit_size; + int unit_offset = offset % unit_size; + int sec_cluster = large ? + nvp_get_cluster_number(shadow, area, zone, index) : + nvp_get_sector_number(shadow, area, zone, index); + if(sec_cluster == 0) + return -1; + //cprintf(GREY, "[sec_cluster=%d]", sec_cluster); + int read = MIN(size, unit_size - unit_offset); + int ret = nvp_read(sec_cluster * unit_size, read, buf); + if(ret) + return ret; + buf += read; + offset += read; + size -= read; + } + return 0; +} + +struct nvp_node_info_t nvp_get_node_info(int node) +{ + return node_info[node]; +} + +int nvp_get_node_size(int node) +{ + struct nvp_node_info_t i = nvp_get_node_info(node); + return nvp_area_info[i.area].zone_info[i.zone].size; +} + +const char *nvp_get_node_name(int node) +{ + struct nvp_node_info_t i = nvp_get_node_info(node); + return nvp_area_info[i.area].zone_info[i.zone].name; +} + +const char *nvp_get_area_name(int area) +{ + return nvp_area_info[area].name; +} + +int nvp_read_node(int node, int offset, void *buffer, int size) +{ + struct nvp_node_info_t i = nvp_get_node_info(node); + return nvp_read_data(0, i.area, i.zone, offset, buffer, size); +} + +int nvp_init(int size, nvp_read_fn_t read, bool debug) +{ + nvp_read = read; + nvp_size = size; + nr_sectors = nvp_size / NVP_SECTOR_SIZE; + nr_clusters = (nr_sectors + NVP_SECTOR_PER_CLUSTER) / NVP_SECTOR_PER_CLUSTER; + // check that the tables are consistent and compute the number of nodes + if(debug) + cprintf(BLUE, "NVP Debug\n"); + for(int i = 0; i < NVP_NR_AREAS; i++) + { + if(debug) + { + cprintf(RED, " %s Area: ", nvp_area_info[i].kind == NVP_AREA_SMALL_KIND ? "Small" : "Large"); + cprintf(GREEN, "%s\n", nvp_area_info[i].name); + } + if(nvp_area_info[i].zone_info == NULL) + continue; + + struct nvp_zone_info_entry_t *zones = nvp_area_info[i].zone_info; + int nr_zones = nvp_area_info[i].nr_zones; + int kind = nvp_area_info[i].kind; + if(kind != NVP_AREA_SMALL_KIND && kind != NVP_AREA_LARGE_KIND) + continue; + + uint32_t bitmap[256]; + memset(bitmap, 0, sizeof(bitmap)); + + for(int j = 0; j < nr_zones; j++) + { + if(debug) + { + cprintf_field(" Zone ", "%s", zones[j].name); + cprintf_field(" Node ", "%d", zones[j].node); + cprintf_field(" Start ", "%#x", zones[j].start); + cprintf_field(" Count ", "%#x", zones[j].count); + cprintf_field(" Size ", "%#x\n", zones[j].size); + } + + if(kind == NVP_AREA_LARGE_KIND) + { + if(zones[j].start >= NVP_LARGE_AREA_MAX_CLUSTER || + zones[j].start + zones[j].count > NVP_LARGE_AREA_MAX_CLUSTER) + { + cprintf(GREY, "Bad zone start/count\n"); + return 95; + } + if(zones[j].size > zones[j].count * NVP_LARGE_AREA_SIZE) + { + cprintf(GREY, "Bad zone size\n"); + return 96; + } + } + else + { + if(zones[j].start >= NVP_SMALL_AREA_MAX_CLUSTER || + zones[j].start + zones[j].count > NVP_SMALL_AREA_MAX_CLUSTER) + { + cprintf(GREY, "Bad zone start/count\n"); + return 97; + } + if(zones[j].size > zones[j].count * NVP_SMALL_AREA_SIZE) + { + cprintf(GREY, "Bad zone size\n"); + return 98; + } + } + + nr_nodes++; + + for(int k = 0; k < zones[j].count; k++) + { + if(bitmap[zones[j].start + k]) + { + cprintf(GREY, "Zone overlap !\n"); + return 99; + } + bitmap[zones[j].start + k] = 0xffffffff; + } + } + } + + // build node table + nr_nodes++; // nodes start at 1 ?! + node_info = malloc(nr_nodes * sizeof(struct nvp_node_info_t)); + memset(node_info, 0xff, nr_nodes * sizeof(struct nvp_node_info_t)); + + for(int i = 0; i < NVP_NR_AREAS; i++) + { + if(nvp_area_info[i].zone_info == NULL) + continue; + + struct nvp_zone_info_entry_t *zones = nvp_area_info[i].zone_info; + int nr_zones = nvp_area_info[i].nr_zones; + int kind = nvp_area_info[i].kind; + if(kind != NVP_AREA_SMALL_KIND && kind != NVP_AREA_LARGE_KIND) + continue; + + for(int j = 0; j < nr_zones; j++) + { + int node = zones[j].node; + if(node >= nr_nodes) + { + cprintf(GREY, "Node out of bounds !\n"); + return 89; + } + if(node_info[node].area != -1 && node_info[node].zone != -1) + { + cprintf(GREY, "Node overlap: area=%d zone=%d node=%d to area=%d zone=%d !\n", + i, j, node, node_info[node].area, node_info[node].zone); + return 88; + } + node_info[node].area = i; + node_info[node].zone = j; + } + } + + // load allocation table + nvp_table = malloc(NVP_CLUSTER_SIZE); + int ret = nvp_read(NVP_TABLE_SECTOR * NVP_SECTOR_SIZE, NVP_CLUSTER_SIZE, nvp_table); + if(ret) return ret; + + // init shadow table + nvp_shadow = malloc(NVP_CLUSTER_SIZE); + memset(nvp_shadow, 0, NVP_CLUSTER_SIZE); + + // init bitmap + nvp_bitmap = malloc(sizeof(uint16_t) * nr_clusters); + memset(nvp_bitmap, 0, sizeof(uint16_t) * nr_clusters); + + // read map + for(int i = 0; i < NVP_NR_AREAS; i++) + { + if(nvp_area_info[i].zone_info == NULL) + continue; + int kind = nvp_area_info[i].kind; + if(kind != NVP_AREA_SMALL_KIND && kind != NVP_AREA_LARGE_KIND) + continue; + + if(kind == NVP_AREA_LARGE_KIND) + { + for(int cluster = 0; cluster < NVP_LARGE_AREA_MAX_CLUSTER; cluster++) + { + uint16_t entry = *(uint16_t *)&nvp_table[i * NVP_AREA_TABLE_SIZE + cluster * 2]; + if(entry == 0) + continue; + if(nvp_get_cluster_status(entry) != 0) + { + cprintf(GREY, "cluster already used: area=%d cluster=%d entry=%d\n", i, cluster, entry); + return 78; + } + nvp_set_cluster_status(entry, 0xffff); + } + } + else + { + for(int cluster = 0; cluster < NVP_SMALL_AREA_MAX_CLUSTER; cluster++) + { + uint32_t entry = *(uint32_t *)&nvp_table[i * NVP_AREA_TABLE_SIZE + cluster * 4]; + if(entry == 0) + continue; + if(nvp_get_sector_status(entry) != 0) + { + cprintf(GREY, "sector already used: area=%d cluster=%d entry=%d\n", i, cluster, entry); + return 76; + } + nvp_set_sector_status(entry, 1); + } + } + } + + return 0; +} + +int nvp_info(void) +{ + uint32_t version; + int ret = nvp_read(0, sizeof(version), &version); + if(ret) return ret; + + cprintf(BLUE, "NVP\n"); + cprintf_field(" Version: ", "%x\n", version); + + for(int i = 0; i < NVP_NR_AREAS; i++) + { + cprintf(RED, " Area: "); + cprintf(GREEN, "%s\n", nvp_area_info[i].name); + if(nvp_area_info[i].zone_info == NULL) + continue; + + struct nvp_zone_info_entry_t *zones = nvp_area_info[i].zone_info; + int nr_zones = nvp_area_info[i].nr_zones; + + for(int j = 0; j < nr_zones; j++) + { + cprintf_field(" Zone ", "%s", zones[j].name); + cprintf(BLUE, " ->"); + uint8_t buf[0x20]; + int ret = nvp_read_data(0, i, j, 0, buf, MIN(0x20, zones[j].size)); + if(ret) + { + cprintf(RED, " No data\n"); + continue; + } + for(int i = 0; i < MIN(0x20, zones[j].size); i++) + cprintf(YELLOW, " %02x", buf[i]); + cprintf(BLUE, " -> "); + for(int i = 0; i < MIN(0x20, zones[j].size); i++) + cprintf(YELLOW, "%c", isprint(buf[i]) ? buf[i] : '.'); + printf("\n"); + } + } + + return 0; +} \ No newline at end of file diff --git a/utils/nwztools/emmctools/nvp.h b/utils/nwztools/emmctools/nvp.h new file mode 100644 index 0000000000..1eff36c19e --- /dev/null +++ b/utils/nwztools/emmctools/nvp.h @@ -0,0 +1,93 @@ +/*************************************************************************** + * __________ __ ___. + * 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. + * + ****************************************************************************/ +#ifndef __NVP_H__ +#define __NVP_H__ + +#include +#include +#include "misc.h" + +#define NVP_AREA_LARGE_KIND 1 +#define NVP_AREA_SMALL_KIND 2 + +#define NVP_SMALL_AREA_MAX_CLUSTER 128 +#define NVP_LARGE_AREA_MAX_CLUSTER 256 +#define NVP_AREA_TABLE_SIZE 512 + +#define NVP_SECTOR_SIZE 512 +#define NVP_TABLE_SECTOR 16 +#define NVP_DATA_SECTOR_MIN 64 +#define NVP_DATA_SECTOR_MAX 32767 +#define NVP_SECTOR_PER_CLUSTER 16 +#define NVP_CLUSTER_SIZE (NVP_SECTOR_SIZE * NVP_SECTOR_PER_CLUSTER) + +#define NVP_LARGE_AREA_SIZE NVP_CLUSTER_SIZE +#define NVP_SMALL_AREA_SIZE NVP_SECTOR_SIZE + +struct nvp_zone_info_entry_t +{ + int node; + int start; // in 4 unit + int count; // in 512 unit for kind 2 and in 8192 for kind 1 + int size; + int res0, res1, res2, res3; + const char *name; +}; + +struct nvp_area_info_entry_t +{ + int kind; + struct nvp_zone_info_entry_t *zone_info; + int nr_zones; + int res0, res1, res2, res3; + const char *name; +}; + +struct nvp_node_info_t +{ + int area; + int zone; +}; + +#define NVP_NR_AREAS 16 + +extern struct nvp_area_info_entry_t nvp_area_info[NVP_NR_AREAS]; + +typedef int (*nvp_read_fn_t)(uint32_t offset, uint32_t size, void *buf); + +int nvp_init(int nvp_size, nvp_read_fn_t read, bool debug); +struct nvp_node_info_t nvp_get_node_info(int node); +int nvp_get_node_size(int node); +const char *nvp_get_node_name(int node); +const char *nvp_get_area_name(int node); +int nvp_read_node(int node, int offset, void *buffer, int size); + +int nvp_info(void); + +int nvp_get_cluster_status(int cluster); +int nvp_set_cluster_status(int cluster, int status); +int nvp_get_sector_status(int sector); +int nvp_set_sector_status(int sector, int status); +int nvp_get_cluster_number(int shadow, int area, int zone, int index); +int nvp_get_sector_number(int shadow, int area, int zone, int index); +int nvp_read_data(int shadow, int area, int zone, int offset, void *buffer, int size); + +#endif /* __NVP_H__ */ -- 2.11.4.GIT