1 #include "config.h"
2 
3 #include "item_updater_static.hpp"
4 
5 #include "activation.hpp"
6 #include "version.hpp"
7 
8 #include <cstring>
9 #include <filesystem>
10 #include <fstream>
11 #include <phosphor-logging/elog-errors.hpp>
12 #include <phosphor-logging/log.hpp>
13 #include <string>
14 
15 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
16 using namespace phosphor::logging;
17 
18 // When you see server:: you know we're referencing our base class
19 namespace server = sdbusplus::xyz::openbmc_project::Software::server;
20 namespace fs = std::filesystem;
21 
22 namespace utils
23 {
24 
25 template <typename... Ts>
26 std::string concat_string(Ts const&... ts)
27 {
28     std::stringstream s;
29     ((s << ts << " "), ...) << std::endl;
30     return s.str();
31 }
32 
33 // Helper function to run pflash command
34 template <typename... Ts>
35 std::string pflash(Ts const&... ts)
36 {
37     std::array<char, 512> buffer;
38     std::string cmd = concat_string("pflash", ts...);
39     std::stringstream result;
40     std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"),
41                                                   pclose);
42     if (!pipe)
43     {
44         throw std::runtime_error("popen() failed!");
45     }
46     while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr)
47     {
48         result << buffer.data();
49     }
50     return result.str();
51 }
52 
53 std::string getPNORVersion()
54 {
55     // A signed version partition will have an extra 4K header starting with
56     // the magic number 17082011 in big endian:
57     // https://github.com/open-power/skiboot/blob/master/libstb/container.h#L47
58 
59     constexpr uint8_t MAGIC[] = {0x17, 0x08, 0x20, 0x11};
60     constexpr auto MAGIC_SIZE = sizeof(MAGIC);
61     static_assert(MAGIC_SIZE == 4);
62 
63     auto tmp = fs::temp_directory_path();
64     std::string tmpDir(tmp / "versionXXXXXX");
65     if (!mkdtemp(tmpDir.data()))
66     {
67         log<level::ERR>("Failed to create temp dir");
68         return {};
69     }
70 
71     fs::path versionFile = tmpDir;
72     versionFile /= "version";
73 
74     pflash("-P VERSION -r", versionFile.string(), "2>&1 > /dev/null");
75     std::ifstream f(versionFile.c_str(), std::ios::in | std::ios::binary);
76     uint8_t magic[MAGIC_SIZE];
77     std::string version;
78 
79     f.read(reinterpret_cast<char*>(magic), MAGIC_SIZE);
80     f.seekg(0, std::ios::beg);
81     if (std::memcmp(magic, MAGIC, MAGIC_SIZE) == 0)
82     {
83         // Skip the first 4K header
84         f.ignore(4096);
85     }
86 
87     getline(f, version, '\0');
88     f.close();
89 
90     // Clear the temp dir
91     std::error_code ec;
92     fs::remove_all(tmpDir, ec);
93     if (ec)
94     {
95         log<level::ERR>("Failed to remove temp dir",
96                         entry("DIR=%s", tmpDir.c_str()),
97                         entry("ERR=%s", ec.message().c_str()));
98     }
99 
100     return version;
101 }
102 
103 } // namespace utils
104 
105 namespace openpower
106 {
107 namespace software
108 {
109 namespace updater
110 {
111 std::unique_ptr<Activation> ItemUpdaterStatic::createActivationObject(
112     const std::string& path, const std::string& versionId,
113     const std::string& extVersion,
114     sdbusplus::xyz::openbmc_project::Software::server::Activation::Activations
115         activationStatus,
116     AssociationList& assocs)
117 {
118     return {};
119 }
120 
121 std::unique_ptr<Version> ItemUpdaterStatic::createVersionObject(
122     const std::string& objPath, const std::string& versionId,
123     const std::string& versionString,
124     sdbusplus::xyz::openbmc_project::Software::server::Version::VersionPurpose
125         versionPurpose,
126     const std::string& filePath)
127 {
128     return {};
129 }
130 
131 bool ItemUpdaterStatic::validateImage(const std::string& path)
132 {
133     return true;
134 }
135 
136 void ItemUpdaterStatic::processPNORImage()
137 {
138     auto fullVersion = utils::getPNORVersion();
139 
140     const auto& [version, extendedVersion] = Version::getVersions(fullVersion);
141     auto id = Version::getId(version);
142 
143     auto activationState = server::Activation::Activations::Active;
144     if (version.empty())
145     {
146         log<level::ERR>("Failed to read version",
147                         entry("VERSION=%s", fullVersion.c_str()));
148         activationState = server::Activation::Activations::Invalid;
149     }
150 
151     if (extendedVersion.empty())
152     {
153         log<level::ERR>("Failed to read extendedVersion",
154                         entry("VERSION=%s", fullVersion.c_str()));
155         activationState = server::Activation::Activations::Invalid;
156     }
157 
158     auto purpose = server::Version::VersionPurpose::Host;
159     auto path = fs::path(SOFTWARE_OBJPATH) / id;
160     AssociationList associations = {};
161 
162     if (activationState == server::Activation::Activations::Active)
163     {
164         // Create an association to the host inventory item
165         associations.emplace_back(std::make_tuple(ACTIVATION_FWD_ASSOCIATION,
166                                                   ACTIVATION_REV_ASSOCIATION,
167                                                   HOST_INVENTORY_PATH));
168 
169         // Create an active association since this image is active
170         createActiveAssociation(path);
171     }
172 
173     // Create Version instance for this version.
174     auto versionPtr = std::make_unique<Version>(
175         bus, path, *this, id, version, purpose, "",
176         std::bind(&ItemUpdaterStatic::erase, this, std::placeholders::_1));
177     versionPtr->deleteObject = std::make_unique<Delete>(bus, path, *versionPtr);
178     versions.insert(std::make_pair(id, std::move(versionPtr)));
179 
180     if (!id.empty())
181     {
182         updateFunctionalAssociation(id);
183     }
184 }
185 
186 void ItemUpdaterStatic::reset()
187 {
188 }
189 
190 bool ItemUpdaterStatic::isVersionFunctional(const std::string& versionId)
191 {
192     return true;
193 }
194 
195 void ItemUpdaterStatic::freePriority(uint8_t value,
196                                      const std::string& versionId)
197 {
198 }
199 
200 void ItemUpdaterStatic::deleteAll()
201 {
202 }
203 
204 void ItemUpdaterStatic::freeSpace()
205 {
206 }
207 
208 void GardReset::reset()
209 {
210 }
211 
212 } // namespace updater
213 } // namespace software
214 } // namespace openpower
215