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