blob: de6515e4c978d5e0c2766f44e4cbcb4ea5e36ede [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 Jeffery2c07f6f2018-08-10 16:24:32 +0930286static const hiomap_command hiomap_commands[] = {
287 [0] = NULL, /* 0 is an invalid command ID */
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930288 [1] = hiomap_reset,
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930289 [2] = hiomap_get_info,
Andrew Jefferydb688e92018-08-23 21:21:30 +0930290 [3] = hiomap_get_flash_info,
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930291};
292
293/* FIXME: Define this in the "right" place, wherever that is */
294/* FIXME: Double evaluation */
295#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
296
297static ipmi_ret_t hiomap_dispatch(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
298 ipmi_request_t request,
299 ipmi_response_t response,
300 ipmi_data_len_t data_len,
301 ipmi_context_t context)
302{
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930303 struct hiomap *ctx = static_cast<struct hiomap *>(context);
304
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930305 if (*data_len < 2)
306 {
307 *data_len = 0;
308 return IPMI_CC_REQ_DATA_LEN_INVALID;
309 }
310
311 uint8_t *ipmi_req = (uint8_t *)request;
312 uint8_t *ipmi_resp = (uint8_t *)response;
313 uint8_t hiomap_cmd = ipmi_req[0];
314
315 if (hiomap_cmd == 0 || hiomap_cmd > ARRAY_SIZE(hiomap_commands) - 1)
316 {
317 *data_len = 0;
318 return IPMI_CC_PARM_OUT_OF_RANGE;
319 }
320 uint8_t *flash_req = ipmi_req + 2;
321 size_t flash_len = *data_len - 2;
322 uint8_t *flash_resp = ipmi_resp + 2;
323
324 ipmi_ret_t cc =
325 hiomap_commands[hiomap_cmd](flash_req, flash_resp, &flash_len, context);
326 if (cc != IPMI_CC_OK)
327 {
328 *data_len = 0;
329 return cc;
330 }
331
332 /* Populate the response command and sequence */
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930333 ipmi_resp[0] = hiomap_cmd;
334 ipmi_resp[1] = ipmi_req[1];
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930335
336 *data_len = flash_len + 2;
337
338 return cc;
339}
340} // namespace flash
341} // namespace openpower
342
343static void register_openpower_hiomap_commands()
344{
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930345 using namespace openpower::flash;
346
347 /* FIXME: Clean this up? Can we unregister? */
348 struct hiomap *ctx = new hiomap();
349
350 /* Initialise mapping from signal and property names to status bit */
351 ctx->event_lookup["DaemonReady"] = BMC_EVENT_DAEMON_READY;
352 ctx->event_lookup["FlashControlLost"] = BMC_EVENT_FLASH_CTRL_LOST;
353 ctx->event_lookup["WindowReset"] = BMC_EVENT_WINDOW_RESET;
354 ctx->event_lookup["ProtocolReset"] = BMC_EVENT_PROTOCOL_RESET;
355
356 ctx->bus = new bus::bus(ipmid_get_sd_bus_connection());
357
358 /* Initialise signal handling */
359
360 /*
361 * Can't use temporaries here because that causes SEGFAULTs due to slot
362 * destruction (!?), so enjoy the weird wrapping.
363 */
364 ctx->properties =
365 new bus::match::match(std::move(hiomap_match_properties(ctx)));
366 ctx->bmc_reboot = new bus::match::match(
367 std::move(hiomap_match_signal_v2(ctx, "ProtocolReset")));
368 ctx->window_reset = new bus::match::match(
369 std::move(hiomap_match_signal_v2(ctx, "WindowReset")));
370
371 ipmi_register_callback(NETFUN_IBM_OEM, IPMI_CMD_HIOMAP, ctx,
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930372 openpower::flash::hiomap_dispatch, SYSTEM_INTERFACE);
373}