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
dev_papr_vpd_open_close(void)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
dev_papr_vpd_get_handle_all(void)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
dev_papr_vpd_get_handle_byte_at_a_time(void)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
dev_papr_vpd_unterm_loc_code(void)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
dev_papr_vpd_null_handle(void)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
papr_vpd_close_handle_without_reading(void)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 = { .str = "", };
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
papr_vpd_reread(void)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
get_system_loc_code(struct papr_location_code * lc)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
papr_vpd_system_loc_code(void)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(devfd < 0 && errno == ENOENT,
267 DEVPATH " not present");
268 SKIP_IF_MSG(get_system_loc_code(&lc),
269 "Cannot determine system location code");
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
main(void)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