blob: c8ee4ba0e44e68b1bbed9ec422b8bcfc9eaa9f3f [file] [log] [blame]
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +09301// SPDX-License-Identifier: Apache-2.0
2// Copyright (C) 2018 IBM Corp.
3
4#include "config.h"
5
6#include "hiomap.hpp"
7
8#include <endian.h>
9#include <host-ipmid/ipmid-api.h>
10#include <string.h>
11#include <systemd/sd-bus.h>
12
13#include <fstream>
Andrew Jeffery0a3358e2018-08-21 10:42:09 +093014#include <functional>
15#include <host-ipmid/ipmid-host-cmd-utils.hpp>
16#include <host-ipmid/ipmid-host-cmd.hpp>
17#include <iostream>
18#include <phosphor-logging/log.hpp>
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +093019#include <sdbusplus/bus.hpp>
Andrew Jeffery0a3358e2018-08-21 10:42:09 +093020#include <sdbusplus/bus/match.hpp>
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +093021#include <sdbusplus/exception.hpp>
22
23using namespace sdbusplus;
Andrew Jeffery0a3358e2018-08-21 10:42:09 +093024using namespace phosphor::host::command;
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +093025
26static void register_openpower_hiomap_commands() __attribute__((constructor));
27
28namespace openpower
29{
30namespace flash
31{
Andrew Jeffery0a3358e2018-08-21 10:42:09 +093032constexpr auto BMC_EVENT_DAEMON_READY = 1 << 7;
33constexpr auto BMC_EVENT_FLASH_CTRL_LOST = 1 << 6;
34constexpr auto BMC_EVENT_WINDOW_RESET = 1 << 1;
35constexpr auto BMC_EVENT_PROTOCOL_RESET = 1 << 0;
36
37constexpr auto IPMI_CMD_HIOMAP_EVENT = 0x0f;
38
39constexpr auto HIOMAPD_SERVICE = "xyz.openbmc_project.Hiomapd";
40constexpr auto HIOMAPD_OBJECT = "/xyz/openbmc_project/Hiomapd";
41constexpr auto HIOMAPD_IFACE = "xyz.openbmc_project.Hiomapd.Protocol";
42constexpr auto HIOMAPD_IFACE_V2 = "xyz.openbmc_project.Hiomapd.Protocol.V2";
43
44constexpr auto DBUS_IFACE_PROPERTIES = "org.freedesktop.DBus.Properties";
45
46struct hiomap
47{
48 bus::bus *bus;
49
50 /* Signals */
51 bus::match::match *properties;
52 bus::match::match *window_reset;
53 bus::match::match *bmc_reboot;
54
55 /* Protocol state */
56 std::map<std::string, int> event_lookup;
57 uint8_t bmc_events;
58};
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +093059
60/* TODO: Replace get/put with packed structs and direct assignment */
61template <typename T> static inline T get(void *buf)
62{
63 T t;
64 memcpy(&t, buf, sizeof(t));
65 return t;
66}
67
68template <typename T> static inline void put(void *buf, T &&t)
69{
70 memcpy(buf, &t, sizeof(t));
71}
72
73typedef ipmi_ret_t (*hiomap_command)(ipmi_request_t req, ipmi_response_t resp,
74 ipmi_data_len_t data_len,
75 ipmi_context_t context);
76
77struct errno_cc_entry
78{
79 int err;
80 int cc;
81};
82
83static const errno_cc_entry errno_cc_map[] = {
84 {0, IPMI_CC_OK},
85 {EBUSY, IPMI_CC_BUSY},
86 {ENOTSUP, IPMI_CC_INVALID},
87 {ETIMEDOUT, 0xc3}, /* FIXME: Replace when defined in ipmid-api.h */
88 {ENOSPC, 0xc4}, /* FIXME: Replace when defined in ipmid-api.h */
89 {EINVAL, IPMI_CC_PARM_OUT_OF_RANGE},
90 {ENODEV, IPMI_CC_SENSOR_INVALID},
91 {EPERM, IPMI_CC_INSUFFICIENT_PRIVILEGE},
92 {EACCES, IPMI_CC_INSUFFICIENT_PRIVILEGE},
93 {-1, IPMI_CC_UNSPECIFIED_ERROR},
94};
95
96static int hiomap_xlate_errno(int err)
97{
98 const errno_cc_entry *entry = &errno_cc_map[0];
99
100 while (!(entry->err == err || entry->err == -1))
101 {
102 entry++;
103 }
104
105 return entry->cc;
106}
107
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930108static void ipmi_hiomap_event_response(IpmiCmdData cmd, bool status)
109{
110 using namespace phosphor::logging;
111
112 if (!status)
113 {
114 log<level::ERR>("Failed to deliver host command",
115 entry("SEL_COMMAND=%x:%x", cmd.first, cmd.second));
116 }
117}
118
119static int hiomap_handle_property_update(struct hiomap *ctx,
120 sdbusplus::message::message &msg)
121{
122 std::map<std::string, sdbusplus::message::variant<bool>> msgData;
123
124 std::string iface;
125 msg.read(iface, msgData);
126
127 for (auto const &x : msgData)
128 {
129 if (!ctx->event_lookup.count(x.first))
130 {
131 /* Unsupported event? */
132 continue;
133 }
134
135 uint8_t mask = ctx->event_lookup[x.first];
136 auto value = sdbusplus::message::variant_ns::get<bool>(x.second);
137
138 if (value)
139 {
140 ctx->bmc_events |= mask;
141 }
142 else
143 {
144 ctx->bmc_events &= ~mask;
145 }
146 }
147
148 auto cmd = std::make_pair(IPMI_CMD_HIOMAP_EVENT, ctx->bmc_events);
149
150 ipmid_send_cmd_to_host(std::make_tuple(cmd, ipmi_hiomap_event_response));
151
152 return 0;
153}
154
155static bus::match::match hiomap_match_properties(struct hiomap *ctx)
156{
157 auto properties =
158 bus::match::rules::propertiesChanged(HIOMAPD_OBJECT, HIOMAPD_IFACE_V2);
159
160 bus::match::match match(
161 *ctx->bus, properties,
162 std::bind(hiomap_handle_property_update, ctx, std::placeholders::_1));
163
164 return match;
165}
166
167static int hiomap_handle_signal_v2(struct hiomap *ctx, const char *name)
168{
169 ctx->bmc_events |= ctx->event_lookup[name];
170
171 auto cmd = std::make_pair(IPMI_CMD_HIOMAP_EVENT, ctx->bmc_events);
172
173 ipmid_send_cmd_to_host(std::make_tuple(cmd, ipmi_hiomap_event_response));
174
175 return 0;
176}
177
178static bus::match::match hiomap_match_signal_v2(struct hiomap *ctx,
179 const char *name)
180{
181 using namespace bus::match;
182
183 auto signals = rules::type::signal() + rules::path(HIOMAPD_OBJECT) +
184 rules::interface(HIOMAPD_IFACE_V2) + rules::member(name);
185
186 bus::match::match match(*ctx->bus, signals,
187 std::bind(hiomap_handle_signal_v2, ctx, name));
188
189 return match;
190}
191
192static ipmi_ret_t hiomap_reset(ipmi_request_t request, ipmi_response_t response,
193 ipmi_data_len_t data_len, ipmi_context_t context)
194{
195 struct hiomap *ctx = static_cast<struct hiomap *>(context);
196
197 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
198 HIOMAPD_IFACE, "Reset");
199 try
200 {
201 ctx->bus->call(m);
202
203 *data_len = 0;
204 }
205 catch (const exception::SdBusError &e)
206 {
207 return hiomap_xlate_errno(e.get_errno());
208 }
209
210 return IPMI_CC_OK;
211}
212
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930213static ipmi_ret_t hiomap_get_info(ipmi_request_t request,
214 ipmi_response_t response,
215 ipmi_data_len_t data_len,
216 ipmi_context_t context)
217{
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930218 struct hiomap *ctx = static_cast<struct hiomap *>(context);
219
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930220 if (*data_len < 1)
221 {
222 return IPMI_CC_REQ_DATA_LEN_INVALID;
223 }
224
225 uint8_t *reqdata = (uint8_t *)request;
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930226 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
227 HIOMAPD_IFACE, "GetInfo");
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930228 m.append(reqdata[0]);
229
230 try
231 {
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930232 auto reply = ctx->bus->call(m);
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930233
234 uint8_t version;
235 uint8_t blockSizeShift;
236 uint16_t timeout;
237 reply.read(version, blockSizeShift, timeout);
238
239 uint8_t *respdata = (uint8_t *)response;
240
241 /* FIXME: Assumes v2! */
242 put(&respdata[0], version);
243 put(&respdata[1], blockSizeShift);
244 put(&respdata[2], htole16(timeout));
245
246 *data_len = 4;
247 }
248 catch (const exception::SdBusError &e)
249 {
250 return hiomap_xlate_errno(e.get_errno());
251 }
252
253 return IPMI_CC_OK;
254}
255
Andrew Jefferydb688e92018-08-23 21:21:30 +0930256static ipmi_ret_t hiomap_get_flash_info(ipmi_request_t request,
257 ipmi_response_t response,
258 ipmi_data_len_t data_len,
259 ipmi_context_t context)
260{
261 struct hiomap *ctx = static_cast<struct hiomap *>(context);
262
263 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
264 HIOMAPD_IFACE_V2, "GetFlashInfo");
265 try
266 {
267 auto reply = ctx->bus->call(m);
268
269 uint16_t flashSize, eraseSize;
270 reply.read(flashSize, eraseSize);
271
272 uint8_t *respdata = (uint8_t *)response;
273 put(&respdata[0], htole16(flashSize));
274 put(&respdata[2], htole16(eraseSize));
275
276 *data_len = 4;
277 }
278 catch (const exception::SdBusError &e)
279 {
280 return hiomap_xlate_errno(e.get_errno());
281 }
282
283 return IPMI_CC_OK;
284}
285
Andrew Jefferya00f59b2018-08-23 22:25:49 +0930286static ipmi_ret_t hiomap_create_window(struct hiomap *ctx, bool ro,
287 ipmi_request_t request,
288 ipmi_response_t response,
289 ipmi_data_len_t data_len)
290{
291 if (*data_len < 4)
292 {
293 return IPMI_CC_REQ_DATA_LEN_INVALID;
294 }
295
296 uint8_t *reqdata = (uint8_t *)request;
297 auto windowType = ro ? "CreateReadWindow" : "CreateWriteWindow";
298
299 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
300 HIOMAPD_IFACE_V2, windowType);
301 m.append(le16toh(get<uint16_t>(&reqdata[0])));
302 m.append(le16toh(get<uint16_t>(&reqdata[2])));
303
304 try
305 {
306 auto reply = ctx->bus->call(m);
307
308 uint16_t lpcAddress, size, offset;
309 reply.read(lpcAddress, size, offset);
310
311 uint8_t *respdata = (uint8_t *)response;
312
313 /* FIXME: Assumes v2! */
314 put(&respdata[0], htole16(lpcAddress));
315 put(&respdata[2], htole16(size));
316 put(&respdata[4], htole16(offset));
317
318 *data_len = 6;
319 }
320 catch (const exception::SdBusError &e)
321 {
322 return hiomap_xlate_errno(e.get_errno());
323 }
324
325 return IPMI_CC_OK;
326}
327
328static ipmi_ret_t hiomap_create_read_window(ipmi_request_t request,
329 ipmi_response_t response,
330 ipmi_data_len_t data_len,
331 ipmi_context_t context)
332{
333 struct hiomap *ctx = static_cast<struct hiomap *>(context);
334
335 return hiomap_create_window(ctx, true, request, response, data_len);
336}
337
338static ipmi_ret_t hiomap_create_write_window(ipmi_request_t request,
339 ipmi_response_t response,
340 ipmi_data_len_t data_len,
341 ipmi_context_t context)
342{
343 struct hiomap *ctx = static_cast<struct hiomap *>(context);
344
345 return hiomap_create_window(ctx, false, request, response, data_len);
346}
347
Andrew Jefferyb52822c2018-08-23 23:01:39 +0930348static ipmi_ret_t hiomap_close_window(ipmi_request_t request,
349 ipmi_response_t response,
350 ipmi_data_len_t data_len,
351 ipmi_context_t context)
352{
353 struct hiomap *ctx = static_cast<struct hiomap *>(context);
354
355 if (*data_len < 1)
356 {
357 return IPMI_CC_REQ_DATA_LEN_INVALID;
358 }
359
360 uint8_t *reqdata = (uint8_t *)request;
361 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
362 HIOMAPD_IFACE_V2, "CloseWindow");
363 m.append(reqdata[0]);
364
365 try
366 {
367 auto reply = ctx->bus->call(m);
368
369 *data_len = 0;
370 }
371 catch (const exception::SdBusError &e)
372 {
373 return hiomap_xlate_errno(e.get_errno());
374 }
375
376 return IPMI_CC_OK;
377}
378
Andrew Jeffery9847f1c2018-08-24 09:13:40 +0930379static ipmi_ret_t hiomap_mark_dirty(ipmi_request_t request,
380 ipmi_response_t response,
381 ipmi_data_len_t data_len,
382 ipmi_context_t context)
383{
384 struct hiomap *ctx = static_cast<struct hiomap *>(context);
385
386 if (*data_len < 4)
387 {
388 return IPMI_CC_REQ_DATA_LEN_INVALID;
389 }
390
391 uint8_t *reqdata = (uint8_t *)request;
392 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
393 HIOMAPD_IFACE_V2, "MarkDirty");
394 /* FIXME: Assumes v2 */
395 m.append(le16toh(get<uint16_t>(&reqdata[0]))); /* offset */
396 m.append(le16toh(get<uint16_t>(&reqdata[2]))); /* size */
397
398 try
399 {
400 auto reply = ctx->bus->call(m);
401
402 *data_len = 0;
403 }
404 catch (const exception::SdBusError &e)
405 {
406 return hiomap_xlate_errno(e.get_errno());
407 }
408
409 return IPMI_CC_OK;
410}
411
Andrew Jeffery7b225fb2018-08-24 09:19:21 +0930412static ipmi_ret_t hiomap_flush(ipmi_request_t request, ipmi_response_t response,
413 ipmi_data_len_t data_len, ipmi_context_t context)
414{
415 struct hiomap *ctx = static_cast<struct hiomap *>(context);
416
417 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
418 HIOMAPD_IFACE_V2, "Flush");
419
420 try
421 {
422 /* FIXME: No argument call assumes v2 */
423 auto reply = ctx->bus->call(m);
424
425 *data_len = 0;
426 }
427 catch (const exception::SdBusError &e)
428 {
429 return hiomap_xlate_errno(e.get_errno());
430 }
431
432 return IPMI_CC_OK;
433}
434
Andrew Jeffery99f277a2018-08-24 09:24:04 +0930435static ipmi_ret_t hiomap_ack(ipmi_request_t request, ipmi_response_t response,
436 ipmi_data_len_t data_len, ipmi_context_t context)
437{
438 struct hiomap *ctx = static_cast<struct hiomap *>(context);
439
440 if (*data_len < 1)
441 {
442 return IPMI_CC_REQ_DATA_LEN_INVALID;
443 }
444
445 uint8_t *reqdata = (uint8_t *)request;
446 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
447 HIOMAPD_IFACE_V2, "Ack");
448 auto acked = reqdata[0];
449 m.append(acked);
450
451 try
452 {
453 auto reply = ctx->bus->call(m);
454
455 /* Update our cache: Necessary because the signals do not carry a value
456 */
457 ctx->bmc_events &= ~acked;
458
459 *data_len = 0;
460 }
461 catch (const exception::SdBusError &e)
462 {
463 return hiomap_xlate_errno(e.get_errno());
464 }
465
466 return IPMI_CC_OK;
467}
468
Andrew Jefferya1e35b82018-08-24 09:39:10 +0930469static ipmi_ret_t hiomap_erase(ipmi_request_t request, ipmi_response_t response,
470 ipmi_data_len_t data_len, ipmi_context_t context)
471{
472 struct hiomap *ctx = static_cast<struct hiomap *>(context);
473
474 if (*data_len < 4)
475 {
476 return IPMI_CC_REQ_DATA_LEN_INVALID;
477 }
478
479 uint8_t *reqdata = (uint8_t *)request;
480 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
481 HIOMAPD_IFACE_V2, "Erase");
482 /* FIXME: Assumes v2 */
483 m.append(le16toh(get<uint16_t>(&reqdata[0]))); /* offset */
484 m.append(le16toh(get<uint16_t>(&reqdata[2]))); /* size */
485
486 try
487 {
488 auto reply = ctx->bus->call(m);
489
490 *data_len = 0;
491 }
492 catch (const exception::SdBusError &e)
493 {
494 return hiomap_xlate_errno(e.get_errno());
495 }
496
497 return IPMI_CC_OK;
498}
499
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930500static const hiomap_command hiomap_commands[] = {
501 [0] = NULL, /* 0 is an invalid command ID */
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930502 [1] = hiomap_reset,
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930503 [2] = hiomap_get_info,
Andrew Jefferydb688e92018-08-23 21:21:30 +0930504 [3] = hiomap_get_flash_info,
Andrew Jefferya00f59b2018-08-23 22:25:49 +0930505 [4] = hiomap_create_read_window,
Andrew Jefferyb52822c2018-08-23 23:01:39 +0930506 [5] = hiomap_close_window,
Andrew Jefferya00f59b2018-08-23 22:25:49 +0930507 [6] = hiomap_create_write_window,
Andrew Jeffery9847f1c2018-08-24 09:13:40 +0930508 [7] = hiomap_mark_dirty,
Andrew Jeffery7b225fb2018-08-24 09:19:21 +0930509 [8] = hiomap_flush,
Andrew Jeffery99f277a2018-08-24 09:24:04 +0930510 [9] = hiomap_ack,
Andrew Jefferya1e35b82018-08-24 09:39:10 +0930511 [10] = hiomap_erase,
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930512};
513
514/* FIXME: Define this in the "right" place, wherever that is */
515/* FIXME: Double evaluation */
516#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
517
518static ipmi_ret_t hiomap_dispatch(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
519 ipmi_request_t request,
520 ipmi_response_t response,
521 ipmi_data_len_t data_len,
522 ipmi_context_t context)
523{
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930524 struct hiomap *ctx = static_cast<struct hiomap *>(context);
525
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930526 if (*data_len < 2)
527 {
528 *data_len = 0;
529 return IPMI_CC_REQ_DATA_LEN_INVALID;
530 }
531
532 uint8_t *ipmi_req = (uint8_t *)request;
533 uint8_t *ipmi_resp = (uint8_t *)response;
534 uint8_t hiomap_cmd = ipmi_req[0];
535
536 if (hiomap_cmd == 0 || hiomap_cmd > ARRAY_SIZE(hiomap_commands) - 1)
537 {
538 *data_len = 0;
539 return IPMI_CC_PARM_OUT_OF_RANGE;
540 }
541 uint8_t *flash_req = ipmi_req + 2;
542 size_t flash_len = *data_len - 2;
543 uint8_t *flash_resp = ipmi_resp + 2;
544
545 ipmi_ret_t cc =
546 hiomap_commands[hiomap_cmd](flash_req, flash_resp, &flash_len, context);
547 if (cc != IPMI_CC_OK)
548 {
549 *data_len = 0;
550 return cc;
551 }
552
553 /* Populate the response command and sequence */
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930554 ipmi_resp[0] = hiomap_cmd;
555 ipmi_resp[1] = ipmi_req[1];
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930556
557 *data_len = flash_len + 2;
558
559 return cc;
560}
561} // namespace flash
562} // namespace openpower
563
564static void register_openpower_hiomap_commands()
565{
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930566 using namespace openpower::flash;
567
568 /* FIXME: Clean this up? Can we unregister? */
569 struct hiomap *ctx = new hiomap();
570
571 /* Initialise mapping from signal and property names to status bit */
572 ctx->event_lookup["DaemonReady"] = BMC_EVENT_DAEMON_READY;
573 ctx->event_lookup["FlashControlLost"] = BMC_EVENT_FLASH_CTRL_LOST;
574 ctx->event_lookup["WindowReset"] = BMC_EVENT_WINDOW_RESET;
575 ctx->event_lookup["ProtocolReset"] = BMC_EVENT_PROTOCOL_RESET;
576
577 ctx->bus = new bus::bus(ipmid_get_sd_bus_connection());
578
579 /* Initialise signal handling */
580
581 /*
582 * Can't use temporaries here because that causes SEGFAULTs due to slot
583 * destruction (!?), so enjoy the weird wrapping.
584 */
585 ctx->properties =
586 new bus::match::match(std::move(hiomap_match_properties(ctx)));
587 ctx->bmc_reboot = new bus::match::match(
588 std::move(hiomap_match_signal_v2(ctx, "ProtocolReset")));
589 ctx->window_reset = new bus::match::match(
590 std::move(hiomap_match_signal_v2(ctx, "WindowReset")));
591
592 ipmi_register_callback(NETFUN_IBM_OEM, IPMI_CMD_HIOMAP, ctx,
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930593 openpower::flash::hiomap_dispatch, SYSTEM_INTERFACE);
594}