xref: /openbmc/openpower-proc-control/procedures/phal/reinit_devtree.cpp (revision 1e43be06b83f426360cab248c60822c242bbd544)
194fc70cbSJayanth Othayoth #include "config.h"
294fc70cbSJayanth Othayoth 
394fc70cbSJayanth Othayoth #include "extensions/phal/create_pel.hpp"
494fc70cbSJayanth Othayoth #include "registration.hpp"
594fc70cbSJayanth Othayoth #include "temporary_file.hpp"
694fc70cbSJayanth Othayoth 
794fc70cbSJayanth Othayoth #include <fcntl.h>
894fc70cbSJayanth Othayoth 
994fc70cbSJayanth Othayoth #include <nlohmann/json.hpp>
1094fc70cbSJayanth Othayoth #include <phosphor-logging/elog-errors.hpp>
1194fc70cbSJayanth Othayoth 
1294fc70cbSJayanth Othayoth #include <cstdio>
1394fc70cbSJayanth Othayoth #include <filesystem>
14e0dd7af4SJayanth Othayoth #include <format>
1594fc70cbSJayanth Othayoth 
1694fc70cbSJayanth Othayoth extern "C"
1794fc70cbSJayanth Othayoth {
1894fc70cbSJayanth Othayoth #include <dtree.h>
1994fc70cbSJayanth Othayoth }
2094fc70cbSJayanth Othayoth 
2194fc70cbSJayanth Othayoth namespace openpower
2294fc70cbSJayanth Othayoth {
2394fc70cbSJayanth Othayoth namespace phal
2494fc70cbSJayanth Othayoth {
255409e877SJayanth Othayoth using namespace phosphor::logging;
26773fd244SJayanth Othayoth 
27773fd244SJayanth Othayoth struct FileCloser
28773fd244SJayanth Othayoth {
operator ()openpower::phal::FileCloser29773fd244SJayanth Othayoth     void operator()(FILE* fp) const
30773fd244SJayanth Othayoth     {
31773fd244SJayanth Othayoth         fclose(fp);
32773fd244SJayanth Othayoth     }
33773fd244SJayanth Othayoth };
34773fd244SJayanth Othayoth using FILE_Ptr = std::unique_ptr<FILE, FileCloser>;
35773fd244SJayanth Othayoth 
365409e877SJayanth Othayoth namespace fs = std::filesystem;
375409e877SJayanth Othayoth 
applyAttrOverride(fs::path & devtreeFile)385409e877SJayanth Othayoth void applyAttrOverride(fs::path& devtreeFile)
395409e877SJayanth Othayoth {
405409e877SJayanth Othayoth     constexpr auto DEVTREE_ATTR_OVERRIDE_PATH = "/tmp/devtree_attr_override";
415409e877SJayanth Othayoth     auto overrideFile = fs::path(DEVTREE_ATTR_OVERRIDE_PATH);
425409e877SJayanth Othayoth     if (!fs::exists(overrideFile))
435409e877SJayanth Othayoth     {
445409e877SJayanth Othayoth         return;
455409e877SJayanth Othayoth     }
465409e877SJayanth Othayoth 
475409e877SJayanth Othayoth     // Open attribute override file in r/o mode
48773fd244SJayanth Othayoth     FILE_Ptr fpOverride(fopen(DEVTREE_ATTR_OVERRIDE_PATH, "r"), FileCloser());
495409e877SJayanth Othayoth 
505409e877SJayanth Othayoth     // Update Devtree with attribute override data.
515409e877SJayanth Othayoth     auto ret = dtree_cronus_import(devtreeFile.c_str(), CEC_INFODB_PATH,
525409e877SJayanth Othayoth                                    fpOverride.get());
535409e877SJayanth Othayoth     if (ret)
545409e877SJayanth Othayoth     {
555409e877SJayanth Othayoth         log<level::ERR>(
56e0dd7af4SJayanth Othayoth             std::format("Failed({}) to update attribute override data", ret)
575409e877SJayanth Othayoth                 .c_str());
585409e877SJayanth Othayoth         throw std::runtime_error(
595409e877SJayanth Othayoth             "applyAttrOverride: dtree_cronus_import failed");
605409e877SJayanth Othayoth     }
615409e877SJayanth Othayoth     log<level::INFO>("DEVTREE: Applied attribute override data");
625409e877SJayanth Othayoth }
635409e877SJayanth Othayoth 
6494fc70cbSJayanth Othayoth /**
6590166c15SMarri Devender Rao  * @brief Compute RO device tree file path from RW symbolic link
6690166c15SMarri Devender Rao  * @return RO file path one failure exception will be thrown
6790166c15SMarri Devender Rao  */
computeRODeviceTreePath()6890166c15SMarri Devender Rao fs::path computeRODeviceTreePath()
6990166c15SMarri Devender Rao {
7090166c15SMarri Devender Rao     // Symbolic links are not created for RO files, compute the lid name
7190166c15SMarri Devender Rao     // for the RW symbolic link and use it to compute RO file.
7290166c15SMarri Devender Rao     // Example:
7390166c15SMarri Devender Rao     // RW file = /media/hostfw/running/DEVTREE -> 81e00672.lid
7490166c15SMarri Devender Rao     // RO file = /media/hostfw/running-ro/ + 81e00672.lid
7590166c15SMarri Devender Rao     fs::path rwFileName = fs::read_symlink(CEC_DEVTREE_RW_PATH);
7690166c15SMarri Devender Rao     if (rwFileName.empty())
7790166c15SMarri Devender Rao     {
7890166c15SMarri Devender Rao         std::string err =
79e0dd7af4SJayanth Othayoth             std::format("Failed to read the target file "
8090166c15SMarri Devender Rao                         "for the RW device tree symbolic link ({})",
8190166c15SMarri Devender Rao                         CEC_DEVTREE_RW_PATH);
8290166c15SMarri Devender Rao         log<level::ERR>(err.c_str());
8390166c15SMarri Devender Rao         throw std::runtime_error(err);
8490166c15SMarri Devender Rao     }
8590166c15SMarri Devender Rao     fs::path roFilePath = CEC_DEVTREE_RO_BASE_PATH / rwFileName;
8690166c15SMarri Devender Rao     if (!fs::exists(roFilePath))
8790166c15SMarri Devender Rao     {
88e0dd7af4SJayanth Othayoth         auto err = std::format("RO device tree file ({}) does not "
8990166c15SMarri Devender Rao                                "exit ",
9090166c15SMarri Devender Rao                                roFilePath.string());
9190166c15SMarri Devender Rao         log<level::ERR>(err.c_str());
9290166c15SMarri Devender Rao         throw std::runtime_error(err);
9390166c15SMarri Devender Rao     }
9490166c15SMarri Devender Rao     return roFilePath;
9590166c15SMarri Devender Rao }
9690166c15SMarri Devender Rao 
9790166c15SMarri Devender Rao /**
9894fc70cbSJayanth Othayoth  * @brief reinitialize the devtree attributes.
9994fc70cbSJayanth Othayoth  * In the regular host boot path devtree attribute need to
10094fc70cbSJayanth Othayoth  * initialize the default data and also some of the selected
10194fc70cbSJayanth Othayoth  * attributes need to preserve with previous boot value.
10294fc70cbSJayanth Othayoth  * Preserve attribute list is available BMC pre-defined location.
10394fc70cbSJayanth Othayoth  * This function helps to meet the host ipl requirement
10494fc70cbSJayanth Othayoth  * related to attribute persistency management for host ipl.
10594fc70cbSJayanth Othayoth  * Steps involved
10694fc70cbSJayanth Othayoth  * 1. Create attribute data file from devtree r/w version based on
10794fc70cbSJayanth Othayoth  *    the reinit attribute list file bmc /usr/share/pdata path.
10894fc70cbSJayanth Othayoth  * 2. Create temporary devtree file by copying devtree r/o file
10994fc70cbSJayanth Othayoth  * 3. Override temporary copy of devtree with attribute data file
11094fc70cbSJayanth Othayoth  *    from step 1.
1115409e877SJayanth Othayoth  * 3a. Apply user provided attribute override if present in the
1125409e877SJayanth Othayoth  *     predefined location.
11394fc70cbSJayanth Othayoth  * 4. Copy  temporary copy devtree to r/w devtree version file.
11494fc70cbSJayanth Othayoth  */
11594fc70cbSJayanth Othayoth 
reinitDevtree()11694fc70cbSJayanth Othayoth void reinitDevtree()
11794fc70cbSJayanth Othayoth {
11894fc70cbSJayanth Othayoth     using json = nlohmann::json;
1194d5b5bfeSMarri Devender Rao     using Severity =
1204d5b5bfeSMarri Devender Rao         sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level;
12194fc70cbSJayanth Othayoth 
12294fc70cbSJayanth Othayoth     log<level::INFO>("reinitDevtree: started");
12394fc70cbSJayanth Othayoth 
12494fc70cbSJayanth Othayoth     // All the file operations is done on temporary copy
12594fc70cbSJayanth Othayoth     // This is to avoid any file corruption issue during
12694fc70cbSJayanth Othayoth     // copy or attribute import path.
12794fc70cbSJayanth Othayoth     openpower::util::TemporaryFile tmpDevtreeFile{};
12894fc70cbSJayanth Othayoth     const auto copyOptions = std::filesystem::copy_options::overwrite_existing;
12994fc70cbSJayanth Othayoth     auto tmpDevtreePath = tmpDevtreeFile.getPath();
13094fc70cbSJayanth Othayoth     bool tmpReinitDone = false;
13194fc70cbSJayanth Othayoth     // To store callouts details in json format as per pel expectation.
13294fc70cbSJayanth Othayoth     json jsonCalloutDataList;
13394fc70cbSJayanth Othayoth     jsonCalloutDataList = json::array();
13494fc70cbSJayanth Othayoth 
13594fc70cbSJayanth Othayoth     try
13694fc70cbSJayanth Othayoth     {
13794fc70cbSJayanth Othayoth         // Check devtree reinit attributes list file is present
13894fc70cbSJayanth Othayoth         auto attrFile = fs::path(DEVTREE_REINIT_ATTRS_LIST);
13994fc70cbSJayanth Othayoth         if (!fs::exists(attrFile))
14094fc70cbSJayanth Othayoth         {
14194fc70cbSJayanth Othayoth             log<level::ERR>(
142e0dd7af4SJayanth Othayoth                 std::format(
14394fc70cbSJayanth Othayoth                     "devtree attribute export list file is not available: ({})",
14494fc70cbSJayanth Othayoth                     DEVTREE_REINIT_ATTRS_LIST)
14594fc70cbSJayanth Othayoth                     .c_str());
14694fc70cbSJayanth Othayoth             throw std::runtime_error("reinitDevtree: missing export list file");
14794fc70cbSJayanth Othayoth         }
14894fc70cbSJayanth Othayoth 
14994fc70cbSJayanth Othayoth         // create temporary data file to store the devtree export data
15094fc70cbSJayanth Othayoth         openpower::util::TemporaryFile tmpFile{};
15194fc70cbSJayanth Othayoth 
15294fc70cbSJayanth Othayoth         {
15394fc70cbSJayanth Othayoth             // get temporary datafile pointer.
154773fd244SJayanth Othayoth             FILE_Ptr fpExport(fopen(tmpFile.getPath().c_str(), "w+"),
155773fd244SJayanth Othayoth                               FileCloser());
15694fc70cbSJayanth Othayoth 
15794fc70cbSJayanth Othayoth             if (fpExport.get() == nullptr)
15894fc70cbSJayanth Othayoth             {
15994fc70cbSJayanth Othayoth                 log<level::ERR>(
160e0dd7af4SJayanth Othayoth                     std::format("Temporary data file failed to open: ({})",
16194fc70cbSJayanth Othayoth                                 tmpFile.getPath().c_str())
16294fc70cbSJayanth Othayoth                         .c_str());
16394fc70cbSJayanth Othayoth                 throw std::runtime_error(
16494fc70cbSJayanth Othayoth                     "reinitDevtree: failed to open temporaray data file");
16594fc70cbSJayanth Othayoth             }
16694fc70cbSJayanth Othayoth 
16794fc70cbSJayanth Othayoth             // Step 1: export devtree data based on the reinit attribute list.
168*1e43be06SPatrick Williams             auto ret =
169*1e43be06SPatrick Williams                 dtree_cronus_export(CEC_DEVTREE_RW_PATH, CEC_INFODB_PATH,
170*1e43be06SPatrick Williams                                     DEVTREE_REINIT_ATTRS_LIST, fpExport.get());
17194fc70cbSJayanth Othayoth             if (ret)
17294fc70cbSJayanth Othayoth             {
17394fc70cbSJayanth Othayoth                 log<level::ERR>(
174e0dd7af4SJayanth Othayoth                     std::format("Failed({}) to collect attribute export data",
17594fc70cbSJayanth Othayoth                                 ret)
17694fc70cbSJayanth Othayoth                         .c_str());
17794fc70cbSJayanth Othayoth                 throw std::runtime_error(
17894fc70cbSJayanth Othayoth                     "reinitDevtree: dtree_cronus_export function failed");
17994fc70cbSJayanth Othayoth             }
18094fc70cbSJayanth Othayoth         }
18194fc70cbSJayanth Othayoth 
18294fc70cbSJayanth Othayoth         // Step 2: Create temporary devtree file by copying devtree r/o version
18390166c15SMarri Devender Rao         fs::path roFilePath = computeRODeviceTreePath();
18490166c15SMarri Devender Rao         std::filesystem::copy(roFilePath, tmpDevtreePath, copyOptions);
18594fc70cbSJayanth Othayoth 
18694fc70cbSJayanth Othayoth         // get r/o version data file pointer
187773fd244SJayanth Othayoth         FILE_Ptr fpImport(fopen(tmpFile.getPath().c_str(), "r"), FileCloser());
18894fc70cbSJayanth Othayoth         if (fpImport.get() == nullptr)
18994fc70cbSJayanth Othayoth         {
19094fc70cbSJayanth Othayoth             log<level::ERR>(
191e0dd7af4SJayanth Othayoth                 std::format("import, temporary data file failed to open: ({})",
19294fc70cbSJayanth Othayoth                             tmpFile.getPath().c_str())
19394fc70cbSJayanth Othayoth                     .c_str());
19494fc70cbSJayanth Othayoth             throw std::runtime_error(
19594fc70cbSJayanth Othayoth                 "reinitDevtree: import, failed to open temporaray data file");
19694fc70cbSJayanth Othayoth         }
19794fc70cbSJayanth Othayoth 
19894fc70cbSJayanth Othayoth         // Step 3: Update Devtree r/w version with data file attribute data.
19994fc70cbSJayanth Othayoth         auto ret = dtree_cronus_import(tmpDevtreePath.c_str(), CEC_INFODB_PATH,
20094fc70cbSJayanth Othayoth                                        fpImport.get());
20194fc70cbSJayanth Othayoth         if (ret)
20294fc70cbSJayanth Othayoth         {
20394fc70cbSJayanth Othayoth             log<level::ERR>(
204e0dd7af4SJayanth Othayoth                 std::format("Failed({}) to update attribute data", ret)
20594fc70cbSJayanth Othayoth                     .c_str());
20694fc70cbSJayanth Othayoth             throw std::runtime_error(
20794fc70cbSJayanth Othayoth                 "reinitDevtree: dtree_cronus_import function failed");
20894fc70cbSJayanth Othayoth         }
2095409e877SJayanth Othayoth         // Step 3.a: Apply user provided attribute override data if present.
2105409e877SJayanth Othayoth         applyAttrOverride(tmpDevtreePath);
2115409e877SJayanth Othayoth 
21294fc70cbSJayanth Othayoth         // Temporary file reinit is success.
21394fc70cbSJayanth Othayoth         tmpReinitDone = true;
21494fc70cbSJayanth Othayoth     }
21594fc70cbSJayanth Othayoth     catch (const std::exception& e)
21694fc70cbSJayanth Othayoth     {
21794fc70cbSJayanth Othayoth         // Any failures during temporary file re-init should create PEL
21894fc70cbSJayanth Othayoth         // and continue with current version of devtree file to allow boot.
21994fc70cbSJayanth Othayoth         log<level::ERR>(
220e0dd7af4SJayanth Othayoth             std::format("reinitDevtree failed ({})", e.what()).c_str());
22194fc70cbSJayanth Othayoth         json jsonCalloutDataList;
22294fc70cbSJayanth Othayoth         jsonCalloutDataList = json::array();
22394fc70cbSJayanth Othayoth         json jsonCalloutData;
22494fc70cbSJayanth Othayoth         jsonCalloutData["Procedure"] = "BMC0001";
22594fc70cbSJayanth Othayoth         jsonCalloutData["Priority"] = "M";
22694fc70cbSJayanth Othayoth         jsonCalloutDataList.emplace_back(jsonCalloutData);
22794fc70cbSJayanth Othayoth         openpower::pel::createErrorPEL(
2284d5b5bfeSMarri Devender Rao             "org.open_power.PHAL.Error.devtreeReinit", jsonCalloutDataList, {},
2294d5b5bfeSMarri Devender Rao             Severity::Error);
23094fc70cbSJayanth Othayoth     }
23194fc70cbSJayanth Othayoth 
23294fc70cbSJayanth Othayoth     // Step 4: Update devtree r/w file
23394fc70cbSJayanth Othayoth     try
23494fc70cbSJayanth Othayoth     {
23594fc70cbSJayanth Othayoth         if (tmpReinitDone)
23694fc70cbSJayanth Othayoth         {
23794fc70cbSJayanth Othayoth             // Step 4: Copy temporary version devtree file r/w version file.
23894fc70cbSJayanth Othayoth             // Any copy failures should results service failure.
23994fc70cbSJayanth Othayoth             std::filesystem::copy(tmpDevtreePath, CEC_DEVTREE_RW_PATH,
24094fc70cbSJayanth Othayoth                                   copyOptions);
24194fc70cbSJayanth Othayoth             log<level::INFO>("reinitDevtree: completed successfully");
24294fc70cbSJayanth Othayoth         }
24394fc70cbSJayanth Othayoth         else
24494fc70cbSJayanth Othayoth         {
24594fc70cbSJayanth Othayoth             // Attempt boot with genesis mode attribute data.
24690166c15SMarri Devender Rao             fs::path roFilePath = computeRODeviceTreePath();
24794fc70cbSJayanth Othayoth             log<level::WARNING>("reinitDevtree: DEVTREE(r/w) initilizing with "
24894fc70cbSJayanth Othayoth                                 "genesis mode attribute data");
24990166c15SMarri Devender Rao             std::filesystem::copy(roFilePath, CEC_DEVTREE_RW_PATH, copyOptions);
25094fc70cbSJayanth Othayoth         }
25194fc70cbSJayanth Othayoth     }
25294fc70cbSJayanth Othayoth     catch (const std::exception& e)
25394fc70cbSJayanth Othayoth     {
25494fc70cbSJayanth Othayoth         // Any failures during update on r/w file is serious error should create
25594fc70cbSJayanth Othayoth         // PEL, with code callout. Also failed the service.
25694fc70cbSJayanth Othayoth         // and continue with current version of devtree file to allow boot.
25794fc70cbSJayanth Othayoth         log<level::ERR>(
258e0dd7af4SJayanth Othayoth             std::format("reinitDevtree r/w version update failed ({})",
25994fc70cbSJayanth Othayoth                         e.what())
26094fc70cbSJayanth Othayoth                 .c_str());
26194fc70cbSJayanth Othayoth         json jsonCalloutDataList;
26294fc70cbSJayanth Othayoth         jsonCalloutDataList = json::array();
26394fc70cbSJayanth Othayoth         json jsonCalloutData;
26494fc70cbSJayanth Othayoth         jsonCalloutData["Procedure"] = "BMC0001";
26594fc70cbSJayanth Othayoth         jsonCalloutData["Priority"] = "H";
26694fc70cbSJayanth Othayoth         jsonCalloutDataList.emplace_back(jsonCalloutData);
26794fc70cbSJayanth Othayoth         openpower::pel::createErrorPEL(
2684d5b5bfeSMarri Devender Rao             "org.open_power.PHAL.Error.devtreeReinit", jsonCalloutDataList, {},
2694d5b5bfeSMarri Devender Rao             Severity::Error);
27094fc70cbSJayanth Othayoth         throw;
27194fc70cbSJayanth Othayoth     }
27294fc70cbSJayanth Othayoth }
27394fc70cbSJayanth Othayoth 
27494fc70cbSJayanth Othayoth REGISTER_PROCEDURE("reinitDevtree", reinitDevtree)
27594fc70cbSJayanth Othayoth 
27694fc70cbSJayanth Othayoth } // namespace phal
27794fc70cbSJayanth Othayoth } // namespace openpower
278