353 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2#define _GNU_SOURCE3#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 16static 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 29static 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 68static 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 109static 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; the122 * 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 135static 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 154static 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 177static 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 214static 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 else243 *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);250free_mtm:251 free(mtm);252free_system_id:253 free(system_id);254free_model:255 free(model);256 return err;257}258 259static 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 300struct vpd_test {301 int (*function)(void);302 const char *description;303};304 305static 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 340int 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