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 "mboxd.h"
31 #include "common.h"
32 #include "dbus.h"
33 #include "control_dbus.h"
34 #include "backend.h"
35 #include "lpc.h"
36 #include "transport_dbus.h"
37 #include "windows.h"
38 #include "vpnor/backend.h"
39
40 const char* 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"
45 #ifdef VIRTUAL_PNOR_ENABLED
46 "\t\t-b | --backend <vpnor|mtd[:PATH]|file:PATH>\n"
47 #else
48 "\t\t-b | --backend <mtd[:PATH]|file:PATH>\n"
49 #endif
50 "\t-v | --verbose\t\tBe [more] verbose\n"
51 "\t-s | --syslog\t\tLog output to syslog (pointless without -v)\n"
52 "\t-n | --window-num\tThe number of windows\n"
53 "\t\t\t\t(default: fill the reserved memory region)\n"
54 "\t-w | --window-size\tThe window size (power of 2) in MB\n"
55 "\t\t\t\t(default: 1MB)\n"
56 "\t-f | --flash\t\tSize of flash in [K|M] bytes\n\n"
57 "\t-t | --trace\t\tFile to write trace data to (in blktrace format)\n\n";
58
dbus_init(struct mbox_context * context,const struct transport_ops ** ops)59 static int dbus_init(struct mbox_context *context,
60 const struct transport_ops **ops)
61 {
62 int rc;
63
64 rc = sd_bus_default_system(&context->bus);
65 if (rc < 0) {
66 MSG_ERR("Failed to connect to the system bus: %s\n",
67 strerror(-rc));
68 return rc;
69 }
70
71 rc = control_legacy_init(context);
72 if (rc < 0) {
73 MSG_ERR("Failed to initialise legacy DBus interface: %s\n",
74 strerror(-rc));
75 return rc;
76 }
77
78 rc = control_dbus_init(context);
79 if (rc < 0) {
80 MSG_ERR("Failed to initialise DBus control interface: %s\n",
81 strerror(-rc));
82 return rc;
83 }
84
85 rc = transport_dbus_init(context, ops);
86 if (rc < 0) {
87 MSG_ERR("Failed to initialise DBus protocol interface: %s\n",
88 strerror(-rc));
89 return rc;
90 }
91
92 rc = sd_bus_request_name(context->bus, MBOX_DBUS_NAME,
93 SD_BUS_NAME_ALLOW_REPLACEMENT |
94 SD_BUS_NAME_REPLACE_EXISTING);
95 if (rc < 0) {
96 MSG_ERR("Failed to request DBus name: %s\n", strerror(-rc));
97 return rc;
98 }
99
100 rc = sd_bus_get_fd(context->bus);
101 if (rc < 0) {
102 MSG_ERR("Failed to get bus fd: %s\n", strerror(-rc));
103 return rc;
104 }
105
106 context->fds[DBUS_FD].fd = rc;
107
108 return 0;
109 }
110
dbus_free(struct mbox_context * context)111 static void dbus_free(struct mbox_context *context)
112 {
113 transport_dbus_free(context);
114 control_dbus_free(context);
115 control_legacy_free(context);
116 sd_bus_unref(context->bus);
117 }
118
poll_loop(struct mbox_context * context)119 static int poll_loop(struct mbox_context *context)
120 {
121 int rc = 0, i;
122
123 /* Set POLLIN on polling file descriptors */
124 for (i = 0; i < POLL_FDS; i++) {
125 context->fds[i].events = POLLIN;
126 }
127
128 while (1) {
129 rc = poll(context->fds, POLL_FDS, -1);
130
131 if (rc < 0) { /* Error */
132 MSG_ERR("Error from poll(): %s\n", strerror(errno));
133 break; /* This should mean we clean up nicely */
134 }
135
136 /* Event on Polled File Descriptor - Handle It */
137 if (context->fds[SIG_FD].revents & POLLIN) { /* Signal */
138 struct signalfd_siginfo info = { 0 };
139
140 rc = read(context->fds[SIG_FD].fd, (void *) &info,
141 sizeof(info));
142 if (rc != sizeof(info)) {
143 MSG_ERR("Error reading signal event: %s\n",
144 strerror(errno));
145 }
146
147 MSG_DBG("Received signal: %d\n", info.ssi_signo);
148 switch (info.ssi_signo) {
149 case SIGINT:
150 case SIGTERM:
151 MSG_INFO("Caught Signal - Exiting...\n");
152 context->terminate = true;
153 break;
154 case SIGHUP:
155 rc = protocol_reset(context);
156 if (rc < 0) {
157 MSG_ERR("Failed to reset on SIGHUP\n");
158 }
159 break;
160 default:
161 MSG_ERR("Unhandled Signal: %d\n",
162 info.ssi_signo);
163 break;
164 }
165 }
166 if (context->fds[DBUS_FD].revents & POLLIN) { /* DBUS */
167 while ((rc = sd_bus_process(context->bus, NULL)) > 0) {
168 MSG_DBG("DBUS Event\n");
169 }
170 if (rc < 0) {
171 MSG_ERR("Error handling DBUS event: %s\n",
172 strerror(-rc));
173 }
174 }
175 if (context->terminate) {
176 break; /* This should mean we clean up nicely */
177 }
178 }
179
180 rc = protocol_reset(context);
181 if (rc < 0) {
182 MSG_ERR("Failed to reset during poll loop cleanup\n");
183 }
184
185 return rc;
186 }
187
init_signals(struct mbox_context * context,sigset_t * set)188 static int init_signals(struct mbox_context *context, sigset_t *set)
189 {
190 int rc;
191
192 /* Block SIGHUPs, SIGTERMs and SIGINTs */
193 sigemptyset(set);
194 sigaddset(set, SIGHUP);
195 sigaddset(set, SIGINT);
196 sigaddset(set, SIGTERM);
197 rc = sigprocmask(SIG_BLOCK, set, NULL);
198 if (rc < 0) {
199 MSG_ERR("Failed to set SIG_BLOCK mask %s\n", strerror(errno));
200 return rc;
201 }
202
203 /* Get Signal File Descriptor */
204 rc = signalfd(-1, set, SFD_NONBLOCK);
205 if (rc < 0) {
206 MSG_ERR("Failed to get signalfd %s\n", strerror(errno));
207 return rc;
208 }
209
210 context->fds[SIG_FD].fd = rc;
211 return 0;
212 }
213
usage(const char * name)214 static void usage(const char *name)
215 {
216 printf(USAGE, name);
217 }
218
parse_cmdline(int argc,char ** argv,struct mbox_context * context)219 static bool parse_cmdline(int argc, char **argv,
220 struct mbox_context *context)
221 {
222 char *endptr;
223 int opt;
224
225 static const struct option long_options[] = {
226 { "flash", required_argument, 0, 'f' },
227 { "backend", required_argument, 0, 'b' },
228 { "window-size", optional_argument, 0, 'w' },
229 { "window-num", optional_argument, 0, 'n' },
230 { "verbose", no_argument, 0, 'v' },
231 { "syslog", no_argument, 0, 's' },
232 { "trace", optional_argument, 0, 't' },
233 { "version", no_argument, 0, 'V' },
234 { "help", no_argument, 0, 'h' },
235 { 0, 0, 0, 0 }
236 };
237
238 verbosity = MBOX_LOG_NONE;
239 mbox_vlog = &mbox_log_console;
240
241 context->current = NULL; /* No current window */
242
243 while ((opt = getopt_long(argc, argv, "f:b:w::n::vst::Vh", long_options, NULL))
244 != -1) {
245 switch (opt) {
246 case 0:
247 break;
248 case 'f':
249 context->backend.flash_size = strtol(optarg, &endptr, 10);
250 if (optarg == endptr) {
251 fprintf(stderr, "Unparseable flash size\n");
252 return false;
253 }
254 switch (*endptr) {
255 case '\0':
256 break;
257 case 'M':
258 context->backend.flash_size <<= 20;
259 break;
260 case 'K':
261 context->backend.flash_size <<= 10;
262 break;
263 default:
264 fprintf(stderr, "Unknown units '%c'\n",
265 *endptr);
266 return false;
267 }
268 break;
269 case 'b':
270 context->source = optarg;
271 break;
272 case 'n':
273 context->windows.num = strtol(argv[optind], &endptr,
274 10);
275 if (optarg == endptr || *endptr != '\0') {
276 fprintf(stderr, "Unparseable window num\n");
277 return false;
278 }
279 break;
280 case 'w':
281 context->windows.default_size = strtol(argv[optind],
282 &endptr, 10);
283 context->windows.default_size <<= 20; /* Given in MB */
284 if (optarg == endptr || (*endptr != '\0' &&
285 *endptr != 'M')) {
286 fprintf(stderr, "Unparseable window size\n");
287 return false;
288 }
289 if (!is_power_of_2(context->windows.default_size)) {
290 fprintf(stderr, "Window size not power of 2\n");
291 return false;
292 }
293 break;
294 case 'v':
295 verbosity++;
296 break;
297 case 's':
298 /* Avoid a double openlog() */
299 if (mbox_vlog != &vsyslog) {
300 openlog(PREFIX, LOG_ODELAY, LOG_DAEMON);
301 mbox_vlog = &vsyslog;
302 }
303 break;
304 case 'V':
305 printf("%s V%s\n", THIS_NAME, PACKAGE_VERSION);
306 exit(0);
307 case 't':
308 context->blktracefd = open(argv[optind],
309 O_CREAT|O_TRUNC|O_WRONLY,
310 0666);
311 printf("Recording blktrace output to %s\n",
312 argv[optind]);
313 if (context->blktracefd == -1) {
314 perror("Couldn't open blktrace file for writing");
315 exit(2);
316 }
317 break;
318 case 'h':
319 return false; /* This will print the usage message */
320 default:
321 return false;
322 }
323 }
324
325 if (!context->backend.flash_size) {
326 fprintf(stderr, "Must specify a non-zero flash size\n");
327 return false;
328 }
329
330 MSG_INFO("Flash size: 0x%.8x\n", context->backend.flash_size);
331
332 if (verbosity) {
333 MSG_INFO("%s logging\n", verbosity == MBOX_LOG_DEBUG ? "Debug" :
334 "Verbose");
335 }
336
337 return true;
338 }
339
mboxd_backend_init(struct mbox_context * context)340 static int mboxd_backend_init(struct mbox_context *context)
341 {
342 const char *delim;
343 const char *path;
344 int rc;
345
346 if (!context->source) {
347 struct vpnor_partition_paths paths;
348 vpnor_default_paths(&paths);
349
350 rc = backend_probe_vpnor(&context->backend, &paths);
351 if(rc < 0)
352 rc = backend_probe_mtd(&context->backend, NULL);
353
354 return rc;
355 }
356
357 delim = strchr(context->source, ':');
358 path = delim ? delim + 1 : NULL;
359
360 if (!strncmp(context->source, "vpnor", strlen("vpnor"))) {
361 struct vpnor_partition_paths paths;
362
363 if (path) {
364 rc = -EINVAL;
365 } else {
366 vpnor_default_paths(&paths);
367 rc = backend_probe_vpnor(&context->backend, &paths);
368 }
369 } else if (!strncmp(context->source, "mtd", strlen("mtd"))) {
370 rc = backend_probe_mtd(&context->backend, path);
371 } else if (!strncmp(context->source, "file", strlen("file"))) {
372 rc = backend_probe_file(&context->backend, path);
373 } else {
374 rc = -EINVAL;
375 }
376
377 if (rc < 0)
378 MSG_ERR("Invalid backend argument: %s\n", context->source);
379
380 return rc;
381 }
382
main(int argc,char ** argv)383 int main(int argc, char **argv)
384 {
385 const struct transport_ops *dbus_ops;
386 struct mbox_context *context;
387 char *name = argv[0];
388 sigset_t set;
389 int rc, i;
390
391 context = calloc(1, sizeof(*context));
392 if (!context) {
393 fprintf(stderr, "Memory allocation failed\n");
394 exit(1);
395 }
396
397 if (!parse_cmdline(argc, argv, context)) {
398 usage(name);
399 free(context);
400 exit(0);
401 }
402
403 for (i = 0; i < TOTAL_FDS; i++) {
404 context->fds[i].fd = -1;
405 }
406
407 MSG_INFO("Starting Daemon\n");
408
409 rc = init_signals(context, &set);
410 if (rc) {
411 goto cleanup_context;
412 }
413
414 rc = mboxd_backend_init(context);
415 if (rc) {
416 goto cleanup_context;
417 }
418
419 rc = protocol_init(context);
420 if (rc) {
421 goto cleanup_backend;
422 }
423
424 rc = lpc_dev_init(context);
425 if (rc) {
426 goto cleanup_protocol;
427 }
428
429 /* We've found the reserved memory region -> we can assign to windows */
430 rc = windows_init(context);
431 if (rc) {
432 goto cleanup_lpc;
433 }
434
435 rc = dbus_init(context, &dbus_ops);
436 if (rc) {
437 goto cleanup_windows;
438 }
439
440 /* Set the LPC bus mapping */
441 __protocol_reset(context);
442
443 /* We're ready to go, alert the host */
444 context->bmc_events |= BMC_EVENT_DAEMON_READY;
445 context->bmc_events |= BMC_EVENT_PROTOCOL_RESET;
446
447 /* Alert on all supported transports, as required */
448 rc = protocol_events_put(context, dbus_ops);
449 if (rc) {
450 goto cleanup;
451 }
452
453 MSG_INFO("Entering Polling Loop\n");
454 rc = poll_loop(context);
455
456 MSG_INFO("Exiting Poll Loop: %d\n", rc);
457
458 MSG_INFO("Daemon Exiting...\n");
459 context->bmc_events &= ~BMC_EVENT_DAEMON_READY;
460 context->bmc_events |= BMC_EVENT_PROTOCOL_RESET;
461
462 /* Alert on all supported transports, as required */
463 protocol_events_put(context, dbus_ops);
464
465 cleanup:
466 dbus_free(context);
467 cleanup_windows:
468 windows_free(context);
469 cleanup_lpc:
470 lpc_dev_free(context);
471 cleanup_protocol:
472 protocol_free(context);
473 cleanup_backend:
474 backend_free(&context->backend);
475 cleanup_context:
476 if (context->blktracefd)
477 close(context->blktracefd);
478
479 free(context);
480
481 return rc;
482 }
483