xref: /openbmc/phosphor-mboxd/mboxd_dbus.c (revision acdbdd141ece313f259ad6231e132f0a4bc3e062)
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