forked from ~ljy/RK356X_SDK_RELEASE

hc
2024-10-22 8ac6c7a54ed1b98d142dce24b11c6de6a1e239a5
kernel/tools/testing/selftests/x86/single_step_syscall.c
....@@ -1,15 +1,7 @@
1
+// SPDX-License-Identifier: GPL-2.0-only
12 /*
23 * single_step_syscall.c - single-steps various x86 syscalls
34 * Copyright (c) 2014-2015 Andrew Lutomirski
4
- *
5
- * This program is free software; you can redistribute it and/or modify
6
- * it under the terms and conditions of the GNU General Public License,
7
- * version 2, as published by the Free Software Foundation.
8
- *
9
- * This program is distributed in the hope it will be useful, but
10
- * WITHOUT ANY WARRANTY; without even the implied warranty of
11
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
- * General Public License for more details.
135 *
146 * This is a very simple series of tests that makes system calls with
157 * the TF flag set. This exercises some nasty kernel code in the
....@@ -39,6 +31,8 @@
3931 #include <sys/ptrace.h>
4032 #include <sys/user.h>
4133
34
+#include "helpers.h"
35
+
4236 static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
4337 int flags)
4438 {
....@@ -51,7 +45,19 @@
5145 err(1, "sigaction");
5246 }
5347
54
-static volatile sig_atomic_t sig_traps;
48
+static void clearhandler(int sig)
49
+{
50
+ struct sigaction sa;
51
+ memset(&sa, 0, sizeof(sa));
52
+ sa.sa_handler = SIG_DFL;
53
+ sigemptyset(&sa.sa_mask);
54
+ if (sigaction(sig, &sa, 0))
55
+ err(1, "sigaction");
56
+}
57
+
58
+static volatile sig_atomic_t sig_traps, sig_eflags;
59
+sigjmp_buf jmpbuf;
60
+static unsigned char altstack_data[SIGSTKSZ];
5561
5662 #ifdef __x86_64__
5763 # define REG_IP REG_RIP
....@@ -62,21 +68,6 @@
6268 # define WIDTH "l"
6369 # define INT80_CLOBBERS
6470 #endif
65
-
66
-static unsigned long get_eflags(void)
67
-{
68
- unsigned long eflags;
69
- asm volatile ("pushf" WIDTH "\n\tpop" WIDTH " %0" : "=rm" (eflags));
70
- return eflags;
71
-}
72
-
73
-static void set_eflags(unsigned long eflags)
74
-{
75
- asm volatile ("push" WIDTH " %0\n\tpopf" WIDTH
76
- : : "rm" (eflags) : "flags");
77
-}
78
-
79
-#define X86_EFLAGS_TF (1UL << 8)
8071
8172 static void sigtrap(int sig, siginfo_t *info, void *ctx_void)
8273 {
....@@ -98,6 +89,25 @@
9889 }
9990 }
10091
92
+static char const * const signames[] = {
93
+ [SIGSEGV] = "SIGSEGV",
94
+ [SIGBUS] = "SIBGUS",
95
+ [SIGTRAP] = "SIGTRAP",
96
+ [SIGILL] = "SIGILL",
97
+};
98
+
99
+static void print_and_longjmp(int sig, siginfo_t *si, void *ctx_void)
100
+{
101
+ ucontext_t *ctx = ctx_void;
102
+
103
+ printf("\tGot %s with RIP=%lx, TF=%ld\n", signames[sig],
104
+ (unsigned long)ctx->uc_mcontext.gregs[REG_IP],
105
+ (unsigned long)ctx->uc_mcontext.gregs[REG_EFL] & X86_EFLAGS_TF);
106
+
107
+ sig_eflags = (unsigned long)ctx->uc_mcontext.gregs[REG_EFL];
108
+ siglongjmp(jmpbuf, 1);
109
+}
110
+
101111 static void check_result(void)
102112 {
103113 unsigned long new_eflags = get_eflags();
....@@ -115,6 +125,22 @@
115125
116126 printf("[OK]\tSurvived with TF set and %d traps\n", (int)sig_traps);
117127 sig_traps = 0;
128
+}
129
+
130
+static void fast_syscall_no_tf(void)
131
+{
132
+ sig_traps = 0;
133
+ printf("[RUN]\tFast syscall with TF cleared\n");
134
+ fflush(stdout); /* Force a syscall */
135
+ if (get_eflags() & X86_EFLAGS_TF) {
136
+ printf("[FAIL]\tTF is now set\n");
137
+ exit(1);
138
+ }
139
+ if (sig_traps) {
140
+ printf("[FAIL]\tGot SIGTRAP\n");
141
+ exit(1);
142
+ }
143
+ printf("[OK]\tNothing unexpected happened\n");
118144 }
119145
120146 int main()
....@@ -171,17 +197,46 @@
171197 check_result();
172198
173199 /* Now make sure that another fast syscall doesn't set TF again. */
174
- printf("[RUN]\tFast syscall with TF cleared\n");
175
- fflush(stdout); /* Force a syscall */
176
- if (get_eflags() & X86_EFLAGS_TF) {
177
- printf("[FAIL]\tTF is now set\n");
200
+ fast_syscall_no_tf();
201
+
202
+ /*
203
+ * And do a forced SYSENTER to make sure that this works even if
204
+ * fast syscalls don't use SYSENTER.
205
+ *
206
+ * Invoking SYSENTER directly breaks all the rules. Just handle
207
+ * the SIGSEGV.
208
+ */
209
+ if (sigsetjmp(jmpbuf, 1) == 0) {
210
+ unsigned long nr = SYS_getpid;
211
+ printf("[RUN]\tSet TF and check SYSENTER\n");
212
+ stack_t stack = {
213
+ .ss_sp = altstack_data,
214
+ .ss_size = SIGSTKSZ,
215
+ };
216
+ if (sigaltstack(&stack, NULL) != 0)
217
+ err(1, "sigaltstack");
218
+ sethandler(SIGSEGV, print_and_longjmp,
219
+ SA_RESETHAND | SA_ONSTACK);
220
+ sethandler(SIGILL, print_and_longjmp, SA_RESETHAND);
221
+ set_eflags(get_eflags() | X86_EFLAGS_TF);
222
+ /* Clear EBP first to make sure we segfault cleanly. */
223
+ asm volatile ("xorl %%ebp, %%ebp; SYSENTER" : "+a" (nr) :: "flags", "rcx"
224
+#ifdef __x86_64__
225
+ , "r11"
226
+#endif
227
+ );
228
+
229
+ /* We're unreachable here. SYSENTER forgets RIP. */
230
+ }
231
+ clearhandler(SIGSEGV);
232
+ clearhandler(SIGILL);
233
+ if (!(sig_eflags & X86_EFLAGS_TF)) {
234
+ printf("[FAIL]\tTF was cleared\n");
178235 exit(1);
179236 }
180
- if (sig_traps) {
181
- printf("[FAIL]\tGot SIGTRAP\n");
182
- exit(1);
183
- }
184
- printf("[OK]\tNothing unexpected happened\n");
237
+
238
+ /* Now make sure that another fast syscall doesn't set TF again. */
239
+ fast_syscall_no_tf();
185240
186241 return 0;
187242 }