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 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 412 static 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 435 static const hiomap_command hiomap_commands[] = { 436 [0] = NULL, /* 0 is an invalid command ID */ 437 [1] = hiomap_reset, 438 [2] = hiomap_get_info, 439 [3] = hiomap_get_flash_info, 440 [4] = hiomap_create_read_window, 441 [5] = hiomap_close_window, 442 [6] = hiomap_create_write_window, 443 [7] = hiomap_mark_dirty, 444 [8] = hiomap_flush, 445 }; 446 447 /* FIXME: Define this in the "right" place, wherever that is */ 448 /* FIXME: Double evaluation */ 449 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 450 451 static ipmi_ret_t hiomap_dispatch(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 452 ipmi_request_t request, 453 ipmi_response_t response, 454 ipmi_data_len_t data_len, 455 ipmi_context_t context) 456 { 457 struct hiomap *ctx = static_cast<struct hiomap *>(context); 458 459 if (*data_len < 2) 460 { 461 *data_len = 0; 462 return IPMI_CC_REQ_DATA_LEN_INVALID; 463 } 464 465 uint8_t *ipmi_req = (uint8_t *)request; 466 uint8_t *ipmi_resp = (uint8_t *)response; 467 uint8_t hiomap_cmd = ipmi_req[0]; 468 469 if (hiomap_cmd == 0 || hiomap_cmd > ARRAY_SIZE(hiomap_commands) - 1) 470 { 471 *data_len = 0; 472 return IPMI_CC_PARM_OUT_OF_RANGE; 473 } 474 uint8_t *flash_req = ipmi_req + 2; 475 size_t flash_len = *data_len - 2; 476 uint8_t *flash_resp = ipmi_resp + 2; 477 478 ipmi_ret_t cc = 479 hiomap_commands[hiomap_cmd](flash_req, flash_resp, &flash_len, context); 480 if (cc != IPMI_CC_OK) 481 { 482 *data_len = 0; 483 return cc; 484 } 485 486 /* Populate the response command and sequence */ 487 ipmi_resp[0] = hiomap_cmd; 488 ipmi_resp[1] = ipmi_req[1]; 489 490 *data_len = flash_len + 2; 491 492 return cc; 493 } 494 } // namespace flash 495 } // namespace openpower 496 497 static void register_openpower_hiomap_commands() 498 { 499 using namespace openpower::flash; 500 501 /* FIXME: Clean this up? Can we unregister? */ 502 struct hiomap *ctx = new hiomap(); 503 504 /* Initialise mapping from signal and property names to status bit */ 505 ctx->event_lookup["DaemonReady"] = BMC_EVENT_DAEMON_READY; 506 ctx->event_lookup["FlashControlLost"] = BMC_EVENT_FLASH_CTRL_LOST; 507 ctx->event_lookup["WindowReset"] = BMC_EVENT_WINDOW_RESET; 508 ctx->event_lookup["ProtocolReset"] = BMC_EVENT_PROTOCOL_RESET; 509 510 ctx->bus = new bus::bus(ipmid_get_sd_bus_connection()); 511 512 /* Initialise signal handling */ 513 514 /* 515 * Can't use temporaries here because that causes SEGFAULTs due to slot 516 * destruction (!?), so enjoy the weird wrapping. 517 */ 518 ctx->properties = 519 new bus::match::match(std::move(hiomap_match_properties(ctx))); 520 ctx->bmc_reboot = new bus::match::match( 521 std::move(hiomap_match_signal_v2(ctx, "ProtocolReset"))); 522 ctx->window_reset = new bus::match::match( 523 std::move(hiomap_match_signal_v2(ctx, "WindowReset"))); 524 525 ipmi_register_callback(NETFUN_IBM_OEM, IPMI_CMD_HIOMAP, ctx, 526 openpower::flash::hiomap_dispatch, SYSTEM_INTERFACE); 527 } 528