From 0804d18a023575ec65e5139703f600fcb0391c89 Mon Sep 17 00:00:00 2001
From: Georg-Johann Lay <avr@gjlay.de>
Date: Tue, 12 Dec 2023 11:29:16 +0000
Subject: [PATCH] Support rodata in flash for more AVR devices

  PR 31124
  * Makefile.am (ALL_EMULATION_SOURCES): Add eavrxmega2_flmap.c and eavrxmega4_flmap.c.
  * Makefile.in: Regenerate.
  * configure.tgt: Add eavrxmega2_flmap and eavrxmega4_flmap to avr's targ_extra_emuls list.
  * emulparams/avrxmega2.sh (MAYBE_FLMAP): Define.
  * emulparams/avrxmega2_flmap.sh: New file.
  * emulparams/avrxmega4.sh (MAYBE_FLMAP): Define.
  * emulparams/avrxmega4_flmap.sh: New file.
  * scripttempl/avr.sc: Add support for HAVE_FLMAP.
---
 ld/ChangeLog                     |  14 ++++
 ld/Makefile.am                   |   2 +
 ld/Makefile.in                   |   4 ++
 ld/configure.tgt                 |   2 +-
 ld/emulparams/avrxmega2.sh       |   2 +
 ld/emulparams/avrxmega2_flmap.sh |  31 ++++++++
 ld/emulparams/avrxmega4.sh       |   2 +
 ld/emulparams/avrxmega4_flmap.sh |  31 ++++++++
 ld/scripttempl/avr.sc            | 119 +++++++++++++++++++++++++++++--
 9 files changed, 202 insertions(+), 5 deletions(-)
 create mode 100644 ld/emulparams/avrxmega2_flmap.sh
 create mode 100644 ld/emulparams/avrxmega4_flmap.sh

diff --git a/ld/ChangeLog b/ld/ChangeLog
index 6834cf6d4b0..cbcb29de0a0 100644
--- a/ld/ChangeLog
+++ b/ld/ChangeLog
@@ -1,3 +1,17 @@
+2023-12-12  Georg-Johann Lay  <avr@gjlay.de>
+
+	PR 31124
+	* Makefile.am (ALL_EMULATION_SOURCES): Add eavrxmega2_flmap.c and
+	eavrxmega4_flmap.c.
+	* Makefile.in: Regenerate.
+	* configure.tgt: Add eavrxmega2_flmap and eavrxmega4_flmap to
+	avr's targ_extra_emuls list.
+	* emulparams/avrxmega2.sh (MAYBE_FLMAP): Define.
+	* emulparams/avrxmega2_flmap.sh: New file.
+	* emulparams/avrxmega4.sh (MAYBE_FLMAP): Define.
+	* emulparams/avrxmega4_flmap.sh: New file.
+	* scripttempl/avr.sc: Add support for HAVE_FLMAP.
+
 2023-11-28  Jakub Jelinek  <jakub@redhat.com>
 
 	* ldbuildid.c (generate_build_id): Use sha1_choose_process_bytes ()
diff --git a/ld/Makefile.am b/ld/Makefile.am
index 9664d4c7709..5769f3cee87 100644
--- a/ld/Makefile.am
+++ b/ld/Makefile.am
@@ -198,8 +198,10 @@ ALL_EMULATION_SOURCES = \
 	eavrtiny.c   \
 	eavrxmega1.c \
 	eavrxmega2.c \
+	eavrxmega2_flmap.c \
 	eavrxmega3.c \
 	eavrxmega4.c \
+	eavrxmega4_flmap.c \
 	eavrxmega5.c \
 	eavrxmega6.c \
 	eavrxmega7.c \
diff --git a/ld/Makefile.in b/ld/Makefile.in
index d025227971a..41b0d8cccf8 100644
--- a/ld/Makefile.in
+++ b/ld/Makefile.in
@@ -709,8 +709,10 @@ ALL_EMULATION_SOURCES = \
 	eavrtiny.c   \
 	eavrxmega1.c \
 	eavrxmega2.c \
+	eavrxmega2_flmap.c \
 	eavrxmega3.c \
 	eavrxmega4.c \
+	eavrxmega4_flmap.c \
 	eavrxmega5.c \
 	eavrxmega6.c \
 	eavrxmega7.c \
@@ -1331,8 +1333,10 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eavrtiny.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eavrxmega1.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eavrxmega2.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eavrxmega2_flmap.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eavrxmega3.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eavrxmega4.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eavrxmega4_flmap.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eavrxmega5.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eavrxmega6.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eavrxmega7.Po@am__quote@
diff --git a/ld/configure.tgt b/ld/configure.tgt
index 3aa595ef130..afd4303246e 100644
--- a/ld/configure.tgt
+++ b/ld/configure.tgt
@@ -253,7 +253,7 @@ arm*-*-haiku*)		targ_emul=armelf_haiku
 			targ_extra_emuls=armelf
 			;;
 avr-*-*)		targ_emul=avr2
-			targ_extra_emuls="avr1 avr25 avr3 avr31 avr35 avr4 avr5 avr51 avr6 avrxmega1 avrxmega2 avrxmega3 avrxmega4 avrxmega5 avrxmega6 avrxmega7 avrtiny"
+			targ_extra_emuls="avr1 avr25 avr3 avr31 avr35 avr4 avr5 avr51 avr6 avrxmega1 avrxmega2 avrxmega2_flmap avrxmega3 avrxmega4 avrxmega4_flmap avrxmega5 avrxmega6 avrxmega7 avrtiny"
 			;;
 bfin-*-elf | bfin-*-rtems*)
 			targ_emul=elf32bfin
diff --git a/ld/emulparams/avrxmega2.sh b/ld/emulparams/avrxmega2.sh
index 3f3d86518de..d8c7e194a28 100644
--- a/ld/emulparams/avrxmega2.sh
+++ b/ld/emulparams/avrxmega2.sh
@@ -18,3 +18,5 @@ FUSE_LENGTH=1K
 LOCK_LENGTH=1K
 SIGNATURE_LENGTH=1K
 USER_SIGNATURE_LENGTH=1K
+
+MAYBE_FLMAP=1
diff --git a/ld/emulparams/avrxmega2_flmap.sh b/ld/emulparams/avrxmega2_flmap.sh
new file mode 100644
index 00000000000..1e5adbe9fc2
--- /dev/null
+++ b/ld/emulparams/avrxmega2_flmap.sh
@@ -0,0 +1,31 @@
+ARCH=avr:102
+MACHINE=
+SCRIPT_NAME=avr
+OUTPUT_FORMAT="elf32-avr"
+MAXPAGESIZE=1
+EMBEDDED=yes
+TEMPLATE_NAME=elf
+
+TEXT_LENGTH=1024K
+DATA_ORIGIN=0x802000
+DATA_LENGTH=0xffa0
+EXTRA_EM_FILE=avrelf
+
+FUSE_NAME=fuse
+
+EEPROM_LENGTH=64K
+FUSE_LENGTH=1K
+LOCK_LENGTH=1K
+SIGNATURE_LENGTH=1K
+USER_SIGNATURE_LENGTH=1K
+
+MAYBE_FLMAP=1
+
+# Above is a copy of the contents of avrxmega2.sh
+
+HAVE_FLMAP=1
+RODATA_VMA=0xa00000
+RODATA_LENGTH=32K
+RODATA_LDS_OFFSET=0x8000
+RODATA_FLASH_START=32K
+FLMAP_MASK=0x1
diff --git a/ld/emulparams/avrxmega4.sh b/ld/emulparams/avrxmega4.sh
index d7b377135ee..787e516ee9a 100644
--- a/ld/emulparams/avrxmega4.sh
+++ b/ld/emulparams/avrxmega4.sh
@@ -18,3 +18,5 @@ FUSE_LENGTH=1K
 LOCK_LENGTH=1K
 SIGNATURE_LENGTH=1K
 USER_SIGNATURE_LENGTH=1K
+
+MAYBE_FLMAP=1
diff --git a/ld/emulparams/avrxmega4_flmap.sh b/ld/emulparams/avrxmega4_flmap.sh
new file mode 100644
index 00000000000..c2f6b9ce0b4
--- /dev/null
+++ b/ld/emulparams/avrxmega4_flmap.sh
@@ -0,0 +1,31 @@
+ARCH=avr:104
+MACHINE=
+SCRIPT_NAME=avr
+OUTPUT_FORMAT="elf32-avr"
+MAXPAGESIZE=1
+EMBEDDED=yes
+TEMPLATE_NAME=elf
+
+TEXT_LENGTH=1024K
+DATA_ORIGIN=0x802000
+DATA_LENGTH=0xffa0
+EXTRA_EM_FILE=avrelf
+
+FUSE_NAME=fuse
+
+EEPROM_LENGTH=64K
+FUSE_LENGTH=1K
+LOCK_LENGTH=1K
+SIGNATURE_LENGTH=1K
+USER_SIGNATURE_LENGTH=1K
+
+MAYBE_FLMAP=1
+
+# Above is a copy of the contents of avrxmega4.sh
+
+HAVE_FLMAP=1
+RODATA_VMA=0xa00000
+RODATA_LENGTH=32K
+RODATA_LDS_OFFSET=0x8000
+RODATA_FLASH_START=96K
+FLMAP_MASK=0x3
diff --git a/ld/scripttempl/avr.sc b/ld/scripttempl/avr.sc
index 61cccfccde8..9f2977cfd6d 100644
--- a/ld/scripttempl/avr.sc
+++ b/ld/scripttempl/avr.sc
@@ -5,9 +5,9 @@
 # notice and this notice are preserved.
 
 # RODATA_PM_OFFSET
-#         If empty, .rodata sections will be part of .data.  This is for
-#         devices where it is not possible to use LD* instructions to read
-#         from flash.
+#         If empty and not HAVE_FLMAP, .rodata sections will be part of .data.
+#         This is for devices where it is not possible to use LD* instructions
+#         to read from flash.
 #
 #         If non-empty, .rodata is not part of .data and the .rodata
 #         objects are assigned addresses at an offest of RODATA_PM_OFFSET.
@@ -15,6 +15,60 @@
 #         LD* instructions, provided the addresses are offset by
 #         __RODATA_PM_OFFSET__ (which defaults to RODATA_PM_OFFSET).
 
+# HAVE_FLMAP
+#         The .rodata section is located in program memory. Devices from
+#         the AVR64* and AVR128* families (from avrxmega2 and avrxmega4)
+#         see a 32k segment of their program memory in their RAM address
+#         space.  Which 32k segment is visible is determined by the
+#         bit-field NVMCTRL_CTRLB.FLMAP.
+#         Output section .rodata is placed in MEMORY region rodata.
+#         The LMA of the .rodata section can be set by means of:
+#         * __flmap specifies which 32k block is visible in RAM.
+#         * __RODATA_FLASH_START__ specifies the byte address of the
+#           rodata LMA and is used if __flmap is undefined.
+#         * When __flmap and __RODATA_FLASH_START__ are undefined, then an
+#           emulation-specific default is used (the last 32k block).
+
+# MAYBE_FLMAP
+#         For devices from avrxmega2 and avrxmega4: The user can chose whether
+#         or not .rodata is located in flash (if HAVE_FLMAP) or located in
+#         in RAM (if not HAVE_FLMAP by means of -mrodata-in-ram).  This is
+#         achieved by new emulations avrxmega2_flmap and avrxmega4_flmap that
+#         are selected by compiler option -mno-rodata-in-ram.
+#
+#         In order to facilitate initialization of NVMCTRL_CTRLB.FLMAP in
+#         the startup code irrespective of HAVE_FLMAP, the follwing symbols
+#         are defined in order to communicate with the startup code.
+#         Notice that the hardware default for FLMAP is the last 32k block,
+#         so that explicit initialization of FLMAP is only required when the
+#         user wants to deviate from the defauls.
+#
+#         __flmap = HAVE_FLMAP
+#                   ? given by __flmap resp. __RODATA_FLASH_START__ >> 15
+#                   : 0;
+#
+#         __flmap_lsl4 = __flmap << 4;
+#
+#         __flmap_init_label = HAVE_FLMAP
+#                              ? __flmap_init_start
+#                              : __flmap_noinit_start;
+#             Supposed to be used as a jump target for RJMP so that the code
+#             can initialize FLMAP / skip initialization of FLMAP depending
+#             on the chosen emulation, and without the need to support two code
+#             versions of crt<mcu>.o for the two possble emulations.
+#
+#         __do_init_flmap = HAVE_FLMAP ? 1 : 0;
+#             Whether or not FLMAP is supposed to be initialized according
+#             to, and for the purpose of, .rodata in flash.
+#
+#         Apart from that, the compiler (device-specs actually) defines the
+#         following macros:
+#
+#         __AVR_HAVE_FLMAP__
+#             Defined if a device has the NVMCTRL_CTRLB.FLMAP bitfield
+#             *AND* if it's unknown at compile-time / assembler-time whether
+#             emulation avrxmega* is used or avrxmega*_flmap.
+
 cat <<EOF
 /* Copyright (C) 2014-2023 Free Software Foundation, Inc.
 
@@ -37,6 +91,10 @@ __LOCK_REGION_LENGTH__ = DEFINED(__LOCK_REGION_LENGTH__) ? __LOCK_REGION_LENGTH_
 __SIGNATURE_REGION_LENGTH__ = DEFINED(__SIGNATURE_REGION_LENGTH__) ? __SIGNATURE_REGION_LENGTH__ : $SIGNATURE_LENGTH;
 ${USER_SIGNATURE_LENGTH+__USER_SIGNATURE_REGION_LENGTH__ = DEFINED(__USER_SIGNATURE_REGION_LENGTH__) ? __USER_SIGNATURE_REGION_LENGTH__ : $USER_SIGNATURE_LENGTH;}
 ${RODATA_PM_OFFSET+__RODATA_PM_OFFSET__ = DEFINED(__RODATA_PM_OFFSET__) ? __RODATA_PM_OFFSET__ : $RODATA_PM_OFFSET;}
+${HAVE_FLMAP+__RODATA_VMA__ = ${RODATA_VMA};}
+${HAVE_FLMAP+__RODATA_LDS_OFFSET__ = DEFINED(__RODATA_LDS_OFFSET__) ? __RODATA_LDS_OFFSET__ : ${RODATA_LDS_OFFSET};}
+${HAVE_FLMAP+__RODATA_REGION_LENGTH__ = DEFINED(__RODATA_REGION_LENGTH__) ? __RODATA_REGION_LENGTH__ : ${RODATA_LENGTH};}
+${HAVE_FLMAP+__RODATA_ORIGIN__ = __RODATA_VMA__ + __RODATA_LDS_OFFSET__;}
 MEMORY
 {
   text   (rx)   : ORIGIN = 0, LENGTH = __TEXT_REGION_LENGTH__
@@ -46,6 +104,7 @@ ${EEPROM_LENGTH+  eeprom (rw!x) : ORIGIN = 0x810000, LENGTH = __EEPROM_REGION_LE
   lock      (rw!x) : ORIGIN = 0x830000, LENGTH = __LOCK_REGION_LENGTH__
   signature (rw!x) : ORIGIN = 0x840000, LENGTH = __SIGNATURE_REGION_LENGTH__
 ${USER_SIGNATURE_LENGTH+  user_signatures (rw!x) : ORIGIN = 0x850000, LENGTH = __USER_SIGNATURE_REGION_LENGTH__}
+${HAVE_FLMAP+  rodata (r!x) : ORIGIN = __RODATA_ORIGIN__, LENGTH = __RODATA_REGION_LENGTH__}
 }
 EOF
 
@@ -243,7 +302,7 @@ EOF
 # need .rodata to be part of .data because the compiler will use LD*
 # instructions and LD* cannot access flash.
 
-if test -z "$RODATA_PM_OFFSET" && test -n "${RELOCATING}"; then
+if test -z "$RODATA_PM_OFFSET" && test -z "${HAVE_FLMAP}" && test -n "${RELOCATING}"; then
     cat <<EOF
     *(.rodata)  /* We need to include .rodata here if gcc is used */
     *(.rodata*) /* with -fdata-sections.  */
@@ -280,6 +339,58 @@ cat <<EOF
   } ${RELOCATING+ > data}
 EOF
 
+# Devices like AVR128DA32 and AVR64DA32 see a 32 KiB block of their program
+# memory at 0x8000 (RODATA_LDS_OFFSET).  Which portion will be determined by
+# bitfield NVMCTRL_CTRLB.FLMAP.
+
+if test -n "${MAYBE_FLMAP}" && test -n "${RELOCATING}"; then
+    cat <<EOF
+
+__do_init_flmap = ${HAVE_FLMAP-0};
+
+EOF
+fi
+
+if test -z "${HAVE_FLMAP}" && test -n "${RELOCATING}"; then
+    cat <<EOF
+
+PROVIDE (__flmap_init_label = DEFINED(__flmap_noinit_start) ? __flmap_noinit_start : 0) ;
+PROVIDE (__flmap = DEFINED(__flmap) ? __flmap : 0) ;
+PROVIDE (__flmap_lsl4 = __flmap << 4) ;
+
+EOF
+fi
+
+if test -n "${HAVE_FLMAP}"; then
+    cat <<EOF
+
+${RELOCATING+
+PROVIDE (__flmap_init_label = DEFINED(__flmap_init_start) ? __flmap_init_start : 0) ;
+/* User can specify position of .rodata in flash (LMA) by supplying
+   __RODATA_FLASH_START__ or __flmap, where __flmap takes precedence. */
+__RODATA_FLASH_START__ = DEFINED(__flmap)
+   ? __flmap * 32K
+   : DEFINED(__RODATA_FLASH_START__) ? __RODATA_FLASH_START__ : ${RODATA_FLASH_START};
+ASSERT (__RODATA_FLASH_START__ % 32K == 0, \"__RODATA_FLASH_START__ must be a multiple of 32 KiB\")
+__flmap = ${FLMAP_MASK} & (DEFINED(__flmap) ? __flmap : __RODATA_FLASH_START__ >> 15);
+__RODATA_FLASH_START__ = __flmap << 15;
+__rodata_load_start = MAX (__data_load_end, __RODATA_FLASH_START__);
+PROVIDE (__flmap_lsl4 = __flmap << 4) ;
+__rodata_start = __RODATA_ORIGIN__ + __rodata_load_start - __RODATA_FLASH_START__;}
+
+  .rodata ${RELOCATING+ __rodata_start} ${RELOCATING-0} : ${RELOCATING+ AT (__rodata_load_start)}
+  {
+    *(.rodata)
+    ${RELOCATING+ *(.rodata*)}
+    ${RELOCATING+ *(.gnu.linkonce.r*)}
+    ${RELOCATING+ __rodata_end = ABSOLUTE(.) ;}
+  } ${RELOCATING+ > rodata}
+
+${RELOCATING+ __rodata_load_end = __rodata_load_start + __rodata_end - __rodata_start;}
+
+EOF
+fi
+
 if test -n "${EEPROM_LENGTH}"; then
 cat <<EOF