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