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 = { .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 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(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 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