blob: 3dc1611c0c24eb97cff22418aab9b12a173a941f [file] [log] [blame]
Andrew Jeffery4fe996c2018-02-27 12:16:48 +10301// SPDX-License-Identifier: Apache-2.0
2// Copyright (C) 2018 IBM Corp.
Andrew Jeffery5ab4e3e2017-04-12 14:23:24 +09303
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 "mboxd_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
Andrew Jefferyee7af882018-03-01 11:57:50 +103026void dump_buf(const void *buf, size_t len)
Andrew Jeffery5ab4e3e2017-04-12 14:23:24 +093027{
Andrew Jefferyee7af882018-03-01 11:57:50 +103028 const uint8_t *buf8 = buf;
Andrew Jeffery5ab4e3e2017-04-12 14:23:24 +093029 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++)
Andrew Jefferyee7af882018-03-01 11:57:50 +103041 printf("0x%02x, ", buf8[i + j]);
Andrew Jeffery5ab4e3e2017-04-12 14:23:24 +093042
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
62int 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
90int mbox_command_write(struct mbox_context *context, const uint8_t *command,
91 size_t len)
92{
93 size_t remaining;
94 int rc;
95 int fd;
96
97 fd = context->fds[MBOX_FD].fd;
98 rc = lseek(fd, 0, SEEK_SET);
99 if (rc != 0)
100 return -1;
101
102 remaining = len;
103 while (remaining > 0) {
104 rc = write(fd, command, remaining);
105 if (rc < 0)
106 goto out;
107 remaining -= rc;
108 }
109
110 rc = lseek(fd, 0, SEEK_SET);
111 if (rc != 0)
112 return -1;
113
114out:
115 return rc;
116}
117
118int mbox_command_dispatch(struct mbox_context *context, const uint8_t *command,
119 size_t len)
120{
121 uint8_t status;
122 int rc;
123
124 rc = mbox_command_write(context, command, len);
125 if (rc < 0)
126 return rc;
127
128 rc = dispatch_mbox(context);
129 if (rc < 0)
130 return -rc;
131
132 /*
133 * The aspeed-lpc-ctrl driver implements mailbox register access
134 * through the usual read()/write() chardev interface.
135 *
136 * The typical access sequence is:
137 *
138 * 1. Read all the registers out
139 * 2. Perform the action specified
140 * 3. Write a response to the registers.
141 *
142 * For the tests the "device" file descriptor is backed by a temporary
143 * file. The above sequence leads to a file-size of 30 bytes: 16 bytes
144 * for the issued command, followed by a 14-byte response.
145 *
146 * However, the typical access pattern isn't the only access pattern.
147 * Individual byte registers can be accessed by lseek()'ing on the
148 * device's file descriptor and issuing read() or write() as desired.
149 * The daemon uses this property to manage the BMC status byte, and the
150 * implementation cleans up after status byte operations by lseek()'ing
151 * back to offset zero.
152 *
153 * Thus for the BMC_EVENT_ACK command the file only reaches a size of
154 * 16 bytes; the daemon's response overwrites the first 14 bytes of the
155 * command injected by the tests.
156 *
157 * The consequence of this is that the response status byte can either
158 * appear at offset 13, or offset 29, depending on the command.
159 *
160 * However, regardless of what command is issued the response data is
161 * written to the device and the file descriptor is left in the
162 * post-write() state. This means the status byte can always be
163 * accessed relative to the current position by an lseek() of type
164 * SEEK_CUR for offset -1.
165 *
166 */
167 rc = lseek(context->fds[MBOX_FD].fd, -1, SEEK_CUR);
168 if (rc < 0)
169 return rc;
170
171 rc = read(context->fds[MBOX_FD].fd, &status, sizeof(status));
172 if (rc < 0)
173 return rc;
174
175 return status;
176}
177
178struct mbox_test_context {
179 struct tmpf mbox;
180 struct tmpf flash;
181 struct tmpf lpc;
182 struct mbox_context context;
183} test;
184
185void cleanup(void)
186{
187 tmpf_destroy(&test.mbox);
188 tmpf_destroy(&test.flash);
189 tmpf_destroy(&test.lpc);
190}
191
192int __init_mbox_dev(struct mbox_context *context, const char *path);
193int __init_lpc_dev(struct mbox_context *context, const char *path);
194
195struct mbox_context *mbox_create_test_context(int n_windows, size_t len)
196{
197 int rc;
198
199 mbox_vlog = &mbox_log_console;
200 verbosity = 2;
201
202 atexit(cleanup);
203
Andrew Jefferyc3144042018-02-26 13:24:52 +1030204 rc = tmpf_init(&test.mbox, "mbox-store.XXXXXX");
Andrew Jeffery5ab4e3e2017-04-12 14:23:24 +0930205 assert(rc == 0);
206
Andrew Jefferyc3144042018-02-26 13:24:52 +1030207 rc = tmpf_init(&test.flash, "flash-store.XXXXXX");
Andrew Jeffery5ab4e3e2017-04-12 14:23:24 +0930208 assert(rc == 0);
209
Andrew Jefferyc3144042018-02-26 13:24:52 +1030210 rc = tmpf_init(&test.lpc, "lpc-store.XXXXXX");
Andrew Jeffery5ab4e3e2017-04-12 14:23:24 +0930211 assert(rc == 0);
212
213 test.context.windows.num = n_windows;
214 test.context.windows.default_size = len;
215
216 /*
217 * We need to control MBOX_FD, so don't call __init_mbox_dev().
218 * Instead, insert our temporary file's fd directly into the context
219 */
220 test.context.fds[MBOX_FD].fd = test.mbox.fd;
221
222 rc = init_flash_dev(&test.context);
223 assert(rc == 0);
224
225 rc = fallocate(test.flash.fd, 0, 0, test.context.mtd_info.size);
226 assert(rc == 0);
227
228 rc = fallocate(test.lpc.fd, 0, 0, test.context.mtd_info.size);
229 assert(rc == 0);
230
231 rc = __init_lpc_dev(&test.context, test.lpc.path);
232 assert(rc == 0);
233
234 rc = init_windows(&test.context);
235 assert(rc == 0);
236
237 return rc ? NULL : &test.context;
238}
239
240/* From ccan's container_of module, CC0 license */
241#define container_of(member_ptr, containing_type, member) \
242 ((containing_type *) \
243 ((char *)(member_ptr) \
244 - container_off(containing_type, member)) \
245 + check_types_match(*(member_ptr), ((containing_type *)0)->member))
246
247/* From ccan's container_of module, CC0 license */
248#define container_off(containing_type, member) \
249 offsetof(containing_type, member)
250
251/* From ccan's check_type module, CC0 license */
252#define check_type(expr, type) \
253 ((typeof(expr) *)0 != (type *)0)
254
255/* From ccan's check_type module, CC0 license */
256#define check_types_match(expr1, expr2) \
257 ((typeof(expr1) *)0 != (typeof(expr2) *)0)
258
259int mbox_set_mtd_data(struct mbox_context *context, const void *data,
260 size_t len)
261{
262 struct mbox_test_context *arg;
263 void *map;
264
265 /* Sanity check */
266 arg = container_of(context, struct mbox_test_context, context);
267 if (&test != arg)
268 return -1;
269
270 if (len > test.context.mtd_info.size)
271 return -2;
272
273 map = mmap(NULL, test.context.mtd_info.size, PROT_WRITE, MAP_SHARED,
274 test.flash.fd, 0);
275 assert(map != MAP_FAILED);
276 memcpy(map, data, len);
277 munmap(map, test.context.mtd_info.size);
278
279 return 0;
280}
281
282char *get_dev_mtd(void)
283{
284 return strdup(test.flash.path);
285}