xref: /openbmc/phosphor-mboxd/mboxd.c (revision a0370754)
1 /*
2  * Mailbox Daemon Implementation
3  *
4  * Copyright 2016 IBM
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  */
19 
20 #define _GNU_SOURCE
21 #include <assert.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <getopt.h>
25 #include <limits.h>
26 #include <poll.h>
27 #include <stdbool.h>
28 #include <stdint.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <syslog.h>
33 #include <signal.h>
34 #include <sys/ioctl.h>
35 #include <sys/mman.h>
36 #include <sys/stat.h>
37 #include <sys/timerfd.h>
38 #include <sys/types.h>
39 #include <sys/signalfd.h>
40 #include <time.h>
41 #include <unistd.h>
42 #include <inttypes.h>
43 #include <systemd/sd-bus.h>
44 
45 #include "config.h"
46 #include "mbox.h"
47 #include "common.h"
48 #include "dbus.h"
49 #include "mboxd_dbus.h"
50 #include "mboxd_flash.h"
51 #include "mboxd_lpc.h"
52 #include "mboxd_msg.h"
53 #include "mboxd_windows.h"
54 
55 #define USAGE \
56 "\nUsage: %s [-V | --version] [-h | --help] [-v[v] | --verbose] [-s | --syslog]\n" \
57 "\t\t[-n | --window-num <num>]\n" \
58 "\t\t[-w | --window-size <size>M]\n" \
59 "\t\t-f | --flash <size>[K|M]\n\n" \
60 "\t-v | --verbose\t\tBe [more] verbose\n" \
61 "\t-s | --syslog\t\tLog output to syslog (pointless without -v)\n" \
62 "\t-n | --window-num\tThe number of windows\n" \
63 "\t\t\t\t(default: fill the reserved memory region)\n" \
64 "\t-w | --window-size\tThe window size (power of 2) in MB\n" \
65 "\t\t\t\t(default: 1MB)\n" \
66 "\t-f | --flash\t\tSize of flash in [K|M] bytes\n\n"
67 
68 static int poll_loop(struct mbox_context *context)
69 {
70 	int rc = 0, i;
71 
72 	/* Set POLLIN on polling file descriptors */
73 	for (i = 0; i < POLL_FDS; i++) {
74 		context->fds[i].events = POLLIN;
75 	}
76 
77 	while (1) {
78 		rc = poll(context->fds, POLL_FDS, -1);
79 
80 		if (rc < 0) { /* Error */
81 			MSG_ERR("Error from poll(): %s\n", strerror(errno));
82 			break; /* This should mean we clean up nicely */
83 		}
84 
85 		/* Event on Polled File Descriptor - Handle It */
86 		if (context->fds[SIG_FD].revents & POLLIN) { /* Signal */
87 			struct signalfd_siginfo info = { 0 };
88 
89 			rc = read(context->fds[SIG_FD].fd, (void *) &info,
90 				  sizeof(info));
91 			if (rc != sizeof(info)) {
92 				MSG_ERR("Error reading signal event: %s\n",
93 					strerror(errno));
94 			}
95 
96 			MSG_DBG("Received signal: %d\n", info.ssi_signo);
97 			switch (info.ssi_signo) {
98 			case SIGINT:
99 			case SIGTERM:
100 				MSG_INFO("Caught Signal - Exiting...\n");
101 				context->terminate = true;
102 				break;
103 			case SIGHUP:
104 				/* Host didn't request reset -> Notify it */
105 				reset_all_windows(context, SET_BMC_EVENT);
106 				rc = point_to_flash(context);
107 				if (rc < 0) {
108 					MSG_ERR("WARNING: Failed to point the "
109 						"LPC bus back to flash on "
110 						"SIGHUP\nIf the host requires "
111 						"this expect problems...\n");
112 				}
113 				break;
114 			default:
115 				MSG_ERR("Unhandled Signal: %d\n",
116 					info.ssi_signo);
117 				break;
118 			}
119 		}
120 		if (context->fds[DBUS_FD].revents & POLLIN) { /* DBUS */
121 			while ((rc = sd_bus_process(context->bus, NULL)) > 0) {
122 				MSG_DBG("DBUS Event\n");
123 			}
124 			if (rc < 0) {
125 				MSG_ERR("Error handling DBUS event: %s\n",
126 						strerror(-rc));
127 			}
128 		}
129 		if (context->terminate) {
130 			break; /* This should mean we clean up nicely */
131 		}
132 		if (context->fds[MBOX_FD].revents & POLLIN) { /* MBOX */
133 			MSG_DBG("MBOX Event\n");
134 			rc = dispatch_mbox(context);
135 			if (rc < 0) {
136 				MSG_ERR("Error handling MBOX event\n");
137 			}
138 		}
139 	}
140 
141 	/* Best to reset windows and point back to flash for safety */
142 	/* Host didn't request reset -> Notify it */
143 	reset_all_windows(context, SET_BMC_EVENT);
144 	rc = point_to_flash(context);
145 	/* Not much we can do if this fails */
146 	if (rc < 0) {
147 		MSG_ERR("WARNING: Failed to point the LPC bus back to flash\n"
148 			"If the host requires this expect problems...\n");
149 	}
150 
151 	return rc;
152 }
153 
154 static int init_signals(struct mbox_context *context, sigset_t *set)
155 {
156 	int rc;
157 
158 	/* Block SIGHUPs, SIGTERMs and SIGINTs */
159 	sigemptyset(set);
160 	sigaddset(set, SIGHUP);
161 	sigaddset(set, SIGINT);
162 	sigaddset(set, SIGTERM);
163 	rc = sigprocmask(SIG_BLOCK, set, NULL);
164 	if (rc < 0) {
165 		MSG_ERR("Failed to set SIG_BLOCK mask %s\n", strerror(errno));
166 		return rc;
167 	}
168 
169 	/* Get Signal File Descriptor */
170 	rc = signalfd(-1, set, SFD_NONBLOCK);
171 	if (rc < 0) {
172 		MSG_ERR("Failed to get signalfd %s\n", strerror(errno));
173 		return rc;
174 	}
175 
176 	context->fds[SIG_FD].fd = rc;
177 	return 0;
178 }
179 
180 static void usage(const char *name)
181 {
182 	printf(USAGE, name);
183 }
184 
185 static bool parse_cmdline(int argc, char **argv,
186 			  struct mbox_context *context)
187 {
188 	char *endptr;
189 	int opt;
190 
191 	static const struct option long_options[] = {
192 		{ "flash",		required_argument,	0, 'f' },
193 		{ "window-size",	optional_argument,	0, 'w' },
194 		{ "window-num",		optional_argument,	0, 'n' },
195 		{ "verbose",		no_argument,		0, 'v' },
196 		{ "syslog",		no_argument,		0, 's' },
197 		{ "version",		no_argument,		0, 'V' },
198 		{ "help",		no_argument,		0, 'h' },
199 		{ 0,			0,			0, 0   }
200 	};
201 
202 	verbosity = MBOX_LOG_NONE;
203 	mbox_vlog = &mbox_log_console;
204 
205 	context->current = NULL; /* No current window */
206 
207 	while ((opt = getopt_long(argc, argv, "f:w::n::vsVh", long_options, NULL))
208 			!= -1) {
209 		switch (opt) {
210 		case 0:
211 			break;
212 		case 'f':
213 			context->flash_size = strtol(optarg, &endptr, 10);
214 			if (optarg == endptr) {
215 				fprintf(stderr, "Unparseable flash size\n");
216 				return false;
217 			}
218 			switch (*endptr) {
219 			case '\0':
220 				break;
221 			case 'M':
222 				context->flash_size <<= 10;
223 			case 'K':
224 				context->flash_size <<= 10;
225 				break;
226 			default:
227 				fprintf(stderr, "Unknown units '%c'\n",
228 					*endptr);
229 				return false;
230 			}
231 			break;
232 		case 'n':
233 			context->windows.num = strtol(argv[optind], &endptr,
234 						      10);
235 			if (optarg == endptr || *endptr != '\0') {
236 				fprintf(stderr, "Unparseable window num\n");
237 				return false;
238 			}
239 			break;
240 		case 'w':
241 			context->windows.default_size = strtol(argv[optind],
242 							       &endptr, 10);
243 			context->windows.default_size <<= 20; /* Given in MB */
244 			if (optarg == endptr || (*endptr != '\0' &&
245 						 *endptr != 'M')) {
246 				fprintf(stderr, "Unparseable window size\n");
247 				return false;
248 			}
249 			if (!is_power_of_2(context->windows.default_size)) {
250 				fprintf(stderr, "Window size not power of 2\n");
251 				return false;
252 			}
253 			break;
254 		case 'v':
255 			verbosity++;
256 			break;
257 		case 's':
258 			/* Avoid a double openlog() */
259 			if (mbox_vlog != &vsyslog) {
260 				openlog(PREFIX, LOG_ODELAY, LOG_DAEMON);
261 				mbox_vlog = &vsyslog;
262 			}
263 			break;
264 		case 'V':
265 			printf("%s V%s\n", THIS_NAME, PACKAGE_VERSION);
266 			exit(0);
267 		case 'h':
268 			return false; /* This will print the usage message */
269 		default:
270 			return false;
271 		}
272 	}
273 
274 	if (!context->flash_size) {
275 		fprintf(stderr, "Must specify a non-zero flash size\n");
276 		return false;
277 	}
278 
279 	MSG_INFO("Flash size: 0x%.8x\n", context->flash_size);
280 
281 	if (verbosity) {
282 		MSG_INFO("%s logging\n", verbosity == MBOX_LOG_DEBUG ? "Debug" :
283 					"Verbose");
284 	}
285 
286 	return true;
287 }
288 
289 int main(int argc, char **argv)
290 {
291 	struct mbox_context *context;
292 	char *name = argv[0];
293 	sigset_t set;
294 	int rc, i;
295 
296 	context = calloc(1, sizeof(*context));
297 	if (!context) {
298 		fprintf(stderr, "Memory allocation failed\n");
299 		exit(1);
300 	}
301 
302 	if (!parse_cmdline(argc, argv, context)) {
303 		usage(name);
304 		free(context);
305 		exit(0);
306 	}
307 
308 	for (i = 0; i < TOTAL_FDS; i++) {
309 		context->fds[i].fd = -1;
310 	}
311 
312 	MSG_INFO("Starting Daemon\n");
313 
314 	rc = init_signals(context, &set);
315 	if (rc) {
316 		goto finish;
317 	}
318 
319 	rc = init_mbox_dev(context);
320 	if (rc) {
321 		goto finish;
322 	}
323 
324 	rc = init_lpc_dev(context);
325 	if (rc) {
326 		goto finish;
327 	}
328 
329 	/* We've found the reserved memory region -> we can assign to windows */
330 	rc = init_windows(context);
331 	if (rc) {
332 		goto finish;
333 	}
334 
335 	rc = init_flash_dev(context);
336 	if (rc) {
337 		goto finish;
338 	}
339 
340 	rc = init_mboxd_dbus(context);
341 	if (rc) {
342 		goto finish;
343 	}
344 
345 	/* Set the LPC bus mapping to point to the physical flash device */
346 	rc = point_to_flash(context);
347 	if (rc) {
348 		goto finish;
349 	}
350 
351 	rc = set_bmc_events(context, BMC_EVENT_DAEMON_READY, SET_BMC_EVENT);
352 	if (rc) {
353 		goto finish;
354 	}
355 
356 	MSG_INFO("Entering Polling Loop\n");
357 	rc = poll_loop(context);
358 
359 	MSG_INFO("Exiting Poll Loop: %d\n", rc);
360 
361 finish:
362 	MSG_INFO("Daemon Exiting...\n");
363 	clr_bmc_events(context, BMC_EVENT_DAEMON_READY, SET_BMC_EVENT);
364 
365 	free_mboxd_dbus(context);
366 	free_flash_dev(context);
367 	free_lpc_dev(context);
368 	free_mbox_dev(context);
369 	free_windows(context);
370 	free(context);
371 
372 	return rc;
373 }
374