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