1 #include "config.h"
2 
3 #include "bios_handler.hpp"
4 
5 #include "const.hpp"
6 #include "ibm_vpd_utils.hpp"
7 #include "manager.hpp"
8 #include "types.hpp"
9 
10 #include <sdbusplus/bus.hpp>
11 
12 #include <iostream>
13 #include <memory>
14 #include <string>
15 #include <tuple>
16 #include <variant>
17 
18 using namespace openpower::vpd;
19 using namespace openpower::vpd::constants;
20 
21 namespace openpower
22 {
23 namespace vpd
24 {
25 namespace manager
26 {
27 void BiosHandler::checkAndListenPLDMService()
28 {
29     // Setup a match on NameOwnerChanged to determine when PLDM is up. In
30     // the signal handler, call restoreBIOSAttribs
31     static std::shared_ptr<sdbusplus::bus::match_t> nameOwnerMatch =
32         std::make_shared<sdbusplus::bus::match_t>(
33             *conn,
34             sdbusplus::bus::match::rules::nameOwnerChanged(
35                 "xyz.openbmc_project.PLDM"),
36             [this](sdbusplus::message_t& msg) {
37         if (msg.is_method_error())
38         {
39             std::cerr << "Error in reading name owner signal " << std::endl;
40             return;
41         }
42         std::string name;
43         std::string newOwner;
44         std::string oldOwner;
45 
46         msg.read(name, oldOwner, newOwner);
47         if (newOwner != "" && name == "xyz.openbmc_project.PLDM")
48         {
49             this->restoreBIOSAttribs();
50             // We don't need the match anymore
51             nameOwnerMatch.reset();
52         }
53     });
54     // Check if PLDM is already running, if it is, we can go ahead and attempt
55     // to sync BIOS attributes (since PLDM would have initialized them by the
56     // time it acquires a bus name).
57     bool isPLDMRunning = false;
58     try
59     {
60         auto bus = sdbusplus::bus::new_default();
61         auto method =
62             bus.new_method_call("org.freedesktop.DBus", "/org/freedesktop/DBus",
63                                 "org.freedesktop.DBus", "NameHasOwner");
64         method.append("xyz.openbmc_project.PLDM");
65 
66         auto result = bus.call(method);
67         result.read(isPLDMRunning);
68     }
69     catch (const sdbusplus::exception::SdBusError& e)
70     {
71         std::cerr << "Failed to check if PLDM is running, assume false"
72                   << std::endl;
73         std::cerr << e.what() << std::endl;
74     }
75 
76     std::cout << "Is PLDM running: " << isPLDMRunning << std::endl;
77 
78     if (isPLDMRunning)
79     {
80         nameOwnerMatch.reset();
81         restoreBIOSAttribs();
82     }
83 }
84 
85 void BiosHandler::listenBiosAttribs()
86 {
87     static std::shared_ptr<sdbusplus::bus::match_t> biosMatcher =
88         std::make_shared<sdbusplus::bus::match_t>(
89             *conn,
90             sdbusplus::bus::match::rules::propertiesChanged(
91                 "/xyz/openbmc_project/bios_config/manager",
92                 "xyz.openbmc_project.BIOSConfig.Manager"),
93             [this](sdbusplus::message_t& msg) { biosAttribsCallback(msg); });
94 }
95 
96 void BiosHandler::biosAttribsCallback(sdbusplus::message_t& msg)
97 {
98     if (msg.is_method_error())
99     {
100         std::cerr << "Error in reading BIOS attribute signal " << std::endl;
101         return;
102     }
103     using BiosProperty = std::tuple<
104         std::string, bool, std::string, std::string, std::string,
105         std::variant<int64_t, std::string>, std::variant<int64_t, std::string>,
106         std::vector<
107             std::tuple<std::string, std::variant<int64_t, std::string>>>>;
108     using BiosBaseTable = std::variant<std::map<std::string, BiosProperty>>;
109     using BiosBaseTableType = std::map<std::string, BiosBaseTable>;
110 
111     std::string object;
112     BiosBaseTableType propMap;
113     msg.read(object, propMap);
114     for (auto prop : propMap)
115     {
116         if (prop.first == "BaseBIOSTable")
117         {
118             auto list = std::get<0>(prop.second);
119             for (const auto& item : list)
120             {
121                 std::string attributeName = std::get<0>(item);
122                 if (attributeName == "hb_memory_mirror_mode")
123                 {
124                     auto attrValue = std::get<5>(std::get<1>(item));
125                     auto val = std::get_if<std::string>(&attrValue);
126                     if (val)
127                     {
128                         saveAMMToVPD(*val);
129                     }
130                 }
131                 else if (attributeName == "hb_field_core_override")
132                 {
133                     auto attrValue = std::get<5>(std::get<1>(item));
134                     auto val = std::get_if<int64_t>(&attrValue);
135                     if (val)
136                     {
137                         saveFCOToVPD(*val);
138                     }
139                 }
140                 else if (attributeName == "pvm_keep_and_clear")
141                 {
142                     auto attrValue = std::get<5>(std::get<1>(item));
143                     auto val = std::get_if<std::string>(&attrValue);
144                     if (val)
145                     {
146                         saveKeepAndClearToVPD(*val);
147                     }
148                 }
149                 else if (attributeName == "pvm_create_default_lpar")
150                 {
151                     auto attrValue = std::get<5>(std::get<1>(item));
152                     auto val = std::get_if<std::string>(&attrValue);
153                     if (val)
154                     {
155                         saveCreateDefaultLparToVPD(*val);
156                     }
157                 }
158                 else if (attributeName == "pvm_clear_nvram")
159                 {
160                     auto attrValue = std::get<5>(std::get<1>(item));
161                     auto val = std::get_if<std::string>(&attrValue);
162                     if (val)
163                     {
164                         saveClearNVRAMToVPD(*val);
165                     }
166                 }
167             }
168         }
169     }
170 }
171 
172 void BiosHandler::saveFCOToVPD(int64_t fcoVal)
173 {
174     if (fcoVal == -1)
175     {
176         std::cerr << "Invalid FCO value from BIOS: " << fcoVal << std::endl;
177         return;
178     }
179 
180     Binary vpdVal = {0, 0, 0, static_cast<uint8_t>(fcoVal)};
181     auto valInVPD = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.VSYS", "RG");
182 
183     if (valInVPD.size() != 4)
184     {
185         std::cerr << "Read bad size for VSYS/RG: " << valInVPD.size()
186                   << std::endl;
187         return;
188     }
189 
190     if (std::memcmp(vpdVal.data(), valInVPD.data(), 4) != 0)
191     {
192         std::cout << "Writing FCO to VPD: " << fcoVal << std::endl;
193         manager.writeKeyword(sdbusplus::message::object_path{SYSTEM_OBJECT},
194                              "VSYS", "RG", vpdVal);
195     }
196 }
197 
198 void BiosHandler::saveAMMToVPD(const std::string& mirrorMode)
199 {
200     Binary vpdVal;
201     auto valInVPD = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.UTIL", "D0");
202 
203     if (valInVPD.size() != 1)
204     {
205         std::cerr << "Read bad size for UTIL/D0: " << valInVPD.size()
206                   << std::endl;
207         return;
208     }
209 
210     if (mirrorMode != "Enabled" && mirrorMode != "Disabled")
211     {
212         std::cerr << "Bad value for Mirror mode BIOS attribute: " << mirrorMode
213                   << std::endl;
214         return;
215     }
216 
217     // Write to VPD only if the value is not already what we want to write.
218     if (mirrorMode == "Enabled" && valInVPD.at(0) != 2)
219     {
220         vpdVal.emplace_back(2);
221     }
222     else if (mirrorMode == "Disabled" && valInVPD.at(0) != 1)
223     {
224         vpdVal.emplace_back(1);
225     }
226 
227     if (!vpdVal.empty())
228     {
229         std::cout << "Writing AMM to VPD: " << static_cast<int>(vpdVal.at(0))
230                   << std::endl;
231         manager.writeKeyword(sdbusplus::message::object_path{SYSTEM_OBJECT},
232                              "UTIL", "D0", vpdVal);
233     }
234 }
235 
236 void BiosHandler::saveKeepAndClearToVPD(const std::string& keepAndClear)
237 {
238     Binary vpdVal;
239     auto valInVPD = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.UTIL", "D1");
240 
241     if (valInVPD.size() != 1)
242     {
243         std::cerr << "Read bad size for UTIL/D1: " << valInVPD.size()
244                   << std::endl;
245         return;
246     }
247 
248     if (keepAndClear != "Enabled" && keepAndClear != "Disabled")
249     {
250         std::cerr << "Bad value for keep and clear BIOS attribute: "
251                   << keepAndClear << std::endl;
252         return;
253     }
254 
255     // Write to VPD only if the value is not already what we want to write.
256     if (keepAndClear == "Enabled" && ((valInVPD.at(0) & 0x01) != 0x01))
257     {
258         vpdVal.emplace_back(valInVPD.at(0) | 0x01);
259     }
260     else if (keepAndClear == "Disabled" && ((valInVPD.at(0) & 0x01) != 0))
261     {
262         vpdVal.emplace_back(valInVPD.at(0) & ~(0x01));
263     }
264 
265     if (!vpdVal.empty())
266     {
267         std::cout << "Writing Keep and Clear to VPD: "
268                   << static_cast<int>(vpdVal.at(0)) << std::endl;
269         manager.writeKeyword(sdbusplus::message::object_path{SYSTEM_OBJECT},
270                              "UTIL", "D1", vpdVal);
271     }
272 }
273 
274 void BiosHandler::saveCreateDefaultLparToVPD(
275     const std::string& createDefaultLpar)
276 {
277     Binary vpdVal;
278     auto valInVPD = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.UTIL", "D1");
279 
280     if (valInVPD.size() != 1)
281     {
282         std::cerr << "Read bad size for UTIL/D1: " << valInVPD.size()
283                   << std::endl;
284         return;
285     }
286 
287     if (createDefaultLpar != "Enabled" && createDefaultLpar != "Disabled")
288     {
289         std::cerr << "Bad value for create default lpar BIOS attribute: "
290                   << createDefaultLpar << std::endl;
291         return;
292     }
293 
294     // Write to VPD only if the value is not already what we want to write.
295     if (createDefaultLpar == "Enabled" && ((valInVPD.at(0) & 0x02) != 0x02))
296     {
297         vpdVal.emplace_back(valInVPD.at(0) | 0x02);
298     }
299     else if (createDefaultLpar == "Disabled" && ((valInVPD.at(0) & 0x02) != 0))
300     {
301         vpdVal.emplace_back(valInVPD.at(0) & ~(0x02));
302     }
303 
304     if (!vpdVal.empty())
305     {
306         std::cout << "Writing create default lpar to VPD: "
307                   << static_cast<int>(vpdVal.at(0)) << std::endl;
308         manager.writeKeyword(sdbusplus::message::object_path{SYSTEM_OBJECT},
309                              "UTIL", "D1", vpdVal);
310     }
311 }
312 
313 void BiosHandler::saveClearNVRAMToVPD(const std::string& clearNVRAM)
314 {
315     Binary vpdVal;
316     auto valInVPD = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.UTIL", "D1");
317 
318     if (valInVPD.size() != 1)
319     {
320         std::cerr << "Read bad size for UTIL/D1: " << valInVPD.size()
321                   << std::endl;
322         return;
323     }
324 
325     if (clearNVRAM != "Enabled" && clearNVRAM != "Disabled")
326     {
327         std::cerr << "Bad value for clear NVRAM BIOS attribute: " << clearNVRAM
328                   << std::endl;
329         return;
330     }
331 
332     // Write to VPD only if the value is not already what we want to write.
333     if (clearNVRAM == "Enabled" && ((valInVPD.at(0) & 0x04) != 0x04))
334     {
335         vpdVal.emplace_back(valInVPD.at(0) | 0x04);
336     }
337     else if (clearNVRAM == "Disabled" && ((valInVPD.at(0) & 0x04) != 0))
338     {
339         vpdVal.emplace_back(valInVPD.at(0) & ~(0x04));
340     }
341 
342     if (!vpdVal.empty())
343     {
344         std::cout << "Writing clear NVRAM to VPD: "
345                   << static_cast<int>(vpdVal.at(0)) << std::endl;
346         manager.writeKeyword(sdbusplus::message::object_path{SYSTEM_OBJECT},
347                              "UTIL", "D1", vpdVal);
348     }
349 }
350 
351 int64_t BiosHandler::readBIOSFCO()
352 {
353     int64_t fcoVal = -1;
354     auto val = readBIOSAttribute("hb_field_core_override");
355 
356     if (auto pVal = std::get_if<int64_t>(&val))
357     {
358         fcoVal = *pVal;
359     }
360     else
361     {
362         std::cerr << "FCO is not an int" << std::endl;
363     }
364     return fcoVal;
365 }
366 
367 std::string BiosHandler::readBIOSAMM()
368 {
369     std::string ammVal{};
370     auto val = readBIOSAttribute("hb_memory_mirror_mode");
371 
372     if (auto pVal = std::get_if<std::string>(&val))
373     {
374         ammVal = *pVal;
375     }
376     else
377     {
378         std::cerr << "AMM is not a string" << std::endl;
379     }
380     return ammVal;
381 }
382 
383 std::string BiosHandler::readBIOSKeepAndClear()
384 {
385     std::string keepAndClear{};
386     auto val = readBIOSAttribute("pvm_keep_and_clear");
387 
388     if (auto pVal = std::get_if<std::string>(&val))
389     {
390         keepAndClear = *pVal;
391     }
392     else
393     {
394         std::cerr << "Keep and clear is not a string" << std::endl;
395     }
396     return keepAndClear;
397 }
398 
399 std::string BiosHandler::readBIOSCreateDefaultLpar()
400 {
401     std::string createDefaultLpar{};
402     auto val = readBIOSAttribute("pvm_create_default_lpar");
403 
404     if (auto pVal = std::get_if<std::string>(&val))
405     {
406         createDefaultLpar = *pVal;
407     }
408     else
409     {
410         std::cerr << "Create default LPAR is not a string" << std::endl;
411     }
412     return createDefaultLpar;
413 }
414 
415 std::string BiosHandler::readBIOSClearNVRAM()
416 {
417     std::string clearNVRAM{};
418     auto val = readBIOSAttribute("pvm_clear_nvram");
419 
420     if (auto pVal = std::get_if<std::string>(&val))
421     {
422         clearNVRAM = *pVal;
423     }
424     else
425     {
426         std::cerr << "Clear NVRAM is not a string" << std::endl;
427     }
428     return clearNVRAM;
429 }
430 
431 void BiosHandler::saveFCOToBIOS(const std::string& fcoVal, int64_t fcoInBIOS)
432 {
433     if (fcoVal.size() != 4)
434     {
435         std::cerr << "Bad size for FCO in VPD: " << fcoVal.size() << std::endl;
436         return;
437     }
438 
439     // Need to write?
440     if (fcoInBIOS == static_cast<int64_t>(fcoVal.at(3)))
441     {
442         std::cout << "Skip FCO BIOS write, value is already: " << fcoInBIOS
443                   << std::endl;
444         return;
445     }
446 
447     PendingBIOSAttrsType biosAttrs;
448     biosAttrs.push_back(
449         std::make_pair("hb_field_core_override",
450                        std::make_tuple("xyz.openbmc_project.BIOSConfig.Manager."
451                                        "AttributeType.Integer",
452                                        fcoVal.at(3))));
453 
454     std::cout << "Set hb_field_core_override to: "
455               << static_cast<int>(fcoVal.at(3)) << std::endl;
456 
457     setBusProperty<PendingBIOSAttrsType>(
458         "xyz.openbmc_project.BIOSConfigManager",
459         "/xyz/openbmc_project/bios_config/manager",
460         "xyz.openbmc_project.BIOSConfig.Manager", "PendingAttributes",
461         biosAttrs);
462 }
463 
464 void BiosHandler::saveAMMToBIOS(const std::string& ammVal,
465                                 const std::string& ammInBIOS)
466 {
467     if (ammVal.size() != 1)
468     {
469         std::cerr << "Bad size for AMM in VPD: " << ammVal.size() << std::endl;
470         return;
471     }
472 
473     // Make sure data in VPD is sane
474     if (ammVal.at(0) != 1 && ammVal.at(0) != 2)
475     {
476         std::cerr << "Bad value for AMM read from VPD: "
477                   << static_cast<int>(ammVal.at(0)) << std::endl;
478         return;
479     }
480 
481     // Need to write?
482     std::string toWrite = (ammVal.at(0) == 2) ? "Enabled" : "Disabled";
483     if (ammInBIOS == toWrite)
484     {
485         std::cout << "Skip AMM BIOS write, value is already: " << toWrite
486                   << std::endl;
487         return;
488     }
489 
490     PendingBIOSAttrsType biosAttrs;
491     biosAttrs.push_back(
492         std::make_pair("hb_memory_mirror_mode",
493                        std::make_tuple("xyz.openbmc_project.BIOSConfig.Manager."
494                                        "AttributeType.Enumeration",
495                                        toWrite)));
496 
497     std::cout << "Set hb_memory_mirror_mode to: " << toWrite << std::endl;
498 
499     setBusProperty<PendingBIOSAttrsType>(
500         "xyz.openbmc_project.BIOSConfigManager",
501         "/xyz/openbmc_project/bios_config/manager",
502         "xyz.openbmc_project.BIOSConfig.Manager", "PendingAttributes",
503         biosAttrs);
504 }
505 
506 void BiosHandler::saveKeepAndClearToBIOS(const std::string& keepAndClear,
507                                          const std::string& keepAndClearInBIOS)
508 {
509     if (keepAndClear.size() != 1)
510     {
511         std::cerr << "Bad size for Keep and Clear in VPD: "
512                   << keepAndClear.size() << std::endl;
513         return;
514     }
515 
516     // Need to write?
517     std::string toWrite = (keepAndClear.at(0) & 0x01) ? "Enabled" : "Disabled";
518     if (keepAndClearInBIOS == toWrite)
519     {
520         std::cout << "Skip Keep and Clear BIOS write, value is already: "
521                   << toWrite << std::endl;
522         return;
523     }
524 
525     PendingBIOSAttrsType biosAttrs;
526     biosAttrs.push_back(
527         std::make_pair("pvm_keep_and_clear",
528                        std::make_tuple("xyz.openbmc_project.BIOSConfig.Manager."
529                                        "AttributeType.Enumeration",
530                                        toWrite)));
531 
532     std::cout << "Set pvm_keep_and_clear to: " << toWrite << std::endl;
533 
534     setBusProperty<PendingBIOSAttrsType>(
535         "xyz.openbmc_project.BIOSConfigManager",
536         "/xyz/openbmc_project/bios_config/manager",
537         "xyz.openbmc_project.BIOSConfig.Manager", "PendingAttributes",
538         biosAttrs);
539 }
540 
541 void BiosHandler::saveCreateDefaultLparToBIOS(
542     const std::string& createDefaultLpar,
543     const std::string& createDefaultLparInBIOS)
544 {
545     if (createDefaultLpar.size() != 1)
546     {
547         std::cerr << "Bad size for Create default LPAR in VPD: "
548                   << createDefaultLpar.size() << std::endl;
549         return;
550     }
551 
552     // Need to write?
553     std::string toWrite = (createDefaultLpar.at(0) & 0x02) ? "Enabled"
554                                                            : "Disabled";
555     if (createDefaultLparInBIOS == toWrite)
556     {
557         std::cout << "Skip Create default LPAR BIOS write, value is already: "
558                   << toWrite << std::endl;
559         return;
560     }
561 
562     PendingBIOSAttrsType biosAttrs;
563     biosAttrs.push_back(
564         std::make_pair("pvm_create_default_lpar",
565                        std::make_tuple("xyz.openbmc_project.BIOSConfig.Manager."
566                                        "AttributeType.Enumeration",
567                                        toWrite)));
568 
569     std::cout << "Set pvm_create_default_lpar to: " << toWrite << std::endl;
570 
571     setBusProperty<PendingBIOSAttrsType>(
572         "xyz.openbmc_project.BIOSConfigManager",
573         "/xyz/openbmc_project/bios_config/manager",
574         "xyz.openbmc_project.BIOSConfig.Manager", "PendingAttributes",
575         biosAttrs);
576 }
577 
578 void BiosHandler::saveClearNVRAMToBIOS(const std::string& clearNVRAM,
579                                        const std::string& clearNVRAMInBIOS)
580 {
581     if (clearNVRAM.size() != 1)
582     {
583         std::cerr << "Bad size for Clear NVRAM in VPD: " << clearNVRAM.size()
584                   << std::endl;
585         return;
586     }
587 
588     // Need to write?
589     std::string toWrite = (clearNVRAM.at(0) & 0x04) ? "Enabled" : "Disabled";
590     if (clearNVRAMInBIOS == toWrite)
591     {
592         std::cout << "Skip Clear NVRAM BIOS write, value is already: "
593                   << toWrite << std::endl;
594         return;
595     }
596 
597     PendingBIOSAttrsType biosAttrs;
598     biosAttrs.push_back(
599         std::make_pair("pvm_clear_nvram",
600                        std::make_tuple("xyz.openbmc_project.BIOSConfig.Manager."
601                                        "AttributeType.Enumeration",
602                                        toWrite)));
603 
604     std::cout << "Set pvm_clear_nvram to: " << toWrite << std::endl;
605 
606     setBusProperty<PendingBIOSAttrsType>(
607         "xyz.openbmc_project.BIOSConfigManager",
608         "/xyz/openbmc_project/bios_config/manager",
609         "xyz.openbmc_project.BIOSConfig.Manager", "PendingAttributes",
610         biosAttrs);
611 }
612 
613 void BiosHandler::restoreBIOSAttribs()
614 {
615     // TODO: We could make this slightly more scalable by defining a table of
616     // attributes and their corresponding VPD keywords. However, that needs much
617     // more thought.
618     std::cout << "Attempting BIOS attribute reset" << std::endl;
619     // Check if the VPD contains valid data for FCO, AMM, Keep and Clear,
620     // Create default LPAR and Clear NVRAM *and* that it differs from the data
621     // already in the attributes. If so, set the BIOS attributes as per the
622     // value in the VPD. If the VPD contains default data, then initialize the
623     // VPD keywords with data taken from the BIOS.
624     auto fcoInVPD = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.VSYS", "RG");
625     auto ammInVPD = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.UTIL", "D0");
626     auto keepAndClearInVPD = readBusProperty(SYSTEM_OBJECT,
627                                              "com.ibm.ipzvpd.UTIL", "D1");
628     auto fcoInBIOS = readBIOSFCO();
629     auto ammInBIOS = readBIOSAMM();
630     auto keepAndClearInBIOS = readBIOSKeepAndClear();
631     auto createDefaultLparInBIOS = readBIOSCreateDefaultLpar();
632     auto clearNVRAMInBIOS = readBIOSClearNVRAM();
633 
634     if (fcoInVPD == "    ")
635     {
636         saveFCOToVPD(fcoInBIOS);
637     }
638     else
639     {
640         saveFCOToBIOS(fcoInVPD, fcoInBIOS);
641     }
642 
643     if (ammInVPD.at(0) == 0)
644     {
645         saveAMMToVPD(ammInBIOS);
646     }
647     else
648     {
649         saveAMMToBIOS(ammInVPD, ammInBIOS);
650     }
651 
652     // No uninitialized handling needed for keep and clear, create default
653     // lpar and clear nvram attributes. Their defaults in VPD are 0's which is
654     // what we want.
655     saveKeepAndClearToBIOS(keepAndClearInVPD, keepAndClearInBIOS);
656     // Have to read D1 again because two attributes are stored in the same
657     // keyword.
658     auto createDefaultLparInVPD = readBusProperty(SYSTEM_OBJECT,
659                                                   "com.ibm.ipzvpd.UTIL", "D1");
660     saveCreateDefaultLparToBIOS(createDefaultLparInVPD,
661                                 createDefaultLparInBIOS);
662 
663     auto clearNVRAMInVPD = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.UTIL",
664                                            "D1");
665     saveClearNVRAMToBIOS(clearNVRAMInVPD, clearNVRAMInBIOS);
666 
667     // Start listener now that we have done the restore
668     listenBiosAttribs();
669 }
670 } // namespace manager
671 } // namespace vpd
672 } // namespace openpower
673