From: Manfred Steiner <sx@htl-kaindorf.at>
Date: Fri, 2 Dec 2022 10:21:35 +0000 (+0100)
Subject: simavr is now able to load multiple overlapping flash sections
X-Git-Url: https://git.htl-mechatronik.at/public/?a=commitdiff_plain;h=bbb0d352a35fee2d22bb2eb9ffbfd5bb62280127;p=sx%2Fsimavr.git

simavr is now able to load multiple overlapping flash sections
---

diff --git a/simavr/sim/sim_elf.c b/simavr/sim/sim_elf.c
index 371b9db..246af57 100644
--- a/simavr/sim/sim_elf.c
+++ b/simavr/sim/sim_elf.c
@@ -279,7 +279,7 @@ elf_copy_segment(int fd, Elf32_Phdr *php, uint8_t **dest)
 
 	if (*dest == NULL)
 		*dest = malloc(php->p_filesz);
-	if (!*dest)
+	if (*dest == NULL)
 		return -1;
 
 	lseek(fd, php->p_offset, SEEK_SET);
@@ -291,8 +291,6 @@ elf_copy_segment(int fd, Elf32_Phdr *php, uint8_t **dest)
 				rv, php->p_filesz, php->p_vaddr, php->p_offset);
 		return -1;
 	}
-	AVR_LOG(NULL, LOG_DEBUG, "Loaded %d bytes at 0x%04x\n",
-			php->p_filesz, php->p_vaddr);
 	return 0;
 }
 
@@ -304,10 +302,15 @@ elf_handle_segment(int fd, Elf32_Phdr *php, uint8_t **dest, const char *name)
 				"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;
+	} else if (elf_copy_segment(fd, php, dest)) {
+		AVR_LOG(NULL, LOG_ERROR,
+				"copy segment fails (%d bytes at %x)!\n",
+				name, php->p_filesz, php->p_vaddr);
+		return -1;
 	}
+	AVR_LOG(NULL, LOG_DEBUG, "  Loaded %d (0x%0x) bytes (section offset 0x%04x)\n",
+		php->p_filesz, php->p_filesz, php->p_vaddr);
+	return 0;
 }
 
 /* The structure *firmware must be pre-initialised to zero, then optionally
@@ -363,6 +366,8 @@ elf_read_firmware(
 		return -1;
 	}
 
+	int isTextSectionLoaded = 0;
+	uint32_t offset = 0;
 	for (i = 0; i < (int)ph_count; ++i, ++php) {
 #if 0
 		printf("Header %d type %d addr %x/%x size %d/%d flags %x\n",
@@ -371,62 +376,90 @@ elf_read_firmware(
 #endif
 		if (php->p_type != PT_LOAD || php->p_filesz == 0)
 			continue;
-		if (php->p_vaddr < 0x800000) {
-			/* Explicit flash section. Load it. */
+
+		if (php->p_vaddr < 0x810000) {	// .text or .data section
 			if (firmware->flash == NULL) {
+				avr_t *avr = avr_make_mcu_by_name(firmware->mmcu);
 				firmware->flashbase = 0;
-				firmware->flashsize = php->p_vaddr + php->p_filesz;
-				firmware->flash = malloc(firmware->flashsize);
-				memset(firmware->flash, 0xff, firmware->flashsize);
-				uint8_t *where = firmware->flash + php->p_vaddr;
-				elf_copy_segment(fd, php, &where);
-
-			} else {
-				long gapSize = php->p_vaddr - firmware->flashsize;
-				if (gapSize < 0) {
-					AVR_LOG(NULL, LOG_ERROR,
-						"Overlapping flash sections!\n");
+				firmware->flashsize = avr->flashend + 1;
+				if (firmware->flashsize <= 0) {
+					AVR_LOG(NULL, LOG_ERROR, "cannot initialize flash, unknown size!\n");
 					return -1;
-				} else 	if (gapSize > 0) {
-					firmware->flash = realloc(firmware->flash,php->p_vaddr );
-					uint8_t *where = firmware->flash + firmware->flashsize;
-					memset(where, 0xff, gapSize);
-					firmware->flashsize = php->p_vaddr;
 				}
-				firmware->flash = realloc(firmware->flash,
-										  firmware->flashsize + php->p_filesz);
-				if (!firmware->flash)
+				firmware->flash = (uint8_t *)malloc(firmware->flashsize);
+				if (firmware->flash == NULL) {
+					AVR_LOG(NULL, LOG_ERROR, "malloc flash memory fails!\n");
 					return -1;
-				uint8_t *where = firmware->flash + firmware->flashsize;
-				elf_copy_segment(fd, php, &where);
-				firmware->flashsize += php->p_filesz;
+				}
+				memset(firmware->flash, 0xff, firmware->flashsize);
+				AVR_LOG(NULL, LOG_DEBUG, "flash initialization with 0xff (%d bytes)\n", firmware->flashsize);
 			}
 
-		} 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 != NULL) {
-				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);
-				AVR_LOG(NULL, LOG_DEBUG, ".data section %d bytes copied to flash at 0x%04x\n", php->p_filesz, firmware->flashsize);
-				firmware->flashsize += php->p_filesz;
-			} else {
-				/* If this ever happens, add a second pass. */
-
+			if (php->p_vaddr < 0x800000) {
+				/* Explicit flash section. Load it. */
+				isTextSectionLoaded = 1;
+				offset = php->p_vaddr;
+			} else if (isTextSectionLoaded ) {
+				/* 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.
+				*/
+				isTextSectionLoaded = 0;
+			} else if (!isTextSectionLoaded) {
 				AVR_LOG(NULL, LOG_ERROR,
-						"Initialialised data but no flash (%d bytes at %x)!\n",
-						php->p_filesz, php->p_vaddr);
+						"Initialialised data but no flash (%d bytes at %x in file \"%s\")!\n",
+						php->p_filesz, php->p_vaddr, file);
 				return -1;
 			}
+			uint8_t *section = NULL;
+			if (elf_copy_segment(fd, php, &section) || section == NULL) {
+				AVR_LOG(NULL, LOG_ERROR,
+					"cannot read section (%d bytes at %x in file \"%s\")!\n",
+					php->p_filesz, php->p_vaddr, file);
+				if (section != NULL) {
+					free(section);
+					section = NULL;
+				}
+				return -1;
+			} else {
+				AVR_LOG(NULL, LOG_DEBUG, "  Loading %d (0x%0x) bytes (section offset 0x%04x) to flash address 0x%04x\n",
+					php->p_filesz, php->p_filesz, php->p_vaddr, offset);
+			}
+
+			uint32_t overlapping_start = UINT32_MAX;
+			uint32_t overlapping_end = 0;
+			int overlapping_error = 0;
+			for (uint32_t addr = offset; addr < (offset + php->p_filesz); addr++) {
+				if (addr >= firmware->flashsize) {
+					AVR_LOG(NULL, LOG_ERROR,
+						"section out of flash address range (%d bytes at %x in file \"%s\")!\n",
+						php->p_filesz, php->p_vaddr, file);
+					return -1;
+				}
+				if (firmware->flash[addr] != 0xff) {
+					overlapping_start = addr < overlapping_start ? addr : overlapping_start;
+					overlapping_end = addr > overlapping_end ? addr : overlapping_end;
+					if (firmware->flash[addr] != section[addr - offset]) {
+						overlapping_error = 1;
+					}
+				}
+				firmware->flash[addr] = section[addr - offset];
+			}
+			offset += php->p_filesz;
+			if (overlapping_start <= overlapping_end) {
+				if (overlapping_error) {
+					AVR_LOG(NULL, LOG_OUTPUT,
+						"WARNING: overlapping section with different content (0x%04x..0x%04x)!\n",
+						overlapping_start, overlapping_end);
+				} else {
+					AVR_LOG(NULL, LOG_OUTPUT,
+						"overlapping section with same content (0x%04x..0x%04x)!\n",
+						overlapping_start, overlapping_end);
+				}
+			}
+			free(section);
+			section = NULL;
+
 		} else if (php->p_vaddr < 0x820000) {
 			/* EEPROM. */
 
@@ -446,27 +479,6 @@ elf_read_firmware(
 		}
 	}
 
-	avr_t *avr = avr_make_mcu_by_name(firmware->mmcu);
-	if (avr != NULL) {
-		if (firmware->flash == NULL) {
-			AVR_LOG(NULL, LOG_ERROR,
-					"no flash!\n",
-					php->p_filesz, php->p_vaddr);
-			return -1;
-		} else {
-			long gapSize = avr->flashend + 1 - firmware->flashsize;
-			if (gapSize < 0) {
-				AVR_LOG(NULL, LOG_ERROR,
-					"flash size 0x%04x too large for selected mmcu device %s!\n", firmware->flashsize, firmware->mmcu);
-				return -1;
-			} else 	if (gapSize > 0) {
-				firmware->flash = realloc(firmware->flash, firmware->flashsize + gapSize);
-				uint8_t *where = firmware->flash + firmware->flashsize;
-				memset(where, 0xff, gapSize);
-				firmware->flashsize += gapSize;
-			}
-		}
-	}
 	/* Scan the section table for .mmcu magic and symbols. */
 
 	while ((scn = elf_nextscn(elf, scn)) != NULL) {
@@ -539,9 +551,9 @@ elf_read_firmware(
 		}
 #endif // ELF_SYMBOLS
 	}
-	if (firmware->flash != NULL) {
-		AVR_LOG(NULL, LOG_DEBUG, "end of flash initialization (size=0x%04x)\n", firmware->flashsize);
-	}
+	// if (firmware->flash != NULL) {
+	// 	AVR_LOG(NULL, LOG_DEBUG, "end of flash initialization (size=0x%04x)\n", firmware->flashsize);
+	// }
 	elf_end(elf);
 	close(fd);
 	return 0;