1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: LGPL-2.1-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright (C) 2005-2006 David Gibson & Adam Litke, IBM Corporation.
4f08c3bdfSopenharmony_ci * Copyright (c) Linux Test Project, 2022-2023
5f08c3bdfSopenharmony_ci * Author: David Gibson & Adam Litke
6f08c3bdfSopenharmony_ci */
7f08c3bdfSopenharmony_ci
8f08c3bdfSopenharmony_ci/*\
9f08c3bdfSopenharmony_ci * [Description]
10f08c3bdfSopenharmony_ci *
11f08c3bdfSopenharmony_ci * Older ppc64 kernels don't properly flush dcache to icache before
12f08c3bdfSopenharmony_ci * giving a cleared page to userspace.  With some exceedingly
13f08c3bdfSopenharmony_ci * hairy code, this attempts to test for this bug.
14f08c3bdfSopenharmony_ci *
15f08c3bdfSopenharmony_ci * This test will never trigger (obviously) on machines with coherent
16f08c3bdfSopenharmony_ci * icache and dcache (including x86 and POWER5).  On any given run,
17f08c3bdfSopenharmony_ci * even on a buggy kernel there's a chance the bug won't trigger -
18f08c3bdfSopenharmony_ci * either because we don't get the same physical page back when we
19f08c3bdfSopenharmony_ci * remap, or because the icache happens to get flushed in the interim.
20f08c3bdfSopenharmony_ci */
21f08c3bdfSopenharmony_ci
22f08c3bdfSopenharmony_ci#if defined(__clang__)
23f08c3bdfSopenharmony_ci	#pragma clang optimize off
24f08c3bdfSopenharmony_ci#endif
25f08c3bdfSopenharmony_ci
26f08c3bdfSopenharmony_ci#define _GNU_SOURCE
27f08c3bdfSopenharmony_ci#include "hugetlb.h"
28f08c3bdfSopenharmony_ci
29f08c3bdfSopenharmony_ci#if defined(__powerpc__) || defined(__powerpc64__) || defined(__ia64__) || \
30f08c3bdfSopenharmony_ci	defined(__s390__) || defined(__s390x__) || defined(__sparc__) || \
31f08c3bdfSopenharmony_ci	defined(__aarch64__) || (defined(__riscv) && __riscv_xlen == 64) || \
32f08c3bdfSopenharmony_ci	defined(__i386__) || defined(__x86_64__) || defined(__arm__)
33f08c3bdfSopenharmony_ci
34f08c3bdfSopenharmony_ci#include <setjmp.h>
35f08c3bdfSopenharmony_ci
36f08c3bdfSopenharmony_ci#define SUCC_JMP 1
37f08c3bdfSopenharmony_ci#define FAIL_JMP 2
38f08c3bdfSopenharmony_ci#define COPY_SIZE	128
39f08c3bdfSopenharmony_ci
40f08c3bdfSopenharmony_ci/* Seems to be enough to trigger reliably */
41f08c3bdfSopenharmony_ci#define NUM_REPETITIONS	64
42f08c3bdfSopenharmony_ci#define MNTPOINT "hugetlbfs/"
43f08c3bdfSopenharmony_cistatic long hpage_size;
44f08c3bdfSopenharmony_cistatic int  fd = -1;
45f08c3bdfSopenharmony_ci
46f08c3bdfSopenharmony_cistatic void cacheflush(void *p)
47f08c3bdfSopenharmony_ci{
48f08c3bdfSopenharmony_ci#if defined(__powerpc__)
49f08c3bdfSopenharmony_ci	asm volatile("dcbst 0,%0; sync; icbi 0,%0; isync" : : "r"(p));
50f08c3bdfSopenharmony_ci#elif defined(__arm__) || defined(__aarch64__)
51f08c3bdfSopenharmony_ci	__clear_cache(p, p + COPY_SIZE);
52f08c3bdfSopenharmony_ci#else
53f08c3bdfSopenharmony_ci	(void)p;
54f08c3bdfSopenharmony_ci#endif
55f08c3bdfSopenharmony_ci}
56f08c3bdfSopenharmony_ci
57f08c3bdfSopenharmony_cistatic void jumpfunc(int copy, void *p)
58f08c3bdfSopenharmony_ci{
59f08c3bdfSopenharmony_ci	/*
60f08c3bdfSopenharmony_ci	 * gcc bug workaround: if there is exactly one &&label
61f08c3bdfSopenharmony_ci	 * construct in the function, gcc assumes the computed goto
62f08c3bdfSopenharmony_ci	 * goes there, leading to the complete elision of the goto in
63f08c3bdfSopenharmony_ci	 * this case
64f08c3bdfSopenharmony_ci	 */
65f08c3bdfSopenharmony_ci	void *l = &&dummy;
66f08c3bdfSopenharmony_ci
67f08c3bdfSopenharmony_ci	l = &&jumplabel;
68f08c3bdfSopenharmony_ci
69f08c3bdfSopenharmony_ci	if (copy) {
70f08c3bdfSopenharmony_ci		memcpy(p, l, COPY_SIZE);
71f08c3bdfSopenharmony_ci		cacheflush(p);
72f08c3bdfSopenharmony_ci	}
73f08c3bdfSopenharmony_ci
74f08c3bdfSopenharmony_ci	goto *p;
75f08c3bdfSopenharmony_ci dummy:
76f08c3bdfSopenharmony_ci	tst_res(TWARN, "unreachable?");
77f08c3bdfSopenharmony_ci
78f08c3bdfSopenharmony_ci jumplabel:
79f08c3bdfSopenharmony_ci	return;
80f08c3bdfSopenharmony_ci}
81f08c3bdfSopenharmony_ci
82f08c3bdfSopenharmony_cistatic sigjmp_buf sig_escape;
83f08c3bdfSopenharmony_cistatic void *sig_expected;
84f08c3bdfSopenharmony_ci
85f08c3bdfSopenharmony_cistatic void sig_handler(int signum, siginfo_t *si, void *uc)
86f08c3bdfSopenharmony_ci{
87f08c3bdfSopenharmony_ci#if defined(__powerpc__) || defined(__powerpc64__) || defined(__ia64__) || \
88f08c3bdfSopenharmony_ci	defined(__s390__) || defined(__s390x__) || defined(__sparc__) || \
89f08c3bdfSopenharmony_ci	defined(__aarch64__) || (defined(__riscv) && __riscv_xlen == 64)
90f08c3bdfSopenharmony_ci	/* On powerpc, ia64, s390 and Aarch64, 0 bytes are an illegal
91f08c3bdfSopenharmony_ci	 * instruction, so, if the icache is cleared properly, we SIGILL
92f08c3bdfSopenharmony_ci	 * as soon as we jump into the cleared page
93f08c3bdfSopenharmony_ci	 */
94f08c3bdfSopenharmony_ci	if (signum == SIGILL) {
95f08c3bdfSopenharmony_ci		tst_res(TINFO, "SIGILL at %p (sig_expected=%p)", si->si_addr,
96f08c3bdfSopenharmony_ci				sig_expected);
97f08c3bdfSopenharmony_ci		if (si->si_addr == sig_expected)
98f08c3bdfSopenharmony_ci			siglongjmp(sig_escape, SUCC_JMP);
99f08c3bdfSopenharmony_ci		siglongjmp(sig_escape, FAIL_JMP + SIGILL);
100f08c3bdfSopenharmony_ci	}
101f08c3bdfSopenharmony_ci#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__)
102f08c3bdfSopenharmony_ci	/* On x86, zero bytes form a valid instruction:
103f08c3bdfSopenharmony_ci	 *	add %al,(%eax)		(i386)
104f08c3bdfSopenharmony_ci	 * or	add %al,(%rax)		(x86_64)
105f08c3bdfSopenharmony_ci	 *
106f08c3bdfSopenharmony_ci	 * So, behaviour depends on the contents of [ER]AX, which in
107f08c3bdfSopenharmony_ci	 * turn depends on the details of code generation.  If [ER]AX
108f08c3bdfSopenharmony_ci	 * contains a valid pointer, we will execute the instruction
109f08c3bdfSopenharmony_ci	 * repeatedly until we run off that hugepage and get a SIGBUS
110f08c3bdfSopenharmony_ci	 * on the second, truncated page.  If [ER]AX does not contain
111f08c3bdfSopenharmony_ci	 * a valid pointer, we will SEGV on the first instruction in
112f08c3bdfSopenharmony_ci	 * the cleared page.  We check for both possibilities
113f08c3bdfSopenharmony_ci	 * below.
114f08c3bdfSopenharmony_ci	 *
115f08c3bdfSopenharmony_ci	 * On 32 bit ARM, zero bytes are interpreted as follows:
116f08c3bdfSopenharmony_ci	 *  andeq	r0, r0, r0	(ARM state, 4 bytes)
117f08c3bdfSopenharmony_ci	 *  movs	r0, r0		(Thumb state, 2 bytes)
118f08c3bdfSopenharmony_ci	 *
119f08c3bdfSopenharmony_ci	 * So, we only expect to run off the end of the huge page and
120f08c3bdfSopenharmony_ci	 * generate a SIGBUS.
121f08c3bdfSopenharmony_ci	 */
122f08c3bdfSopenharmony_ci	if (signum == SIGBUS) {
123f08c3bdfSopenharmony_ci		tst_res(TINFO, "SIGBUS at %p (sig_expected=%p)", si->si_addr,
124f08c3bdfSopenharmony_ci				sig_expected);
125f08c3bdfSopenharmony_ci		if (sig_expected
126f08c3bdfSopenharmony_ci		    && (PALIGN(sig_expected, hpage_size)
127f08c3bdfSopenharmony_ci			== si->si_addr)) {
128f08c3bdfSopenharmony_ci			siglongjmp(sig_escape, SUCC_JMP);
129f08c3bdfSopenharmony_ci		}
130f08c3bdfSopenharmony_ci		siglongjmp(sig_escape, FAIL_JMP + SIGBUS);
131f08c3bdfSopenharmony_ci	}
132f08c3bdfSopenharmony_ci#if defined(__x86_64__) || defined(__i386__)
133f08c3bdfSopenharmony_ci	if (signum == SIGSEGV) {
134f08c3bdfSopenharmony_ci#ifdef __x86_64__
135f08c3bdfSopenharmony_ci		void *pc = (void *)((ucontext_t *)uc)->uc_mcontext.gregs[REG_RIP];
136f08c3bdfSopenharmony_ci#else
137f08c3bdfSopenharmony_ci		void *pc = (void *)((ucontext_t *)uc)->uc_mcontext.gregs[REG_EIP];
138f08c3bdfSopenharmony_ci#endif
139f08c3bdfSopenharmony_ci		tst_res(TINFO, "SIGSEGV at %p, PC=%p (sig_expected=%p)",
140f08c3bdfSopenharmony_ci				si->si_addr, pc, sig_expected);
141f08c3bdfSopenharmony_ci		if (sig_expected == pc)
142f08c3bdfSopenharmony_ci			siglongjmp(sig_escape, SUCC_JMP);
143f08c3bdfSopenharmony_ci		siglongjmp(sig_escape, FAIL_JMP + SIGSEGV);
144f08c3bdfSopenharmony_ci	}
145f08c3bdfSopenharmony_ci#endif
146f08c3bdfSopenharmony_ci#endif
147f08c3bdfSopenharmony_ci}
148f08c3bdfSopenharmony_ci
149f08c3bdfSopenharmony_cistatic int test_once(int fd)
150f08c3bdfSopenharmony_ci{
151f08c3bdfSopenharmony_ci	void *p, *q;
152f08c3bdfSopenharmony_ci
153f08c3bdfSopenharmony_ci	SAFE_FTRUNCATE(fd, 0);
154f08c3bdfSopenharmony_ci
155f08c3bdfSopenharmony_ci	switch (sigsetjmp(sig_escape, 1)) {
156f08c3bdfSopenharmony_ci	case SUCC_JMP:
157f08c3bdfSopenharmony_ci		sig_expected = NULL;
158f08c3bdfSopenharmony_ci		SAFE_FTRUNCATE(fd, 0);
159f08c3bdfSopenharmony_ci		return 0;
160f08c3bdfSopenharmony_ci	case FAIL_JMP + SIGILL:
161f08c3bdfSopenharmony_ci		tst_res(TFAIL, "SIGILL somewhere unexpected");
162f08c3bdfSopenharmony_ci		return -1;
163f08c3bdfSopenharmony_ci	case FAIL_JMP + SIGBUS:
164f08c3bdfSopenharmony_ci		tst_res(TFAIL, "SIGBUS somewhere unexpected");
165f08c3bdfSopenharmony_ci		return -1;
166f08c3bdfSopenharmony_ci	case FAIL_JMP + SIGSEGV:
167f08c3bdfSopenharmony_ci		tst_res(TFAIL, "SIGSEGV somewhere unexpected");
168f08c3bdfSopenharmony_ci		return -1;
169f08c3bdfSopenharmony_ci	default:
170f08c3bdfSopenharmony_ci		break;
171f08c3bdfSopenharmony_ci	}
172f08c3bdfSopenharmony_ci	p = SAFE_MMAP(NULL, 2*hpage_size, PROT_READ|PROT_WRITE|PROT_EXEC,
173f08c3bdfSopenharmony_ci		 MAP_SHARED, fd, 0);
174f08c3bdfSopenharmony_ci
175f08c3bdfSopenharmony_ci	SAFE_FTRUNCATE(fd, hpage_size);
176f08c3bdfSopenharmony_ci
177f08c3bdfSopenharmony_ci	q = p + hpage_size - COPY_SIZE;
178f08c3bdfSopenharmony_ci
179f08c3bdfSopenharmony_ci	jumpfunc(1, q);
180f08c3bdfSopenharmony_ci
181f08c3bdfSopenharmony_ci	SAFE_FTRUNCATE(fd, 0);
182f08c3bdfSopenharmony_ci	p = SAFE_MMAP(p, hpage_size, PROT_READ|PROT_WRITE|PROT_EXEC,
183f08c3bdfSopenharmony_ci		 MAP_SHARED|MAP_FIXED, fd, 0);
184f08c3bdfSopenharmony_ci
185f08c3bdfSopenharmony_ci	q = p + hpage_size - COPY_SIZE;
186f08c3bdfSopenharmony_ci	sig_expected = q;
187f08c3bdfSopenharmony_ci
188f08c3bdfSopenharmony_ci	jumpfunc(0, q); /* This should blow up */
189f08c3bdfSopenharmony_ci
190f08c3bdfSopenharmony_ci	tst_res(TFAIL, "icache unclean");
191f08c3bdfSopenharmony_ci	return -1;
192f08c3bdfSopenharmony_ci}
193f08c3bdfSopenharmony_ci
194f08c3bdfSopenharmony_cistatic void run_test(void)
195f08c3bdfSopenharmony_ci{
196f08c3bdfSopenharmony_ci	int i;
197f08c3bdfSopenharmony_ci
198f08c3bdfSopenharmony_ci	struct sigaction sa = {
199f08c3bdfSopenharmony_ci		.sa_sigaction = sig_handler,
200f08c3bdfSopenharmony_ci		.sa_flags = SA_SIGINFO,
201f08c3bdfSopenharmony_ci	};
202f08c3bdfSopenharmony_ci
203f08c3bdfSopenharmony_ci	SAFE_SIGACTION(SIGILL, &sa, NULL);
204f08c3bdfSopenharmony_ci	SAFE_SIGACTION(SIGBUS, &sa, NULL);
205f08c3bdfSopenharmony_ci	SAFE_SIGACTION(SIGSEGV, &sa, NULL);
206f08c3bdfSopenharmony_ci
207f08c3bdfSopenharmony_ci	fd = tst_creat_unlinked(MNTPOINT, 0);
208f08c3bdfSopenharmony_ci
209f08c3bdfSopenharmony_ci	for (i = 0; i < NUM_REPETITIONS; i++)
210f08c3bdfSopenharmony_ci		if (test_once(fd))
211f08c3bdfSopenharmony_ci			goto cleanup;
212f08c3bdfSopenharmony_ci
213f08c3bdfSopenharmony_ci	tst_res(TPASS, "Successfully tested dcache to icache flush");
214f08c3bdfSopenharmony_cicleanup:
215f08c3bdfSopenharmony_ci	SAFE_CLOSE(fd);
216f08c3bdfSopenharmony_ci}
217f08c3bdfSopenharmony_ci
218f08c3bdfSopenharmony_cistatic void setup(void)
219f08c3bdfSopenharmony_ci{
220f08c3bdfSopenharmony_ci	hpage_size = SAFE_READ_MEMINFO("Hugepagesize:")*1024;
221f08c3bdfSopenharmony_ci}
222f08c3bdfSopenharmony_ci
223f08c3bdfSopenharmony_cistatic void cleanup(void)
224f08c3bdfSopenharmony_ci{
225f08c3bdfSopenharmony_ci	if (fd > 0)
226f08c3bdfSopenharmony_ci		SAFE_CLOSE(fd);
227f08c3bdfSopenharmony_ci}
228f08c3bdfSopenharmony_ci
229f08c3bdfSopenharmony_cistatic struct tst_test test = {
230f08c3bdfSopenharmony_ci	.tags = (struct tst_tag[]) {
231f08c3bdfSopenharmony_ci		{"linux-git", "cbf52afdc0eb"},
232f08c3bdfSopenharmony_ci		{}
233f08c3bdfSopenharmony_ci	},
234f08c3bdfSopenharmony_ci	.needs_root = 1,
235f08c3bdfSopenharmony_ci	.mntpoint = MNTPOINT,
236f08c3bdfSopenharmony_ci	.needs_hugetlbfs = 1,
237f08c3bdfSopenharmony_ci	.needs_tmpdir = 1,
238f08c3bdfSopenharmony_ci	.setup = setup,
239f08c3bdfSopenharmony_ci	.cleanup = cleanup,
240f08c3bdfSopenharmony_ci	.test_all = run_test,
241f08c3bdfSopenharmony_ci	.hugepages = {3, TST_NEEDS},
242f08c3bdfSopenharmony_ci};
243f08c3bdfSopenharmony_ci#else
244f08c3bdfSopenharmony_ci	TST_TEST_TCONF("Signal handler for this architecture hasn't been written");
245f08c3bdfSopenharmony_ci#endif
246