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