| #!/usr/bin/env perl |
| # Copyright (c) 2019, Google Inc. |
| # |
| # Permission to use, copy, modify, and/or distribute this software for any |
| # purpose with or without fee is hereby granted, provided that the above |
| # copyright notice and this permission notice appear in all copies. |
| # |
| # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
| # SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
| # OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
| # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| |
| # This file defines helper functions for crypto/test/abi_test.h on ppc64le. See |
| # that header for details on how to use this. |
| # |
| # For convenience, this file is linked into libcrypto, where consuming builds |
| # already support architecture-specific sources. The static linker should drop |
| # this code in non-test binaries. This includes a shared library build of |
| # libcrypto, provided --gc-sections or equivalent is used. |
| # |
| # References: |
| # |
| # ELFv2: http://openpowerfoundation.org/wp-content/uploads/resources/leabi/leabi-20170510.pdf |
| |
| use strict; |
| |
| my $flavour = shift; |
| my $output = shift; |
| if ($flavour =~ /\./) { $output = $flavour; undef $flavour; } |
| |
| $0 =~ m/(.*[\/\\])[^\/\\]+$/; |
| my $dir = $1; |
| my $xlate; |
| ( $xlate="${dir}ppc-xlate.pl" and -f $xlate ) or |
| ( $xlate="${dir}../../perlasm/ppc-xlate.pl" and -f $xlate) or |
| die "can't locate ppc-xlate.pl"; |
| |
| open OUT, "| \"$^X\" \"$xlate\" $flavour \"$output\""; |
| *STDOUT = *OUT; |
| |
| unless ($flavour =~ /linux.*64le/) { |
| die "This file only supports the ELFv2 ABI, used by ppc64le"; |
| } |
| |
| my $code = ""; |
| |
| sub load_or_store_regs { |
| # $op is "l" or "st". |
| my ($op, $base_reg, $base_offset) = @_; |
| # Vector registers. |
| foreach (20..31) { |
| my $offset = $base_offset + ($_ - 20) * 16; |
| # Vector registers only support indexed register addressing. |
| $code .= "\tli\tr11, $offset\n"; |
| $code .= "\t${op}vx\tv$_, r11, $base_reg\n"; |
| } |
| # Save general registers. |
| foreach (14..31) { |
| my $offset = $base_offset + 192 + ($_ - 14) * 8; |
| $code .= "\t${op}d\tr$_, $offset($base_reg)\n"; |
| } |
| # Save floating point registers. |
| foreach (14..31) { |
| my $offset = $base_offset + 336 + ($_ - 14) * 8; |
| $code .= "\t${op}fd\tf$_, $offset($base_reg)\n"; |
| } |
| } |
| |
| sub load_regs { |
| my ($base_reg, $base_offset) = @_; |
| load_or_store_regs("l", $base_reg, $base_offset); |
| } |
| |
| sub store_regs { |
| my ($base_reg, $base_offset) = @_; |
| load_or_store_regs("st", $base_reg, $base_offset); |
| } |
| |
| my ($func, $state, $argv, $argc) = ("r3", "r4", "r5", "r6"); |
| $code .= <<____; |
| .machine "any" |
| .text |
| |
| # abi_test_trampoline loads callee-saved registers from |state|, calls |func| |
| # with |argv|, then saves the callee-saved registers into |state|. It returns |
| # the result of |func|. The |unwind| argument is unused. |
| # uint64_t abi_test_trampoline(void (*func)(...), CallerState *state, |
| # const uint64_t *argv, size_t argc, |
| # uint64_t unwind); |
| .globl abi_test_trampoline |
| .align 5 |
| abi_test_trampoline: |
| # LR is saved into the caller's stack frame. |
| mflr r0 |
| std r0, 16(r1) |
| |
| # Allocate 66*8 = 528 bytes of stack frame. From the top of the stack |
| # to the bottom, the stack frame is: |
| # |
| # 0(r1) - Back chain pointer |
| # 8(r1) - CR save area |
| # 16(r1) - LR save area (for |func|) |
| # 24(r1) - TOC pointer save area |
| # 32(r1) - Saved copy of |state| |
| # 40(r1) - Padding |
| # 48(r1) - Vector register save area (v20-v31, 12 registers) |
| # 240(r1) - General register save area (r14-r31, 18 registers) |
| # 384(r1) - Floating point register save area (f14-f31, 18 registers) |
| # |
| # Note the layouts of the register save areas and CallerState match. |
| # |
| # In the ELFv2 ABI, the parameter save area is optional if the function |
| # is non-variadic and all parameters fit in registers. We only support |
| # such functions, so we omit it to test that |func| does not rely on it. |
| stdu r1, -528(r1) |
| |
| mfcr r0 |
| std r0, 8(r1) # Save CR |
| std r2, 24(r1) # Save TOC |
| std $state, 32(r1) # Save |state| |
| ____ |
| # Save registers to the stack. |
| store_regs("r1", 48); |
| # Load registers from the caller. |
| load_regs($state, 0); |
| $code .= <<____; |
| # Load CR from |state|. |
| ld r0, 480($state) |
| mtcr r0 |
| |
| # Move parameters into temporary registers so they are not clobbered. |
| addi r11, $argv, -8 # Adjust for ldu below |
| mr r12, $func |
| |
| # Load parameters into registers. |
| cmpdi $argc, 0 |
| beq .Largs_done |
| mtctr $argc |
| ldu r3, 8(r11) |
| bdz .Largs_done |
| ldu r4, 8(r11) |
| bdz .Largs_done |
| ldu r5, 8(r11) |
| bdz .Largs_done |
| ldu r6, 8(r11) |
| bdz .Largs_done |
| ldu r7, 8(r11) |
| bdz .Largs_done |
| ldu r8, 8(r11) |
| bdz .Largs_done |
| ldu r9, 8(r11) |
| bdz .Largs_done |
| ldu r10, 8(r11) |
| |
| .Largs_done: |
| li r2, 0 # Clear TOC to test |func|'s global entry point |
| mtctr r12 |
| bctrl |
| ld r2, 24(r1) # Restore TOC |
| |
| ld $state, 32(r1) # Reload |state| |
| ____ |
| # Output resulting registers to the caller. |
| store_regs($state, 0); |
| # Restore registers from the stack. |
| load_regs("r1", 48); |
| $code .= <<____; |
| mfcr r0 |
| std r0, 480($state) # Output CR to caller |
| ld r0, 8(r1) |
| mtcrf 0b00111000, r0 # Restore CR2-CR4 |
| addi r1, r1, 528 |
| ld r0, 16(r1) # Restore LR |
| mtlr r0 |
| blr |
| .size abi_test_trampoline,.-abi_test_trampoline |
| ____ |
| |
| # abi_test_clobber_* clobbers the corresponding register. These are used to test |
| # the ABI-testing framework. |
| foreach (0..31) { |
| # r1 is the stack pointer. r13 is the thread pointer. |
| next if ($_ == 1 || $_ == 13); |
| $code .= <<____; |
| .globl abi_test_clobber_r$_ |
| .align 5 |
| abi_test_clobber_r$_: |
| li r$_, 0 |
| blr |
| .size abi_test_clobber_r$_,.-abi_test_clobber_r$_ |
| ____ |
| } |
| |
| foreach (0..31) { |
| $code .= <<____; |
| .globl abi_test_clobber_f$_ |
| .align 4 |
| abi_test_clobber_f$_: |
| li r0, 0 |
| # Use the red zone. |
| std r0, -8(r1) |
| lfd f$_, -8(r1) |
| blr |
| .size abi_test_clobber_f$_,.-abi_test_clobber_f$_ |
| ____ |
| } |
| |
| foreach (0..31) { |
| $code .= <<____; |
| .globl abi_test_clobber_v$_ |
| .align 4 |
| abi_test_clobber_v$_: |
| vxor v$_, v$_, v$_ |
| blr |
| .size abi_test_clobber_v$_,.-abi_test_clobber_v$_ |
| ____ |
| } |
| |
| foreach (0..7) { |
| # PPC orders CR fields in big-endian, so the mask is reversed from what one |
| # would expect. |
| my $mask = 1 << (7 - $_); |
| $code .= <<____; |
| .globl abi_test_clobber_cr$_ |
| .align 4 |
| abi_test_clobber_cr$_: |
| # Flip the bits on cr$_ rather than setting to zero. With a four-bit |
| # register, zeroing it will do nothing 1 in 16 times. |
| mfcr r0 |
| not r0, r0 |
| mtcrf $mask, r0 |
| blr |
| .size abi_test_clobber_cr$_,.-abi_test_clobber_cr$_ |
| ____ |
| } |
| |
| $code .= <<____; |
| .globl abi_test_clobber_ctr |
| .align 4 |
| abi_test_clobber_ctr: |
| li r0, 0 |
| mtctr r0 |
| blr |
| .size abi_test_clobber_ctr,.-abi_test_clobber_ctr |
| |
| .globl abi_test_clobber_lr |
| .align 4 |
| abi_test_clobber_lr: |
| mflr r0 |
| mtctr r0 |
| li r0, 0 |
| mtlr r0 |
| bctr |
| .size abi_test_clobber_lr,.-abi_test_clobber_lr |
| |
| ____ |
| |
| print $code; |
| close STDOUT or die "error closing STDOUT: $!"; |