Skip to content

Commit 9118c5d

Browse files
nathanlynchmpe
authored andcommitted
powerpc/selftests: Add test for papr-vpd
Add selftests for /dev/papr-vpd, exercising the common expected use cases: * Retrieve all VPD by passing an empty location code. * Retrieve the "system VPD" by passing a location code derived from DT root node properties, as done by the vpdupdate command. The tests also verify that certain intended properties of the driver hold: * Passing an unterminated location code to PAPR_VPD_CREATE_HANDLE gets EINVAL. * Passing a NULL location code pointer to PAPR_VPD_CREATE_HANDLE gets EFAULT. * Closing the device node without first issuing a PAPR_VPD_CREATE_HANDLE command to it succeeds. * Releasing a handle without first consuming any data from it succeeds. * Re-reading the contents of a handle returns the same data as the first time. Some minimal validation of the returned data is performed. The tests are skipped on systems where the papr-vpd driver does not initialize, making this useful only on PowerVM LPARs at this point. Signed-off-by: Nathan Lynch <[email protected]> Signed-off-by: Michael Ellerman <[email protected]> Link: https://msgid.link/20231212-papr-sys_rtas-vs-lockdown-v6-12-e9eafd0c8c6c@linux.ibm.com
1 parent 905b9e4 commit 9118c5d

File tree

4 files changed

+366
-0
lines changed

4 files changed

+366
-0
lines changed

tools/testing/selftests/powerpc/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ SUB_DIRS = alignment \
3232
vphn \
3333
math \
3434
papr_attributes \
35+
papr_vpd \
3536
ptrace \
3637
security \
3738
mce
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/papr_vpd
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
noarg:
3+
$(MAKE) -C ../
4+
5+
TEST_GEN_PROGS := papr_vpd
6+
7+
top_srcdir = ../../../../..
8+
include ../../lib.mk
9+
10+
$(TEST_GEN_PROGS): ../harness.c ../utils.c
11+
12+
$(OUTPUT)/papr_vpd: CFLAGS += $(KHDR_INCLUDES)
Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
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+
}

0 commit comments

Comments
 (0)