xref: /openbmc/hiomapd/test/mbox.c (revision 68a24c9e)
1 // SPDX-License-Identifier: Apache-2.0
2 // Copyright (C) 2018 IBM Corp.
3 
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/ioctl.h>
12 #include <sys/mman.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <unistd.h>
16 
17 #include "config.h"
18 #include "mboxd.h"
19 #include "backend.h"
20 #include "lpc.h"
21 #include "transport_mbox.h"
22 #include "windows.h"
23 
24 #include "test/mbox.h"
25 
26 #define STEP 16
27 
dump_buf(const void * buf,size_t len)28 void dump_buf(const void *buf, size_t len)
29 {
30 	const uint8_t *buf8 = buf;
31 	size_t i;
32 
33 	for (i = 0; i < len; i += STEP) {
34 		int delta;
35 		int max;
36 		int j;
37 
38 		delta = len - i;
39 		max = delta > STEP ? STEP : delta;
40 
41 		printf("0x%08zx:\t", i);
42 		for (j = 0; j < max; j++)
43 			printf("0x%02x, ", buf8[i + j]);
44 
45 		printf("\n");
46 	}
47 	printf("\n");
48 }
49 
50 /*
51  * Because we are using a file and not a pipe for the mbox file descriptor we
52  * need to handle a difference in behaviour. For a given command and response
53  * sequence the first 16 bytes of the file are occupied by the mbox command.
54  * The response occupies the following 14 bytes for a total of 30 bytes.
55  *
56  * We also have to ensure we lseek() to reset the file descriptor offset back
57  * to the start of the file before dispatching the mbox command.
58  */
59 
60 /* Macros for handling the pipe/file discrepancy */
61 #define RESPONSE_OFFSET	16
62 #define RESPONSE_SIZE	14
63 
mbox_cmp(struct mbox_context * context,const uint8_t * expected,size_t len)64 int mbox_cmp(struct mbox_context *context, const uint8_t *expected, size_t len)
65 {
66 	struct stat details;
67 	uint8_t *map;
68 	int rc;
69 	int fd;
70 
71 	fd = context->fds[MBOX_FD].fd;
72 	fstat(fd, &details);
73 
74 	map = mmap(NULL, details.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
75 	printf("%s:%d: details.st_size: %ld, RESPONSE_OFFSET + len: %ld\n",
76 	       __func__, __LINE__, details.st_size, RESPONSE_OFFSET + len);
77 	assert(map != MAP_FAILED);
78 	assert(details.st_size >= (__off_t)(RESPONSE_OFFSET + len));
79 
80 	rc = memcmp(expected, &map[RESPONSE_OFFSET], len);
81 
82 	if (rc != 0) {
83 		printf("\nMBOX state (%ld):\n", details.st_size);
84 		dump_buf(map, details.st_size);
85 		printf("Expected response (%lu):\n", len);
86 		dump_buf(expected, len);
87 	}
88 
89 	munmap(map, details.st_size);
90 
91 	return rc;
92 }
93 
mbox_rspcpy(struct mbox_context * context,struct mbox_msg * msg)94 void mbox_rspcpy(struct mbox_context *context, struct mbox_msg *msg)
95 {
96 	struct stat details;
97 	uint8_t *map;
98 	int fd;
99 
100 	fd = context->fds[MBOX_FD].fd;
101 	fstat(fd, &details);
102 
103 	map = mmap(NULL, details.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
104 	assert(map != MAP_FAILED);
105 	assert(details.st_size >= (RESPONSE_OFFSET + RESPONSE_SIZE));
106 
107 	memcpy(msg, &map[RESPONSE_OFFSET], RESPONSE_SIZE);
108 
109 	munmap(map, details.st_size);
110 }
111 
mbox_command_write(struct mbox_context * context,const uint8_t * command,size_t len)112 int mbox_command_write(struct mbox_context *context, const uint8_t *command,
113 		size_t len)
114 {
115 	size_t remaining;
116 	int rc;
117 	int fd;
118 
119 	fd = context->fds[MBOX_FD].fd;
120 	rc = lseek(fd, 0, SEEK_SET);
121 	if (rc != 0)
122 		return -1;
123 
124 	remaining = len;
125 	while (remaining > 0) {
126 		rc = write(fd, command, remaining);
127 		if (rc < 0)
128 			goto out;
129 		remaining -= rc;
130 	}
131 
132 	rc = lseek(fd, 0, SEEK_SET);
133 	if (rc != 0)
134 		return -1;
135 
136 out:
137 	return rc;
138 }
139 
mbox_command_dispatch(struct mbox_context * context,const uint8_t * command,size_t len)140 int mbox_command_dispatch(struct mbox_context *context, const uint8_t *command,
141 		size_t len)
142 {
143 	uint8_t status;
144 	int rc;
145 
146 	rc = mbox_command_write(context, command, len);
147 	if (rc < 0)
148 		return rc;
149 
150 	rc = transport_mbox_dispatch(context);
151 	if (rc < 0)
152 		return -rc;
153 
154 	/*
155 	 * The aspeed-lpc-ctrl driver implements mailbox register access
156 	 * through the usual read()/write() chardev interface.
157 	 *
158 	 * The typical access sequence is:
159 	 *
160 	 * 1. Read all the registers out
161 	 * 2. Perform the action specified
162 	 * 3. Write a response to the registers.
163 	 *
164 	 * For the tests the "device" file descriptor is backed by a temporary
165 	 * file. The above sequence leads to a file-size of 30 bytes: 16 bytes
166 	 * for the issued command, followed by a 14-byte response.
167 	 *
168 	 * However, the typical access pattern isn't the only access pattern.
169 	 * Individual byte registers can be accessed by lseek()'ing on the
170 	 * device's file descriptor and issuing read() or write() as desired.
171 	 * The daemon uses this property to manage the BMC status byte, and the
172 	 * implementation cleans up after status byte operations by lseek()'ing
173 	 * back to offset zero.
174 	 *
175 	 * Thus for the BMC_EVENT_ACK command the file only reaches a size of
176 	 * 16 bytes; the daemon's response overwrites the first 14 bytes of the
177 	 * command injected by the tests.
178 	 *
179 	 * The consequence of this is that the response status byte can either
180 	 * appear at offset 13, or offset 29, depending on the command.
181 	 *
182 	 * However, regardless of what command is issued the response data is
183 	 * written to the device and the file descriptor is left in the
184 	 * post-write() state. This means the status byte can always be
185 	 * accessed relative to the current position by an lseek() of type
186 	 * SEEK_CUR for offset -1.
187 	 *
188 	 */
189 	rc = lseek(context->fds[MBOX_FD].fd, -1, SEEK_CUR);
190 	if (rc < 0)
191 		return rc;
192 
193 	rc = read(context->fds[MBOX_FD].fd, &status, sizeof(status));
194 	if (rc < 0)
195 		return rc;
196 
197 	return status;
198 }
199 
200 struct mbox_test_context {
201 	struct tmpf mbox;
202 	struct tmpf flash;
203 	struct tmpf lpc;
204 	struct mbox_context context;
205 } test;
206 
cleanup(void)207 void cleanup(void)
208 {
209 	tmpf_destroy(&test.mbox);
210 	tmpf_destroy(&test.flash);
211 	tmpf_destroy(&test.lpc);
212 }
213 
214 int __transport_mbox_init(struct mbox_context *context, const char *path,
215 			  const struct transport_ops **ops);
216 int __lpc_dev_init(struct mbox_context *context, const char *path);
217 
mbox_create_frontend_context(int n_windows,size_t len)218 struct mbox_context *mbox_create_frontend_context(int n_windows, size_t len)
219 {
220 	const struct transport_ops *ops;
221 	struct mtd_info_user mtd_info;
222 	int rc;
223 
224 	mbox_vlog = &mbox_log_console;
225 	verbosity = 2;
226 
227 	atexit(cleanup);
228 
229 	rc = tmpf_init(&test.mbox, "mbox-store.XXXXXX");
230 	assert(rc == 0);
231 
232 	rc = tmpf_init(&test.lpc, "lpc-store.XXXXXX");
233 	assert(rc == 0);
234 
235 	test.context.windows.num = n_windows;
236 	test.context.windows.default_size = len;
237 
238 	rc = protocol_init(&test.context);
239 	assert(rc == 0);
240 
241 	/*
242 	 * We need to call __transport_mbox_init() to initialise the handler table.
243 	 * However, afterwards we need to discard the fd of the clearly useless
244 	 * /dev/null and replace it with our own fd for mbox device emulation
245 	 * by the test framework.
246 	 */
247 	__transport_mbox_init(&test.context, "/dev/null", &ops);
248 	test.context.transport = ops;
249 	rc = close(test.context.fds[MBOX_FD].fd);
250 	assert(rc == 0);
251 	test.context.fds[MBOX_FD].fd = test.mbox.fd;
252 
253 	/* Instantiate the mtd backend */
254 	rc = tmpf_init(&test.flash, "flash-store.XXXXXX");
255 	assert(rc == 0);
256 
257 	rc = ioctl(test.flash.fd, MEMGETINFO, &mtd_info);
258 	assert(rc == 0);
259 
260 	rc = fallocate(test.flash.fd, 0, 0, mtd_info.size);
261 	assert(rc == 0);
262 
263 	test.context.backend.flash_size = mtd_info.size;
264 
265 	rc = __lpc_dev_init(&test.context, test.lpc.path);
266 	assert(rc == 0);
267 
268 	rc = fallocate(test.lpc.fd, 0, 0, test.context.mem_size);
269 	assert(rc == 0);
270 
271 	rc = windows_init(&test.context);
272 	assert(rc == 0);
273 
274 	return rc ? NULL : &test.context;
275 }
276 
mbox_create_test_context(int n_windows,size_t len)277 struct mbox_context *mbox_create_test_context(int n_windows, size_t len)
278 {
279 	struct mbox_context *ctx;
280 	int rc;
281 
282 	ctx = mbox_create_frontend_context(n_windows, len);
283 	assert(ctx);
284 
285 	rc = backend_probe_mtd(&ctx->backend, test.flash.path);
286 	assert(rc == 0);
287 
288 	return ctx;
289 }
290 
291 /* From ccan's container_of module, CC0 license */
292 #define container_of(member_ptr, containing_type, member)		\
293 	 ((containing_type *)						\
294 	  ((char *)(member_ptr)						\
295 	   - container_off(containing_type, member))			\
296 	  + check_types_match(*(member_ptr), ((containing_type *)0)->member))
297 
298 /* From ccan's container_of module, CC0 license */
299 #define container_off(containing_type, member)	\
300 		offsetof(containing_type, member)
301 
302 /* From ccan's check_type module, CC0 license */
303 #define check_type(expr, type)			\
304 	((typeof(expr) *)0 != (type *)0)
305 
306 /* From ccan's check_type module, CC0 license */
307 #define check_types_match(expr1, expr2)		\
308 	((typeof(expr1) *)0 != (typeof(expr2) *)0)
309 
mbox_set_mtd_data(struct mbox_context * context,const void * data,size_t len)310 int mbox_set_mtd_data(struct mbox_context *context, const void *data,
311 		size_t len)
312 {
313 	struct mbox_test_context *arg;
314 	void *map;
315 
316 	assert(test.flash.fd > 2);
317 
318 	/* Sanity check */
319 	arg = container_of(context, struct mbox_test_context, context);
320 	assert(&test == arg);
321 	assert(len <= test.context.backend.flash_size);
322 
323 	map = mmap(NULL, test.context.backend.flash_size,
324 		   PROT_WRITE, MAP_SHARED, test.flash.fd, 0);
325 	assert(map != MAP_FAILED);
326 	memcpy(map, data, len);
327 	munmap(map, test.context.backend.flash_size);
328 
329 	return 0;
330 }
331 
get_dev_mtd(void)332 char *get_dev_mtd(void)
333 {
334 	return strdup(test.flash.path);
335 }
336