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