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