1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) International Business Machines Corp., 2008
4 * Copyright (c) Paul Mackerras, IBM Corp., 2008
5 * Copyright (c) 2018 Linux Test Project
6 */
7
8 /*
9 * Test little-endian mode switch system call. Requires a 64-bit
10 * processor that supports little-endian mode,such as POWER6.
11 */
12
13 #include <errno.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include <elf.h>
18 #include <sys/types.h>
19 #include <sys/wait.h>
20 #include "tst_test.h"
21
22 #if defined(__powerpc64__) || defined(__powerpc__)
23 # ifndef PPC_FEATURE_TRUE_LE
24 # define PPC_FEATURE_TRUE_LE 0x00000002
25 # endif
26
27 # define TST_NO_DEFAULT_MAIN
28
29 /*
30 * Make minimal call to 0x1ebe. If we get ENOSYS then syscall is not
31 * available, likely because of:
32 * commit 727f13616c45 ("powerpc: Disable the fast-endian switch syscall by default")
33 * If we get any other outcome, including crashes with various signals,
34 * then we assume syscall is available and carry on with the test.
35 */
check_le_switch_supported(void)36 void check_le_switch_supported(void)
37 {
38 int status;
39
40 if (SAFE_FORK() == 0) {
41 syscall(0x1ebe);
42 exit(errno);
43 }
44
45 SAFE_WAIT(&status);
46 if (WIFSIGNALED(status)) {
47 int sig = WTERMSIG(status);
48
49 tst_res(TINFO, "check exited with sig %d", sig);
50 } else if (WIFEXITED(status)) {
51 int rc = WEXITSTATUS(status);
52
53 tst_res(TINFO, "check exited with %d", rc);
54 if (rc == ENOSYS)
55 tst_brk(TCONF, "fast endian switch (0x1ebe) N/A");
56 }
57 }
58
test_le_switch(void)59 void test_le_switch(void)
60 {
61 int status;
62
63 if (SAFE_FORK() == 0) {
64 register int r0 asm("r0") = 0x1ebe;
65
66 asm volatile ("sc; .long 0x02000044"
67 : "=&r" (r0)
68 : "0"(r0)
69 : "cr0", "r9", "r10", "r11", "r12");
70 exit(0);
71 }
72
73 SAFE_WAIT(&status);
74 if (WIFSIGNALED(status)) {
75 int sig = WTERMSIG(status);
76
77 tst_res(TFAIL, "test exited with sig %d", sig);
78 } else if (WIFEXITED(status)) {
79 int rc = WEXITSTATUS(status);
80
81 if (rc != 0)
82 tst_res(TFAIL, "test exited with %d", rc);
83 else
84 tst_res(TPASS, "endian_switch() syscall tests passed");
85 }
86 }
87
endian_test(void)88 static void endian_test(void)
89 {
90 check_le_switch_supported();
91 test_le_switch();
92 }
93
94 static struct tst_test test = {
95 .test_all = endian_test,
96 .min_kver = "2.6.26",
97 .forks_child = 1,
98 };
99
main4(int argc,char ** argv,LTP_ATTRIBUTE_UNUSED char ** envp,unsigned long * auxv)100 int main4(int argc, char **argv, LTP_ATTRIBUTE_UNUSED char **envp,
101 unsigned long *auxv)
102 {
103 for (; *auxv != AT_NULL && *auxv != AT_HWCAP; auxv += 2)
104 ;
105
106 if (!(auxv[0] == AT_HWCAP && (auxv[1] & PPC_FEATURE_TRUE_LE)))
107 tst_brk(TCONF, "Processor does not support little-endian mode");
108
109 tst_run_tcases(argc, argv, &test);
110 return 0;
111 }
112
113 #else /* defined (__powerpc64__) || (__powerpc__) */
114 TST_TEST_TCONF("This system does not support running of switch() syscall");
115 #endif
116