/*
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * The initial header information were taken from "imagetag.c" written by
 * Axel Gembe <ago@bastart.eu.org>. It was merged with the information from
 * "recomp.c" (modified Cramfs) from
 * Heinz Peter Hippenstiel <hippenstiel<at>gmx.net>.
 *
 * Compile with: gcc -o imginfo imginfo.c
 *
 * Usage: Rename the firmware file to "firmware.bin" and start ./imginfo to
 *        see if the CRC values match (and to get some information from
 *        your image).
 *
 * Copyright (C) 2008 Sven Bachmann <dev@mcbachmann.de>
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <errno.h>

#define IMAGETAG_MAGIC1			"OpenWRT BCM963xx"
#define IMAGETAG_MAGIC2			"V1"
#define IMAGETAG_VER			"6"
#define IMAGETAG_DEFAULT_LOADADDR	0x80010000
#define IMAGETAG_CRC_START		0xFFFFFFFF
#define FLASH_CFE_SIZE			0x10000
#define FLASH_START			0xBFC00000
#define FLASH_BS			(64 * 1024)

/* Kernel header */
struct kernelhdr {
	uint32_t		loadaddr;	/* Kernel load address */
	uint32_t		entry;		/* Kernel entry point address */
	uint32_t		lzmalen;	/* Compressed length of the LZMA data that follows */
};

/* Image component */
struct imagecomp {
	uint8_t			address[12];	/* Address of this component as ASCII */
	uint8_t			len[10];	/* Length of this component as ASCII */
};

/* Image tag */
struct imagetag {
	uint8_t			tagver[4];	/*   0 -   3: Version of the tag as ASCII (2) */
	uint8_t			sig1[20];	/*   4 -  23: BCM_MAGIC_1 */
	uint8_t			sig2[14];	/*  24 -  37: BCM_MAGIC_2 */
	uint8_t			chipid[6];	/*  38 -  43: Chip id as ASCII (6345) */
	uint8_t			boardid[16];	/*  44 -  59: Board id as ASCII (96345GW2, etc...) */
	uint8_t			bigendian[2];	/*  60 -  61: "1" for big endian, "0" for little endian */
	uint8_t			imagelen[10];	/*  62 -  71: The length of all data that follows */
	struct imagecomp	cfe;		/*  72 -  93: The offset and length of CFE */
	struct imagecomp	rootfs;		/*  94 - 115: The offset and length of the root file system */
	struct imagecomp	kernel;		/* 116 - 137: The offset and length of the kernel */
	uint8_t			dualimage[2];	/* 138 - 139: use "0" here */
	uint8_t			inactive[2];	/* 140 - 141: use "0" here */
	uint8_t			reserved1[50];	/* 142 - 191: reserved */
	uint8_t			headerver;	/*       192: header version */
	uint8_t                 reserved2[3];	/* 193 - 195: reserved */
	uint32_t		payloadcrc;	/* 196 - 199: crc of the payload (net byte order) */
	uint8_t			reserved3[16];	/* 200 - 215: reserved */
	uint32_t		imagecrc;	/* 216 - 219: crc of the images (net byte order) */
	uint8_t			reserved4[16];	/* 220 - 235: reserved */
	/* ~~~~~~~~~~~~~~~~~~ CRC goes till here ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	uint32_t		headercrc;	/* 236 - 239: crc starting from sig1 until headercrc (net byte order) */
	uint8_t			reserved5[16];	/* 240 - 255: reserved */
};

int reverse_endian = 1;

unsigned long htocl(unsigned long x)
{
  
  if (reverse_endian)
    return ((x << 24) & 0xff000000) |
      ((x << 8) & 0x00ff0000) |
      ((x >> 8) & 0x0000ff00) |
      ((x >> 24) & 0x000000ff);
  
  else
    return x;
  
}

unsigned crc32(unsigned char* data,
               unsigned len)
{
  unsigned ct, crc=0xFFFFFFFF;
  for (ct = 0;ct < len; ct++)
    {   
      int j;
      unsigned i=(data[ct]) ^ ((crc) & 0x000000FF);
      for(j = 8; j > 0; j--)
        {
          if(i & 1)
            i = (i >> 1) ^ 0xEDB88320;
          else
            i >>= 1;
        }
      crc = ((crc) >> 8) ^ i;
    }   
  return crc;
}

int main(int argc, char **argv)
{
	int readsize;
	FILE* imgfile;
	struct imagetag buffer;
	uint32_t imagelen;
	uint32_t cfe_addr;
	uint32_t cfe_len;
	uint32_t rootfs_addr;
	uint32_t rootfs_len;
	uint32_t kernel_addr;
	uint32_t kernel_len;
	unsigned char* data;

	imgfile = fopen("firmware.bin", "r");
	if(errno) {
		printf("Could not open firmware.bin.\n");
		exit(0);
	}

	readsize = fread(&buffer, sizeof(struct imagetag), 1, imgfile);
	if(readsize != 1) {
		printf("Could not read header (readsize = 0x%X).\n", readsize);
		fclose(imgfile);
		exit(0);
	}

	imagelen    = strtoul(buffer.imagelen, NULL, 10);
	cfe_addr    = strtoul(((struct imagecomp) buffer.cfe).address, NULL, 10);
	cfe_len     = strtoul(((struct imagecomp) buffer.cfe).len, NULL, 10);
	rootfs_addr = strtoul(((struct imagecomp) buffer.rootfs).address, NULL, 10);
	rootfs_len  = strtoul(((struct imagecomp) buffer.rootfs).len, NULL, 10);
	kernel_addr = strtoul(((struct imagecomp) buffer.kernel).address, NULL, 10);
	kernel_len  = strtoul(((struct imagecomp) buffer.kernel).len, NULL, 10);

	printf("Tag Version: '%-4s'\n",  buffer.tagver);
	printf("BCM Magic 1: '%-20s'\n", buffer.sig1);
	printf("BCM Magic 2: '%-14s'\n", buffer.sig2);
	printf("Chip ID    : '%-6s'\n",  buffer.chipid);
	printf("Board ID   : '%-16s'\n", buffer.boardid);
	printf("Big Endian : '%-2s'\n",  buffer.bigendian);
	printf("Image Len  : '%u'\n",    imagelen);
	printf("CFE Addr   : '0x%X'\n",  cfe_addr);
	printf("CFE Len    : '0x%X'\n",  cfe_len);
	printf("RootFS Addr: '0x%X'\n",  rootfs_addr);
	printf("RootFS Len : '0x%X'\n",  rootfs_len);
	printf("Kernel Addr: '0x%X'\n",  kernel_addr);
	printf("Kernel Len : '0x%X'\n",  kernel_len);
	printf("Dual Image : '%-2s'\n",  buffer.dualimage);
	printf("Header Ver : '%i'\n",    buffer.headerver);
	printf("Header CRC : '%X'\n",    buffer.headercrc);
	printf("Image CRC  : '%X'\n",    buffer.imagecrc);
	printf("Payload CRC: '%X'\n",    buffer.payloadcrc);
	printf("Checking CRC32...\n");

	data = malloc(256);
	fseek(imgfile, 0, SEEK_SET);
	readsize = fread(data, 256, 1, imgfile);
	printf("Header CRC : '%X'\n", ntohl(crc32(data, 236)));
	free(data);

	data = malloc(imagelen);
	fseek(imgfile, 256, SEEK_SET);
	readsize = fread(data, imagelen, 1, imgfile);
	printf("Image CRC  : '%X'\n", ntohl(crc32(data, imagelen)));
	printf("Payload CRC: '%X'\n", ntohl(crc32(data + cfe_len, rootfs_len + kernel_len)));
	free(data);	
	
	fclose(imgfile);


	return 0;
}


