// 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 /* * 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(); }