/************************************************************/ /* Spidcom Technologies FILE NAME : hex_image.c DESCRIPTION : intel hex image loading HISTORY : -------------------------------------------------------------- DATE | AUTHOR | Version | Description -------------------------------------------------------------- 16/02/04 | Petillon | 1.0 | Creation */ /************************************************************/ #include #include #include "hex_image.h" #include "platform.h" #include "copy.h" #include "overlap.h" #include "bootloader.h" typedef struct { volatile caddr_t start; volatile caddr_t current; size_t remaining; } hex_mem_file_t; static char error_string[128]; /* internal */ static INLINE uint8_t hex_char(const char c) { if (c>='0' && c<='9') return c-'0'; else if (c>='a' && c<='f') return c-'a'+10; else if (c>='A' && c<='F') return c-'A'+10; else return 0xFF; } // returns -1 on End Of File, -2 invalid ASCII static int hex_next_char(hex_mem_file_t *hex_mem_file, char *c) { if (hex_mem_file->remaining==0) return -1; else { *c = *hex_mem_file->current; hex_mem_file->current++; hex_mem_file->remaining--; return 0; } } // returns -1 on End Of File, -2 invalid ASCII static int hex_next_byte(hex_mem_file_t *hex_mem_file, uint8_t *byte) { if (hex_mem_file->remaining<2) return -1; else { uint8_t tmp; if ((*byte = hex_char(hex_mem_file->current[0]))==0xFF || (tmp = hex_char(hex_mem_file->current[1]))==0xFF) return -2; *byte = (*byte) << 4 | tmp; hex_mem_file->current += 2; hex_mem_file->remaining -= 2; return 0; } } static int relocate_hex_source(hex_mem_file_t *hex_mem_file); // Reads an hex record // data: out: data read from data record, must contain 16 bytes // size: out: data size // copy_data: copy data at base_address+offset (use 0 only to check record) // base_address: in : current base address; out: new base address // finished: out: end of file record correctly read static int read_record(hex_mem_file_t *hex_mem_file, int copy_data, caddr_t *base_address, caddr_t *entry_point, int *finished, size_t *offset, uint8_t *len, const segment_list_t *reserved_segments) { int error; uint8_t high_byte; uint8_t low_byte; uint8_t data; uint8_t reclen; uint8_t rectyp; uint8_t chksum; uint8_t read_chksum; uint8_t sum; caddr_t address; char c; int rsv_seg_index = -1; segment_t segment; *finished = 0; do { error = hex_next_char(hex_mem_file,&c); } while (c!=':' && !error); // only use he last error code because in case of an error, // the subsequent calls return the same error hex_next_byte(hex_mem_file, &reclen); hex_next_byte(hex_mem_file, &high_byte); hex_next_byte(hex_mem_file, &low_byte); error = hex_next_byte(hex_mem_file, &rectyp); chksum = reclen + high_byte + low_byte + rectyp; if (!error) { switch (rectyp) { case 0x00: /* Data Record */ if (offset) *offset = (high_byte << 8) | low_byte; address = *base_address + ((high_byte << 8) | low_byte); if (copy_data) { segment.address = address; segment.size = reclen; if (reserved_segments && (rsv_seg_index=segment_overlap(&segment,reserved_segments))>=0) error = -5; /* check if we're gonna write on the source hex! */ if (overlap(address,reclen,hex_mem_file->current,hex_mem_file->remaining)) /* OOps, need to relocate */ error = relocate_hex_source(hex_mem_file); } if (len) *len = reclen; while (reclen-- > 0 && !error) { error = hex_next_byte(hex_mem_file, &data); if (copy_data) *address++ = data; chksum += data; } break; case 0x01: /* End Of File Record */ *finished = 1; break; case 0x02: /* Extended segment address record */ hex_next_byte(hex_mem_file, &high_byte); error = hex_next_byte(hex_mem_file, &low_byte); // use segment address *base_address = (caddr_t)(((high_byte << 8) | low_byte) << 4); chksum += high_byte + low_byte; if (offset) *offset = 0; if (len) *len = 0; break; case 0x03: /* Start segment address record */ case 0x05: /* Start Linear Address Record */ hex_next_byte(hex_mem_file, &high_byte); hex_next_byte(hex_mem_file, &low_byte); if (entry_point) *entry_point = (caddr_t)(((high_byte << 8) | low_byte) << 16); chksum += high_byte + low_byte; hex_next_byte(hex_mem_file, &high_byte); error = hex_next_byte(hex_mem_file, &low_byte); if (entry_point) *entry_point = (caddr_t)((((high_byte << 8) | low_byte)) | (uint32_t)*entry_point); chksum += high_byte + low_byte; break; case 0x04: /* Extended Linear Address Record */ hex_next_byte(hex_mem_file, &high_byte); error = hex_next_byte(hex_mem_file, &low_byte); // use linear address *base_address = (caddr_t)(((high_byte << 8) | low_byte) << 16); chksum += high_byte + low_byte; if (offset) *offset = 0; if (len) *len = 0; break; default: /* Unknown or unsupported record type */ error = -3; break; } if (!error) { if (hex_next_byte(hex_mem_file, &read_chksum)==0) { sum = chksum+read_chksum; if (sum!=0) error = -4; else /* Pass end of line & ':' character */ error = hex_next_char(hex_mem_file,&c); } } } if (error==-5) // write on reserved segment { #ifdef USE_STDIO sprintf(error_string, "attempt to write in reserved region '%s': 0x%.8lx-0x%.8lx", reserved_segments->segments[rsv_seg_index].name, (unsigned long)reserved_segments->segments[rsv_seg_index].address, (unsigned long)reserved_segments->segments[rsv_seg_index].address+ reserved_segments->segments[rsv_seg_index].size-1); #endif // USE_STDIO return -1; } else if (error==-4) // cksum error { #ifdef USE_STDIO sprintf(error_string, "checksum error"); #endif // USE_STDIO return -1; } else if (error==-3) // invalid rectype { #ifdef USE_STDIO sprintf(error_string, "unsupported record type 0x%2.2x",rectyp); #endif // USE_STDIO return -1; } else if (error==-2) // invalid ASCII { #ifdef USE_STDIO sprintf(error_string, "invalid hex file"); #endif // USE_STDIO return -1; } if (error==-1 && !*finished) // EOF { #ifdef USE_STDIO sprintf(error_string, "incomplete hex file"); #endif // USE_STDIO return -1; } return 0; } static int relocate_hex_source(hex_mem_file_t *hex_mem_file) { segment_list_t segment_list; segment_t segment; caddr_t base_address; size_t offset; size_t min_offset; uint8_t len; int finished; hex_mem_file_t initial_file; int result; /* first, build the list of segments */ segment_list_init(&segment_list); initial_file.start = hex_mem_file->start; initial_file.current = initial_file.start; initial_file.remaining = hex_mem_file->remaining+hex_mem_file->current-hex_mem_file->start; base_address = (caddr_t) 0x0; segment.address = base_address; segment.size = 0; min_offset = 0xFFFFFFFF; while(read_record(&initial_file,0 /*no copy*/,&base_address,NULL,&finished,&offset,&len,NULL)>=0 && !finished) { if (base_address!=segment.address) { /* base changed: we need to add current segment to the list */ if (segment.size>0) { segment.address += min_offset; segment.size -= min_offset; add_segment(&segment,&segment_list); } segment.address = base_address; segment.size = 0; min_offset = 0xFFFFFFFF; } else { if (offset+len>segment.size) segment.size = offset+len; if (offset0) /* add the last segment, if needed */ { segment.address += min_offset; segment.size -= min_offset; add_segment(&segment,&segment_list); } if (!finished) result = -1; else { segment.size = hex_mem_file->remaining; if ((result=find_segment((caddr_t)SDRAM_BASE+get_vectors_size(), get_run_code_base()-((caddr_t)SDRAM_BASE+get_vectors_size()), &segment_list, &segment))>=0) { /* move data */ /* BEWARE: the regions may overlap! */ if (segment.addresscurrent) copy_forward(segment.address,hex_mem_file->current,hex_mem_file->remaining); else copy_backward(segment.address,hex_mem_file->current,hex_mem_file->remaining); hex_mem_file->start = segment.address-(hex_mem_file->current-hex_mem_file->start); hex_mem_file->current = segment.address; } #ifdef USE_STDIO else sprintf(error_string, "unable to relocate source"); #endif // USE_STDIO } segment_list_delete(&segment_list); return result; } /* exported */ int load_hex_image(uint8_t *image_data, size_t image_size, caddr_t *entry_point, const segment_list_t *reserved_segments) { hex_mem_file_t hex_mem_file; caddr_t base_address; int finished; hex_mem_file.start = image_data; hex_mem_file.current = hex_mem_file.start; hex_mem_file.remaining = image_size; base_address = (caddr_t) 0x0; while(read_record(&hex_mem_file,1 /*effective copy*/,&base_address,entry_point,&finished,NULL,NULL,reserved_segments)>=0 && !finished) ; if (finished) return 0; else return -1; } size_t hex_header_min_size() { return 128; // > 2*size of full data record } int check_hex_header(uint8_t *header, size_t header_size) { hex_mem_file_t hex_mem_file; caddr_t base_address; caddr_t entry_point; int finished; if (header_size=0) { if (finished) return 1; else { if (read_record(&hex_mem_file,0,&base_address,&entry_point,&finished,NULL,NULL,NULL)>=0) return 1; else return 0; } } else return 0; } const char *hex_image_error(void) { return error_string; }