Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

+ Support ELF files with multiple segments that go into flash memory. #504

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
4 changes: 0 additions & 4 deletions simavr/sim/run_avr.c
Expand Up @@ -251,10 +251,6 @@ main(
#endif //CONFIG_SIMAVR_TRACE

avr_load_firmware(avr, &f);
if (f.flashbase) {
printf("Attempted to load a bootloader at %04x\n", f.flashbase);
avr->pc = f.flashbase;
}
for (int ti = 0; ti < trace_vectors_count; ti++) {
for (int vi = 0; vi < avr->interrupts.vector_count; vi++)
if (avr->interrupts.vector[vi]->vector == trace_vectors[ti])
Expand Down
99 changes: 32 additions & 67 deletions simavr/sim/sim_elf.c
Expand Up @@ -74,9 +74,8 @@ avr_load_firmware(
#endif

avr_loadcode(avr, firmware->flash,
firmware->flashsize, firmware->flashbase);
avr->codeend = firmware->flashsize +
firmware->flashbase - firmware->datasize;
firmware->flashsize, 0);
avr->codeend = firmware->flashsize - firmware->datasize;

if (firmware->eeprom && firmware->eesize) {
avr_eeprom_desc_t d = {
Expand Down Expand Up @@ -273,43 +272,41 @@ elf_parse_mmcu_section(
}

static int
elf_copy_segment(int fd, Elf32_Phdr *php, uint8_t **dest)
elf_copy_segment(int fd, Elf32_Phdr *php, uint8_t **buffer, uint32_t *bufsize)
{
int rv;

if (*dest == NULL)
*dest = malloc(php->p_filesz);
if (!*dest)
Elf32_Addr segment_end;

/* Allocate enough memory to load the segment.
Note that both malloc_usable_size and realloc do the right thing in
case *buffer==NULL (i.o.w. no memory allocated yet) */
segment_end = php->p_paddr + php->p_memsz;
if (*bufsize < segment_end) {
*buffer = realloc(*buffer, segment_end);
*bufsize = segment_end;
}
if (!*buffer)
return -1;

lseek(fd, php->p_offset, SEEK_SET);
rv = read(fd, *dest, php->p_filesz);
rv = read(fd, *buffer + php->p_paddr, php->p_filesz);
if (rv != php->p_filesz) {
AVR_LOG(NULL, LOG_ERROR,
"Got %d when reading %d bytes for %x at offset %d "
"from ELF file\n",
rv, php->p_filesz, php->p_vaddr, php->p_offset);
return -1;
}
/* ELF specifications make a differnce between file size and memory
size of the segment. If the memory size is larger than the file
file, the remainder should be cleared with zeros. */
if (php->p_memsz > php->p_filesz)
memset(*buffer + php->p_filesz, 0, php->p_memsz - php->p_filesz);
AVR_LOG(NULL, LOG_DEBUG, "Loaded %d bytes at %x\n",
php->p_filesz, php->p_vaddr);
php->p_filesz, php->p_paddr);
return 0;
}

static int
elf_handle_segment(int fd, Elf32_Phdr *php, uint8_t **dest, const char *name)
{
if (*dest) {
AVR_LOG(NULL, LOG_ERROR,
"Unexpected extra %s data: %d bytes at %x.\n",
name, php->p_filesz, php->p_vaddr);
return -1;
} else {
elf_copy_segment(fd, php, dest);
return 0;
}
}

/* The structure *firmware must be pre-initialised to zero, then optionally
* tracing and VCD information may be added.
*/
Expand Down Expand Up @@ -371,53 +368,24 @@ elf_read_firmware(
#endif
if (php->p_type != PT_LOAD || php->p_filesz == 0)
continue;
if (php->p_vaddr < 0x800000) {
if (php->p_paddr < 0x800000) {
/* Explicit flash section. Load it. */

if (elf_handle_segment(fd, php, &firmware->flash, "Flash"))
if (elf_copy_segment(fd, php, &firmware->flash, &firmware->flashsize))
continue;
firmware->flashsize = php->p_filesz;
firmware->flashbase = php->p_vaddr;
} else if (php->p_vaddr < 0x810000) {
/* Data space. If there are initialised variables, treat
* them as extra initialised flash. The C startup function
* understands that and will copy them to RAM.
*/

if (firmware->flash) {
uint8_t *where;

firmware->flash = realloc(firmware->flash,
firmware->flashsize + php->p_filesz);
if (!firmware->flash)
return -1;
where = firmware->flash + firmware->flashsize;
elf_copy_segment(fd, php, &where);
firmware->flashsize += php->p_filesz;
} else {
/* If this ever happens, add a second pass. */

AVR_LOG(NULL, LOG_ERROR,
"Initialialised data but no flash (%d bytes at %x)!\n",
php->p_filesz, php->p_vaddr);
return -1;
}
} else if (php->p_vaddr < 0x820000) {
} else if (php->p_paddr < 0x810000) {
AVR_LOG(NULL, LOG_ERROR, "ELF file attempts to load data into AVR SRAM!\n");
return -1;
} else if (php->p_paddr < 0x820000) {
/* EEPROM. */

if (elf_handle_segment(fd, php, &firmware->eeprom, "EEPROM"))
if (elf_copy_segment(fd, php, &firmware->eeprom, &firmware->eesize))
continue;
firmware->eesize = php->p_filesz;
} else if (php->p_vaddr < 0x830000) {
} else if (php->p_paddr < 0x830000) {
/* Fuses. */

if (elf_handle_segment(fd, php, &firmware->fuse, "Fuses"))
if (elf_copy_segment(fd, php, &firmware->fuse, &firmware->fusesize))
continue;
firmware->fusesize = php->p_filesz;
} else if (php->p_vaddr < 0x840000) {
} else if (php->p_paddr < 0x840000) {
/* Lock bits. */

elf_handle_segment(fd, php, &firmware->lockbits, "Lock bits");
elf_copy_segment(fd, php, &firmware->lockbits, &firmware->locksize);
}
}

Expand Down Expand Up @@ -463,9 +431,6 @@ elf_read_firmware(
ELF32_ST_TYPE(sym.st_info) == STT_OBJECT) {
const char * name = elf_strptr(elf, shdr.sh_link, sym.st_name);

// if its a bootloader, this symbol will be the entry point we need
if (!strcmp(name, "__vectors"))
firmware->flashbase = sym.st_value;
avr_symbol_t * s = malloc(sizeof(avr_symbol_t) + strlen(name) + 1);
strcpy((char*)s->symbol, name);
s->addr = sym.st_value;
Expand Down
2 changes: 1 addition & 1 deletion simavr/sim/sim_elf.h
Expand Up @@ -64,7 +64,6 @@ typedef struct elf_firmware_t {
uint16_t command_register_addr;
uint16_t console_register_addr;

uint32_t flashbase; // base address
uint8_t * flash;
uint32_t flashsize;
uint32_t datasize;
Expand All @@ -75,6 +74,7 @@ typedef struct elf_firmware_t {
uint8_t * fuse;
uint32_t fusesize;
uint8_t * lockbits;
uint32_t locksize;

#if ELF_SYMBOLS
avr_symbol_t ** symbol;
Expand Down
25 changes: 12 additions & 13 deletions simavr/sim/sim_hex.c
Expand Up @@ -220,19 +220,18 @@ sim_setup_firmware(const char * filename, uint32_t loadBase,

for (int ci = 0; ci < cnt; ci++) {
if (chunk[ci].baseaddr < (1*1024*1024)) {
if (fp->flash) {
printf("Ignoring chunk %d, "
"possible flash redefinition %08x, %d\n",
ci, chunk[ci].baseaddr, chunk[ci].size);
free(chunk[ci].data);
chunk[ci].data = NULL;
continue;
}
fp->flash = chunk[ci].data;
fp->flashsize = chunk[ci].size;
fp->flashbase = chunk[ci].baseaddr;
printf("Load HEX flash %08x, %d at %08x\n",
fp->flashbase, fp->flashsize, fp->flashbase);
uint32_t segment_end = chunk[ci].baseaddr + chunk[ci].size;
if (fp->flashsize<segment_end)
/* Allocate enough memory to load the segment.
Note that realloc does the right thing in
case fp->flash==NULL (i.o.w. no memory allocated yet) */
fp->flash = realloc(fp->flash,segment_end);
memcpy(fp->flash + chunk[ci].baseaddr, chunk[ci].data, chunk[ci].size);
free(chunk[ci].data);
chunk[ci].data = NULL;
fp->flashsize = segment_end;
printf("Load HEX flash addr=%08x sz=%d\n",
chunk[ci].baseaddr, fp->flashsize);
} else if (chunk[ci].baseaddr >= AVR_SEGMENT_OFFSET_EEPROM ||
(chunk[ci].baseaddr + loadBase) >=
AVR_SEGMENT_OFFSET_EEPROM) {
Expand Down