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