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