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::optional<std::string> 98 redfishPcieGenerationFromDbus(const std::string& generationInUse) 99 { 100 if (generationInUse == 101 "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen1") 102 { 103 return "Gen1"; 104 } 105 if (generationInUse == 106 "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen2") 107 { 108 return "Gen2"; 109 } 110 if (generationInUse == 111 "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen3") 112 { 113 return "Gen3"; 114 } 115 if (generationInUse == 116 "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen4") 117 { 118 return "Gen4"; 119 } 120 if (generationInUse == 121 "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen5") 122 { 123 return "Gen5"; 124 } 125 if (generationInUse.empty() || 126 generationInUse == 127 "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Unknown") 128 { 129 return ""; 130 } 131 132 // The value is not unknown or Gen1-5, need return an internal error. 133 return std::nullopt; 134 } 135 136 inline void requestRoutesSystemPCIeDevice(App& app) 137 { 138 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/PCIeDevices/<str>/") 139 .privileges(redfish::privileges::getPCIeDevice) 140 .methods(boost::beast::http::verb::get)( 141 [](const crow::Request&, 142 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 143 const std::string& device) 144 145 { 146 auto getPCIeDeviceCallback = [asyncResp, device]( 147 const boost::system::error_code 148 ec, 149 boost::container::flat_map< 150 std::string, 151 std::variant<std::string>>& 152 pcieDevProperties) { 153 if (ec) 154 { 155 BMCWEB_LOG_DEBUG 156 << "failed to get PCIe Device properties ec: " 157 << ec.value() << ": " << ec.message(); 158 if (ec.value() == 159 boost::system::linux_error::bad_request_descriptor) 160 { 161 messages::resourceNotFound(asyncResp->res, 162 "PCIeDevice", device); 163 } 164 else 165 { 166 messages::internalError(asyncResp->res); 167 } 168 return; 169 } 170 171 asyncResp->res.jsonValue = { 172 {"@odata.type", "#PCIeDevice.v1_4_0.PCIeDevice"}, 173 {"@odata.id", 174 "/redfish/v1/Systems/system/PCIeDevices/" + device}, 175 {"Name", "PCIe Device"}, 176 {"Id", device}}; 177 178 if (std::string* property = std::get_if<std::string>( 179 &pcieDevProperties["Manufacturer"]); 180 property) 181 { 182 asyncResp->res.jsonValue["Manufacturer"] = *property; 183 } 184 185 if (std::string* property = std::get_if<std::string>( 186 &pcieDevProperties["DeviceType"]); 187 property) 188 { 189 asyncResp->res.jsonValue["DeviceType"] = *property; 190 } 191 192 if (std::string* property = std::get_if<std::string>( 193 &pcieDevProperties["GenerationInUse"]); 194 property) 195 { 196 std::optional<std::string> generationInUse = 197 redfishPcieGenerationFromDbus(*property); 198 if (!generationInUse) 199 { 200 messages::internalError(asyncResp->res); 201 return; 202 } 203 if (*generationInUse == "") 204 { 205 // unknown, no need to handle 206 return; 207 } 208 asyncResp->res.jsonValue["PCIeInterface"]["PcieType"] = 209 *generationInUse; 210 } 211 asyncResp->res.jsonValue["PCIeFunctions"] = { 212 {"@odata.id", 213 "/redfish/v1/Systems/system/PCIeDevices/" + device + 214 "/PCIeFunctions"}}; 215 }; 216 std::string escapedPath = std::string(pciePath) + "/" + device; 217 dbus::utility::escapePathForDbus(escapedPath); 218 crow::connections::systemBus->async_method_call( 219 std::move(getPCIeDeviceCallback), pcieService, escapedPath, 220 "org.freedesktop.DBus.Properties", "GetAll", 221 pcieDeviceInterface); 222 }); 223 } 224 225 inline void requestRoutesSystemPCIeFunctionCollection(App& app) 226 { 227 /** 228 * Functions triggers appropriate requests on DBus 229 */ 230 BMCWEB_ROUTE(app, 231 "/redfish/v1/Systems/system/PCIeDevices/<str>/PCIeFunctions/") 232 .privileges(redfish::privileges::getPCIeFunctionCollection) 233 .methods(boost::beast::http::verb::get)( 234 [](const crow::Request&, 235 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 236 const std::string& device) 237 238 { 239 asyncResp->res.jsonValue = { 240 {"@odata.type", 241 "#PCIeFunctionCollection.PCIeFunctionCollection"}, 242 {"@odata.id", "/redfish/v1/Systems/system/PCIeDevices/" + 243 device + "/PCIeFunctions"}, 244 {"Name", "PCIe Function Collection"}, 245 {"Description", 246 "Collection of PCIe Functions for PCIe Device " + device}}; 247 248 auto getPCIeDeviceCallback = [asyncResp, device]( 249 const boost::system::error_code 250 ec, 251 boost::container::flat_map< 252 std::string, 253 std::variant<std::string>>& 254 pcieDevProperties) { 255 if (ec) 256 { 257 BMCWEB_LOG_DEBUG 258 << "failed to get PCIe Device properties ec: " 259 << ec.value() << ": " << ec.message(); 260 if (ec.value() == 261 boost::system::linux_error::bad_request_descriptor) 262 { 263 messages::resourceNotFound(asyncResp->res, 264 "PCIeDevice", device); 265 } 266 else 267 { 268 messages::internalError(asyncResp->res); 269 } 270 return; 271 } 272 273 nlohmann::json& pcieFunctionList = 274 asyncResp->res.jsonValue["Members"]; 275 pcieFunctionList = nlohmann::json::array(); 276 static constexpr const int maxPciFunctionNum = 8; 277 for (int functionNum = 0; functionNum < maxPciFunctionNum; 278 functionNum++) 279 { 280 // Check if this function exists by looking for a device 281 // ID 282 std::string devIDProperty = 283 "Function" + std::to_string(functionNum) + 284 "DeviceId"; 285 std::string* property = std::get_if<std::string>( 286 &pcieDevProperties[devIDProperty]); 287 if (property && !property->empty()) 288 { 289 pcieFunctionList.push_back( 290 {{"@odata.id", 291 "/redfish/v1/Systems/system/PCIeDevices/" + 292 device + "/PCIeFunctions/" + 293 std::to_string(functionNum)}}); 294 } 295 } 296 asyncResp->res.jsonValue["PCIeFunctions@odata.count"] = 297 pcieFunctionList.size(); 298 }; 299 std::string escapedPath = std::string(pciePath) + "/" + device; 300 dbus::utility::escapePathForDbus(escapedPath); 301 crow::connections::systemBus->async_method_call( 302 std::move(getPCIeDeviceCallback), pcieService, escapedPath, 303 "org.freedesktop.DBus.Properties", "GetAll", 304 pcieDeviceInterface); 305 }); 306 } 307 308 inline void requestRoutesSystemPCIeFunction(App& app) 309 { 310 BMCWEB_ROUTE( 311 app, 312 "/redfish/v1/Systems/system/PCIeDevices/<str>/PCIeFunctions/<str>/") 313 .privileges(redfish::privileges::getPCIeFunction) 314 .methods( 315 boost::beast::http::verb::get)([](const crow::Request&, 316 const std::shared_ptr< 317 bmcweb::AsyncResp>& asyncResp, 318 const std::string& device, 319 const std::string& function) { 320 auto getPCIeDeviceCallback = [asyncResp, device, function]( 321 const boost::system::error_code ec, 322 boost::container::flat_map< 323 std::string, 324 std::variant<std::string>>& 325 pcieDevProperties) { 326 if (ec) 327 { 328 BMCWEB_LOG_DEBUG 329 << "failed to get PCIe Device properties ec: " 330 << ec.value() << ": " << ec.message(); 331 if (ec.value() == 332 boost::system::linux_error::bad_request_descriptor) 333 { 334 messages::resourceNotFound(asyncResp->res, "PCIeDevice", 335 device); 336 } 337 else 338 { 339 messages::internalError(asyncResp->res); 340 } 341 return; 342 } 343 344 // Check if this function exists by looking for a device ID 345 std::string devIDProperty = "Function" + function + "DeviceId"; 346 if (std::string* property = std::get_if<std::string>( 347 &pcieDevProperties[devIDProperty]); 348 property && property->empty()) 349 { 350 messages::resourceNotFound(asyncResp->res, "PCIeFunction", 351 function); 352 return; 353 } 354 355 asyncResp->res.jsonValue = { 356 {"@odata.type", "#PCIeFunction.v1_2_0.PCIeFunction"}, 357 {"@odata.id", "/redfish/v1/Systems/system/PCIeDevices/" + 358 device + "/PCIeFunctions/" + function}, 359 {"Name", "PCIe Function"}, 360 {"Id", function}, 361 {"FunctionId", std::stoi(function)}, 362 {"Links", 363 {{"PCIeDevice", 364 {{"@odata.id", 365 "/redfish/v1/Systems/system/PCIeDevices/" + 366 device}}}}}}; 367 368 if (std::string* property = std::get_if<std::string>( 369 &pcieDevProperties["Function" + function + "DeviceId"]); 370 property) 371 { 372 asyncResp->res.jsonValue["DeviceId"] = *property; 373 } 374 375 if (std::string* property = std::get_if<std::string>( 376 &pcieDevProperties["Function" + function + "VendorId"]); 377 property) 378 { 379 asyncResp->res.jsonValue["VendorId"] = *property; 380 } 381 382 if (std::string* property = std::get_if<std::string>( 383 &pcieDevProperties["Function" + function + 384 "FunctionType"]); 385 property) 386 { 387 asyncResp->res.jsonValue["FunctionType"] = *property; 388 } 389 390 if (std::string* property = std::get_if<std::string>( 391 &pcieDevProperties["Function" + function + 392 "DeviceClass"]); 393 property) 394 { 395 asyncResp->res.jsonValue["DeviceClass"] = *property; 396 } 397 398 if (std::string* property = std::get_if<std::string>( 399 &pcieDevProperties["Function" + function + 400 "ClassCode"]); 401 property) 402 { 403 asyncResp->res.jsonValue["ClassCode"] = *property; 404 } 405 406 if (std::string* property = std::get_if<std::string>( 407 &pcieDevProperties["Function" + function + 408 "RevisionId"]); 409 property) 410 { 411 asyncResp->res.jsonValue["RevisionId"] = *property; 412 } 413 414 if (std::string* property = std::get_if<std::string>( 415 &pcieDevProperties["Function" + function + 416 "SubsystemId"]); 417 property) 418 { 419 asyncResp->res.jsonValue["SubsystemId"] = *property; 420 } 421 422 if (std::string* property = std::get_if<std::string>( 423 &pcieDevProperties["Function" + function + 424 "SubsystemVendorId"]); 425 property) 426 { 427 asyncResp->res.jsonValue["SubsystemVendorId"] = *property; 428 } 429 }; 430 std::string escapedPath = std::string(pciePath) + "/" + device; 431 dbus::utility::escapePathForDbus(escapedPath); 432 crow::connections::systemBus->async_method_call( 433 std::move(getPCIeDeviceCallback), pcieService, escapedPath, 434 "org.freedesktop.DBus.Properties", "GetAll", 435 pcieDeviceInterface); 436 }); 437 } 438 439 } // namespace redfish 440