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 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 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 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 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 214 static void usage(const char *name) 215 { 216 printf(USAGE, name); 217 } 218 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 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 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