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