coreboot/src/lib/program.ld
Jeremy Compostella b7832de026 x86: Add .data section support for pre-memory stages
x86 pre-memory stages do not support the `.data` section and as a
result developers are required to include runtime initialization code
instead of relying on C global variable definition.

To illustrate the impact of this lack of `.data` section support, here
are two limitations I personally ran into:

1. The inclusion of libgfxinit in romstage for Raptor Lake has
   required some changes in libgfxinit to ensure data is initialized at
   runtime. In addition, we had to manually map some `.data` symbols in
   the `_bss` region.

2. CBFS cache is currently not supported in pre-memory stages and
   enabling it would require to add an initialization function and
   find a generic spot to call it.

Other platforms do not have that limitation. Hence, resolving it would
help to align code and reduce compilation based restriction (cf. the
use of `ENV_HAS_DATA_SECTION` compilation flag in various places of
coreboot code).

We identified three cases to consider:

1. eXecute-In-Place pre-memory stages
   - code is in SPINOR
   - data is also stored in SPINOR but must be linked in Cache-As-RAM
     and copied there at runtime

2. `bootblock` stage is a bit different as it uses Cache-As-Ram but
   the memory mapping and its entry code different

3. pre-memory stages loaded in and executed from
   Cache-As-RAM (cf. `CONFIG_NO_XIP_EARLY_STAGES`).

eXecute-In-Place pre-memory stages (#1) require the creation of a new
ELF segment as the code segment Virtual Memory Address and Load Memory
Address are identical but the data needs to be linked in
cache-As-RAM (VMA) but to be stored right after the code (LMA).

Here is the output `readelf --segments` on a `romstage.debug` ELF
binary.

    Program Headers:
      Type    Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
      LOAD    0x000080 0x02000000 0x02000000 0x21960 0x21960 R E 0x20
      LOAD    0x0219e0 0xfefb1640 0x02021960 0x00018 0x00018 RW  0x4

     Section to Segment mapping:
      Segment Sections...
       00     .text
       01     .data

Segment 0 `VirtAddr` and `PhysAddr` are at the same address while they
are totally different for the Segment 1 holding the `.data`
section. Since we need the data section `VirtAddr` to be in the
Cache-As-Ram and its `PhysAddr` right after the `.text` section, the
use of a new segment is mandatory.

`bootblock` (#2) also uses this new segment to store the data right
after the code and load it to Cache-As-RAM at runtime. However, the
code involved is different.

Not eXecute-In-Place pre-memory stages (#3) do not really need any
special work other than enabling a data section as the code and data
VMA / LMA translation vector is the same.

TEST=#1 and #2 verified on rex and qemu 32 and 64 bits:
     - The `bootblock.debug`, `romstage.debug` and
       `verstage.debug` all have data stored at the end of the `.text`
       section and code to copy the data content to the Cache-As-RAM.
     - The CBFS stages included in the final image has not improperly
       relocated any of the `.data` section symbol.
     - Test purposes global data symbols we added in bootblock,
       romstage and verstage are properly accessible at runtime
     #3: for "Intel Apollolake DDR3 RVP1" board, we verified that the
     generated romstage ELF includes a .data section similarly to a
     regular memory enabled stage.

Change-Id: I030407fcc72776e59def476daa5b86ad0495debe
Signed-off-by: Jeremy Compostella <jeremy.compostella@intel.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/77289
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
2023-09-14 21:02:07 +00:00

162 lines
3.5 KiB
Text

/* SPDX-License-Identifier: GPL-2.0-only */
#include <memlayout.h>
/* This file is included inside a SECTIONS block */
/* First we place the code and read only data (typically const declared).
* This could theoretically be placed in rom.
* The '.' in '.text . : {' is actually significant to prevent missing some
* SoC's entry points due to artificial alignment restrictions, see
* https://sourceware.org/binutils/docs/ld/Output-Section-Address.html
*/
.text . : {
_program = .;
_text = .;
#if !(ENV_X86 && ENV_BOOTBLOCK)
*(.init._start);
*(.init);
*(.init.*);
#endif
*(.text._start);
*(.text.stage_entry);
KEEP(*(.metadata_hash_anchor));
*(.text);
*(.text.*);
#if ENV_HAS_CBMEM
. = ALIGN(ARCH_POINTER_ALIGN_SIZE);
_cbmem_init_hooks = .;
KEEP(*(.rodata.cbmem_init_hooks_early));
KEEP(*(.rodata.cbmem_init_hooks));
_ecbmem_init_hooks = .;
RECORD_SIZE(cbmem_init_hooks)
#endif
. = ALIGN(ARCH_POINTER_ALIGN_SIZE);
_rsbe_init_begin = .;
KEEP(*(.rsbe_init));
_ersbe_init_begin = .;
RECORD_SIZE(rsbe_init_begin)
#if ENV_RAMSTAGE
. = ALIGN(ARCH_POINTER_ALIGN_SIZE);
_pci_drivers = .;
KEEP(*(.rodata.pci_driver));
_epci_drivers = .;
RECORD_SIZE(pci_drivers)
. = ALIGN(ARCH_POINTER_ALIGN_SIZE);
_cpu_drivers = .;
KEEP(*(.rodata.cpu_driver));
_ecpu_drivers = .;
RECORD_SIZE(cpu_drivers)
#endif
. = ALIGN(ARCH_POINTER_ALIGN_SIZE);
*(.rodata);
*(.rodata.*);
. = ALIGN(ARCH_POINTER_ALIGN_SIZE);
_etext = .;
RECORD_SIZE(text)
} : to_load
#if ENV_RAMSTAGE && (CONFIG(COVERAGE) || CONFIG(ASAN_IN_RAMSTAGE))
.ctors . : {
. = ALIGN(0x100);
__CTOR_LIST__ = .;
KEEP(*(.ctors));
LONG(0);
LONG(0);
__CTOR_END__ = .;
}
#endif
/* Include data, bss, and heap in that order. Not defined for all stages. */
#if !ENV_SEPARATE_DATA_AND_BSS
.data . : {
. = ALIGN(ARCH_CACHELINE_ALIGN_SIZE);
_data = .;
/*
* The postcar phase uses a stack value that is located in the relocatable
* module section. While the postcar stage could be linked like smm and
* other rmodules the postcar stage needs similar semantics of the more
* traditional stages in the coreboot infrastructure. Therefore it's easier
* to specialize this case.
*/
#if ENV_RMODULE || ENV_POSTCAR
_rmodule_params = .;
KEEP(*(.module_parameters));
_ermodule_params = .;
RECORD_SIZE(rmodule_params)
#endif
*(.data);
*(.data.*);
*(.sdata);
*(.sdata.*);
#if ENV_ROMSTAGE_OR_BEFORE
PROVIDE(_preram_cbmem_console = .);
PROVIDE(_epreram_cbmem_console = _preram_cbmem_console);
PROVIDE(_preram_cbmem_console_size = ABSOLUTE(0));
#elif ENV_RAMSTAGE
. = ALIGN(ARCH_POINTER_ALIGN_SIZE);
_bs_init_begin = .;
KEEP(*(.bs_init));
LONG(0);
LONG(0);
_ebs_init_begin = .;
RECORD_SIZE(bs_init_begin)
#endif
. = ALIGN(ARCH_POINTER_ALIGN_SIZE);
_edata = .;
RECORD_SIZE(data)
}
#endif
#if !ENV_SEPARATE_DATA_AND_BSS
.bss . : {
. = ALIGN(ARCH_POINTER_ALIGN_SIZE);
_bss = .;
*(.bss)
*(.bss.*)
*(.sbss)
*(.sbss.*)
. = ALIGN(ARCH_POINTER_ALIGN_SIZE);
_ebss = .;
RECORD_SIZE(bss)
}
#endif
#if ENV_HAS_HEAP_SECTION
.heap . : {
. = ALIGN(ARCH_POINTER_ALIGN_SIZE);
_heap = .;
. += (ENV_RMODULE ? __heap_size : CONFIG_HEAP_SIZE);
. = ALIGN(ARCH_POINTER_ALIGN_SIZE);
_eheap = .;
RECORD_SIZE(heap)
}
#endif
#if ENV_RAMSTAGE && CONFIG(ASAN_IN_RAMSTAGE)
_shadow_size = (_eheap - _data) >> 3;
REGION(asan_shadow, ., _shadow_size, ARCH_POINTER_ALIGN_SIZE)
#endif
_eprogram = .;
RECORD_SIZE(program)
/* Discard the sections we don't need/want */
zeroptr = 0;
/DISCARD/ : {
*(.comment)
*(.comment.*)
*(.note)
*(.note.*)
*(.eh_frame);
}