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