summaryrefslogtreecommitdiffstats
path: root/tools/firmware-utils/src/cvimg.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/firmware-utils/src/cvimg.c')
-rw-r--r--tools/firmware-utils/src/cvimg.c364
1 files changed, 364 insertions, 0 deletions
diff --git a/tools/firmware-utils/src/cvimg.c b/tools/firmware-utils/src/cvimg.c
new file mode 100644
index 000000000..a2b9519d5
--- /dev/null
+++ b/tools/firmware-utils/src/cvimg.c
@@ -0,0 +1,364 @@
+/*
+ * Tool to convert ELF image to be the AP downloadable binary
+ *
+ * Authors: David Hsu <davidhsu@realtek.com.tw>
+ *
+ * $Id: cvimg.c,v 1.4 2009/06/12 07:10:44 michael Exp $
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#define HOME_GATEWAY
+
+#define CONFIG_RTL_8196C
+
+#include "apmib.h"
+
+#define COMPACT_FILENAME_BUFFER
+
+
+static int friendly_strcmp(const char *s1, const char *s2)
+{
+ if (!s1 || !s2) return (s1>s2) ? -1 : !(s1==s2);
+ return strcmp(s1,s2);
+}
+
+/* 32-bit ELF base types. */
+typedef unsigned int Elf32_Addr;
+typedef unsigned short Elf32_Half;
+typedef unsigned int Elf32_Off;
+typedef unsigned int Elf32_Word;
+
+#define EI_NIDENT 16
+
+typedef struct elf32_hdr{
+ unsigned char e_ident[EI_NIDENT];
+ Elf32_Half e_type;
+ Elf32_Half e_machine;
+ Elf32_Word e_version;
+ Elf32_Addr e_entry; /* Entry point */
+ Elf32_Off e_phoff;
+ Elf32_Off e_shoff;
+ Elf32_Word e_flags;
+ Elf32_Half e_ehsize;
+ Elf32_Half e_phentsize;
+ Elf32_Half e_phnum;
+ Elf32_Half e_shentsize;
+ Elf32_Half e_shnum;
+ Elf32_Half e_shstrndx;
+} Elf32_Ehdr;
+
+#define DEFAULT_START_ADDR 0x80500000
+#define DEFAULT_BASE_ADDR 0x80000000
+
+static unsigned short calculateChecksum(char *buf, int len);
+
+/////////////////////////////////////////////////////////
+static unsigned int calculate_long_checksum(unsigned int *buf, int len)
+{
+ int i, j;
+ unsigned int sum=0, tmp;
+
+ j = len/4;
+
+ for (i=0; i<j; buf++, i++) {
+ tmp = *buf;
+ sum += DWORD_SWAP(tmp);
+ }
+ return ~sum+1;
+}
+
+unsigned int extractStartAddr(char *filename)
+{
+ int fh;
+ Elf32_Ehdr hdr;
+ char *buf;
+
+ buf = (char *)&hdr;
+ fh = open(filename, O_RDONLY);
+ if ( fh == -1 ) {
+ printf("Open input file error2!\n");
+ exit(1);
+ }
+ lseek(fh, 0L, SEEK_SET);
+ if ( read(fh, buf, sizeof(Elf32_Ehdr)) != sizeof(Elf32_Ehdr)) {
+ printf("Read file error2!\n");
+ close(fh);
+ exit(1);
+ }
+ close(fh);
+
+ return(hdr.e_entry);
+}
+
+void printf_usage(void)
+{
+ printf("Version: 1.1\n");
+ printf("Usage: cvimg <option> input-filename output-filename start-addr burn-addr [signature]\n");
+ printf("<option>: root|linux|boot|all|vmlinux|vmlinuxhdr|signature\n");
+ printf("[signature]: user-specified signature (4 characters)\n");
+}
+
+int main(int argc, char** argv)
+{
+#ifdef COMPACT_FILENAME_BUFFER
+ const char *inFile = NULL, *outFile = NULL;
+#else
+ char inFile[80]={0}, outFile[80]={0};
+#endif
+ int fh, size;
+ struct stat status;
+ char *buf;
+ IMG_HEADER_Tp pHeader;
+ unsigned int startAddr;
+ unsigned int burnAddr;
+ unsigned short checksum;
+ int is_vmlinux = 0;
+ int is_vmlinuxhdr = 0;
+ int is_signature = 0;
+ unsigned int lchecksum, padding_len;
+ unsigned int start_addr;
+
+ if (argc == 4 && !friendly_strcmp(argv[1], "size_chk")) {
+ unsigned int total_size;
+
+#ifdef COMPACT_FILENAME_BUFFER
+ inFile = argv[2];
+#else
+ sscanf(argv[2], "%s", inFile);
+#endif
+ sscanf(argv[3], "%x", &startAddr);
+ if ( stat(inFile, &status) < 0 ) {
+ printf("Can't stat file! [%s]\n", inFile );
+ exit(1);
+ }
+ printf("==============================================\n");
+ printf("Summary ==>\n");
+ printf("Image loading addr :0x%x\n", (int)startAddr);
+ printf("Image decompress end addr :0x%x\n", ((unsigned int)DEFAULT_BASE_ADDR)+(unsigned int)status.st_size);
+
+ total_size = startAddr - ((unsigned int)DEFAULT_BASE_ADDR);
+
+ if (status.st_size > (int)total_size)
+ {
+ printf("Error!!!! : Kernel image decompress will overwirte load image\n");
+ exit(1);
+ }
+ else
+ printf("Available size :0x%08x\n",
+ (unsigned int)(total_size - status.st_size));
+
+ exit(0);
+ }
+
+#ifdef CONFIG_RTL_FLASH_MAPPING_ENABLE
+ if (argc == 3 && !friendly_strcmp(argv[1], "flash_size_chk")) {
+ unsigned int total_size;
+
+#ifdef COMPACT_FILENAME_BUFFER
+ inFile = argv[2];
+#else
+ sscanf(argv[2], "%s", inFile);
+#endif
+ if ( stat(inFile, &status) < 0 ) {
+ printf("Can't stat file! [%s]\n", inFile );
+ exit(1);
+ }
+ printf("==============================================\n");
+ printf("Summary ==>\n");
+ printf("Image flash start addr :0x%x\n", (unsigned int)CODE_IMAGE_OFFSET);
+ printf("Image flash end addr :0x%x\n", (unsigned int)CODE_IMAGE_OFFSET+(unsigned int)status.st_size);
+
+#ifdef CONFIG_RTL_802_1X_CLIENT_SUPPORT
+ total_size = ROOT_IMAGE_OFFSET - CODE_IMAGE_OFFSET - CERT_SIZE; // To reserve CERT_SIZE for 802.1x wlan client mode to store 802.1x certs
+#else
+ total_size = ROOT_IMAGE_OFFSET - CODE_IMAGE_OFFSET;
+#endif
+
+ if (status.st_size > (int)total_size)
+ {
+ printf("Error!!!! : Kernel image too big will overwirte rootfs image, cur size(%d), available size(%d).\n",status.st_size, total_size);
+ exit(1);
+ }
+ else
+ printf("Available size :0x%08x\n",
+ (unsigned int)(total_size - status.st_size));
+
+ exit(0);
+ }
+#endif
+
+ if (argc == 4 && !friendly_strcmp(argv[1], "vmlinux"))
+ is_vmlinux = 1;
+
+ if (argc == 5 && !friendly_strcmp(argv[1], "vmlinuxhdr")) {
+ is_vmlinuxhdr = 1;
+ start_addr = extractStartAddr(argv[4]);
+ }
+
+ if (!friendly_strcmp(argv[1], "signature")) {
+ is_signature = 1;
+ if (argc != 7) {
+ printf_usage();
+ exit(1);
+ }
+ }
+
+ // parse input arguments
+ if ( argc != 6 && !is_vmlinux && !is_vmlinuxhdr && !is_signature) {
+ printf_usage();
+ exit(1);
+ }
+#ifdef COMPACT_FILENAME_BUFFER
+ inFile = argv[2];
+ outFile = argv[3];
+#else
+ sscanf(argv[2], "%s", inFile);
+ sscanf(argv[3], "%s", outFile);
+#endif
+
+ if ((!is_vmlinux) && (!is_vmlinuxhdr)) {
+ sscanf(argv[4], "%x", &startAddr);
+ sscanf(argv[5], "%x", &burnAddr);
+ }
+ // check input file and allocate buffer
+ if ( stat(inFile, &status) < 0 ) {
+ printf("Can't stat file! [%s]\n", inFile );
+ exit(1);
+ }
+
+ if (is_vmlinuxhdr) {
+ size = status.st_size + sizeof(padding_len) + sizeof(lchecksum) + sizeof(start_addr);
+ padding_len = 4 - (size%4);
+ size += padding_len;
+ }
+ else if (!is_vmlinux) {
+ size = status.st_size + sizeof(IMG_HEADER_T) + sizeof(checksum);
+ if (size%2)
+ size++; // pad
+ }
+ else {
+ size = status.st_size + sizeof(padding_len) + sizeof(lchecksum);
+ padding_len = 4 - (size%4);
+ size += padding_len;
+ }
+
+ buf = malloc(size);
+ if (buf == NULL) {
+ printf("Malloc buffer failed!\n");
+ exit(1);
+ }
+ memset(buf, '\0', size);
+ pHeader = (IMG_HEADER_Tp)buf;
+
+ if (is_vmlinuxhdr)
+ buf += 8; // skip padding-length field and start-address field
+ else if (!is_vmlinux)
+ buf += sizeof(IMG_HEADER_T);
+ else
+ buf += 4; // skip padding-length field
+
+ // Read data and generate header
+ fh = open(inFile, O_RDONLY);
+ if ( fh == -1 ) {
+ printf("Open input file error!\n");
+ free( pHeader );
+ exit(1);
+ }
+ lseek(fh, 0L, SEEK_SET);
+ if ( read(fh, buf, status.st_size) != status.st_size) {
+ printf("Read file error!\n");
+ close(fh);
+ free(pHeader);
+ exit(1);
+ }
+ close(fh);
+
+ if (is_vmlinuxhdr) {
+ *((unsigned int *)pHeader) = DWORD_SWAP(padding_len);
+ *((unsigned int *)((char *)pHeader+4)) = start_addr;
+ lchecksum = DWORD_SWAP(calculate_long_checksum((unsigned int *)buf, size-12));
+ memcpy(&buf[size-12], &lchecksum, 4);
+ }
+ else if (!is_vmlinux) {
+ if( !friendly_strcmp("root", argv[1]))
+ memcpy(pHeader->signature, ROOT_HEADER, SIGNATURE_LEN);
+ else if ( !friendly_strcmp("boot", argv[1]))
+ memcpy(pHeader->signature, BOOT_HEADER, SIGNATURE_LEN);
+ else if ( !friendly_strcmp("linux", argv[1]))
+ memcpy(pHeader->signature, FW_HEADER, SIGNATURE_LEN);
+ else if ( !friendly_strcmp("linux-ro", argv[1]))
+ memcpy(pHeader->signature, FW_HEADER_WITH_ROOT, SIGNATURE_LEN);
+ else if ( !friendly_strcmp("signature", argv[1]))
+ memcpy(pHeader->signature, argv[6], SIGNATURE_LEN);
+ else{
+ printf("not supported signature\n");
+ exit(1);
+ }
+ pHeader->len = DWORD_SWAP((size-sizeof(IMG_HEADER_T)));
+ pHeader->startAddr = DWORD_SWAP(startAddr);
+ pHeader->burnAddr = DWORD_SWAP(burnAddr);
+
+ if( !friendly_strcmp("root", argv[1])) {
+ #define SIZE_OF_SQFS_SUPER_BLOCK 640
+ unsigned int fs_len;
+ fs_len = DWORD_SWAP((size-sizeof(IMG_HEADER_T) - sizeof(checksum)- SIZE_OF_SQFS_SUPER_BLOCK));
+ memcpy(buf + 8, &fs_len, 4);
+ }
+
+ checksum = WORD_SWAP(calculateChecksum(buf, status.st_size));
+ *((unsigned short *)&buf[size-sizeof(IMG_HEADER_T)-sizeof(checksum)]) = checksum;
+ }
+ else { // is_vmlinux=1
+ *((unsigned int *)pHeader) = DWORD_SWAP(padding_len);
+ lchecksum = DWORD_SWAP(calculate_long_checksum((unsigned int *)buf, size-8));
+ memcpy(&buf[size-8], &lchecksum, 4);
+ }
+
+ // Write image to output file
+ fh = open(outFile, O_RDWR | O_CREAT | O_TRUNC, DEFFILEMODE);
+ if ( fh == -1 ) {
+ printf("Create output file error! [%s]\n", outFile);
+ free(pHeader);
+ exit(1);
+ }
+ write(fh, pHeader, size);
+ close(fh);
+ chmod(outFile, DEFFILEMODE);
+
+ if (is_vmlinuxhdr)
+ printf("Generate image successfully, length=%d, checksum=0x%x, padding=%d, start address=0x%08x\n", size-12-padding_len, lchecksum, padding_len, DWORD_SWAP(start_addr));
+ else if (!is_vmlinux)
+ printf("Generate image successfully, length=%d, checksum=0x%x\n", (int)DWORD_SWAP(pHeader->len), checksum);
+ else
+ printf("Generate image successfully, length=%d, checksum=0x%x, padding=%d\n", size-8-padding_len, lchecksum, padding_len);
+
+ free(pHeader);
+ return 0;
+}
+
+static unsigned short calculateChecksum(char *buf, int len)
+{
+ int i, j;
+ unsigned short sum=0, tmp;
+
+ j = (len/2)*2;
+
+ for (i=0; i<j; i+=2) {
+ tmp = *((unsigned short *)(buf + i));
+ sum += WORD_SWAP(tmp);
+ }
+
+ if ( len % 2 ) {
+ tmp = buf[len-1];
+ sum += WORD_SWAP(tmp);
+ }
+ return ~sum+1;
+}