1 /* 2 * Mailbox Daemon Implementation 3 * 4 * Copyright 2016 IBM 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * 18 */ 19 20 #define _GNU_SOURCE 21 #include <assert.h> 22 #include <errno.h> 23 #include <fcntl.h> 24 #include <getopt.h> 25 #include <limits.h> 26 #include <poll.h> 27 #include <stdbool.h> 28 #include <stdint.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <syslog.h> 33 #include <signal.h> 34 #include <sys/ioctl.h> 35 #include <sys/mman.h> 36 #include <sys/stat.h> 37 #include <sys/timerfd.h> 38 #include <sys/types.h> 39 #include <sys/signalfd.h> 40 #include <time.h> 41 #include <unistd.h> 42 #include <inttypes.h> 43 #include <systemd/sd-bus.h> 44 45 #include "mbox.h" 46 #include "common.h" 47 #include "dbus.h" 48 #include "mboxd_dbus.h" 49 #include "mboxd_flash.h" 50 #include "mboxd_lpc.h" 51 #include "mboxd_msg.h" 52 #include "mboxd_windows.h" 53 54 #define USAGE \ 55 "\nUsage: %s [-V | --version] [-h | --help] [-v[v] | --verbose] [-s | --syslog]\n" \ 56 "\t\t-n | --window-num <num>\n" \ 57 "\t\t-w | --window-size <size>M\n" \ 58 "\t\t-f | --flash <size>[K|M]\n\n" \ 59 "\t-v | --verbose\t\tBe [more] verbose\n" \ 60 "\t-s | --syslog\t\tLog output to syslog (pointless without -v)\n" \ 61 "\t-n | --window-num\tThe number of windows\n" \ 62 "\t-w | --window-size\tThe window size (power of 2) in MB\n" \ 63 "\t-f | --flash\t\tSize of flash in [K|M] bytes\n\n" 64 65 static int poll_loop(struct mbox_context *context) 66 { 67 int rc = 0, i; 68 69 /* Set POLLIN on polling file descriptors */ 70 for (i = 0; i < POLL_FDS; i++) { 71 context->fds[i].events = POLLIN; 72 } 73 74 while (1) { 75 rc = poll(context->fds, POLL_FDS, -1); 76 77 if (rc < 0) { /* Error */ 78 MSG_ERR("Error from poll(): %s\n", strerror(errno)); 79 break; /* This should mean we clean up nicely */ 80 } 81 82 /* Event on Polled File Descriptor - Handle It */ 83 if (context->fds[SIG_FD].revents & POLLIN) { /* Signal */ 84 struct signalfd_siginfo info = { 0 }; 85 86 rc = read(context->fds[SIG_FD].fd, (void *) &info, 87 sizeof(info)); 88 if (rc != sizeof(info)) { 89 MSG_ERR("Error reading signal event: %s\n", 90 strerror(errno)); 91 } 92 93 switch (info.ssi_signo) { 94 case SIGINT: 95 case SIGTERM: 96 MSG_OUT("Caught Signal - Exiting...\n"); 97 context->terminate = true; 98 break; 99 case SIGHUP: 100 /* Host didn't request reset -> Notify it */ 101 reset_all_windows(context, SET_BMC_EVENT); 102 rc = point_to_flash(context); 103 if (rc < 0) { 104 MSG_ERR("WARNING: Failed to point the " 105 "LPC bus back to flash on " 106 "SIGHUP\nIf the host requires " 107 "this expect problems...\n"); 108 } 109 break; 110 default: 111 MSG_ERR("Unhandled Signal: %d\n", 112 info.ssi_signo); 113 break; 114 } 115 } 116 if (context->fds[DBUS_FD].revents & POLLIN) { /* DBUS */ 117 while ((rc = sd_bus_process(context->bus, NULL)) > 0); 118 if (rc < 0) { 119 MSG_ERR("Error handling DBUS event: %s\n", 120 strerror(-rc)); 121 } 122 } 123 if (context->terminate) { 124 break; /* This should mean we clean up nicely */ 125 } 126 if (context->fds[MBOX_FD].revents & POLLIN) { /* MBOX */ 127 rc = dispatch_mbox(context); 128 if (rc < 0) { 129 MSG_ERR("Error handling MBOX event\n"); 130 } 131 } 132 } 133 134 /* Best to reset windows and point back to flash for safety */ 135 /* Host didn't request reset -> Notify it */ 136 reset_all_windows(context, SET_BMC_EVENT); 137 rc = point_to_flash(context); 138 /* Not much we can do if this fails */ 139 if (rc < 0) { 140 MSG_ERR("WARNING: Failed to point the LPC bus back to flash\n" 141 "If the host requires this expect problems...\n"); 142 } 143 144 return rc; 145 } 146 147 static int init_signals(struct mbox_context *context, sigset_t *set) 148 { 149 int rc; 150 151 /* Block SIGHUPs, SIGTERMs and SIGINTs */ 152 sigemptyset(set); 153 sigaddset(set, SIGHUP); 154 sigaddset(set, SIGINT); 155 sigaddset(set, SIGTERM); 156 rc = sigprocmask(SIG_BLOCK, set, NULL); 157 if (rc < 0) { 158 MSG_ERR("Failed to set SIG_BLOCK mask %s\n", strerror(errno)); 159 return rc; 160 } 161 162 /* Get Signal File Descriptor */ 163 rc = signalfd(-1, set, SFD_NONBLOCK); 164 if (rc < 0) { 165 MSG_ERR("Failed to get signalfd %s\n", strerror(errno)); 166 return rc; 167 } 168 169 context->fds[SIG_FD].fd = rc; 170 return 0; 171 } 172 173 static void usage(const char *name) 174 { 175 printf(USAGE, name); 176 } 177 178 static bool parse_cmdline(int argc, char **argv, 179 struct mbox_context *context) 180 { 181 char *endptr; 182 int opt, i; 183 184 static const struct option long_options[] = { 185 { "flash", required_argument, 0, 'f' }, 186 { "window-size", optional_argument, 0, 'w' }, 187 { "window-num", optional_argument, 0, 'n' }, 188 { "verbose", no_argument, 0, 'v' }, 189 { "syslog", no_argument, 0, 's' }, 190 { "version", no_argument, 0, 'V' }, 191 { "help", no_argument, 0, 'h' }, 192 { 0, 0, 0, 0 } 193 }; 194 195 verbosity = MBOX_LOG_NONE; 196 mbox_vlog = &mbox_log_console; 197 198 /* Default to 1 window of size flash_size */ 199 context->windows.default_size = context->flash_size; 200 context->windows.num = 1; 201 context->current = NULL; /* No current window */ 202 203 while ((opt = getopt_long(argc, argv, "f:w::n::vsVh", long_options, NULL)) 204 != -1) { 205 switch (opt) { 206 case 0: 207 break; 208 case 'f': 209 context->flash_size = strtol(optarg, &endptr, 10); 210 if (optarg == endptr) { 211 fprintf(stderr, "Unparseable flash size\n"); 212 return false; 213 } 214 switch (*endptr) { 215 case '\0': 216 break; 217 case 'M': 218 context->flash_size <<= 10; 219 case 'K': 220 context->flash_size <<= 10; 221 break; 222 default: 223 fprintf(stderr, "Unknown units '%c'\n", 224 *endptr); 225 return false; 226 } 227 break; 228 case 'n': 229 context->windows.num = strtol(argv[optind], &endptr, 230 10); 231 if (optarg == endptr || *endptr != '\0') { 232 fprintf(stderr, "Unparseable window num\n"); 233 return false; 234 } 235 break; 236 case 'w': 237 context->windows.default_size = strtol(argv[optind], 238 &endptr, 10); 239 context->windows.default_size <<= 20; /* Given in MB */ 240 if (optarg == endptr || (*endptr != '\0' && 241 *endptr != 'M')) { 242 fprintf(stderr, "Unparseable window size\n"); 243 return false; 244 } 245 break; 246 case 'v': 247 verbosity++; 248 break; 249 case 's': 250 /* Avoid a double openlog() */ 251 if (mbox_vlog != &vsyslog) { 252 openlog(PREFIX, LOG_ODELAY, LOG_DAEMON); 253 mbox_vlog = &vsyslog; 254 } 255 break; 256 case 'V': 257 printf("%s v%d.%.2d\n", THIS_NAME, API_MAX_VERSION, 258 SUB_VERSION); 259 exit(0); 260 case 'h': 261 return false; /* This will print the usage message */ 262 default: 263 return false; 264 } 265 } 266 267 if (!context->flash_size) { 268 fprintf(stderr, "Must specify a non-zero flash size\n"); 269 return false; 270 } 271 272 if (!context->windows.num) { 273 fprintf(stderr, "Must specify a non-zero number of windows\n" 274 "If unsure - select 4 (-n 4)\n"); 275 return false; 276 } 277 278 if (!context->windows.default_size) { 279 fprintf(stderr, "Must specify a non-zero window size\n" 280 "If unsure - select 1M (-w 1)\n"); 281 return false; 282 } 283 284 MSG_OUT("Flash size: 0x%.8x\n", context->flash_size); 285 MSG_OUT("Number of Windows: %d\n", context->windows.num); 286 MSG_OUT("Window size: 0x%.8x\n", context->windows.default_size); 287 288 context->windows.window = calloc(context->windows.num, 289 sizeof(*context->windows.window)); 290 if (!context->windows.window) { 291 MSG_ERR("Memory allocation failed\n"); 292 free(context); 293 exit(1); 294 } 295 296 for (i = 0; i < context->windows.num; i++) { 297 init_window_state(&context->windows.window[i], 298 context->windows.default_size); 299 } 300 301 if (verbosity) { 302 MSG_OUT("%s logging\n", verbosity == MBOX_LOG_DEBUG ? "Debug" : 303 "Verbose"); 304 } 305 306 return true; 307 } 308 309 int main(int argc, char **argv) 310 { 311 struct mbox_context *context; 312 char *name = argv[0]; 313 sigset_t set; 314 int rc, i; 315 316 context = calloc(1, sizeof(*context)); 317 if (!context) { 318 fprintf(stderr, "Memory allocation failed\n"); 319 exit(1); 320 } 321 322 if (!parse_cmdline(argc, argv, context)) { 323 usage(name); 324 free(context); 325 exit(0); 326 } 327 328 for (i = 0; i < TOTAL_FDS; i++) { 329 context->fds[i].fd = -1; 330 } 331 332 MSG_OUT("Starting Daemon\n"); 333 334 rc = init_signals(context, &set); 335 if (rc) { 336 goto finish; 337 } 338 339 rc = init_mbox_dev(context); 340 if (rc) { 341 goto finish; 342 } 343 344 rc = init_lpc_dev(context); 345 if (rc) { 346 goto finish; 347 } 348 349 /* We've found the reserved memory region -> we can assign to windows */ 350 rc = init_window_mem(context); 351 if (rc) { 352 goto finish; 353 } 354 355 rc = init_flash_dev(context); 356 if (rc) { 357 goto finish; 358 } 359 360 rc = init_mboxd_dbus(context); 361 if (rc) { 362 goto finish; 363 } 364 365 /* Set the LPC bus mapping to point to the physical flash device */ 366 rc = point_to_flash(context); 367 if (rc) { 368 goto finish; 369 } 370 371 rc = set_bmc_events(context, BMC_EVENT_DAEMON_READY, SET_BMC_EVENT); 372 if (rc) { 373 goto finish; 374 } 375 376 MSG_OUT("Entering Polling Loop\n"); 377 rc = poll_loop(context); 378 379 MSG_OUT("Exiting Poll Loop: %d\n", rc); 380 381 finish: 382 MSG_OUT("Daemon Exiting...\n"); 383 clr_bmc_events(context, BMC_EVENT_DAEMON_READY, SET_BMC_EVENT); 384 385 free_mboxd_dbus(context); 386 free_flash_dev(context); 387 free_lpc_dev(context); 388 free_mbox_dev(context); 389 free_window_dirty_bytemap(context); 390 free(context->windows.window); 391 free(context); 392 393 return rc; 394 } 395