1 // SPDX-License-Identifier: GPL-2.0-only
2 #define _GNU_SOURCE
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <sys/ioctl.h>
8 #include <unistd.h>
9 
10 #include <asm/papr-vpd.h>
11 
12 #include "utils.h"
13 
14 #define DEVPATH "/dev/papr-vpd"
15 
16 static int dev_papr_vpd_open_close(void)
17 {
18 	const int devfd = open(DEVPATH, O_RDONLY);
19 
20 	SKIP_IF_MSG(devfd < 0 && errno == ENOENT,
21 		    DEVPATH " not present");
22 
23 	FAIL_IF(devfd < 0);
24 	FAIL_IF(close(devfd) != 0);
25 
26 	return 0;
27 }
28 
29 static int dev_papr_vpd_get_handle_all(void)
30 {
31 	const int devfd = open(DEVPATH, O_RDONLY);
32 	struct papr_location_code lc = { .str = "", };
33 	off_t size;
34 	int fd;
35 
36 	SKIP_IF_MSG(devfd < 0 && errno == ENOENT,
37 		    DEVPATH " not present");
38 
39 	FAIL_IF(devfd < 0);
40 
41 	errno = 0;
42 	fd = ioctl(devfd, PAPR_VPD_IOC_CREATE_HANDLE, &lc);
43 	FAIL_IF(errno != 0);
44 	FAIL_IF(fd < 0);
45 
46 	FAIL_IF(close(devfd) != 0);
47 
48 	size = lseek(fd, 0, SEEK_END);
49 	FAIL_IF(size <= 0);
50 
51 	void *buf = malloc((size_t)size);
52 	FAIL_IF(!buf);
53 
54 	ssize_t consumed = pread(fd, buf, size, 0);
55 	FAIL_IF(consumed != size);
56 
57 	/* Ensure EOF */
58 	FAIL_IF(read(fd, buf, size) != 0);
59 	FAIL_IF(close(fd));
60 
61 	/* Verify that the buffer looks like VPD */
62 	static const char needle[] = "System VPD";
63 	FAIL_IF(!memmem(buf, size, needle, strlen(needle)));
64 
65 	return 0;
66 }
67 
68 static int dev_papr_vpd_get_handle_byte_at_a_time(void)
69 {
70 	const int devfd = open(DEVPATH, O_RDONLY);
71 	struct papr_location_code lc = { .str = "", };
72 	int fd;
73 
74 	SKIP_IF_MSG(devfd < 0 && errno == ENOENT,
75 		    DEVPATH " not present");
76 
77 	FAIL_IF(devfd < 0);
78 
79 	errno = 0;
80 	fd = ioctl(devfd, PAPR_VPD_IOC_CREATE_HANDLE, &lc);
81 	FAIL_IF(errno != 0);
82 	FAIL_IF(fd < 0);
83 
84 	FAIL_IF(close(devfd) != 0);
85 
86 	size_t consumed = 0;
87 	while (1) {
88 		ssize_t res;
89 		char c;
90 
91 		errno = 0;
92 		res = read(fd, &c, sizeof(c));
93 		FAIL_IF(res > sizeof(c));
94 		FAIL_IF(res < 0);
95 		FAIL_IF(errno != 0);
96 		consumed += res;
97 		if (res == 0)
98 			break;
99 	}
100 
101 	FAIL_IF(consumed != lseek(fd, 0, SEEK_END));
102 
103 	FAIL_IF(close(fd));
104 
105 	return 0;
106 }
107 
108 
109 static int dev_papr_vpd_unterm_loc_code(void)
110 {
111 	const int devfd = open(DEVPATH, O_RDONLY);
112 	struct papr_location_code lc = {};
113 	int fd;
114 
115 	SKIP_IF_MSG(devfd < 0 && errno == ENOENT,
116 		    DEVPATH " not present");
117 
118 	FAIL_IF(devfd < 0);
119 
120 	/*
121 	 * Place a non-null byte in every element of loc_code; the
122 	 * driver should reject this input.
123 	 */
124 	memset(lc.str, 'x', ARRAY_SIZE(lc.str));
125 
126 	errno = 0;
127 	fd = ioctl(devfd, PAPR_VPD_IOC_CREATE_HANDLE, &lc);
128 	FAIL_IF(fd != -1);
129 	FAIL_IF(errno != EINVAL);
130 
131 	FAIL_IF(close(devfd) != 0);
132 	return 0;
133 }
134 
135 static int dev_papr_vpd_null_handle(void)
136 {
137 	const int devfd = open(DEVPATH, O_RDONLY);
138 	int rc;
139 
140 	SKIP_IF_MSG(devfd < 0 && errno == ENOENT,
141 		    DEVPATH " not present");
142 
143 	FAIL_IF(devfd < 0);
144 
145 	errno = 0;
146 	rc = ioctl(devfd, PAPR_VPD_IOC_CREATE_HANDLE, NULL);
147 	FAIL_IF(rc != -1);
148 	FAIL_IF(errno != EFAULT);
149 
150 	FAIL_IF(close(devfd) != 0);
151 	return 0;
152 }
153 
154 static int papr_vpd_close_handle_without_reading(void)
155 {
156 	const int devfd = open(DEVPATH, O_RDONLY);
157 	struct papr_location_code lc;
158 	int fd;
159 
160 	SKIP_IF_MSG(devfd < 0 && errno == ENOENT,
161 		    DEVPATH " not present");
162 
163 	FAIL_IF(devfd < 0);
164 
165 	errno = 0;
166 	fd = ioctl(devfd, PAPR_VPD_IOC_CREATE_HANDLE, &lc);
167 	FAIL_IF(errno != 0);
168 	FAIL_IF(fd < 0);
169 
170 	/* close the handle without reading it */
171 	FAIL_IF(close(fd) != 0);
172 
173 	FAIL_IF(close(devfd) != 0);
174 	return 0;
175 }
176 
177 static int papr_vpd_reread(void)
178 {
179 	const int devfd = open(DEVPATH, O_RDONLY);
180 	struct papr_location_code lc = { .str = "", };
181 	int fd;
182 
183 	SKIP_IF_MSG(devfd < 0 && errno == ENOENT,
184 		    DEVPATH " not present");
185 
186 	FAIL_IF(devfd < 0);
187 
188 	errno = 0;
189 	fd = ioctl(devfd, PAPR_VPD_IOC_CREATE_HANDLE, &lc);
190 	FAIL_IF(errno != 0);
191 	FAIL_IF(fd < 0);
192 
193 	FAIL_IF(close(devfd) != 0);
194 
195 	const off_t size = lseek(fd, 0, SEEK_END);
196 	FAIL_IF(size <= 0);
197 
198 	char *bufs[2];
199 
200 	for (size_t i = 0; i < ARRAY_SIZE(bufs); ++i) {
201 		bufs[i] = malloc(size);
202 		FAIL_IF(!bufs[i]);
203 		ssize_t consumed = pread(fd, bufs[i], size, 0);
204 		FAIL_IF(consumed != size);
205 	}
206 
207 	FAIL_IF(memcmp(bufs[0], bufs[1], size));
208 
209 	FAIL_IF(close(fd) != 0);
210 
211 	return 0;
212 }
213 
214 static int get_system_loc_code(struct papr_location_code *lc)
215 {
216 	static const char system_id_path[] = "/sys/firmware/devicetree/base/system-id";
217 	static const char model_path[] = "/sys/firmware/devicetree/base/model";
218 	char *system_id;
219 	char *model;
220 	int err = -1;
221 
222 	if (read_file_alloc(model_path, &model, NULL))
223 		return err;
224 
225 	if (read_file_alloc(system_id_path, &system_id, NULL))
226 		goto free_model;
227 
228 	char *mtm;
229 	int sscanf_ret = sscanf(model, "IBM,%ms", &mtm);
230 	if (sscanf_ret != 1)
231 		goto free_system_id;
232 
233 	char *plant_and_seq;
234 	if (sscanf(system_id, "IBM,%*c%*c%ms", &plant_and_seq) != 1)
235 		goto free_mtm;
236 	/*
237 	 * Replace - with . to build location code.
238 	 */
239 	char *sep = strchr(mtm, '-');
240 	if (!sep)
241 		goto free_mtm;
242 	else
243 		*sep = '.';
244 
245 	snprintf(lc->str, sizeof(lc->str),
246 		 "U%s.%s", mtm, plant_and_seq);
247 	err = 0;
248 
249 	free(plant_and_seq);
250 free_mtm:
251 	free(mtm);
252 free_system_id:
253 	free(system_id);
254 free_model:
255 	free(model);
256 	return err;
257 }
258 
259 static int papr_vpd_system_loc_code(void)
260 {
261 	struct papr_location_code lc;
262 	const int devfd = open(DEVPATH, O_RDONLY);
263 	off_t size;
264 	int fd;
265 
266 	SKIP_IF_MSG(get_system_loc_code(&lc),
267 		    "Cannot determine system location code");
268 	SKIP_IF_MSG(devfd < 0 && errno == ENOENT,
269 		    DEVPATH " not present");
270 
271 	FAIL_IF(devfd < 0);
272 
273 	errno = 0;
274 	fd = ioctl(devfd, PAPR_VPD_IOC_CREATE_HANDLE, &lc);
275 	FAIL_IF(errno != 0);
276 	FAIL_IF(fd < 0);
277 
278 	FAIL_IF(close(devfd) != 0);
279 
280 	size = lseek(fd, 0, SEEK_END);
281 	FAIL_IF(size <= 0);
282 
283 	void *buf = malloc((size_t)size);
284 	FAIL_IF(!buf);
285 
286 	ssize_t consumed = pread(fd, buf, size, 0);
287 	FAIL_IF(consumed != size);
288 
289 	/* Ensure EOF */
290 	FAIL_IF(read(fd, buf, size) != 0);
291 	FAIL_IF(close(fd));
292 
293 	/* Verify that the buffer looks like VPD */
294 	static const char needle[] = "System VPD";
295 	FAIL_IF(!memmem(buf, size, needle, strlen(needle)));
296 
297 	return 0;
298 }
299 
300 struct vpd_test {
301 	int (*function)(void);
302 	const char *description;
303 };
304 
305 static const struct vpd_test vpd_tests[] = {
306 	{
307 		.function = dev_papr_vpd_open_close,
308 		.description = "open/close " DEVPATH,
309 	},
310 	{
311 		.function = dev_papr_vpd_unterm_loc_code,
312 		.description = "ensure EINVAL on unterminated location code",
313 	},
314 	{
315 		.function = dev_papr_vpd_null_handle,
316 		.description = "ensure EFAULT on bad handle addr",
317 	},
318 	{
319 		.function = dev_papr_vpd_get_handle_all,
320 		.description = "get handle for all VPD"
321 	},
322 	{
323 		.function = papr_vpd_close_handle_without_reading,
324 		.description = "close handle without consuming VPD"
325 	},
326 	{
327 		.function = dev_papr_vpd_get_handle_byte_at_a_time,
328 		.description = "read all VPD one byte at a time"
329 	},
330 	{
331 		.function = papr_vpd_reread,
332 		.description = "ensure re-read yields same results"
333 	},
334 	{
335 		.function = papr_vpd_system_loc_code,
336 		.description = "get handle for system VPD"
337 	},
338 };
339 
340 int main(void)
341 {
342 	size_t fails = 0;
343 
344 	for (size_t i = 0; i < ARRAY_SIZE(vpd_tests); ++i) {
345 		const struct vpd_test *t = &vpd_tests[i];
346 
347 		if (test_harness(t->function, t->description))
348 			++fails;
349 	}
350 
351 	return fails == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
352 }
353