xref: /openbmc/hiomapd/mboxd.c (revision 8cef63e3)
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 "backend.h"
35 #include "lpc.h"
36 #include "transport_mbox.h"
37 #include "transport_dbus.h"
38 #include "windows.h"
39 #include "vpnor/backend.h"
40 
41 const char* 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"
46 #ifdef VIRTUAL_PNOR_ENABLED
47 	"\t\t-b | --backend <vpnor|mtd[:PATH]|file:PATH>\n"
48 #else
49 	"\t\t-b | --backend <mtd[:PATH]|file:PATH>\n"
50 #endif
51 	"\t-v | --verbose\t\tBe [more] verbose\n"
52 	"\t-s | --syslog\t\tLog output to syslog (pointless without -v)\n"
53 	"\t-n | --window-num\tThe number of windows\n"
54 	"\t\t\t\t(default: fill the reserved memory region)\n"
55 	"\t-w | --window-size\tThe window size (power of 2) in MB\n"
56 	"\t\t\t\t(default: 1MB)\n"
57 	"\t-f | --flash\t\tSize of flash in [K|M] bytes\n\n"
58 	"\t-t | --trace\t\tFile to write trace data to (in blktrace format)\n\n";
59 
60 static int dbus_init(struct mbox_context *context,
61 		     const struct transport_ops **ops)
62 {
63 	int rc;
64 
65 	rc = sd_bus_default_system(&context->bus);
66 	if (rc < 0) {
67 		MSG_ERR("Failed to connect to the system bus: %s\n",
68 			strerror(-rc));
69 		return rc;
70 	}
71 
72 	rc = control_legacy_init(context);
73 	if (rc < 0) {
74 		MSG_ERR("Failed to initialise legacy DBus interface: %s\n",
75 			strerror(-rc));
76 		return rc;
77 	}
78 
79 	rc = control_dbus_init(context);
80 	if (rc < 0) {
81 		MSG_ERR("Failed to initialise DBus control interface: %s\n",
82 			strerror(-rc));
83 		return rc;
84 	}
85 
86 	rc = transport_dbus_init(context, ops);
87 	if (rc < 0) {
88 		MSG_ERR("Failed to initialise DBus protocol interface: %s\n",
89 			strerror(-rc));
90 		return rc;
91 	}
92 
93 	rc = sd_bus_request_name(context->bus, MBOX_DBUS_NAME,
94 				 SD_BUS_NAME_ALLOW_REPLACEMENT |
95 				 SD_BUS_NAME_REPLACE_EXISTING);
96 	if (rc < 0) {
97 		MSG_ERR("Failed to request DBus name: %s\n", strerror(-rc));
98 		return rc;
99 	}
100 
101 	rc = sd_bus_get_fd(context->bus);
102 	if (rc < 0) {
103 		MSG_ERR("Failed to get bus fd: %s\n", strerror(-rc));
104 		return rc;
105 	}
106 
107 	context->fds[DBUS_FD].fd = rc;
108 
109 	return 0;
110 }
111 
112 static void dbus_free(struct mbox_context *context)
113 {
114 	transport_dbus_free(context);
115 	control_dbus_free(context);
116 	control_legacy_free(context);
117 	sd_bus_unref(context->bus);
118 }
119 
120 static int poll_loop(struct mbox_context *context)
121 {
122 	int rc = 0, i;
123 
124 	/* Set POLLIN on polling file descriptors */
125 	for (i = 0; i < POLL_FDS; i++) {
126 		context->fds[i].events = POLLIN;
127 	}
128 
129 	while (1) {
130 		rc = poll(context->fds, POLL_FDS, -1);
131 
132 		if (rc < 0) { /* Error */
133 			MSG_ERR("Error from poll(): %s\n", strerror(errno));
134 			break; /* This should mean we clean up nicely */
135 		}
136 
137 		/* Event on Polled File Descriptor - Handle It */
138 		if (context->fds[SIG_FD].revents & POLLIN) { /* Signal */
139 			struct signalfd_siginfo info = { 0 };
140 
141 			rc = read(context->fds[SIG_FD].fd, (void *) &info,
142 				  sizeof(info));
143 			if (rc != sizeof(info)) {
144 				MSG_ERR("Error reading signal event: %s\n",
145 					strerror(errno));
146 			}
147 
148 			MSG_DBG("Received signal: %d\n", info.ssi_signo);
149 			switch (info.ssi_signo) {
150 			case SIGINT:
151 			case SIGTERM:
152 				MSG_INFO("Caught Signal - Exiting...\n");
153 				context->terminate = true;
154 				break;
155 			case SIGHUP:
156 				rc = protocol_reset(context);
157 				if (rc < 0) {
158 					MSG_ERR("Failed to reset on SIGHUP\n");
159 				}
160 				break;
161 			default:
162 				MSG_ERR("Unhandled Signal: %d\n",
163 					info.ssi_signo);
164 				break;
165 			}
166 		}
167 		if (context->fds[DBUS_FD].revents & POLLIN) { /* DBUS */
168 			while ((rc = sd_bus_process(context->bus, NULL)) > 0) {
169 				MSG_DBG("DBUS Event\n");
170 			}
171 			if (rc < 0) {
172 				MSG_ERR("Error handling DBUS event: %s\n",
173 						strerror(-rc));
174 			}
175 		}
176 		if (context->terminate) {
177 			break; /* This should mean we clean up nicely */
178 		}
179 		if (context->fds[MBOX_FD].revents & POLLIN) { /* MBOX */
180 			MSG_DBG("MBOX Event\n");
181 			rc = transport_mbox_dispatch(context);
182 			if (rc < 0) {
183 				MSG_ERR("Error handling MBOX event\n");
184 			}
185 		}
186 	}
187 
188 	rc = protocol_reset(context);
189 	if (rc < 0) {
190 		MSG_ERR("Failed to reset during poll loop cleanup\n");
191 	}
192 
193 	return rc;
194 }
195 
196 static int init_signals(struct mbox_context *context, sigset_t *set)
197 {
198 	int rc;
199 
200 	/* Block SIGHUPs, SIGTERMs and SIGINTs */
201 	sigemptyset(set);
202 	sigaddset(set, SIGHUP);
203 	sigaddset(set, SIGINT);
204 	sigaddset(set, SIGTERM);
205 	rc = sigprocmask(SIG_BLOCK, set, NULL);
206 	if (rc < 0) {
207 		MSG_ERR("Failed to set SIG_BLOCK mask %s\n", strerror(errno));
208 		return rc;
209 	}
210 
211 	/* Get Signal File Descriptor */
212 	rc = signalfd(-1, set, SFD_NONBLOCK);
213 	if (rc < 0) {
214 		MSG_ERR("Failed to get signalfd %s\n", strerror(errno));
215 		return rc;
216 	}
217 
218 	context->fds[SIG_FD].fd = rc;
219 	return 0;
220 }
221 
222 static void usage(const char *name)
223 {
224 	printf(USAGE, name);
225 }
226 
227 static bool parse_cmdline(int argc, char **argv,
228 			  struct mbox_context *context)
229 {
230 	char *endptr;
231 	int opt;
232 
233 	static const struct option long_options[] = {
234 		{ "flash",		required_argument,	0, 'f' },
235 		{ "backend",		required_argument,	0, 'b' },
236 		{ "window-size",	optional_argument,	0, 'w' },
237 		{ "window-num",		optional_argument,	0, 'n' },
238 		{ "verbose",		no_argument,		0, 'v' },
239 		{ "syslog",		no_argument,		0, 's' },
240 		{ "trace",		optional_argument,	0, 't' },
241 		{ "version",		no_argument,		0, 'V' },
242 		{ "help",		no_argument,		0, 'h' },
243 		{ 0,			0,			0, 0   }
244 	};
245 
246 	verbosity = MBOX_LOG_NONE;
247 	mbox_vlog = &mbox_log_console;
248 
249 	context->current = NULL; /* No current window */
250 
251 	while ((opt = getopt_long(argc, argv, "f:b:w::n::vst::Vh", long_options, NULL))
252 			!= -1) {
253 		switch (opt) {
254 		case 0:
255 			break;
256 		case 'f':
257 			context->backend.flash_size = strtol(optarg, &endptr, 10);
258 			if (optarg == endptr) {
259 				fprintf(stderr, "Unparseable flash size\n");
260 				return false;
261 			}
262 			switch (*endptr) {
263 			case '\0':
264 				break;
265 			case 'M':
266 				context->backend.flash_size <<= 10;
267 			case 'K':
268 				context->backend.flash_size <<= 10;
269 				break;
270 			default:
271 				fprintf(stderr, "Unknown units '%c'\n",
272 					*endptr);
273 				return false;
274 			}
275 			break;
276 		case 'b':
277 			context->source = optarg;
278 			break;
279 		case 'n':
280 			context->windows.num = strtol(argv[optind], &endptr,
281 						      10);
282 			if (optarg == endptr || *endptr != '\0') {
283 				fprintf(stderr, "Unparseable window num\n");
284 				return false;
285 			}
286 			break;
287 		case 'w':
288 			context->windows.default_size = strtol(argv[optind],
289 							       &endptr, 10);
290 			context->windows.default_size <<= 20; /* Given in MB */
291 			if (optarg == endptr || (*endptr != '\0' &&
292 						 *endptr != 'M')) {
293 				fprintf(stderr, "Unparseable window size\n");
294 				return false;
295 			}
296 			if (!is_power_of_2(context->windows.default_size)) {
297 				fprintf(stderr, "Window size not power of 2\n");
298 				return false;
299 			}
300 			break;
301 		case 'v':
302 			verbosity++;
303 			break;
304 		case 's':
305 			/* Avoid a double openlog() */
306 			if (mbox_vlog != &vsyslog) {
307 				openlog(PREFIX, LOG_ODELAY, LOG_DAEMON);
308 				mbox_vlog = &vsyslog;
309 			}
310 			break;
311 		case 'V':
312 			printf("%s V%s\n", THIS_NAME, PACKAGE_VERSION);
313 			exit(0);
314 		case 't':
315 			context->blktracefd = open(argv[optind],
316 						   O_CREAT|O_TRUNC|O_WRONLY,
317 						   0666);
318 			printf("Recording blktrace output to %s\n",
319 			       argv[optind]);
320 			if (context->blktracefd == -1) {
321 				perror("Couldn't open blktrace file for writing");
322 				exit(2);
323 			}
324 			break;
325 		case 'h':
326 			return false; /* This will print the usage message */
327 		default:
328 			return false;
329 		}
330 	}
331 
332 	if (!context->backend.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->backend.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 static int mboxd_backend_init(struct mbox_context *context)
348 {
349 	const char *delim;
350 	const char *path;
351 	int rc;
352 
353 	if (!context->source) {
354 		struct vpnor_partition_paths paths;
355 		vpnor_default_paths(&paths);
356 
357 		rc = backend_probe_vpnor(&context->backend, &paths);
358 		if(rc < 0)
359 			rc = backend_probe_mtd(&context->backend, NULL);
360 
361 		return rc;
362 	}
363 
364 	delim = strchr(context->source, ':');
365 	path = delim ? delim + 1 : NULL;
366 
367 	if (!strncmp(context->source, "vpnor", strlen("vpnor"))) {
368 		struct vpnor_partition_paths paths;
369 
370 		if (path) {
371 			rc = -EINVAL;
372 		} else {
373 			vpnor_default_paths(&paths);
374 			rc = backend_probe_vpnor(&context->backend, &paths);
375 		}
376 	} else if (!strncmp(context->source, "mtd", strlen("mtd"))) {
377 		rc = backend_probe_mtd(&context->backend, path);
378 	} else if (!strncmp(context->source, "file", strlen("file"))) {
379 		rc = backend_probe_file(&context->backend, path);
380 	} else {
381 		rc = -EINVAL;
382 	}
383 
384 	if (rc < 0)
385 		MSG_ERR("Invalid backend argument: %s\n", context->source);
386 
387 	return rc;
388 }
389 
390 int main(int argc, char **argv)
391 {
392 	const struct transport_ops *mbox_ops, *dbus_ops;
393 	struct mbox_context *context;
394 	bool have_transport_mbox;
395 	char *name = argv[0];
396 	sigset_t set;
397 	int rc, i;
398 
399 	context = calloc(1, sizeof(*context));
400 	if (!context) {
401 		fprintf(stderr, "Memory allocation failed\n");
402 		exit(1);
403 	}
404 
405 	if (!parse_cmdline(argc, argv, context)) {
406 		usage(name);
407 		free(context);
408 		exit(0);
409 	}
410 
411 	for (i = 0; i < TOTAL_FDS; i++) {
412 		context->fds[i].fd = -1;
413 	}
414 
415 	MSG_INFO("Starting Daemon\n");
416 
417 	rc = init_signals(context, &set);
418 	if (rc) {
419 		goto cleanup_context;
420 	}
421 
422 	rc = mboxd_backend_init(context);
423 	if (rc) {
424 		goto cleanup_context;
425 	}
426 
427 	rc = protocol_init(context);
428 	if (rc) {
429 		goto cleanup_backend;
430 	}
431 
432 	rc = transport_mbox_init(context, &mbox_ops);
433 	/* TODO: Think about whether we could use a less branch-y strategy */
434 	have_transport_mbox = rc == 0;
435 	if (!have_transport_mbox) {
436 		/* Disable MBOX for poll()ing purposes */
437 		context->fds[MBOX_FD].fd = -1;
438 		MSG_DBG("Failed to initialise MBOX transport: %d\n", rc);
439 		MSG_INFO("MBOX transport unavailable\n");
440 	}
441 
442 	rc = lpc_dev_init(context);
443 	if (rc) {
444 		goto cleanup_mbox;
445 	}
446 
447 	/* We've found the reserved memory region -> we can assign to windows */
448 	rc = windows_init(context);
449 	if (rc) {
450 		goto cleanup_lpc;
451 	}
452 
453 	rc = dbus_init(context, &dbus_ops);
454 	if (rc) {
455 		goto cleanup_windows;
456 	}
457 
458 	/* Set the LPC bus mapping */
459 	__protocol_reset(context);
460 
461 	/* We're ready to go, alert the host */
462 	context->bmc_events |= BMC_EVENT_DAEMON_READY;
463 	context->bmc_events |= BMC_EVENT_PROTOCOL_RESET;
464 
465 	/* Alert on all supported transports, as required */
466 	if (have_transport_mbox) {
467 		rc = protocol_events_put(context, mbox_ops);
468 		if (rc) {
469 			goto cleanup;
470 		}
471 	}
472 
473 	rc = protocol_events_put(context, dbus_ops);
474 	if (rc) {
475 		goto cleanup;
476 	}
477 
478 	MSG_INFO("Entering Polling Loop\n");
479 	rc = poll_loop(context);
480 
481 	MSG_INFO("Exiting Poll Loop: %d\n", rc);
482 
483 	MSG_INFO("Daemon Exiting...\n");
484 	context->bmc_events &= ~BMC_EVENT_DAEMON_READY;
485 	context->bmc_events |= BMC_EVENT_PROTOCOL_RESET;
486 
487 	/* Alert on all supported transports, as required */
488 	if (have_transport_mbox) {
489 		protocol_events_put(context, mbox_ops);
490 	}
491 
492 	protocol_events_put(context, dbus_ops);
493 
494 cleanup:
495 	dbus_free(context);
496 cleanup_windows:
497 	windows_free(context);
498 cleanup_lpc:
499 	lpc_dev_free(context);
500 cleanup_mbox:
501 	if (have_transport_mbox) {
502 		transport_mbox_free(context);
503 	}
504 	protocol_free(context);
505 cleanup_backend:
506 	backend_free(&context->backend);
507 cleanup_context:
508 	if (context->blktracefd)
509 		close(context->blktracefd);
510 
511 	free(context);
512 
513 	return rc;
514 }
515