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