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