blob: ca6d9ad8990e1263c8863f318c2783dcc4c6364c [file] [log] [blame]
Suraj Jitindar Singhddf0edb2017-03-28 10:50:40 +11001/*
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
66static 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
75struct mboxctl_context {
76 sd_bus *bus;
77};
78
79static void usage(char *name)
80{
81 MSG_OUT(USAGE, name);
82 exit(0);
83}
84
85static 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
94static 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
107static 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
180out:
181 sd_bus_error_free(&error);
182 sd_bus_message_unref(m);
183 sd_bus_message_unref(n);
184
185 return rc;
186}
187
188static 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
207static 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
235out:
236 free(resp.args);
237 return rc;
238}
239
240static 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
271out:
272 free(resp.args);
273 return rc;
274}
275
276static 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
295static 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
314static 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
333static 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
371out:
372 free(msg.args);
373 return rc;
374}
375
376static 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
395static 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
469int 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}