xref: /openbmc/hiomapd/mboxd.c (revision 23a48212)
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 				windows_reset_all(context, EVENT_TRIGGER);
151 				rc = lpc_reset(context);
152 				if (rc < 0) {
153 					MSG_ERR("WARNING: Failed to point the "
154 						"LPC bus back to flash on "
155 						"SIGHUP\nIf the host requires "
156 						"this expect problems...\n");
157 				}
158 				break;
159 			default:
160 				MSG_ERR("Unhandled Signal: %d\n",
161 					info.ssi_signo);
162 				break;
163 			}
164 		}
165 		if (context->fds[DBUS_FD].revents & POLLIN) { /* DBUS */
166 			while ((rc = sd_bus_process(context->bus, NULL)) > 0) {
167 				MSG_DBG("DBUS Event\n");
168 			}
169 			if (rc < 0) {
170 				MSG_ERR("Error handling DBUS event: %s\n",
171 						strerror(-rc));
172 			}
173 		}
174 		if (context->terminate) {
175 			break; /* This should mean we clean up nicely */
176 		}
177 		if (context->fds[MBOX_FD].revents & POLLIN) { /* MBOX */
178 			MSG_DBG("MBOX Event\n");
179 			rc = transport_mbox_dispatch(context);
180 			if (rc < 0) {
181 				MSG_ERR("Error handling MBOX event\n");
182 			}
183 		}
184 	}
185 
186 	/* Best to reset windows and the lpc mapping for safety */
187 	/* Host didn't request reset -> Notify it */
188 	windows_reset_all(context, EVENT_TRIGGER);
189 	rc = lpc_reset(context);
190 	/* Not much we can do if this fails */
191 	if (rc < 0) {
192 		MSG_ERR("WARNING: Failed to point the LPC bus back to flash\n"
193 			"If the host requires this expect problems...\n");
194 	}
195 
196 	return rc;
197 }
198 
199 static int init_signals(struct mbox_context *context, sigset_t *set)
200 {
201 	int rc;
202 
203 	/* Block SIGHUPs, SIGTERMs and SIGINTs */
204 	sigemptyset(set);
205 	sigaddset(set, SIGHUP);
206 	sigaddset(set, SIGINT);
207 	sigaddset(set, SIGTERM);
208 	rc = sigprocmask(SIG_BLOCK, set, NULL);
209 	if (rc < 0) {
210 		MSG_ERR("Failed to set SIG_BLOCK mask %s\n", strerror(errno));
211 		return rc;
212 	}
213 
214 	/* Get Signal File Descriptor */
215 	rc = signalfd(-1, set, SFD_NONBLOCK);
216 	if (rc < 0) {
217 		MSG_ERR("Failed to get signalfd %s\n", strerror(errno));
218 		return rc;
219 	}
220 
221 	context->fds[SIG_FD].fd = rc;
222 	return 0;
223 }
224 
225 static void usage(const char *name)
226 {
227 	printf(USAGE, name);
228 }
229 
230 static bool parse_cmdline(int argc, char **argv,
231 			  struct mbox_context *context)
232 {
233 	char *endptr;
234 	int opt;
235 
236 	static const struct option long_options[] = {
237 		{ "flash",		required_argument,	0, 'f' },
238 		{ "window-size",	optional_argument,	0, 'w' },
239 		{ "window-num",		optional_argument,	0, 'n' },
240 		{ "verbose",		no_argument,		0, 'v' },
241 		{ "syslog",		no_argument,		0, 's' },
242 		{ "version",		no_argument,		0, 'V' },
243 		{ "help",		no_argument,		0, 'h' },
244 		{ 0,			0,			0, 0   }
245 	};
246 
247 	verbosity = MBOX_LOG_NONE;
248 	mbox_vlog = &mbox_log_console;
249 
250 	context->current = NULL; /* No current window */
251 
252 	while ((opt = getopt_long(argc, argv, "f:w::n::vsVh", long_options, NULL))
253 			!= -1) {
254 		switch (opt) {
255 		case 0:
256 			break;
257 		case 'f':
258 			context->flash_size = strtol(optarg, &endptr, 10);
259 			if (optarg == endptr) {
260 				fprintf(stderr, "Unparseable flash size\n");
261 				return false;
262 			}
263 			switch (*endptr) {
264 			case '\0':
265 				break;
266 			case 'M':
267 				context->flash_size <<= 10;
268 			case 'K':
269 				context->flash_size <<= 10;
270 				break;
271 			default:
272 				fprintf(stderr, "Unknown units '%c'\n",
273 					*endptr);
274 				return false;
275 			}
276 			break;
277 		case 'n':
278 			context->windows.num = strtol(argv[optind], &endptr,
279 						      10);
280 			if (optarg == endptr || *endptr != '\0') {
281 				fprintf(stderr, "Unparseable window num\n");
282 				return false;
283 			}
284 			break;
285 		case 'w':
286 			context->windows.default_size = strtol(argv[optind],
287 							       &endptr, 10);
288 			context->windows.default_size <<= 20; /* Given in MB */
289 			if (optarg == endptr || (*endptr != '\0' &&
290 						 *endptr != 'M')) {
291 				fprintf(stderr, "Unparseable window size\n");
292 				return false;
293 			}
294 			if (!is_power_of_2(context->windows.default_size)) {
295 				fprintf(stderr, "Window size not power of 2\n");
296 				return false;
297 			}
298 			break;
299 		case 'v':
300 			verbosity++;
301 			break;
302 		case 's':
303 			/* Avoid a double openlog() */
304 			if (mbox_vlog != &vsyslog) {
305 				openlog(PREFIX, LOG_ODELAY, LOG_DAEMON);
306 				mbox_vlog = &vsyslog;
307 			}
308 			break;
309 		case 'V':
310 			printf("%s V%s\n", THIS_NAME, PACKAGE_VERSION);
311 			exit(0);
312 		case 'h':
313 			return false; /* This will print the usage message */
314 		default:
315 			return false;
316 		}
317 	}
318 
319 	if (!context->flash_size) {
320 		fprintf(stderr, "Must specify a non-zero flash size\n");
321 		return false;
322 	}
323 
324 	MSG_INFO("Flash size: 0x%.8x\n", context->flash_size);
325 
326 	if (verbosity) {
327 		MSG_INFO("%s logging\n", verbosity == MBOX_LOG_DEBUG ? "Debug" :
328 					"Verbose");
329 	}
330 
331 	return true;
332 }
333 
334 int main(int argc, char **argv)
335 {
336 	struct mbox_context *context;
337 	char *name = argv[0];
338 	sigset_t set;
339 	int rc, i;
340 
341 	context = calloc(1, sizeof(*context));
342 	if (!context) {
343 		fprintf(stderr, "Memory allocation failed\n");
344 		exit(1);
345 	}
346 
347 	if (!parse_cmdline(argc, argv, context)) {
348 		usage(name);
349 		free(context);
350 		exit(0);
351 	}
352 
353 	for (i = 0; i < TOTAL_FDS; i++) {
354 		context->fds[i].fd = -1;
355 	}
356 
357 	MSG_INFO("Starting Daemon\n");
358 
359 	rc = init_signals(context, &set);
360 	if (rc) {
361 		goto finish;
362 	}
363 
364 	rc = protocol_init(context);
365 	if (rc) {
366 		goto finish;
367 	}
368 
369 	rc = transport_mbox_init(context);
370 	if (rc) {
371 		goto finish;
372 	}
373 
374 	rc = lpc_dev_init(context);
375 	if (rc) {
376 		goto finish;
377 	}
378 
379 	/* We've found the reserved memory region -> we can assign to windows */
380 	rc = windows_init(context);
381 	if (rc) {
382 		goto finish;
383 	}
384 
385 	rc = flash_dev_init(context);
386 	if (rc) {
387 		goto finish;
388 	}
389 
390 	rc = dbus_init(context);
391 	if (rc) {
392 		goto finish;
393 	}
394 
395 #ifdef VIRTUAL_PNOR_ENABLED
396 	init_vpnor(context);
397 #endif
398 
399 	/* Set the LPC bus mapping */
400 	rc = lpc_reset(context);
401 	if (rc) {
402 		MSG_ERR("LPC configuration failed, RESET required: %d\n", rc);
403 	}
404 
405 	rc = protocol_events_set(context, BMC_EVENT_DAEMON_READY, EVENT_TRIGGER);
406 	if (rc) {
407 		goto finish;
408 	}
409 
410 	MSG_INFO("Entering Polling Loop\n");
411 	rc = poll_loop(context);
412 
413 	MSG_INFO("Exiting Poll Loop: %d\n", rc);
414 
415 finish:
416 	MSG_INFO("Daemon Exiting...\n");
417 	protocol_events_clear(context, BMC_EVENT_DAEMON_READY, EVENT_TRIGGER);
418 
419 #ifdef VIRTUAL_PNOR_ENABLED
420 	destroy_vpnor(context);
421 #endif
422 	dbus_free(context);
423 	flash_dev_free(context);
424 	lpc_dev_free(context);
425 	transport_mbox_free(context);
426 	windows_free(context);
427 	protocol_free(context);
428 	free(context);
429 
430 	return rc;
431 }
432