2021-06-11 09:43:44

by Nicholas Piggin

[permalink] [raw]
Subject: [PATCH v4 2/2] powerpc/64: Option to use ELF V2 ABI for big-endian kernels

Provide an option to build big-endian kernels using the ELFv2 ABI. This
works on GCC only so far, although it is rumored to work with clang
that's not been tested yet. A new module version check ensures the
module ELF ABI level matches the kernel build.

This can give big-endian kernels some useful advantages of the ELFv2 ABI
(e.g., less stack usage, -mprofile-kernel, better compatibility with eBPF
tools).

BE+ELFv2 is not officially supported by the GNU toolchain, but it works
fine in testing and has been used by some userspace for some time (e.g.,
Void Linux).

Tested-by: Michal Suchánek <[email protected]>
Reviewed-by: Segher Boessenkool <[email protected]>
Signed-off-by: Nicholas Piggin <[email protected]>
---
arch/powerpc/Kconfig | 22 ++++++++++++++++++++++
arch/powerpc/Makefile | 18 ++++++++++++------
arch/powerpc/boot/Makefile | 4 +++-
arch/powerpc/include/asm/module.h | 24 ++++++++++++++++++++++++
arch/powerpc/kernel/vdso64/Makefile | 13 +++++++++++++
drivers/crypto/vmx/Makefile | 8 ++++++--
drivers/crypto/vmx/ppc-xlate.pl | 10 ++++++----
7 files changed, 86 insertions(+), 13 deletions(-)

diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 088dd2afcfe4..093f973a28b9 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -163,6 +163,7 @@ config PPC
select ARCH_WEAK_RELEASE_ACQUIRE
select BINFMT_ELF
select BUILDTIME_TABLE_SORT
+ select PPC64_BUILD_ELF_V2_ABI if PPC64 && CPU_LITTLE_ENDIAN
select CLONE_BACKWARDS
select DCACHE_WORD_ACCESS if PPC64 && CPU_LITTLE_ENDIAN
select DMA_OPS_BYPASS if PPC64
@@ -561,6 +562,27 @@ config KEXEC_FILE
config ARCH_HAS_KEXEC_PURGATORY
def_bool KEXEC_FILE

+config PPC64_BUILD_ELF_V2_ABI
+ bool
+
+config PPC64_BUILD_BIG_ENDIAN_ELF_V2_ABI
+ bool "Build big-endian kernel using ELF ABI V2 (EXPERIMENTAL)"
+ depends on PPC64 && CPU_BIG_ENDIAN && EXPERT
+ depends on CC_IS_GCC && LD_VERSION >= 22400
+ default n
+ select PPC64_BUILD_ELF_V2_ABI
+ help
+ This builds the kernel image using the "Power Architecture 64-Bit ELF
+ V2 ABI Specification", which has a reduced stack overhead and faster
+ function calls. This internal kernel ABI option does not affect
+ userspace compatibility.
+
+ The V2 ABI is standard for 64-bit little-endian, but for big-endian
+ it is less well tested by kernel and toolchain. However some distros
+ build userspace this way, and it can produce a functioning kernel.
+
+ This requires GCC and binutils 2.24 or newer.
+
config RELOCATABLE
bool "Build a relocatable kernel"
depends on PPC64 || (FLATMEM && (44x || FSL_BOOKE))
diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile
index 3212d076ac6a..b90b5cb799aa 100644
--- a/arch/powerpc/Makefile
+++ b/arch/powerpc/Makefile
@@ -91,10 +91,14 @@ endif

ifdef CONFIG_PPC64
ifndef CONFIG_CC_IS_CLANG
-cflags-$(CONFIG_CPU_BIG_ENDIAN) += $(call cc-option,-mabi=elfv1)
-cflags-$(CONFIG_CPU_BIG_ENDIAN) += $(call cc-option,-mcall-aixdesc)
-aflags-$(CONFIG_CPU_BIG_ENDIAN) += $(call cc-option,-mabi=elfv1)
-aflags-$(CONFIG_CPU_LITTLE_ENDIAN) += -mabi=elfv2
+ifdef CONFIG_PPC64_BUILD_ELF_V2_ABI
+cflags-y += $(call cc-option,-mabi=elfv2)
+aflags-y += $(call cc-option,-mabi=elfv2)
+else
+cflags-y += $(call cc-option,-mabi=elfv1)
+cflags-y += $(call cc-option,-mcall-aixdesc)
+aflags-y += $(call cc-option,-mabi=elfv1)
+endif
endif
endif

@@ -142,15 +146,17 @@ endif

CFLAGS-$(CONFIG_PPC64) := $(call cc-option,-mtraceback=no)
ifndef CONFIG_CC_IS_CLANG
-ifdef CONFIG_CPU_LITTLE_ENDIAN
-CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mabi=elfv2,$(call cc-option,-mcall-aixdesc))
+ifdef CONFIG_PPC64_BUILD_ELF_V2_ABI
+CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mabi=elfv2)
AFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mabi=elfv2)
else
+# Keep these in synch with arch/powerpc/kernel/vdso64/Makefile
CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mabi=elfv1)
CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mcall-aixdesc)
AFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mabi=elfv1)
endif
endif
+
CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mcmodel=medium,$(call cc-option,-mminimal-toc))
CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mno-pointers-to-nested-functions)

diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile
index 2b8da923ceca..be84a72f8258 100644
--- a/arch/powerpc/boot/Makefile
+++ b/arch/powerpc/boot/Makefile
@@ -40,6 +40,9 @@ BOOTCFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \

ifdef CONFIG_PPC64_BOOT_WRAPPER
BOOTCFLAGS += -m64
+ifdef CONFIG_PPC64_BUILD_ELF_V2_ABI
+BOOTCFLAGS += $(call cc-option,-mabi=elfv2)
+endif
else
BOOTCFLAGS += -m32
endif
@@ -50,7 +53,6 @@ ifdef CONFIG_CPU_BIG_ENDIAN
BOOTCFLAGS += -mbig-endian
else
BOOTCFLAGS += -mlittle-endian
-BOOTCFLAGS += $(call cc-option,-mabi=elfv2)
endif

BOOTAFLAGS := -D__ASSEMBLY__ $(BOOTCFLAGS) -nostdinc
diff --git a/arch/powerpc/include/asm/module.h b/arch/powerpc/include/asm/module.h
index 857d9ff24295..043e11068ff4 100644
--- a/arch/powerpc/include/asm/module.h
+++ b/arch/powerpc/include/asm/module.h
@@ -52,6 +52,30 @@ struct mod_arch_specific {
unsigned int num_bugs;
};

+/*
+ * Check kernel module ELF header architecture specific compatibility.
+ */
+static inline bool elf_check_module_arch(Elf_Ehdr *hdr)
+{
+ if (!elf_check_arch(hdr))
+ return false;
+
+ if (IS_ENABLED(CONFIG_PPC64)) {
+ unsigned long abi_level = hdr->e_flags & 0x3;
+
+ if (IS_ENABLED(CONFIG_PPC64_BUILD_ELF_V2_ABI)) {
+ if (abi_level != 2)
+ return false;
+ } else {
+ if (abi_level >= 2)
+ return false;
+ }
+ }
+
+ return true;
+}
+#define elf_check_module_arch elf_check_module_arch
+
/*
* Select ELF headers.
* Make empty section for module_frob_arch_sections to expand.
diff --git a/arch/powerpc/kernel/vdso64/Makefile b/arch/powerpc/kernel/vdso64/Makefile
index 2813e3f98db6..d783c07e558f 100644
--- a/arch/powerpc/kernel/vdso64/Makefile
+++ b/arch/powerpc/kernel/vdso64/Makefile
@@ -25,6 +25,19 @@ KCOV_INSTRUMENT := n
UBSAN_SANITIZE := n
KASAN_SANITIZE := n

+# Always build vdso64 with ELFv1 ABI for BE kernels
+ifdef CONFIG_PPC64_BUILD_ELF_V2_ABI
+ifdef CONFIG_CPU_BIG_ENDIAN
+KBUILD_CFLAGS := $(filter-out -mabi=elfv2,$(KBUILD_CFLAGS))
+KBUILD_AFLAGS := $(filter-out -mabi=elfv2,$(KBUILD_AFLAGS))
+
+# These are derived from arch/powerpc/Makefile
+KBUILD_CFLAGS += $(call cc-option,-mabi=elfv1)
+KBUILD_CFLAGS += $(call cc-option,-mcall-aixdesc)
+KBUILD_AFLAGS += $(call cc-option,-mabi=elfv1)
+endif
+endif
+
ccflags-y := -shared -fno-common -fno-builtin -nostdlib \
-Wl,-soname=linux-vdso64.so.1 -Wl,--hash-style=both
asflags-y := -D__VDSO64__ -s
diff --git a/drivers/crypto/vmx/Makefile b/drivers/crypto/vmx/Makefile
index 709670d2b553..d9ccf9fc3483 100644
--- a/drivers/crypto/vmx/Makefile
+++ b/drivers/crypto/vmx/Makefile
@@ -5,18 +5,22 @@ vmx-crypto-objs := vmx.o aesp8-ppc.o ghashp8-ppc.o aes.o aes_cbc.o aes_ctr.o aes
ifeq ($(CONFIG_CPU_LITTLE_ENDIAN),y)
override flavour := linux-ppc64le
else
+ifdef CONFIG_PPC64_BUILD_ELF_V2_ABI
+override flavour := linux-ppc64-elfv2
+else
override flavour := linux-ppc64
endif
+endif

quiet_cmd_perl = PERL $@
cmd_perl = $(PERL) $(<) $(flavour) > $(@)

targets += aesp8-ppc.S ghashp8-ppc.S

-$(obj)/aesp8-ppc.S: $(src)/aesp8-ppc.pl FORCE
+$(obj)/aesp8-ppc.S: $(src)/aesp8-ppc.pl $(src)/ppc-xlate.pl FORCE
$(call if_changed,perl)

-$(obj)/ghashp8-ppc.S: $(src)/ghashp8-ppc.pl FORCE
+$(obj)/ghashp8-ppc.S: $(src)/ghashp8-ppc.pl $(src)/ppc-xlate.pl FORCE
$(call if_changed,perl)

clean-files := aesp8-ppc.S ghashp8-ppc.S
diff --git a/drivers/crypto/vmx/ppc-xlate.pl b/drivers/crypto/vmx/ppc-xlate.pl
index 36db2ef09e5b..b583898c11ae 100644
--- a/drivers/crypto/vmx/ppc-xlate.pl
+++ b/drivers/crypto/vmx/ppc-xlate.pl
@@ -9,6 +9,8 @@ open STDOUT,">$output" || die "can't open $output: $!";

my %GLOBALS;
my $dotinlocallabels=($flavour=~/linux/)?1:0;
+my $elfv2abi=(($flavour =~ /linux-ppc64le/) or ($flavour =~ /linux-ppc64-elfv2/))?1:0;
+my $dotfunctions=($elfv2abi=~1)?0:1;

################################################################
# directives which need special treatment on different platforms
@@ -40,7 +42,7 @@ my $globl = sub {
};
my $text = sub {
my $ret = ($flavour =~ /aix/) ? ".csect\t.text[PR],7" : ".text";
- $ret = ".abiversion 2\n".$ret if ($flavour =~ /linux.*64le/);
+ $ret = ".abiversion 2\n".$ret if ($elfv2abi);
$ret;
};
my $machine = sub {
@@ -56,8 +58,8 @@ my $size = sub {
if ($flavour =~ /linux/)
{ shift;
my $name = shift; $name =~ s|^[\.\_]||;
- my $ret = ".size $name,.-".($flavour=~/64$/?".":"").$name;
- $ret .= "\n.size .$name,.-.$name" if ($flavour=~/64$/);
+ my $ret = ".size $name,.-".($dotfunctions?".":"").$name;
+ $ret .= "\n.size .$name,.-.$name" if ($dotfunctions);
$ret;
}
else
@@ -142,7 +144,7 @@ my $vmr = sub {

# Some ABIs specify vrsave, special-purpose register #256, as reserved
# for system use.
-my $no_vrsave = ($flavour =~ /linux-ppc64le/);
+my $no_vrsave = ($elfv2abi);
my $mtspr = sub {
my ($f,$idx,$ra) = @_;
if ($idx == 256 && $no_vrsave) {
--
2.23.0


2021-06-11 09:59:44

by Michal Suchánek

[permalink] [raw]
Subject: Re: [PATCH v4 2/2] powerpc/64: Option to use ELF V2 ABI for big-endian kernels

On Fri, Jun 11, 2021 at 07:39:59PM +1000, Nicholas Piggin wrote:
> Provide an option to build big-endian kernels using the ELFv2 ABI. This
> works on GCC only so far, although it is rumored to work with clang
> that's not been tested yet. A new module version check ensures the
> module ELF ABI level matches the kernel build.
>
> This can give big-endian kernels some useful advantages of the ELFv2 ABI
> (e.g., less stack usage, -mprofile-kernel, better compatibility with eBPF
> tools).
>
> BE+ELFv2 is not officially supported by the GNU toolchain, but it works
> fine in testing and has been used by some userspace for some time (e.g.,
> Void Linux).
>
> Tested-by: Michal Such?nek <[email protected]>
> Reviewed-by: Segher Boessenkool <[email protected]>
> Signed-off-by: Nicholas Piggin <[email protected]>
> ---
> arch/powerpc/Kconfig | 22 ++++++++++++++++++++++
> arch/powerpc/Makefile | 18 ++++++++++++------
> arch/powerpc/boot/Makefile | 4 +++-
> arch/powerpc/include/asm/module.h | 24 ++++++++++++++++++++++++
> arch/powerpc/kernel/vdso64/Makefile | 13 +++++++++++++
> drivers/crypto/vmx/Makefile | 8 ++++++--
> drivers/crypto/vmx/ppc-xlate.pl | 10 ++++++----
> 7 files changed, 86 insertions(+), 13 deletions(-)
>
> diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
> index 088dd2afcfe4..093f973a28b9 100644
> --- a/arch/powerpc/Kconfig
> +++ b/arch/powerpc/Kconfig
> @@ -163,6 +163,7 @@ config PPC
> select ARCH_WEAK_RELEASE_ACQUIRE
> select BINFMT_ELF
> select BUILDTIME_TABLE_SORT
> + select PPC64_BUILD_ELF_V2_ABI if PPC64 && CPU_LITTLE_ENDIAN
> select CLONE_BACKWARDS
> select DCACHE_WORD_ACCESS if PPC64 && CPU_LITTLE_ENDIAN
> select DMA_OPS_BYPASS if PPC64
> @@ -561,6 +562,27 @@ config KEXEC_FILE
> config ARCH_HAS_KEXEC_PURGATORY
> def_bool KEXEC_FILE
>
> +config PPC64_BUILD_ELF_V2_ABI
> + bool
> +
> +config PPC64_BUILD_BIG_ENDIAN_ELF_V2_ABI
> + bool "Build big-endian kernel using ELF ABI V2 (EXPERIMENTAL)"
> + depends on PPC64 && CPU_BIG_ENDIAN && EXPERT
> + depends on CC_IS_GCC && LD_VERSION >= 22400
> + default n
> + select PPC64_BUILD_ELF_V2_ABI
> + help
> + This builds the kernel image using the "Power Architecture 64-Bit ELF
> + V2 ABI Specification", which has a reduced stack overhead and faster
> + function calls. This internal kernel ABI option does not affect
> + userspace compatibility.
> +
> + The V2 ABI is standard for 64-bit little-endian, but for big-endian
> + it is less well tested by kernel and toolchain. However some distros
> + build userspace this way, and it can produce a functioning kernel.
> +
> + This requires GCC and binutils 2.24 or newer.
> +
> config RELOCATABLE
> bool "Build a relocatable kernel"
> depends on PPC64 || (FLATMEM && (44x || FSL_BOOKE))
> diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile
> index 3212d076ac6a..b90b5cb799aa 100644
> --- a/arch/powerpc/Makefile
> +++ b/arch/powerpc/Makefile
> @@ -91,10 +91,14 @@ endif
>
> ifdef CONFIG_PPC64
> ifndef CONFIG_CC_IS_CLANG
> -cflags-$(CONFIG_CPU_BIG_ENDIAN) += $(call cc-option,-mabi=elfv1)
> -cflags-$(CONFIG_CPU_BIG_ENDIAN) += $(call cc-option,-mcall-aixdesc)
> -aflags-$(CONFIG_CPU_BIG_ENDIAN) += $(call cc-option,-mabi=elfv1)
> -aflags-$(CONFIG_CPU_LITTLE_ENDIAN) += -mabi=elfv2
> +ifdef CONFIG_PPC64_BUILD_ELF_V2_ABI
> +cflags-y += $(call cc-option,-mabi=elfv2)
> +aflags-y += $(call cc-option,-mabi=elfv2)
> +else
> +cflags-y += $(call cc-option,-mabi=elfv1)
> +cflags-y += $(call cc-option,-mcall-aixdesc)
> +aflags-y += $(call cc-option,-mabi=elfv1)
> +endif
> endif
> endif
>
> @@ -142,15 +146,17 @@ endif
>
> CFLAGS-$(CONFIG_PPC64) := $(call cc-option,-mtraceback=no)
> ifndef CONFIG_CC_IS_CLANG
> -ifdef CONFIG_CPU_LITTLE_ENDIAN
> -CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mabi=elfv2,$(call cc-option,-mcall-aixdesc))
> +ifdef CONFIG_PPC64_BUILD_ELF_V2_ABI
> +CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mabi=elfv2)
> AFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mabi=elfv2)
> else
> +# Keep these in synch with arch/powerpc/kernel/vdso64/Makefile
> CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mabi=elfv1)
> CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mcall-aixdesc)
> AFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mabi=elfv1)
> endif
> endif
> +
> CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mcmodel=medium,$(call cc-option,-mminimal-toc))
> CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mno-pointers-to-nested-functions)
>
> diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile
> index 2b8da923ceca..be84a72f8258 100644
> --- a/arch/powerpc/boot/Makefile
> +++ b/arch/powerpc/boot/Makefile
> @@ -40,6 +40,9 @@ BOOTCFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
>
> ifdef CONFIG_PPC64_BOOT_WRAPPER
> BOOTCFLAGS += -m64
> +ifdef CONFIG_PPC64_BUILD_ELF_V2_ABI
> +BOOTCFLAGS += $(call cc-option,-mabi=elfv2)
> +endif
> else
> BOOTCFLAGS += -m32
> endif
> @@ -50,7 +53,6 @@ ifdef CONFIG_CPU_BIG_ENDIAN
> BOOTCFLAGS += -mbig-endian
> else
> BOOTCFLAGS += -mlittle-endian
> -BOOTCFLAGS += $(call cc-option,-mabi=elfv2)
> endif
>
> BOOTAFLAGS := -D__ASSEMBLY__ $(BOOTCFLAGS) -nostdinc
> diff --git a/arch/powerpc/include/asm/module.h b/arch/powerpc/include/asm/module.h
> index 857d9ff24295..043e11068ff4 100644
> --- a/arch/powerpc/include/asm/module.h
> +++ b/arch/powerpc/include/asm/module.h
> @@ -52,6 +52,30 @@ struct mod_arch_specific {
> unsigned int num_bugs;
> };
>
> +/*
> + * Check kernel module ELF header architecture specific compatibility.
> + */
> +static inline bool elf_check_module_arch(Elf_Ehdr *hdr)
> +{
> + if (!elf_check_arch(hdr))
> + return false;
> +
> + if (IS_ENABLED(CONFIG_PPC64)) {
> + unsigned long abi_level = hdr->e_flags & 0x3;
> +
> + if (IS_ENABLED(CONFIG_PPC64_BUILD_ELF_V2_ABI)) {
> + if (abi_level != 2)
> + return false;
> + } else {
> + if (abi_level >= 2)
> + return false;
> + }
> + }
> +
> + return true;
> +}
> +#define elf_check_module_arch elf_check_module_arch
> +
> /*
> * Select ELF headers.
> * Make empty section for module_frob_arch_sections to expand.
Shouldn't this part go to the second patch?

Thanks

Michal
> diff --git a/arch/powerpc/kernel/vdso64/Makefile b/arch/powerpc/kernel/vdso64/Makefile
> index 2813e3f98db6..d783c07e558f 100644
> --- a/arch/powerpc/kernel/vdso64/Makefile
> +++ b/arch/powerpc/kernel/vdso64/Makefile
> @@ -25,6 +25,19 @@ KCOV_INSTRUMENT := n
> UBSAN_SANITIZE := n
> KASAN_SANITIZE := n
>
> +# Always build vdso64 with ELFv1 ABI for BE kernels
> +ifdef CONFIG_PPC64_BUILD_ELF_V2_ABI
> +ifdef CONFIG_CPU_BIG_ENDIAN
> +KBUILD_CFLAGS := $(filter-out -mabi=elfv2,$(KBUILD_CFLAGS))
> +KBUILD_AFLAGS := $(filter-out -mabi=elfv2,$(KBUILD_AFLAGS))
> +
> +# These are derived from arch/powerpc/Makefile
> +KBUILD_CFLAGS += $(call cc-option,-mabi=elfv1)
> +KBUILD_CFLAGS += $(call cc-option,-mcall-aixdesc)
> +KBUILD_AFLAGS += $(call cc-option,-mabi=elfv1)
> +endif
> +endif
> +
> ccflags-y := -shared -fno-common -fno-builtin -nostdlib \
> -Wl,-soname=linux-vdso64.so.1 -Wl,--hash-style=both
> asflags-y := -D__VDSO64__ -s
> diff --git a/drivers/crypto/vmx/Makefile b/drivers/crypto/vmx/Makefile
> index 709670d2b553..d9ccf9fc3483 100644
> --- a/drivers/crypto/vmx/Makefile
> +++ b/drivers/crypto/vmx/Makefile
> @@ -5,18 +5,22 @@ vmx-crypto-objs := vmx.o aesp8-ppc.o ghashp8-ppc.o aes.o aes_cbc.o aes_ctr.o aes
> ifeq ($(CONFIG_CPU_LITTLE_ENDIAN),y)
> override flavour := linux-ppc64le
> else
> +ifdef CONFIG_PPC64_BUILD_ELF_V2_ABI
> +override flavour := linux-ppc64-elfv2
> +else
> override flavour := linux-ppc64
> endif
> +endif
>
> quiet_cmd_perl = PERL $@
> cmd_perl = $(PERL) $(<) $(flavour) > $(@)
>
> targets += aesp8-ppc.S ghashp8-ppc.S
>
> -$(obj)/aesp8-ppc.S: $(src)/aesp8-ppc.pl FORCE
> +$(obj)/aesp8-ppc.S: $(src)/aesp8-ppc.pl $(src)/ppc-xlate.pl FORCE
> $(call if_changed,perl)
>
> -$(obj)/ghashp8-ppc.S: $(src)/ghashp8-ppc.pl FORCE
> +$(obj)/ghashp8-ppc.S: $(src)/ghashp8-ppc.pl $(src)/ppc-xlate.pl FORCE
> $(call if_changed,perl)
>
> clean-files := aesp8-ppc.S ghashp8-ppc.S
> diff --git a/drivers/crypto/vmx/ppc-xlate.pl b/drivers/crypto/vmx/ppc-xlate.pl
> index 36db2ef09e5b..b583898c11ae 100644
> --- a/drivers/crypto/vmx/ppc-xlate.pl
> +++ b/drivers/crypto/vmx/ppc-xlate.pl
> @@ -9,6 +9,8 @@ open STDOUT,">$output" || die "can't open $output: $!";
>
> my %GLOBALS;
> my $dotinlocallabels=($flavour=~/linux/)?1:0;
> +my $elfv2abi=(($flavour =~ /linux-ppc64le/) or ($flavour =~ /linux-ppc64-elfv2/))?1:0;
> +my $dotfunctions=($elfv2abi=~1)?0:1;
>
> ################################################################
> # directives which need special treatment on different platforms
> @@ -40,7 +42,7 @@ my $globl = sub {
> };
> my $text = sub {
> my $ret = ($flavour =~ /aix/) ? ".csect\t.text[PR],7" : ".text";
> - $ret = ".abiversion 2\n".$ret if ($flavour =~ /linux.*64le/);
> + $ret = ".abiversion 2\n".$ret if ($elfv2abi);
> $ret;
> };
> my $machine = sub {
> @@ -56,8 +58,8 @@ my $size = sub {
> if ($flavour =~ /linux/)
> { shift;
> my $name = shift; $name =~ s|^[\.\_]||;
> - my $ret = ".size $name,.-".($flavour=~/64$/?".":"").$name;
> - $ret .= "\n.size .$name,.-.$name" if ($flavour=~/64$/);
> + my $ret = ".size $name,.-".($dotfunctions?".":"").$name;
> + $ret .= "\n.size .$name,.-.$name" if ($dotfunctions);
> $ret;
> }
> else
> @@ -142,7 +144,7 @@ my $vmr = sub {
>
> # Some ABIs specify vrsave, special-purpose register #256, as reserved
> # for system use.
> -my $no_vrsave = ($flavour =~ /linux-ppc64le/);
> +my $no_vrsave = ($elfv2abi);
> my $mtspr = sub {
> my ($f,$idx,$ra) = @_;
> if ($idx == 256 && $no_vrsave) {
> --
> 2.23.0
>

2021-06-11 10:24:02

by Michal Suchánek

[permalink] [raw]
Subject: Re: [PATCH v4 2/2] powerpc/64: Option to use ELF V2 ABI for big-endian kernels

On Fri, Jun 11, 2021 at 11:58:19AM +0200, Michal Such?nek wrote:
> On Fri, Jun 11, 2021 at 07:39:59PM +1000, Nicholas Piggin wrote:
> > Provide an option to build big-endian kernels using the ELFv2 ABI. This
> > works on GCC only so far, although it is rumored to work with clang
> > that's not been tested yet. A new module version check ensures the
> > module ELF ABI level matches the kernel build.
> >
> > This can give big-endian kernels some useful advantages of the ELFv2 ABI
> > (e.g., less stack usage, -mprofile-kernel, better compatibility with eBPF
> > tools).
> >
> > BE+ELFv2 is not officially supported by the GNU toolchain, but it works
> > fine in testing and has been used by some userspace for some time (e.g.,
> > Void Linux).
> >
> > Tested-by: Michal Such?nek <[email protected]>
> > Reviewed-by: Segher Boessenkool <[email protected]>
> > Signed-off-by: Nicholas Piggin <[email protected]>
> > ---
> > arch/powerpc/Kconfig | 22 ++++++++++++++++++++++
> > arch/powerpc/Makefile | 18 ++++++++++++------
> > arch/powerpc/boot/Makefile | 4 +++-
> > arch/powerpc/include/asm/module.h | 24 ++++++++++++++++++++++++
> > arch/powerpc/kernel/vdso64/Makefile | 13 +++++++++++++
> > drivers/crypto/vmx/Makefile | 8 ++++++--
> > drivers/crypto/vmx/ppc-xlate.pl | 10 ++++++----
> > 7 files changed, 86 insertions(+), 13 deletions(-)
> >
> > diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
> > index 088dd2afcfe4..093f973a28b9 100644
> > --- a/arch/powerpc/Kconfig
> > +++ b/arch/powerpc/Kconfig
> > @@ -163,6 +163,7 @@ config PPC
> > select ARCH_WEAK_RELEASE_ACQUIRE
> > select BINFMT_ELF
> > select BUILDTIME_TABLE_SORT
> > + select PPC64_BUILD_ELF_V2_ABI if PPC64 && CPU_LITTLE_ENDIAN
> > select CLONE_BACKWARDS
> > select DCACHE_WORD_ACCESS if PPC64 && CPU_LITTLE_ENDIAN
> > select DMA_OPS_BYPASS if PPC64
> > @@ -561,6 +562,27 @@ config KEXEC_FILE
> > config ARCH_HAS_KEXEC_PURGATORY
> > def_bool KEXEC_FILE
> >
> > +config PPC64_BUILD_ELF_V2_ABI
> > + bool
> > +
> > +config PPC64_BUILD_BIG_ENDIAN_ELF_V2_ABI
> > + bool "Build big-endian kernel using ELF ABI V2 (EXPERIMENTAL)"
> > + depends on PPC64 && CPU_BIG_ENDIAN && EXPERT
> > + depends on CC_IS_GCC && LD_VERSION >= 22400
> > + default n
> > + select PPC64_BUILD_ELF_V2_ABI
> > + help
> > + This builds the kernel image using the "Power Architecture 64-Bit ELF
> > + V2 ABI Specification", which has a reduced stack overhead and faster
> > + function calls. This internal kernel ABI option does not affect
> > + userspace compatibility.
> > +
> > + The V2 ABI is standard for 64-bit little-endian, but for big-endian
> > + it is less well tested by kernel and toolchain. However some distros
> > + build userspace this way, and it can produce a functioning kernel.
> > +
> > + This requires GCC and binutils 2.24 or newer.
> > +
> > config RELOCATABLE
> > bool "Build a relocatable kernel"
> > depends on PPC64 || (FLATMEM && (44x || FSL_BOOKE))
> > diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile
> > index 3212d076ac6a..b90b5cb799aa 100644
> > --- a/arch/powerpc/Makefile
> > +++ b/arch/powerpc/Makefile
> > @@ -91,10 +91,14 @@ endif
> >
> > ifdef CONFIG_PPC64
> > ifndef CONFIG_CC_IS_CLANG
> > -cflags-$(CONFIG_CPU_BIG_ENDIAN) += $(call cc-option,-mabi=elfv1)
> > -cflags-$(CONFIG_CPU_BIG_ENDIAN) += $(call cc-option,-mcall-aixdesc)
> > -aflags-$(CONFIG_CPU_BIG_ENDIAN) += $(call cc-option,-mabi=elfv1)
> > -aflags-$(CONFIG_CPU_LITTLE_ENDIAN) += -mabi=elfv2
> > +ifdef CONFIG_PPC64_BUILD_ELF_V2_ABI
> > +cflags-y += $(call cc-option,-mabi=elfv2)
> > +aflags-y += $(call cc-option,-mabi=elfv2)
> > +else
> > +cflags-y += $(call cc-option,-mabi=elfv1)
> > +cflags-y += $(call cc-option,-mcall-aixdesc)
> > +aflags-y += $(call cc-option,-mabi=elfv1)
> > +endif
> > endif
> > endif
> >
> > @@ -142,15 +146,17 @@ endif
> >
> > CFLAGS-$(CONFIG_PPC64) := $(call cc-option,-mtraceback=no)
> > ifndef CONFIG_CC_IS_CLANG
> > -ifdef CONFIG_CPU_LITTLE_ENDIAN
> > -CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mabi=elfv2,$(call cc-option,-mcall-aixdesc))
> > +ifdef CONFIG_PPC64_BUILD_ELF_V2_ABI
> > +CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mabi=elfv2)
> > AFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mabi=elfv2)
> > else
> > +# Keep these in synch with arch/powerpc/kernel/vdso64/Makefile
> > CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mabi=elfv1)
> > CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mcall-aixdesc)
> > AFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mabi=elfv1)
> > endif
> > endif
> > +
> > CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mcmodel=medium,$(call cc-option,-mminimal-toc))
> > CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mno-pointers-to-nested-functions)
> >
> > diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile
> > index 2b8da923ceca..be84a72f8258 100644
> > --- a/arch/powerpc/boot/Makefile
> > +++ b/arch/powerpc/boot/Makefile
> > @@ -40,6 +40,9 @@ BOOTCFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
> >
> > ifdef CONFIG_PPC64_BOOT_WRAPPER
> > BOOTCFLAGS += -m64
> > +ifdef CONFIG_PPC64_BUILD_ELF_V2_ABI
> > +BOOTCFLAGS += $(call cc-option,-mabi=elfv2)
> > +endif
> > else
> > BOOTCFLAGS += -m32
> > endif
> > @@ -50,7 +53,6 @@ ifdef CONFIG_CPU_BIG_ENDIAN
> > BOOTCFLAGS += -mbig-endian
> > else
> > BOOTCFLAGS += -mlittle-endian
> > -BOOTCFLAGS += $(call cc-option,-mabi=elfv2)
> > endif
> >
> > BOOTAFLAGS := -D__ASSEMBLY__ $(BOOTCFLAGS) -nostdinc
> > diff --git a/arch/powerpc/include/asm/module.h b/arch/powerpc/include/asm/module.h
> > index 857d9ff24295..043e11068ff4 100644
> > --- a/arch/powerpc/include/asm/module.h
> > +++ b/arch/powerpc/include/asm/module.h
> > @@ -52,6 +52,30 @@ struct mod_arch_specific {
> > unsigned int num_bugs;
> > };
> >
> > +/*
> > + * Check kernel module ELF header architecture specific compatibility.
> > + */
> > +static inline bool elf_check_module_arch(Elf_Ehdr *hdr)
> > +{
> > + if (!elf_check_arch(hdr))
> > + return false;
> > +
> > + if (IS_ENABLED(CONFIG_PPC64)) {
> > + unsigned long abi_level = hdr->e_flags & 0x3;
> > +
> > + if (IS_ENABLED(CONFIG_PPC64_BUILD_ELF_V2_ABI)) {
> > + if (abi_level != 2)
> > + return false;
> > + } else {
> > + if (abi_level >= 2)
> > + return false;
> > + }
> > + }
> > +
> > + return true;
> > +}
> > +#define elf_check_module_arch elf_check_module_arch
> > +
> > /*
> > * Select ELF headers.
> > * Make empty section for module_frob_arch_sections to expand.
> Shouldn't this part go to the second patch?
Nevermind, this is the second patch now.
>
> Thanks
>
> Michal
> > diff --git a/arch/powerpc/kernel/vdso64/Makefile b/arch/powerpc/kernel/vdso64/Makefile
> > index 2813e3f98db6..d783c07e558f 100644
> > --- a/arch/powerpc/kernel/vdso64/Makefile
> > +++ b/arch/powerpc/kernel/vdso64/Makefile
> > @@ -25,6 +25,19 @@ KCOV_INSTRUMENT := n
> > UBSAN_SANITIZE := n
> > KASAN_SANITIZE := n
> >
> > +# Always build vdso64 with ELFv1 ABI for BE kernels
> > +ifdef CONFIG_PPC64_BUILD_ELF_V2_ABI
> > +ifdef CONFIG_CPU_BIG_ENDIAN
> > +KBUILD_CFLAGS := $(filter-out -mabi=elfv2,$(KBUILD_CFLAGS))
> > +KBUILD_AFLAGS := $(filter-out -mabi=elfv2,$(KBUILD_AFLAGS))
> > +
> > +# These are derived from arch/powerpc/Makefile
> > +KBUILD_CFLAGS += $(call cc-option,-mabi=elfv1)
> > +KBUILD_CFLAGS += $(call cc-option,-mcall-aixdesc)
> > +KBUILD_AFLAGS += $(call cc-option,-mabi=elfv1)
> > +endif
> > +endif
> > +
> > ccflags-y := -shared -fno-common -fno-builtin -nostdlib \
> > -Wl,-soname=linux-vdso64.so.1 -Wl,--hash-style=both
> > asflags-y := -D__VDSO64__ -s
> > diff --git a/drivers/crypto/vmx/Makefile b/drivers/crypto/vmx/Makefile
> > index 709670d2b553..d9ccf9fc3483 100644
> > --- a/drivers/crypto/vmx/Makefile
> > +++ b/drivers/crypto/vmx/Makefile
> > @@ -5,18 +5,22 @@ vmx-crypto-objs := vmx.o aesp8-ppc.o ghashp8-ppc.o aes.o aes_cbc.o aes_ctr.o aes
> > ifeq ($(CONFIG_CPU_LITTLE_ENDIAN),y)
> > override flavour := linux-ppc64le
> > else
> > +ifdef CONFIG_PPC64_BUILD_ELF_V2_ABI
> > +override flavour := linux-ppc64-elfv2
> > +else
> > override flavour := linux-ppc64
> > endif
> > +endif
> >
> > quiet_cmd_perl = PERL $@
> > cmd_perl = $(PERL) $(<) $(flavour) > $(@)
> >
> > targets += aesp8-ppc.S ghashp8-ppc.S
> >
> > -$(obj)/aesp8-ppc.S: $(src)/aesp8-ppc.pl FORCE
> > +$(obj)/aesp8-ppc.S: $(src)/aesp8-ppc.pl $(src)/ppc-xlate.pl FORCE
> > $(call if_changed,perl)
> >
> > -$(obj)/ghashp8-ppc.S: $(src)/ghashp8-ppc.pl FORCE
> > +$(obj)/ghashp8-ppc.S: $(src)/ghashp8-ppc.pl $(src)/ppc-xlate.pl FORCE
> > $(call if_changed,perl)
> >
> > clean-files := aesp8-ppc.S ghashp8-ppc.S
> > diff --git a/drivers/crypto/vmx/ppc-xlate.pl b/drivers/crypto/vmx/ppc-xlate.pl
> > index 36db2ef09e5b..b583898c11ae 100644
> > --- a/drivers/crypto/vmx/ppc-xlate.pl
> > +++ b/drivers/crypto/vmx/ppc-xlate.pl
> > @@ -9,6 +9,8 @@ open STDOUT,">$output" || die "can't open $output: $!";
> >
> > my %GLOBALS;
> > my $dotinlocallabels=($flavour=~/linux/)?1:0;
> > +my $elfv2abi=(($flavour =~ /linux-ppc64le/) or ($flavour =~ /linux-ppc64-elfv2/))?1:0;
> > +my $dotfunctions=($elfv2abi=~1)?0:1;
> >
> > ################################################################
> > # directives which need special treatment on different platforms
> > @@ -40,7 +42,7 @@ my $globl = sub {
> > };
> > my $text = sub {
> > my $ret = ($flavour =~ /aix/) ? ".csect\t.text[PR],7" : ".text";
> > - $ret = ".abiversion 2\n".$ret if ($flavour =~ /linux.*64le/);
> > + $ret = ".abiversion 2\n".$ret if ($elfv2abi);
> > $ret;
> > };
> > my $machine = sub {
> > @@ -56,8 +58,8 @@ my $size = sub {
> > if ($flavour =~ /linux/)
> > { shift;
> > my $name = shift; $name =~ s|^[\.\_]||;
> > - my $ret = ".size $name,.-".($flavour=~/64$/?".":"").$name;
> > - $ret .= "\n.size .$name,.-.$name" if ($flavour=~/64$/);
> > + my $ret = ".size $name,.-".($dotfunctions?".":"").$name;
> > + $ret .= "\n.size .$name,.-.$name" if ($dotfunctions);
> > $ret;
> > }
> > else
> > @@ -142,7 +144,7 @@ my $vmr = sub {
> >
> > # Some ABIs specify vrsave, special-purpose register #256, as reserved
> > # for system use.
> > -my $no_vrsave = ($flavour =~ /linux-ppc64le/);
> > +my $no_vrsave = ($elfv2abi);
> > my $mtspr = sub {
> > my ($f,$idx,$ra) = @_;
> > if ($idx == 256 && $no_vrsave) {
> > --
> > 2.23.0
> >