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