xref: /openbmc/hiomapd/mboxd.c (revision d5f1d40f2ddf9234099b331e1d90c54ece0ee88b)
1  // SPDX-License-Identifier: Apache-2.0
2  // Copyright (C) 2018 IBM Corp.
3  
4  #define _GNU_SOURCE
5  #include <assert.h>
6  #include <errno.h>
7  #include <fcntl.h>
8  #include <getopt.h>
9  #include <limits.h>
10  #include <poll.h>
11  #include <stdbool.h>
12  #include <stdint.h>
13  #include <stdio.h>
14  #include <stdlib.h>
15  #include <string.h>
16  #include <syslog.h>
17  #include <signal.h>
18  #include <sys/ioctl.h>
19  #include <sys/mman.h>
20  #include <sys/stat.h>
21  #include <sys/timerfd.h>
22  #include <sys/types.h>
23  #include <sys/signalfd.h>
24  #include <time.h>
25  #include <unistd.h>
26  #include <inttypes.h>
27  #include <systemd/sd-bus.h>
28  
29  #include "config.h"
30  #include "mboxd.h"
31  #include "common.h"
32  #include "dbus.h"
33  #include "control_dbus.h"
34  #include "flash.h"
35  #include "lpc.h"
36  #include "transport_mbox.h"
37  #include "transport_dbus.h"
38  #include "windows.h"
39  #include "vpnor/mboxd_pnor_partition_table.h"
40  
41  #define USAGE \
42  "\nUsage: %s [-V | --version] [-h | --help] [-v[v] | --verbose] [-s | --syslog]\n" \
43  "\t\t[-n | --window-num <num>]\n" \
44  "\t\t[-w | --window-size <size>M]\n" \
45  "\t\t-f | --flash <size>[K|M]\n\n" \
46  "\t-v | --verbose\t\tBe [more] verbose\n" \
47  "\t-s | --syslog\t\tLog output to syslog (pointless without -v)\n" \
48  "\t-n | --window-num\tThe number of windows\n" \
49  "\t\t\t\t(default: fill the reserved memory region)\n" \
50  "\t-w | --window-size\tThe window size (power of 2) in MB\n" \
51  "\t\t\t\t(default: 1MB)\n" \
52  "\t-f | --flash\t\tSize of flash in [K|M] bytes\n\n"
53  
54  static int dbus_init(struct mbox_context *context)
55  {
56  	int rc;
57  
58  	rc = sd_bus_default_system(&context->bus);
59  	if (rc < 0) {
60  		MSG_ERR("Failed to connect to the system bus: %s\n",
61  			strerror(-rc));
62  		return rc;
63  	}
64  
65  	rc = control_legacy_init(context);
66  	if (rc < 0) {
67  		MSG_ERR("Failed to initialise legacy DBus interface: %s\n",
68  			strerror(-rc));
69  		return rc;
70  	}
71  
72  	rc = control_dbus_init(context);
73  	if (rc < 0) {
74  		MSG_ERR("Failed to initialise DBus control interface: %s\n",
75  			strerror(-rc));
76  		return rc;
77  	}
78  
79  	rc = transport_dbus_init(context);
80  	if (rc < 0) {
81  		MSG_ERR("Failed to initialise DBus protocol interface: %s\n",
82  			strerror(-rc));
83  		return rc;
84  	}
85  
86  	rc = sd_bus_request_name(context->bus, MBOX_DBUS_NAME,
87  				 SD_BUS_NAME_ALLOW_REPLACEMENT |
88  				 SD_BUS_NAME_REPLACE_EXISTING);
89  	if (rc < 0) {
90  		MSG_ERR("Failed to request DBus name: %s\n", strerror(-rc));
91  		return rc;
92  	}
93  
94  	rc = sd_bus_get_fd(context->bus);
95  	if (rc < 0) {
96  		MSG_ERR("Failed to get bus fd: %s\n", strerror(-rc));
97  		return rc;
98  	}
99  
100  	context->fds[DBUS_FD].fd = rc;
101  
102  	return 0;
103  }
104  
105  static void dbus_free(struct mbox_context *context)
106  {
107  	transport_dbus_free(context);
108  	control_dbus_free(context);
109  	control_legacy_free(context);
110  	sd_bus_unref(context->bus);
111  }
112  
113  static int poll_loop(struct mbox_context *context)
114  {
115  	int rc = 0, i;
116  
117  	/* Set POLLIN on polling file descriptors */
118  	for (i = 0; i < POLL_FDS; i++) {
119  		context->fds[i].events = POLLIN;
120  	}
121  
122  	while (1) {
123  		rc = poll(context->fds, POLL_FDS, -1);
124  
125  		if (rc < 0) { /* Error */
126  			MSG_ERR("Error from poll(): %s\n", strerror(errno));
127  			break; /* This should mean we clean up nicely */
128  		}
129  
130  		/* Event on Polled File Descriptor - Handle It */
131  		if (context->fds[SIG_FD].revents & POLLIN) { /* Signal */
132  			struct signalfd_siginfo info = { 0 };
133  
134  			rc = read(context->fds[SIG_FD].fd, (void *) &info,
135  				  sizeof(info));
136  			if (rc != sizeof(info)) {
137  				MSG_ERR("Error reading signal event: %s\n",
138  					strerror(errno));
139  			}
140  
141  			MSG_DBG("Received signal: %d\n", info.ssi_signo);
142  			switch (info.ssi_signo) {
143  			case SIGINT:
144  			case SIGTERM:
145  				MSG_INFO("Caught Signal - Exiting...\n");
146  				context->terminate = true;
147  				break;
148  			case SIGHUP:
149  				/* Host didn't request reset -> Notify it */
150  				if (windows_reset_all(context)) {
151  				       rc = protocol_events_set(context,
152  						     BMC_EVENT_WINDOW_RESET);
153  				       if (rc < 0) {
154  					      MSG_ERR("Failed to notify host of reset, expect host-side corruption\n");
155  					      break;
156  				       }
157  				}
158  				rc = lpc_reset(context);
159  				if (rc < 0) {
160  					MSG_ERR("WARNING: Failed to point the "
161  						"LPC bus back to flash on "
162  						"SIGHUP\nIf the host requires "
163  						"this expect problems...\n");
164  				}
165  				break;
166  			default:
167  				MSG_ERR("Unhandled Signal: %d\n",
168  					info.ssi_signo);
169  				break;
170  			}
171  		}
172  		if (context->fds[DBUS_FD].revents & POLLIN) { /* DBUS */
173  			while ((rc = sd_bus_process(context->bus, NULL)) > 0) {
174  				MSG_DBG("DBUS Event\n");
175  			}
176  			if (rc < 0) {
177  				MSG_ERR("Error handling DBUS event: %s\n",
178  						strerror(-rc));
179  			}
180  		}
181  		if (context->terminate) {
182  			break; /* This should mean we clean up nicely */
183  		}
184  		if (context->fds[MBOX_FD].revents & POLLIN) { /* MBOX */
185  			MSG_DBG("MBOX Event\n");
186  			rc = transport_mbox_dispatch(context);
187  			if (rc < 0) {
188  				MSG_ERR("Error handling MBOX event\n");
189  			}
190  		}
191  	}
192  
193  	/* Best to reset windows and the lpc mapping for safety */
194  	/* Host didn't request reset -> Notify it */
195  	if (windows_reset_all(context)) {
196  	       rc = protocol_events_set(context, BMC_EVENT_WINDOW_RESET);
197  	       if (rc < 0) {
198  		      MSG_ERR("Failed to notify host of reset, expect host-side corruption\n");
199  		      return rc;
200  	       }
201  	}
202  	rc = lpc_reset(context);
203  	/* Not much we can do if this fails */
204  	if (rc < 0) {
205  		MSG_ERR("WARNING: Failed to point the LPC bus back to flash\n"
206  			"If the host requires this expect problems...\n");
207  	}
208  
209  	return rc;
210  }
211  
212  static int init_signals(struct mbox_context *context, sigset_t *set)
213  {
214  	int rc;
215  
216  	/* Block SIGHUPs, SIGTERMs and SIGINTs */
217  	sigemptyset(set);
218  	sigaddset(set, SIGHUP);
219  	sigaddset(set, SIGINT);
220  	sigaddset(set, SIGTERM);
221  	rc = sigprocmask(SIG_BLOCK, set, NULL);
222  	if (rc < 0) {
223  		MSG_ERR("Failed to set SIG_BLOCK mask %s\n", strerror(errno));
224  		return rc;
225  	}
226  
227  	/* Get Signal File Descriptor */
228  	rc = signalfd(-1, set, SFD_NONBLOCK);
229  	if (rc < 0) {
230  		MSG_ERR("Failed to get signalfd %s\n", strerror(errno));
231  		return rc;
232  	}
233  
234  	context->fds[SIG_FD].fd = rc;
235  	return 0;
236  }
237  
238  static void usage(const char *name)
239  {
240  	printf(USAGE, name);
241  }
242  
243  static bool parse_cmdline(int argc, char **argv,
244  			  struct mbox_context *context)
245  {
246  	char *endptr;
247  	int opt;
248  
249  	static const struct option long_options[] = {
250  		{ "flash",		required_argument,	0, 'f' },
251  		{ "window-size",	optional_argument,	0, 'w' },
252  		{ "window-num",		optional_argument,	0, 'n' },
253  		{ "verbose",		no_argument,		0, 'v' },
254  		{ "syslog",		no_argument,		0, 's' },
255  		{ "version",		no_argument,		0, 'V' },
256  		{ "help",		no_argument,		0, 'h' },
257  		{ 0,			0,			0, 0   }
258  	};
259  
260  	verbosity = MBOX_LOG_NONE;
261  	mbox_vlog = &mbox_log_console;
262  
263  	context->current = NULL; /* No current window */
264  
265  	while ((opt = getopt_long(argc, argv, "f:w::n::vsVh", long_options, NULL))
266  			!= -1) {
267  		switch (opt) {
268  		case 0:
269  			break;
270  		case 'f':
271  			context->flash_size = strtol(optarg, &endptr, 10);
272  			if (optarg == endptr) {
273  				fprintf(stderr, "Unparseable flash size\n");
274  				return false;
275  			}
276  			switch (*endptr) {
277  			case '\0':
278  				break;
279  			case 'M':
280  				context->flash_size <<= 10;
281  			case 'K':
282  				context->flash_size <<= 10;
283  				break;
284  			default:
285  				fprintf(stderr, "Unknown units '%c'\n",
286  					*endptr);
287  				return false;
288  			}
289  			break;
290  		case 'n':
291  			context->windows.num = strtol(argv[optind], &endptr,
292  						      10);
293  			if (optarg == endptr || *endptr != '\0') {
294  				fprintf(stderr, "Unparseable window num\n");
295  				return false;
296  			}
297  			break;
298  		case 'w':
299  			context->windows.default_size = strtol(argv[optind],
300  							       &endptr, 10);
301  			context->windows.default_size <<= 20; /* Given in MB */
302  			if (optarg == endptr || (*endptr != '\0' &&
303  						 *endptr != 'M')) {
304  				fprintf(stderr, "Unparseable window size\n");
305  				return false;
306  			}
307  			if (!is_power_of_2(context->windows.default_size)) {
308  				fprintf(stderr, "Window size not power of 2\n");
309  				return false;
310  			}
311  			break;
312  		case 'v':
313  			verbosity++;
314  			break;
315  		case 's':
316  			/* Avoid a double openlog() */
317  			if (mbox_vlog != &vsyslog) {
318  				openlog(PREFIX, LOG_ODELAY, LOG_DAEMON);
319  				mbox_vlog = &vsyslog;
320  			}
321  			break;
322  		case 'V':
323  			printf("%s V%s\n", THIS_NAME, PACKAGE_VERSION);
324  			exit(0);
325  		case 'h':
326  			return false; /* This will print the usage message */
327  		default:
328  			return false;
329  		}
330  	}
331  
332  	if (!context->flash_size) {
333  		fprintf(stderr, "Must specify a non-zero flash size\n");
334  		return false;
335  	}
336  
337  	MSG_INFO("Flash size: 0x%.8x\n", context->flash_size);
338  
339  	if (verbosity) {
340  		MSG_INFO("%s logging\n", verbosity == MBOX_LOG_DEBUG ? "Debug" :
341  					"Verbose");
342  	}
343  
344  	return true;
345  }
346  
347  int main(int argc, char **argv)
348  {
349  	struct mbox_context *context;
350  	char *name = argv[0];
351  	sigset_t set;
352  	int rc, i;
353  
354  	context = calloc(1, sizeof(*context));
355  	if (!context) {
356  		fprintf(stderr, "Memory allocation failed\n");
357  		exit(1);
358  	}
359  
360  	if (!parse_cmdline(argc, argv, context)) {
361  		usage(name);
362  		free(context);
363  		exit(0);
364  	}
365  
366  	for (i = 0; i < TOTAL_FDS; i++) {
367  		context->fds[i].fd = -1;
368  	}
369  
370  	MSG_INFO("Starting Daemon\n");
371  
372  	rc = init_signals(context, &set);
373  	if (rc) {
374  		goto finish;
375  	}
376  
377  	rc = protocol_init(context);
378  	if (rc) {
379  		goto finish;
380  	}
381  
382  	rc = transport_mbox_init(context);
383  	if (rc) {
384  		goto finish;
385  	}
386  
387  	rc = lpc_dev_init(context);
388  	if (rc) {
389  		goto finish;
390  	}
391  
392  	/* We've found the reserved memory region -> we can assign to windows */
393  	rc = windows_init(context);
394  	if (rc) {
395  		goto finish;
396  	}
397  
398  	rc = flash_dev_init(context);
399  	if (rc) {
400  		goto finish;
401  	}
402  
403  	rc = dbus_init(context);
404  	if (rc) {
405  		goto finish;
406  	}
407  
408  #ifdef VIRTUAL_PNOR_ENABLED
409  	init_vpnor(context);
410  #endif
411  
412  	/* Set the LPC bus mapping */
413  	rc = lpc_reset(context);
414  	if (rc) {
415  		MSG_ERR("LPC configuration failed, RESET required: %d\n", rc);
416  	}
417  
418  	rc = protocol_events_set(context, BMC_EVENT_DAEMON_READY);
419  	if (rc) {
420  		goto finish;
421  	}
422  
423  	MSG_INFO("Entering Polling Loop\n");
424  	rc = poll_loop(context);
425  
426  	MSG_INFO("Exiting Poll Loop: %d\n", rc);
427  
428  finish:
429  	MSG_INFO("Daemon Exiting...\n");
430  	protocol_events_clear(context, BMC_EVENT_DAEMON_READY);
431  
432  #ifdef VIRTUAL_PNOR_ENABLED
433  	destroy_vpnor(context);
434  #endif
435  	dbus_free(context);
436  	flash_dev_free(context);
437  	lpc_dev_free(context);
438  	transport_mbox_free(context);
439  	windows_free(context);
440  	protocol_free(context);
441  	free(context);
442  
443  	return rc;
444  }
445