1 // SPDX-License-Identifier: GPL-2.0-or-later
2
3 /*
4 * Copyright (C) 2018 Intel Corporation
5 * Author: Neri, Ricardo <ricardo.neri@intel.com>
6 * Pengfei, Xu <pengfei.xu@intel.com>
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 }
133
134 if (WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) {
135 tst_res(TPASS, "Got SIGSEGV");
136 return;
137 }
138 tst_res(TFAIL, "Didn't receive SIGSEGV, child exited with %s",
139 tst_strstatus(status));
140 }
141
setup(void)142 static void setup(void)
143 {
144 FILE *fp;
145 char buf[2048];
146
147 fp = SAFE_FOPEN(CPUINFO_FILE, "r");
148 while (!feof(fp)) {
149 if (fgets(buf, sizeof(buf), fp) == NULL) {
150 SAFE_FCLOSE(fp);
151 tst_brk(TCONF, "cpuinfo show: cpu does not support umip");
152 }
153
154 if (!strstr(buf, "flags"))
155 continue;
156
157 if (strstr(buf, "umip")) {
158 tst_res(TINFO, "cpuinfo contains umip, CPU supports umip");
159 break;
160 } else
161 continue;
162 }
163
164 SAFE_FCLOSE(fp);
165 }
166
167 static struct tst_test test = {
168 .min_kver = "4.1",
169 .setup = setup,
170 .tcnt = 5,
171 .forks_child = 1,
172 .test = verify_umip_instruction,
173 .needs_kconfigs = (const char *[]){
174 "CONFIG_X86_INTEL_UMIP=y",
175 NULL
176 },
177 .needs_root = 1,
178 };
179
180 #else
181
182 TST_TEST_TCONF("Tests needs x86_64 CPU");
183
184 #endif /* __x86_64__ */
185