1 /* 2 * Mailbox Daemon DBUS Helpers 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 <time.h> 40 #include <unistd.h> 41 #include <inttypes.h> 42 #include <systemd/sd-bus.h> 43 44 #include "mbox.h" 45 #include "common.h" 46 #include "dbus.h" 47 #include "mboxd_dbus.h" 48 #include "mboxd_windows.h" 49 #include "mboxd_msg.h" 50 #include "mboxd_lpc.h" 51 #include "mboxd_flash.h" 52 53 typedef int (*mboxd_dbus_handler)(struct mbox_context *, struct mbox_dbus_msg *, 54 struct mbox_dbus_msg *); 55 56 /* DBUS Functions */ 57 58 /* 59 * Command: DBUS Ping 60 * Ping the daemon 61 * 62 * Args: NONE 63 * Resp: NONE 64 */ 65 static int dbus_handle_ping(struct mbox_context *context, 66 struct mbox_dbus_msg *req, 67 struct mbox_dbus_msg *resp) 68 { 69 return 0; 70 } 71 72 /* 73 * Command: DBUS Status 74 * Get the status of the daemon 75 * 76 * Args: NONE 77 * Resp[0]: Status Code 78 */ 79 static int dbus_handle_daemon_state(struct mbox_context *context, 80 struct mbox_dbus_msg *req, 81 struct mbox_dbus_msg *resp) 82 { 83 resp->num_args = DAEMON_STATE_NUM_ARGS; 84 resp->args = calloc(resp->num_args, sizeof(*resp->args)); 85 resp->args[0] = (context->state & STATE_SUSPENDED) ? 86 DAEMON_STATE_SUSPENDED : DAEMON_STATE_ACTIVE; 87 88 return 0; 89 } 90 91 /* 92 * Command: DBUS LPC State 93 * Get the state of the lpc bus mapping (whether it points to memory or flash 94 * 95 * Args: NONE 96 * Resp[0]: LPC Bus State Code 97 */ 98 static int dbus_handle_lpc_state(struct mbox_context *context, 99 struct mbox_dbus_msg *req, 100 struct mbox_dbus_msg *resp) 101 { 102 resp->num_args = LPC_STATE_NUM_ARGS; 103 resp->args = calloc(resp->num_args, sizeof(*resp->args)); 104 if ((context->state & MAPS_MEM) && !(context->state & MAPS_FLASH)) { 105 resp->args[0] = LPC_STATE_MEM; 106 } else if (!(context->state & MAPS_MEM) && 107 (context->state & MAPS_FLASH)) { 108 resp->args[0] = LPC_STATE_FLASH; 109 } else { 110 resp->args[0] = LPC_STATE_INVALID; 111 } 112 113 return 0; 114 } 115 116 /* 117 * Command: DBUS Reset 118 * Reset the daemon state, final operation TBA. 119 * For now we just point the lpc mapping back at the flash. 120 * 121 * Args: NONE 122 * Resp: NONE 123 */ 124 static int dbus_handle_reset(struct mbox_context *context, 125 struct mbox_dbus_msg *req, 126 struct mbox_dbus_msg *resp) 127 { 128 int rc; 129 130 /* We don't let the host access flash if the daemon is suspened */ 131 if (context->state & STATE_SUSPENDED) { 132 return -E_DBUS_REJECTED; 133 } 134 135 /* 136 * This will close (and flush) the current window and reset the lpc bus 137 * mapping back to flash, or memory in case we're using a virtual pnor. 138 * Better set the bmc event to notify the host of this. 139 */ 140 reset_all_windows(context, SET_BMC_EVENT); 141 rc = reset_lpc(context); 142 if (rc < 0) { 143 return -E_DBUS_HARDWARE; 144 } 145 146 return 0; 147 } 148 149 /* 150 * Command: DBUS Kill 151 * Stop the daemon 152 * 153 * Args: NONE 154 * Resp: NONE 155 */ 156 static int dbus_handle_kill(struct mbox_context *context, 157 struct mbox_dbus_msg *req, 158 struct mbox_dbus_msg *resp) 159 { 160 context->terminate = 1; 161 162 MSG_INFO("DBUS Kill - Exiting...\n"); 163 164 return 0; 165 } 166 167 /* 168 * Command: DBUS Flash Modified 169 * Used to notify the daemon that the flash has been modified out from under 170 * it - We need to reset all out windows to ensure flash will be reloaded 171 * when a new window is opened. 172 * Note: We don't flush any previously opened windows 173 * 174 * Args: NONE 175 * Resp: NONE 176 */ 177 static int dbus_handle_modified(struct mbox_context *context, 178 struct mbox_dbus_msg *req, 179 struct mbox_dbus_msg *resp) 180 { 181 /* Flash has been modified - can no longer trust our erased bytemap */ 182 set_flash_bytemap(context, 0, context->flash_size, FLASH_DIRTY); 183 184 /* Force daemon to reload all windows -> Set BMC event to notify host */ 185 reset_all_windows(context, SET_BMC_EVENT); 186 187 return 0; 188 } 189 190 /* 191 * Command: DBUS Suspend 192 * Suspend the daemon to inhibit it from performing flash accesses. 193 * This is used to synchronise access to the flash between the daemon and 194 * directly from the BMC. 195 * 196 * Args: NONE 197 * Resp: NONE 198 */ 199 static int dbus_handle_suspend(struct mbox_context *context, 200 struct mbox_dbus_msg *req, 201 struct mbox_dbus_msg *resp) 202 { 203 int rc; 204 205 if (context->state & STATE_SUSPENDED) { 206 /* Already Suspended */ 207 return DBUS_SUCCESS; 208 } 209 210 /* Nothing to check - Just set the bit to notify the host */ 211 rc = set_bmc_events(context, BMC_EVENT_FLASH_CTRL_LOST, SET_BMC_EVENT); 212 if (rc < 0) { 213 return -E_DBUS_HARDWARE; 214 } 215 216 context->state |= STATE_SUSPENDED; 217 218 return rc; 219 } 220 221 /* 222 * Command: DBUS Resume 223 * Resume the daemon to let it perform flash accesses again. 224 * 225 * Args[0]: Flash Modified (0 - no | 1 - yes) 226 * Resp: NONE 227 */ 228 static int dbus_handle_resume(struct mbox_context *context, 229 struct mbox_dbus_msg *req, 230 struct mbox_dbus_msg *resp) 231 { 232 int rc; 233 234 if (req->num_args != 1) { 235 return -E_DBUS_INVAL; 236 } 237 238 if (!(context->state & STATE_SUSPENDED)) { 239 /* We weren't suspended... */ 240 return DBUS_SUCCESS; 241 } 242 243 if (req->args[0] == RESUME_FLASH_MODIFIED) { 244 /* Clear the bit and call the flash modified handler */ 245 clr_bmc_events(context, BMC_EVENT_FLASH_CTRL_LOST, 246 NO_BMC_EVENT); 247 rc = dbus_handle_modified(context, req, resp); 248 } else { 249 /* Flash wasn't modified - just clear the bit with writeback */ 250 rc = clr_bmc_events(context, BMC_EVENT_FLASH_CTRL_LOST, 251 SET_BMC_EVENT); 252 } 253 254 if (rc < 0) { 255 rc = -E_DBUS_HARDWARE; 256 } 257 context->state &= ~STATE_SUSPENDED; 258 259 return rc; 260 } 261 262 static const mboxd_dbus_handler dbus_handlers[NUM_DBUS_CMDS] = { 263 dbus_handle_ping, 264 dbus_handle_daemon_state, 265 dbus_handle_reset, 266 dbus_handle_suspend, 267 dbus_handle_resume, 268 dbus_handle_modified, 269 dbus_handle_kill, 270 dbus_handle_lpc_state 271 }; 272 273 static int method_cmd(sd_bus_message *m, void *userdata, 274 sd_bus_error *ret_error) 275 { 276 struct mbox_dbus_msg req = { 0 }, resp = { 0 }; 277 struct mbox_context *context; 278 sd_bus_message *n; 279 int rc, i; 280 281 context = (struct mbox_context *) userdata; 282 if (!context) { 283 MSG_ERR("DBUS Internal Error\n"); 284 rc = -E_DBUS_INTERNAL; 285 goto out; 286 } 287 288 /* Read the command */ 289 rc = sd_bus_message_read(m, "y", &req.cmd); 290 if (rc < 0) { 291 MSG_ERR("DBUS error reading message: %s\n", strerror(-rc)); 292 rc = -E_DBUS_INTERNAL; 293 goto out; 294 } 295 MSG_DBG("DBUS request: %u\n", req.cmd); 296 297 /* Read the args */ 298 rc = sd_bus_message_read_array(m, 'y', (const void **) &req.args, 299 &req.num_args); 300 if (rc < 0) { 301 MSG_ERR("DBUS error reading message: %s\n", strerror(-rc)); 302 rc = -E_DBUS_INTERNAL; 303 goto out; 304 } 305 MSG_DBG("DBUS num_args: %u\n", (unsigned) req.num_args); 306 for (i = 0; i < req.num_args; i++) { 307 MSG_DBG("DBUS arg[%d]: %u\n", i, req.args[i]); 308 } 309 310 /* Handle the command */ 311 if (req.cmd >= NUM_DBUS_CMDS) { 312 rc = -E_DBUS_INVAL; 313 MSG_ERR("Received unknown dbus cmd: %d\n", req.cmd); 314 } else { 315 rc = dbus_handlers[req.cmd](context, &req, &resp); 316 } 317 318 out: 319 if (rc < 0) { 320 resp.cmd = -rc; 321 } 322 rc = sd_bus_message_new_method_return(m, &n); /* Generate response */ 323 if (rc < 0) { 324 MSG_ERR("sd_bus_message_new_method_return failed: %d\n", rc); 325 goto cleanup; 326 } 327 328 rc = sd_bus_message_append(n, "y", resp.cmd); /* Set return code */ 329 if (rc < 0) { 330 MSG_ERR("sd_bus_message_append failed: %d\n", rc); 331 goto cleanup; 332 } 333 334 rc = sd_bus_message_append_array(n, 'y', resp.args, resp.num_args); 335 if (rc < 0) { 336 MSG_ERR("sd_bus_message_append_array failed: %d\n", rc); 337 goto cleanup; 338 } 339 340 MSG_DBG("DBUS response: %u\n", resp.cmd); 341 MSG_DBG("DBUS num_args: %u\n", (unsigned) resp.num_args); 342 for (i = 0; i < resp.num_args; i++) { 343 MSG_DBG("DBUS arg[%d]: %u\n", i, resp.args[i]); 344 } 345 346 rc = sd_bus_send(NULL, n, NULL); /* Send response */ 347 if (rc < 0) 348 MSG_ERR("sd_bus_send failed: %d\n", rc); 349 350 cleanup: 351 free(resp.args); 352 return rc; 353 } 354 355 static const sd_bus_vtable mboxd_vtable[] = { 356 SD_BUS_VTABLE_START(0), 357 SD_BUS_METHOD("cmd", "yay", "yay", &method_cmd, 358 SD_BUS_VTABLE_UNPRIVILEGED), 359 SD_BUS_VTABLE_END 360 }; 361 362 int init_mboxd_dbus(struct mbox_context *context) 363 { 364 int rc; 365 366 rc = sd_bus_default_system(&context->bus); 367 if (rc < 0) { 368 MSG_ERR("Failed to connect to the system bus: %s\n", 369 strerror(-rc)); 370 return rc; 371 } 372 373 rc = sd_bus_add_object_vtable(context->bus, NULL, DOBJ_NAME, DBUS_NAME, 374 mboxd_vtable, context); 375 if (rc < 0) { 376 MSG_ERR("Failed to register vtable: %s\n", strerror(-rc)); 377 return rc; 378 } 379 380 rc = sd_bus_request_name(context->bus, DBUS_NAME, 381 SD_BUS_NAME_ALLOW_REPLACEMENT | 382 SD_BUS_NAME_REPLACE_EXISTING); 383 if (rc < 0) { 384 MSG_ERR("Failed to acquire service name: %s\n", strerror(-rc)); 385 return rc; 386 } 387 388 rc = sd_bus_get_fd(context->bus); 389 if (rc < 0) { 390 MSG_ERR("Failed to get bus fd: %s\n", strerror(-rc)); 391 return rc; 392 } 393 394 context->fds[DBUS_FD].fd = rc; 395 396 return 0; 397 } 398 399 void free_mboxd_dbus(struct mbox_context *context) 400 { 401 sd_bus_unref(context->bus); 402 } 403