xref: /openbmc/hiomapd/mboxctl.c (revision 04e8ffda)
1 /*
2  * Mailbox Control Implementation
3  *
4  * Copyright 2017 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 
43 #include <systemd/sd-bus.h>
44 
45 #include "dbus.h"
46 
47 #define USAGE \
48 "\nUsage: %s [--silent | -s] <command> [args]\n\n" \
49 "\t\t--silent\t\t- no output on the command line\n\n" \
50 "\tCommands: (num args)\n" \
51 "\t\t--ping\t\t\t- ping the daemon (0)\n" \
52 "\t\t--daemon-state\t\t- check state of the daemon (0)\n" \
53 "\t\t--lpc-state\t\t- check the state of the lpc mapping (0)\n" \
54 "\t\t--kill\t\t\t- stop the daemon [no flush] (0)\n" \
55 "\t\t--reset\t\t\t- hard reset the daemon state (0)\n" \
56 "\t\t--point-to-flash\t- point the lpc mapping back to flash (0)\n" \
57 "\t\t--suspend\t\t- suspend the daemon to inhibit flash accesses (0)\n" \
58 "\t\t--resume\t\t- resume the daemon (1)\n" \
59 "\t\t\targ[0]: < \"clean\" | \"modified\" >\n" \
60 "\t\t--clear-cache\t- tell the daemon to discard any caches (0)\n"
61 
62 #define NAME		"MBOX Control"
63 #define VERSION		1
64 #define SUBVERSION	0
65 
66 static bool silent;
67 
68 #define MSG_OUT(...)	do { if (!silent) { \
69 				fprintf(stdout, __VA_ARGS__); } \
70 			} while (0)
71 #define MSG_ERR(...)	do { if (!silent) { \
72 				fprintf(stderr, __VA_ARGS__); } \
73 			} while (0)
74 
75 struct mboxctl_context {
76 	sd_bus *bus;
77 };
78 
79 static void usage(char *name)
80 {
81 	MSG_OUT(USAGE, name);
82 	exit(0);
83 }
84 
85 static const char *dbus_err_str[] = {
86 	"Success",
87 	"Failed - Internal Error",
88 	"Failed - Invalid Command or Request",
89 	"Failed - Request Rejected by Daemon",
90 	"Failed - BMC Hardware Error",
91 	"Failed - Insufficient Memory for Allocation Request"
92 };
93 
94 static int init_mboxctl_dbus(struct mboxctl_context *context)
95 {
96 	int rc;
97 
98 	rc = sd_bus_default_system(&context->bus);
99 	if (rc < 0) {
100 		MSG_ERR("Failed to connect to the system bus: %s\n",
101 			strerror(-rc));
102 	}
103 
104 	return rc;
105 }
106 
107 static int send_dbus_msg(struct mboxctl_context *context,
108 			 struct mbox_dbus_msg *msg,
109 			 struct mbox_dbus_msg *resp)
110 {
111 	sd_bus_error error = SD_BUS_ERROR_NULL;
112 	sd_bus_message *m = NULL, *n = NULL;
113 	uint8_t *buf;
114 	size_t sz;
115 	int rc;
116 
117 	/* Generate the bus message */
118 	rc = sd_bus_message_new_method_call(context->bus, &m, DBUS_NAME,
119 					    DOBJ_NAME, DBUS_NAME, "cmd");
120 	if (rc < 0) {
121 		MSG_ERR("Failed to init method call: %s\n",
122 			strerror(-rc));
123 		rc = -E_DBUS_INTERNAL;
124 		goto out;
125 	}
126 
127 	/* Add the command */
128 	rc = sd_bus_message_append(m, "y", msg->cmd);
129 	if (rc < 0) {
130 		MSG_ERR("Failed to add cmd to message: %s\n",
131 			strerror(-rc));
132 		rc = -E_DBUS_INTERNAL;
133 		goto out;
134 	}
135 
136 	/* Add the args */
137 	rc = sd_bus_message_append_array(m, 'y', msg->args, msg->num_args);
138 	if (rc < 0) {
139 		MSG_ERR("Failed to add args to message: %s\n",
140 			strerror(-rc));
141 		rc = -E_DBUS_INTERNAL;
142 		goto out;
143 	}
144 
145 	/* Send the message */
146 	rc = sd_bus_call(context->bus, m, 0, &error, &n);
147 	if (rc < 0) {
148 		MSG_ERR("Failed to post message: %s\n", strerror(-rc));
149 		rc = -E_DBUS_INTERNAL;
150 		goto out;
151 	}
152 
153 	/* Read response code */
154 	rc = sd_bus_message_read(n, "y", &resp->cmd);
155 	if (rc < 0) {
156 		MSG_ERR("Failed to read response code: %s\n",
157 			strerror(-rc));
158 		rc = -E_DBUS_INTERNAL;
159 		goto out;
160 	}
161 
162 	/* Read response args */
163 	rc = sd_bus_message_read_array(n, 'y', (const void **) &buf, &sz);
164 	if (rc < 0) {
165 		MSG_ERR("Failed to read response args: %s\n",
166 			strerror(-rc));
167 		rc = -E_DBUS_INTERNAL;
168 		goto out;
169 	}
170 
171 	if (sz < resp->num_args) {
172 		MSG_ERR("Command returned insufficient response args\n");
173 		rc = -E_DBUS_INTERNAL;
174 		goto out;
175 	}
176 
177 	memcpy(resp->args, buf, resp->num_args);
178 	rc = 0;
179 
180 out:
181 	sd_bus_error_free(&error);
182 	sd_bus_message_unref(m);
183 	sd_bus_message_unref(n);
184 
185 	return rc;
186 }
187 
188 static int handle_cmd_ping(struct mboxctl_context *context)
189 {
190 	struct mbox_dbus_msg msg = { 0 }, resp = { 0 };
191 	int rc;
192 
193 	msg.cmd = DBUS_C_PING;
194 
195 	rc = send_dbus_msg(context, &msg, &resp);
196 	if (rc < 0) {
197 		MSG_ERR("Failed to send ping command\n");
198 		return rc;
199 	}
200 
201 	rc = -resp.cmd;
202 	MSG_OUT("Ping: %s\n", dbus_err_str[-rc]);
203 
204 	return rc;
205 }
206 
207 static int handle_cmd_daemon_state(struct mboxctl_context *context)
208 {
209 	struct mbox_dbus_msg msg = { 0 }, resp = { 0 };
210 	int rc;
211 
212 	msg.cmd = DBUS_C_DAEMON_STATE;
213 	resp.num_args = DAEMON_STATE_NUM_ARGS;
214 	resp.args = calloc(resp.num_args, sizeof(*resp.args));
215 	if (!resp.args) {
216 		MSG_ERR("Memory allocation failed\n");
217 		return -E_DBUS_NO_MEM;
218 	}
219 
220 	rc = send_dbus_msg(context, &msg, &resp);
221 	if (rc < 0) {
222 		MSG_ERR("Failed to send daemon state command\n");
223 		goto out;
224 	}
225 
226 	rc = -resp.cmd;
227 	if (resp.cmd != DBUS_SUCCESS) {
228 		MSG_ERR("Daemon state command failed\n");
229 		goto out;
230 	}
231 
232 	MSG_OUT("Daemon State: %s\n", resp.args[0] == DAEMON_STATE_ACTIVE ?
233 				      "Active" : "Suspended");
234 
235 out:
236 	free(resp.args);
237 	return rc;
238 }
239 
240 static int handle_cmd_lpc_state(struct mboxctl_context *context)
241 {
242 	struct mbox_dbus_msg msg = { 0 }, resp = { 0 };
243 	int rc;
244 
245 	msg.cmd = DBUS_C_LPC_STATE;
246 	resp.num_args = LPC_STATE_NUM_ARGS;
247 	resp.args = calloc(resp.num_args, sizeof(*resp.args));
248 	if (!resp.args) {
249 		MSG_ERR("Memory allocation failed\n");
250 		return -E_DBUS_NO_MEM;
251 	}
252 
253 	rc = send_dbus_msg(context, &msg, &resp);
254 	if (rc < 0) {
255 		MSG_ERR("Failed to send lpc state command\n");
256 		goto out;
257 	}
258 
259 	rc = -resp.cmd;
260 	if (resp.cmd != DBUS_SUCCESS) {
261 		MSG_ERR("LPC state command failed\n");
262 		goto out;
263 	}
264 
265 	MSG_OUT("LPC Bus Maps: %s\n", resp.args[0] == LPC_STATE_MEM ?
266 				      "BMC Memory" :
267 				      (resp.args[0] == LPC_STATE_FLASH ?
268 				       "Flash Device" :
269 				       "Invalid System State"));
270 
271 out:
272 	free(resp.args);
273 	return rc;
274 }
275 
276 static int handle_cmd_kill(struct mboxctl_context *context)
277 {
278 	struct mbox_dbus_msg msg = { 0 }, resp = { 0 };
279 	int rc;
280 
281 	msg.cmd = DBUS_C_KILL;
282 
283 	rc = send_dbus_msg(context, &msg, &resp);
284 	if (rc < 0) {
285 		MSG_ERR("Failed to send kill command\n");
286 		return rc;
287 	}
288 
289 	rc = -resp.cmd;
290 	MSG_OUT("Kill: %s\n", dbus_err_str[-rc]);
291 
292 	return rc;
293 }
294 
295 static int handle_cmd_reset(struct mboxctl_context *context)
296 {
297 	struct mbox_dbus_msg msg = { 0 }, resp = { 0 };
298 	int rc;
299 
300 	msg.cmd = DBUS_C_RESET;
301 
302 	rc = send_dbus_msg(context, &msg, &resp);
303 	if (rc < 0) {
304 		MSG_ERR("Failed to send reset command\n");
305 		return rc;
306 	}
307 
308 	rc = -resp.cmd;
309 	MSG_OUT("Reset: %s\n", dbus_err_str[-rc]);
310 
311 	return rc;
312 }
313 
314 static int handle_cmd_suspend(struct mboxctl_context *context)
315 {
316 	struct mbox_dbus_msg msg = { 0 }, resp = { 0 };
317 	int rc;
318 
319 	msg.cmd = DBUS_C_SUSPEND;
320 
321 	rc = send_dbus_msg(context, &msg, &resp);
322 	if (rc < 0) {
323 		MSG_ERR("Failed to send suspend command\n");
324 		return rc;
325 	}
326 
327 	rc = -resp.cmd;
328 	MSG_OUT("Suspend: %s\n", dbus_err_str[-rc]);
329 
330 	return rc;
331 }
332 
333 static int handle_cmd_resume(struct mboxctl_context *context, char *arg)
334 {
335 	struct mbox_dbus_msg msg = { 0 }, resp = { 0 };
336 	int rc;
337 
338 	if (!arg) {
339 		MSG_ERR("Resume command takes an argument\n");
340 		return -E_DBUS_INVAL;
341 	}
342 
343 	msg.cmd = DBUS_C_RESUME;
344 	msg.num_args = RESUME_NUM_ARGS;
345 	msg.args = calloc(msg.num_args, sizeof(*msg.args));
346 	if (!msg.args) {
347 		MSG_ERR("Memory allocation failed\n");
348 		return -E_DBUS_NO_MEM;
349 	}
350 
351 	if (!strncmp(arg, "clean", strlen("clean"))) {
352 		msg.args[0] = RESUME_NOT_MODIFIED;
353 	} else if (!strncmp(arg, "modified", strlen("modified"))) {
354 		msg.args[0] = RESUME_FLASH_MODIFIED;
355 	} else {
356 		MSG_ERR("Resume command takes argument < \"clean\" | "
357 			"\"modified\" >\n");
358 		rc = -E_DBUS_INVAL;
359 		goto out;
360 	}
361 
362 	rc = send_dbus_msg(context, &msg, &resp);
363 	if (rc < 0) {
364 		MSG_ERR("Failed to send resume command\n");
365 		goto out;
366 	}
367 
368 	rc = -resp.cmd;
369 	MSG_OUT("Resume: %s\n", dbus_err_str[-rc]);
370 
371 out:
372 	free(msg.args);
373 	return rc;
374 }
375 
376 static int handle_cmd_modified(struct mboxctl_context *context)
377 {
378 	struct mbox_dbus_msg msg = { 0 }, resp = { 0 };
379 	int rc;
380 
381 	msg.cmd = DBUS_C_MODIFIED;
382 
383 	rc = send_dbus_msg(context, &msg, &resp);
384 	if (rc < 0) {
385 		MSG_ERR("Failed to send flash modified command\n");
386 		return rc;
387 	}
388 
389 	rc = -resp.cmd;
390 	MSG_OUT("Clear Cache: %s\n", dbus_err_str[-rc]);
391 
392 	return rc;
393 }
394 
395 static int parse_cmdline(struct mboxctl_context *context, int argc, char **argv)
396 {
397 	int opt, rc = -1;
398 
399 	static const struct option long_options[] = {
400 		{ "silent",		no_argument,		0, 's' },
401 		{ "ping",		no_argument,		0, 'p' },
402 		{ "daemon-state",	no_argument,		0, 'd' },
403 		{ "lpc-state",		no_argument,		0, 'l' },
404 		{ "kill",		no_argument,		0, 'k' },
405 		{ "reset",		no_argument,		0, 'r' },
406 		{ "point-to-flash",	no_argument,		0, 'f' },
407 		{ "suspend",		no_argument,		0, 'u' },
408 		{ "resume",		required_argument,	0, 'e' },
409 		{ "clear-cache",	no_argument,		0, 'c' },
410 		{ "version",		no_argument,		0, 'v' },
411 		{ "help",		no_argument,		0, 'h' },
412 		{ 0,			0,			0, 0   }
413 	};
414 
415 	if (argc <= 1) {
416 		usage(argv[0]);
417 		return -E_DBUS_INVAL;
418 	}
419 
420 	while ((opt = getopt_long(argc, argv, "spdlkrfue:cvh", long_options,
421 				  NULL)) != -1) {
422 		switch (opt) {
423 		case 's':
424 			silent = true;
425 			continue;
426 		case 'p':
427 			rc = handle_cmd_ping(context);
428 			break;
429 		case 'd':
430 			rc = handle_cmd_daemon_state(context);
431 			break;
432 		case 'l':
433 			rc = handle_cmd_lpc_state(context);
434 			break;
435 		case 'k':
436 			rc = handle_cmd_kill(context);
437 			break;
438 		case 'r': /* These are the same for now (reset may change) */
439 		case 'f':
440 			rc = handle_cmd_reset(context);
441 			break;
442 		case 'u':
443 			rc = handle_cmd_suspend(context);
444 			break;
445 		case 'e':
446 			rc = handle_cmd_resume(context, optarg);
447 			break;
448 		case 'c':
449 			rc = handle_cmd_modified(context);
450 			break;
451 		case 'v':
452 			MSG_OUT("%s V%d.%.2d\n", NAME, VERSION, SUBVERSION);
453 			rc = 0;
454 			break;
455 		case 'h':
456 			usage(argv[0]);
457 			rc = 0;
458 			break;
459 		default:
460 			usage(argv[0]);
461 			rc = -E_DBUS_INVAL;
462 			break;
463 		}
464 	}
465 
466 	return rc;
467 }
468 
469 int main(int argc, char **argv)
470 {
471 	struct mboxctl_context context;
472 	int rc;
473 
474 	silent = false;
475 
476 	rc = init_mboxctl_dbus(&context);
477 	if (rc < 0) {
478 		MSG_ERR("Failed to init dbus\n");
479 		return rc;
480 	}
481 
482 	rc = parse_cmdline(&context, argc, argv);
483 
484 	return rc;
485 }
486