1 #include "sensorhandler.hpp" 2 3 #include "fruread.hpp" 4 #include "ipmid.hpp" 5 #include "types.hpp" 6 #include "utils.hpp" 7 8 #include <ipmid/api.h> 9 #include <mapper.h> 10 #include <systemd/sd-bus.h> 11 12 #include <bitset> 13 #include <cmath> 14 #include <cstring> 15 #include <phosphor-logging/elog-errors.hpp> 16 #include <phosphor-logging/log.hpp> 17 #include <sdbusplus/message/types.hpp> 18 #include <set> 19 #include <xyz/openbmc_project/Common/error.hpp> 20 #include <xyz/openbmc_project/Sensor/Value/server.hpp> 21 22 static constexpr uint8_t fruInventoryDevice = 0x10; 23 static constexpr uint8_t IPMIFruInventory = 0x02; 24 static constexpr uint8_t BMCSlaveAddress = 0x20; 25 26 extern int updateSensorRecordFromSSRAESC(const void*); 27 extern sd_bus* bus; 28 extern const ipmi::sensor::IdInfoMap sensors; 29 extern const FruMap frus; 30 extern const ipmi::sensor::EntityInfoMap entities; 31 32 using namespace phosphor::logging; 33 using InternalFailure = 34 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 35 36 namespace variant_ns = sdbusplus::message::variant_ns; 37 38 void register_netfn_sen_functions() __attribute__((constructor)); 39 40 struct sensorTypemap_t 41 { 42 uint8_t number; 43 uint8_t typecode; 44 char dbusname[32]; 45 }; 46 47 sensorTypemap_t g_SensorTypeMap[] = { 48 49 {0x01, 0x6F, "Temp"}, 50 {0x0C, 0x6F, "DIMM"}, 51 {0x0C, 0x6F, "MEMORY_BUFFER"}, 52 {0x07, 0x6F, "PROC"}, 53 {0x07, 0x6F, "CORE"}, 54 {0x07, 0x6F, "CPU"}, 55 {0x0F, 0x6F, "BootProgress"}, 56 {0xe9, 0x09, "OccStatus"}, // E9 is an internal mapping to handle sensor 57 // type code os 0x09 58 {0xC3, 0x6F, "BootCount"}, 59 {0x1F, 0x6F, "OperatingSystemStatus"}, 60 {0x12, 0x6F, "SYSTEM_EVENT"}, 61 {0xC7, 0x03, "SYSTEM"}, 62 {0xC7, 0x03, "MAIN_PLANAR"}, 63 {0xC2, 0x6F, "PowerCap"}, 64 {0x0b, 0xCA, "PowerSupplyRedundancy"}, 65 {0xDA, 0x03, "TurboAllowed"}, 66 {0xD8, 0xC8, "PowerSupplyDerating"}, 67 {0xFF, 0x00, ""}, 68 }; 69 70 struct sensor_data_t 71 { 72 uint8_t sennum; 73 } __attribute__((packed)); 74 75 struct sensorreadingresp_t 76 { 77 uint8_t value; 78 uint8_t operation; 79 uint8_t indication[2]; 80 } __attribute__((packed)); 81 82 int get_bus_for_path(const char* path, char** busname) 83 { 84 return mapper_get_service(bus, path, busname); 85 } 86 87 // Use a lookup table to find the interface name of a specific sensor 88 // This will be used until an alternative is found. this is the first 89 // step for mapping IPMI 90 int find_openbmc_path(uint8_t num, dbus_interface_t* interface) 91 { 92 int rc; 93 94 const auto& sensor_it = sensors.find(num); 95 if (sensor_it == sensors.end()) 96 { 97 // The sensor map does not contain the sensor requested 98 return -EINVAL; 99 } 100 101 const auto& info = sensor_it->second; 102 103 char* busname = nullptr; 104 rc = get_bus_for_path(info.sensorPath.c_str(), &busname); 105 if (rc < 0) 106 { 107 std::fprintf(stderr, "Failed to get %s busname: %s\n", 108 info.sensorPath.c_str(), busname); 109 goto final; 110 } 111 112 interface->sensortype = info.sensorType; 113 strcpy(interface->bus, busname); 114 strcpy(interface->path, info.sensorPath.c_str()); 115 // Take the interface name from the beginning of the DbusInterfaceMap. This 116 // works for the Value interface but may not suffice for more complex 117 // sensors. 118 // tracked https://github.com/openbmc/phosphor-host-ipmid/issues/103 119 strcpy(interface->interface, 120 info.propertyInterfaces.begin()->first.c_str()); 121 interface->sensornumber = num; 122 123 final: 124 free(busname); 125 return rc; 126 } 127 128 ///////////////////////////////////////////////////////////////////// 129 // 130 // Routines used by ipmi commands wanting to interact on the dbus 131 // 132 ///////////////////////////////////////////////////////////////////// 133 int set_sensor_dbus_state_s(uint8_t number, const char* method, 134 const char* value) 135 { 136 137 dbus_interface_t a; 138 int r; 139 sd_bus_error error = SD_BUS_ERROR_NULL; 140 sd_bus_message* m = NULL; 141 142 std::fprintf(ipmidbus, 143 "Attempting to set a dbus Variant Sensor 0x%02x via %s with a " 144 "value of %s\n", 145 number, method, value); 146 147 r = find_openbmc_path(number, &a); 148 149 if (r < 0) 150 { 151 std::fprintf(stderr, "Failed to find Sensor 0x%02x\n", number); 152 return 0; 153 } 154 155 r = sd_bus_message_new_method_call(bus, &m, a.bus, a.path, a.interface, 156 method); 157 if (r < 0) 158 { 159 std::fprintf(stderr, "Failed to create a method call: %s", 160 strerror(-r)); 161 goto final; 162 } 163 164 r = sd_bus_message_append(m, "v", "s", value); 165 if (r < 0) 166 { 167 std::fprintf(stderr, "Failed to create a input parameter: %s", 168 strerror(-r)); 169 goto final; 170 } 171 172 r = sd_bus_call(bus, m, 0, &error, NULL); 173 if (r < 0) 174 { 175 std::fprintf(stderr, "Failed to call the method: %s", strerror(-r)); 176 } 177 178 final: 179 sd_bus_error_free(&error); 180 m = sd_bus_message_unref(m); 181 182 return 0; 183 } 184 int set_sensor_dbus_state_y(uint8_t number, const char* method, 185 const uint8_t value) 186 { 187 188 dbus_interface_t a; 189 int r; 190 sd_bus_error error = SD_BUS_ERROR_NULL; 191 sd_bus_message* m = NULL; 192 193 std::fprintf(ipmidbus, 194 "Attempting to set a dbus Variant Sensor 0x%02x via %s with a " 195 "value of 0x%02x\n", 196 number, method, value); 197 198 r = find_openbmc_path(number, &a); 199 200 if (r < 0) 201 { 202 std::fprintf(stderr, "Failed to find Sensor 0x%02x\n", number); 203 return 0; 204 } 205 206 r = sd_bus_message_new_method_call(bus, &m, a.bus, a.path, a.interface, 207 method); 208 if (r < 0) 209 { 210 std::fprintf(stderr, "Failed to create a method call: %s", 211 strerror(-r)); 212 goto final; 213 } 214 215 r = sd_bus_message_append(m, "v", "i", value); 216 if (r < 0) 217 { 218 std::fprintf(stderr, "Failed to create a input parameter: %s", 219 strerror(-r)); 220 goto final; 221 } 222 223 r = sd_bus_call(bus, m, 0, &error, NULL); 224 if (r < 0) 225 { 226 std::fprintf(stderr, "12 Failed to call the method: %s", strerror(-r)); 227 } 228 229 final: 230 sd_bus_error_free(&error); 231 m = sd_bus_message_unref(m); 232 233 return 0; 234 } 235 236 uint8_t dbus_to_sensor_type(char* p) 237 { 238 239 sensorTypemap_t* s = g_SensorTypeMap; 240 char r = 0; 241 while (s->number != 0xFF) 242 { 243 if (!strcmp(s->dbusname, p)) 244 { 245 r = s->typecode; 246 break; 247 } 248 s++; 249 } 250 251 if (s->number == 0xFF) 252 printf("Failed to find Sensor Type %s\n", p); 253 254 return r; 255 } 256 257 uint8_t get_type_from_interface(dbus_interface_t dbus_if) 258 { 259 260 uint8_t type; 261 262 // This is where sensors that do not exist in dbus but do 263 // exist in the host code stop. This should indicate it 264 // is not a supported sensor 265 if (dbus_if.interface[0] == 0) 266 { 267 return 0; 268 } 269 270 // Fetch type from interface itself. 271 if (dbus_if.sensortype != 0) 272 { 273 type = dbus_if.sensortype; 274 } 275 else 276 { 277 // Non InventoryItems 278 char* p = strrchr(dbus_if.path, '/'); 279 type = dbus_to_sensor_type(p + 1); 280 } 281 282 return type; 283 } 284 285 // Replaces find_sensor 286 uint8_t find_type_for_sensor_number(uint8_t num) 287 { 288 int r; 289 dbus_interface_t dbus_if; 290 r = find_openbmc_path(num, &dbus_if); 291 if (r < 0) 292 { 293 std::fprintf(stderr, "Could not find sensor %d\n", num); 294 return 0; 295 } 296 return get_type_from_interface(dbus_if); 297 } 298 299 ipmi_ret_t ipmi_sen_get_sensor_type(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 300 ipmi_request_t request, 301 ipmi_response_t response, 302 ipmi_data_len_t data_len, 303 ipmi_context_t context) 304 { 305 auto reqptr = static_cast<sensor_data_t*>(request); 306 ipmi_ret_t rc = IPMI_CC_OK; 307 308 printf("IPMI GET_SENSOR_TYPE [0x%02X]\n", reqptr->sennum); 309 310 // TODO Not sure what the System-event-sensor is suppose to return 311 // need to ask Hostboot team 312 unsigned char buf[] = {0x00, 0x6F}; 313 314 buf[0] = find_type_for_sensor_number(reqptr->sennum); 315 316 // HACK UNTIL Dbus gets updated or we find a better way 317 if (buf[0] == 0) 318 { 319 rc = IPMI_CC_SENSOR_INVALID; 320 } 321 322 *data_len = sizeof(buf); 323 std::memcpy(response, &buf, *data_len); 324 325 return rc; 326 } 327 328 const std::set<std::string> analogSensorInterfaces = { 329 "xyz.openbmc_project.Sensor.Value", 330 "xyz.openbmc_project.Control.FanPwm", 331 }; 332 333 bool isAnalogSensor(const std::string& interface) 334 { 335 return (analogSensorInterfaces.count(interface)); 336 } 337 338 ipmi_ret_t setSensorReading(void* request) 339 { 340 ipmi::sensor::SetSensorReadingReq cmdData = 341 *(static_cast<ipmi::sensor::SetSensorReadingReq*>(request)); 342 343 // Check if the Sensor Number is present 344 const auto iter = sensors.find(cmdData.number); 345 if (iter == sensors.end()) 346 { 347 return IPMI_CC_SENSOR_INVALID; 348 } 349 350 try 351 { 352 if (ipmi::sensor::Mutability::Write != 353 (iter->second.mutability & ipmi::sensor::Mutability::Write)) 354 { 355 log<level::ERR>("Sensor Set operation is not allowed", 356 entry("SENSOR_NUM=%d", cmdData.number)); 357 return IPMI_CC_ILLEGAL_COMMAND; 358 } 359 return iter->second.updateFunc(cmdData, iter->second); 360 } 361 catch (InternalFailure& e) 362 { 363 log<level::ERR>("Set sensor failed", 364 entry("SENSOR_NUM=%d", cmdData.number)); 365 commit<InternalFailure>(); 366 } 367 catch (const std::runtime_error& e) 368 { 369 log<level::ERR>(e.what()); 370 } 371 372 return IPMI_CC_UNSPECIFIED_ERROR; 373 } 374 375 ipmi_ret_t ipmi_sen_set_sensor(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 376 ipmi_request_t request, ipmi_response_t response, 377 ipmi_data_len_t data_len, ipmi_context_t context) 378 { 379 auto reqptr = static_cast<sensor_data_t*>(request); 380 381 log<level::DEBUG>("IPMI SET_SENSOR", 382 entry("SENSOR_NUM=0x%02x", reqptr->sennum)); 383 384 /* 385 * This would support the Set Sensor Reading command for the presence 386 * and functional state of Processor, Core & DIMM. For the remaining 387 * sensors the existing support is invoked. 388 */ 389 auto ipmiRC = setSensorReading(request); 390 391 if (ipmiRC == IPMI_CC_SENSOR_INVALID) 392 { 393 updateSensorRecordFromSSRAESC(reqptr); 394 ipmiRC = IPMI_CC_OK; 395 } 396 397 *data_len = 0; 398 return ipmiRC; 399 } 400 401 ipmi_ret_t ipmi_sen_get_sensor_reading(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 402 ipmi_request_t request, 403 ipmi_response_t response, 404 ipmi_data_len_t data_len, 405 ipmi_context_t context) 406 { 407 auto reqptr = static_cast<sensor_data_t*>(request); 408 auto resp = static_cast<sensorreadingresp_t*>(response); 409 ipmi::sensor::GetSensorResponse getResponse{}; 410 static constexpr auto scanningEnabledBit = 6; 411 412 const auto iter = sensors.find(reqptr->sennum); 413 if (iter == sensors.end()) 414 { 415 return IPMI_CC_SENSOR_INVALID; 416 } 417 if (ipmi::sensor::Mutability::Read != 418 (iter->second.mutability & ipmi::sensor::Mutability::Read)) 419 { 420 return IPMI_CC_ILLEGAL_COMMAND; 421 } 422 423 try 424 { 425 getResponse = iter->second.getFunc(iter->second); 426 *data_len = getResponse.size(); 427 std::memcpy(resp, getResponse.data(), *data_len); 428 resp->operation = 1 << scanningEnabledBit; 429 return IPMI_CC_OK; 430 } 431 catch (const std::exception& e) 432 { 433 *data_len = getResponse.size(); 434 std::memcpy(resp, getResponse.data(), *data_len); 435 return IPMI_CC_OK; 436 } 437 } 438 439 void getSensorThresholds(uint8_t sensorNum, 440 get_sdr::GetSensorThresholdsResponse* response) 441 { 442 constexpr auto warningThreshIntf = 443 "xyz.openbmc_project.Sensor.Threshold.Warning"; 444 constexpr auto criticalThreshIntf = 445 "xyz.openbmc_project.Sensor.Threshold.Critical"; 446 447 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 448 449 const auto iter = sensors.find(sensorNum); 450 const auto info = iter->second; 451 452 auto service = ipmi::getService(bus, info.sensorInterface, info.sensorPath); 453 454 auto warnThresholds = ipmi::getAllDbusProperties( 455 bus, service, info.sensorPath, warningThreshIntf); 456 457 double warnLow = variant_ns::visit(ipmi::VariantToDoubleVisitor(), 458 warnThresholds["WarningLow"]); 459 double warnHigh = variant_ns::visit(ipmi::VariantToDoubleVisitor(), 460 warnThresholds["WarningHigh"]); 461 462 if (warnLow != 0) 463 { 464 warnLow *= std::pow(10, info.scale - info.exponentR); 465 response->lowerNonCritical = static_cast<uint8_t>( 466 (warnLow - info.scaledOffset) / info.coefficientM); 467 response->validMask |= static_cast<uint8_t>( 468 ipmi::sensor::ThresholdMask::NON_CRITICAL_LOW_MASK); 469 } 470 471 if (warnHigh != 0) 472 { 473 warnHigh *= std::pow(10, info.scale - info.exponentR); 474 response->upperNonCritical = static_cast<uint8_t>( 475 (warnHigh - info.scaledOffset) / info.coefficientM); 476 response->validMask |= static_cast<uint8_t>( 477 ipmi::sensor::ThresholdMask::NON_CRITICAL_HIGH_MASK); 478 } 479 480 auto critThresholds = ipmi::getAllDbusProperties( 481 bus, service, info.sensorPath, criticalThreshIntf); 482 double critLow = variant_ns::visit(ipmi::VariantToDoubleVisitor(), 483 critThresholds["CriticalLow"]); 484 double critHigh = variant_ns::visit(ipmi::VariantToDoubleVisitor(), 485 critThresholds["CriticalHigh"]); 486 487 if (critLow != 0) 488 { 489 critLow *= std::pow(10, info.scale - info.exponentR); 490 response->lowerCritical = static_cast<uint8_t>( 491 (critLow - info.scaledOffset) / info.coefficientM); 492 response->validMask |= static_cast<uint8_t>( 493 ipmi::sensor::ThresholdMask::CRITICAL_LOW_MASK); 494 } 495 496 if (critHigh != 0) 497 { 498 critHigh *= std::pow(10, info.scale - info.exponentR); 499 response->upperCritical = static_cast<uint8_t>( 500 (critHigh - info.scaledOffset) / info.coefficientM); 501 response->validMask |= static_cast<uint8_t>( 502 ipmi::sensor::ThresholdMask::CRITICAL_HIGH_MASK); 503 } 504 } 505 506 ipmi_ret_t ipmi_sen_get_sensor_thresholds(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 507 ipmi_request_t request, 508 ipmi_response_t response, 509 ipmi_data_len_t data_len, 510 ipmi_context_t context) 511 { 512 constexpr auto valueInterface = "xyz.openbmc_project.Sensor.Value"; 513 514 if (*data_len != sizeof(uint8_t)) 515 { 516 *data_len = 0; 517 return IPMI_CC_REQ_DATA_LEN_INVALID; 518 } 519 520 auto sensorNum = *(reinterpret_cast<const uint8_t*>(request)); 521 *data_len = 0; 522 523 const auto iter = sensors.find(sensorNum); 524 if (iter == sensors.end()) 525 { 526 return IPMI_CC_SENSOR_INVALID; 527 } 528 529 const auto info = iter->second; 530 531 // Proceed only if the sensor value interface is implemented. 532 if (info.propertyInterfaces.find(valueInterface) == 533 info.propertyInterfaces.end()) 534 { 535 // return with valid mask as 0 536 return IPMI_CC_OK; 537 } 538 539 auto responseData = 540 reinterpret_cast<get_sdr::GetSensorThresholdsResponse*>(response); 541 542 try 543 { 544 getSensorThresholds(sensorNum, responseData); 545 } 546 catch (std::exception& e) 547 { 548 // Mask if the property is not present 549 responseData->validMask = 0; 550 } 551 552 *data_len = sizeof(get_sdr::GetSensorThresholdsResponse); 553 return IPMI_CC_OK; 554 } 555 556 ipmi_ret_t ipmi_sen_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 557 ipmi_request_t request, ipmi_response_t response, 558 ipmi_data_len_t data_len, ipmi_context_t context) 559 { 560 ipmi_ret_t rc = IPMI_CC_INVALID; 561 562 printf("IPMI S/E Wildcard Netfn:[0x%X], Cmd:[0x%X]\n", netfn, cmd); 563 *data_len = 0; 564 565 return rc; 566 } 567 568 ipmi_ret_t ipmi_sen_get_sdr_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 569 ipmi_request_t request, 570 ipmi_response_t response, 571 ipmi_data_len_t data_len, 572 ipmi_context_t context) 573 { 574 auto resp = static_cast<get_sdr_info::GetSdrInfoResp*>(response); 575 if (request == nullptr || 576 get_sdr_info::request::get_count(request) == false) 577 { 578 // Get Sensor Count 579 resp->count = sensors.size() + frus.size() + entities.size(); 580 } 581 else 582 { 583 resp->count = 1; 584 } 585 586 // Multiple LUNs not supported. 587 namespace response = get_sdr_info::response; 588 response::set_lun_present(0, &(resp->luns_and_dynamic_population)); 589 response::set_lun_not_present(1, &(resp->luns_and_dynamic_population)); 590 response::set_lun_not_present(2, &(resp->luns_and_dynamic_population)); 591 response::set_lun_not_present(3, &(resp->luns_and_dynamic_population)); 592 response::set_static_population(&(resp->luns_and_dynamic_population)); 593 594 *data_len = SDR_INFO_RESP_SIZE; 595 596 return IPMI_CC_OK; 597 } 598 599 ipmi_ret_t ipmi_sen_reserve_sdr(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 600 ipmi_request_t request, 601 ipmi_response_t response, 602 ipmi_data_len_t data_len, 603 ipmi_context_t context) 604 { 605 // A constant reservation ID is okay until we implement add/remove SDR. 606 const uint16_t reservation_id = 1; 607 *(uint16_t*)response = reservation_id; 608 *data_len = sizeof(uint16_t); 609 610 printf("Created new IPMI SDR reservation ID %d\n", *(uint16_t*)response); 611 return IPMI_CC_OK; 612 } 613 614 void setUnitFieldsForObject(const ipmi::sensor::Info* info, 615 get_sdr::SensorDataFullRecordBody* body) 616 { 617 namespace server = sdbusplus::xyz::openbmc_project::Sensor::server; 618 try 619 { 620 auto unit = server::Value::convertUnitFromString(info->unit); 621 // Unit strings defined in 622 // phosphor-dbus-interfaces/xyz/openbmc_project/Sensor/Value.interface.yaml 623 switch (unit) 624 { 625 case server::Value::Unit::DegreesC: 626 body->sensor_units_2_base = get_sdr::SENSOR_UNIT_DEGREES_C; 627 break; 628 case server::Value::Unit::RPMS: 629 body->sensor_units_2_base = get_sdr::SENSOR_UNIT_RPM; 630 break; 631 case server::Value::Unit::Volts: 632 body->sensor_units_2_base = get_sdr::SENSOR_UNIT_VOLTS; 633 break; 634 case server::Value::Unit::Meters: 635 body->sensor_units_2_base = get_sdr::SENSOR_UNIT_METERS; 636 break; 637 case server::Value::Unit::Amperes: 638 body->sensor_units_2_base = get_sdr::SENSOR_UNIT_AMPERES; 639 break; 640 case server::Value::Unit::Joules: 641 body->sensor_units_2_base = get_sdr::SENSOR_UNIT_JOULES; 642 break; 643 case server::Value::Unit::Watts: 644 body->sensor_units_2_base = get_sdr::SENSOR_UNIT_WATTS; 645 break; 646 default: 647 // Cannot be hit. 648 std::fprintf(stderr, "Unknown value unit type: = %s\n", 649 info->unit.c_str()); 650 } 651 } 652 catch (const sdbusplus::exception::InvalidEnumString& e) 653 { 654 log<level::WARNING>("Warning: no unit provided for sensor!"); 655 } 656 } 657 658 ipmi_ret_t populate_record_from_dbus(get_sdr::SensorDataFullRecordBody* body, 659 const ipmi::sensor::Info* info, 660 ipmi_data_len_t data_len) 661 { 662 /* Functional sensor case */ 663 if (isAnalogSensor(info->propertyInterfaces.begin()->first)) 664 { 665 666 body->sensor_units_1 = 0; // unsigned, no rate, no modifier, not a % 667 668 /* Unit info */ 669 setUnitFieldsForObject(info, body); 670 671 get_sdr::body::set_b(info->coefficientB, body); 672 get_sdr::body::set_m(info->coefficientM, body); 673 get_sdr::body::set_b_exp(info->exponentB, body); 674 get_sdr::body::set_r_exp(info->exponentR, body); 675 676 get_sdr::body::set_id_type(0b00, body); // 00 = unicode 677 } 678 679 /* ID string */ 680 auto id_string = info->sensorNameFunc(*info); 681 682 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH) 683 { 684 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH, body); 685 } 686 else 687 { 688 get_sdr::body::set_id_strlen(id_string.length(), body); 689 } 690 strncpy(body->id_string, id_string.c_str(), 691 get_sdr::body::get_id_strlen(body)); 692 693 return IPMI_CC_OK; 694 }; 695 696 ipmi_ret_t ipmi_fru_get_sdr(ipmi_request_t request, ipmi_response_t response, 697 ipmi_data_len_t data_len) 698 { 699 auto req = reinterpret_cast<get_sdr::GetSdrReq*>(request); 700 auto resp = reinterpret_cast<get_sdr::GetSdrResp*>(response); 701 get_sdr::SensorDataFruRecord record{}; 702 auto dataLength = 0; 703 704 auto fru = frus.begin(); 705 uint8_t fruID{}; 706 auto recordID = get_sdr::request::get_record_id(req); 707 708 fruID = recordID - FRU_RECORD_ID_START; 709 fru = frus.find(fruID); 710 if (fru == frus.end()) 711 { 712 return IPMI_CC_SENSOR_INVALID; 713 } 714 715 /* Header */ 716 get_sdr::header::set_record_id(recordID, &(record.header)); 717 record.header.sdr_version = SDR_VERSION; // Based on IPMI Spec v2.0 rev 1.1 718 record.header.record_type = get_sdr::SENSOR_DATA_FRU_RECORD; 719 record.header.record_length = sizeof(record.key) + sizeof(record.body); 720 721 /* Key */ 722 record.key.fruID = fruID; 723 record.key.accessLun |= IPMI_LOGICAL_FRU; 724 record.key.deviceAddress = BMCSlaveAddress; 725 726 /* Body */ 727 record.body.entityID = fru->second[0].entityID; 728 record.body.entityInstance = fru->second[0].entityInstance; 729 record.body.deviceType = fruInventoryDevice; 730 record.body.deviceTypeModifier = IPMIFruInventory; 731 732 /* Device ID string */ 733 auto deviceID = 734 fru->second[0].path.substr(fru->second[0].path.find_last_of('/') + 1, 735 fru->second[0].path.length()); 736 737 if (deviceID.length() > get_sdr::FRU_RECORD_DEVICE_ID_MAX_LENGTH) 738 { 739 get_sdr::body::set_device_id_strlen( 740 get_sdr::FRU_RECORD_DEVICE_ID_MAX_LENGTH, &(record.body)); 741 } 742 else 743 { 744 get_sdr::body::set_device_id_strlen(deviceID.length(), &(record.body)); 745 } 746 747 strncpy(record.body.deviceID, deviceID.c_str(), 748 get_sdr::body::get_device_id_strlen(&(record.body))); 749 750 if (++fru == frus.end()) 751 { 752 // we have reached till end of fru, so assign the next record id to 753 // 512(Max fru ID = 511) + Entity Record ID(may start with 0). 754 auto next_record_id = 755 (entities.size()) ? entities.begin()->first + ENTITY_RECORD_ID_START 756 : END_OF_RECORD; 757 get_sdr::response::set_next_record_id(next_record_id, resp); 758 } 759 else 760 { 761 get_sdr::response::set_next_record_id( 762 (FRU_RECORD_ID_START + fru->first), resp); 763 } 764 765 // Check for invalid offset size 766 if (req->offset > sizeof(record)) 767 { 768 return IPMI_CC_PARM_OUT_OF_RANGE; 769 } 770 771 dataLength = std::min(static_cast<size_t>(req->bytes_to_read), 772 sizeof(record) - req->offset); 773 774 std::memcpy(resp->record_data, 775 reinterpret_cast<uint8_t*>(&record) + req->offset, dataLength); 776 777 *data_len = dataLength; 778 *data_len += 2; // additional 2 bytes for next record ID 779 780 return IPMI_CC_OK; 781 } 782 783 ipmi_ret_t ipmi_entity_get_sdr(ipmi_request_t request, ipmi_response_t response, 784 ipmi_data_len_t data_len) 785 { 786 auto req = reinterpret_cast<get_sdr::GetSdrReq*>(request); 787 auto resp = reinterpret_cast<get_sdr::GetSdrResp*>(response); 788 get_sdr::SensorDataEntityRecord record{}; 789 auto dataLength = 0; 790 791 auto entity = entities.begin(); 792 uint8_t entityRecordID; 793 auto recordID = get_sdr::request::get_record_id(req); 794 795 entityRecordID = recordID - ENTITY_RECORD_ID_START; 796 entity = entities.find(entityRecordID); 797 if (entity == entities.end()) 798 { 799 return IPMI_CC_SENSOR_INVALID; 800 } 801 802 /* Header */ 803 get_sdr::header::set_record_id(recordID, &(record.header)); 804 record.header.sdr_version = SDR_VERSION; // Based on IPMI Spec v2.0 rev 1.1 805 record.header.record_type = get_sdr::SENSOR_DATA_ENTITY_RECORD; 806 record.header.record_length = sizeof(record.key) + sizeof(record.body); 807 808 /* Key */ 809 record.key.containerEntityId = entity->second.containerEntityId; 810 record.key.containerEntityInstance = entity->second.containerEntityInstance; 811 get_sdr::key::set_flags(entity->second.isList, entity->second.isLinked, 812 &(record.key)); 813 record.key.entityId1 = entity->second.containedEntities[0].first; 814 record.key.entityInstance1 = entity->second.containedEntities[0].second; 815 816 /* Body */ 817 record.body.entityId2 = entity->second.containedEntities[1].first; 818 record.body.entityInstance2 = entity->second.containedEntities[1].second; 819 record.body.entityId3 = entity->second.containedEntities[2].first; 820 record.body.entityInstance3 = entity->second.containedEntities[2].second; 821 record.body.entityId4 = entity->second.containedEntities[3].first; 822 record.body.entityInstance4 = entity->second.containedEntities[3].second; 823 824 if (++entity == entities.end()) 825 { 826 get_sdr::response::set_next_record_id(END_OF_RECORD, 827 resp); // last record 828 } 829 else 830 { 831 get_sdr::response::set_next_record_id( 832 (ENTITY_RECORD_ID_START + entity->first), resp); 833 } 834 835 // Check for invalid offset size 836 if (req->offset > sizeof(record)) 837 { 838 return IPMI_CC_PARM_OUT_OF_RANGE; 839 } 840 841 dataLength = std::min(static_cast<size_t>(req->bytes_to_read), 842 sizeof(record) - req->offset); 843 844 std::memcpy(resp->record_data, 845 reinterpret_cast<uint8_t*>(&record) + req->offset, dataLength); 846 847 *data_len = dataLength; 848 *data_len += 2; // additional 2 bytes for next record ID 849 850 return IPMI_CC_OK; 851 } 852 853 ipmi_ret_t ipmi_sen_get_sdr(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 854 ipmi_request_t request, ipmi_response_t response, 855 ipmi_data_len_t data_len, ipmi_context_t context) 856 { 857 ipmi_ret_t ret = IPMI_CC_OK; 858 get_sdr::GetSdrReq* req = (get_sdr::GetSdrReq*)request; 859 get_sdr::GetSdrResp* resp = (get_sdr::GetSdrResp*)response; 860 get_sdr::SensorDataFullRecord record = {0}; 861 if (req != NULL) 862 { 863 // Note: we use an iterator so we can provide the next ID at the end of 864 // the call. 865 auto sensor = sensors.begin(); 866 auto recordID = get_sdr::request::get_record_id(req); 867 868 // At the beginning of a scan, the host side will send us id=0. 869 if (recordID != 0) 870 { 871 // recordID 0 to 255 means it is a FULL record. 872 // recordID 256 to 511 means it is a FRU record. 873 // recordID greater then 511 means it is a Entity Association 874 // record. Currently we are supporting three record types: FULL 875 // record, FRU record and Enttiy Association record. 876 if (recordID >= ENTITY_RECORD_ID_START) 877 { 878 return ipmi_entity_get_sdr(request, response, data_len); 879 } 880 else if (recordID >= FRU_RECORD_ID_START && 881 recordID < ENTITY_RECORD_ID_START) 882 { 883 return ipmi_fru_get_sdr(request, response, data_len); 884 } 885 else 886 { 887 sensor = sensors.find(recordID); 888 if (sensor == sensors.end()) 889 { 890 return IPMI_CC_SENSOR_INVALID; 891 } 892 } 893 } 894 895 uint8_t sensor_id = sensor->first; 896 897 /* Header */ 898 get_sdr::header::set_record_id(sensor_id, &(record.header)); 899 record.header.sdr_version = 0x51; // Based on IPMI Spec v2.0 rev 1.1 900 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD; 901 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord); 902 903 /* Key */ 904 get_sdr::key::set_owner_id_bmc(&(record.key)); 905 record.key.sensor_number = sensor_id; 906 907 /* Body */ 908 record.body.entity_id = sensor->second.entityType; 909 record.body.sensor_type = sensor->second.sensorType; 910 record.body.event_reading_type = sensor->second.sensorReadingType; 911 record.body.entity_instance = sensor->second.instance; 912 if (ipmi::sensor::Mutability::Write == 913 (sensor->second.mutability & ipmi::sensor::Mutability::Write)) 914 { 915 get_sdr::body::init_settable_state(true, &(record.body)); 916 } 917 918 // Set the type-specific details given the DBus interface 919 ret = populate_record_from_dbus(&(record.body), &(sensor->second), 920 data_len); 921 922 if (++sensor == sensors.end()) 923 { 924 // we have reached till end of sensor, so assign the next record id 925 // to 256(Max Sensor ID = 255) + FRU ID(may start with 0). 926 auto next_record_id = 927 (frus.size()) ? frus.begin()->first + FRU_RECORD_ID_START 928 : END_OF_RECORD; 929 930 get_sdr::response::set_next_record_id(next_record_id, resp); 931 } 932 else 933 { 934 get_sdr::response::set_next_record_id(sensor->first, resp); 935 } 936 937 if (req->offset > sizeof(record)) 938 { 939 return IPMI_CC_PARM_OUT_OF_RANGE; 940 } 941 942 // data_len will ultimately be the size of the record, plus 943 // the size of the next record ID: 944 *data_len = std::min(static_cast<size_t>(req->bytes_to_read), 945 sizeof(record) - req->offset); 946 947 std::memcpy(resp->record_data, 948 reinterpret_cast<uint8_t*>(&record) + req->offset, 949 *data_len); 950 951 // data_len should include the LSB and MSB: 952 *data_len += 953 sizeof(resp->next_record_id_lsb) + sizeof(resp->next_record_id_msb); 954 } 955 956 return ret; 957 } 958 959 static bool isFromSystemChannel() 960 { 961 // TODO we could not figure out where the request is from based on IPMI 962 // command handler parameters. because of it, we can not differentiate 963 // request from SMS/SMM or IPMB channel 964 return true; 965 } 966 967 ipmi_ret_t ipmicmdPlatformEvent(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 968 ipmi_request_t request, 969 ipmi_response_t response, 970 ipmi_data_len_t dataLen, ipmi_context_t context) 971 { 972 uint16_t generatorID; 973 size_t count; 974 bool assert = true; 975 std::string sensorPath; 976 size_t paraLen = *dataLen; 977 PlatformEventRequest* req; 978 *dataLen = 0; 979 980 if ((paraLen < selSystemEventSizeWith1Bytes) || 981 (paraLen > selSystemEventSizeWith3Bytes)) 982 { 983 return IPMI_CC_REQ_DATA_LEN_INVALID; 984 } 985 986 if (isFromSystemChannel()) 987 { // first byte for SYSTEM Interface is Generator ID 988 // +1 to get common struct 989 req = reinterpret_cast<PlatformEventRequest*>((uint8_t*)request + 1); 990 // Capture the generator ID 991 generatorID = *reinterpret_cast<uint8_t*>(request); 992 // Platform Event usually comes from other firmware, like BIOS. 993 // Unlike BMC sensor, it does not have BMC DBUS sensor path. 994 sensorPath = "System"; 995 } 996 else 997 { 998 req = reinterpret_cast<PlatformEventRequest*>(request); 999 // TODO GenratorID for IPMB is combination of RqSA and RqLUN 1000 generatorID = 0xff; 1001 sensorPath = "IPMB"; 1002 } 1003 // Content of event data field depends on sensor class. 1004 // When data0 bit[5:4] is non-zero, valid data counts is 3. 1005 // When data0 bit[7:6] is non-zero, valid data counts is 2. 1006 if (((req->data[0] & byte3EnableMask) != 0 && 1007 paraLen < selSystemEventSizeWith3Bytes) || 1008 ((req->data[0] & byte2EnableMask) != 0 && 1009 paraLen < selSystemEventSizeWith2Bytes)) 1010 { 1011 return IPMI_CC_REQ_DATA_LEN_INVALID; 1012 } 1013 1014 // Count bytes of Event Data 1015 if ((req->data[0] & byte3EnableMask) != 0) 1016 { 1017 count = 3; 1018 } 1019 else if ((req->data[0] & byte2EnableMask) != 0) 1020 { 1021 count = 2; 1022 } 1023 else 1024 { 1025 count = 1; 1026 } 1027 assert = req->eventDirectionType & directionMask ? false : true; 1028 std::vector<uint8_t> eventData(req->data, req->data + count); 1029 1030 sdbusplus::bus::bus dbus(bus); 1031 std::string service = 1032 ipmi::getService(dbus, ipmiSELAddInterface, ipmiSELPath); 1033 sdbusplus::message::message writeSEL = dbus.new_method_call( 1034 service.c_str(), ipmiSELPath, ipmiSELAddInterface, "IpmiSelAdd"); 1035 writeSEL.append(ipmiSELAddMessage, sensorPath, eventData, assert, 1036 generatorID); 1037 try 1038 { 1039 dbus.call(writeSEL); 1040 } 1041 catch (sdbusplus::exception_t& e) 1042 { 1043 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 1044 return IPMI_CC_UNSPECIFIED_ERROR; 1045 } 1046 return IPMI_CC_OK; 1047 } 1048 1049 void register_netfn_sen_functions() 1050 { 1051 // <Wildcard Command> 1052 ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_WILDCARD, nullptr, 1053 ipmi_sen_wildcard, PRIVILEGE_USER); 1054 1055 // <Platform Event Message> 1056 ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_PLATFORM_EVENT, nullptr, 1057 ipmicmdPlatformEvent, PRIVILEGE_OPERATOR); 1058 // <Get Sensor Type> 1059 ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_GET_SENSOR_TYPE, nullptr, 1060 ipmi_sen_get_sensor_type, PRIVILEGE_USER); 1061 1062 // <Set Sensor Reading and Event Status> 1063 ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_SET_SENSOR, nullptr, 1064 ipmi_sen_set_sensor, PRIVILEGE_OPERATOR); 1065 1066 // <Get Sensor Reading> 1067 ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_GET_SENSOR_READING, nullptr, 1068 ipmi_sen_get_sensor_reading, PRIVILEGE_USER); 1069 1070 // <Reserve Device SDR Repository> 1071 ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_RESERVE_DEVICE_SDR_REPO, 1072 nullptr, ipmi_sen_reserve_sdr, PRIVILEGE_USER); 1073 1074 // <Get Device SDR Info> 1075 ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_GET_DEVICE_SDR_INFO, nullptr, 1076 ipmi_sen_get_sdr_info, PRIVILEGE_USER); 1077 1078 // <Get Device SDR> 1079 ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_GET_DEVICE_SDR, nullptr, 1080 ipmi_sen_get_sdr, PRIVILEGE_USER); 1081 1082 // <Get Sensor Thresholds> 1083 ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_GET_SENSOR_THRESHOLDS, 1084 nullptr, ipmi_sen_get_sensor_thresholds, 1085 PRIVILEGE_USER); 1086 1087 return; 1088 } 1089