1/* 2 * Copyright (c) 2025 CISPA Helmholtz Center for Information Security 3 * 4 * SPDX-License-Identifier: Apache-2.0 5 */ 6 7#include <zephyr/toolchain.h> 8 9/* 32-bit global defined in C */ 10GDATA(_data_segment_symbol) 11 12GTEXT(_riscv_edge_case_non_paired_hi20_lo12) 13 14/* 15 * Tests an edge case in the RISC-V PSABI: In the medany and the medlow code models, 16 * the compiler emits auipc/lui (U-type) and ld/sw (I-type/S-type) instruction pairs 17 * for accessing a non-local symbol. 18 * The U-type instruction sets the upper 20 bits, the I/S-type the lower 12. 19 * Thus, any address in a 32-bit range from 0 (medlow) / the PC (medany) can be reached. 20 * Often, the U-type and I-type/S-type instruction pairs are adjacent in code. 21 * However, this need not be the case - compilers can re-use the upper 20 bits set by 22 * the U-type instruction with multiple I/S-type instructions, which is a useful optimization 23 * for multiple loads/stores of or within the same symbol. 24 * The U-type instruction can also appear after the I-type in code, e.g., due to control flow. 25 * When the U-type and I/S-type instructions are not in sequence, this triggers an edge case 26 * in the llext loader. 27 * This test triggers this edge case by loading a global, modifying it and storing it back. 28 */ 29SECTION_FUNC(TEXT, _riscv_edge_case_non_paired_hi20_lo12) 30 /* jump beyond the I-type/load instruction initially to break sequence assumption */ 31 j _do_utype 32 33_do_load: 34 /* re-use the upper-bit value set by the U-type below for a load */ 35 lw a0, %pcrel_lo(.LUtype)(a1) 36 37 addi t1, a0, 42 38 39 j _do_store 40 41_do_utype: 42 /* this u-type sets the higher 20 bits of the global */ 43 .LUtype: auipc a1, %pcrel_hi(_data_segment_symbol) 44 45 /* backwards jump to test loading */ 46 j _do_load 47 48_do_store: 49 50 /* write the modified value back for the C code to check */ 51 sw t1, %pcrel_lo(.LUtype)(a1) 52 53 /* return a0, i.e., the value we read, for the C code to check */ 54 ret 55