1 // SPDX-License-Identifier: GPL-2.0-or-later
2
3 /*
4 * Copyright (C) 2018 Intel Corporation
5 * Author: Neri, Ricardo <[email protected]>
6 * Pengfei, Xu <[email protected]>
7 */
8
9 /*
10 * This test will check if Intel umip(User-Mode Execution Prevention) is
11 * working.
12 *
13 * Intel CPU of ICE lake or newer is required for the test
14 * kconfig requirement:CONFIG_X86_INTEL_UMIP=y
15 */
16
17 #define _GNU_SOURCE
18 #include <unistd.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <sys/wait.h>
23 #include <signal.h>
24
25 #include "tst_test.h"
26 #include "tst_safe_stdio.h"
27
28 #define CPUINFO_FILE "/proc/cpuinfo"
29
30 #define GDT_LEN 10
31 #define IDT_LEN 10
32
33 #ifdef __x86_64__
34
asm_sgdt(void)35 static void asm_sgdt(void)
36 {
37 unsigned char val[GDT_LEN];
38
39 memset(val, 0, sizeof(val));
40 tst_res(TINFO, "TEST sgdt, sgdt result save at [%p]", val);
41 asm volatile("sgdt %0\n" : "=m" (val));
42 exit(0);
43 }
44
asm_sidt(void)45 static void asm_sidt(void)
46 {
47 unsigned char val[IDT_LEN];
48
49 memset(val, 0, sizeof(val));
50 tst_res(TINFO, "TEST sidt, sidt result save at [%p]", val);
51 asm volatile("sidt %0\n" : "=m" (val));
52 exit(0);
53 }
54
asm_sldt(void)55 static void asm_sldt(void)
56 {
57 unsigned long val;
58
59 tst_res(TINFO, "TEST sldt, sldt result save at [%p]", &val);
60 asm volatile("sldt %0\n" : "=m" (val));
61 exit(0);
62 }
63
asm_smsw(void)64 static void asm_smsw(void)
65 {
66 unsigned long val;
67
68 tst_res(TINFO, "TEST smsw, smsw result save at [%p]", &val);
69 asm volatile("smsw %0\n" : "=m" (val));
70 exit(0);
71 }
72
asm_str(void)73 static void asm_str(void)
74 {
75 unsigned long val;
76
77 tst_res(TINFO, "TEST str, str result save at [%p]", &val);
78 asm volatile("str %0\n" : "=m" (val));
79 exit(0);
80 }
81
verify_umip_instruction(unsigned int n)82 static void verify_umip_instruction(unsigned int n)
83 {
84 int status;
85 pid_t pid;
86
87 pid = SAFE_FORK();
88 if (pid == 0) {
89 tst_no_corefile(0);
90
91 switch (n) {
92 case 0:
93 asm_sgdt();
94 break;
95 case 1:
96 asm_sidt();
97 break;
98 case 2:
99 asm_sldt();
100 break;
101 case 3:
102 asm_smsw();
103 break;
104 case 4:
105 asm_str();
106 break;
107 default:
108 tst_brk(TBROK, "Invalid tcase parameter: %d", n);
109 }
110 exit(0);
111 }
112
113 SAFE_WAITPID(pid, &status, 0);
114
115 switch (n) {
116 case 0:
117 case 1:
118 case 3:
119 /* after linux kernel v5.4 mainline, 64bit SGDT SIDT SMSW will return
120 dummy value and not trigger SIGSEGV due to kernel code change */
121 if ((tst_kvercmp(5, 4, 0)) >= 0) {
122 tst_res(TINFO, "Linux kernel version is v5.4 or after than v5.4");
123 if (WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) {
124 tst_res(TFAIL, "Got SIGSEGV");
125 return;
126 }
127 tst_res(TPASS, "Didn't receive SIGSEGV, child exited with %s",
128 tst_strstatus(status));
129 return;
130 } else
131 tst_res(TINFO, "Linux kernel version is before than v5.4");
132 break;
133 case 2:
134 case 4:
135 /* after Linux kernel v5.10 mainline, SLDT and STR will return
136 dummy value and not trigger SIGSEGV due to kernel code change */
137 if ((tst_kvercmp(5, 10, 0)) >= 0) {
138 tst_res(TINFO, "Linux kernel version is v5.10 or higher");
139 if (WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) {
140 tst_res(TFAIL, "Got SIGSEGV");
141 return;
142 }
143 tst_res(TPASS, "Didn't receive SIGSEGV, child exited with %s",
144 tst_strstatus(status));
145 return;
146 } else
147 tst_res(TINFO, "Linux kernel version is earlier than v5.10");
148 break;
149 }
150
151 if (WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) {
152 tst_res(TPASS, "Got SIGSEGV");
153 return;
154 }
155 tst_res(TFAIL, "Didn't receive SIGSEGV, child exited with %s",
156 tst_strstatus(status));
157 }
158
setup(void)159 static void setup(void)
160 {
161 FILE *fp;
162 char buf[2048];
163
164 fp = SAFE_FOPEN(CPUINFO_FILE, "r");
165 while (!feof(fp)) {
166 if (fgets(buf, sizeof(buf), fp) == NULL) {
167 SAFE_FCLOSE(fp);
168 tst_brk(TCONF, "cpuinfo show: cpu does not support umip");
169 }
170
171 if (!strstr(buf, "flags"))
172 continue;
173
174 if (strstr(buf, "umip")) {
175 tst_res(TINFO, "cpuinfo contains umip, CPU supports umip");
176 break;
177 } else
178 continue;
179 }
180
181 SAFE_FCLOSE(fp);
182 }
183
184 static struct tst_test test = {
185 .min_kver = "4.1",
186 .setup = setup,
187 .tcnt = 5,
188 .forks_child = 1,
189 .test = verify_umip_instruction,
190 .needs_kconfigs = (const char *[]){
191 "CONFIG_X86_INTEL_UMIP=y | CONFIG_X86_UMIP=y",
192 NULL
193 },
194 .needs_root = 1,
195 };
196
197 #else
198
199 TST_TEST_TCONF("Tests needs x86_64 CPU");
200
201 #endif /* __x86_64__ */
202