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