blob: 92b387ca7cdebf1d40ecdba84256a8d93fa86008 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright IBM Corp. 2023
*
* Test EXECUTE (RELATIVE LONG).
* These instructions execute a target instruction. The target instruction is formed
* by reading an instruction from memory and optionally modifying some of its bits.
* The execution of the target instruction is the same as if it was executed
* normally as part of the instruction sequence, except for the instruction
* address and the instruction-length code.
*/
#include <libcflat.h>
/*
* Accesses to the operand of execute-type instructions are instruction fetches.
* Minimum alignment is two, since the relative offset is specified by number of halfwords.
*/
asm ( ".pushsection .text.exrl_targets,\"x\"\n"
" .balign 2\n"
" .popsection\n"
);
/*
* BRANCH AND SAVE, register register variant.
* Saves the next instruction address (address from PSW + length of instruction)
* to the first register. No branch is taken in this test, because 0 is
* specified as target.
* BASR does *not* perform a relative address calculation with an intermediate.
*/
static void test_basr(void)
{
uint64_t ret_addr, after_ex;
report_prefix_push("BASR");
asm volatile ( ".pushsection .text.exrl_targets\n"
"0: basr %[ret_addr],0\n"
" .popsection\n"
" larl %[after_ex],1f\n"
" exrl 0,0b\n"
"1:\n"
: [ret_addr] "=d" (ret_addr),
[after_ex] "=d" (after_ex)
);
report(ret_addr == after_ex, "return address after EX");
report_prefix_pop();
}
/*
* BRANCH RELATIVE AND SAVE.
* According to PoP (Branch-Address Generation), the address calculated relative
* to the instruction address is relative to BRAS when it is the target of an
* execute-type instruction, not relative to the execute-type instruction.
*/
static void test_bras(void)
{
uint64_t after_target, ret_addr, after_ex, branch_addr;
report_prefix_push("BRAS");
asm volatile ( ".pushsection .text.exrl_targets\n"
"0: bras %[ret_addr],1f\n"
" nopr %%r7\n"
"1: larl %[branch_addr],0\n"
" j 4f\n"
" .popsection\n"
" larl %[after_target],1b\n"
" larl %[after_ex],3f\n"
"2: exrl 0,0b\n"
/*
* In case the address calculation is correct, we jump by the relative offset 1b-0b from 0b to 1b.
* In case the address calculation is relative to the exrl (i.e. a test failure),
* put a valid instruction at the same relative offset from the exrl, so the test continues in a
* controlled manner.
*/
"3: larl %[branch_addr],0\n"
"4:\n"
/*
* Clang 15 doesn't like the if below, guard it s.t. we still have the assertion
* when compiling with GCC.
*
* s390x/ex.c:81:4: error: expected absolute expression
* " .if (1b - 0b) != (3b - 2b)\n"
*/
#ifndef __clang__
" .if (1b - 0b) != (3b - 2b)\n"
" .error \"right and wrong target must have same offset\"\n"
" .endif\n"
#endif
: [after_target] "=d" (after_target),
[ret_addr] "=d" (ret_addr),
[after_ex] "=d" (after_ex),
[branch_addr] "=d" (branch_addr)
);
report(after_target == branch_addr, "address calculated relative to BRAS");
report(ret_addr == after_ex, "return address after EX");
report_prefix_pop();
}
/*
* LOAD ADDRESS RELATIVE LONG.
* If it is the target of an execute-type instruction, the address is relative
* to the LARL.
*/
static void test_larl(void)
{
uint64_t target, addr;
report_prefix_push("LARL");
asm volatile ( ".pushsection .text.exrl_targets\n"
"0: larl %[addr],0\n"
" .popsection\n"
" larl %[target],0b\n"
" exrl 0,0b\n"
: [target] "=d" (target),
[addr] "=d" (addr)
);
report(target == addr, "address calculated relative to LARL");
report_prefix_pop();
}
/* LOAD LOGICAL RELATIVE LONG.
* If it is the target of an execute-type instruction, the address is relative
* to the LLGFRL.
*/
static void test_llgfrl(void)
{
uint64_t target, value;
report_prefix_push("LLGFRL");
asm volatile ( ".pushsection .text.exrl_targets\n"
" .balign 4\n"
//operand of llgfrl must be word aligned
"0: llgfrl %[value],0\n"
" .popsection\n"
" llgfrl %[target],0b\n"
//align (pad with nop), in case the wrong operand is used
" .balignw 4,0x0707\n"
" exrl 0,0b\n"
: [target] "=d" (target),
[value] "=d" (value)
);
report(target == value, "loaded correct value");
report_prefix_pop();
}
/*
* COMPARE RELATIVE LONG
* If it is the target of an execute-type instruction, the address is relative
* to the CRL.
*/
static void test_crl(void)
{
uint32_t program_mask, cc, crl_word;
report_prefix_push("CRL");
asm volatile ( ".pushsection .text.exrl_targets\n"
//operand of crl must be word aligned
" .balign 4\n"
"0: crl %[crl_word],0\n"
" .popsection\n"
" lrl %[crl_word],0b\n"
//align (pad with nop), in case the wrong operand is used
" .balignw 4,0x0707\n"
" exrl 0,0b\n"
" ipm %[program_mask]\n"
: [program_mask] "=d" (program_mask),
[crl_word] "=d" (crl_word)
:: "cc"
);
cc = program_mask >> 28;
report(!cc, "operand compared to is relative to CRL");
report_prefix_pop();
}
int main(int argc, char **argv)
{
report_prefix_push("ex");
test_basr();
test_bras();
test_larl();
test_llgfrl();
test_crl();
report_prefix_pop();
return report_summary();
}