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