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
35static 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
45static 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
55static 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
64static 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
73static 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
82static 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
159static 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
184static 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
199TST_TEST_TCONF("Tests needs x86_64 CPU");
200
201#endif /* __x86_64__ */
202