1// SPDX-License-Identifier: GPL-2.0-only
2#include <errno.h>
3#include <fcntl.h>
4#include <stdlib.h>
5#include <sys/ioctl.h>
6#include <unistd.h>
7#include <asm/papr-sysparm.h>
8
9#include "utils.h"
10
11#define DEVPATH "/dev/papr-sysparm"
12
13static int open_close(void)
14{
15	const int devfd = open(DEVPATH, O_RDONLY);
16
17	SKIP_IF_MSG(devfd < 0 && errno == ENOENT,
18		    DEVPATH " not present");
19
20	FAIL_IF(devfd < 0);
21	FAIL_IF(close(devfd) != 0);
22
23	return 0;
24}
25
26static int get_splpar(void)
27{
28	struct papr_sysparm_io_block sp = {
29		.parameter = 20, // SPLPAR characteristics
30	};
31	const int devfd = open(DEVPATH, O_RDONLY);
32
33	SKIP_IF_MSG(devfd < 0 && errno == ENOENT,
34		    DEVPATH " not present");
35
36	FAIL_IF(devfd < 0);
37	FAIL_IF(ioctl(devfd, PAPR_SYSPARM_IOC_GET, &sp) != 0);
38	FAIL_IF(sp.length == 0);
39	FAIL_IF(sp.length > sizeof(sp.data));
40	FAIL_IF(close(devfd) != 0);
41
42	return 0;
43}
44
45static int get_bad_parameter(void)
46{
47	struct papr_sysparm_io_block sp = {
48		.parameter = UINT32_MAX, // there are only ~60 specified parameters
49	};
50	const int devfd = open(DEVPATH, O_RDONLY);
51
52	SKIP_IF_MSG(devfd < 0 && errno == ENOENT,
53		    DEVPATH " not present");
54
55	FAIL_IF(devfd < 0);
56
57	// Ensure expected error
58	FAIL_IF(ioctl(devfd, PAPR_SYSPARM_IOC_GET, &sp) != -1);
59	FAIL_IF(errno != EOPNOTSUPP);
60
61	// Ensure the buffer is unchanged
62	FAIL_IF(sp.length != 0);
63	for (size_t i = 0; i < ARRAY_SIZE(sp.data); ++i)
64		FAIL_IF(sp.data[i] != 0);
65
66	FAIL_IF(close(devfd) != 0);
67
68	return 0;
69}
70
71static int check_efault_common(unsigned long cmd)
72{
73	const int devfd = open(DEVPATH, O_RDWR);
74
75	SKIP_IF_MSG(devfd < 0 && errno == ENOENT,
76		    DEVPATH " not present");
77
78	FAIL_IF(devfd < 0);
79
80	// Ensure expected error
81	FAIL_IF(ioctl(devfd, cmd, NULL) != -1);
82	FAIL_IF(errno != EFAULT);
83
84	FAIL_IF(close(devfd) != 0);
85
86	return 0;
87}
88
89static int check_efault_get(void)
90{
91	return check_efault_common(PAPR_SYSPARM_IOC_GET);
92}
93
94static int check_efault_set(void)
95{
96	return check_efault_common(PAPR_SYSPARM_IOC_SET);
97}
98
99static int set_hmc0(void)
100{
101	struct papr_sysparm_io_block sp = {
102		.parameter = 0, // HMC0, not a settable parameter
103	};
104	const int devfd = open(DEVPATH, O_RDWR);
105
106	SKIP_IF_MSG(devfd < 0 && errno == ENOENT,
107		    DEVPATH " not present");
108
109	FAIL_IF(devfd < 0);
110
111	// Ensure expected error
112	FAIL_IF(ioctl(devfd, PAPR_SYSPARM_IOC_SET, &sp) != -1);
113	SKIP_IF_MSG(errno == EOPNOTSUPP, "operation not supported");
114	FAIL_IF(errno != EPERM);
115
116	FAIL_IF(close(devfd) != 0);
117
118	return 0;
119}
120
121static int set_with_ro_fd(void)
122{
123	struct papr_sysparm_io_block sp = {
124		.parameter = 0, // HMC0, not a settable parameter.
125	};
126	const int devfd = open(DEVPATH, O_RDONLY);
127
128	SKIP_IF_MSG(devfd < 0 && errno == ENOENT,
129		    DEVPATH " not present");
130
131	FAIL_IF(devfd < 0);
132
133	// Ensure expected error
134	FAIL_IF(ioctl(devfd, PAPR_SYSPARM_IOC_SET, &sp) != -1);
135	SKIP_IF_MSG(errno == EOPNOTSUPP, "operation not supported");
136
137	// HMC0 isn't a settable parameter and we would normally
138	// expect to get EPERM on attempts to modify it. However, when
139	// the file is open read-only, we expect the driver to prevent
140	// the attempt with a distinct error.
141	FAIL_IF(errno != EBADF);
142
143	FAIL_IF(close(devfd) != 0);
144
145	return 0;
146}
147
148struct sysparm_test {
149	int (*function)(void);
150	const char *description;
151};
152
153static const struct sysparm_test sysparm_tests[] = {
154	{
155		.function = open_close,
156		.description = "open and close " DEVPATH " without issuing commands",
157	},
158	{
159		.function = get_splpar,
160		.description = "retrieve SPLPAR characteristics",
161	},
162	{
163		.function = get_bad_parameter,
164		.description = "verify EOPNOTSUPP for known-bad parameter",
165	},
166	{
167		.function = check_efault_get,
168		.description = "PAPR_SYSPARM_IOC_GET returns EFAULT on bad address",
169	},
170	{
171		.function = check_efault_set,
172		.description = "PAPR_SYSPARM_IOC_SET returns EFAULT on bad address",
173	},
174	{
175		.function = set_hmc0,
176		.description = "ensure EPERM on attempt to update HMC0",
177	},
178	{
179		.function = set_with_ro_fd,
180		.description = "PAPR_IOC_SYSPARM_SET returns EACCES on read-only fd",
181	},
182};
183
184int main(void)
185{
186	size_t fails = 0;
187
188	for (size_t i = 0; i < ARRAY_SIZE(sysparm_tests); ++i) {
189		const struct sysparm_test *t = &sysparm_tests[i];
190
191		if (test_harness(t->function, t->description))
192			++fails;
193	}
194
195	return fails == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
196}
197