xref: /openbmc/phosphor-mboxd/mboxd_dbus.c (revision 30bcf84c932a579532e5f8417af549494e11b6e9)
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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 		/* Clear the bit and call the flash modified handler */
229 		clr_bmc_events(context, BMC_EVENT_FLASH_CTRL_LOST,
230 			       NO_BMC_EVENT);
231 		rc = dbus_handle_modified(context, req, resp);
232 	} else {
233 		/* Flash wasn't modified - just clear the bit with writeback */
234 		rc = clr_bmc_events(context, BMC_EVENT_FLASH_CTRL_LOST,
235 				    SET_BMC_EVENT);
236 	}
237 
238 	if (rc < 0) {
239 		rc = -E_DBUS_HARDWARE;
240 	}
241 	context->state &= ~STATE_SUSPENDED;
242 
243 	return rc;
244 }
245 
246 static const mboxd_dbus_handler dbus_handlers[NUM_DBUS_CMDS] = {
247 	dbus_handle_ping,
248 	dbus_handle_daemon_state,
249 	dbus_handle_reset,
250 	dbus_handle_suspend,
251 	dbus_handle_resume,
252 	dbus_handle_modified,
253 	dbus_handle_kill,
254 	dbus_handle_lpc_state
255 };
256 
257 static int method_cmd(sd_bus_message *m, void *userdata,
258 		      sd_bus_error *ret_error)
259 {
260 	struct mbox_dbus_msg req = { 0 }, resp = { 0 };
261 	struct mbox_context *context;
262 	sd_bus_message *n;
263 	int rc, i;
264 
265 	context = (struct mbox_context *) userdata;
266 	if (!context) {
267 		MSG_ERR("DBUS Internal Error\n");
268 		rc = -E_DBUS_INTERNAL;
269 		goto out;
270 	}
271 
272 	/* Read the command */
273 	rc = sd_bus_message_read(m, "y", &req.cmd);
274 	if (rc < 0) {
275 		MSG_ERR("DBUS error reading message: %s\n", strerror(-rc));
276 		rc = -E_DBUS_INTERNAL;
277 		goto out;
278 	}
279 	MSG_DBG("DBUS request: %u\n", req.cmd);
280 
281 	/* Read the args */
282 	rc = sd_bus_message_read_array(m, 'y', (const void **) &req.args,
283 				       &req.num_args);
284 	if (rc < 0) {
285 		MSG_ERR("DBUS error reading message: %s\n", strerror(-rc));
286 		rc = -E_DBUS_INTERNAL;
287 		goto out;
288 	}
289 	MSG_DBG("DBUS num_args: %u\n", (unsigned) req.num_args);
290 	for (i = 0; i < req.num_args; i++) {
291 		MSG_DBG("DBUS arg[%d]: %u\n", i, req.args[i]);
292 	}
293 
294 	/* Handle the command */
295 	if (req.cmd >= NUM_DBUS_CMDS) {
296 		rc = -E_DBUS_INVAL;
297 		MSG_ERR("Received unknown dbus cmd: %d\n", req.cmd);
298 	} else {
299 		rc = dbus_handlers[req.cmd](context, &req, &resp);
300 	}
301 
302 out:
303 	if (rc < 0) {
304 		resp.cmd = -rc;
305 	}
306 	rc = sd_bus_message_new_method_return(m, &n); /* Generate response */
307 	if (rc < 0) {
308 		MSG_ERR("sd_bus_message_new_method_return failed: %d\n", rc);
309 		goto cleanup;
310 	}
311 
312 	rc = sd_bus_message_append(n, "y", resp.cmd); /* Set return code */
313 	if (rc < 0) {
314 		MSG_ERR("sd_bus_message_append failed: %d\n", rc);
315 		goto cleanup;
316 	}
317 
318 	rc = sd_bus_message_append_array(n, 'y', resp.args, resp.num_args);
319 	if (rc < 0) {
320 		MSG_ERR("sd_bus_message_append_array failed: %d\n", rc);
321 		goto cleanup;
322 	}
323 
324 	MSG_DBG("DBUS response: %u\n", resp.cmd);
325 	MSG_DBG("DBUS num_args: %u\n", (unsigned) resp.num_args);
326 	for (i = 0; i < resp.num_args; i++) {
327 		MSG_DBG("DBUS arg[%d]: %u\n", i, resp.args[i]);
328 	}
329 
330 	rc = sd_bus_send(NULL, n, NULL); /* Send response */
331 	if (rc < 0)
332 		MSG_ERR("sd_bus_send failed: %d\n", rc);
333 
334 cleanup:
335 	free(resp.args);
336 	return rc;
337 }
338 
339 static const sd_bus_vtable mboxd_vtable[] = {
340 	SD_BUS_VTABLE_START(0),
341 	SD_BUS_METHOD("cmd", "yay", "yay", &method_cmd,
342 		      SD_BUS_VTABLE_UNPRIVILEGED),
343 	SD_BUS_VTABLE_END
344 };
345 
346 int init_mboxd_dbus(struct mbox_context *context)
347 {
348 	int rc;
349 
350 	rc = sd_bus_default_system(&context->bus);
351 	if (rc < 0) {
352 		MSG_ERR("Failed to connect to the system bus: %s\n",
353 			strerror(-rc));
354 		return rc;
355 	}
356 
357 	rc = sd_bus_add_object_vtable(context->bus, NULL, DOBJ_NAME, DBUS_NAME,
358 				      mboxd_vtable, context);
359 	if (rc < 0) {
360 		MSG_ERR("Failed to register vtable: %s\n", strerror(-rc));
361 		return rc;
362 	}
363 
364 	rc = sd_bus_request_name(context->bus, DBUS_NAME,
365 				 SD_BUS_NAME_ALLOW_REPLACEMENT |
366 				 SD_BUS_NAME_REPLACE_EXISTING);
367 	if (rc < 0) {
368 		MSG_ERR("Failed to acquire service name: %s\n", strerror(-rc));
369 		return rc;
370 	}
371 
372 	rc = sd_bus_get_fd(context->bus);
373 	if (rc < 0) {
374 		MSG_ERR("Failed to get bus fd: %s\n", strerror(-rc));
375 		return rc;
376 	}
377 
378 	context->fds[DBUS_FD].fd = rc;
379 
380 	return 0;
381 }
382 
383 void free_mboxd_dbus(struct mbox_context *context)
384 {
385 	sd_bus_unref(context->bus);
386 }
387