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 } // namespace interface 61 62 using namespace sdbusplus::xyz::openbmc_project::State::OperatingSystem::server; 63 using sdbusplus::exception::SdBusError; 64 65 DataInterface::DataInterface(sdbusplus::bus::bus& bus) : _bus(bus) 66 { 67 readBMCFWVersion(); 68 readServerFWVersion(); 69 readBMCFWVersionID(); 70 readMotherboardCCIN(); 71 72 // Watch both the Model and SN properties on the system's Asset iface 73 _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>( 74 bus, object_path::systemInv, interface::invAsset, *this, 75 [this](const auto& properties) { 76 auto model = properties.find("Model"); 77 if (model != properties.end()) 78 { 79 this->_machineTypeModel = std::get<std::string>(model->second); 80 } 81 82 auto sn = properties.find("SerialNumber"); 83 if (sn != properties.end()) 84 { 85 this->_machineSerialNumber = std::get<std::string>(sn->second); 86 } 87 })); 88 89 // Watch the OperatingSystemState property 90 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>( 91 bus, object_path::hostState, interface::osStatus, 92 "OperatingSystemState", *this, [this](const auto& value) { 93 auto status = 94 Status::convertOSStatusFromString(std::get<std::string>(value)); 95 96 if ((status == Status::OSStatus::BootComplete) || 97 (status == Status::OSStatus::Standby)) 98 { 99 setHostUp(true); 100 } 101 else 102 { 103 setHostUp(false); 104 } 105 })); 106 107 // Watch the host PEL enable property 108 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>( 109 bus, object_path::enableHostPELs, interface::enable, "Enabled", *this, 110 [this](const auto& value) { 111 this->_sendPELsToHost = std::get<bool>(value); 112 })); 113 114 // Watch the BMCState property 115 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>( 116 bus, object_path::bmcState, interface::bmcState, "CurrentBMCState", 117 *this, [this](const auto& value) { 118 this->_bmcState = std::get<std::string>(value); 119 })); 120 121 // Watch the chassis current and requested power state properties 122 _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>( 123 bus, object_path::chassisState, interface::chassisState, *this, 124 [this](const auto& properties) { 125 auto state = properties.find("CurrentPowerState"); 126 if (state != properties.end()) 127 { 128 this->_chassisState = std::get<std::string>(state->second); 129 } 130 131 auto trans = properties.find("RequestedPowerTransition"); 132 if (trans != properties.end()) 133 { 134 this->_chassisTransition = std::get<std::string>(trans->second); 135 } 136 })); 137 138 // Watch the CurrentHostState property 139 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>( 140 bus, object_path::hostState, interface::hostState, "CurrentHostState", 141 *this, [this](const auto& value) { 142 this->_hostState = std::get<std::string>(value); 143 })); 144 } 145 146 DBusPropertyMap 147 DataInterface::getAllProperties(const std::string& service, 148 const std::string& objectPath, 149 const std::string& interface) const 150 { 151 DBusPropertyMap properties; 152 153 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(), 154 interface::dbusProperty, "GetAll"); 155 method.append(interface); 156 auto reply = _bus.call(method); 157 158 reply.read(properties); 159 160 return properties; 161 } 162 163 void DataInterface::getProperty(const std::string& service, 164 const std::string& objectPath, 165 const std::string& interface, 166 const std::string& property, 167 DBusValue& value) const 168 { 169 170 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(), 171 interface::dbusProperty, "Get"); 172 method.append(interface, property); 173 auto reply = _bus.call(method); 174 175 reply.read(value); 176 } 177 178 DBusPathList DataInterface::getPaths(const DBusInterfaceList& interfaces) const 179 { 180 181 auto method = _bus.new_method_call( 182 service_name::objectMapper, object_path::objectMapper, 183 interface::objectMapper, "GetSubTreePaths"); 184 185 method.append(std::string{"/"}, 0, interfaces); 186 187 auto reply = _bus.call(method); 188 189 DBusPathList paths; 190 reply.read(paths); 191 192 return paths; 193 } 194 195 DBusService DataInterface::getService(const std::string& objectPath, 196 const std::string& interface) const 197 { 198 auto method = _bus.new_method_call(service_name::objectMapper, 199 object_path::objectMapper, 200 interface::objectMapper, "GetObject"); 201 202 method.append(objectPath, std::vector<std::string>({interface})); 203 204 auto reply = _bus.call(method); 205 206 std::map<DBusService, DBusInterfaceList> response; 207 reply.read(response); 208 209 if (!response.empty()) 210 { 211 return response.begin()->first; 212 } 213 214 return std::string{}; 215 } 216 217 /** 218 * @brief Return a value found in the /etc/os-release file 219 * 220 * @param[in] key - The key name, like "VERSION" 221 * 222 * @return std::optional<std::string> - The value 223 */ 224 std::optional<std::string> getOSReleaseValue(const std::string& key) 225 { 226 std::ifstream versionFile{BMC_VERSION_FILE}; 227 std::string line; 228 std::string keyPattern{key + '='}; 229 230 while (std::getline(versionFile, line)) 231 { 232 if (line.find(keyPattern) != std::string::npos) 233 { 234 auto pos = line.find_first_of('"') + 1; 235 auto value = line.substr(pos, line.find_last_of('"') - pos); 236 return value; 237 } 238 } 239 240 return std::nullopt; 241 } 242 243 void DataInterface::readBMCFWVersion() 244 { 245 _bmcFWVersion = getOSReleaseValue("VERSION").value_or(""); 246 } 247 248 void DataInterface::readServerFWVersion() 249 { 250 // Not available yet 251 } 252 253 void DataInterface::readBMCFWVersionID() 254 { 255 _bmcFWVersionID = getOSReleaseValue("VERSION_ID").value_or(""); 256 } 257 258 void DataInterface::readMotherboardCCIN() 259 { 260 try 261 { 262 // First, try to find the motherboard 263 auto motherboards = getPaths({interface::invMotherboard}); 264 if (motherboards.empty()) 265 { 266 throw std::runtime_error("No motherboards yet"); 267 } 268 269 // Found it, so now get the CCIN 270 _properties.emplace_back( 271 std::make_unique<PropertyWatcher<DataInterface>>( 272 _bus, motherboards.front(), interface::viniRecordVPD, "CC", 273 *this, 274 [this](const auto& ccin) { this->setMotherboardCCIN(ccin); })); 275 } 276 catch (const std::exception& e) 277 { 278 // No motherboard in the inventory yet - watch for it 279 _inventoryIfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>( 280 _bus, match_rules::interfacesAdded(object_path::baseInv), 281 std::bind(std::mem_fn(&DataInterface::motherboardIfaceAdded), this, 282 std::placeholders::_1)); 283 } 284 } 285 286 void DataInterface::motherboardIfaceAdded(sdbusplus::message::message& msg) 287 { 288 sdbusplus::message::object_path path; 289 DBusInterfaceMap interfaces; 290 291 msg.read(path, interfaces); 292 293 // This is watching the whole inventory, so check if it's what we want 294 if (interfaces.find(interface::invMotherboard) == interfaces.end()) 295 { 296 return; 297 } 298 299 // Done watching for any new inventory interfaces 300 _inventoryIfacesAddedMatch.reset(); 301 302 // Watch the motherboard CCIN, using the service from this signal 303 // for the initial property read. 304 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>( 305 _bus, path, interface::viniRecordVPD, "CC", msg.get_sender(), *this, 306 [this](const auto& ccin) { this->setMotherboardCCIN(ccin); })); 307 } 308 309 } // namespace pels 310 } // namespace openpower 311