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