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