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