blob: 07a3afbde0791bd72942b0da1e23cdb81efc2420 [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 Jeffery2c07f6f2018-08-10 16:24:32 +0930412static const hiomap_command hiomap_commands[] = {
413 [0] = NULL, /* 0 is an invalid command ID */
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930414 [1] = hiomap_reset,
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930415 [2] = hiomap_get_info,
Andrew Jefferydb688e92018-08-23 21:21:30 +0930416 [3] = hiomap_get_flash_info,
Andrew Jefferya00f59b2018-08-23 22:25:49 +0930417 [4] = hiomap_create_read_window,
Andrew Jefferyb52822c2018-08-23 23:01:39 +0930418 [5] = hiomap_close_window,
Andrew Jefferya00f59b2018-08-23 22:25:49 +0930419 [6] = hiomap_create_write_window,
Andrew Jeffery9847f1c2018-08-24 09:13:40 +0930420 [7] = hiomap_mark_dirty,
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930421};
422
423/* FIXME: Define this in the "right" place, wherever that is */
424/* FIXME: Double evaluation */
425#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
426
427static ipmi_ret_t hiomap_dispatch(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
428 ipmi_request_t request,
429 ipmi_response_t response,
430 ipmi_data_len_t data_len,
431 ipmi_context_t context)
432{
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930433 struct hiomap *ctx = static_cast<struct hiomap *>(context);
434
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930435 if (*data_len < 2)
436 {
437 *data_len = 0;
438 return IPMI_CC_REQ_DATA_LEN_INVALID;
439 }
440
441 uint8_t *ipmi_req = (uint8_t *)request;
442 uint8_t *ipmi_resp = (uint8_t *)response;
443 uint8_t hiomap_cmd = ipmi_req[0];
444
445 if (hiomap_cmd == 0 || hiomap_cmd > ARRAY_SIZE(hiomap_commands) - 1)
446 {
447 *data_len = 0;
448 return IPMI_CC_PARM_OUT_OF_RANGE;
449 }
450 uint8_t *flash_req = ipmi_req + 2;
451 size_t flash_len = *data_len - 2;
452 uint8_t *flash_resp = ipmi_resp + 2;
453
454 ipmi_ret_t cc =
455 hiomap_commands[hiomap_cmd](flash_req, flash_resp, &flash_len, context);
456 if (cc != IPMI_CC_OK)
457 {
458 *data_len = 0;
459 return cc;
460 }
461
462 /* Populate the response command and sequence */
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930463 ipmi_resp[0] = hiomap_cmd;
464 ipmi_resp[1] = ipmi_req[1];
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930465
466 *data_len = flash_len + 2;
467
468 return cc;
469}
470} // namespace flash
471} // namespace openpower
472
473static void register_openpower_hiomap_commands()
474{
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930475 using namespace openpower::flash;
476
477 /* FIXME: Clean this up? Can we unregister? */
478 struct hiomap *ctx = new hiomap();
479
480 /* Initialise mapping from signal and property names to status bit */
481 ctx->event_lookup["DaemonReady"] = BMC_EVENT_DAEMON_READY;
482 ctx->event_lookup["FlashControlLost"] = BMC_EVENT_FLASH_CTRL_LOST;
483 ctx->event_lookup["WindowReset"] = BMC_EVENT_WINDOW_RESET;
484 ctx->event_lookup["ProtocolReset"] = BMC_EVENT_PROTOCOL_RESET;
485
486 ctx->bus = new bus::bus(ipmid_get_sd_bus_connection());
487
488 /* Initialise signal handling */
489
490 /*
491 * Can't use temporaries here because that causes SEGFAULTs due to slot
492 * destruction (!?), so enjoy the weird wrapping.
493 */
494 ctx->properties =
495 new bus::match::match(std::move(hiomap_match_properties(ctx)));
496 ctx->bmc_reboot = new bus::match::match(
497 std::move(hiomap_match_signal_v2(ctx, "ProtocolReset")));
498 ctx->window_reset = new bus::match::match(
499 std::move(hiomap_match_signal_v2(ctx, "WindowReset")));
500
501 ipmi_register_callback(NETFUN_IBM_OEM, IPMI_CMD_HIOMAP, ctx,
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930502 openpower::flash::hiomap_dispatch, SYSTEM_INTERFACE);
503}