1 /** 2 * Copyright © 2019 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #include "config.h" 17 18 #include "data_interface.hpp" 19 20 #include "util.hpp" 21 22 #include <fmt/format.h> 23 24 #include <fstream> 25 #include <phosphor-logging/log.hpp> 26 #include <xyz/openbmc_project/State/Boot/Progress/server.hpp> 27 28 namespace openpower 29 { 30 namespace pels 31 { 32 33 namespace service_name 34 { 35 constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper"; 36 constexpr auto vpdManager = "com.ibm.VPD.Manager"; 37 constexpr auto ledGroupManager = "xyz.openbmc_project.LED.GroupManager"; 38 constexpr auto logSetting = "xyz.openbmc_project.Settings"; 39 } // namespace service_name 40 41 namespace object_path 42 { 43 constexpr auto objectMapper = "/xyz/openbmc_project/object_mapper"; 44 constexpr auto systemInv = "/xyz/openbmc_project/inventory/system"; 45 constexpr auto chassisInv = "/xyz/openbmc_project/inventory/system/chassis"; 46 constexpr auto motherBoardInv = 47 "/xyz/openbmc_project/inventory/system/chassis/motherboard"; 48 constexpr auto baseInv = "/xyz/openbmc_project/inventory"; 49 constexpr auto bmcState = "/xyz/openbmc_project/state/bmc0"; 50 constexpr auto chassisState = "/xyz/openbmc_project/state/chassis0"; 51 constexpr auto hostState = "/xyz/openbmc_project/state/host0"; 52 constexpr auto pldm = "/xyz/openbmc_project/pldm"; 53 constexpr auto enableHostPELs = 54 "/xyz/openbmc_project/logging/send_event_logs_to_host"; 55 constexpr auto vpdManager = "/com/ibm/VPD/Manager"; 56 constexpr auto logSetting = "/xyz/openbmc_project/logging/settings"; 57 } // namespace object_path 58 59 namespace interface 60 { 61 constexpr auto dbusProperty = "org.freedesktop.DBus.Properties"; 62 constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper"; 63 constexpr auto invAsset = "xyz.openbmc_project.Inventory.Decorator.Asset"; 64 constexpr auto bootProgress = "xyz.openbmc_project.State.Boot.Progress"; 65 constexpr auto pldmRequester = "xyz.openbmc_project.PLDM.Requester"; 66 constexpr auto enable = "xyz.openbmc_project.Object.Enable"; 67 constexpr auto bmcState = "xyz.openbmc_project.State.BMC"; 68 constexpr auto chassisState = "xyz.openbmc_project.State.Chassis"; 69 constexpr auto hostState = "xyz.openbmc_project.State.Host"; 70 constexpr auto invMotherboard = 71 "xyz.openbmc_project.Inventory.Item.Board.Motherboard"; 72 constexpr auto viniRecordVPD = "com.ibm.ipzvpd.VINI"; 73 constexpr auto vsbpRecordVPD = "com.ibm.ipzvpd.VSBP"; 74 constexpr auto locCode = "com.ibm.ipzvpd.Location"; 75 constexpr auto compatible = 76 "xyz.openbmc_project.Configuration.IBMCompatibleSystem"; 77 constexpr auto vpdManager = "com.ibm.VPD.Manager"; 78 constexpr auto ledGroup = "xyz.openbmc_project.Led.Group"; 79 constexpr auto operationalStatus = 80 "xyz.openbmc_project.State.Decorator.OperationalStatus"; 81 constexpr auto logSetting = "xyz.openbmc_project.Logging.Settings"; 82 } // namespace interface 83 84 using namespace sdbusplus::xyz::openbmc_project::State::Boot::server; 85 using sdbusplus::exception::SdBusError; 86 using namespace phosphor::logging; 87 88 DataInterface::DataInterface(sdbusplus::bus::bus& bus) : _bus(bus) 89 { 90 readBMCFWVersion(); 91 readServerFWVersion(); 92 readBMCFWVersionID(); 93 94 // Watch the BootProgress property 95 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>( 96 bus, object_path::hostState, interface::bootProgress, "BootProgress", 97 *this, [this](const auto& value) { 98 auto status = Progress::convertProgressStagesFromString( 99 std::get<std::string>(value)); 100 101 if ((status == Progress::ProgressStages::SystemInitComplete) || 102 (status == Progress::ProgressStages::OSStart) || 103 (status == Progress::ProgressStages::OSRunning)) 104 { 105 setHostUp(true); 106 } 107 else 108 { 109 setHostUp(false); 110 } 111 })); 112 113 // Watch the host PEL enable property 114 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>( 115 bus, object_path::enableHostPELs, interface::enable, "Enabled", *this, 116 [this](const auto& value) { 117 this->_sendPELsToHost = std::get<bool>(value); 118 })); 119 120 // Watch the BMCState property 121 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>( 122 bus, object_path::bmcState, interface::bmcState, "CurrentBMCState", 123 *this, [this](const auto& value) { 124 this->_bmcState = std::get<std::string>(value); 125 })); 126 127 // Watch the chassis current and requested power state properties 128 _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>( 129 bus, object_path::chassisState, interface::chassisState, *this, 130 [this](const auto& properties) { 131 auto state = properties.find("CurrentPowerState"); 132 if (state != properties.end()) 133 { 134 this->_chassisState = std::get<std::string>(state->second); 135 } 136 137 auto trans = properties.find("RequestedPowerTransition"); 138 if (trans != properties.end()) 139 { 140 this->_chassisTransition = std::get<std::string>(trans->second); 141 } 142 })); 143 144 // Watch the CurrentHostState property 145 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>( 146 bus, object_path::hostState, interface::hostState, "CurrentHostState", 147 *this, [this](const auto& value) { 148 this->_hostState = std::get<std::string>(value); 149 })); 150 } 151 152 DBusPropertyMap 153 DataInterface::getAllProperties(const std::string& service, 154 const std::string& objectPath, 155 const std::string& interface) const 156 { 157 DBusPropertyMap properties; 158 159 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(), 160 interface::dbusProperty, "GetAll"); 161 method.append(interface); 162 auto reply = _bus.call(method); 163 164 reply.read(properties); 165 166 return properties; 167 } 168 169 void DataInterface::getProperty(const std::string& service, 170 const std::string& objectPath, 171 const std::string& interface, 172 const std::string& property, 173 DBusValue& value) const 174 { 175 176 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(), 177 interface::dbusProperty, "Get"); 178 method.append(interface, property); 179 auto reply = _bus.call(method); 180 181 reply.read(value); 182 } 183 184 DBusPathList DataInterface::getPaths(const DBusInterfaceList& interfaces) const 185 { 186 187 auto method = _bus.new_method_call( 188 service_name::objectMapper, object_path::objectMapper, 189 interface::objectMapper, "GetSubTreePaths"); 190 191 method.append(std::string{"/"}, 0, interfaces); 192 193 auto reply = _bus.call(method); 194 195 DBusPathList paths; 196 reply.read(paths); 197 198 return paths; 199 } 200 201 DBusService DataInterface::getService(const std::string& objectPath, 202 const std::string& interface) const 203 { 204 auto method = _bus.new_method_call(service_name::objectMapper, 205 object_path::objectMapper, 206 interface::objectMapper, "GetObject"); 207 208 method.append(objectPath, std::vector<std::string>({interface})); 209 210 auto reply = _bus.call(method); 211 212 std::map<DBusService, DBusInterfaceList> response; 213 reply.read(response); 214 215 if (!response.empty()) 216 { 217 return response.begin()->first; 218 } 219 220 return std::string{}; 221 } 222 223 void DataInterface::readBMCFWVersion() 224 { 225 _bmcFWVersion = 226 phosphor::logging::util::getOSReleaseValue("VERSION").value_or(""); 227 } 228 229 void DataInterface::readServerFWVersion() 230 { 231 auto value = 232 phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or(""); 233 if ((value != "") && (value.find_last_of(')') != std::string::npos)) 234 { 235 std::size_t pos = value.find_first_of('(') + 1; 236 _serverFWVersion = value.substr(pos, value.find_last_of(')') - pos); 237 } 238 } 239 240 void DataInterface::readBMCFWVersionID() 241 { 242 _bmcFWVersionID = 243 phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or(""); 244 } 245 246 std::string DataInterface::getMachineTypeModel() const 247 { 248 std::string model; 249 try 250 { 251 252 auto service = getService(object_path::systemInv, interface::invAsset); 253 if (!service.empty()) 254 { 255 DBusValue value; 256 getProperty(service, object_path::systemInv, interface::invAsset, 257 "Model", value); 258 259 model = std::get<std::string>(value); 260 } 261 } 262 catch (const std::exception& e) 263 { 264 log<level::WARNING>(fmt::format("Failed reading Model property from " 265 "Interface: {} exception: {}", 266 interface::invAsset, e.what()) 267 .c_str()); 268 } 269 270 return model; 271 } 272 273 std::string DataInterface::getMachineSerialNumber() const 274 { 275 std::string sn; 276 try 277 { 278 279 auto service = getService(object_path::systemInv, interface::invAsset); 280 if (!service.empty()) 281 { 282 DBusValue value; 283 getProperty(service, object_path::systemInv, interface::invAsset, 284 "SerialNumber", value); 285 286 sn = std::get<std::string>(value); 287 } 288 } 289 catch (const std::exception& e) 290 { 291 log<level::WARNING>( 292 fmt::format("Failed reading SerialNumber property from " 293 "Interface: {} exception: {}", 294 interface::invAsset, e.what()) 295 .c_str()); 296 } 297 298 return sn; 299 } 300 301 std::string DataInterface::getMotherboardCCIN() const 302 { 303 std::string ccin; 304 305 try 306 { 307 auto service = 308 getService(object_path::motherBoardInv, interface::viniRecordVPD); 309 if (!service.empty()) 310 { 311 DBusValue value; 312 getProperty(service, object_path::motherBoardInv, 313 interface::viniRecordVPD, "CC", value); 314 315 auto cc = std::get<std::vector<uint8_t>>(value); 316 ccin = std::string{cc.begin(), cc.end()}; 317 } 318 } 319 catch (const std::exception& e) 320 { 321 log<level::WARNING>( 322 fmt::format("Failed reading Motherboard CCIN property from " 323 "Interface: {} exception: {}", 324 interface::viniRecordVPD, e.what()) 325 .c_str()); 326 } 327 328 return ccin; 329 } 330 331 std::vector<uint8_t> DataInterface::getSystemIMKeyword() const 332 { 333 std::vector<uint8_t> systemIM; 334 335 try 336 { 337 auto service = 338 getService(object_path::motherBoardInv, interface::vsbpRecordVPD); 339 if (!service.empty()) 340 { 341 DBusValue value; 342 getProperty(service, object_path::motherBoardInv, 343 interface::vsbpRecordVPD, "IM", value); 344 345 systemIM = std::get<std::vector<uint8_t>>(value); 346 } 347 } 348 catch (const std::exception& e) 349 { 350 log<level::WARNING>( 351 fmt::format("Failed reading System IM property from " 352 "Interface: {} exception: {}", 353 interface::vsbpRecordVPD, e.what()) 354 .c_str()); 355 } 356 357 return systemIM; 358 } 359 360 void DataInterface::getHWCalloutFields(const std::string& inventoryPath, 361 std::string& fruPartNumber, 362 std::string& ccin, 363 std::string& serialNumber) const 364 { 365 // For now, attempt to get all of the properties directly on the path 366 // passed in. In the future, may need to make use of an algorithm 367 // to figure out which inventory objects actually hold these 368 // interfaces in the case of non FRUs, or possibly another service 369 // will provide this info. Any missing interfaces will result 370 // in exceptions being thrown. 371 372 auto service = getService(inventoryPath, interface::viniRecordVPD); 373 374 auto properties = 375 getAllProperties(service, inventoryPath, interface::viniRecordVPD); 376 377 auto value = std::get<std::vector<uint8_t>>(properties["FN"]); 378 fruPartNumber = std::string{value.begin(), value.end()}; 379 380 value = std::get<std::vector<uint8_t>>(properties["CC"]); 381 ccin = std::string{value.begin(), value.end()}; 382 383 value = std::get<std::vector<uint8_t>>(properties["SN"]); 384 serialNumber = std::string{value.begin(), value.end()}; 385 } 386 387 std::string 388 DataInterface::getLocationCode(const std::string& inventoryPath) const 389 { 390 auto service = getService(inventoryPath, interface::locCode); 391 392 DBusValue locCode; 393 getProperty(service, inventoryPath, interface::locCode, "LocationCode", 394 locCode); 395 396 return std::get<std::string>(locCode); 397 } 398 399 std::string 400 DataInterface::addLocationCodePrefix(const std::string& locationCode) 401 { 402 static const std::string locationCodePrefix{"Ufcs-"}; 403 404 // Technically there are 2 location code prefixes, Ufcs and Umts, so 405 // if it already starts with a U then don't need to do anything. 406 if (locationCode.front() != 'U') 407 { 408 return locationCodePrefix + locationCode; 409 } 410 411 return locationCode; 412 } 413 414 std::string DataInterface::expandLocationCode(const std::string& locationCode, 415 uint16_t /*node*/) const 416 { 417 auto method = 418 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager, 419 interface::vpdManager, "GetExpandedLocationCode"); 420 421 method.append(addLocationCodePrefix(locationCode), 422 static_cast<uint16_t>(0)); 423 424 auto reply = _bus.call(method); 425 426 std::string expandedLocationCode; 427 reply.read(expandedLocationCode); 428 429 return expandedLocationCode; 430 } 431 432 std::string 433 DataInterface::getInventoryFromLocCode(const std::string& locationCode, 434 uint16_t node, bool expanded) const 435 { 436 std::string methodName = expanded ? "GetFRUsByExpandedLocationCode" 437 : "GetFRUsByUnexpandedLocationCode"; 438 439 auto method = 440 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager, 441 interface::vpdManager, methodName.c_str()); 442 443 if (expanded) 444 { 445 method.append(locationCode); 446 } 447 else 448 { 449 method.append(addLocationCodePrefix(locationCode), node); 450 } 451 452 auto reply = _bus.call(method); 453 454 std::vector<sdbusplus::message::object_path> entries; 455 reply.read(entries); 456 457 // Get the shortest entry from the paths received, as this 458 // would be the path furthest up the inventory hierarchy so 459 // would be the parent FRU. There is guaranteed to at least 460 // be one entry if the call didn't fail. 461 std::string shortest{entries[0]}; 462 463 std::for_each(entries.begin(), entries.end(), 464 [&shortest](const auto& path) { 465 if (path.str.size() < shortest.size()) 466 { 467 shortest = path; 468 } 469 }); 470 471 return shortest; 472 } 473 474 void DataInterface::assertLEDGroup(const std::string& ledGroup, 475 bool value) const 476 { 477 DBusValue variant = value; 478 auto method = 479 _bus.new_method_call(service_name::ledGroupManager, ledGroup.c_str(), 480 interface::dbusProperty, "Set"); 481 method.append(interface::ledGroup, "Asserted", variant); 482 _bus.call(method); 483 } 484 485 void DataInterface::setFunctional(const std::string& objectPath, 486 bool value) const 487 { 488 DBusValue variant = value; 489 auto service = getService(objectPath, interface::operationalStatus); 490 491 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(), 492 interface::dbusProperty, "Set"); 493 494 method.append(interface::operationalStatus, "Functional", variant); 495 _bus.call(method); 496 } 497 498 std::vector<std::string> DataInterface::getSystemNames() const 499 { 500 DBusSubTree subtree; 501 DBusValue names; 502 503 auto method = _bus.new_method_call(service_name::objectMapper, 504 object_path::objectMapper, 505 interface::objectMapper, "GetSubTree"); 506 method.append(std::string{"/"}, 0, 507 std::vector<std::string>{interface::compatible}); 508 auto reply = _bus.call(method); 509 510 reply.read(subtree); 511 if (subtree.empty()) 512 { 513 throw std::runtime_error("Compatible interface not on D-Bus"); 514 } 515 516 const auto& object = *(subtree.begin()); 517 const auto& path = object.first; 518 const auto& service = object.second.begin()->first; 519 520 getProperty(service, path, interface::compatible, "Names", names); 521 522 return std::get<std::vector<std::string>>(names); 523 } 524 525 bool DataInterface::getQuiesceOnError() const 526 { 527 bool ret = false; 528 529 try 530 { 531 auto service = 532 getService(object_path::logSetting, interface::logSetting); 533 if (!service.empty()) 534 { 535 DBusValue value; 536 getProperty(service, object_path::logSetting, interface::logSetting, 537 "QuiesceOnHwError", value); 538 539 ret = std::get<bool>(value); 540 } 541 } 542 catch (const std::exception& e) 543 { 544 log<level::WARNING>( 545 fmt::format("Failed reading QuiesceOnHwError property from " 546 "Interface: {} exception: {}", 547 interface::logSetting, e.what()) 548 .c_str()); 549 } 550 551 return ret; 552 } 553 554 } // namespace pels 555 } // namespace openpower 556