On Mon, 2017-05-29 at 17:39 +1000, Nicholas Piggin wrote:
> Very large kernels may require linker stubs for branches from HEAD
> text code. The linker may place these stubs before the HEAD text
> sections, which breaks the assumption that HEAD text is located at 0
> (or the .text section being located at 0x7000/0x8000 on Book3S
> kernels).
> 
> Provide an option to create a small section just before the .text
> section with an empty 256 - 4 bytes, and adjust the start of the .text
> section to match. The linker will tend to put stubs in that section
> and not break our relative-to-absolute offset assumptions.
>

I wonder if the size should be configurable in case more than expected
number of stubs are created. I've seen at-least 1 with as I was playing
with linker script alignment of sections.
 
> This causes a small waste of space on common kernels, but allows large
> kernels to build and boot. For now, it is an EXPERT config option,
> defaulting to =n, but a reference is provided for it in the build-time
> check for such breakage. This is good enough for allyesconfig and
> custom users / hackers.
> 
> Signed-off-by: Nicholas Piggin <npig...@gmail.com>
> ---
>  arch/powerpc/Kconfig               | 11 +++++++++++
>  arch/powerpc/include/asm/head-64.h | 18 ++++++++++++++++++
>  arch/powerpc/kernel/vmlinux.lds.S  |  5 +++++
>  arch/powerpc/tools/head_check.sh   |  7 +++++++
>  4 files changed, 41 insertions(+)
> 
> diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
> index f7c8f9972f61..6eb70c96ec5e 100644
> --- a/arch/powerpc/Kconfig
> +++ b/arch/powerpc/Kconfig
> @@ -454,6 +454,17 @@ config PPC_TRANSACTIONAL_MEM
>         ---help---
>           Support user-mode Transactional Memory on POWERPC.
>  
> +config LD_HEAD_STUB_CATCH
> +     bool "Reserve 256 bytes to cope with linker stubs in HEAD text" if 
> EXPERT
> +     depends on PPC64
> +     default n
> +     help
> +       Very large kernels can cause linker branch stubs to be generated by
> +       code in head_64.S, which moves the head text sections out of their
> +       specified location. This option can work around the problem.

I think we should also say that this relies on binutils/linker doing the
right thing, I don't think this is documented, just an observation that
always works.

> +
> +       If unsure, say "N".
> +
>  config DISABLE_MPROFILE_KERNEL
>       bool "Disable use of mprofile-kernel for kernel tracing"
>       depends on PPC64 && CPU_LITTLE_ENDIAN
> diff --git a/arch/powerpc/include/asm/head-64.h 
> b/arch/powerpc/include/asm/head-64.h
> index 99c9f01d02df..7ab95798f170 100644
> --- a/arch/powerpc/include/asm/head-64.h
> +++ b/arch/powerpc/include/asm/head-64.h
> @@ -63,11 +63,29 @@
>       . = 0x0;                                                \
>  start_##sname:
>  
> +/*
> + * .linker_stub_catch section is used to catch linker stubs from being
> + * inserted in our .text section, above the start_text label (which breaks
> + * the ABS_ADDR calculation). See kernel/vmlinux.lds.S and 
> tools/head_check.sh
> + * for more details. We would prefer to just keep a cacheline (0x80), but
> + * 0x100 seems to be how the linker aligns branch stub groups.
> + */
> +#ifdef CONFIG_LD_HEAD_STUB_CATCH
> +#define OPEN_TEXT_SECTION(start)                             \
> +     .section ".linker_stub_catch","ax",@progbits;           \
> +linker_stub_catch:                                           \
> +     . = 0x4;                                                \

Start at 4? 

> +     text_start = (start) + 0x100;                           \
> +     .section ".text","ax",@progbits;                        \
> +     .balign 0x100;                                          \
> +start_text:
> +#else
>  #define OPEN_TEXT_SECTION(start)                             \
>       text_start = (start);                                   \
>       .section ".text","ax",@progbits;                        \
>       . = 0x0;                                                \
>  start_text:
> +#endif
>

I could apply these on linux-next, it does not have the head_check.sh bits.
I wanted to see what the final vmlinux layout looks like after this. I presume
start_text would be at 0x8100
  
>  #define ZERO_FIXED_SECTION(sname, start, end)                        \
>       sname##_start = (start);                                \
> diff --git a/arch/powerpc/kernel/vmlinux.lds.S 
> b/arch/powerpc/kernel/vmlinux.lds.S
> index 5aa434e84605..e69155f0db36 100644
> --- a/arch/powerpc/kernel/vmlinux.lds.S
> +++ b/arch/powerpc/kernel/vmlinux.lds.S
> @@ -81,6 +81,11 @@ SECTIONS
>        * section placement to work.
>        */
>       .text BLOCK(0) : AT(ADDR(.text) - LOAD_OFFSET) {
> +#ifdef CONFIG_LD_HEAD_STUB_CATCH
> +             *(.linker_stub_catch);
> +             . = . ;
> +#endif
> +
>  #else
>       .text : AT(ADDR(.text) - LOAD_OFFSET) {
>               ALIGN_FUNCTION();
> diff --git a/arch/powerpc/tools/head_check.sh 
> b/arch/powerpc/tools/head_check.sh
> index bfde937564f9..ad9e57209aa4 100755
> --- a/arch/powerpc/tools/head_check.sh
> +++ b/arch/powerpc/tools/head_check.sh
> @@ -21,6 +21,11 @@
>  # fixed section region (0 - 0x8000ish). Check what code is calling those 
> stubs,
>  # and perhaps change so a direct branch can reach.
>  #
> +# A ".linker_stub_catch" section is used to catch some stubs generated by
> +# early .text code, which tend to get placed at the start of the section.
> +# If there are too many such stubs, they can overflow this section. Expanding
> +# it may help (or reducing the number of stub branches).
> +#
>  # Linker stubs use the TOC pointer, so even if fixed section code could
>  # tolerate them being inserted into head code, they can't be allowed in low
>  # level entry code (boot, interrupt vectors, etc) until r2 is set up. This
> @@ -50,6 +55,7 @@ start_head_addr=$(cat .tmp_symbols.txt | grep " t 
> start_first_256B$" | cut -d' '
>  
>  if [ "$start_head_addr" != "$expected_start_head_addr" ]; then
>       echo "ERROR: head code starts at $start_head_addr, should be 
> $expected_start_head_addr"
> +     echo "ERROR: try to enable LD_HEAD_STUB_CATCH config option"
>       echo "ERROR: see comments in arch/powerpc/tools/head_check.sh"
>  
>       exit 1
> @@ -63,6 +69,7 @@ start_text_addr=$(cat .tmp_symbols.txt | grep " t 
> start_text$" | cut -d' ' -f1)
>  
>  if [ "$start_text_addr" != "$expected_start_text_addr" ]; then
>       echo "ERROR: start_text address is $start_text_addr, should be 
> $expected_start_text_addr"
> +     echo "ERROR: try to enable LD_HEAD_STUB_CATCH config option"
>       echo "ERROR: see comments in arch/powerpc/tools/head_check.sh"
>  
>       exit 1

Overall, I think this is very useful. I am not sure how to test this and
what features it would be incompatible with -- kexec/crash dump?

Balbir Singh.

Reply via email to