-
Notifications
You must be signed in to change notification settings - Fork 28
/
ibm_vpd_app.cpp
1981 lines (1797 loc) · 71.1 KB
/
ibm_vpd_app.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include "config.h"
#include "common_utility.hpp"
#include "defines.hpp"
#include "editor_impl.hpp"
#include "ibm_vpd_utils.hpp"
#include "ipz_parser.hpp"
#include "keyword_vpd_parser.hpp"
#include "memory_vpd_parser.hpp"
#include "parser_factory.hpp"
#include "vpd_exceptions.hpp"
#include <assert.h>
#include <ctype.h>
#include <CLI/CLI.hpp>
#include <boost/algorithm/string.hpp>
#include <gpiod.hpp>
#include <phosphor-logging/log.hpp>
#include <algorithm>
#include <cstdarg>
#include <exception>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <iterator>
#include <regex>
#include <thread>
using namespace std;
using namespace openpower::vpd;
using namespace CLI;
using namespace vpd::keyword::parser;
using namespace openpower::vpd::constants;
namespace fs = filesystem;
using json = nlohmann::json;
using namespace openpower::vpd::parser::factory;
using namespace openpower::vpd::inventory;
using namespace openpower::vpd::memory::parser;
using namespace openpower::vpd::parser::interface;
using namespace openpower::vpd::exceptions;
using namespace phosphor::logging;
using namespace openpower::vpd::manager::editor;
/**
* @brief API declaration, Populate Dbus.
*
* This method invokes all the populateInterface functions
* and notifies PIM about dbus object.
*
* @param[in] vpdMap - Either IPZ vpd map or Keyword vpd map based on the
* input.
* @param[in] js - Inventory json object
* @param[in] filePath - Path of the vpd file
* @param[in] preIntrStr - Interface string
*/
template <typename T>
static void populateDbus(T& vpdMap, nlohmann::json& js, const string& filePath);
/**
* @brief Returns the BMC state
*/
static auto getBMCState()
{
std::string bmcState;
try
{
auto bus = sdbusplus::bus::new_default();
auto properties = bus.new_method_call(
"xyz.openbmc_project.State.BMC", "/xyz/openbmc_project/state/bmc0",
"org.freedesktop.DBus.Properties", "Get");
properties.append("xyz.openbmc_project.State.BMC");
properties.append("CurrentBMCState");
auto result = bus.call(properties);
std::variant<std::string> val;
result.read(val);
if (auto pVal = std::get_if<std::string>(&val))
{
bmcState = *pVal;
}
}
catch (const sdbusplus::exception::SdBusError& e)
{
// Ignore any error
std::cerr << "Failed to get BMC state: " << e.what() << "\n";
// Since we failed set to not ready state
bmcState = "xyz.openbmc_project.State.BMC.BMCState.NotReady";
}
return bmcState;
}
/**
* @brief Check if the FRU is in the cache
*
* Checks if the FRU associated with the supplied D-Bus object path is already
* on D-Bus. This can be used to test if a VPD collection is required for this
* FRU. It uses the "xyz.openbmc_project.Inventory.Item, Present" property to
* determine the presence of a FRU in the cache.
*
* @param objectPath - The D-Bus object path without the PIM prefix.
* @return true if the object exists on D-Bus, false otherwise.
*/
static auto isFruInVpdCache(const std::string& objectPath)
{
try
{
auto bus = sdbusplus::bus::new_default();
auto invPath = std::string{pimPath} + objectPath;
auto props = bus.new_method_call(
"xyz.openbmc_project.Inventory.Manager", invPath.c_str(),
"org.freedesktop.DBus.Properties", "Get");
props.append("xyz.openbmc_project.Inventory.Item");
props.append("Present");
auto result = bus.call(props);
std::variant<bool> present;
result.read(present);
if (auto pVal = std::get_if<bool>(&present))
{
return *pVal;
}
return false;
}
catch (const sdbusplus::exception::SdBusError& e)
{
std::cout << "FRU: " << objectPath << " not in D-Bus\n";
// Assume not present in case of an error
return false;
}
}
/**
* @brief Check if VPD recollection is needed for the given EEPROM
*
* Not all FRUs can be swapped at BMC ready state. This function does the
* following:
* -- Check if the FRU is marked as "pluggableAtStandby" OR
* "concurrentlyMaintainable". If so, return true.
* -- Check if we are at BMC NotReady state. If we are, then return true.
* -- Else check if the FRU is not present in the VPD cache (to cover for VPD
* force collection). If not found in the cache, return true.
* -- Else return false.
*
* @param js - JSON Object.
* @param filePath - The EEPROM file.
* @return true if collection should be attempted, false otherwise.
*/
static auto needsRecollection(const nlohmann::json& js, const string& filePath)
{
if (js["frus"][filePath].at(0).value("pluggableAtStandby", false) ||
js["frus"][filePath].at(0).value("concurrentlyMaintainable", false))
{
return true;
}
if (getBMCState() == "xyz.openbmc_project.State.BMC.BMCState.NotReady")
{
return true;
}
if (!isFruInVpdCache(js["frus"][filePath].at(0).value("inventoryPath", "")))
{
return true;
}
return false;
}
/**
* @brief Expands location codes
*/
static auto expandLocationCode(const string& unexpanded, const Parsed& vpdMap,
bool isSystemVpd)
{
auto expanded{unexpanded};
static constexpr auto SYSTEM_OBJECT = "/system/chassis/motherboard";
static constexpr auto VCEN_IF = "com.ibm.ipzvpd.VCEN";
static constexpr auto VSYS_IF = "com.ibm.ipzvpd.VSYS";
size_t idx = expanded.find("fcs");
try
{
if (idx != string::npos)
{
string fc{};
string se{};
if (isSystemVpd)
{
const auto& fcData = vpdMap.at("VCEN").at("FC");
const auto& seData = vpdMap.at("VCEN").at("SE");
fc = string(fcData.data(), fcData.size());
se = string(seData.data(), seData.size());
}
else
{
fc = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "FC");
se = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "SE");
}
// TODO: See if ND0 can be placed in the JSON
expanded.replace(idx, 3, fc.substr(0, 4) + ".ND0." + se);
}
else
{
idx = expanded.find("mts");
if (idx != string::npos)
{
string mt{};
string se{};
if (isSystemVpd)
{
const auto& mtData = vpdMap.at("VSYS").at("TM");
const auto& seData = vpdMap.at("VSYS").at("SE");
mt = string(mtData.data(), mtData.size());
se = string(seData.data(), seData.size());
}
else
{
mt = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "TM");
se = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "SE");
}
replace(mt.begin(), mt.end(), '-', '.');
expanded.replace(idx, 3, mt + "." + se);
}
}
}
catch (const exception& e)
{
std::cerr << "Failed to expand location code with exception: "
<< e.what() << "\n";
}
return expanded;
}
/**
* @brief Populate FRU specific interfaces.
*
* This is a common method which handles both
* ipz and keyword specific interfaces thus,
* reducing the code redundancy.
* @param[in] map - Reference to the innermost keyword-value map.
* @param[in] preIntrStr - Reference to the interface string.
* @param[out] interfaces - Reference to interface map.
*/
template <typename T>
static void populateFruSpecificInterfaces(
const T& map, const string& preIntrStr, inventory::InterfaceMap& interfaces)
{
inventory::PropertyMap prop;
for (const auto& kwVal : map)
{
auto kw = kwVal.first;
if (kw[0] == '#')
{
kw = string("PD_") + kw[1];
}
else if (isdigit(kw[0]))
{
kw = string("N_") + kw;
}
if constexpr (is_same<T, KeywordVpdMap>::value)
{
if (auto keywordValue = get_if<Binary>(&kwVal.second))
{
Binary vec((*keywordValue).begin(), (*keywordValue).end());
prop.emplace(move(kw), move(vec));
}
else if (auto keywordValue = get_if<std::string>(&kwVal.second))
{
Binary vec((*keywordValue).begin(), (*keywordValue).end());
prop.emplace(move(kw), move(vec));
}
else if (auto keywordValue = get_if<size_t>(&kwVal.second))
{
if (kw == "MemorySizeInKB")
{
inventory::PropertyMap memProp;
memProp.emplace(move(kw), ((*keywordValue)));
interfaces.emplace(
"xyz.openbmc_project.Inventory.Item.Dimm",
move(memProp));
}
else
{
std::cerr << "Unknown Keyword[" << kw << "] found ";
}
}
else
{
std::cerr << "Unknown Variant found ";
}
}
else
{
Binary vec(kwVal.second.begin(), kwVal.second.end());
prop.emplace(move(kw), move(vec));
}
}
interfaces.emplace(preIntrStr, move(prop));
}
/**
* @brief Populate Interfaces.
*
* This method populates common and extra interfaces to dbus.
* @param[in] js - json object
* @param[out] interfaces - Reference to interface map
* @param[in] vpdMap - Reference to the parsed vpd map.
* @param[in] isSystemVpd - Denotes whether we are collecting the system VPD.
*/
template <typename T>
static void populateInterfaces(const nlohmann::json& js,
inventory::InterfaceMap& interfaces,
const T& vpdMap, bool isSystemVpd)
{
for (const auto& ifs : js.items())
{
string inf = ifs.key();
inventory::PropertyMap props;
for (const auto& itr : ifs.value().items())
{
const string& busProp = itr.key();
if (itr.value().is_boolean())
{
props.emplace(busProp, itr.value().get<bool>());
}
else if (itr.value().is_string())
{
if (busProp == "LocationCode" && inf == IBM_LOCATION_CODE_INF)
{
std::string prop;
if constexpr (is_same<T, Parsed>::value)
{
// TODO deprecate the com.ibm interface later
prop = expandLocationCode(itr.value().get<string>(),
vpdMap, isSystemVpd);
}
else if constexpr (is_same<T, KeywordVpdMap>::value)
{
// Send empty Parsed object to expandLocationCode api.
prop = expandLocationCode(itr.value().get<string>(),
Parsed{}, false);
}
props.emplace(busProp, prop);
interfaces.emplace(XYZ_LOCATION_CODE_INF, props);
interfaces.emplace(IBM_LOCATION_CODE_INF, props);
}
else
{
props.emplace(busProp, itr.value().get<string>());
}
}
else if (itr.value().is_array())
{
try
{
props.emplace(busProp, itr.value().get<Binary>());
}
catch (const nlohmann::detail::type_error& e)
{
std::cerr << "Type exception: " << e.what() << "\n";
// Ignore any type errors
}
}
else if (itr.value().is_object())
{
const string& rec = itr.value().value("recordName", "");
const string& kw = itr.value().value("keywordName", "");
const string& encoding = itr.value().value("encoding", "");
if constexpr (is_same<T, Parsed>::value)
{
if (!rec.empty() && !kw.empty() && vpdMap.count(rec) &&
vpdMap.at(rec).count(kw))
{
auto encoded =
encodeKeyword(vpdMap.at(rec).at(kw), encoding);
props.emplace(busProp, encoded);
}
}
else if constexpr (is_same<T, KeywordVpdMap>::value)
{
if (!kw.empty() && vpdMap.count(kw))
{
if (auto kwValue = get_if<Binary>(&vpdMap.at(kw)))
{
auto prop =
string((*kwValue).begin(), (*kwValue).end());
auto encoded = encodeKeyword(prop, encoding);
props.emplace(busProp, encoded);
}
else if (auto kwValue =
get_if<std::string>(&vpdMap.at(kw)))
{
auto prop =
string((*kwValue).begin(), (*kwValue).end());
auto encoded = encodeKeyword(prop, encoding);
props.emplace(busProp, encoded);
}
else if (auto uintValue =
get_if<size_t>(&vpdMap.at(kw)))
{
props.emplace(busProp, *uintValue);
}
else
{
std::cerr << " Unknown Keyword [" << kw
<< "] Encountered";
}
}
}
}
else if (itr.value().is_number())
{
// For now assume the value is a size_t. In the future it would
// be nice to come up with a way to get the type from the JSON.
props.emplace(busProp, itr.value().get<size_t>());
}
}
insertOrMerge(interfaces, inf, move(props));
}
}
/**
* @brief This API checks if this FRU is pcie_devices. If yes then it further
* checks whether it is PASS1 planar.
*/
static bool isThisPcieOnPass1planar(const nlohmann::json& js,
const string& file)
{
auto isThisPCIeDev = false;
auto isPASS1 = false;
// Check if it is a PCIE device
if (js["frus"].find(file) != js["frus"].end())
{
if ((js["frus"][file].at(0).find("extraInterfaces") !=
js["frus"][file].at(0).end()))
{
if (js["frus"][file].at(0)["extraInterfaces"].find(
"xyz.openbmc_project.Inventory.Item.PCIeDevice") !=
js["frus"][file].at(0)["extraInterfaces"].end())
{
isThisPCIeDev = true;
}
}
}
if (isThisPCIeDev)
{
// Collect HW version and SystemType to know if it is PASS1 planar.
auto bus = sdbusplus::bus::new_default();
auto property1 = bus.new_method_call(
INVENTORY_MANAGER_SERVICE,
"/xyz/openbmc_project/inventory/system/chassis/motherboard",
"org.freedesktop.DBus.Properties", "Get");
property1.append("com.ibm.ipzvpd.VINI");
property1.append("HW");
auto result1 = bus.call(property1);
inventory::Value hwVal;
result1.read(hwVal);
// SystemType
auto property2 = bus.new_method_call(
INVENTORY_MANAGER_SERVICE,
"/xyz/openbmc_project/inventory/system/chassis/motherboard",
"org.freedesktop.DBus.Properties", "Get");
property2.append("com.ibm.ipzvpd.VSBP");
property2.append("IM");
auto result2 = bus.call(property2);
inventory::Value imVal;
result2.read(imVal);
auto pVal1 = get_if<Binary>(&hwVal);
auto pVal2 = get_if<Binary>(&imVal);
if (pVal1 && pVal2)
{
auto hwVersion = *pVal1;
auto systemType = *pVal2;
// IM kw for Everest
Binary everestSystem{80, 00, 48, 00};
if (systemType == everestSystem)
{
if (hwVersion[1] < 21)
{
isPASS1 = true;
}
}
else if (hwVersion[1] < 2)
{
isPASS1 = true;
}
}
}
return (isThisPCIeDev && isPASS1);
}
/** Performs any pre-action needed to get the FRU setup for collection.
*
* @param[in] json - json object
* @param[in] file - eeprom file path
*/
static void preAction(const nlohmann::json& json, const string& file)
{
if ((json["frus"][file].at(0)).find("preAction") ==
json["frus"][file].at(0).end())
{
return;
}
try
{
if (executePreAction(json, file))
{
if (json["frus"][file].at(0).find("devAddress") !=
json["frus"][file].at(0).end())
{
// Now bind the device
string bind = json["frus"][file].at(0).value("devAddress", "");
std::cout << "Binding device " << bind << std::endl;
string bindCmd = string("echo \"") + bind +
string("\" > /sys/bus/i2c/drivers/at24/bind");
std::cout << bindCmd << std::endl;
executeCmd(bindCmd);
// Check if device showed up (test for file)
if (!fs::exists(file))
{
std::cerr
<< "EEPROM " << file
<< " does not exist. Take failure action" << std::endl;
// If not, then take failure postAction
executePostFailAction(json, file);
}
}
else
{
// missing required information
std::cerr << "VPD inventory JSON missing basic information of "
"preAction "
"for this FRU : ["
<< file << "]. Executing executePostFailAction."
<< std::endl;
// Take failure postAction
executePostFailAction(json, file);
return;
}
}
else
{
// If the FRU is not there, clear the VINI/CCIN data.
// Entity manager probes for this keyword to look for this
// FRU, now if the data is persistent on BMC and FRU is
// removed this can lead to ambiguity. Hence clearing this
// Keyword if FRU is absent.
const auto& invPath =
json["frus"][file].at(0).value("inventoryPath", "");
if (!invPath.empty())
{
inventory::ObjectMap pimObjMap{
{invPath, {{"com.ibm.ipzvpd.VINI", {{"CC", Binary{}}}}}}};
common::utility::callPIM(move(pimObjMap));
}
else
{
throw std::runtime_error("Path empty in Json");
}
}
}
catch (const GpioException& e)
{
PelAdditionalData additionalData{};
additionalData.emplace("DESCRIPTION", e.what());
createPEL(additionalData, PelSeverity::WARNING, errIntfForGpioError,
nullptr);
}
}
/**
* @brief Fills the Decorator.AssetTag property into the interfaces map
*
* This function should only be called in cases where we did not find a JSON
* symlink. A missing symlink in /var/lib will be considered as a factory reset
* and this function will be used to default the AssetTag property.
*
* @param interfaces A possibly pre-populated map of inetrfaces to properties.
* @param vpdMap A VPD map of the system VPD data.
*/
static void fillAssetTag(inventory::InterfaceMap& interfaces,
const Parsed& vpdMap)
{
// Read the system serial number and MTM
// Default asset tag is Server-MTM-System Serial
inventory::Interface assetIntf{
"xyz.openbmc_project.Inventory.Decorator.AssetTag"};
inventory::PropertyMap assetTagProps;
std::string defaultAssetTag =
std::string{"Server-"} + getKwVal(vpdMap, "VSYS", "TM") +
std::string{"-"} + getKwVal(vpdMap, "VSYS", "SE");
assetTagProps.emplace("AssetTag", defaultAssetTag);
insertOrMerge(interfaces, assetIntf, std::move(assetTagProps));
}
/**
* @brief Set certain one time properties in the inventory
* Use this function to insert the Functional and Enabled properties into the
* inventory map. This function first checks if the object in question already
* has these properties hosted on D-Bus, if the property is already there, it is
* not modified, hence the name "one time". If the property is not already
* present, it will be added to the map with a suitable default value (true for
* Functional and Enabled)
*
* @param[in] object - The inventory D-Bus object without the inventory prefix.
* @param[in,out] interfaces - Reference to a map of inventory interfaces to
* which the properties will be attached.
*/
static void setOneTimeProperties(const std::string& object,
inventory::InterfaceMap& interfaces)
{
auto bus = sdbusplus::bus::new_default();
auto objectPath = INVENTORY_PATH + object;
auto prop = bus.new_method_call("xyz.openbmc_project.Inventory.Manager",
objectPath.c_str(),
"org.freedesktop.DBus.Properties", "Get");
prop.append("xyz.openbmc_project.State.Decorator.OperationalStatus");
prop.append("Functional");
try
{
auto result = bus.call(prop);
}
catch (const sdbusplus::exception::SdBusError& e)
{
// Treat as property unavailable
inventory::PropertyMap prop;
prop.emplace("Functional", true);
interfaces.emplace(
"xyz.openbmc_project.State.Decorator.OperationalStatus",
move(prop));
}
prop = bus.new_method_call("xyz.openbmc_project.Inventory.Manager",
objectPath.c_str(),
"org.freedesktop.DBus.Properties", "Get");
prop.append("xyz.openbmc_project.Object.Enable");
prop.append("Enabled");
try
{
auto result = bus.call(prop);
}
catch (const sdbusplus::exception::SdBusError& e)
{
// Treat as property unavailable
inventory::PropertyMap prop;
prop.emplace("Enabled", true);
interfaces.emplace("xyz.openbmc_project.Object.Enable", move(prop));
}
}
/**
* @brief Prime the Inventory
* Prime the inventory by populating only the location code,
* type interface and the inventory object for the frus
* which are not system vpd fru.
*
* @param[in] jsObject - Reference to vpd inventory json object
* @param[in] vpdMap - Reference to the parsed vpd map
*
* @returns Map of items in extraInterface.
*/
template <typename T>
inventory::ObjectMap
primeInventory(const nlohmann::json& jsObject, const T& vpdMap)
{
inventory::ObjectMap objects;
for (auto& itemFRUS : jsObject["frus"].items())
{
for (auto& itemEEPROM : itemFRUS.value())
{
// Take pre actions if needed
if (itemEEPROM.find("preAction") != itemEEPROM.end())
{
preAction(jsObject, itemFRUS.key());
}
inventory::InterfaceMap interfaces;
inventory::Object object(itemEEPROM.at("inventoryPath"));
if ((itemFRUS.key() != systemVpdFilePath) &&
!itemEEPROM.value("noprime", false))
{
inventory::PropertyMap presProp;
// Do not populate Present property for frus whose
// synthesized=true. synthesized=true says the fru VPD is
// synthesized and owned by a separate component.
// In some cases, the FRU has its own VPD, but still a separate
// application handles the FRU's presence. So VPD parser skips
// populating Present property by checking the JSON flag,
// "handlePresence".
if (!itemEEPROM.value("synthesized", false))
{
if (itemEEPROM.value("handlePresence", true))
{
presProp.emplace("Present", false);
interfaces.emplace("xyz.openbmc_project.Inventory.Item",
presProp);
if ((jsObject["frus"][itemFRUS.key()].at(0).contains(
"extraInterfaces")) &&
(jsObject["frus"][itemFRUS.key()]
.at(0)["extraInterfaces"]
.contains("xyz.openbmc_project.Inventory."
"Item.PCIeDevice")))
{
// check if any subtree exist under the parent
// path.
std::vector<std::string> interfaceList{
"xyz.openbmc_project.Inventory.Item"};
MapperResponse subTree =
getObjectSubtreeForInterfaces(
INVENTORY_PATH + std::string(object), 0,
interfaceList);
for (auto [objectPath, serviceInterfaceMap] :
subTree)
{
std::string subTreeObjPath{objectPath};
// Strip any inventory prefix in path
if (subTreeObjPath.find(INVENTORY_PATH) == 0)
{
subTreeObjPath = subTreeObjPath.substr(
sizeof(INVENTORY_PATH) - 1);
}
// If subtree present, set its presence to
// false and functional to true.
inventory::ObjectMap objectMap{
{subTreeObjPath,
{{"xyz.openbmc_project.State."
"Decorator."
"OperationalStatus",
{{"Functional", true}}},
{"xyz.openbmc_project.Inventory.Item",
{{"Present", false}}}}}};
common::utility::callPIM(move(objectMap));
}
}
}
}
setOneTimeProperties(object, interfaces);
if (itemEEPROM.find("extraInterfaces") != itemEEPROM.end())
{
for (const auto& eI : itemEEPROM["extraInterfaces"].items())
{
inventory::PropertyMap props;
if (eI.key() == IBM_LOCATION_CODE_INF)
{
if constexpr (std::is_same<T, Parsed>::value)
{
for (auto& lC : eI.value().items())
{
auto propVal = expandLocationCode(
lC.value().get<string>(), vpdMap, true);
props.emplace(move(lC.key()),
move(propVal));
interfaces.emplace(XYZ_LOCATION_CODE_INF,
props);
interfaces.emplace(move(eI.key()),
move(props));
}
}
}
else if (eI.key() ==
"xyz.openbmc_project.Inventory.Item")
{
for (auto& val : eI.value().items())
{
if (val.key() == "PrettyName")
{
presProp.emplace(val.key(),
val.value().get<string>());
}
}
// Use insert_or_assign here as we may already have
// inserted the present property only earlier in
// this function under this same interface.
interfaces.insert_or_assign(eI.key(),
move(presProp));
}
else
{
interfaces.emplace(move(eI.key()), move(props));
}
}
}
objects.emplace(move(object), move(interfaces));
}
}
}
return objects;
}
/**
* @brief This API executes command to set environment variable
* And then reboot the system
* @param[in] key -env key to set new value
* @param[in] value -value to set.
*/
void setEnvAndReboot(const string& key, const string& value)
{
// set env and reboot and break.
executeCmd("/sbin/fw_setenv", key, value);
log<level::INFO>("Rebooting BMC to pick up new device tree");
// make dbus call to reboot
auto bus = sdbusplus::bus::new_default_system();
auto method = bus.new_method_call(
"org.freedesktop.systemd1", "/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager", "Reboot");
bus.call_noreply(method);
}
/*
* @brief This API checks for env var fitconfig.
* If not initialised OR updated as per the current system type,
* update this env var and reboot the system.
*
* @param[in] systemType IM kwd in vpd tells about which system type it is.
* */
void setDevTreeEnv(const string& systemType)
{
// Init with default dtb
string newDeviceTree = "conf-aspeed-bmc-ibm-rainier-p1.dtb";
static const deviceTreeMap deviceTreeSystemTypeMap = {
{RAINIER_2U, "conf-aspeed-bmc-ibm-rainier-p1.dtb"},
{RAINIER_2U_V2, "conf-aspeed-bmc-ibm-rainier.dtb"},
{RAINIER_4U, "conf-aspeed-bmc-ibm-rainier-4u-p1.dtb"},
{RAINIER_4U_V2, "conf-aspeed-bmc-ibm-rainier-4u.dtb"},
{RAINIER_1S4U, "conf-aspeed-bmc-ibm-rainier-1s4u.dtb"},
{EVEREST, "conf-aspeed-bmc-ibm-everest.dtb"},
{EVEREST_V2, "conf-aspeed-bmc-ibm-everest.dtb"},
{BONNELL, "conf-aspeed-bmc-ibm-bonnell.dtb"},
{BLUERIDGE_2U, "conf-aspeed-bmc-ibm-blueridge-p1.dtb"},
{BLUERIDGE_2U_V2, "conf-aspeed-bmc-ibm-blueridge.dtb"},
{BLUERIDGE_4U, "conf-aspeed-bmc-ibm-blueridge-4u-p1.dtb"},
{BLUERIDGE_4U_V2, "conf-aspeed-bmc-ibm-blueridge-4u.dtb"},
{BLUERIDGE_1S4U, "conf-aspeed-bmc-ibm-blueridge-1s4u.dtb"},
{FUJI, "conf-aspeed-bmc-ibm-fuji.dtb"},
{HUYGENS, "conf-aspeed-bmc-ibm-huygens.dtb"},
{FUJI_V2, "conf-aspeed-bmc-ibm-fuji.dtb"}};
if (deviceTreeSystemTypeMap.find(systemType) !=
deviceTreeSystemTypeMap.end())
{
newDeviceTree = deviceTreeSystemTypeMap.at(systemType);
}
else
{
// System type not supported
string err = "This System type not found/supported in dtb table " +
systemType +
".Please check the HW and IM keywords in the system "
"VPD.Breaking...";
// map to hold additional data in case of logging pel
PelAdditionalData additionalData{};
additionalData.emplace("DESCRIPTION", err);
createPEL(additionalData, PelSeverity::WARNING,
errIntfForInvalidSystemType, nullptr);
exit(-1);
}
string readVarValue;
bool envVarFound = false;
vector<string> output = executeCmd("/sbin/fw_printenv");
for (const auto& entry : output)
{
size_t pos = entry.find("=");
string key = entry.substr(0, pos);
if (key != "fitconfig")
{
continue;
}
envVarFound = true;
if (pos + 1 < entry.size())
{
readVarValue = entry.substr(pos + 1);
if (readVarValue.find(newDeviceTree) != string::npos)
{
// fitconfig is Updated. No action needed
break;
}
}
// set env and reboot and break.
setEnvAndReboot(key, newDeviceTree);
exit(0);
}
// check If env var Not found
if (!envVarFound)
{
setEnvAndReboot("fitconfig", newDeviceTree);
}
}
/**
* @brief Parse the given EEPROM file.
*
* @param[in] vpdFilePath - Path of EEPROM file
* @param[in] js- Reference to vpd inventory json object
* @return Parsed VPD map
*/
std::variant<KeywordVpdMap, openpower::vpd::Store>
parseVpdFile(const std::string& vpdFilePath, const nlohmann::json& js)
{
uint32_t vpdStartOffset = 0;
for (const auto& item : js["frus"][vpdFilePath])
{
if (item.find("offset") != item.end())
{
vpdStartOffset = item["offset"];
break;
}
}
Binary vpdVector = getVpdDataInVector(js, vpdFilePath);
ParserInterface* parser = ParserFactory::getParser(
vpdVector,
(pimPath + js["frus"][vpdFilePath][0]["inventoryPath"]
.get_ref<const nlohmann::json::string_t&>()),
vpdFilePath, vpdStartOffset);
auto parseResult = parser->parse();
// release the parser object
ParserFactory::freeParser(parser);
return parseResult;
}
/*
* @brief This API retrieves the hardware backup in map
*
* @param[in] systemVpdBackupPath - The path that backs up the system VPD.
* @param[in] backupVpdInvPath - FRU inventory path.
* @param[in] js - JSON object.
* @param[out] backupVpdMap - An IPZ VPD map containing the parsed backup VPD.
*
* */
void getBackupVpdInMap(const string& systemVpdBackupPath,
const string& backupVpdInvPath, const nlohmann::json& js,
Parsed& backupVpdMap)
{
PelAdditionalData additionalData{};
if (!fs::exists(systemVpdBackupPath))
{
string errorMsg = "Device path ";
errorMsg += systemVpdBackupPath;
errorMsg += " does not exist";
additionalData.emplace("DESCRIPTION", errorMsg);
additionalData.emplace("CALLOUT_INVENTORY_PATH",
INVENTORY_PATH + backupVpdInvPath);
createPEL(additionalData, PelSeverity::ERROR, errIntfForStreamFail,
nullptr);
}
else
{
auto backupVpdParsedResult = parseVpdFile(systemVpdBackupPath, js);
if (auto pVal = get_if<Store>(&backupVpdParsedResult))
{
backupVpdMap = pVal->getVpdMap();
}
else
{
std::cerr << "Invalid format of VPD in back up. Restore aborted."
<< std::endl;
}