|  | /************************************************************************* | 
|  | |  COPYRIGHT (c) 2000 BY ABATRON AG | 
|  | |************************************************************************* | 
|  | | | 
|  | |  PROJECT NAME: Linux Image to S-record Conversion Utility | 
|  | |  FILENAME    : img2srec.c | 
|  | | | 
|  | |  COMPILER    : GCC | 
|  | | | 
|  | |  TARGET OS   : LINUX / UNIX | 
|  | |  TARGET HW   : - | 
|  | | | 
|  | |  PROGRAMMER  : Abatron / RD | 
|  | |  CREATION    : 07.07.00 | 
|  | | | 
|  | |************************************************************************* | 
|  | | | 
|  | |  DESCRIPTION : | 
|  | | | 
|  | |  Utility to convert a Linux Boot Image to S-record: | 
|  | |  ================================================== | 
|  | | | 
|  | |  This command line utility can be used to convert a Linux boot image | 
|  | |  (zimage.initrd) to S-Record format used for flash programming. | 
|  | |  This conversion takes care of the special sections "IMAGE" and INITRD". | 
|  | | | 
|  | |  img2srec [-o offset] image > image.srec | 
|  | | | 
|  | | | 
|  | |  Build the utility: | 
|  | |  ================== | 
|  | | | 
|  | |  To build the utility use GCC as follows: | 
|  | | | 
|  | |  gcc img2srec.c -o img2srec | 
|  | | | 
|  | | | 
|  | |************************************************************************* | 
|  | | | 
|  | | | 
|  | |  UPDATES     : | 
|  | | | 
|  | |  DATE      NAME  CHANGES | 
|  | |  ----------------------------------------------------------- | 
|  | |  Latest update | 
|  | | | 
|  | |  07.07.00  aba   Initial release | 
|  | | | 
|  | |*************************************************************************/ | 
|  |  | 
|  | /************************************************************************* | 
|  | |  INCLUDES | 
|  | |*************************************************************************/ | 
|  |  | 
|  | #include "os_support.h" | 
|  | #include <stdbool.h> | 
|  | #include <stddef.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <ctype.h> | 
|  | #include <string.h> | 
|  | #include <elf.h> | 
|  | #include <unistd.h> | 
|  | #include <errno.h> | 
|  |  | 
|  | /************************************************************************* | 
|  | |  FUNCTIONS | 
|  | |*************************************************************************/ | 
|  |  | 
|  | static char* ExtractHex (uint32_t* value,  char* getPtr) | 
|  | { | 
|  | uint32_t num; | 
|  | uint32_t digit; | 
|  | uint8_t  c; | 
|  |  | 
|  | while (*getPtr == ' ') getPtr++; | 
|  | num = 0; | 
|  | for (;;) { | 
|  | c = *getPtr; | 
|  | if      ((c >= '0') && (c <= '9')) digit = (uint32_t)(c - '0'); | 
|  | else if ((c >= 'A') && (c <= 'F')) digit = (uint32_t)(c - 'A' + 10); | 
|  | else if ((c >= 'a') && (c <= 'f')) digit = (uint32_t)(c - 'a' + 10); | 
|  | else break; | 
|  | num <<= 4; | 
|  | num += digit; | 
|  | getPtr++; | 
|  | } /* for */ | 
|  | *value = num; | 
|  | return getPtr; | 
|  | } /* ExtractHex */ | 
|  |  | 
|  | static char* ExtractDecimal (uint32_t* value,  char* getPtr) | 
|  | { | 
|  | uint32_t num; | 
|  | uint32_t digit; | 
|  | uint8_t  c; | 
|  |  | 
|  | while (*getPtr == ' ') getPtr++; | 
|  | num = 0; | 
|  | for (;;) { | 
|  | c = *getPtr; | 
|  | if      ((c >= '0') && (c <= '9')) digit = (uint32_t)(c - '0'); | 
|  | else break; | 
|  | num *= 10; | 
|  | num += digit; | 
|  | getPtr++; | 
|  | } /* for */ | 
|  | *value = num; | 
|  | return getPtr; | 
|  | } /* ExtractDecimal */ | 
|  |  | 
|  |  | 
|  | static void ExtractNumber (uint32_t* value,  char* getPtr) | 
|  | { | 
|  | bool  neg = false;; | 
|  |  | 
|  | while (*getPtr == ' ') getPtr++; | 
|  | if (*getPtr == '-') { | 
|  | neg = true; | 
|  | getPtr++; | 
|  | } /* if */ | 
|  | if ((*getPtr == '0') && ((*(getPtr+1) == 'x') || (*(getPtr+1) == 'X'))) { | 
|  | getPtr +=2; | 
|  | (void)ExtractHex(value, getPtr); | 
|  | } /* if */ | 
|  | else { | 
|  | (void)ExtractDecimal(value, getPtr); | 
|  | } /* else */ | 
|  | if (neg) *value = -(*value); | 
|  | } /* ExtractNumber */ | 
|  |  | 
|  |  | 
|  | static uint8_t* ExtractWord(uint16_t* value, uint8_t* buffer) | 
|  | { | 
|  | uint16_t x; | 
|  | x = (uint16_t)*buffer++; | 
|  | x = (x<<8) + (uint16_t)*buffer++; | 
|  | *value = x; | 
|  | return buffer; | 
|  | } /* ExtractWord */ | 
|  |  | 
|  |  | 
|  | static uint8_t* ExtractLong(uint32_t* value, uint8_t* buffer) | 
|  | { | 
|  | uint32_t x; | 
|  | x = (uint32_t)*buffer++; | 
|  | x = (x<<8) + (uint32_t)*buffer++; | 
|  | x = (x<<8) + (uint32_t)*buffer++; | 
|  | x = (x<<8) + (uint32_t)*buffer++; | 
|  | *value = x; | 
|  | return buffer; | 
|  | } /* ExtractLong */ | 
|  |  | 
|  |  | 
|  | static uint8_t* ExtractBlock(uint16_t count, uint8_t* data, uint8_t* buffer) | 
|  | { | 
|  | while (count--) *data++ = *buffer++; | 
|  | return buffer; | 
|  | } /* ExtractBlock */ | 
|  |  | 
|  |  | 
|  | static char* WriteHex(char* pa, uint8_t value, uint16_t* pCheckSum) | 
|  | { | 
|  | uint16_t  temp; | 
|  |  | 
|  | static  char ByteToHex[] = "0123456789ABCDEF"; | 
|  |  | 
|  | *pCheckSum += value; | 
|  | temp  = value / 16; | 
|  | *pa++ = ByteToHex[temp]; | 
|  | temp  = value % 16; | 
|  | *pa++ = ByteToHex[temp]; | 
|  | return pa; | 
|  | } | 
|  |  | 
|  |  | 
|  | static char* BuildSRecord(char* pa, uint16_t sType, uint32_t addr, | 
|  | const uint8_t* data, int nCount) | 
|  | { | 
|  | uint16_t  addrLen; | 
|  | uint16_t  sRLen; | 
|  | uint16_t  checkSum; | 
|  | uint16_t  i; | 
|  |  | 
|  | switch (sType) { | 
|  | case 0: | 
|  | case 1: | 
|  | case 9: | 
|  | addrLen = 2; | 
|  | break; | 
|  | case 2: | 
|  | case 8: | 
|  | addrLen = 3; | 
|  | break; | 
|  | case 3: | 
|  | case 7: | 
|  | addrLen = 4; | 
|  | break; | 
|  | default: | 
|  | return pa; | 
|  | } /* switch */ | 
|  |  | 
|  | *pa++ = 'S'; | 
|  | *pa++ = (char)(sType + '0'); | 
|  | sRLen = addrLen + nCount + 1; | 
|  | checkSum = 0; | 
|  | pa = WriteHex(pa, (uint8_t)sRLen, &checkSum); | 
|  |  | 
|  | /* Write address field */ | 
|  | for (i = 1; i <= addrLen; i++) { | 
|  | pa = WriteHex(pa, (uint8_t)(addr >> (8 * (addrLen - i))), &checkSum); | 
|  | } /* for */ | 
|  |  | 
|  | /* Write code/data fields */ | 
|  | for (i = 0; i < nCount; i++) { | 
|  | pa = WriteHex(pa, *data++, &checkSum); | 
|  | } /* for */ | 
|  |  | 
|  | /* Write checksum field */ | 
|  | checkSum = ~checkSum; | 
|  | pa = WriteHex(pa, (uint8_t)checkSum, &checkSum); | 
|  | *pa++ = '\0'; | 
|  | return pa; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void ConvertELF(char* fileName, uint32_t loadOffset) | 
|  | { | 
|  | FILE*         file; | 
|  | int           i; | 
|  | int           rxCount; | 
|  | uint8_t          rxBlock[1024]; | 
|  | uint32_t         loadSize; | 
|  | uint32_t         firstAddr; | 
|  | uint32_t         loadAddr; | 
|  | uint32_t         loadDiff = 0; | 
|  | Elf32_Ehdr    elfHeader; | 
|  | Elf32_Shdr    sectHeader[32]; | 
|  | uint8_t*         getPtr; | 
|  | char          srecLine[128]; | 
|  | char		*hdr_name; | 
|  |  | 
|  |  | 
|  | /* open file */ | 
|  | if ((file = fopen(fileName,"rb")) == NULL) { | 
|  | fprintf (stderr, "Can't open %s: %s\n", fileName, strerror(errno)); | 
|  | return; | 
|  | } /* if */ | 
|  |  | 
|  | /* read ELF header */ | 
|  | rxCount = fread(rxBlock, 1, sizeof elfHeader, file); | 
|  | getPtr = ExtractBlock(sizeof elfHeader.e_ident, elfHeader.e_ident, rxBlock); | 
|  | getPtr = ExtractWord(&elfHeader.e_type, getPtr); | 
|  | getPtr = ExtractWord(&elfHeader.e_machine, getPtr); | 
|  | getPtr = ExtractLong((uint32_t *)&elfHeader.e_version, getPtr); | 
|  | getPtr = ExtractLong((uint32_t *)&elfHeader.e_entry, getPtr); | 
|  | getPtr = ExtractLong((uint32_t *)&elfHeader.e_phoff, getPtr); | 
|  | getPtr = ExtractLong((uint32_t *)&elfHeader.e_shoff, getPtr); | 
|  | getPtr = ExtractLong((uint32_t *)&elfHeader.e_flags, getPtr); | 
|  | getPtr = ExtractWord(&elfHeader.e_ehsize, getPtr); | 
|  | getPtr = ExtractWord(&elfHeader.e_phentsize, getPtr); | 
|  | getPtr = ExtractWord(&elfHeader.e_phnum, getPtr); | 
|  | getPtr = ExtractWord(&elfHeader.e_shentsize, getPtr); | 
|  | getPtr = ExtractWord(&elfHeader.e_shnum, getPtr); | 
|  | getPtr = ExtractWord(&elfHeader.e_shstrndx, getPtr); | 
|  | if (    (rxCount              != sizeof elfHeader) | 
|  | || (elfHeader.e_ident[0] != ELFMAG0) | 
|  | || (elfHeader.e_ident[1] != ELFMAG1) | 
|  | || (elfHeader.e_ident[2] != ELFMAG2) | 
|  | || (elfHeader.e_ident[3] != ELFMAG3) | 
|  | || (elfHeader.e_type     != ET_EXEC) | 
|  | ) { | 
|  | fclose(file); | 
|  | fprintf (stderr, "*** illegal file format\n"); | 
|  | return; | 
|  | } /* if */ | 
|  |  | 
|  | /* read all section headers */ | 
|  | fseek(file, elfHeader.e_shoff, SEEK_SET); | 
|  | for (i = 0; i < elfHeader.e_shnum; i++) { | 
|  | rxCount = fread(rxBlock, 1, sizeof sectHeader[0], file); | 
|  | getPtr = ExtractLong((uint32_t *)§Header[i].sh_name, rxBlock); | 
|  | getPtr = ExtractLong((uint32_t *)§Header[i].sh_type, getPtr); | 
|  | getPtr = ExtractLong((uint32_t *)§Header[i].sh_flags, getPtr); | 
|  | getPtr = ExtractLong((uint32_t *)§Header[i].sh_addr, getPtr); | 
|  | getPtr = ExtractLong((uint32_t *)§Header[i].sh_offset, getPtr); | 
|  | getPtr = ExtractLong((uint32_t *)§Header[i].sh_size, getPtr); | 
|  | getPtr = ExtractLong((uint32_t *)§Header[i].sh_link, getPtr); | 
|  | getPtr = ExtractLong((uint32_t *)§Header[i].sh_info, getPtr); | 
|  | getPtr = ExtractLong((uint32_t *)§Header[i].sh_addralign, getPtr); | 
|  | getPtr = ExtractLong((uint32_t *)§Header[i].sh_entsize, getPtr); | 
|  | if (rxCount != sizeof sectHeader[0]) { | 
|  | fclose(file); | 
|  | fprintf (stderr, "*** illegal file format\n"); | 
|  | return; | 
|  | } /* if */ | 
|  | } /* for */ | 
|  |  | 
|  | if ((hdr_name = strrchr(fileName, '/')) == NULL) { | 
|  | hdr_name = fileName; | 
|  | } else { | 
|  | ++hdr_name; | 
|  | } | 
|  | /* write start record */ | 
|  | (void)BuildSRecord(srecLine, 0, 0, (uint8_t *)hdr_name, strlen(hdr_name)); | 
|  | printf("%s\r\n",srecLine); | 
|  |  | 
|  | /* write data records */ | 
|  | firstAddr = ~0; | 
|  | loadAddr  =  0; | 
|  | for (i = 0; i < elfHeader.e_shnum; i++) { | 
|  | if (    (sectHeader[i].sh_type == SHT_PROGBITS) | 
|  | && (sectHeader[i].sh_size != 0) | 
|  | ) { | 
|  | loadSize = sectHeader[i].sh_size; | 
|  | if (sectHeader[i].sh_flags != 0) { | 
|  | loadAddr = sectHeader[i].sh_addr; | 
|  | loadDiff = loadAddr - sectHeader[i].sh_offset; | 
|  | } /* if */ | 
|  | else { | 
|  | loadAddr = sectHeader[i].sh_offset + loadDiff; | 
|  | } /* else */ | 
|  |  | 
|  | if (loadAddr < firstAddr) | 
|  | firstAddr = loadAddr; | 
|  |  | 
|  | /* build s-records */ | 
|  | loadSize = sectHeader[i].sh_size; | 
|  | fseek(file, sectHeader[i].sh_offset, SEEK_SET); | 
|  | while (loadSize) { | 
|  | rxCount = fread(rxBlock, 1, (loadSize > 32) ? 32 : loadSize, file); | 
|  | if (rxCount < 0) { | 
|  | fclose(file); | 
|  | fprintf (stderr, "*** illegal file format\n"); | 
|  | return; | 
|  | } /* if */ | 
|  | (void)BuildSRecord(srecLine, 3, loadAddr + loadOffset, rxBlock, rxCount); | 
|  | loadSize -= rxCount; | 
|  | loadAddr += rxCount; | 
|  | printf("%s\r\n",srecLine); | 
|  | } /* while */ | 
|  | } /* if */ | 
|  | } /* for */ | 
|  |  | 
|  | /* add end record */ | 
|  | (void)BuildSRecord(srecLine, 7, firstAddr + loadOffset, 0, 0); | 
|  | printf("%s\r\n",srecLine); | 
|  | fclose(file); | 
|  | } /* ConvertELF */ | 
|  |  | 
|  |  | 
|  | /************************************************************************* | 
|  | |  MAIN | 
|  | |*************************************************************************/ | 
|  |  | 
|  | int main( int argc, char *argv[ ]) | 
|  | { | 
|  | uint32_t offset; | 
|  |  | 
|  | if (argc == 2) { | 
|  | ConvertELF(argv[1], 0); | 
|  | } /* if */ | 
|  | else if ((argc == 4) && (strcmp(argv[1], "-o") == 0)) { | 
|  | ExtractNumber(&offset, argv[2]); | 
|  | ConvertELF(argv[3], offset); | 
|  | } /* if */ | 
|  | else { | 
|  | fprintf (stderr, "Usage: img2srec [-o offset] <image>\n"); | 
|  | } /* if */ | 
|  |  | 
|  | return 0; | 
|  | } /* main */ |