Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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 <nathanl@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://msgid.link/20231212-papr-sys_rtas-vs-lockdown-v6-12-e9eafd0c8c6c@linux.ibm.com

authored by

Nathan Lynch and committed by
Michael Ellerman
9118c5d3 905b9e48

+366
+1
tools/testing/selftests/powerpc/Makefile
··· 32 32 vphn \ 33 33 math \ 34 34 papr_attributes \ 35 + papr_vpd \ 35 36 ptrace \ 36 37 security \ 37 38 mce
+1
tools/testing/selftests/powerpc/papr_vpd/.gitignore
··· 1 + /papr_vpd
+12
tools/testing/selftests/powerpc/papr_vpd/Makefile
··· 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)
+352
tools/testing/selftests/powerpc/papr_vpd/papr_vpd.c
··· 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 + }