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