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