blob: eb72ca4f8f0c56f5c947d247cbc2b34147d37468 [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 Jeffery2c07f6f2018-08-10 16:24:32 +0930348static const hiomap_command hiomap_commands[] = {
349 [0] = NULL, /* 0 is an invalid command ID */
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930350 [1] = hiomap_reset,
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930351 [2] = hiomap_get_info,
Andrew Jefferydb688e92018-08-23 21:21:30 +0930352 [3] = hiomap_get_flash_info,
Andrew Jefferya00f59b2018-08-23 22:25:49 +0930353 [4] = hiomap_create_read_window,
354 [5] = NULL, /* CLOSE_WINDOW */
355 [6] = hiomap_create_write_window,
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930356};
357
358/* FIXME: Define this in the "right" place, wherever that is */
359/* FIXME: Double evaluation */
360#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
361
362static ipmi_ret_t hiomap_dispatch(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
363 ipmi_request_t request,
364 ipmi_response_t response,
365 ipmi_data_len_t data_len,
366 ipmi_context_t context)
367{
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930368 struct hiomap *ctx = static_cast<struct hiomap *>(context);
369
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930370 if (*data_len < 2)
371 {
372 *data_len = 0;
373 return IPMI_CC_REQ_DATA_LEN_INVALID;
374 }
375
376 uint8_t *ipmi_req = (uint8_t *)request;
377 uint8_t *ipmi_resp = (uint8_t *)response;
378 uint8_t hiomap_cmd = ipmi_req[0];
379
380 if (hiomap_cmd == 0 || hiomap_cmd > ARRAY_SIZE(hiomap_commands) - 1)
381 {
382 *data_len = 0;
383 return IPMI_CC_PARM_OUT_OF_RANGE;
384 }
385 uint8_t *flash_req = ipmi_req + 2;
386 size_t flash_len = *data_len - 2;
387 uint8_t *flash_resp = ipmi_resp + 2;
388
389 ipmi_ret_t cc =
390 hiomap_commands[hiomap_cmd](flash_req, flash_resp, &flash_len, context);
391 if (cc != IPMI_CC_OK)
392 {
393 *data_len = 0;
394 return cc;
395 }
396
397 /* Populate the response command and sequence */
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930398 ipmi_resp[0] = hiomap_cmd;
399 ipmi_resp[1] = ipmi_req[1];
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930400
401 *data_len = flash_len + 2;
402
403 return cc;
404}
405} // namespace flash
406} // namespace openpower
407
408static void register_openpower_hiomap_commands()
409{
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930410 using namespace openpower::flash;
411
412 /* FIXME: Clean this up? Can we unregister? */
413 struct hiomap *ctx = new hiomap();
414
415 /* Initialise mapping from signal and property names to status bit */
416 ctx->event_lookup["DaemonReady"] = BMC_EVENT_DAEMON_READY;
417 ctx->event_lookup["FlashControlLost"] = BMC_EVENT_FLASH_CTRL_LOST;
418 ctx->event_lookup["WindowReset"] = BMC_EVENT_WINDOW_RESET;
419 ctx->event_lookup["ProtocolReset"] = BMC_EVENT_PROTOCOL_RESET;
420
421 ctx->bus = new bus::bus(ipmid_get_sd_bus_connection());
422
423 /* Initialise signal handling */
424
425 /*
426 * Can't use temporaries here because that causes SEGFAULTs due to slot
427 * destruction (!?), so enjoy the weird wrapping.
428 */
429 ctx->properties =
430 new bus::match::match(std::move(hiomap_match_properties(ctx)));
431 ctx->bmc_reboot = new bus::match::match(
432 std::move(hiomap_match_signal_v2(ctx, "ProtocolReset")));
433 ctx->window_reset = new bus::match::match(
434 std::move(hiomap_match_signal_v2(ctx, "WindowReset")));
435
436 ipmi_register_callback(NETFUN_IBM_OEM, IPMI_CMD_HIOMAP, ctx,
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930437 openpower::flash::hiomap_dispatch, SYSTEM_INTERFACE);
438}