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