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 messages::internalError(asyncResp->res);
259 return;
260 }
261 if (endpoints.empty())
262 {
263 // If the device doesn't have an association, return without
264 // PCIe Slot properties
265 BMCWEB_LOG_DEBUG("PCIeDevice is not associated with PCIeSlot");
266 return;
267 }
268 callback(endpoints[0]);
269 });
270 }
271
afterGetDbusObject(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & pcieDeviceSlot,const boost::system::error_code & ec,const dbus::utility::MapperGetObject & object)272 inline void afterGetDbusObject(
273 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
274 const std::string& pcieDeviceSlot, const boost::system::error_code& ec,
275 const dbus::utility::MapperGetObject& object)
276 {
277 if (ec || object.empty())
278 {
279 BMCWEB_LOG_ERROR("DBUS response error for getDbusObject {}",
280 ec.value());
281 messages::internalError(asyncResp->res);
282 return;
283 }
284 dbus::utility::getAllProperties(
285 object.begin()->first, pcieDeviceSlot,
286 "xyz.openbmc_project.Inventory.Item.PCIeSlot",
287 [asyncResp](
288 const boost::system::error_code& ec2,
289 const dbus::utility::DBusPropertiesMap& pcieSlotProperties) {
290 addPCIeSlotProperties(asyncResp->res, ec2, pcieSlotProperties);
291 });
292 }
293
afterGetPCIeDeviceSlotPath(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & pcieDeviceSlot)294 inline void afterGetPCIeDeviceSlotPath(
295 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
296 const std::string& pcieDeviceSlot)
297 {
298 dbus::utility::getDbusObject(
299 pcieDeviceSlot, pcieSlotInterface,
300 [asyncResp,
301 pcieDeviceSlot](const boost::system::error_code& ec,
302 const dbus::utility::MapperGetObject& object) {
303 afterGetDbusObject(asyncResp, pcieDeviceSlot, ec, object);
304 });
305 }
306
getPCIeDeviceHealth(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & pcieDevicePath,const std::string & service)307 inline void getPCIeDeviceHealth(
308 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
309 const std::string& pcieDevicePath, const std::string& service)
310 {
311 dbus::utility::getProperty<bool>(
312 service, pcieDevicePath,
313 "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional",
314 [asyncResp](const boost::system::error_code& ec, const bool value) {
315 if (ec)
316 {
317 if (ec.value() != EBADR)
318 {
319 BMCWEB_LOG_ERROR("DBUS response error for Health {}",
320 ec.value());
321 messages::internalError(asyncResp->res);
322 }
323 return;
324 }
325
326 if (!value)
327 {
328 asyncResp->res.jsonValue["Status"]["Health"] =
329 resource::Health::Critical;
330 }
331 });
332 }
333
getPCIeDeviceState(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & pcieDevicePath,const std::string & service)334 inline void getPCIeDeviceState(
335 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
336 const std::string& pcieDevicePath, const std::string& service)
337 {
338 dbus::utility::getProperty<bool>(
339 service, pcieDevicePath, "xyz.openbmc_project.Inventory.Item",
340 "Present",
341 [asyncResp](const boost::system::error_code& ec, bool value) {
342 if (ec)
343 {
344 if (ec.value() != EBADR)
345 {
346 BMCWEB_LOG_ERROR("DBUS response error for State");
347 messages::internalError(asyncResp->res);
348 }
349 return;
350 }
351
352 if (!value)
353 {
354 asyncResp->res.jsonValue["Status"]["State"] =
355 resource::State::Absent;
356 }
357 });
358 }
359
getPCIeDeviceAsset(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & pcieDevicePath,const std::string & service)360 inline void getPCIeDeviceAsset(
361 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
362 const std::string& pcieDevicePath, const std::string& service)
363 {
364 dbus::utility::getAllProperties(
365 service, pcieDevicePath,
366 "xyz.openbmc_project.Inventory.Decorator.Asset",
367 [pcieDevicePath, asyncResp{asyncResp}](
368 const boost::system::error_code& ec,
369 const dbus::utility::DBusPropertiesMap& assetList) {
370 if (ec)
371 {
372 if (ec.value() != EBADR)
373 {
374 BMCWEB_LOG_ERROR("DBUS response error for Properties{}",
375 ec.value());
376 messages::internalError(asyncResp->res);
377 }
378 return;
379 }
380
381 const std::string* manufacturer = nullptr;
382 const std::string* model = nullptr;
383 const std::string* partNumber = nullptr;
384 const std::string* serialNumber = nullptr;
385 const std::string* sparePartNumber = nullptr;
386
387 const bool success = sdbusplus::unpackPropertiesNoThrow(
388 dbus_utils::UnpackErrorPrinter(), assetList, "Manufacturer",
389 manufacturer, "Model", model, "PartNumber", partNumber,
390 "SerialNumber", serialNumber, "SparePartNumber",
391 sparePartNumber);
392
393 if (!success)
394 {
395 messages::internalError(asyncResp->res);
396 return;
397 }
398
399 if (manufacturer != nullptr)
400 {
401 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
402 }
403 if (model != nullptr)
404 {
405 asyncResp->res.jsonValue["Model"] = *model;
406 }
407
408 if (partNumber != nullptr)
409 {
410 asyncResp->res.jsonValue["PartNumber"] = *partNumber;
411 }
412
413 if (serialNumber != nullptr)
414 {
415 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
416 }
417
418 if (sparePartNumber != nullptr && !sparePartNumber->empty())
419 {
420 asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber;
421 }
422 });
423 }
424
addPCIeDeviceProperties(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & pcieDeviceId,const dbus::utility::DBusPropertiesMap & pcieDevProperties)425 inline void addPCIeDeviceProperties(
426 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
427 const std::string& pcieDeviceId,
428 const dbus::utility::DBusPropertiesMap& pcieDevProperties)
429 {
430 const std::string* generationInUse = nullptr;
431 const std::string* generationSupported = nullptr;
432 const size_t* lanesInUse = nullptr;
433 const size_t* maxLanes = nullptr;
434
435 const bool success = sdbusplus::unpackPropertiesNoThrow(
436 dbus_utils::UnpackErrorPrinter(), pcieDevProperties, "GenerationInUse",
437 generationInUse, "GenerationSupported", generationSupported,
438 "LanesInUse", lanesInUse, "MaxLanes", maxLanes);
439
440 if (!success)
441 {
442 messages::internalError(asyncResp->res);
443 return;
444 }
445
446 if (generationInUse != nullptr)
447 {
448 std::optional<pcie_device::PCIeTypes> redfishGenerationInUse =
449 pcie_util::redfishPcieGenerationFromDbus(*generationInUse);
450
451 if (!redfishGenerationInUse)
452 {
453 BMCWEB_LOG_WARNING("Unknown PCIe Device Generation: {}",
454 *generationInUse);
455 }
456 else
457 {
458 if (*redfishGenerationInUse == pcie_device::PCIeTypes::Invalid)
459 {
460 BMCWEB_LOG_ERROR("Invalid PCIe Device Generation: {}",
461 *generationInUse);
462 messages::internalError(asyncResp->res);
463 return;
464 }
465 asyncResp->res.jsonValue["PCIeInterface"]["PCIeType"] =
466 *redfishGenerationInUse;
467 }
468 }
469
470 if (generationSupported != nullptr)
471 {
472 std::optional<pcie_device::PCIeTypes> redfishGenerationSupported =
473 pcie_util::redfishPcieGenerationFromDbus(*generationSupported);
474
475 if (!redfishGenerationSupported)
476 {
477 BMCWEB_LOG_WARNING("Unknown PCIe Device Generation: {}",
478 *generationSupported);
479 }
480 else
481 {
482 if (*redfishGenerationSupported == pcie_device::PCIeTypes::Invalid)
483 {
484 BMCWEB_LOG_ERROR("Invalid PCIe Device Generation: {}",
485 *generationSupported);
486 messages::internalError(asyncResp->res);
487 return;
488 }
489 asyncResp->res.jsonValue["PCIeInterface"]["MaxPCIeType"] =
490 *redfishGenerationSupported;
491 }
492 }
493
494 if (lanesInUse != nullptr)
495 {
496 if (*lanesInUse == std::numeric_limits<size_t>::max())
497 {
498 // The default value of LanesInUse is "maxint", and the field will
499 // be null if it is a default value.
500 asyncResp->res.jsonValue["PCIeInterface"]["LanesInUse"] = nullptr;
501 }
502 else
503 {
504 asyncResp->res.jsonValue["PCIeInterface"]["LanesInUse"] =
505 *lanesInUse;
506 }
507 }
508 // The default value of MaxLanes is 0, and the field will be
509 // left as off if it is a default value.
510 if (maxLanes != nullptr && *maxLanes != 0)
511 {
512 asyncResp->res.jsonValue["PCIeInterface"]["MaxLanes"] = *maxLanes;
513 }
514
515 asyncResp->res.jsonValue["PCIeFunctions"]["@odata.id"] =
516 boost::urls::format(
517 "/redfish/v1/Systems/{}/PCIeDevices/{}/PCIeFunctions",
518 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId);
519 }
520
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)521 inline void getPCIeDeviceProperties(
522 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
523 const std::string& pcieDevicePath, const std::string& service,
524 const std::function<void(
525 const dbus::utility::DBusPropertiesMap& pcieDevProperties)>&& callback)
526 {
527 dbus::utility::getAllProperties(
528 service, pcieDevicePath,
529 "xyz.openbmc_project.Inventory.Item.PCIeDevice",
530 [asyncResp,
531 callback](const boost::system::error_code& ec,
532 const dbus::utility::DBusPropertiesMap& pcieDevProperties) {
533 if (ec)
534 {
535 if (ec.value() != EBADR)
536 {
537 BMCWEB_LOG_ERROR("DBUS response error for Properties");
538 messages::internalError(asyncResp->res);
539 }
540 return;
541 }
542 callback(pcieDevProperties);
543 });
544 }
545
addPCIeDeviceCommonProperties(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & pcieDeviceId)546 inline void addPCIeDeviceCommonProperties(
547 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
548 const std::string& pcieDeviceId)
549 {
550 asyncResp->res.addHeader(
551 boost::beast::http::field::link,
552 "</redfish/v1/JsonSchemas/PCIeDevice/PCIeDevice.json>; rel=describedby");
553 asyncResp->res.jsonValue["@odata.type"] = "#PCIeDevice.v1_9_0.PCIeDevice";
554 asyncResp->res.jsonValue["@odata.id"] =
555 boost::urls::format("/redfish/v1/Systems/{}/PCIeDevices/{}",
556 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId);
557 asyncResp->res.jsonValue["Name"] = "PCIe Device";
558 asyncResp->res.jsonValue["Id"] = pcieDeviceId;
559 asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
560 asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::OK;
561 }
562
afterGetValidPcieDevicePath(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & pcieDeviceId,const std::string & pcieDevicePath,const std::string & service)563 inline void afterGetValidPcieDevicePath(
564 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
565 const std::string& pcieDeviceId, const std::string& pcieDevicePath,
566 const std::string& service)
567 {
568 addPCIeDeviceCommonProperties(asyncResp, pcieDeviceId);
569 getPCIeDeviceAsset(asyncResp, pcieDevicePath, service);
570 getPCIeDeviceState(asyncResp, pcieDevicePath, service);
571 getPCIeDeviceHealth(asyncResp, pcieDevicePath, service);
572 getPCIeDeviceProperties(
573 asyncResp, pcieDevicePath, service,
574 std::bind_front(addPCIeDeviceProperties, asyncResp, pcieDeviceId));
575 getPCIeDeviceSlotPath(
576 pcieDevicePath, asyncResp,
577 std::bind_front(afterGetPCIeDeviceSlotPath, asyncResp));
578 }
579
handlePCIeDeviceGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & pcieDeviceId)580 inline void handlePCIeDeviceGet(
581 App& app, const crow::Request& req,
582 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
583 const std::string& systemName, const std::string& pcieDeviceId)
584 {
585 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
586 {
587 return;
588 }
589 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
590 {
591 // Option currently returns no systems. TBD
592 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
593 systemName);
594 return;
595 }
596 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
597 {
598 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
599 systemName);
600 return;
601 }
602
603 getValidPCIeDevicePath(
604 pcieDeviceId, asyncResp,
605 std::bind_front(afterGetValidPcieDevicePath, asyncResp, pcieDeviceId));
606 }
607
requestRoutesSystemPCIeDevice(App & app)608 inline void requestRoutesSystemPCIeDevice(App& app)
609 {
610 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/PCIeDevices/<str>/")
611 .privileges(redfish::privileges::getPCIeDevice)
612 .methods(boost::beast::http::verb::get)(
613 std::bind_front(handlePCIeDeviceGet, std::ref(app)));
614 }
615
addPCIeFunctionList(crow::Response & res,const std::string & pcieDeviceId,const dbus::utility::DBusPropertiesMap & pcieDevProperties)616 inline void addPCIeFunctionList(
617 crow::Response& res, const std::string& pcieDeviceId,
618 const dbus::utility::DBusPropertiesMap& pcieDevProperties)
619 {
620 nlohmann::json& pcieFunctionList = res.jsonValue["Members"];
621 pcieFunctionList = nlohmann::json::array();
622 static constexpr const int maxPciFunctionNum = 8;
623
624 for (int functionNum = 0; functionNum < maxPciFunctionNum; functionNum++)
625 {
626 // Check if this function exists by
627 // looking for a device ID
628 std::string devIDProperty =
629 "Function" + std::to_string(functionNum) + "DeviceId";
630 const std::string* property = nullptr;
631 for (const auto& propEntry : pcieDevProperties)
632 {
633 if (propEntry.first == devIDProperty)
634 {
635 property = std::get_if<std::string>(&propEntry.second);
636 break;
637 }
638 }
639 if (property == nullptr || property->empty())
640 {
641 continue;
642 }
643
644 nlohmann::json::object_t pcieFunction;
645 pcieFunction["@odata.id"] = boost::urls::format(
646 "/redfish/v1/Systems/{}/PCIeDevices/{}/PCIeFunctions/{}",
647 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId,
648 std::to_string(functionNum));
649 pcieFunctionList.emplace_back(std::move(pcieFunction));
650 }
651 res.jsonValue["PCIeFunctions@odata.count"] = pcieFunctionList.size();
652 }
653
handlePCIeFunctionCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & pcieDeviceId)654 inline void handlePCIeFunctionCollectionGet(
655 App& app, const crow::Request& req,
656 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
657 const std::string& systemName, const std::string& pcieDeviceId)
658 {
659 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
660 {
661 return;
662 }
663 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
664 {
665 // Option currently returns no systems. TBD
666 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
667 systemName);
668 return;
669 }
670
671 getValidPCIeDevicePath(
672 pcieDeviceId, asyncResp,
673 [asyncResp, pcieDeviceId](const std::string& pcieDevicePath,
674 const std::string& service) {
675 asyncResp->res.addHeader(
676 boost::beast::http::field::link,
677 "</redfish/v1/JsonSchemas/PCIeFunctionCollection/PCIeFunctionCollection.json>; rel=describedby");
678 asyncResp->res.jsonValue["@odata.type"] =
679 "#PCIeFunctionCollection.PCIeFunctionCollection";
680 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
681 "/redfish/v1/Systems/{}/PCIeDevices/{}/PCIeFunctions",
682 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId);
683 asyncResp->res.jsonValue["Name"] = "PCIe Function Collection";
684 asyncResp->res.jsonValue["Description"] =
685 "Collection of PCIe Functions for PCIe Device " + pcieDeviceId;
686 getPCIeDeviceProperties(
687 asyncResp, pcieDevicePath, service,
688 [asyncResp, pcieDeviceId](
689 const dbus::utility::DBusPropertiesMap& pcieDevProperties) {
690 addPCIeFunctionList(asyncResp->res, pcieDeviceId,
691 pcieDevProperties);
692 });
693 });
694 }
695
requestRoutesSystemPCIeFunctionCollection(App & app)696 inline void requestRoutesSystemPCIeFunctionCollection(App& app)
697 {
698 /**
699 * Functions triggers appropriate requests on DBus
700 */
701 BMCWEB_ROUTE(app,
702 "/redfish/v1/Systems/<str>/PCIeDevices/<str>/PCIeFunctions/")
703 .privileges(redfish::privileges::getPCIeFunctionCollection)
704 .methods(boost::beast::http::verb::get)(
705 std::bind_front(handlePCIeFunctionCollectionGet, std::ref(app)));
706 }
707
validatePCIeFunctionId(uint64_t pcieFunctionId,const dbus::utility::DBusPropertiesMap & pcieDevProperties)708 inline bool validatePCIeFunctionId(
709 uint64_t pcieFunctionId,
710 const dbus::utility::DBusPropertiesMap& pcieDevProperties)
711 {
712 std::string functionName = "Function" + std::to_string(pcieFunctionId);
713 std::string devIDProperty = functionName + "DeviceId";
714
715 const std::string* devIdProperty = nullptr;
716 for (const auto& property : pcieDevProperties)
717 {
718 if (property.first == devIDProperty)
719 {
720 devIdProperty = std::get_if<std::string>(&property.second);
721 break;
722 }
723 }
724 return (devIdProperty != nullptr && !devIdProperty->empty());
725 }
726
addPCIeFunctionProperties(crow::Response & resp,uint64_t pcieFunctionId,const dbus::utility::DBusPropertiesMap & pcieDevProperties)727 inline void addPCIeFunctionProperties(
728 crow::Response& resp, uint64_t pcieFunctionId,
729 const dbus::utility::DBusPropertiesMap& pcieDevProperties)
730 {
731 std::string functionName = "Function" + std::to_string(pcieFunctionId);
732 for (const auto& property : pcieDevProperties)
733 {
734 const std::string* strProperty =
735 std::get_if<std::string>(&property.second);
736 if (strProperty == nullptr)
737 {
738 continue;
739 }
740 if (property.first == functionName + "DeviceId")
741 {
742 resp.jsonValue["DeviceId"] = *strProperty;
743 }
744 if (property.first == functionName + "VendorId")
745 {
746 resp.jsonValue["VendorId"] = *strProperty;
747 }
748 // TODO: FunctionType and DeviceClass are Redfish enums. The D-Bus
749 // property strings should be mapped correctly to ensure these
750 // strings are Redfish enum values. For now just check for empty.
751 if (property.first == functionName + "FunctionType")
752 {
753 if (!strProperty->empty())
754 {
755 resp.jsonValue["FunctionType"] = *strProperty;
756 }
757 }
758 if (property.first == functionName + "DeviceClass")
759 {
760 if (!strProperty->empty())
761 {
762 resp.jsonValue["DeviceClass"] = *strProperty;
763 }
764 }
765 if (property.first == functionName + "ClassCode")
766 {
767 resp.jsonValue["ClassCode"] = *strProperty;
768 }
769 if (property.first == functionName + "RevisionId")
770 {
771 resp.jsonValue["RevisionId"] = *strProperty;
772 }
773 if (property.first == functionName + "SubsystemId")
774 {
775 resp.jsonValue["SubsystemId"] = *strProperty;
776 }
777 if (property.first == functionName + "SubsystemVendorId")
778 {
779 resp.jsonValue["SubsystemVendorId"] = *strProperty;
780 }
781 }
782 }
783
addPCIeFunctionCommonProperties(crow::Response & resp,const std::string & pcieDeviceId,uint64_t pcieFunctionId)784 inline void addPCIeFunctionCommonProperties(crow::Response& resp,
785 const std::string& pcieDeviceId,
786 uint64_t pcieFunctionId)
787 {
788 resp.addHeader(
789 boost::beast::http::field::link,
790 "</redfish/v1/JsonSchemas/PCIeFunction/PCIeFunction.json>; rel=describedby");
791 resp.jsonValue["@odata.type"] = "#PCIeFunction.v1_2_3.PCIeFunction";
792 resp.jsonValue["@odata.id"] = boost::urls::format(
793 "/redfish/v1/Systems/{}/PCIeDevices/{}/PCIeFunctions/{}",
794 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId,
795 std::to_string(pcieFunctionId));
796 resp.jsonValue["Name"] = "PCIe Function";
797 resp.jsonValue["Id"] = std::to_string(pcieFunctionId);
798 resp.jsonValue["FunctionId"] = pcieFunctionId;
799 resp.jsonValue["Links"]["PCIeDevice"]["@odata.id"] =
800 boost::urls::format("/redfish/v1/Systems/{}/PCIeDevices/{}",
801 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId);
802 }
803
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)804 inline void handlePCIeFunctionGet(
805 App& app, const crow::Request& req,
806 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
807 const std::string& systemName, const std::string& pcieDeviceId,
808 const std::string& pcieFunctionIdStr)
809 {
810 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
811 {
812 return;
813 }
814 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
815 {
816 // Option currently returns no systems. TBD
817 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
818 systemName);
819 return;
820 }
821 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
822 {
823 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
824 systemName);
825 return;
826 }
827 std::string_view pcieFunctionIdView = pcieFunctionIdStr;
828
829 uint64_t pcieFunctionId = 0;
830 std::from_chars_result result = std::from_chars(
831 pcieFunctionIdView.begin(), pcieFunctionIdView.end(), pcieFunctionId);
832 if (result.ec != std::errc{} || result.ptr != pcieFunctionIdView.end())
833 {
834 messages::resourceNotFound(asyncResp->res, "PCIeFunction",
835 pcieFunctionIdStr);
836 return;
837 }
838
839 getValidPCIeDevicePath(
840 pcieDeviceId, asyncResp,
841 [asyncResp, pcieDeviceId, pcieFunctionId](
842 const std::string& pcieDevicePath, const std::string& service) {
843 getPCIeDeviceProperties(
844 asyncResp, pcieDevicePath, service,
845 [asyncResp, pcieDeviceId, pcieFunctionId](
846 const dbus::utility::DBusPropertiesMap& pcieDevProperties) {
847 addPCIeFunctionCommonProperties(
848 asyncResp->res, pcieDeviceId, pcieFunctionId);
849 addPCIeFunctionProperties(asyncResp->res, pcieFunctionId,
850 pcieDevProperties);
851 });
852 });
853 }
854
requestRoutesSystemPCIeFunction(App & app)855 inline void requestRoutesSystemPCIeFunction(App& app)
856 {
857 BMCWEB_ROUTE(
858 app, "/redfish/v1/Systems/<str>/PCIeDevices/<str>/PCIeFunctions/<str>/")
859 .privileges(redfish::privileges::getPCIeFunction)
860 .methods(boost::beast::http::verb::get)(
861 std::bind_front(handlePCIeFunctionGet, std::ref(app)));
862 }
863
864 } // namespace redfish
865