xref: /openbmc/hiomapd/mboxd.c (revision 2ebfd20f)
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