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