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