|
|
|
#!/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: $!";
|