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