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