blob: 1f0306c9e7512bac8134878dbd261179fd82d5e8 [file] [log] [blame]
Santosh Puranikf2d3b532022-04-19 06:44:07 -05001#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 <iostream>
11#include <memory>
12#include <sdbusplus/bus.hpp>
13#include <string>
14#include <tuple>
15#include <variant>
16
17using namespace openpower::vpd;
18using namespace openpower::vpd::constants;
19
20namespace openpower
21{
22namespace vpd
23{
24namespace manager
25{
26void BiosHandler::checkAndListenPLDMService()
27{
28 // Setup a match on NameOwnerChanged to determine when PLDM is up. In
29 // the signal handler, call restoreBIOSAttribs
Patrick Williams2eb01762022-07-22 19:26:56 -050030 static std::shared_ptr<sdbusplus::bus::match_t> nameOwnerMatch =
31 std::make_shared<sdbusplus::bus::match_t>(
Santosh Puranikf2d3b532022-04-19 06:44:07 -050032 bus,
33 sdbusplus::bus::match::rules::nameOwnerChanged(
34 "xyz.openbmc_project.PLDM"),
Patrick Williams2eb01762022-07-22 19:26:56 -050035 [this](sdbusplus::message_t& msg) {
Santosh Puranikf2d3b532022-04-19 06:44:07 -050036 if (msg.is_method_error())
37 {
38 std::cerr << "Error in reading name owner signal "
39 << 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
85void BiosHandler::listenBiosAttribs()
86{
Patrick Williams2eb01762022-07-22 19:26:56 -050087 static std::shared_ptr<sdbusplus::bus::match_t> biosMatcher =
88 std::make_shared<sdbusplus::bus::match_t>(
Santosh Puranikf2d3b532022-04-19 06:44:07 -050089 bus,
90 sdbusplus::bus::match::rules::propertiesChanged(
91 "/xyz/openbmc_project/bios_config/manager",
92 "xyz.openbmc_project.BIOSConfig.Manager"),
Patrick Williams2eb01762022-07-22 19:26:56 -050093 [this](sdbusplus::message_t& msg) { biosAttribsCallback(msg); });
Santosh Puranikf2d3b532022-04-19 06:44:07 -050094}
95
Patrick Williams2eb01762022-07-22 19:26:56 -050096void BiosHandler::biosAttribsCallback(sdbusplus::message_t& msg)
Santosh Puranikf2d3b532022-04-19 06:44:07 -050097{
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 }
Santosh Puranikb2c2ccc2022-05-14 05:15:44 -0500140 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 }
Santosh Puranikf2d3b532022-04-19 06:44:07 -0500158 }
159 }
160 }
161}
162
163void BiosHandler::saveFCOToVPD(int64_t fcoVal)
164{
165 if (fcoVal == -1)
166 {
167 std::cerr << "Invalid FCO value from BIOS: " << fcoVal << std::endl;
168 return;
169 }
170
171 Binary vpdVal = {0, 0, 0, static_cast<uint8_t>(fcoVal)};
172 auto valInVPD = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.VSYS", "RG");
173
174 if (valInVPD.size() != 4)
175 {
176 std::cerr << "Read bad size for VSYS/RG: " << valInVPD.size()
177 << std::endl;
178 return;
179 }
180
181 if (valInVPD.at(3) != fcoVal)
182 {
183 std::cout << "Writing FCO to VPD: " << fcoVal << std::endl;
184 manager.writeKeyword(sdbusplus::message::object_path{SYSTEM_OBJECT},
185 "VSYS", "RG", vpdVal);
186 }
187}
188
189void BiosHandler::saveAMMToVPD(const std::string& mirrorMode)
190{
191 Binary vpdVal;
192 auto valInVPD = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.UTIL", "D0");
193
194 if (valInVPD.size() != 1)
195 {
196 std::cerr << "Read bad size for UTIL/D0: " << valInVPD.size()
197 << std::endl;
198 return;
199 }
200
201 if (mirrorMode != "Enabled" && mirrorMode != "Disabled")
202 {
203 std::cerr << "Bad value for Mirror mode BIOS arttribute: " << mirrorMode
204 << std::endl;
205 return;
206 }
207
208 // Write to VPD only if the value is not already what we want to write.
209 if (mirrorMode == "Enabled" && valInVPD.at(0) != 2)
210 {
211 vpdVal.emplace_back(2);
212 }
213 else if (mirrorMode == "Disabled" && valInVPD.at(0) != 1)
214 {
215 vpdVal.emplace_back(1);
216 }
217
218 if (!vpdVal.empty())
219 {
220 std::cout << "Writing AMM to VPD: " << static_cast<int>(vpdVal.at(0))
221 << std::endl;
222 manager.writeKeyword(sdbusplus::message::object_path{SYSTEM_OBJECT},
223 "UTIL", "D0", vpdVal);
224 }
225}
226
Santosh Puranikb2c2ccc2022-05-14 05:15:44 -0500227void BiosHandler::saveKeepAndClearToVPD(const std::string& keepAndClear)
228{
229 Binary vpdVal;
230 auto valInVPD = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.UTIL", "D1");
231
232 if (valInVPD.size() != 1)
233 {
234 std::cerr << "Read bad size for UTIL/D1: " << valInVPD.size()
235 << std::endl;
236 return;
237 }
238
239 if (keepAndClear != "Enabled" && keepAndClear != "Disabled")
240 {
241 std::cerr << "Bad value for keep and clear BIOS arttribute: "
242 << keepAndClear << std::endl;
243 return;
244 }
245
246 // Write to VPD only if the value is not already what we want to write.
247 if (keepAndClear == "Enabled" && ((valInVPD.at(0) & 0x01) != 0x01))
248 {
249 vpdVal.emplace_back(valInVPD.at(0) | 0x01);
250 }
251 else if (keepAndClear == "Disabled" && ((valInVPD.at(0) & 0x01) != 0))
252 {
253 vpdVal.emplace_back(valInVPD.at(0) & ~(0x01));
254 }
255
256 if (!vpdVal.empty())
257 {
258 std::cout << "Writing Keep and Clear to VPD: "
259 << static_cast<int>(vpdVal.at(0)) << std::endl;
260 manager.writeKeyword(sdbusplus::message::object_path{SYSTEM_OBJECT},
261 "UTIL", "D1", vpdVal);
262 }
263}
264
265void BiosHandler::saveCreateDefaultLparToVPD(
266 const std::string& createDefaultLpar)
267{
268 Binary vpdVal;
269 auto valInVPD = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.UTIL", "D1");
270
271 if (valInVPD.size() != 1)
272 {
273 std::cerr << "Read bad size for UTIL/D1: " << valInVPD.size()
274 << std::endl;
275 return;
276 }
277
278 if (createDefaultLpar != "Enabled" && createDefaultLpar != "Disabled")
279 {
280 std::cerr << "Bad value for create default lpar BIOS arttribute: "
281 << createDefaultLpar << std::endl;
282 return;
283 }
284
285 // Write to VPD only if the value is not already what we want to write.
286 if (createDefaultLpar == "Enabled" && ((valInVPD.at(0) & 0x02) != 0x02))
287 {
288 vpdVal.emplace_back(valInVPD.at(0) | 0x02);
289 }
290 else if (createDefaultLpar == "Disabled" && ((valInVPD.at(0) & 0x02) != 0))
291 {
292 vpdVal.emplace_back(valInVPD.at(0) & ~(0x02));
293 }
294
295 if (!vpdVal.empty())
296 {
297 std::cout << "Writing create default lpar to VPD: "
298 << static_cast<int>(vpdVal.at(0)) << std::endl;
299 manager.writeKeyword(sdbusplus::message::object_path{SYSTEM_OBJECT},
300 "UTIL", "D1", vpdVal);
301 }
302}
303
Santosh Puranikf2d3b532022-04-19 06:44:07 -0500304int64_t BiosHandler::readBIOSFCO()
305{
306 int64_t fcoVal = -1;
307 auto val = readBIOSAttribute("hb_field_core_override");
308
309 if (auto pVal = std::get_if<int64_t>(&val))
310 {
311 fcoVal = *pVal;
312 }
313 else
314 {
315 std::cerr << "FCO is not an int" << std::endl;
316 }
317 return fcoVal;
318}
319
320std::string BiosHandler::readBIOSAMM()
321{
322 std::string ammVal{};
323 auto val = readBIOSAttribute("hb_memory_mirror_mode");
324
325 if (auto pVal = std::get_if<std::string>(&val))
326 {
327 ammVal = *pVal;
328 }
329 else
330 {
331 std::cerr << "AMM is not a string" << std::endl;
332 }
333 return ammVal;
334}
335
Santosh Puranikb2c2ccc2022-05-14 05:15:44 -0500336std::string BiosHandler::readBIOSKeepAndClear()
337{
338 std::string keepAndClear{};
339 auto val = readBIOSAttribute("pvm_keep_and_clear");
340
341 if (auto pVal = std::get_if<std::string>(&val))
342 {
343 keepAndClear = *pVal;
344 }
345 else
346 {
347 std::cerr << "Keep and clear is not a string" << std::endl;
348 }
349 return keepAndClear;
350}
351
352std::string BiosHandler::readBIOSCreateDefaultLpar()
353{
354 std::string createDefaultLpar{};
355 auto val = readBIOSAttribute("pvm_create_default_lpar");
356
357 if (auto pVal = std::get_if<std::string>(&val))
358 {
359 createDefaultLpar = *pVal;
360 }
361 else
362 {
363 std::cerr << "Create default LPAR is not a string" << std::endl;
364 }
365 return createDefaultLpar;
366}
367
Santosh Puranikf7f8da62022-05-06 13:01:19 -0500368void BiosHandler::saveFCOToBIOS(const std::string& fcoVal, int64_t fcoInBIOS)
Santosh Puranikf2d3b532022-04-19 06:44:07 -0500369{
370 if (fcoVal.size() != 4)
371 {
372 std::cerr << "Bad size for FCO in VPD: " << fcoVal.size() << std::endl;
373 return;
374 }
Santosh Puranikf7f8da62022-05-06 13:01:19 -0500375
376 // Need to write?
377 if (fcoInBIOS == static_cast<int64_t>(fcoVal.at(3)))
378 {
379 std::cout << "Skip FCO BIOS write, value is already: " << fcoInBIOS
380 << std::endl;
381 return;
382 }
383
Santosh Puranikf2d3b532022-04-19 06:44:07 -0500384 PendingBIOSAttrsType biosAttrs;
385 biosAttrs.push_back(
386 std::make_pair("hb_field_core_override",
387 std::make_tuple("xyz.openbmc_project.BIOSConfig.Manager."
388 "AttributeType.Integer",
389 fcoVal.at(3))));
390
391 std::cout << "Set hb_field_core_override to: "
392 << static_cast<int>(fcoVal.at(3)) << std::endl;
393
394 setBusProperty<PendingBIOSAttrsType>(
395 "xyz.openbmc_project.BIOSConfigManager",
396 "/xyz/openbmc_project/bios_config/manager",
397 "xyz.openbmc_project.BIOSConfig.Manager", "PendingAttributes",
398 biosAttrs);
399}
400
Santosh Puranikf7f8da62022-05-06 13:01:19 -0500401void BiosHandler::saveAMMToBIOS(const std::string& ammVal,
402 const std::string& ammInBIOS)
Santosh Puranikf2d3b532022-04-19 06:44:07 -0500403{
404 if (ammVal.size() != 1)
405 {
406 std::cerr << "Bad size for AMM in VPD: " << ammVal.size() << std::endl;
407 return;
408 }
409
410 // Make sure data in VPD is sane
411 if (ammVal.at(0) != 1 && ammVal.at(0) != 2)
412 {
413 std::cerr << "Bad value for AMM read from VPD: "
414 << static_cast<int>(ammVal.at(0)) << std::endl;
415 return;
416 }
Santosh Puranikf2d3b532022-04-19 06:44:07 -0500417
Santosh Puranikf7f8da62022-05-06 13:01:19 -0500418 // Need to write?
419 std::string toWrite = (ammVal.at(0) == 2) ? "Enabled" : "Disabled";
420 if (ammInBIOS == toWrite)
421 {
422 std::cout << "Skip AMM BIOS write, value is already: " << toWrite
423 << std::endl;
424 return;
425 }
426
427 PendingBIOSAttrsType biosAttrs;
428 biosAttrs.push_back(
429 std::make_pair("hb_memory_mirror_mode",
430 std::make_tuple("xyz.openbmc_project.BIOSConfig.Manager."
431 "AttributeType.Enumeration",
432 toWrite)));
433
434 std::cout << "Set hb_memory_mirror_mode to: " << toWrite << std::endl;
Santosh Puranikf2d3b532022-04-19 06:44:07 -0500435
436 setBusProperty<PendingBIOSAttrsType>(
437 "xyz.openbmc_project.BIOSConfigManager",
438 "/xyz/openbmc_project/bios_config/manager",
439 "xyz.openbmc_project.BIOSConfig.Manager", "PendingAttributes",
440 biosAttrs);
441}
442
Santosh Puranikb2c2ccc2022-05-14 05:15:44 -0500443void BiosHandler::saveKeepAndClearToBIOS(const std::string& keepAndClear,
444 const std::string& keepAndClearInBIOS)
445{
446 if (keepAndClear.size() != 1)
447 {
448 std::cerr << "Bad size for Keep and Clear in VPD: "
449 << keepAndClear.size() << std::endl;
450 return;
451 }
452
453 // Need to write?
454 std::string toWrite = (keepAndClear.at(0) & 0x01) ? "Enabled" : "Disabled";
455 if (keepAndClearInBIOS == toWrite)
456 {
457 std::cout << "Skip Keep and Clear BIOS write, value is already: "
458 << toWrite << std::endl;
459 return;
460 }
461
462 PendingBIOSAttrsType biosAttrs;
463 biosAttrs.push_back(
464 std::make_pair("pvm_keep_and_clear",
465 std::make_tuple("xyz.openbmc_project.BIOSConfig.Manager."
466 "AttributeType.Enumeration",
467 toWrite)));
468
469 std::cout << "Set pvm_keep_and_clear to: " << toWrite << std::endl;
470
471 setBusProperty<PendingBIOSAttrsType>(
472 "xyz.openbmc_project.BIOSConfigManager",
473 "/xyz/openbmc_project/bios_config/manager",
474 "xyz.openbmc_project.BIOSConfig.Manager", "PendingAttributes",
475 biosAttrs);
476}
477
478void BiosHandler::saveCreateDefaultLparToBIOS(
479 const std::string& createDefaultLpar,
480 const std::string& createDefaultLparInBIOS)
481{
482 if (createDefaultLpar.size() != 1)
483 {
484 std::cerr << "Bad size for Create default LPAR in VPD: "
485 << createDefaultLpar.size() << std::endl;
486 return;
487 }
488
489 // Need to write?
490 std::string toWrite =
491 (createDefaultLpar.at(0) & 0x02) ? "Enabled" : "Disabled";
492 if (createDefaultLparInBIOS == toWrite)
493 {
494 std::cout << "Skip Create default LPAR BIOS write, value is already: "
495 << toWrite << std::endl;
496 return;
497 }
498
499 PendingBIOSAttrsType biosAttrs;
500 biosAttrs.push_back(
501 std::make_pair("pvm_create_default_lpar",
502 std::make_tuple("xyz.openbmc_project.BIOSConfig.Manager."
503 "AttributeType.Enumeration",
504 toWrite)));
505
506 std::cout << "Set pvm_create_default_lpar to: " << toWrite << std::endl;
507
508 setBusProperty<PendingBIOSAttrsType>(
509 "xyz.openbmc_project.BIOSConfigManager",
510 "/xyz/openbmc_project/bios_config/manager",
511 "xyz.openbmc_project.BIOSConfig.Manager", "PendingAttributes",
512 biosAttrs);
513}
514
Santosh Puranikf2d3b532022-04-19 06:44:07 -0500515void BiosHandler::restoreBIOSAttribs()
516{
517 // TODO: We could make this slightly more scalable by defining a table of
518 // attributes and their corresponding VPD keywords. However, that needs much
519 // more thought.
520 std::cout << "Attempting BIOS attribute reset" << std::endl;
Santosh Puranikb2c2ccc2022-05-14 05:15:44 -0500521 // Check if the VPD contains valid data for FCO, AMM, Keep and Clear and
522 // Create default LPAR *and* that it differs from the data already in the
523 // attributes. If so, set the BIOS attributes as per the value in the VPD.
Santosh Puranikf2d3b532022-04-19 06:44:07 -0500524 // If the VPD contains default data, then initialize the VPD keywords with
525 // data taken from the BIOS.
526 auto fcoInVPD = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.VSYS", "RG");
527 auto ammInVPD = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.UTIL", "D0");
Santosh Puranikb2c2ccc2022-05-14 05:15:44 -0500528 auto keepAndClearInVPD =
529 readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.UTIL", "D1");
Santosh Puranikf2d3b532022-04-19 06:44:07 -0500530 auto fcoInBIOS = readBIOSFCO();
531 auto ammInBIOS = readBIOSAMM();
Santosh Puranikb2c2ccc2022-05-14 05:15:44 -0500532 auto keepAndClearInBIOS = readBIOSKeepAndClear();
533 auto createDefaultLparInBIOS = readBIOSCreateDefaultLpar();
Santosh Puranikf2d3b532022-04-19 06:44:07 -0500534
535 if (fcoInVPD == " ")
536 {
537 saveFCOToVPD(fcoInBIOS);
538 }
539 else
540 {
Santosh Puranikf7f8da62022-05-06 13:01:19 -0500541 saveFCOToBIOS(fcoInVPD, fcoInBIOS);
Santosh Puranikf2d3b532022-04-19 06:44:07 -0500542 }
543
544 if (ammInVPD.at(0) == 0)
545 {
546 saveAMMToVPD(ammInBIOS);
547 }
548 else
549 {
Santosh Puranikf7f8da62022-05-06 13:01:19 -0500550 saveAMMToBIOS(ammInVPD, ammInBIOS);
Santosh Puranikf2d3b532022-04-19 06:44:07 -0500551 }
552
Santosh Puranikb2c2ccc2022-05-14 05:15:44 -0500553 // No uninitialized handling needed for keep and clear and create default
554 // lpar attributes. Their defaults in VPD are 0's which is what we want.
555 saveKeepAndClearToBIOS(keepAndClearInVPD, keepAndClearInBIOS);
556 // Have to read D1 again because two attributes are stored in the same
557 // keyword.
558 auto createDefaultLparInVPD =
559 readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.UTIL", "D1");
560 saveCreateDefaultLparToBIOS(createDefaultLparInVPD,
561 createDefaultLparInBIOS);
562
Santosh Puranikf2d3b532022-04-19 06:44:07 -0500563 // Start listener now that we have done the restore
564 listenBiosAttribs();
565}
566} // namespace manager
567} // namespace vpd
568} // namespace openpower