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