1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright (C) 2018 IBM Corp. 3 4 #define _GNU_SOURCE /* fallocate */ 5 #include <assert.h> 6 #include <fcntl.h> 7 #include <stdint.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <string.h> 11 #include <sys/ioctl.h> 12 #include <sys/mman.h> 13 #include <sys/types.h> 14 #include <sys/stat.h> 15 #include <unistd.h> 16 17 #include "config.h" 18 #include "mboxd.h" 19 #include "backend.h" 20 #include "lpc.h" 21 #include "transport_mbox.h" 22 #include "windows.h" 23 24 #include "test/mbox.h" 25 26 #define STEP 16 27 28 void dump_buf(const void *buf, size_t len) 29 { 30 const uint8_t *buf8 = buf; 31 size_t i; 32 33 for (i = 0; i < len; i += STEP) { 34 int delta; 35 int max; 36 int j; 37 38 delta = len - i; 39 max = delta > STEP ? STEP : delta; 40 41 printf("0x%08zx:\t", i); 42 for (j = 0; j < max; j++) 43 printf("0x%02x, ", buf8[i + j]); 44 45 printf("\n"); 46 } 47 printf("\n"); 48 } 49 50 /* 51 * Because we are using a file and not a pipe for the mbox file descriptor we 52 * need to handle a difference in behaviour. For a given command and response 53 * sequence the first 16 bytes of the file are occupied by the mbox command. 54 * The response occupies the following 14 bytes for a total of 30 bytes. 55 * 56 * We also have to ensure we lseek() to reset the file descriptor offset back 57 * to the start of the file before dispatching the mbox command. 58 */ 59 60 /* Macros for handling the pipe/file discrepancy */ 61 #define RESPONSE_OFFSET 16 62 #define RESPONSE_SIZE 14 63 64 int mbox_cmp(struct mbox_context *context, const uint8_t *expected, size_t len) 65 { 66 struct stat details; 67 uint8_t *map; 68 int rc; 69 int fd; 70 71 fd = context->fds[MBOX_FD].fd; 72 fstat(fd, &details); 73 74 map = mmap(NULL, details.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 75 printf("%s:%d: details.st_size: %ld, RESPONSE_OFFSET + len: %ld\n", 76 __func__, __LINE__, details.st_size, RESPONSE_OFFSET + len); 77 assert(map != MAP_FAILED); 78 assert(details.st_size >= (__off_t)(RESPONSE_OFFSET + len)); 79 80 rc = memcmp(expected, &map[RESPONSE_OFFSET], len); 81 82 if (rc != 0) { 83 printf("\nMBOX state (%ld):\n", details.st_size); 84 dump_buf(map, details.st_size); 85 printf("Expected response (%lu):\n", len); 86 dump_buf(expected, len); 87 } 88 89 munmap(map, details.st_size); 90 91 return rc; 92 } 93 94 void mbox_rspcpy(struct mbox_context *context, struct mbox_msg *msg) 95 { 96 struct stat details; 97 uint8_t *map; 98 int fd; 99 100 fd = context->fds[MBOX_FD].fd; 101 fstat(fd, &details); 102 103 map = mmap(NULL, details.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 104 assert(map != MAP_FAILED); 105 assert(details.st_size >= (RESPONSE_OFFSET + RESPONSE_SIZE)); 106 107 memcpy(msg, &map[RESPONSE_OFFSET], RESPONSE_SIZE); 108 109 munmap(map, details.st_size); 110 } 111 112 int mbox_command_write(struct mbox_context *context, const uint8_t *command, 113 size_t len) 114 { 115 size_t remaining; 116 int rc; 117 int fd; 118 119 fd = context->fds[MBOX_FD].fd; 120 rc = lseek(fd, 0, SEEK_SET); 121 if (rc != 0) 122 return -1; 123 124 remaining = len; 125 while (remaining > 0) { 126 rc = write(fd, command, remaining); 127 if (rc < 0) 128 goto out; 129 remaining -= rc; 130 } 131 132 rc = lseek(fd, 0, SEEK_SET); 133 if (rc != 0) 134 return -1; 135 136 out: 137 return rc; 138 } 139 140 int mbox_command_dispatch(struct mbox_context *context, const uint8_t *command, 141 size_t len) 142 { 143 uint8_t status; 144 int rc; 145 146 rc = mbox_command_write(context, command, len); 147 if (rc < 0) 148 return rc; 149 150 rc = transport_mbox_dispatch(context); 151 if (rc < 0) 152 return -rc; 153 154 /* 155 * The aspeed-lpc-ctrl driver implements mailbox register access 156 * through the usual read()/write() chardev interface. 157 * 158 * The typical access sequence is: 159 * 160 * 1. Read all the registers out 161 * 2. Perform the action specified 162 * 3. Write a response to the registers. 163 * 164 * For the tests the "device" file descriptor is backed by a temporary 165 * file. The above sequence leads to a file-size of 30 bytes: 16 bytes 166 * for the issued command, followed by a 14-byte response. 167 * 168 * However, the typical access pattern isn't the only access pattern. 169 * Individual byte registers can be accessed by lseek()'ing on the 170 * device's file descriptor and issuing read() or write() as desired. 171 * The daemon uses this property to manage the BMC status byte, and the 172 * implementation cleans up after status byte operations by lseek()'ing 173 * back to offset zero. 174 * 175 * Thus for the BMC_EVENT_ACK command the file only reaches a size of 176 * 16 bytes; the daemon's response overwrites the first 14 bytes of the 177 * command injected by the tests. 178 * 179 * The consequence of this is that the response status byte can either 180 * appear at offset 13, or offset 29, depending on the command. 181 * 182 * However, regardless of what command is issued the response data is 183 * written to the device and the file descriptor is left in the 184 * post-write() state. This means the status byte can always be 185 * accessed relative to the current position by an lseek() of type 186 * SEEK_CUR for offset -1. 187 * 188 */ 189 rc = lseek(context->fds[MBOX_FD].fd, -1, SEEK_CUR); 190 if (rc < 0) 191 return rc; 192 193 rc = read(context->fds[MBOX_FD].fd, &status, sizeof(status)); 194 if (rc < 0) 195 return rc; 196 197 return status; 198 } 199 200 struct mbox_test_context { 201 struct tmpf mbox; 202 struct tmpf flash; 203 struct tmpf lpc; 204 struct mbox_context context; 205 } test; 206 207 void cleanup(void) 208 { 209 tmpf_destroy(&test.mbox); 210 tmpf_destroy(&test.flash); 211 tmpf_destroy(&test.lpc); 212 } 213 214 int __transport_mbox_init(struct mbox_context *context, const char *path, 215 const struct transport_ops **ops); 216 int __lpc_dev_init(struct mbox_context *context, const char *path); 217 218 struct mbox_context *mbox_create_frontend_context(int n_windows, size_t len) 219 { 220 const struct transport_ops *ops; 221 struct mtd_info_user mtd_info; 222 int rc; 223 224 mbox_vlog = &mbox_log_console; 225 verbosity = 2; 226 227 atexit(cleanup); 228 229 rc = tmpf_init(&test.mbox, "mbox-store.XXXXXX"); 230 assert(rc == 0); 231 232 rc = tmpf_init(&test.lpc, "lpc-store.XXXXXX"); 233 assert(rc == 0); 234 235 test.context.windows.num = n_windows; 236 test.context.windows.default_size = len; 237 238 rc = protocol_init(&test.context); 239 assert(rc == 0); 240 241 /* 242 * We need to call __transport_mbox_init() to initialise the handler table. 243 * However, afterwards we need to discard the fd of the clearly useless 244 * /dev/null and replace it with our own fd for mbox device emulation 245 * by the test framework. 246 */ 247 __transport_mbox_init(&test.context, "/dev/null", &ops); 248 test.context.transport = ops; 249 rc = close(test.context.fds[MBOX_FD].fd); 250 assert(rc == 0); 251 test.context.fds[MBOX_FD].fd = test.mbox.fd; 252 253 /* Instantiate the mtd backend */ 254 rc = tmpf_init(&test.flash, "flash-store.XXXXXX"); 255 assert(rc == 0); 256 257 rc = ioctl(test.flash.fd, MEMGETINFO, &mtd_info); 258 assert(rc == 0); 259 260 rc = fallocate(test.flash.fd, 0, 0, mtd_info.size); 261 assert(rc == 0); 262 263 test.context.backend.flash_size = mtd_info.size; 264 265 rc = __lpc_dev_init(&test.context, test.lpc.path); 266 assert(rc == 0); 267 268 rc = fallocate(test.lpc.fd, 0, 0, test.context.mem_size); 269 assert(rc == 0); 270 271 rc = windows_init(&test.context); 272 assert(rc == 0); 273 274 return rc ? NULL : &test.context; 275 } 276 277 struct mbox_context *mbox_create_test_context(int n_windows, size_t len) 278 { 279 struct mbox_context *ctx; 280 int rc; 281 282 ctx = mbox_create_frontend_context(n_windows, len); 283 assert(ctx); 284 285 rc = backend_probe_mtd(&ctx->backend, test.flash.path); 286 assert(rc == 0); 287 288 return ctx; 289 } 290 291 /* From ccan's container_of module, CC0 license */ 292 #define container_of(member_ptr, containing_type, member) \ 293 ((containing_type *) \ 294 ((char *)(member_ptr) \ 295 - container_off(containing_type, member)) \ 296 + check_types_match(*(member_ptr), ((containing_type *)0)->member)) 297 298 /* From ccan's container_of module, CC0 license */ 299 #define container_off(containing_type, member) \ 300 offsetof(containing_type, member) 301 302 /* From ccan's check_type module, CC0 license */ 303 #define check_type(expr, type) \ 304 ((typeof(expr) *)0 != (type *)0) 305 306 /* From ccan's check_type module, CC0 license */ 307 #define check_types_match(expr1, expr2) \ 308 ((typeof(expr1) *)0 != (typeof(expr2) *)0) 309 310 int mbox_set_mtd_data(struct mbox_context *context, const void *data, 311 size_t len) 312 { 313 struct mbox_test_context *arg; 314 void *map; 315 316 assert(test.flash.fd > 2); 317 318 /* Sanity check */ 319 arg = container_of(context, struct mbox_test_context, context); 320 assert(&test == arg); 321 assert(len <= test.context.backend.flash_size); 322 323 map = mmap(NULL, test.context.backend.flash_size, 324 PROT_WRITE, MAP_SHARED, test.flash.fd, 0); 325 assert(map != MAP_FAILED); 326 memcpy(map, data, len); 327 munmap(map, test.context.backend.flash_size); 328 329 return 0; 330 } 331 332 char *get_dev_mtd(void) 333 { 334 return strdup(test.flash.path); 335 } 336