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