xref: /openbmc/openpower-pnor-code-mgmt/static/item_updater_static.cpp (revision b53425d44b49fc494750d46a1fe4050ecd3db434)
1 #include "config.h"
2 
3 #include "item_updater_static.hpp"
4 
5 #include "activation_static.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 std::make_unique<ActivationStatic>(
119         bus, path, *this, versionId, extVersion, activationStatus, assocs);
120 }
121 
122 std::unique_ptr<Version> ItemUpdaterStatic::createVersionObject(
123     const std::string& objPath, const std::string& versionId,
124     const std::string& versionString,
125     sdbusplus::xyz::openbmc_project::Software::server::Version::VersionPurpose
126         versionPurpose,
127     const std::string& filePath)
128 {
129     auto version = std::make_unique<Version>(
130         bus, objPath, *this, versionId, versionString, versionPurpose, filePath,
131         std::bind(&ItemUpdaterStatic::erase, this, std::placeholders::_1));
132     version->deleteObject = std::make_unique<Delete>(bus, objPath, *version);
133     return version;
134 }
135 
136 bool ItemUpdaterStatic::validateImage(const std::string& path)
137 {
138     // There is no need to validate static layout pnor
139     return true;
140 }
141 
142 void ItemUpdaterStatic::processPNORImage()
143 {
144     auto fullVersion = utils::getPNORVersion();
145 
146     const auto& [version, extendedVersion] = Version::getVersions(fullVersion);
147     auto id = Version::getId(version);
148 
149     auto activationState = server::Activation::Activations::Active;
150     if (version.empty())
151     {
152         log<level::ERR>("Failed to read version",
153                         entry("VERSION=%s", fullVersion.c_str()));
154         activationState = server::Activation::Activations::Invalid;
155     }
156 
157     if (extendedVersion.empty())
158     {
159         log<level::ERR>("Failed to read extendedVersion",
160                         entry("VERSION=%s", fullVersion.c_str()));
161         activationState = server::Activation::Activations::Invalid;
162     }
163 
164     auto purpose = server::Version::VersionPurpose::Host;
165     auto path = fs::path(SOFTWARE_OBJPATH) / id;
166     AssociationList associations = {};
167 
168     if (activationState == server::Activation::Activations::Active)
169     {
170         // Create an association to the host inventory item
171         associations.emplace_back(std::make_tuple(ACTIVATION_FWD_ASSOCIATION,
172                                                   ACTIVATION_REV_ASSOCIATION,
173                                                   HOST_INVENTORY_PATH));
174 
175         // Create an active association since this image is active
176         createActiveAssociation(path);
177     }
178 
179     // Create Activation instance for this version.
180     activations.insert(std::make_pair(
181         id, std::make_unique<ActivationStatic>(bus, path, *this, id,
182                                                extendedVersion, activationState,
183                                                associations)));
184 
185     // If Active, create RedundancyPriority instance for this version.
186     if (activationState == server::Activation::Activations::Active)
187     {
188         // For now only one PNOR is supported with static layout
189         activations.find(id)->second->redundancyPriority =
190             std::make_unique<RedundancyPriority>(
191                 bus, path, *(activations.find(id)->second), 0);
192     }
193 
194     // Create Version instance for this version.
195     auto versionPtr = std::make_unique<Version>(
196         bus, path, *this, id, version, purpose, "",
197         std::bind(&ItemUpdaterStatic::erase, this, std::placeholders::_1));
198     versionPtr->deleteObject = std::make_unique<Delete>(bus, path, *versionPtr);
199     versions.insert(std::make_pair(id, std::move(versionPtr)));
200 
201     if (!id.empty())
202     {
203         updateFunctionalAssociation(id);
204     }
205 }
206 
207 void ItemUpdaterStatic::reset()
208 {
209 }
210 
211 bool ItemUpdaterStatic::isVersionFunctional(const std::string& versionId)
212 {
213     return true;
214 }
215 
216 void ItemUpdaterStatic::freePriority(uint8_t value,
217                                      const std::string& versionId)
218 {
219 }
220 
221 void ItemUpdaterStatic::deleteAll()
222 {
223 }
224 
225 void ItemUpdaterStatic::freeSpace()
226 {
227 }
228 
229 void GardReset::reset()
230 {
231 }
232 
233 } // namespace updater
234 } // namespace software
235 } // namespace openpower
236