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