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