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 "flash.h" 35 #include "lpc.h" 36 #include "transport_mbox.h" 37 #include "transport_dbus.h" 38 #include "windows.h" 39 #include "vpnor/mboxd_pnor_partition_table.h" 40 41 #define 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\n" \ 46 "\t-v | --verbose\t\tBe [more] verbose\n" \ 47 "\t-s | --syslog\t\tLog output to syslog (pointless without -v)\n" \ 48 "\t-n | --window-num\tThe number of windows\n" \ 49 "\t\t\t\t(default: fill the reserved memory region)\n" \ 50 "\t-w | --window-size\tThe window size (power of 2) in MB\n" \ 51 "\t\t\t\t(default: 1MB)\n" \ 52 "\t-f | --flash\t\tSize of flash in [K|M] bytes\n\n" 53 54 static int dbus_init(struct mbox_context *context) 55 { 56 int rc; 57 58 rc = sd_bus_default_system(&context->bus); 59 if (rc < 0) { 60 MSG_ERR("Failed to connect to the system bus: %s\n", 61 strerror(-rc)); 62 return rc; 63 } 64 65 rc = control_legacy_init(context); 66 if (rc < 0) { 67 MSG_ERR("Failed to initialise legacy DBus interface: %s\n", 68 strerror(-rc)); 69 return rc; 70 } 71 72 rc = control_dbus_init(context); 73 if (rc < 0) { 74 MSG_ERR("Failed to initialise DBus control interface: %s\n", 75 strerror(-rc)); 76 return rc; 77 } 78 79 rc = transport_dbus_init(context); 80 if (rc < 0) { 81 MSG_ERR("Failed to initialise DBus protocol interface: %s\n", 82 strerror(-rc)); 83 return rc; 84 } 85 86 rc = sd_bus_request_name(context->bus, MBOX_DBUS_NAME, 87 SD_BUS_NAME_ALLOW_REPLACEMENT | 88 SD_BUS_NAME_REPLACE_EXISTING); 89 if (rc < 0) { 90 MSG_ERR("Failed to request DBus name: %s\n", strerror(-rc)); 91 return rc; 92 } 93 94 rc = sd_bus_get_fd(context->bus); 95 if (rc < 0) { 96 MSG_ERR("Failed to get bus fd: %s\n", strerror(-rc)); 97 return rc; 98 } 99 100 context->fds[DBUS_FD].fd = rc; 101 102 return 0; 103 } 104 105 static void dbus_free(struct mbox_context *context) 106 { 107 transport_dbus_free(context); 108 control_dbus_free(context); 109 control_legacy_free(context); 110 sd_bus_unref(context->bus); 111 } 112 113 static int poll_loop(struct mbox_context *context) 114 { 115 int rc = 0, i; 116 117 /* Set POLLIN on polling file descriptors */ 118 for (i = 0; i < POLL_FDS; i++) { 119 context->fds[i].events = POLLIN; 120 } 121 122 while (1) { 123 rc = poll(context->fds, POLL_FDS, -1); 124 125 if (rc < 0) { /* Error */ 126 MSG_ERR("Error from poll(): %s\n", strerror(errno)); 127 break; /* This should mean we clean up nicely */ 128 } 129 130 /* Event on Polled File Descriptor - Handle It */ 131 if (context->fds[SIG_FD].revents & POLLIN) { /* Signal */ 132 struct signalfd_siginfo info = { 0 }; 133 134 rc = read(context->fds[SIG_FD].fd, (void *) &info, 135 sizeof(info)); 136 if (rc != sizeof(info)) { 137 MSG_ERR("Error reading signal event: %s\n", 138 strerror(errno)); 139 } 140 141 MSG_DBG("Received signal: %d\n", info.ssi_signo); 142 switch (info.ssi_signo) { 143 case SIGINT: 144 case SIGTERM: 145 MSG_INFO("Caught Signal - Exiting...\n"); 146 context->terminate = true; 147 break; 148 case SIGHUP: 149 /* Host didn't request reset -> Notify it */ 150 if (windows_reset_all(context)) { 151 rc = protocol_events_set(context, 152 BMC_EVENT_WINDOW_RESET); 153 if (rc < 0) { 154 MSG_ERR("Failed to notify host of reset, expect host-side corruption\n"); 155 break; 156 } 157 } 158 rc = lpc_reset(context); 159 if (rc < 0) { 160 MSG_ERR("WARNING: Failed to point the " 161 "LPC bus back to flash on " 162 "SIGHUP\nIf the host requires " 163 "this expect problems...\n"); 164 } 165 break; 166 default: 167 MSG_ERR("Unhandled Signal: %d\n", 168 info.ssi_signo); 169 break; 170 } 171 } 172 if (context->fds[DBUS_FD].revents & POLLIN) { /* DBUS */ 173 while ((rc = sd_bus_process(context->bus, NULL)) > 0) { 174 MSG_DBG("DBUS Event\n"); 175 } 176 if (rc < 0) { 177 MSG_ERR("Error handling DBUS event: %s\n", 178 strerror(-rc)); 179 } 180 } 181 if (context->terminate) { 182 break; /* This should mean we clean up nicely */ 183 } 184 if (context->fds[MBOX_FD].revents & POLLIN) { /* MBOX */ 185 MSG_DBG("MBOX Event\n"); 186 rc = transport_mbox_dispatch(context); 187 if (rc < 0) { 188 MSG_ERR("Error handling MBOX event\n"); 189 } 190 } 191 } 192 193 /* Best to reset windows and the lpc mapping for safety */ 194 /* Host didn't request reset -> Notify it */ 195 if (windows_reset_all(context)) { 196 rc = protocol_events_set(context, BMC_EVENT_WINDOW_RESET); 197 if (rc < 0) { 198 MSG_ERR("Failed to notify host of reset, expect host-side corruption\n"); 199 return rc; 200 } 201 } 202 rc = lpc_reset(context); 203 /* Not much we can do if this fails */ 204 if (rc < 0) { 205 MSG_ERR("WARNING: Failed to point the LPC bus back to flash\n" 206 "If the host requires this expect problems...\n"); 207 } 208 209 return rc; 210 } 211 212 static int init_signals(struct mbox_context *context, sigset_t *set) 213 { 214 int rc; 215 216 /* Block SIGHUPs, SIGTERMs and SIGINTs */ 217 sigemptyset(set); 218 sigaddset(set, SIGHUP); 219 sigaddset(set, SIGINT); 220 sigaddset(set, SIGTERM); 221 rc = sigprocmask(SIG_BLOCK, set, NULL); 222 if (rc < 0) { 223 MSG_ERR("Failed to set SIG_BLOCK mask %s\n", strerror(errno)); 224 return rc; 225 } 226 227 /* Get Signal File Descriptor */ 228 rc = signalfd(-1, set, SFD_NONBLOCK); 229 if (rc < 0) { 230 MSG_ERR("Failed to get signalfd %s\n", strerror(errno)); 231 return rc; 232 } 233 234 context->fds[SIG_FD].fd = rc; 235 return 0; 236 } 237 238 static void usage(const char *name) 239 { 240 printf(USAGE, name); 241 } 242 243 static bool parse_cmdline(int argc, char **argv, 244 struct mbox_context *context) 245 { 246 char *endptr; 247 int opt; 248 249 static const struct option long_options[] = { 250 { "flash", required_argument, 0, 'f' }, 251 { "window-size", optional_argument, 0, 'w' }, 252 { "window-num", optional_argument, 0, 'n' }, 253 { "verbose", no_argument, 0, 'v' }, 254 { "syslog", no_argument, 0, 's' }, 255 { "version", no_argument, 0, 'V' }, 256 { "help", no_argument, 0, 'h' }, 257 { 0, 0, 0, 0 } 258 }; 259 260 verbosity = MBOX_LOG_NONE; 261 mbox_vlog = &mbox_log_console; 262 263 context->current = NULL; /* No current window */ 264 265 while ((opt = getopt_long(argc, argv, "f:w::n::vsVh", long_options, NULL)) 266 != -1) { 267 switch (opt) { 268 case 0: 269 break; 270 case 'f': 271 context->flash_size = strtol(optarg, &endptr, 10); 272 if (optarg == endptr) { 273 fprintf(stderr, "Unparseable flash size\n"); 274 return false; 275 } 276 switch (*endptr) { 277 case '\0': 278 break; 279 case 'M': 280 context->flash_size <<= 10; 281 case 'K': 282 context->flash_size <<= 10; 283 break; 284 default: 285 fprintf(stderr, "Unknown units '%c'\n", 286 *endptr); 287 return false; 288 } 289 break; 290 case 'n': 291 context->windows.num = strtol(argv[optind], &endptr, 292 10); 293 if (optarg == endptr || *endptr != '\0') { 294 fprintf(stderr, "Unparseable window num\n"); 295 return false; 296 } 297 break; 298 case 'w': 299 context->windows.default_size = strtol(argv[optind], 300 &endptr, 10); 301 context->windows.default_size <<= 20; /* Given in MB */ 302 if (optarg == endptr || (*endptr != '\0' && 303 *endptr != 'M')) { 304 fprintf(stderr, "Unparseable window size\n"); 305 return false; 306 } 307 if (!is_power_of_2(context->windows.default_size)) { 308 fprintf(stderr, "Window size not power of 2\n"); 309 return false; 310 } 311 break; 312 case 'v': 313 verbosity++; 314 break; 315 case 's': 316 /* Avoid a double openlog() */ 317 if (mbox_vlog != &vsyslog) { 318 openlog(PREFIX, LOG_ODELAY, LOG_DAEMON); 319 mbox_vlog = &vsyslog; 320 } 321 break; 322 case 'V': 323 printf("%s V%s\n", THIS_NAME, PACKAGE_VERSION); 324 exit(0); 325 case 'h': 326 return false; /* This will print the usage message */ 327 default: 328 return false; 329 } 330 } 331 332 if (!context->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->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 int main(int argc, char **argv) 348 { 349 struct mbox_context *context; 350 char *name = argv[0]; 351 sigset_t set; 352 int rc, i; 353 354 context = calloc(1, sizeof(*context)); 355 if (!context) { 356 fprintf(stderr, "Memory allocation failed\n"); 357 exit(1); 358 } 359 360 if (!parse_cmdline(argc, argv, context)) { 361 usage(name); 362 free(context); 363 exit(0); 364 } 365 366 for (i = 0; i < TOTAL_FDS; i++) { 367 context->fds[i].fd = -1; 368 } 369 370 MSG_INFO("Starting Daemon\n"); 371 372 rc = init_signals(context, &set); 373 if (rc) { 374 goto finish; 375 } 376 377 rc = protocol_init(context); 378 if (rc) { 379 goto finish; 380 } 381 382 rc = transport_mbox_init(context); 383 if (rc) { 384 goto finish; 385 } 386 387 rc = lpc_dev_init(context); 388 if (rc) { 389 goto finish; 390 } 391 392 /* We've found the reserved memory region -> we can assign to windows */ 393 rc = windows_init(context); 394 if (rc) { 395 goto finish; 396 } 397 398 rc = flash_dev_init(context); 399 if (rc) { 400 goto finish; 401 } 402 403 rc = dbus_init(context); 404 if (rc) { 405 goto finish; 406 } 407 408 #ifdef VIRTUAL_PNOR_ENABLED 409 init_vpnor(context); 410 #endif 411 412 /* Set the LPC bus mapping */ 413 rc = lpc_reset(context); 414 if (rc) { 415 MSG_ERR("LPC configuration failed, RESET required: %d\n", rc); 416 } 417 418 rc = protocol_events_set(context, BMC_EVENT_DAEMON_READY); 419 if (rc) { 420 goto finish; 421 } 422 423 MSG_INFO("Entering Polling Loop\n"); 424 rc = poll_loop(context); 425 426 MSG_INFO("Exiting Poll Loop: %d\n", rc); 427 428 finish: 429 MSG_INFO("Daemon Exiting...\n"); 430 protocol_events_clear(context, BMC_EVENT_DAEMON_READY); 431 432 #ifdef VIRTUAL_PNOR_ENABLED 433 destroy_vpnor(context); 434 #endif 435 dbus_free(context); 436 flash_dev_free(context); 437 lpc_dev_free(context); 438 transport_mbox_free(context); 439 windows_free(context); 440 protocol_free(context); 441 free(context); 442 443 return rc; 444 } 445