The register variable form of gd cannot be used with LTO (GCC bug
68384). The LTO link fails with "multiple definition of `r2'". In LTO
builds define gd through inline assembly accessors that read and write
r2, following ARM and RISC-V. Other builds keep the register variable,
and set_gd() assigns it.

Every assignment to gd has to go through set_gd() for that to work, so
convert all PowerPC ones, including those in common code, and give
PowerPC its own arch_setup_gd() in the new arch/powerpc/lib/gd.c
instead of the weak one, which assigns gd. Rename the gd parameter of
the SPL board_init_r() functions, which the gd macro would otherwise
replace.

Signed-off-by: Josef Schlehofer <[email protected]>
Reviewed-by: Marek Mojík <[email protected]>
Cc: Marek Behún <[email protected]>
Cc: Christophe Leroy <[email protected]>
Cc: Mario Six <[email protected]>
Cc: Priyanka Jain <[email protected]>
Cc: Qiang Zhao <[email protected]>
Cc: Peng Fan <[email protected]>
Cc: Shengzhou Liu <[email protected]>
Cc: Marek Mojík <[email protected]>
---
(no changes since v1)

The mpc83xx and common/spl/spl.c hunks change no build: mpc83xx cannot
enable LTO and no PowerPC board builds common/spl/spl.c. They are
converted so that no PowerPC code assigns gd directly.

Unlike on ARM and RISC-V, set_gd() assigns the register variable in
builds without LTO. GCC requires inline assembly that accesses a
global register variable to declare that access through constraints.

In the T-series SPLs, the board_init_r() bodies now use the global gd
instead of the parameter. relocate_code() passes the CONFIG_SPL_GD_ADDR
that board_init_f() has already set as gd and leaves r2 alone, so the
pointer is the same.

checkpatch asks for IS_ENABLED() on the #if lines in common/board_r.c,
common/init/board_init.c and common/spl/spl.c. They choose how gd is
set per architecture: set_gd() exists only on ARM, RISC-V and PowerPC,
so IS_ENABLED() cannot replace them. The new arch/powerpc/lib/gd.c is
covered by the POWERPC entry in MAINTAINERS.
---
 arch/powerpc/cpu/mpc83xx/cpu_init.c       |  3 +--
 arch/powerpc/cpu/mpc83xx/spl_minimal.c    |  3 +--
 arch/powerpc/cpu/mpc85xx/cpu_init_early.c |  3 +--
 arch/powerpc/include/asm/global_data.h    | 30 +++++++++++++++++++++++
 arch/powerpc/lib/Makefile                 |  1 +
 arch/powerpc/lib/gd.c                     | 14 +++++++++++
 board/nxp/p1010rdb/spl_minimal.c          |  2 +-
 board/nxp/p1_p2_rdb_pc/spl_minimal.c      |  2 +-
 board/nxp/t102xrdb/spl.c                  |  4 +--
 board/nxp/t104xrdb/spl.c                  |  4 +--
 board/nxp/t208xqds/spl.c                  |  4 +--
 board/nxp/t208xrdb/spl.c                  |  4 +--
 board/nxp/t4rdb/spl.c                     |  4 +--
 common/board_r.c                          |  2 +-
 common/init/board_init.c                  |  9 ++++---
 common/spl/spl.c                          |  4 ++-
 16 files changed, 69 insertions(+), 24 deletions(-)
 create mode 100644 arch/powerpc/lib/gd.c

diff --git a/arch/powerpc/cpu/mpc83xx/cpu_init.c 
b/arch/powerpc/cpu/mpc83xx/cpu_init.c
index 340f9a0da56..5618b79a78b 100644
--- a/arch/powerpc/cpu/mpc83xx/cpu_init.c
+++ b/arch/powerpc/cpu/mpc83xx/cpu_init.c
@@ -139,8 +139,7 @@ void cpu_init_f (volatile immap_t * im)
 #endif
                0;
 
-       /* Pointer is writable since we allocated a register for it */
-       gd = (gd_t *)SYS_INIT_SP_ADDR;
+       set_gd((gd_t *)SYS_INIT_SP_ADDR);
 
        /* global data region was cleared in start.S */
 
diff --git a/arch/powerpc/cpu/mpc83xx/spl_minimal.c 
b/arch/powerpc/cpu/mpc83xx/spl_minimal.c
index 7036e3fae0c..1d48428573d 100644
--- a/arch/powerpc/cpu/mpc83xx/spl_minimal.c
+++ b/arch/powerpc/cpu/mpc83xx/spl_minimal.c
@@ -25,8 +25,7 @@ DECLARE_GLOBAL_DATA_PTR;
  */
 void cpu_init_f (volatile immap_t * im)
 {
-       /* Pointer is writable since we allocated a register for it */
-       gd = (gd_t *)SYS_INIT_SP_ADDR;
+       set_gd((gd_t *)SYS_INIT_SP_ADDR);
 
        /* global data region was cleared in start.S */
 
diff --git a/arch/powerpc/cpu/mpc85xx/cpu_init_early.c 
b/arch/powerpc/cpu/mpc85xx/cpu_init_early.c
index 75bfc07fe97..fb3d662c9d3 100644
--- a/arch/powerpc/cpu/mpc85xx/cpu_init_early.c
+++ b/arch/powerpc/cpu/mpc85xx/cpu_init_early.c
@@ -95,8 +95,7 @@ void cpu_init_early_f(void *fdt)
        int i;
 #endif
 
-       /* Pointer is writable since we allocated a register for it */
-       gd = (gd_t *)SYS_INIT_SP_ADDR;
+       set_gd((gd_t *)SYS_INIT_SP_ADDR);
 
        /* gd area was zeroed during startup */
 
diff --git a/arch/powerpc/include/asm/global_data.h 
b/arch/powerpc/include/asm/global_data.h
index 26cbc7854d9..d8aefd2ca0a 100644
--- a/arch/powerpc/include/asm/global_data.h
+++ b/arch/powerpc/include/asm/global_data.h
@@ -93,8 +93,38 @@ struct arch_global_data {
 
 #include <asm-generic/global_data.h>
 
+#ifdef LTO_ENABLE
+
+#define DECLARE_GLOBAL_DATA_PTR
+#define gd     get_gd()
+
+static inline gd_t *get_gd(void)
+{
+       gd_t *gd_ptr;
+
+       __asm__ volatile("mr %0, 2\n" : "=r" (gd_ptr));
+
+       return gd_ptr;
+}
+
+static inline void set_gd(gd_t *gd_ptr)
+{
+       __asm__ volatile("mr 2, %0\n" : : "r" (gd_ptr));
+}
+
+#else
+
 #define DECLARE_GLOBAL_DATA_PTR     register gd_t *gd asm ("r2")
 
+/*
+ * GCC does not see an asm statement write r2 and assumes that the
+ * register variable is unchanged, so it may move gd accesses across the
+ * write. Assign the variable itself.
+ */
+#define set_gd(gd_ptr) (gd = (gd_ptr))
+
+#endif
+
 #include <asm/u-boot.h>
 
 #endif /* __ASM_GBL_DATA_H */
diff --git a/arch/powerpc/lib/Makefile b/arch/powerpc/lib/Makefile
index c0caa703caa..a3048975ae7 100644
--- a/arch/powerpc/lib/Makefile
+++ b/arch/powerpc/lib/Makefile
@@ -18,6 +18,7 @@ endif
 endif
 
 obj-y  += bdinfo.o
+obj-y  += gd.o
 
 ifdef MINIMAL
 obj-y += cache.o time.o
diff --git a/arch/powerpc/lib/gd.c b/arch/powerpc/lib/gd.c
new file mode 100644
index 00000000000..d36f784fbf7
--- /dev/null
+++ b/arch/powerpc/lib/gd.c
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2026 Josef Schlehofer <[email protected]>
+ */
+
+#include <init.h>
+#include <asm/global_data.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+void arch_setup_gd(gd_t *new_gd)
+{
+       set_gd(new_gd);
+}
diff --git a/board/nxp/p1010rdb/spl_minimal.c b/board/nxp/p1010rdb/spl_minimal.c
index 8cd79c6fb5f..33930fef8d9 100644
--- a/board/nxp/p1010rdb/spl_minimal.c
+++ b/board/nxp/p1010rdb/spl_minimal.c
@@ -45,7 +45,7 @@ void board_init_f(ulong bootflag)
        relocate_code(CONFIG_SPL_RELOC_STACK, 0, CONFIG_SPL_RELOC_TEXT_BASE);
 }
 
-void board_init_r(gd_t *gd, ulong dest_addr)
+void board_init_r(gd_t *dummy, ulong dest_addr)
 {
        puts("\nSecond program loader running in sram...");
        nand_boot();
diff --git a/board/nxp/p1_p2_rdb_pc/spl_minimal.c 
b/board/nxp/p1_p2_rdb_pc/spl_minimal.c
index 511bcf5506b..960b2c41457 100644
--- a/board/nxp/p1_p2_rdb_pc/spl_minimal.c
+++ b/board/nxp/p1_p2_rdb_pc/spl_minimal.c
@@ -43,7 +43,7 @@ void board_init_f(ulong bootflag)
        relocate_code(CONFIG_SPL_RELOC_STACK, 0, CONFIG_SPL_RELOC_TEXT_BASE);
 }
 
-void board_init_r(gd_t *gd, ulong dest_addr)
+void board_init_r(gd_t *dummy, ulong dest_addr)
 {
        puts("\nSecond program loader running in sram...");
        nand_boot();
diff --git a/board/nxp/t102xrdb/spl.c b/board/nxp/t102xrdb/spl.c
index de6cdda194e..f527f62a5dd 100644
--- a/board/nxp/t102xrdb/spl.c
+++ b/board/nxp/t102xrdb/spl.c
@@ -58,7 +58,7 @@ void board_init_f(ulong bootflag)
        memcpy((void *)CONFIG_SPL_GD_ADDR, (void *)gd, sizeof(gd_t));
 
        /* Update GD pointer */
-       gd = (gd_t *)(CONFIG_SPL_GD_ADDR);
+       set_gd((gd_t *)(CONFIG_SPL_GD_ADDR));
 
        console_init_f();
 
@@ -87,7 +87,7 @@ void board_init_f(ulong bootflag)
        relocate_code(CONFIG_SPL_RELOC_STACK, (gd_t *)CONFIG_SPL_GD_ADDR, 0x0);
 }
 
-void board_init_r(gd_t *gd, ulong dest_addr)
+void board_init_r(gd_t *dummy, ulong dest_addr)
 {
        struct bd_info *bd;
 
diff --git a/board/nxp/t104xrdb/spl.c b/board/nxp/t104xrdb/spl.c
index e02a1f95d4c..0310baa381c 100644
--- a/board/nxp/t104xrdb/spl.c
+++ b/board/nxp/t104xrdb/spl.c
@@ -55,7 +55,7 @@ void board_init_f(ulong bootflag)
        memcpy((void *)CONFIG_SPL_GD_ADDR, (void *)gd, sizeof(gd_t));
 
        /* Update GD pointer */
-       gd = (gd_t *)(CONFIG_SPL_GD_ADDR);
+       set_gd((gd_t *)(CONFIG_SPL_GD_ADDR));
 
 #ifdef CONFIG_DEEP_SLEEP
        /* disable the console if boot from deep sleep */
@@ -78,7 +78,7 @@ void board_init_f(ulong bootflag)
        relocate_code(CONFIG_SPL_RELOC_STACK, (gd_t *)CONFIG_SPL_GD_ADDR, 0x0);
 }
 
-void board_init_r(gd_t *gd, ulong dest_addr)
+void board_init_r(gd_t *dummy, ulong dest_addr)
 {
        struct bd_info *bd;
 
diff --git a/board/nxp/t208xqds/spl.c b/board/nxp/t208xqds/spl.c
index 44ad4e68d9f..f8c641a9c64 100644
--- a/board/nxp/t208xqds/spl.c
+++ b/board/nxp/t208xqds/spl.c
@@ -73,7 +73,7 @@ void board_init_f(ulong bootflag)
        memcpy((void *)CONFIG_SPL_GD_ADDR, (void *)gd, sizeof(gd_t));
 
        /* Update GD pointer */
-       gd = (gd_t *)(CONFIG_SPL_GD_ADDR);
+       set_gd((gd_t *)(CONFIG_SPL_GD_ADDR));
 
        console_init_f();
 
@@ -96,7 +96,7 @@ void board_init_f(ulong bootflag)
        relocate_code(CONFIG_SPL_RELOC_STACK, (gd_t *)CONFIG_SPL_GD_ADDR, 0x0);
 }
 
-void board_init_r(gd_t *gd, ulong dest_addr)
+void board_init_r(gd_t *dummy, ulong dest_addr)
 {
        struct bd_info *bd;
 
diff --git a/board/nxp/t208xrdb/spl.c b/board/nxp/t208xrdb/spl.c
index df3b9c6fe40..4b1ab3618c3 100644
--- a/board/nxp/t208xrdb/spl.c
+++ b/board/nxp/t208xrdb/spl.c
@@ -33,7 +33,7 @@ void board_init_f(ulong bootflag)
        memcpy((void *)CONFIG_SPL_GD_ADDR, (void *)gd, sizeof(gd_t));
 
        /* Update GD pointer */
-       gd = (gd_t *)(CONFIG_SPL_GD_ADDR);
+       set_gd((gd_t *)(CONFIG_SPL_GD_ADDR));
 
        console_init_f();
 
@@ -56,7 +56,7 @@ void board_init_f(ulong bootflag)
        relocate_code(CONFIG_SPL_RELOC_STACK, (gd_t *)CONFIG_SPL_GD_ADDR, 0x0);
 }
 
-void board_init_r(gd_t *gd, ulong dest_addr)
+void board_init_r(gd_t *dummy, ulong dest_addr)
 {
        struct bd_info *bd;
 
diff --git a/board/nxp/t4rdb/spl.c b/board/nxp/t4rdb/spl.c
index 9d2472dec25..441015bf5a4 100644
--- a/board/nxp/t4rdb/spl.c
+++ b/board/nxp/t4rdb/spl.c
@@ -39,7 +39,7 @@ void board_init_f(ulong bootflag)
        memcpy((void *)CONFIG_SPL_GD_ADDR, (void *)gd, sizeof(gd_t));
 
        /* Update GD pointer */
-       gd = (gd_t *)(CONFIG_SPL_GD_ADDR);
+       set_gd((gd_t *)(CONFIG_SPL_GD_ADDR));
 
        /* compiler optimization barrier needed for GCC >= 3.4 */
        __asm__ __volatile__("" : : : "memory");
@@ -59,7 +59,7 @@ void board_init_f(ulong bootflag)
        relocate_code(CONFIG_SPL_RELOC_STACK, (gd_t *)CONFIG_SPL_GD_ADDR, 0x0);
 }
 
-void board_init_r(gd_t *gd, ulong dest_addr)
+void board_init_r(gd_t *dummy, ulong dest_addr)
 {
        struct bd_info *bd;
 
diff --git a/common/board_r.c b/common/board_r.c
index a397e20959d..7a9de6ba5f3 100644
--- a/common/board_r.c
+++ b/common/board_r.c
@@ -796,7 +796,7 @@ void board_init_r(gd_t *new_gd, ulong dest_addr)
        if (CONFIG_IS_ENABLED(X86_64) && !IS_ENABLED(CONFIG_EFI_APP))
                arch_setup_gd(new_gd);
 
-#if defined(CONFIG_RISCV)
+#if defined(CONFIG_RISCV) || defined(CONFIG_PPC)
        set_gd(new_gd);
 #elif !defined(CONFIG_X86) && !defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
        gd = new_gd;
diff --git a/common/init/board_init.c b/common/init/board_init.c
index 2a6f39f51ad..2faea5432c0 100644
--- a/common/init/board_init.c
+++ b/common/init/board_init.c
@@ -14,15 +14,16 @@
 DECLARE_GLOBAL_DATA_PTR;
 
 /*
- * Unfortunately x86, ARM and RISC-V can't compile this code as gd is defined
- * as macro and cannot be assigned.
+ * x86, RISC-V and PowerPC provide their own arch_setup_gd(), and ARM does
+ * not call it.
  */
-#if !defined(CONFIG_X86) && !defined(CONFIG_ARM) && !defined(CONFIG_RISCV)
+#if !defined(CONFIG_X86) && !defined(CONFIG_ARM) && !defined(CONFIG_RISCV) && \
+       !defined(CONFIG_PPC)
 __weak void arch_setup_gd(struct global_data *gd_ptr)
 {
        gd = gd_ptr;
 }
-#endif /* !CONFIG_X86 && !CONFIG_ARM */
+#endif /* !CONFIG_X86 && !CONFIG_ARM && !CONFIG_RISCV && !CONFIG_PPC */
 
 /**
  * This function is called from board_init_f_init_reserve to set up
diff --git a/common/spl/spl.c b/common/spl/spl.c
index 6c9a1a1cbcc..81aec2b1108 100644
--- a/common/spl/spl.c
+++ b/common/spl/spl.c
@@ -956,7 +956,9 @@ ulong spl_relocate_stack_gd(void)
 #if CONFIG_IS_ENABLED(LOG)
        log_fixup_for_gd_move(new_gd);
 #endif
-#if !defined(CONFIG_ARM) && !defined(CONFIG_RISCV)
+#if defined(CONFIG_PPC)
+       set_gd(new_gd);
+#elif !defined(CONFIG_ARM) && !defined(CONFIG_RISCV)
        gd = new_gd;
 #endif
        return ptr;
-- 
2.54.0 (Apple Git-157)

Reply via email to