1 /* 2 // Copyright (c) 2018 Intel 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 17 #pragma once 18 19 #include <app.hpp> 20 #include <boost/system/linux_error.hpp> 21 22 namespace redfish 23 { 24 25 static constexpr char const* pcieService = "xyz.openbmc_project.PCIe"; 26 static constexpr char const* pciePath = "/xyz/openbmc_project/PCIe"; 27 static constexpr char const* pcieDeviceInterface = 28 "xyz.openbmc_project.PCIe.Device"; 29 30 static inline void 31 getPCIeDeviceList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 32 const std::string& name) 33 { 34 auto getPCIeMapCallback = [asyncResp, name]( 35 const boost::system::error_code ec, 36 std::vector<std::string>& pcieDevicePaths) { 37 if (ec) 38 { 39 BMCWEB_LOG_DEBUG << "no PCIe device paths found ec: " 40 << ec.message(); 41 // Not an error, system just doesn't have PCIe info 42 return; 43 } 44 nlohmann::json& pcieDeviceList = asyncResp->res.jsonValue[name]; 45 pcieDeviceList = nlohmann::json::array(); 46 for (const std::string& pcieDevicePath : pcieDevicePaths) 47 { 48 size_t devStart = pcieDevicePath.rfind('/'); 49 if (devStart == std::string::npos) 50 { 51 continue; 52 } 53 54 std::string devName = pcieDevicePath.substr(devStart + 1); 55 if (devName.empty()) 56 { 57 continue; 58 } 59 pcieDeviceList.push_back( 60 {{"@odata.id", 61 "/redfish/v1/Systems/system/PCIeDevices/" + devName}}); 62 } 63 asyncResp->res.jsonValue[name + "@odata.count"] = pcieDeviceList.size(); 64 }; 65 crow::connections::systemBus->async_method_call( 66 std::move(getPCIeMapCallback), "xyz.openbmc_project.ObjectMapper", 67 "/xyz/openbmc_project/object_mapper", 68 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 69 std::string(pciePath) + "/", 1, std::array<std::string, 0>()); 70 } 71 72 inline void requestRoutesSystemPCIeDeviceCollection(App& app) 73 { 74 /** 75 * Functions triggers appropriate requests on DBus 76 */ 77 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/PCIeDevices/") 78 .privileges({"Login"}) 79 .methods(boost::beast::http::verb::get)( 80 [](const crow::Request&, 81 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 82 83 { 84 asyncResp->res.jsonValue = { 85 {"@odata.type", 86 "#PCIeDeviceCollection.PCIeDeviceCollection"}, 87 {"@odata.id", "/redfish/v1/Systems/system/PCIeDevices"}, 88 {"Name", "PCIe Device Collection"}, 89 {"Description", "Collection of PCIe Devices"}, 90 {"Members", nlohmann::json::array()}, 91 {"Members@odata.count", 0}}; 92 getPCIeDeviceList(asyncResp, "Members"); 93 }); 94 } 95 96 inline void requestRoutesSystemPCIeDevice(App& app) 97 { 98 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/PCIeDevices/<str>/") 99 .privileges({"Login"}) 100 .methods(boost::beast::http::verb::get)( 101 [](const crow::Request&, 102 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 103 const std::string& device) 104 105 { 106 auto getPCIeDeviceCallback = [asyncResp, device]( 107 const boost::system::error_code 108 ec, 109 boost::container::flat_map< 110 std::string, 111 std::variant<std::string>>& 112 pcieDevProperties) { 113 if (ec) 114 { 115 BMCWEB_LOG_DEBUG 116 << "failed to get PCIe Device properties ec: " 117 << ec.value() << ": " << ec.message(); 118 if (ec.value() == 119 boost::system::linux_error::bad_request_descriptor) 120 { 121 messages::resourceNotFound(asyncResp->res, 122 "PCIeDevice", device); 123 } 124 else 125 { 126 messages::internalError(asyncResp->res); 127 } 128 return; 129 } 130 131 asyncResp->res.jsonValue = { 132 {"@odata.type", "#PCIeDevice.v1_4_0.PCIeDevice"}, 133 {"@odata.id", 134 "/redfish/v1/Systems/system/PCIeDevices/" + device}, 135 {"Name", "PCIe Device"}, 136 {"Id", device}}; 137 138 if (std::string* property = std::get_if<std::string>( 139 &pcieDevProperties["Manufacturer"]); 140 property) 141 { 142 asyncResp->res.jsonValue["Manufacturer"] = *property; 143 } 144 145 if (std::string* property = std::get_if<std::string>( 146 &pcieDevProperties["DeviceType"]); 147 property) 148 { 149 asyncResp->res.jsonValue["DeviceType"] = *property; 150 } 151 152 asyncResp->res.jsonValue["PCIeFunctions"] = { 153 {"@odata.id", 154 "/redfish/v1/Systems/system/PCIeDevices/" + device + 155 "/PCIeFunctions"}}; 156 }; 157 std::string escapedPath = std::string(pciePath) + "/" + device; 158 dbus::utility::escapePathForDbus(escapedPath); 159 crow::connections::systemBus->async_method_call( 160 std::move(getPCIeDeviceCallback), pcieService, escapedPath, 161 "org.freedesktop.DBus.Properties", "GetAll", 162 pcieDeviceInterface); 163 }); 164 } 165 166 inline void requestRoutesSystemPCIeFunctionCollection(App& app) 167 { 168 /** 169 * Functions triggers appropriate requests on DBus 170 */ 171 BMCWEB_ROUTE(app, 172 "/redfish/v1/Systems/system/PCIeDevices/<str>/PCIeFunctions/") 173 .privileges({"Login"}) 174 .methods(boost::beast::http::verb::get)( 175 [](const crow::Request&, 176 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 177 const std::string& device) 178 179 { 180 asyncResp->res.jsonValue = { 181 {"@odata.type", 182 "#PCIeFunctionCollection.PCIeFunctionCollection"}, 183 {"@odata.id", "/redfish/v1/Systems/system/PCIeDevices/" + 184 device + "/PCIeFunctions"}, 185 {"Name", "PCIe Function Collection"}, 186 {"Description", 187 "Collection of PCIe Functions for PCIe Device " + device}}; 188 189 auto getPCIeDeviceCallback = [asyncResp, device]( 190 const boost::system::error_code 191 ec, 192 boost::container::flat_map< 193 std::string, 194 std::variant<std::string>>& 195 pcieDevProperties) { 196 if (ec) 197 { 198 BMCWEB_LOG_DEBUG 199 << "failed to get PCIe Device properties ec: " 200 << ec.value() << ": " << ec.message(); 201 if (ec.value() == 202 boost::system::linux_error::bad_request_descriptor) 203 { 204 messages::resourceNotFound(asyncResp->res, 205 "PCIeDevice", device); 206 } 207 else 208 { 209 messages::internalError(asyncResp->res); 210 } 211 return; 212 } 213 214 nlohmann::json& pcieFunctionList = 215 asyncResp->res.jsonValue["Members"]; 216 pcieFunctionList = nlohmann::json::array(); 217 static constexpr const int maxPciFunctionNum = 8; 218 for (int functionNum = 0; functionNum < maxPciFunctionNum; 219 functionNum++) 220 { 221 // Check if this function exists by looking for a device 222 // ID 223 std::string devIDProperty = 224 "Function" + std::to_string(functionNum) + 225 "DeviceId"; 226 std::string* property = std::get_if<std::string>( 227 &pcieDevProperties[devIDProperty]); 228 if (property && !property->empty()) 229 { 230 pcieFunctionList.push_back( 231 {{"@odata.id", 232 "/redfish/v1/Systems/system/PCIeDevices/" + 233 device + "/PCIeFunctions/" + 234 std::to_string(functionNum)}}); 235 } 236 } 237 asyncResp->res.jsonValue["PCIeFunctions@odata.count"] = 238 pcieFunctionList.size(); 239 }; 240 std::string escapedPath = std::string(pciePath) + "/" + device; 241 dbus::utility::escapePathForDbus(escapedPath); 242 crow::connections::systemBus->async_method_call( 243 std::move(getPCIeDeviceCallback), pcieService, escapedPath, 244 "org.freedesktop.DBus.Properties", "GetAll", 245 pcieDeviceInterface); 246 }); 247 } 248 249 inline void requestRoutesSystemPCIeFunction(App& app) 250 { 251 BMCWEB_ROUTE( 252 app, 253 "/redfish/v1/Systems/system/PCIeDevices/<str>/PCIeFunctions/<str>/") 254 .privileges({"Login"}) 255 .methods( 256 boost::beast::http::verb::get)([](const crow::Request&, 257 const std::shared_ptr< 258 bmcweb::AsyncResp>& asyncResp, 259 const std::string& device, 260 const std::string& function) { 261 auto getPCIeDeviceCallback = [asyncResp, device, function]( 262 const boost::system::error_code ec, 263 boost::container::flat_map< 264 std::string, 265 std::variant<std::string>>& 266 pcieDevProperties) { 267 if (ec) 268 { 269 BMCWEB_LOG_DEBUG 270 << "failed to get PCIe Device properties ec: " 271 << ec.value() << ": " << ec.message(); 272 if (ec.value() == 273 boost::system::linux_error::bad_request_descriptor) 274 { 275 messages::resourceNotFound(asyncResp->res, "PCIeDevice", 276 device); 277 } 278 else 279 { 280 messages::internalError(asyncResp->res); 281 } 282 return; 283 } 284 285 // Check if this function exists by looking for a device ID 286 std::string devIDProperty = "Function" + function + "DeviceId"; 287 if (std::string* property = std::get_if<std::string>( 288 &pcieDevProperties[devIDProperty]); 289 property && property->empty()) 290 { 291 messages::resourceNotFound(asyncResp->res, "PCIeFunction", 292 function); 293 return; 294 } 295 296 asyncResp->res.jsonValue = { 297 {"@odata.type", "#PCIeFunction.v1_2_0.PCIeFunction"}, 298 {"@odata.id", "/redfish/v1/Systems/system/PCIeDevices/" + 299 device + "/PCIeFunctions/" + function}, 300 {"Name", "PCIe Function"}, 301 {"Id", function}, 302 {"FunctionId", std::stoi(function)}, 303 {"Links", 304 {{"PCIeDevice", 305 {{"@odata.id", 306 "/redfish/v1/Systems/system/PCIeDevices/" + 307 device}}}}}}; 308 309 if (std::string* property = std::get_if<std::string>( 310 &pcieDevProperties["Function" + function + "DeviceId"]); 311 property) 312 { 313 asyncResp->res.jsonValue["DeviceId"] = *property; 314 } 315 316 if (std::string* property = std::get_if<std::string>( 317 &pcieDevProperties["Function" + function + "VendorId"]); 318 property) 319 { 320 asyncResp->res.jsonValue["VendorId"] = *property; 321 } 322 323 if (std::string* property = std::get_if<std::string>( 324 &pcieDevProperties["Function" + function + 325 "FunctionType"]); 326 property) 327 { 328 asyncResp->res.jsonValue["FunctionType"] = *property; 329 } 330 331 if (std::string* property = std::get_if<std::string>( 332 &pcieDevProperties["Function" + function + 333 "DeviceClass"]); 334 property) 335 { 336 asyncResp->res.jsonValue["DeviceClass"] = *property; 337 } 338 339 if (std::string* property = std::get_if<std::string>( 340 &pcieDevProperties["Function" + function + 341 "ClassCode"]); 342 property) 343 { 344 asyncResp->res.jsonValue["ClassCode"] = *property; 345 } 346 347 if (std::string* property = std::get_if<std::string>( 348 &pcieDevProperties["Function" + function + 349 "RevisionId"]); 350 property) 351 { 352 asyncResp->res.jsonValue["RevisionId"] = *property; 353 } 354 355 if (std::string* property = std::get_if<std::string>( 356 &pcieDevProperties["Function" + function + 357 "SubsystemId"]); 358 property) 359 { 360 asyncResp->res.jsonValue["SubsystemId"] = *property; 361 } 362 363 if (std::string* property = std::get_if<std::string>( 364 &pcieDevProperties["Function" + function + 365 "SubsystemVendorId"]); 366 property) 367 { 368 asyncResp->res.jsonValue["SubsystemVendorId"] = *property; 369 } 370 }; 371 std::string escapedPath = std::string(pciePath) + "/" + device; 372 dbus::utility::escapePathForDbus(escapedPath); 373 crow::connections::systemBus->async_method_call( 374 std::move(getPCIeDeviceCallback), pcieService, escapedPath, 375 "org.freedesktop.DBus.Properties", "GetAll", 376 pcieDeviceInterface); 377 }); 378 } 379 380 } // namespace redfish 381