1 // 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> 14 #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> 19 #include <sdbusplus/bus.hpp> 20 #include <sdbusplus/bus/match.hpp> 21 #include <sdbusplus/exception.hpp> 22 23 using namespace sdbusplus; 24 using namespace phosphor::host::command; 25 26 static void register_openpower_hiomap_commands() __attribute__((constructor)); 27 28 namespace openpower 29 { 30 namespace flash 31 { 32 constexpr auto BMC_EVENT_DAEMON_READY = 1 << 7; 33 constexpr auto BMC_EVENT_FLASH_CTRL_LOST = 1 << 6; 34 constexpr auto BMC_EVENT_WINDOW_RESET = 1 << 1; 35 constexpr auto BMC_EVENT_PROTOCOL_RESET = 1 << 0; 36 37 constexpr auto IPMI_CMD_HIOMAP_EVENT = 0x0f; 38 39 constexpr auto HIOMAPD_SERVICE = "xyz.openbmc_project.Hiomapd"; 40 constexpr auto HIOMAPD_OBJECT = "/xyz/openbmc_project/Hiomapd"; 41 constexpr auto HIOMAPD_IFACE = "xyz.openbmc_project.Hiomapd.Protocol"; 42 constexpr auto HIOMAPD_IFACE_V2 = "xyz.openbmc_project.Hiomapd.Protocol.V2"; 43 44 constexpr auto DBUS_IFACE_PROPERTIES = "org.freedesktop.DBus.Properties"; 45 46 struct 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 }; 59 60 /* TODO: Replace get/put with packed structs and direct assignment */ 61 template <typename T> static inline T get(void *buf) 62 { 63 T t; 64 memcpy(&t, buf, sizeof(t)); 65 return t; 66 } 67 68 template <typename T> static inline void put(void *buf, T &&t) 69 { 70 memcpy(buf, &t, sizeof(t)); 71 } 72 73 typedef 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 77 struct errno_cc_entry 78 { 79 int err; 80 int cc; 81 }; 82 83 static 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 96 static 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 108 static 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 119 static 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 155 static 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 167 static 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 178 static 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 192 static 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 213 static 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 { 218 struct hiomap *ctx = static_cast<struct hiomap *>(context); 219 220 if (*data_len < 1) 221 { 222 return IPMI_CC_REQ_DATA_LEN_INVALID; 223 } 224 225 uint8_t *reqdata = (uint8_t *)request; 226 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT, 227 HIOMAPD_IFACE, "GetInfo"); 228 m.append(reqdata[0]); 229 230 try 231 { 232 auto reply = ctx->bus->call(m); 233 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 256 static 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 286 static 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 328 static 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 338 static 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 348 static 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 379 static const hiomap_command hiomap_commands[] = { 380 [0] = NULL, /* 0 is an invalid command ID */ 381 [1] = hiomap_reset, 382 [2] = hiomap_get_info, 383 [3] = hiomap_get_flash_info, 384 [4] = hiomap_create_read_window, 385 [5] = hiomap_close_window, 386 [6] = hiomap_create_write_window, 387 }; 388 389 /* FIXME: Define this in the "right" place, wherever that is */ 390 /* FIXME: Double evaluation */ 391 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 392 393 static ipmi_ret_t hiomap_dispatch(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 394 ipmi_request_t request, 395 ipmi_response_t response, 396 ipmi_data_len_t data_len, 397 ipmi_context_t context) 398 { 399 struct hiomap *ctx = static_cast<struct hiomap *>(context); 400 401 if (*data_len < 2) 402 { 403 *data_len = 0; 404 return IPMI_CC_REQ_DATA_LEN_INVALID; 405 } 406 407 uint8_t *ipmi_req = (uint8_t *)request; 408 uint8_t *ipmi_resp = (uint8_t *)response; 409 uint8_t hiomap_cmd = ipmi_req[0]; 410 411 if (hiomap_cmd == 0 || hiomap_cmd > ARRAY_SIZE(hiomap_commands) - 1) 412 { 413 *data_len = 0; 414 return IPMI_CC_PARM_OUT_OF_RANGE; 415 } 416 uint8_t *flash_req = ipmi_req + 2; 417 size_t flash_len = *data_len - 2; 418 uint8_t *flash_resp = ipmi_resp + 2; 419 420 ipmi_ret_t cc = 421 hiomap_commands[hiomap_cmd](flash_req, flash_resp, &flash_len, context); 422 if (cc != IPMI_CC_OK) 423 { 424 *data_len = 0; 425 return cc; 426 } 427 428 /* Populate the response command and sequence */ 429 ipmi_resp[0] = hiomap_cmd; 430 ipmi_resp[1] = ipmi_req[1]; 431 432 *data_len = flash_len + 2; 433 434 return cc; 435 } 436 } // namespace flash 437 } // namespace openpower 438 439 static void register_openpower_hiomap_commands() 440 { 441 using namespace openpower::flash; 442 443 /* FIXME: Clean this up? Can we unregister? */ 444 struct hiomap *ctx = new hiomap(); 445 446 /* Initialise mapping from signal and property names to status bit */ 447 ctx->event_lookup["DaemonReady"] = BMC_EVENT_DAEMON_READY; 448 ctx->event_lookup["FlashControlLost"] = BMC_EVENT_FLASH_CTRL_LOST; 449 ctx->event_lookup["WindowReset"] = BMC_EVENT_WINDOW_RESET; 450 ctx->event_lookup["ProtocolReset"] = BMC_EVENT_PROTOCOL_RESET; 451 452 ctx->bus = new bus::bus(ipmid_get_sd_bus_connection()); 453 454 /* Initialise signal handling */ 455 456 /* 457 * Can't use temporaries here because that causes SEGFAULTs due to slot 458 * destruction (!?), so enjoy the weird wrapping. 459 */ 460 ctx->properties = 461 new bus::match::match(std::move(hiomap_match_properties(ctx))); 462 ctx->bmc_reboot = new bus::match::match( 463 std::move(hiomap_match_signal_v2(ctx, "ProtocolReset"))); 464 ctx->window_reset = new bus::match::match( 465 std::move(hiomap_match_signal_v2(ctx, "WindowReset"))); 466 467 ipmi_register_callback(NETFUN_IBM_OEM, IPMI_CMD_HIOMAP, ctx, 468 openpower::flash::hiomap_dispatch, SYSTEM_INTERFACE); 469 } 470