135e83f3eSSaqib Khan #include <fstream>
2ec1b41c4SGunnar Mills #include <string>
32ce7da29SGunnar Mills #include <phosphor-logging/log.hpp>
4ec1b41c4SGunnar Mills #include "config.h"
52ce7da29SGunnar Mills #include "item_updater.hpp"
62ce7da29SGunnar Mills #include "xyz/openbmc_project/Software/Version/server.hpp"
735e83f3eSSaqib Khan #include <experimental/filesystem>
8705f1bfcSSaqib Khan #include "version.hpp"
95d532675SSaqib Khan #include "serialize.hpp"
10ec1b41c4SGunnar Mills 
11ec1b41c4SGunnar Mills namespace phosphor
12ec1b41c4SGunnar Mills {
13ec1b41c4SGunnar Mills namespace software
14ec1b41c4SGunnar Mills {
15ec1b41c4SGunnar Mills namespace updater
16ec1b41c4SGunnar Mills {
17ec1b41c4SGunnar Mills 
182ce7da29SGunnar Mills // When you see server:: you know we're referencing our base class
192ce7da29SGunnar Mills namespace server = sdbusplus::xyz::openbmc_project::Software::server;
200129d926SMichael Tritz namespace control = sdbusplus::xyz::openbmc_project::Control::server;
212ce7da29SGunnar Mills 
222ce7da29SGunnar Mills using namespace phosphor::logging;
2335e83f3eSSaqib Khan namespace fs = std::experimental::filesystem;
2435e83f3eSSaqib Khan 
25b1cfdf99SMichael Tritz const std::vector<std::string> bmcImages = { "image-kernel",
26b1cfdf99SMichael Tritz                                              "image-rofs",
27b1cfdf99SMichael Tritz                                              "image-rwfs",
28b1cfdf99SMichael Tritz                                              "image-u-boot" };
292ce7da29SGunnar Mills 
30e75d10f5SPatrick Williams void ItemUpdater::createActivation(sdbusplus::message::message& msg)
31ec1b41c4SGunnar Mills {
3284a0e693SSaqib Khan 
3384a0e693SSaqib Khan     using SVersion = server::Version;
3484a0e693SSaqib Khan     using VersionPurpose = SVersion::VersionPurpose;
35*9a782243SGunnar Mills     using VersionClass = phosphor::software::manager::Version;
3684a0e693SSaqib Khan     namespace mesg = sdbusplus::message;
3784a0e693SSaqib Khan     namespace variant_ns = mesg::variant_ns;
3884a0e693SSaqib Khan 
3984a0e693SSaqib Khan     mesg::object_path objPath;
4084a0e693SSaqib Khan     auto purpose = VersionPurpose::Unknown;
41705f1bfcSSaqib Khan     std::string version;
422ce7da29SGunnar Mills     std::map<std::string,
432ce7da29SGunnar Mills              std::map<std::string,
4484a0e693SSaqib Khan                       mesg::variant<std::string>>> interfaces;
45e75d10f5SPatrick Williams     msg.read(objPath, interfaces);
462ce7da29SGunnar Mills     std::string path(std::move(objPath));
4719177d3eSSaqib Khan     std::string filePath;
482ce7da29SGunnar Mills 
492ce7da29SGunnar Mills     for (const auto& intf : interfaces)
502ce7da29SGunnar Mills     {
51705f1bfcSSaqib Khan         if (intf.first == VERSION_IFACE)
522ce7da29SGunnar Mills         {
532ce7da29SGunnar Mills             for (const auto& property : intf.second)
542ce7da29SGunnar Mills             {
55705f1bfcSSaqib Khan                 if (property.first == "Purpose")
562ce7da29SGunnar Mills                 {
5784a0e693SSaqib Khan                     auto value = SVersion::convertVersionPurposeFromString(
5884a0e693SSaqib Khan                             variant_ns::get<std::string>(property.second));
5984a0e693SSaqib Khan                     if (value == VersionPurpose::BMC ||
6084a0e693SSaqib Khan                         value == VersionPurpose::System)
6184a0e693SSaqib Khan                     {
6284a0e693SSaqib Khan                         purpose = value;
6384a0e693SSaqib Khan                     }
64705f1bfcSSaqib Khan                 }
65705f1bfcSSaqib Khan                 else if (property.first == "Version")
66705f1bfcSSaqib Khan                 {
6784a0e693SSaqib Khan                     version = variant_ns::get<std::string>(property.second);
68705f1bfcSSaqib Khan                 }
69705f1bfcSSaqib Khan             }
70705f1bfcSSaqib Khan         }
7119177d3eSSaqib Khan         else if (intf.first == FILEPATH_IFACE)
7219177d3eSSaqib Khan         {
7319177d3eSSaqib Khan             for (const auto& property : intf.second)
7419177d3eSSaqib Khan             {
7519177d3eSSaqib Khan                 if (property.first == "Path")
7619177d3eSSaqib Khan                 {
7784a0e693SSaqib Khan                     filePath = variant_ns::get<std::string>(property.second);
7819177d3eSSaqib Khan                 }
7919177d3eSSaqib Khan             }
8019177d3eSSaqib Khan         }
81705f1bfcSSaqib Khan     }
82705f1bfcSSaqib Khan     if (version.empty() ||
8319177d3eSSaqib Khan         filePath.empty() ||
8484a0e693SSaqib Khan         purpose == VersionPurpose::Unknown)
852ce7da29SGunnar Mills     {
86e75d10f5SPatrick Williams         return;
872ce7da29SGunnar Mills     }
882ce7da29SGunnar Mills 
892ce7da29SGunnar Mills     // Version id is the last item in the path
902ce7da29SGunnar Mills     auto pos = path.rfind("/");
912ce7da29SGunnar Mills     if (pos == std::string::npos)
922ce7da29SGunnar Mills     {
932ce7da29SGunnar Mills         log<level::ERR>("No version id found in object path",
942ce7da29SGunnar Mills                         entry("OBJPATH=%s", path));
95e75d10f5SPatrick Williams         return;
962ce7da29SGunnar Mills     }
972ce7da29SGunnar Mills 
982ce7da29SGunnar Mills     auto versionId = path.substr(pos + 1);
992ce7da29SGunnar Mills 
100e75d10f5SPatrick Williams     if (activations.find(versionId) == activations.end())
1012ce7da29SGunnar Mills     {
10235e83f3eSSaqib Khan         // Determine the Activation state by processing the given image dir.
10335e83f3eSSaqib Khan         auto activationState = server::Activation::Activations::Invalid;
104*9a782243SGunnar Mills         ItemUpdater::ActivationStatus result =
105*9a782243SGunnar Mills                 ItemUpdater::validateSquashFSImage(filePath);
10635e83f3eSSaqib Khan         if (result == ItemUpdater::ActivationStatus::ready)
10735e83f3eSSaqib Khan         {
10835e83f3eSSaqib Khan             activationState = server::Activation::Activations::Ready;
10935e83f3eSSaqib Khan         }
11035e83f3eSSaqib Khan         activations.insert(std::make_pair(
1112ce7da29SGunnar Mills                                versionId,
112ec1b41c4SGunnar Mills                                std::make_unique<Activation>(
11335e83f3eSSaqib Khan                                         bus,
11435e83f3eSSaqib Khan                                         path,
1154c1aec09SSaqib Khan                                         *this,
11635e83f3eSSaqib Khan                                         versionId,
11735e83f3eSSaqib Khan                                         activationState)));
118705f1bfcSSaqib Khan         versions.insert(std::make_pair(
119705f1bfcSSaqib Khan                             versionId,
120*9a782243SGunnar Mills                             std::make_unique<VersionClass>(
121705f1bfcSSaqib Khan                                 bus,
122705f1bfcSSaqib Khan                                 path,
123705f1bfcSSaqib Khan                                 version,
124705f1bfcSSaqib Khan                                 purpose,
1253526ef73SLeonel Gonzalez                                 filePath,
1263526ef73SLeonel Gonzalez                                 std::bind(&ItemUpdater::erase,
1273526ef73SLeonel Gonzalez                                           this,
1283526ef73SLeonel Gonzalez                                           std::placeholders::_1))));
1292ce7da29SGunnar Mills     }
1307b5010f2SSaqib Khan     else
1317b5010f2SSaqib Khan     {
1327b5010f2SSaqib Khan         log<level::INFO>("Software Object with the same version already exists",
1337b5010f2SSaqib Khan                          entry("VERSION_ID=%s", versionId));
1347b5010f2SSaqib Khan     }
135e75d10f5SPatrick Williams     return;
136ec1b41c4SGunnar Mills }
137ec1b41c4SGunnar Mills 
138ba239881SSaqib Khan void ItemUpdater::processBMCImage()
139ba239881SSaqib Khan {
140*9a782243SGunnar Mills     using VersionClass = phosphor::software::manager::Version;
141ba239881SSaqib Khan     auto purpose = server::Version::VersionPurpose::BMC;
142ba239881SSaqib Khan     auto version = phosphor::software::manager::Version::getBMCVersion();
143ba239881SSaqib Khan     auto id = phosphor::software::manager::Version::getId(version);
144ba239881SSaqib Khan     auto path =  std::string{SOFTWARE_OBJPATH} + '/' + id;
145ba239881SSaqib Khan     activations.insert(std::make_pair(
146ba239881SSaqib Khan                            id,
147ba239881SSaqib Khan                            std::make_unique<Activation>(
148ba239881SSaqib Khan                                bus,
149ba239881SSaqib Khan                                path,
1504c1aec09SSaqib Khan                                *this,
151ba239881SSaqib Khan                                id,
152ba239881SSaqib Khan                                server::Activation::Activations::Active)));
153ba239881SSaqib Khan     versions.insert(std::make_pair(
154ba239881SSaqib Khan                         id,
155*9a782243SGunnar Mills                         std::make_unique<VersionClass>(
156ba239881SSaqib Khan                              bus,
157ba239881SSaqib Khan                              path,
158ba239881SSaqib Khan                              version,
159ba239881SSaqib Khan                              purpose,
1603526ef73SLeonel Gonzalez                              "",
1613526ef73SLeonel Gonzalez                              std::bind(&ItemUpdater::erase,
1623526ef73SLeonel Gonzalez                                        this,
1633526ef73SLeonel Gonzalez                                        std::placeholders::_1))));
1643526ef73SLeonel Gonzalez 
165ba239881SSaqib Khan     return;
166ba239881SSaqib Khan }
167ba239881SSaqib Khan 
1683526ef73SLeonel Gonzalez void ItemUpdater::erase(std::string entryId)
1693526ef73SLeonel Gonzalez {
1700c2eb262SSaqib Khan     // Delete ReadOnly partitions
1713526ef73SLeonel Gonzalez     removeReadOnlyPartition(entryId);
1723526ef73SLeonel Gonzalez 
1733526ef73SLeonel Gonzalez     // Removing entry in versions map
1743526ef73SLeonel Gonzalez     auto it = versions.find(entryId);
1753526ef73SLeonel Gonzalez     if (it == versions.end())
1763526ef73SLeonel Gonzalez     {
1773526ef73SLeonel Gonzalez         log<level::ERR>(("Error: Failed to find version " + entryId + \
1783526ef73SLeonel Gonzalez                          " in item updater versions map." \
1793526ef73SLeonel Gonzalez                          " Unable to remove.").c_str());
1803526ef73SLeonel Gonzalez         return;
1813526ef73SLeonel Gonzalez     }
1823526ef73SLeonel Gonzalez     this->versions.erase(entryId);
1833526ef73SLeonel Gonzalez 
1843526ef73SLeonel Gonzalez     // Removing entry in activations map
1853526ef73SLeonel Gonzalez     auto ita = activations.find(entryId);
1863526ef73SLeonel Gonzalez     if (ita == activations.end())
1873526ef73SLeonel Gonzalez     {
1883526ef73SLeonel Gonzalez         log<level::ERR>(("Error: Failed to find version " + entryId + \
1893526ef73SLeonel Gonzalez                          " in item updater activations map." \
1903526ef73SLeonel Gonzalez                          " Unable to remove.").c_str());
1913526ef73SLeonel Gonzalez         return;
1923526ef73SLeonel Gonzalez     }
1933526ef73SLeonel Gonzalez     // TODO: openbmc/openbmc#1986
1943526ef73SLeonel Gonzalez     //       Test if this is the currently running image
1953526ef73SLeonel Gonzalez     //       If not, don't continue.
1963526ef73SLeonel Gonzalez 
1973526ef73SLeonel Gonzalez     this->activations.erase(entryId);
1985d532675SSaqib Khan     removeFile(entryId);
1993526ef73SLeonel Gonzalez }
2003526ef73SLeonel Gonzalez 
20135e83f3eSSaqib Khan ItemUpdater::ActivationStatus ItemUpdater::validateSquashFSImage(
20219177d3eSSaqib Khan         const std::string& filePath)
20335e83f3eSSaqib Khan {
204b1cfdf99SMichael Tritz     bool invalid = false;
20535e83f3eSSaqib Khan 
206b1cfdf99SMichael Tritz     for (auto& bmcImage : bmcImages)
207b1cfdf99SMichael Tritz     {
20819177d3eSSaqib Khan         fs::path file(filePath);
20935e83f3eSSaqib Khan         file /= bmcImage;
21035e83f3eSSaqib Khan         std::ifstream efile(file.c_str());
211b1cfdf99SMichael Tritz         if (efile.good() != 1)
21235e83f3eSSaqib Khan         {
213b1cfdf99SMichael Tritz             log<level::ERR>("Failed to find the BMC image.",
214b1cfdf99SMichael Tritz                             entry("IMAGE=%s", bmcImage.c_str()));
215b1cfdf99SMichael Tritz             invalid = true;
21635e83f3eSSaqib Khan         }
217b1cfdf99SMichael Tritz     }
218b1cfdf99SMichael Tritz 
219b1cfdf99SMichael Tritz     if (invalid)
22035e83f3eSSaqib Khan     {
22135e83f3eSSaqib Khan         return ItemUpdater::ActivationStatus::invalid;
22235e83f3eSSaqib Khan     }
223b1cfdf99SMichael Tritz 
224b1cfdf99SMichael Tritz     return ItemUpdater::ActivationStatus::ready;
22535e83f3eSSaqib Khan }
22635e83f3eSSaqib Khan 
2274c1aec09SSaqib Khan void ItemUpdater::freePriority(uint8_t value)
2284c1aec09SSaqib Khan {
2294c1aec09SSaqib Khan     //TODO openbmc/openbmc#1896 Improve the performance of this function
2304c1aec09SSaqib Khan     for (const auto& intf : activations)
2314c1aec09SSaqib Khan     {
2324c1aec09SSaqib Khan         if (intf.second->redundancyPriority)
2334c1aec09SSaqib Khan         {
2344c1aec09SSaqib Khan             if (intf.second->redundancyPriority.get()->priority() == value)
2354c1aec09SSaqib Khan             {
2364c1aec09SSaqib Khan                 intf.second->redundancyPriority.get()->priority(value + 1);
2374c1aec09SSaqib Khan             }
2384c1aec09SSaqib Khan         }
2394c1aec09SSaqib Khan     }
2404c1aec09SSaqib Khan }
2414c1aec09SSaqib Khan 
24237a59043SMichael Tritz void ItemUpdater::reset()
24337a59043SMichael Tritz {
24437a59043SMichael Tritz     // Mark the read-write partition for recreation upon reboot.
24537a59043SMichael Tritz     auto method = bus.new_method_call(
24637a59043SMichael Tritz             SYSTEMD_BUSNAME,
24737a59043SMichael Tritz             SYSTEMD_PATH,
24837a59043SMichael Tritz             SYSTEMD_INTERFACE,
24937a59043SMichael Tritz             "StartUnit");
2500129d926SMichael Tritz     method.append("obmc-flash-bmc-setenv@rwreset\\x3dtrue.service", "replace");
25137a59043SMichael Tritz     bus.call_noreply(method);
25237a59043SMichael Tritz 
25337a59043SMichael Tritz     log<level::INFO>("BMC factory reset will take effect upon reboot.");
25437a59043SMichael Tritz 
25537a59043SMichael Tritz     return;
25637a59043SMichael Tritz }
25737a59043SMichael Tritz 
2583526ef73SLeonel Gonzalez void ItemUpdater::removeReadOnlyPartition(std::string versionId)
2593526ef73SLeonel Gonzalez {
2603526ef73SLeonel Gonzalez     auto serviceFile = "obmc-flash-bmc-ubiro-remove@" + versionId +
2613526ef73SLeonel Gonzalez             ".service";
2623526ef73SLeonel Gonzalez 
2633526ef73SLeonel Gonzalez     // Remove the read-only partitions.
2643526ef73SLeonel Gonzalez     auto method = bus.new_method_call(
2653526ef73SLeonel Gonzalez             SYSTEMD_BUSNAME,
2663526ef73SLeonel Gonzalez             SYSTEMD_PATH,
2673526ef73SLeonel Gonzalez             SYSTEMD_INTERFACE,
2683526ef73SLeonel Gonzalez             "StartUnit");
2693526ef73SLeonel Gonzalez     method.append(serviceFile, "replace");
2703526ef73SLeonel Gonzalez     bus.call_noreply(method);
2713526ef73SLeonel Gonzalez }
2723526ef73SLeonel Gonzalez 
2730129d926SMichael Tritz bool ItemUpdater::fieldModeEnabled(bool value)
2740129d926SMichael Tritz {
2750129d926SMichael Tritz     // enabling field mode is intended to be one way: false -> true
2760129d926SMichael Tritz     if (value && !control::FieldMode::fieldModeEnabled())
2770129d926SMichael Tritz     {
2780129d926SMichael Tritz         control::FieldMode::fieldModeEnabled(value);
2790129d926SMichael Tritz 
2800129d926SMichael Tritz         auto method = bus.new_method_call(
2810129d926SMichael Tritz                 SYSTEMD_BUSNAME,
2820129d926SMichael Tritz                 SYSTEMD_PATH,
2830129d926SMichael Tritz                 SYSTEMD_INTERFACE,
2840129d926SMichael Tritz                 "StartUnit");
2850129d926SMichael Tritz         method.append("obmc-flash-bmc-setenv@fieldmode\\x3dtrue.service",
2860129d926SMichael Tritz                       "replace");
2870129d926SMichael Tritz         bus.call_noreply(method);
2880129d926SMichael Tritz 
2890129d926SMichael Tritz         method = bus.new_method_call(
2900129d926SMichael Tritz                 SYSTEMD_BUSNAME,
2910129d926SMichael Tritz                 SYSTEMD_PATH,
2920129d926SMichael Tritz                 SYSTEMD_INTERFACE,
2930129d926SMichael Tritz                 "StopUnit");
2940129d926SMichael Tritz         method.append("usr-local.mount", "replace");
2950129d926SMichael Tritz         bus.call_noreply(method);
2960129d926SMichael Tritz 
2970129d926SMichael Tritz         std::vector<std::string> usrLocal = {"usr-local.mount"};
2980129d926SMichael Tritz 
2990129d926SMichael Tritz         method = bus.new_method_call(
3000129d926SMichael Tritz                 SYSTEMD_BUSNAME,
3010129d926SMichael Tritz                 SYSTEMD_PATH,
3020129d926SMichael Tritz                 SYSTEMD_INTERFACE,
3030129d926SMichael Tritz                 "MaskUnitFiles");
3040129d926SMichael Tritz         method.append(usrLocal, false, true);
3050129d926SMichael Tritz         bus.call_noreply(method);
3060129d926SMichael Tritz     }
3070129d926SMichael Tritz 
3080129d926SMichael Tritz     return control::FieldMode::fieldModeEnabled();
3090129d926SMichael Tritz }
3100129d926SMichael Tritz 
3110129d926SMichael Tritz void ItemUpdater::restoreFieldModeStatus()
3120129d926SMichael Tritz {
3130129d926SMichael Tritz     std::ifstream input("/run/fw_env");
3140129d926SMichael Tritz     std::string envVar;
3150129d926SMichael Tritz     std::getline(input, envVar);
3160129d926SMichael Tritz 
3170129d926SMichael Tritz     if (envVar.find("fieldmode=true") != std::string::npos)
3180129d926SMichael Tritz     {
3190129d926SMichael Tritz         ItemUpdater::fieldModeEnabled(true);
3200129d926SMichael Tritz     }
3210129d926SMichael Tritz }
3220129d926SMichael Tritz 
323ec1b41c4SGunnar Mills } // namespace updater
324ec1b41c4SGunnar Mills } // namespace software
325ec1b41c4SGunnar Mills } // namespace phosphor
326