xref: /openbmc/phosphor-mboxd/test/mbox.c (revision c3144042b9f050c3d880f4ae5464a2a21617fe7d)
1 /*
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 
42 void 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 
77 int 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 
105 int 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 
129 out:
130 	return rc;
131 }
132 
133 int 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 
193 struct mbox_test_context {
194 	struct tmpf mbox;
195 	struct tmpf flash;
196 	struct tmpf lpc;
197 	struct mbox_context context;
198 } test;
199 
200 void cleanup(void)
201 {
202 	tmpf_destroy(&test.mbox);
203 	tmpf_destroy(&test.flash);
204 	tmpf_destroy(&test.lpc);
205 }
206 
207 int __init_mbox_dev(struct mbox_context *context, const char *path);
208 int __init_lpc_dev(struct mbox_context *context, const char *path);
209 
210 struct 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, "mbox-store.XXXXXX");
220 	assert(rc == 0);
221 
222 	rc = tmpf_init(&test.flash, "flash-store.XXXXXX");
223 	assert(rc == 0);
224 
225 	rc = tmpf_init(&test.lpc, "lpc-store.XXXXXX");
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 
274 int 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 
297 char *get_dev_mtd(void)
298 {
299 	return strdup(test.flash.path);
300 }
301