14fe996c2SAndrew Jeffery // SPDX-License-Identifier: Apache-2.0
24fe996c2SAndrew Jeffery // Copyright (C) 2018 IBM Corp.
35ab4e3e8SAndrew Jeffery
45ab4e3e8SAndrew Jeffery #define _GNU_SOURCE /* fallocate */
55ab4e3e8SAndrew Jeffery #include <assert.h>
65ab4e3e8SAndrew Jeffery #include <fcntl.h>
75ab4e3e8SAndrew Jeffery #include <stdint.h>
85ab4e3e8SAndrew Jeffery #include <stdio.h>
95ab4e3e8SAndrew Jeffery #include <stdlib.h>
105ab4e3e8SAndrew Jeffery #include <string.h>
115ab4e3e8SAndrew Jeffery #include <sys/mman.h>
125ab4e3e8SAndrew Jeffery #include <sys/types.h>
135ab4e3e8SAndrew Jeffery #include <sys/stat.h>
145ab4e3e8SAndrew Jeffery #include <unistd.h>
155ab4e3e8SAndrew Jeffery
165ab4e3e8SAndrew Jeffery #include "mbox.h"
175ab4e3e8SAndrew Jeffery #include "mboxd_flash.h"
185ab4e3e8SAndrew Jeffery #include "mboxd_lpc.h"
195ab4e3e8SAndrew Jeffery #include "mboxd_msg.h"
205ab4e3e8SAndrew Jeffery #include "mboxd_windows.h"
215ab4e3e8SAndrew Jeffery
225ab4e3e8SAndrew Jeffery #include "test/mbox.h"
235ab4e3e8SAndrew Jeffery
245ab4e3e8SAndrew Jeffery #define STEP 16
255ab4e3e8SAndrew Jeffery
dump_buf(const void * buf,size_t len)26ee7af883SAndrew Jeffery void dump_buf(const void *buf, size_t len)
275ab4e3e8SAndrew Jeffery {
28ee7af883SAndrew Jeffery const uint8_t *buf8 = buf;
295ab4e3e8SAndrew Jeffery int i;
305ab4e3e8SAndrew Jeffery
315ab4e3e8SAndrew Jeffery for (i = 0; i < len; i += STEP) {
325ab4e3e8SAndrew Jeffery int delta;
335ab4e3e8SAndrew Jeffery int max;
345ab4e3e8SAndrew Jeffery int j;
355ab4e3e8SAndrew Jeffery
365ab4e3e8SAndrew Jeffery delta = len - i;
375ab4e3e8SAndrew Jeffery max = delta > STEP ? STEP : delta;
385ab4e3e8SAndrew Jeffery
395ab4e3e8SAndrew Jeffery printf("0x%08x:\t", i);
405ab4e3e8SAndrew Jeffery for (j = 0; j < max; j++)
41ee7af883SAndrew Jeffery printf("0x%02x, ", buf8[i + j]);
425ab4e3e8SAndrew Jeffery
435ab4e3e8SAndrew Jeffery printf("\n");
445ab4e3e8SAndrew Jeffery }
455ab4e3e8SAndrew Jeffery printf("\n");
465ab4e3e8SAndrew Jeffery }
475ab4e3e8SAndrew Jeffery
485ab4e3e8SAndrew Jeffery /*
495ab4e3e8SAndrew Jeffery * Because we are using a file and not a pipe for the mbox file descriptor we
505ab4e3e8SAndrew Jeffery * need to handle a difference in behaviour. For a given command and response
515ab4e3e8SAndrew Jeffery * sequence the first 16 bytes of the file are occupied by the mbox command.
525ab4e3e8SAndrew Jeffery * The response occupies the following 14 bytes for a total of 30 bytes.
535ab4e3e8SAndrew Jeffery *
545ab4e3e8SAndrew Jeffery * We also have to ensure we lseek() to reset the file descriptor offset back
555ab4e3e8SAndrew Jeffery * to the start of the file before dispatching the mbox command.
565ab4e3e8SAndrew Jeffery */
575ab4e3e8SAndrew Jeffery
585ab4e3e8SAndrew Jeffery /* Macros for handling the pipe/file discrepancy */
595ab4e3e8SAndrew Jeffery #define RESPONSE_OFFSET 16
605ab4e3e8SAndrew Jeffery #define RESPONSE_SIZE 14
615ab4e3e8SAndrew Jeffery
mbox_cmp(struct mbox_context * context,const uint8_t * expected,size_t len)625ab4e3e8SAndrew Jeffery int mbox_cmp(struct mbox_context *context, const uint8_t *expected, size_t len)
635ab4e3e8SAndrew Jeffery {
645ab4e3e8SAndrew Jeffery struct stat details;
655ab4e3e8SAndrew Jeffery uint8_t *map;
665ab4e3e8SAndrew Jeffery int rc;
675ab4e3e8SAndrew Jeffery int fd;
685ab4e3e8SAndrew Jeffery
695ab4e3e8SAndrew Jeffery fd = context->fds[MBOX_FD].fd;
705ab4e3e8SAndrew Jeffery fstat(fd, &details);
715ab4e3e8SAndrew Jeffery
725ab4e3e8SAndrew Jeffery map = mmap(NULL, details.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
735ab4e3e8SAndrew Jeffery assert(map != MAP_FAILED);
745ab4e3e8SAndrew Jeffery assert(details.st_size >= (RESPONSE_OFFSET + len));
755ab4e3e8SAndrew Jeffery
765ab4e3e8SAndrew Jeffery rc = memcmp(expected, &map[RESPONSE_OFFSET], len);
775ab4e3e8SAndrew Jeffery
785ab4e3e8SAndrew Jeffery if (rc != 0) {
795ab4e3e8SAndrew Jeffery printf("\nMBOX state (%ld):\n", details.st_size);
805ab4e3e8SAndrew Jeffery dump_buf(map, details.st_size);
815ab4e3e8SAndrew Jeffery printf("Expected response (%lu):\n", len);
825ab4e3e8SAndrew Jeffery dump_buf(expected, len);
835ab4e3e8SAndrew Jeffery }
845ab4e3e8SAndrew Jeffery
855ab4e3e8SAndrew Jeffery munmap(map, details.st_size);
865ab4e3e8SAndrew Jeffery
875ab4e3e8SAndrew Jeffery return rc;
885ab4e3e8SAndrew Jeffery }
895ab4e3e8SAndrew Jeffery
mbox_rspcpy(struct mbox_context * context,struct mbox_msg * msg)90ca1dfc9eSAndrew Jeffery void mbox_rspcpy(struct mbox_context *context, struct mbox_msg *msg)
91ca1dfc9eSAndrew Jeffery {
92ca1dfc9eSAndrew Jeffery struct stat details;
93ca1dfc9eSAndrew Jeffery uint8_t *map;
94ca1dfc9eSAndrew Jeffery int fd;
95ca1dfc9eSAndrew Jeffery
96ca1dfc9eSAndrew Jeffery fd = context->fds[MBOX_FD].fd;
97ca1dfc9eSAndrew Jeffery fstat(fd, &details);
98ca1dfc9eSAndrew Jeffery
99ca1dfc9eSAndrew Jeffery map = mmap(NULL, details.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
100ca1dfc9eSAndrew Jeffery assert(map != MAP_FAILED);
101ca1dfc9eSAndrew Jeffery assert(details.st_size >= (RESPONSE_OFFSET + RESPONSE_SIZE));
102ca1dfc9eSAndrew Jeffery
103ca1dfc9eSAndrew Jeffery memcpy(msg, &map[RESPONSE_OFFSET], RESPONSE_SIZE);
104ca1dfc9eSAndrew Jeffery
105ca1dfc9eSAndrew Jeffery munmap(map, details.st_size);
106ca1dfc9eSAndrew Jeffery }
107ca1dfc9eSAndrew Jeffery
mbox_command_write(struct mbox_context * context,const uint8_t * command,size_t len)1085ab4e3e8SAndrew Jeffery int mbox_command_write(struct mbox_context *context, const uint8_t *command,
1095ab4e3e8SAndrew Jeffery size_t len)
1105ab4e3e8SAndrew Jeffery {
1115ab4e3e8SAndrew Jeffery size_t remaining;
1125ab4e3e8SAndrew Jeffery int rc;
1135ab4e3e8SAndrew Jeffery int fd;
1145ab4e3e8SAndrew Jeffery
1155ab4e3e8SAndrew Jeffery fd = context->fds[MBOX_FD].fd;
1165ab4e3e8SAndrew Jeffery rc = lseek(fd, 0, SEEK_SET);
1175ab4e3e8SAndrew Jeffery if (rc != 0)
1185ab4e3e8SAndrew Jeffery return -1;
1195ab4e3e8SAndrew Jeffery
1205ab4e3e8SAndrew Jeffery remaining = len;
1215ab4e3e8SAndrew Jeffery while (remaining > 0) {
1225ab4e3e8SAndrew Jeffery rc = write(fd, command, remaining);
1235ab4e3e8SAndrew Jeffery if (rc < 0)
1245ab4e3e8SAndrew Jeffery goto out;
1255ab4e3e8SAndrew Jeffery remaining -= rc;
1265ab4e3e8SAndrew Jeffery }
1275ab4e3e8SAndrew Jeffery
1285ab4e3e8SAndrew Jeffery rc = lseek(fd, 0, SEEK_SET);
1295ab4e3e8SAndrew Jeffery if (rc != 0)
1305ab4e3e8SAndrew Jeffery return -1;
1315ab4e3e8SAndrew Jeffery
1325ab4e3e8SAndrew Jeffery out:
1335ab4e3e8SAndrew Jeffery return rc;
1345ab4e3e8SAndrew Jeffery }
1355ab4e3e8SAndrew Jeffery
mbox_command_dispatch(struct mbox_context * context,const uint8_t * command,size_t len)1365ab4e3e8SAndrew Jeffery int mbox_command_dispatch(struct mbox_context *context, const uint8_t *command,
1375ab4e3e8SAndrew Jeffery size_t len)
1385ab4e3e8SAndrew Jeffery {
1395ab4e3e8SAndrew Jeffery uint8_t status;
1405ab4e3e8SAndrew Jeffery int rc;
1415ab4e3e8SAndrew Jeffery
1425ab4e3e8SAndrew Jeffery rc = mbox_command_write(context, command, len);
1435ab4e3e8SAndrew Jeffery if (rc < 0)
1445ab4e3e8SAndrew Jeffery return rc;
1455ab4e3e8SAndrew Jeffery
1465ab4e3e8SAndrew Jeffery rc = dispatch_mbox(context);
1475ab4e3e8SAndrew Jeffery if (rc < 0)
1485ab4e3e8SAndrew Jeffery return -rc;
1495ab4e3e8SAndrew Jeffery
1505ab4e3e8SAndrew Jeffery /*
1515ab4e3e8SAndrew Jeffery * The aspeed-lpc-ctrl driver implements mailbox register access
1525ab4e3e8SAndrew Jeffery * through the usual read()/write() chardev interface.
1535ab4e3e8SAndrew Jeffery *
1545ab4e3e8SAndrew Jeffery * The typical access sequence is:
1555ab4e3e8SAndrew Jeffery *
1565ab4e3e8SAndrew Jeffery * 1. Read all the registers out
1575ab4e3e8SAndrew Jeffery * 2. Perform the action specified
1585ab4e3e8SAndrew Jeffery * 3. Write a response to the registers.
1595ab4e3e8SAndrew Jeffery *
1605ab4e3e8SAndrew Jeffery * For the tests the "device" file descriptor is backed by a temporary
1615ab4e3e8SAndrew Jeffery * file. The above sequence leads to a file-size of 30 bytes: 16 bytes
1625ab4e3e8SAndrew Jeffery * for the issued command, followed by a 14-byte response.
1635ab4e3e8SAndrew Jeffery *
1645ab4e3e8SAndrew Jeffery * However, the typical access pattern isn't the only access pattern.
1655ab4e3e8SAndrew Jeffery * Individual byte registers can be accessed by lseek()'ing on the
1665ab4e3e8SAndrew Jeffery * device's file descriptor and issuing read() or write() as desired.
1675ab4e3e8SAndrew Jeffery * The daemon uses this property to manage the BMC status byte, and the
1685ab4e3e8SAndrew Jeffery * implementation cleans up after status byte operations by lseek()'ing
1695ab4e3e8SAndrew Jeffery * back to offset zero.
1705ab4e3e8SAndrew Jeffery *
1715ab4e3e8SAndrew Jeffery * Thus for the BMC_EVENT_ACK command the file only reaches a size of
1725ab4e3e8SAndrew Jeffery * 16 bytes; the daemon's response overwrites the first 14 bytes of the
1735ab4e3e8SAndrew Jeffery * command injected by the tests.
1745ab4e3e8SAndrew Jeffery *
1755ab4e3e8SAndrew Jeffery * The consequence of this is that the response status byte can either
1765ab4e3e8SAndrew Jeffery * appear at offset 13, or offset 29, depending on the command.
1775ab4e3e8SAndrew Jeffery *
1785ab4e3e8SAndrew Jeffery * However, regardless of what command is issued the response data is
1795ab4e3e8SAndrew Jeffery * written to the device and the file descriptor is left in the
1805ab4e3e8SAndrew Jeffery * post-write() state. This means the status byte can always be
1815ab4e3e8SAndrew Jeffery * accessed relative to the current position by an lseek() of type
1825ab4e3e8SAndrew Jeffery * SEEK_CUR for offset -1.
1835ab4e3e8SAndrew Jeffery *
1845ab4e3e8SAndrew Jeffery */
1855ab4e3e8SAndrew Jeffery rc = lseek(context->fds[MBOX_FD].fd, -1, SEEK_CUR);
1865ab4e3e8SAndrew Jeffery if (rc < 0)
1875ab4e3e8SAndrew Jeffery return rc;
1885ab4e3e8SAndrew Jeffery
1895ab4e3e8SAndrew Jeffery rc = read(context->fds[MBOX_FD].fd, &status, sizeof(status));
1905ab4e3e8SAndrew Jeffery if (rc < 0)
1915ab4e3e8SAndrew Jeffery return rc;
1925ab4e3e8SAndrew Jeffery
1935ab4e3e8SAndrew Jeffery return status;
1945ab4e3e8SAndrew Jeffery }
1955ab4e3e8SAndrew Jeffery
1965ab4e3e8SAndrew Jeffery struct mbox_test_context {
1975ab4e3e8SAndrew Jeffery struct tmpf mbox;
1985ab4e3e8SAndrew Jeffery struct tmpf flash;
1995ab4e3e8SAndrew Jeffery struct tmpf lpc;
2005ab4e3e8SAndrew Jeffery struct mbox_context context;
2015ab4e3e8SAndrew Jeffery } test;
2025ab4e3e8SAndrew Jeffery
cleanup(void)2035ab4e3e8SAndrew Jeffery void cleanup(void)
2045ab4e3e8SAndrew Jeffery {
2055ab4e3e8SAndrew Jeffery tmpf_destroy(&test.mbox);
2065ab4e3e8SAndrew Jeffery tmpf_destroy(&test.flash);
2075ab4e3e8SAndrew Jeffery tmpf_destroy(&test.lpc);
2085ab4e3e8SAndrew Jeffery }
2095ab4e3e8SAndrew Jeffery
2105ab4e3e8SAndrew Jeffery int __init_mbox_dev(struct mbox_context *context, const char *path);
2115ab4e3e8SAndrew Jeffery int __init_lpc_dev(struct mbox_context *context, const char *path);
2125ab4e3e8SAndrew Jeffery
mbox_create_test_context(int n_windows,size_t len)2135ab4e3e8SAndrew Jeffery struct mbox_context *mbox_create_test_context(int n_windows, size_t len)
2145ab4e3e8SAndrew Jeffery {
2155ab4e3e8SAndrew Jeffery int rc;
2165ab4e3e8SAndrew Jeffery
2175ab4e3e8SAndrew Jeffery mbox_vlog = &mbox_log_console;
2185ab4e3e8SAndrew Jeffery verbosity = 2;
2195ab4e3e8SAndrew Jeffery
2205ab4e3e8SAndrew Jeffery atexit(cleanup);
2215ab4e3e8SAndrew Jeffery
222c3144042SAndrew Jeffery rc = tmpf_init(&test.mbox, "mbox-store.XXXXXX");
2235ab4e3e8SAndrew Jeffery assert(rc == 0);
2245ab4e3e8SAndrew Jeffery
225c3144042SAndrew Jeffery rc = tmpf_init(&test.flash, "flash-store.XXXXXX");
2265ab4e3e8SAndrew Jeffery assert(rc == 0);
2275ab4e3e8SAndrew Jeffery
228c3144042SAndrew Jeffery rc = tmpf_init(&test.lpc, "lpc-store.XXXXXX");
2295ab4e3e8SAndrew Jeffery assert(rc == 0);
2305ab4e3e8SAndrew Jeffery
2315ab4e3e8SAndrew Jeffery test.context.windows.num = n_windows;
2325ab4e3e8SAndrew Jeffery test.context.windows.default_size = len;
2335ab4e3e8SAndrew Jeffery
2345ab4e3e8SAndrew Jeffery /*
235*efb09defSAndrew Jeffery * We need to call __init_mbox_dev() to initialise the handler table.
236*efb09defSAndrew Jeffery * However, afterwards we need to discard the fd of the clearly useless
237*efb09defSAndrew Jeffery * /dev/null and replace it with our own fd for mbox device emulation
238*efb09defSAndrew Jeffery * by the test framework.
2395ab4e3e8SAndrew Jeffery */
240*efb09defSAndrew Jeffery __init_mbox_dev(&test.context, "/dev/null");
241*efb09defSAndrew Jeffery rc = close(test.context.fds[MBOX_FD].fd);
242*efb09defSAndrew Jeffery assert(rc == 0);
2435ab4e3e8SAndrew Jeffery test.context.fds[MBOX_FD].fd = test.mbox.fd;
2445ab4e3e8SAndrew Jeffery
2455ab4e3e8SAndrew Jeffery rc = init_flash_dev(&test.context);
2465ab4e3e8SAndrew Jeffery assert(rc == 0);
2475ab4e3e8SAndrew Jeffery
2485ab4e3e8SAndrew Jeffery rc = fallocate(test.flash.fd, 0, 0, test.context.mtd_info.size);
2495ab4e3e8SAndrew Jeffery assert(rc == 0);
2505ab4e3e8SAndrew Jeffery
251d23affd4SAndrew Jeffery rc = __init_lpc_dev(&test.context, test.lpc.path);
2525ab4e3e8SAndrew Jeffery assert(rc == 0);
2535ab4e3e8SAndrew Jeffery
254d23affd4SAndrew Jeffery rc = fallocate(test.lpc.fd, 0, 0, test.context.mem_size);
2555ab4e3e8SAndrew Jeffery assert(rc == 0);
2565ab4e3e8SAndrew Jeffery
2575ab4e3e8SAndrew Jeffery rc = init_windows(&test.context);
2585ab4e3e8SAndrew Jeffery assert(rc == 0);
2595ab4e3e8SAndrew Jeffery
2605ab4e3e8SAndrew Jeffery return rc ? NULL : &test.context;
2615ab4e3e8SAndrew Jeffery }
2625ab4e3e8SAndrew Jeffery
2635ab4e3e8SAndrew Jeffery /* From ccan's container_of module, CC0 license */
2645ab4e3e8SAndrew Jeffery #define container_of(member_ptr, containing_type, member) \
2655ab4e3e8SAndrew Jeffery ((containing_type *) \
2665ab4e3e8SAndrew Jeffery ((char *)(member_ptr) \
2675ab4e3e8SAndrew Jeffery - container_off(containing_type, member)) \
2685ab4e3e8SAndrew Jeffery + check_types_match(*(member_ptr), ((containing_type *)0)->member))
2695ab4e3e8SAndrew Jeffery
2705ab4e3e8SAndrew Jeffery /* From ccan's container_of module, CC0 license */
2715ab4e3e8SAndrew Jeffery #define container_off(containing_type, member) \
2725ab4e3e8SAndrew Jeffery offsetof(containing_type, member)
2735ab4e3e8SAndrew Jeffery
2745ab4e3e8SAndrew Jeffery /* From ccan's check_type module, CC0 license */
2755ab4e3e8SAndrew Jeffery #define check_type(expr, type) \
2765ab4e3e8SAndrew Jeffery ((typeof(expr) *)0 != (type *)0)
2775ab4e3e8SAndrew Jeffery
2785ab4e3e8SAndrew Jeffery /* From ccan's check_type module, CC0 license */
2795ab4e3e8SAndrew Jeffery #define check_types_match(expr1, expr2) \
2805ab4e3e8SAndrew Jeffery ((typeof(expr1) *)0 != (typeof(expr2) *)0)
2815ab4e3e8SAndrew Jeffery
mbox_set_mtd_data(struct mbox_context * context,const void * data,size_t len)2825ab4e3e8SAndrew Jeffery int mbox_set_mtd_data(struct mbox_context *context, const void *data,
2835ab4e3e8SAndrew Jeffery size_t len)
2845ab4e3e8SAndrew Jeffery {
2855ab4e3e8SAndrew Jeffery struct mbox_test_context *arg;
2865ab4e3e8SAndrew Jeffery void *map;
2875ab4e3e8SAndrew Jeffery
2885ab4e3e8SAndrew Jeffery /* Sanity check */
2895ab4e3e8SAndrew Jeffery arg = container_of(context, struct mbox_test_context, context);
2905ab4e3e8SAndrew Jeffery if (&test != arg)
2915ab4e3e8SAndrew Jeffery return -1;
2925ab4e3e8SAndrew Jeffery
2935ab4e3e8SAndrew Jeffery if (len > test.context.mtd_info.size)
2945ab4e3e8SAndrew Jeffery return -2;
2955ab4e3e8SAndrew Jeffery
2965ab4e3e8SAndrew Jeffery map = mmap(NULL, test.context.mtd_info.size, PROT_WRITE, MAP_SHARED,
2975ab4e3e8SAndrew Jeffery test.flash.fd, 0);
2985ab4e3e8SAndrew Jeffery assert(map != MAP_FAILED);
2995ab4e3e8SAndrew Jeffery memcpy(map, data, len);
3005ab4e3e8SAndrew Jeffery munmap(map, test.context.mtd_info.size);
3015ab4e3e8SAndrew Jeffery
3025ab4e3e8SAndrew Jeffery return 0;
3035ab4e3e8SAndrew Jeffery }
3045ab4e3e8SAndrew Jeffery
get_dev_mtd(void)3055ab4e3e8SAndrew Jeffery char *get_dev_mtd(void)
3065ab4e3e8SAndrew Jeffery {
3075ab4e3e8SAndrew Jeffery return strdup(test.flash.path);
3085ab4e3e8SAndrew Jeffery }
309