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