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 <fstream> 21 #include <xyz/openbmc_project/State/OperatingSystem/Status/server.hpp> 22 23 namespace openpower 24 { 25 namespace pels 26 { 27 28 namespace service_name 29 { 30 constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper"; 31 } // namespace service_name 32 33 namespace object_path 34 { 35 constexpr auto objectMapper = "/xyz/openbmc_project/object_mapper"; 36 constexpr auto systemInv = "/xyz/openbmc_project/inventory/system"; 37 constexpr auto baseInv = "/xyz/openbmc_project/inventory"; 38 constexpr auto bmcState = "/xyz/openbmc_project/state/bmc0"; 39 constexpr auto chassisState = "/xyz/openbmc_project/state/chassis0"; 40 constexpr auto hostState = "/xyz/openbmc_project/state/host0"; 41 constexpr auto pldm = "/xyz/openbmc_project/pldm"; 42 constexpr auto enableHostPELs = 43 "/xyz/openbmc_project/logging/send_event_logs_to_host"; 44 } // namespace object_path 45 46 namespace interface 47 { 48 constexpr auto dbusProperty = "org.freedesktop.DBus.Properties"; 49 constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper"; 50 constexpr auto invAsset = "xyz.openbmc_project.Inventory.Decorator.Asset"; 51 constexpr auto osStatus = "xyz.openbmc_project.State.OperatingSystem.Status"; 52 constexpr auto pldmRequester = "xyz.openbmc_project.PLDM.Requester"; 53 constexpr auto enable = "xyz.openbmc_project.Object.Enable"; 54 constexpr auto bmcState = "xyz.openbmc_project.State.BMC"; 55 constexpr auto chassisState = "xyz.openbmc_project.State.Chassis"; 56 constexpr auto hostState = "xyz.openbmc_project.State.Host"; 57 constexpr auto invMotherboard = 58 "xyz.openbmc_project.Inventory.Item.Board.Motherboard"; 59 constexpr auto viniRecordVPD = "com.ibm.ipzvpd.VINI"; 60 constexpr auto locCode = "com.ibm.ipzvpd.Location"; 61 } // namespace interface 62 63 using namespace sdbusplus::xyz::openbmc_project::State::OperatingSystem::server; 64 using sdbusplus::exception::SdBusError; 65 66 DataInterface::DataInterface(sdbusplus::bus::bus& bus) : _bus(bus) 67 { 68 readBMCFWVersion(); 69 readServerFWVersion(); 70 readBMCFWVersionID(); 71 readMotherboardCCIN(); 72 73 // Watch both the Model and SN properties on the system's Asset iface 74 _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>( 75 bus, object_path::systemInv, interface::invAsset, *this, 76 [this](const auto& properties) { 77 auto model = properties.find("Model"); 78 if (model != properties.end()) 79 { 80 this->_machineTypeModel = std::get<std::string>(model->second); 81 } 82 83 auto sn = properties.find("SerialNumber"); 84 if (sn != properties.end()) 85 { 86 this->_machineSerialNumber = std::get<std::string>(sn->second); 87 } 88 })); 89 90 // Watch the OperatingSystemState property 91 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>( 92 bus, object_path::hostState, interface::osStatus, 93 "OperatingSystemState", *this, [this](const auto& value) { 94 auto status = 95 Status::convertOSStatusFromString(std::get<std::string>(value)); 96 97 if ((status == Status::OSStatus::BootComplete) || 98 (status == Status::OSStatus::Standby)) 99 { 100 setHostUp(true); 101 } 102 else 103 { 104 setHostUp(false); 105 } 106 })); 107 108 // Watch the host PEL enable property 109 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>( 110 bus, object_path::enableHostPELs, interface::enable, "Enabled", *this, 111 [this](const auto& value) { 112 this->_sendPELsToHost = std::get<bool>(value); 113 })); 114 115 // Watch the BMCState property 116 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>( 117 bus, object_path::bmcState, interface::bmcState, "CurrentBMCState", 118 *this, [this](const auto& value) { 119 this->_bmcState = std::get<std::string>(value); 120 })); 121 122 // Watch the chassis current and requested power state properties 123 _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>( 124 bus, object_path::chassisState, interface::chassisState, *this, 125 [this](const auto& properties) { 126 auto state = properties.find("CurrentPowerState"); 127 if (state != properties.end()) 128 { 129 this->_chassisState = std::get<std::string>(state->second); 130 } 131 132 auto trans = properties.find("RequestedPowerTransition"); 133 if (trans != properties.end()) 134 { 135 this->_chassisTransition = std::get<std::string>(trans->second); 136 } 137 })); 138 139 // Watch the CurrentHostState property 140 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>( 141 bus, object_path::hostState, interface::hostState, "CurrentHostState", 142 *this, [this](const auto& value) { 143 this->_hostState = std::get<std::string>(value); 144 })); 145 } 146 147 DBusPropertyMap 148 DataInterface::getAllProperties(const std::string& service, 149 const std::string& objectPath, 150 const std::string& interface) const 151 { 152 DBusPropertyMap properties; 153 154 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(), 155 interface::dbusProperty, "GetAll"); 156 method.append(interface); 157 auto reply = _bus.call(method); 158 159 reply.read(properties); 160 161 return properties; 162 } 163 164 void DataInterface::getProperty(const std::string& service, 165 const std::string& objectPath, 166 const std::string& interface, 167 const std::string& property, 168 DBusValue& value) const 169 { 170 171 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(), 172 interface::dbusProperty, "Get"); 173 method.append(interface, property); 174 auto reply = _bus.call(method); 175 176 reply.read(value); 177 } 178 179 DBusPathList DataInterface::getPaths(const DBusInterfaceList& interfaces) const 180 { 181 182 auto method = _bus.new_method_call( 183 service_name::objectMapper, object_path::objectMapper, 184 interface::objectMapper, "GetSubTreePaths"); 185 186 method.append(std::string{"/"}, 0, interfaces); 187 188 auto reply = _bus.call(method); 189 190 DBusPathList paths; 191 reply.read(paths); 192 193 return paths; 194 } 195 196 DBusService DataInterface::getService(const std::string& objectPath, 197 const std::string& interface) const 198 { 199 auto method = _bus.new_method_call(service_name::objectMapper, 200 object_path::objectMapper, 201 interface::objectMapper, "GetObject"); 202 203 method.append(objectPath, std::vector<std::string>({interface})); 204 205 auto reply = _bus.call(method); 206 207 std::map<DBusService, DBusInterfaceList> response; 208 reply.read(response); 209 210 if (!response.empty()) 211 { 212 return response.begin()->first; 213 } 214 215 return std::string{}; 216 } 217 218 /** 219 * @brief Return a value found in the /etc/os-release file 220 * 221 * @param[in] key - The key name, like "VERSION" 222 * 223 * @return std::optional<std::string> - The value 224 */ 225 std::optional<std::string> getOSReleaseValue(const std::string& key) 226 { 227 std::ifstream versionFile{BMC_VERSION_FILE}; 228 std::string line; 229 std::string keyPattern{key + '='}; 230 231 while (std::getline(versionFile, line)) 232 { 233 if (line.find(keyPattern) != std::string::npos) 234 { 235 auto pos = line.find_first_of('"') + 1; 236 auto value = line.substr(pos, line.find_last_of('"') - pos); 237 return value; 238 } 239 } 240 241 return std::nullopt; 242 } 243 244 void DataInterface::readBMCFWVersion() 245 { 246 _bmcFWVersion = getOSReleaseValue("VERSION").value_or(""); 247 } 248 249 void DataInterface::readServerFWVersion() 250 { 251 // Not available yet 252 } 253 254 void DataInterface::readBMCFWVersionID() 255 { 256 _bmcFWVersionID = getOSReleaseValue("VERSION_ID").value_or(""); 257 } 258 259 void DataInterface::readMotherboardCCIN() 260 { 261 try 262 { 263 // First, try to find the motherboard 264 auto motherboards = getPaths({interface::invMotherboard}); 265 if (motherboards.empty()) 266 { 267 throw std::runtime_error("No motherboards yet"); 268 } 269 270 // Found it, so now get the CCIN 271 _properties.emplace_back( 272 std::make_unique<PropertyWatcher<DataInterface>>( 273 _bus, motherboards.front(), interface::viniRecordVPD, "CC", 274 *this, 275 [this](const auto& ccin) { this->setMotherboardCCIN(ccin); })); 276 } 277 catch (const std::exception& e) 278 { 279 // No motherboard in the inventory yet - watch for it 280 _inventoryIfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>( 281 _bus, match_rules::interfacesAdded(object_path::baseInv), 282 std::bind(std::mem_fn(&DataInterface::motherboardIfaceAdded), this, 283 std::placeholders::_1)); 284 } 285 } 286 287 void DataInterface::motherboardIfaceAdded(sdbusplus::message::message& msg) 288 { 289 sdbusplus::message::object_path path; 290 DBusInterfaceMap interfaces; 291 292 msg.read(path, interfaces); 293 294 // This is watching the whole inventory, so check if it's what we want 295 if (interfaces.find(interface::invMotherboard) == interfaces.end()) 296 { 297 return; 298 } 299 300 // Done watching for any new inventory interfaces 301 _inventoryIfacesAddedMatch.reset(); 302 303 // Watch the motherboard CCIN, using the service from this signal 304 // for the initial property read. 305 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>( 306 _bus, path, interface::viniRecordVPD, "CC", msg.get_sender(), *this, 307 [this](const auto& ccin) { this->setMotherboardCCIN(ccin); })); 308 } 309 310 void DataInterface::getHWCalloutFields(const std::string& inventoryPath, 311 std::string& fruPartNumber, 312 std::string& ccin, 313 std::string& serialNumber) const 314 { 315 // For now, attempt to get all of the properties directly on the path 316 // passed in. In the future, may need to make use of an algorithm 317 // to figure out which inventory objects actually hold these 318 // interfaces in the case of non FRUs, or possibly another service 319 // will provide this info. Any missing interfaces will result 320 // in exceptions being thrown. 321 322 auto service = getService(inventoryPath, interface::viniRecordVPD); 323 324 auto properties = 325 getAllProperties(service, inventoryPath, interface::viniRecordVPD); 326 327 auto value = std::get<std::vector<uint8_t>>(properties["FN"]); 328 fruPartNumber = std::string{value.begin(), value.end()}; 329 330 value = std::get<std::vector<uint8_t>>(properties["CC"]); 331 ccin = std::string{value.begin(), value.end()}; 332 333 value = std::get<std::vector<uint8_t>>(properties["SN"]); 334 serialNumber = std::string{value.begin(), value.end()}; 335 } 336 337 std::string 338 DataInterface::getLocationCode(const std::string& inventoryPath) const 339 { 340 auto service = getService(inventoryPath, interface::locCode); 341 342 DBusValue locCode; 343 getProperty(service, inventoryPath, interface::locCode, "LocationCode", 344 locCode); 345 346 return std::get<std::string>(locCode); 347 } 348 349 } // namespace pels 350 } // namespace openpower 351