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