1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
4
5 #pragma once
6
7 #include "app.hpp"
8 #include "dbus_utility.hpp"
9 #include "generated/enums/resource.hpp"
10 #include "query.hpp"
11 #include "registries/privilege_registry.hpp"
12 #include "utils/collection.hpp"
13 #include "utils/dbus_utils.hpp"
14 #include "utils/pcie_util.hpp"
15
16 #include <boost/system/linux_error.hpp>
17 #include <boost/url/format.hpp>
18 #include <sdbusplus/asio/property.hpp>
19 #include <sdbusplus/unpack_properties.hpp>
20
21 #include <limits>
22
23 namespace redfish
24 {
25
26 static constexpr const char* inventoryPath = "/xyz/openbmc_project/inventory";
27 static constexpr std::array<std::string_view, 1> pcieDeviceInterface = {
28 "xyz.openbmc_project.Inventory.Item.PCIeDevice"};
29 static constexpr std::array<std::string_view, 1> pcieSlotInterface = {
30 "xyz.openbmc_project.Inventory.Item.PCIeSlot"};
31
handlePCIeDevicePath(const std::string & pcieDeviceId,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const dbus::utility::MapperGetSubTreePathsResponse & pcieDevicePaths,const std::function<void (const std::string & pcieDevicePath,const std::string & service)> & callback)32 inline void handlePCIeDevicePath(
33 const std::string& pcieDeviceId,
34 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
35 const dbus::utility::MapperGetSubTreePathsResponse& pcieDevicePaths,
36 const std::function<void(const std::string& pcieDevicePath,
37 const std::string& service)>& callback)
38
39 {
40 for (const std::string& pcieDevicePath : pcieDevicePaths)
41 {
42 std::string pciecDeviceName =
43 sdbusplus::message::object_path(pcieDevicePath).filename();
44 if (pciecDeviceName.empty() || pciecDeviceName != pcieDeviceId)
45 {
46 continue;
47 }
48
49 dbus::utility::getDbusObject(
50 pcieDevicePath, pcieDeviceInterface,
51 [pcieDevicePath, asyncResp,
52 callback](const boost::system::error_code& ec,
53 const dbus::utility::MapperGetObject& object) {
54 if (ec || object.empty())
55 {
56 BMCWEB_LOG_ERROR("DBUS response error {}", ec);
57 messages::internalError(asyncResp->res);
58 return;
59 }
60 callback(pcieDevicePath, object.begin()->first);
61 });
62 return;
63 }
64
65 BMCWEB_LOG_WARNING("PCIe Device not found");
66 messages::resourceNotFound(asyncResp->res, "PCIeDevice", pcieDeviceId);
67 }
68
getValidPCIeDevicePath(const std::string & pcieDeviceId,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::function<void (const std::string & pcieDevicePath,const std::string & service)> & callback)69 inline void getValidPCIeDevicePath(
70 const std::string& pcieDeviceId,
71 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
72 const std::function<void(const std::string& pcieDevicePath,
73 const std::string& service)>& callback)
74 {
75 dbus::utility::getSubTreePaths(
76 inventoryPath, 0, pcieDeviceInterface,
77 [pcieDeviceId, asyncResp,
78 callback](const boost::system::error_code& ec,
79 const dbus::utility::MapperGetSubTreePathsResponse&
80 pcieDevicePaths) {
81 if (ec)
82 {
83 BMCWEB_LOG_ERROR("D-Bus response error on GetSubTree {}", ec);
84 messages::internalError(asyncResp->res);
85 return;
86 }
87 handlePCIeDevicePath(pcieDeviceId, asyncResp, pcieDevicePaths,
88 callback);
89 return;
90 });
91 }
92
handlePCIeDeviceCollectionGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)93 inline void handlePCIeDeviceCollectionGet(
94 crow::App& app, const crow::Request& req,
95 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
96 const std::string& systemName)
97 {
98 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
99 {
100 return;
101 }
102 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
103 {
104 // Option currently returns no systems. TBD
105 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
106 systemName);
107 return;
108 }
109 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
110 {
111 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
112 systemName);
113 return;
114 }
115
116 asyncResp->res.addHeader(boost::beast::http::field::link,
117 "</redfish/v1/JsonSchemas/PCIeDeviceCollection/"
118 "PCIeDeviceCollection.json>; rel=describedby");
119 asyncResp->res.jsonValue["@odata.type"] =
120 "#PCIeDeviceCollection.PCIeDeviceCollection";
121 asyncResp->res.jsonValue["@odata.id"] = std::format(
122 "/redfish/v1/Systems/{}/PCIeDevices", BMCWEB_REDFISH_SYSTEM_URI_NAME);
123 asyncResp->res.jsonValue["Name"] = "PCIe Device Collection";
124 asyncResp->res.jsonValue["Description"] = "Collection of PCIe Devices";
125
126 pcie_util::getPCIeDeviceList(asyncResp,
127 nlohmann::json::json_pointer("/Members"));
128 }
129
requestRoutesSystemPCIeDeviceCollection(App & app)130 inline void requestRoutesSystemPCIeDeviceCollection(App& app)
131 {
132 /**
133 * Functions triggers appropriate requests on DBus
134 */
135 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/PCIeDevices/")
136 .privileges(redfish::privileges::getPCIeDeviceCollection)
137 .methods(boost::beast::http::verb::get)(
138 std::bind_front(handlePCIeDeviceCollectionGet, std::ref(app)));
139 }
140
addPCIeSlotProperties(crow::Response & res,const boost::system::error_code & ec,const dbus::utility::DBusPropertiesMap & pcieSlotProperties)141 inline void addPCIeSlotProperties(
142 crow::Response& res, const boost::system::error_code& ec,
143 const dbus::utility::DBusPropertiesMap& pcieSlotProperties)
144 {
145 if (ec)
146 {
147 BMCWEB_LOG_ERROR("DBUS response error for getAllProperties{}",
148 ec.value());
149 messages::internalError(res);
150 return;
151 }
152 std::string generation;
153 size_t lanes = 0;
154 std::string slotType;
155
156 bool success = sdbusplus::unpackPropertiesNoThrow(
157 dbus_utils::UnpackErrorPrinter(), pcieSlotProperties, "Generation",
158 generation, "Lanes", lanes, "SlotType", slotType);
159
160 if (!success)
161 {
162 messages::internalError(res);
163 return;
164 }
165
166 std::optional<pcie_device::PCIeTypes> pcieType =
167 pcie_util::redfishPcieGenerationFromDbus(generation);
168 if (!pcieType)
169 {
170 BMCWEB_LOG_WARNING("Unknown PCIeType: {}", generation);
171 }
172 else
173 {
174 if (*pcieType == pcie_device::PCIeTypes::Invalid)
175 {
176 BMCWEB_LOG_ERROR("Invalid PCIeType: {}", generation);
177 messages::internalError(res);
178 return;
179 }
180 res.jsonValue["Slot"]["PCIeType"] = *pcieType;
181 }
182
183 if (lanes != 0)
184 {
185 res.jsonValue["Slot"]["Lanes"] = lanes;
186 }
187
188 std::optional<pcie_slots::SlotTypes> redfishSlotType =
189 pcie_util::dbusSlotTypeToRf(slotType);
190 if (!redfishSlotType)
191 {
192 BMCWEB_LOG_WARNING("Unknown PCIeSlot Type: {}", slotType);
193 }
194 else
195 {
196 if (*redfishSlotType == pcie_slots::SlotTypes::Invalid)
197 {
198 BMCWEB_LOG_ERROR("Invalid PCIeSlot type: {}", slotType);
199 messages::internalError(res);
200 return;
201 }
202 res.jsonValue["Slot"]["SlotType"] = *redfishSlotType;
203 }
204 }
205
getPCIeDeviceSlotPath(const std::string & pcieDevicePath,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::function<void (const std::string & pcieDeviceSlot)> && callback)206 inline void getPCIeDeviceSlotPath(
207 const std::string& pcieDevicePath,
208 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
209 std::function<void(const std::string& pcieDeviceSlot)>&& callback)
210 {
211 std::string associationPath = pcieDevicePath + "/contained_by";
212 dbus::utility::getAssociatedSubTreePaths(
213 associationPath, sdbusplus::message::object_path(inventoryPath), 0,
214 pcieSlotInterface,
215 [callback = std::move(callback), asyncResp, pcieDevicePath](
216 const boost::system::error_code& ec,
217 const dbus::utility::MapperGetSubTreePathsResponse& endpoints) {
218 if (ec)
219 {
220 if (ec.value() == EBADR)
221 {
222 // Missing association is not an error
223 return;
224 }
225 BMCWEB_LOG_ERROR(
226 "DBUS response error for getAssociatedSubTreePaths {}",
227 ec.value());
228 messages::internalError(asyncResp->res);
229 return;
230 }
231 if (endpoints.size() > 1)
232 {
233 BMCWEB_LOG_ERROR(
234 "PCIeDevice {} is associated with more than one PCIeSlot: {}",
235 pcieDevicePath, endpoints.size());
236 messages::internalError(asyncResp->res);
237 return;
238 }
239 if (endpoints.empty())
240 {
241 // If the device doesn't have an association, return without
242 // PCIe Slot properties
243 BMCWEB_LOG_DEBUG("PCIeDevice is not associated with PCIeSlot");
244 return;
245 }
246 callback(endpoints[0]);
247 });
248 }
249
afterGetDbusObject(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & pcieDeviceSlot,const boost::system::error_code & ec,const dbus::utility::MapperGetObject & object)250 inline void afterGetDbusObject(
251 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
252 const std::string& pcieDeviceSlot, const boost::system::error_code& ec,
253 const dbus::utility::MapperGetObject& object)
254 {
255 if (ec || object.empty())
256 {
257 BMCWEB_LOG_ERROR("DBUS response error for getDbusObject {}",
258 ec.value());
259 messages::internalError(asyncResp->res);
260 return;
261 }
262 dbus::utility::getAllProperties(
263 object.begin()->first, pcieDeviceSlot,
264 "xyz.openbmc_project.Inventory.Item.PCIeSlot",
265 [asyncResp](
266 const boost::system::error_code& ec2,
267 const dbus::utility::DBusPropertiesMap& pcieSlotProperties) {
268 addPCIeSlotProperties(asyncResp->res, ec2, pcieSlotProperties);
269 });
270 }
271
afterGetPCIeDeviceSlotPath(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & pcieDeviceSlot)272 inline void afterGetPCIeDeviceSlotPath(
273 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
274 const std::string& pcieDeviceSlot)
275 {
276 dbus::utility::getDbusObject(
277 pcieDeviceSlot, pcieSlotInterface,
278 [asyncResp,
279 pcieDeviceSlot](const boost::system::error_code& ec,
280 const dbus::utility::MapperGetObject& object) {
281 afterGetDbusObject(asyncResp, pcieDeviceSlot, ec, object);
282 });
283 }
284
getPCIeDeviceHealth(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & pcieDevicePath,const std::string & service)285 inline void getPCIeDeviceHealth(
286 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
287 const std::string& pcieDevicePath, const std::string& service)
288 {
289 dbus::utility::getProperty<bool>(
290 service, pcieDevicePath,
291 "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional",
292 [asyncResp](const boost::system::error_code& ec, const bool value) {
293 if (ec)
294 {
295 if (ec.value() != EBADR)
296 {
297 BMCWEB_LOG_ERROR("DBUS response error for Health {}",
298 ec.value());
299 messages::internalError(asyncResp->res);
300 }
301 return;
302 }
303
304 if (!value)
305 {
306 asyncResp->res.jsonValue["Status"]["Health"] =
307 resource::Health::Critical;
308 }
309 });
310 }
311
getPCIeDeviceState(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & pcieDevicePath,const std::string & service)312 inline void getPCIeDeviceState(
313 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
314 const std::string& pcieDevicePath, const std::string& service)
315 {
316 dbus::utility::getProperty<bool>(
317 service, pcieDevicePath, "xyz.openbmc_project.Inventory.Item",
318 "Present",
319 [asyncResp](const boost::system::error_code& ec, bool value) {
320 if (ec)
321 {
322 if (ec.value() != EBADR)
323 {
324 BMCWEB_LOG_ERROR("DBUS response error for State");
325 messages::internalError(asyncResp->res);
326 }
327 return;
328 }
329
330 if (!value)
331 {
332 asyncResp->res.jsonValue["Status"]["State"] =
333 resource::State::Absent;
334 }
335 });
336 }
337
getPCIeDeviceAsset(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & pcieDevicePath,const std::string & service)338 inline void getPCIeDeviceAsset(
339 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
340 const std::string& pcieDevicePath, const std::string& service)
341 {
342 dbus::utility::getAllProperties(
343 service, pcieDevicePath,
344 "xyz.openbmc_project.Inventory.Decorator.Asset",
345 [pcieDevicePath, asyncResp{asyncResp}](
346 const boost::system::error_code& ec,
347 const dbus::utility::DBusPropertiesMap& assetList) {
348 if (ec)
349 {
350 if (ec.value() != EBADR)
351 {
352 BMCWEB_LOG_ERROR("DBUS response error for Properties{}",
353 ec.value());
354 messages::internalError(asyncResp->res);
355 }
356 return;
357 }
358
359 const std::string* manufacturer = nullptr;
360 const std::string* model = nullptr;
361 const std::string* partNumber = nullptr;
362 const std::string* serialNumber = nullptr;
363 const std::string* sparePartNumber = nullptr;
364
365 const bool success = sdbusplus::unpackPropertiesNoThrow(
366 dbus_utils::UnpackErrorPrinter(), assetList, "Manufacturer",
367 manufacturer, "Model", model, "PartNumber", partNumber,
368 "SerialNumber", serialNumber, "SparePartNumber",
369 sparePartNumber);
370
371 if (!success)
372 {
373 messages::internalError(asyncResp->res);
374 return;
375 }
376
377 if (manufacturer != nullptr)
378 {
379 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
380 }
381 if (model != nullptr)
382 {
383 asyncResp->res.jsonValue["Model"] = *model;
384 }
385
386 if (partNumber != nullptr)
387 {
388 asyncResp->res.jsonValue["PartNumber"] = *partNumber;
389 }
390
391 if (serialNumber != nullptr)
392 {
393 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
394 }
395
396 if (sparePartNumber != nullptr && !sparePartNumber->empty())
397 {
398 asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber;
399 }
400 });
401 }
402
addPCIeDeviceProperties(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & pcieDeviceId,const dbus::utility::DBusPropertiesMap & pcieDevProperties)403 inline void addPCIeDeviceProperties(
404 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
405 const std::string& pcieDeviceId,
406 const dbus::utility::DBusPropertiesMap& pcieDevProperties)
407 {
408 const std::string* generationInUse = nullptr;
409 const std::string* generationSupported = nullptr;
410 const size_t* lanesInUse = nullptr;
411 const size_t* maxLanes = nullptr;
412
413 const bool success = sdbusplus::unpackPropertiesNoThrow(
414 dbus_utils::UnpackErrorPrinter(), pcieDevProperties, "GenerationInUse",
415 generationInUse, "GenerationSupported", generationSupported,
416 "LanesInUse", lanesInUse, "MaxLanes", maxLanes);
417
418 if (!success)
419 {
420 messages::internalError(asyncResp->res);
421 return;
422 }
423
424 if (generationInUse != nullptr)
425 {
426 std::optional<pcie_device::PCIeTypes> redfishGenerationInUse =
427 pcie_util::redfishPcieGenerationFromDbus(*generationInUse);
428
429 if (!redfishGenerationInUse)
430 {
431 BMCWEB_LOG_WARNING("Unknown PCIe Device Generation: {}",
432 *generationInUse);
433 }
434 else
435 {
436 if (*redfishGenerationInUse == pcie_device::PCIeTypes::Invalid)
437 {
438 BMCWEB_LOG_ERROR("Invalid PCIe Device Generation: {}",
439 *generationInUse);
440 messages::internalError(asyncResp->res);
441 return;
442 }
443 asyncResp->res.jsonValue["PCIeInterface"]["PCIeType"] =
444 *redfishGenerationInUse;
445 }
446 }
447
448 if (generationSupported != nullptr)
449 {
450 std::optional<pcie_device::PCIeTypes> redfishGenerationSupported =
451 pcie_util::redfishPcieGenerationFromDbus(*generationSupported);
452
453 if (!redfishGenerationSupported)
454 {
455 BMCWEB_LOG_WARNING("Unknown PCIe Device Generation: {}",
456 *generationSupported);
457 }
458 else
459 {
460 if (*redfishGenerationSupported == pcie_device::PCIeTypes::Invalid)
461 {
462 BMCWEB_LOG_ERROR("Invalid PCIe Device Generation: {}",
463 *generationSupported);
464 messages::internalError(asyncResp->res);
465 return;
466 }
467 asyncResp->res.jsonValue["PCIeInterface"]["MaxPCIeType"] =
468 *redfishGenerationSupported;
469 }
470 }
471
472 if (lanesInUse != nullptr)
473 {
474 if (*lanesInUse == std::numeric_limits<size_t>::max())
475 {
476 // The default value of LanesInUse is "maxint", and the field will
477 // be null if it is a default value.
478 asyncResp->res.jsonValue["PCIeInterface"]["LanesInUse"] = nullptr;
479 }
480 else
481 {
482 asyncResp->res.jsonValue["PCIeInterface"]["LanesInUse"] =
483 *lanesInUse;
484 }
485 }
486 // The default value of MaxLanes is 0, and the field will be
487 // left as off if it is a default value.
488 if (maxLanes != nullptr && *maxLanes != 0)
489 {
490 asyncResp->res.jsonValue["PCIeInterface"]["MaxLanes"] = *maxLanes;
491 }
492
493 asyncResp->res.jsonValue["PCIeFunctions"]["@odata.id"] =
494 boost::urls::format(
495 "/redfish/v1/Systems/{}/PCIeDevices/{}/PCIeFunctions",
496 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId);
497 }
498
getPCIeDeviceProperties(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & pcieDevicePath,const std::string & service,const std::function<void (const dbus::utility::DBusPropertiesMap & pcieDevProperties)> && callback)499 inline void getPCIeDeviceProperties(
500 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
501 const std::string& pcieDevicePath, const std::string& service,
502 const std::function<void(
503 const dbus::utility::DBusPropertiesMap& pcieDevProperties)>&& callback)
504 {
505 dbus::utility::getAllProperties(
506 service, pcieDevicePath,
507 "xyz.openbmc_project.Inventory.Item.PCIeDevice",
508 [asyncResp,
509 callback](const boost::system::error_code& ec,
510 const dbus::utility::DBusPropertiesMap& pcieDevProperties) {
511 if (ec)
512 {
513 if (ec.value() != EBADR)
514 {
515 BMCWEB_LOG_ERROR("DBUS response error for Properties");
516 messages::internalError(asyncResp->res);
517 }
518 return;
519 }
520 callback(pcieDevProperties);
521 });
522 }
523
addPCIeDeviceCommonProperties(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & pcieDeviceId)524 inline void addPCIeDeviceCommonProperties(
525 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
526 const std::string& pcieDeviceId)
527 {
528 asyncResp->res.addHeader(
529 boost::beast::http::field::link,
530 "</redfish/v1/JsonSchemas/PCIeDevice/PCIeDevice.json>; rel=describedby");
531 asyncResp->res.jsonValue["@odata.type"] = "#PCIeDevice.v1_9_0.PCIeDevice";
532 asyncResp->res.jsonValue["@odata.id"] =
533 boost::urls::format("/redfish/v1/Systems/{}/PCIeDevices/{}",
534 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId);
535 asyncResp->res.jsonValue["Name"] = "PCIe Device";
536 asyncResp->res.jsonValue["Id"] = pcieDeviceId;
537 asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
538 asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::OK;
539 }
540
afterGetValidPcieDevicePath(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & pcieDeviceId,const std::string & pcieDevicePath,const std::string & service)541 inline void afterGetValidPcieDevicePath(
542 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
543 const std::string& pcieDeviceId, const std::string& pcieDevicePath,
544 const std::string& service)
545 {
546 addPCIeDeviceCommonProperties(asyncResp, pcieDeviceId);
547 getPCIeDeviceAsset(asyncResp, pcieDevicePath, service);
548 getPCIeDeviceState(asyncResp, pcieDevicePath, service);
549 getPCIeDeviceHealth(asyncResp, pcieDevicePath, service);
550 getPCIeDeviceProperties(
551 asyncResp, pcieDevicePath, service,
552 std::bind_front(addPCIeDeviceProperties, asyncResp, pcieDeviceId));
553 getPCIeDeviceSlotPath(
554 pcieDevicePath, asyncResp,
555 std::bind_front(afterGetPCIeDeviceSlotPath, asyncResp));
556 }
557
handlePCIeDeviceGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & pcieDeviceId)558 inline void handlePCIeDeviceGet(
559 App& app, const crow::Request& req,
560 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
561 const std::string& systemName, const std::string& pcieDeviceId)
562 {
563 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
564 {
565 return;
566 }
567 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
568 {
569 // Option currently returns no systems. TBD
570 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
571 systemName);
572 return;
573 }
574 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
575 {
576 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
577 systemName);
578 return;
579 }
580
581 getValidPCIeDevicePath(
582 pcieDeviceId, asyncResp,
583 std::bind_front(afterGetValidPcieDevicePath, asyncResp, pcieDeviceId));
584 }
585
requestRoutesSystemPCIeDevice(App & app)586 inline void requestRoutesSystemPCIeDevice(App& app)
587 {
588 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/PCIeDevices/<str>/")
589 .privileges(redfish::privileges::getPCIeDevice)
590 .methods(boost::beast::http::verb::get)(
591 std::bind_front(handlePCIeDeviceGet, std::ref(app)));
592 }
593
addPCIeFunctionList(crow::Response & res,const std::string & pcieDeviceId,const dbus::utility::DBusPropertiesMap & pcieDevProperties)594 inline void addPCIeFunctionList(
595 crow::Response& res, const std::string& pcieDeviceId,
596 const dbus::utility::DBusPropertiesMap& pcieDevProperties)
597 {
598 nlohmann::json& pcieFunctionList = res.jsonValue["Members"];
599 pcieFunctionList = nlohmann::json::array();
600 static constexpr const int maxPciFunctionNum = 8;
601
602 for (int functionNum = 0; functionNum < maxPciFunctionNum; functionNum++)
603 {
604 // Check if this function exists by
605 // looking for a device ID
606 std::string devIDProperty =
607 "Function" + std::to_string(functionNum) + "DeviceId";
608 const std::string* property = nullptr;
609 for (const auto& propEntry : pcieDevProperties)
610 {
611 if (propEntry.first == devIDProperty)
612 {
613 property = std::get_if<std::string>(&propEntry.second);
614 break;
615 }
616 }
617 if (property == nullptr || property->empty())
618 {
619 continue;
620 }
621
622 nlohmann::json::object_t pcieFunction;
623 pcieFunction["@odata.id"] = boost::urls::format(
624 "/redfish/v1/Systems/{}/PCIeDevices/{}/PCIeFunctions/{}",
625 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId,
626 std::to_string(functionNum));
627 pcieFunctionList.emplace_back(std::move(pcieFunction));
628 }
629 res.jsonValue["PCIeFunctions@odata.count"] = pcieFunctionList.size();
630 }
631
handlePCIeFunctionCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & pcieDeviceId)632 inline void handlePCIeFunctionCollectionGet(
633 App& app, const crow::Request& req,
634 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
635 const std::string& systemName, const std::string& pcieDeviceId)
636 {
637 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
638 {
639 return;
640 }
641 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
642 {
643 // Option currently returns no systems. TBD
644 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
645 systemName);
646 return;
647 }
648
649 getValidPCIeDevicePath(
650 pcieDeviceId, asyncResp,
651 [asyncResp, pcieDeviceId](const std::string& pcieDevicePath,
652 const std::string& service) {
653 asyncResp->res.addHeader(
654 boost::beast::http::field::link,
655 "</redfish/v1/JsonSchemas/PCIeFunctionCollection/PCIeFunctionCollection.json>; rel=describedby");
656 asyncResp->res.jsonValue["@odata.type"] =
657 "#PCIeFunctionCollection.PCIeFunctionCollection";
658 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
659 "/redfish/v1/Systems/{}/PCIeDevices/{}/PCIeFunctions",
660 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId);
661 asyncResp->res.jsonValue["Name"] = "PCIe Function Collection";
662 asyncResp->res.jsonValue["Description"] =
663 "Collection of PCIe Functions for PCIe Device " + pcieDeviceId;
664 getPCIeDeviceProperties(
665 asyncResp, pcieDevicePath, service,
666 [asyncResp, pcieDeviceId](
667 const dbus::utility::DBusPropertiesMap& pcieDevProperties) {
668 addPCIeFunctionList(asyncResp->res, pcieDeviceId,
669 pcieDevProperties);
670 });
671 });
672 }
673
requestRoutesSystemPCIeFunctionCollection(App & app)674 inline void requestRoutesSystemPCIeFunctionCollection(App& app)
675 {
676 /**
677 * Functions triggers appropriate requests on DBus
678 */
679 BMCWEB_ROUTE(app,
680 "/redfish/v1/Systems/<str>/PCIeDevices/<str>/PCIeFunctions/")
681 .privileges(redfish::privileges::getPCIeFunctionCollection)
682 .methods(boost::beast::http::verb::get)(
683 std::bind_front(handlePCIeFunctionCollectionGet, std::ref(app)));
684 }
685
validatePCIeFunctionId(uint64_t pcieFunctionId,const dbus::utility::DBusPropertiesMap & pcieDevProperties)686 inline bool validatePCIeFunctionId(
687 uint64_t pcieFunctionId,
688 const dbus::utility::DBusPropertiesMap& pcieDevProperties)
689 {
690 std::string functionName = "Function" + std::to_string(pcieFunctionId);
691 std::string devIDProperty = functionName + "DeviceId";
692
693 const std::string* devIdProperty = nullptr;
694 for (const auto& property : pcieDevProperties)
695 {
696 if (property.first == devIDProperty)
697 {
698 devIdProperty = std::get_if<std::string>(&property.second);
699 break;
700 }
701 }
702 return (devIdProperty != nullptr && !devIdProperty->empty());
703 }
704
addPCIeFunctionProperties(crow::Response & resp,uint64_t pcieFunctionId,const dbus::utility::DBusPropertiesMap & pcieDevProperties)705 inline void addPCIeFunctionProperties(
706 crow::Response& resp, uint64_t pcieFunctionId,
707 const dbus::utility::DBusPropertiesMap& pcieDevProperties)
708 {
709 std::string functionName = "Function" + std::to_string(pcieFunctionId);
710 for (const auto& property : pcieDevProperties)
711 {
712 const std::string* strProperty =
713 std::get_if<std::string>(&property.second);
714 if (strProperty == nullptr)
715 {
716 continue;
717 }
718 if (property.first == functionName + "DeviceId")
719 {
720 resp.jsonValue["DeviceId"] = *strProperty;
721 }
722 if (property.first == functionName + "VendorId")
723 {
724 resp.jsonValue["VendorId"] = *strProperty;
725 }
726 // TODO: FunctionType and DeviceClass are Redfish enums. The D-Bus
727 // property strings should be mapped correctly to ensure these
728 // strings are Redfish enum values. For now just check for empty.
729 if (property.first == functionName + "FunctionType")
730 {
731 if (!strProperty->empty())
732 {
733 resp.jsonValue["FunctionType"] = *strProperty;
734 }
735 }
736 if (property.first == functionName + "DeviceClass")
737 {
738 if (!strProperty->empty())
739 {
740 resp.jsonValue["DeviceClass"] = *strProperty;
741 }
742 }
743 if (property.first == functionName + "ClassCode")
744 {
745 resp.jsonValue["ClassCode"] = *strProperty;
746 }
747 if (property.first == functionName + "RevisionId")
748 {
749 resp.jsonValue["RevisionId"] = *strProperty;
750 }
751 if (property.first == functionName + "SubsystemId")
752 {
753 resp.jsonValue["SubsystemId"] = *strProperty;
754 }
755 if (property.first == functionName + "SubsystemVendorId")
756 {
757 resp.jsonValue["SubsystemVendorId"] = *strProperty;
758 }
759 }
760 }
761
addPCIeFunctionCommonProperties(crow::Response & resp,const std::string & pcieDeviceId,uint64_t pcieFunctionId)762 inline void addPCIeFunctionCommonProperties(crow::Response& resp,
763 const std::string& pcieDeviceId,
764 uint64_t pcieFunctionId)
765 {
766 resp.addHeader(
767 boost::beast::http::field::link,
768 "</redfish/v1/JsonSchemas/PCIeFunction/PCIeFunction.json>; rel=describedby");
769 resp.jsonValue["@odata.type"] = "#PCIeFunction.v1_2_3.PCIeFunction";
770 resp.jsonValue["@odata.id"] = boost::urls::format(
771 "/redfish/v1/Systems/{}/PCIeDevices/{}/PCIeFunctions/{}",
772 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId,
773 std::to_string(pcieFunctionId));
774 resp.jsonValue["Name"] = "PCIe Function";
775 resp.jsonValue["Id"] = std::to_string(pcieFunctionId);
776 resp.jsonValue["FunctionId"] = pcieFunctionId;
777 resp.jsonValue["Links"]["PCIeDevice"]["@odata.id"] =
778 boost::urls::format("/redfish/v1/Systems/{}/PCIeDevices/{}",
779 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId);
780 }
781
handlePCIeFunctionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & pcieDeviceId,const std::string & pcieFunctionIdStr)782 inline void handlePCIeFunctionGet(
783 App& app, const crow::Request& req,
784 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
785 const std::string& systemName, const std::string& pcieDeviceId,
786 const std::string& pcieFunctionIdStr)
787 {
788 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
789 {
790 return;
791 }
792 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
793 {
794 // Option currently returns no systems. TBD
795 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
796 systemName);
797 return;
798 }
799 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
800 {
801 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
802 systemName);
803 return;
804 }
805 std::string_view pcieFunctionIdView = pcieFunctionIdStr;
806
807 uint64_t pcieFunctionId = 0;
808 std::from_chars_result result = std::from_chars(
809 pcieFunctionIdView.begin(), pcieFunctionIdView.end(), pcieFunctionId);
810 if (result.ec != std::errc{} || result.ptr != pcieFunctionIdView.end())
811 {
812 messages::resourceNotFound(asyncResp->res, "PCIeFunction",
813 pcieFunctionIdStr);
814 return;
815 }
816
817 getValidPCIeDevicePath(
818 pcieDeviceId, asyncResp,
819 [asyncResp, pcieDeviceId, pcieFunctionId](
820 const std::string& pcieDevicePath, const std::string& service) {
821 getPCIeDeviceProperties(
822 asyncResp, pcieDevicePath, service,
823 [asyncResp, pcieDeviceId, pcieFunctionId](
824 const dbus::utility::DBusPropertiesMap& pcieDevProperties) {
825 addPCIeFunctionCommonProperties(
826 asyncResp->res, pcieDeviceId, pcieFunctionId);
827 addPCIeFunctionProperties(asyncResp->res, pcieFunctionId,
828 pcieDevProperties);
829 });
830 });
831 }
832
requestRoutesSystemPCIeFunction(App & app)833 inline void requestRoutesSystemPCIeFunction(App& app)
834 {
835 BMCWEB_ROUTE(
836 app, "/redfish/v1/Systems/<str>/PCIeDevices/<str>/PCIeFunctions/<str>/")
837 .privileges(redfish::privileges::getPCIeFunction)
838 .methods(boost::beast::http::verb::get)(
839 std::bind_front(handlePCIeFunctionGet, std::ref(app)));
840 }
841
842 } // namespace redfish
843