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