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