blob: ea9b7b833cc6fdbb61106fcf05a6ac1e740dcd06 [file] [log] [blame]
Lei YU322f3f42019-02-21 16:10:41 +08001#include "config.h"
2
3#include "item_updater_static.hpp"
4
Lei YUb53425d2019-02-22 11:38:40 +08005#include "activation_static.hpp"
Lei YUe4994462019-03-14 14:41:53 +08006#include "utils.hpp"
Lei YUdec8cf92019-02-21 17:47:05 +08007#include "version.hpp"
8
Brad Bishop8facccf2020-11-04 09:44:58 -05009#include <phosphor-logging/elog-errors.hpp>
10#include <phosphor-logging/log.hpp>
11#include <xyz/openbmc_project/Common/error.hpp>
12
Lei YUa7b4ade2019-02-25 17:49:29 +080013#include <array>
Lei YUdec8cf92019-02-21 17:47:05 +080014#include <cstring>
15#include <filesystem>
16#include <fstream>
Lei YUa2e67162019-02-22 17:35:24 +080017#include <sstream>
Lei YUdec8cf92019-02-21 17:47:05 +080018#include <string>
Lei YU5efca582019-02-25 17:05:29 +080019#include <tuple>
Lei YUdec8cf92019-02-21 17:47:05 +080020
21using namespace sdbusplus::xyz::openbmc_project::Common::Error;
22using namespace phosphor::logging;
Lei YU5efca582019-02-25 17:05:29 +080023using sdbusplus::exception::SdBusError;
Lei YUdec8cf92019-02-21 17:47:05 +080024
25// When you see server:: you know we're referencing our base class
26namespace server = sdbusplus::xyz::openbmc_project::Software::server;
27namespace fs = std::filesystem;
28
29namespace utils
30{
31
32template <typename... Ts>
33std::string concat_string(Ts const&... ts)
34{
35 std::stringstream s;
36 ((s << ts << " "), ...) << std::endl;
37 return s.str();
38}
39
40// Helper function to run pflash command
Lei YU5efca582019-02-25 17:05:29 +080041// Returns return code and the stdout
Lei YUdec8cf92019-02-21 17:47:05 +080042template <typename... Ts>
Lei YU5efca582019-02-25 17:05:29 +080043std::pair<int, std::string> pflash(Ts const&... ts)
Lei YUdec8cf92019-02-21 17:47:05 +080044{
45 std::array<char, 512> buffer;
46 std::string cmd = concat_string("pflash", ts...);
47 std::stringstream result;
Lei YU5efca582019-02-25 17:05:29 +080048 int rc;
49 FILE* pipe = popen(cmd.c_str(), "r");
Lei YUdec8cf92019-02-21 17:47:05 +080050 if (!pipe)
51 {
52 throw std::runtime_error("popen() failed!");
53 }
Lei YU5efca582019-02-25 17:05:29 +080054 while (fgets(buffer.data(), buffer.size(), pipe) != nullptr)
Lei YUdec8cf92019-02-21 17:47:05 +080055 {
56 result << buffer.data();
57 }
Lei YU5efca582019-02-25 17:05:29 +080058 rc = pclose(pipe);
59 return {rc, result.str()};
Lei YUdec8cf92019-02-21 17:47:05 +080060}
61
62std::string getPNORVersion()
63{
64 // A signed version partition will have an extra 4K header starting with
65 // the magic number 17082011 in big endian:
66 // https://github.com/open-power/skiboot/blob/master/libstb/container.h#L47
67
68 constexpr uint8_t MAGIC[] = {0x17, 0x08, 0x20, 0x11};
69 constexpr auto MAGIC_SIZE = sizeof(MAGIC);
70 static_assert(MAGIC_SIZE == 4);
71
72 auto tmp = fs::temp_directory_path();
73 std::string tmpDir(tmp / "versionXXXXXX");
74 if (!mkdtemp(tmpDir.data()))
75 {
76 log<level::ERR>("Failed to create temp dir");
77 return {};
78 }
79
80 fs::path versionFile = tmpDir;
81 versionFile /= "version";
82
Lei YU5efca582019-02-25 17:05:29 +080083 auto [rc, r] =
84 pflash("-P VERSION -r", versionFile.string(), "2>&1 > /dev/null");
85 if (rc != 0)
86 {
87 log<level::ERR>("Failed to read VERSION", entry("RETURNCODE=%d", rc));
88 return {};
89 }
90
Lei YUdec8cf92019-02-21 17:47:05 +080091 std::ifstream f(versionFile.c_str(), std::ios::in | std::ios::binary);
92 uint8_t magic[MAGIC_SIZE];
93 std::string version;
94
95 f.read(reinterpret_cast<char*>(magic), MAGIC_SIZE);
96 f.seekg(0, std::ios::beg);
97 if (std::memcmp(magic, MAGIC, MAGIC_SIZE) == 0)
98 {
99 // Skip the first 4K header
100 f.ignore(4096);
101 }
102
103 getline(f, version, '\0');
104 f.close();
105
106 // Clear the temp dir
107 std::error_code ec;
108 fs::remove_all(tmpDir, ec);
109 if (ec)
110 {
111 log<level::ERR>("Failed to remove temp dir",
112 entry("DIR=%s", tmpDir.c_str()),
113 entry("ERR=%s", ec.message().c_str()));
114 }
115
116 return version;
117}
118
Lei YU6cecc9b2019-02-26 17:27:23 +0800119void pnorClear(const std::string& part, bool shouldEcc = true)
Lei YUa7b4ade2019-02-25 17:49:29 +0800120{
121 int rc;
122 std::tie(rc, std::ignore) =
Lei YU6cecc9b2019-02-26 17:27:23 +0800123 utils::pflash("-P", part, shouldEcc ? "-c" : "-e", "-f >/dev/null");
Lei YUa7b4ade2019-02-25 17:49:29 +0800124 if (rc != 0)
125 {
126 log<level::ERR>("Failed to clear partition",
127 entry("PART=%s", part.c_str()),
128 entry("RETURNCODE=%d", rc));
129 }
130 else
131 {
132 log<level::INFO>("Clear partition successfully",
133 entry("PART=%s", part.c_str()));
134 }
135}
136
Lei YU6cecc9b2019-02-26 17:27:23 +0800137// The pair contains the partition name and if it should use ECC clear
138using PartClear = std::pair<std::string, bool>;
139
140std::vector<PartClear> getPartsToClear(const std::string& info)
141{
142 std::vector<PartClear> ret;
143 std::istringstream iss(info);
144 std::string line;
145
146 while (std::getline(iss, line))
147 {
148 // Each line looks like
149 // ID=06 MVPD 0x0012d000..0x001bd000 (actual=0x00090000) [E--P--F-C-]
150 // Flag 'F' means REPROVISION
Alexander Filippovfa9a6be2019-12-18 12:06:47 +0300151 // Flag 'E' means ECC required
Lei YU07830762019-03-01 17:17:28 +0800152 auto pos = line.find('[');
153 if (pos == std::string::npos)
154 {
155 continue;
156 }
157 auto flags = line.substr(pos);
Lei YU6cecc9b2019-02-26 17:27:23 +0800158 if (flags.find('F') != std::string::npos)
159 {
160 // This is a partition to be cleared
Lei YU07830762019-03-01 17:17:28 +0800161 pos = line.find_first_of(' '); // After "ID=xx"
162 if (pos == std::string::npos)
163 {
164 continue;
165 }
166 line = line.substr(pos); // Skiping "ID=xx"
167
168 pos = line.find_first_not_of(' '); // After spaces
169 if (pos == std::string::npos)
170 {
171 continue;
172 }
173 line = line.substr(pos); // Skipping spaces
174
175 pos = line.find_first_of(' '); // The end of part name
176 if (pos == std::string::npos)
177 {
178 continue;
179 }
180 line = line.substr(0, pos); // The part name
181
Alexander Filippovfa9a6be2019-12-18 12:06:47 +0300182 bool ecc = flags.find('E') != std::string::npos;
Lei YU6cecc9b2019-02-26 17:27:23 +0800183 ret.emplace_back(line, ecc);
184 }
185 }
186 return ret;
187}
188
189// Get partitions that should be cleared
190std::vector<PartClear> getPartsToClear()
191{
192 const auto& [rc, pflashInfo] = pflash("-i | grep ^ID | grep 'F'");
193 return getPartsToClear(pflashInfo);
194}
195
Lei YUdec8cf92019-02-21 17:47:05 +0800196} // namespace utils
197
Lei YU322f3f42019-02-21 16:10:41 +0800198namespace openpower
199{
200namespace software
201{
202namespace updater
203{
Lei YU5efca582019-02-25 17:05:29 +0800204
Lei YU322f3f42019-02-21 16:10:41 +0800205std::unique_ptr<Activation> ItemUpdaterStatic::createActivationObject(
206 const std::string& path, const std::string& versionId,
207 const std::string& extVersion,
208 sdbusplus::xyz::openbmc_project::Software::server::Activation::Activations
209 activationStatus,
210 AssociationList& assocs)
211{
Lei YUb53425d2019-02-22 11:38:40 +0800212 return std::make_unique<ActivationStatic>(
213 bus, path, *this, versionId, extVersion, activationStatus, assocs);
Lei YU322f3f42019-02-21 16:10:41 +0800214}
215
216std::unique_ptr<Version> ItemUpdaterStatic::createVersionObject(
217 const std::string& objPath, const std::string& versionId,
218 const std::string& versionString,
219 sdbusplus::xyz::openbmc_project::Software::server::Version::VersionPurpose
220 versionPurpose,
221 const std::string& filePath)
222{
Lei YUb53425d2019-02-22 11:38:40 +0800223 auto version = std::make_unique<Version>(
224 bus, objPath, *this, versionId, versionString, versionPurpose, filePath,
225 std::bind(&ItemUpdaterStatic::erase, this, std::placeholders::_1));
226 version->deleteObject = std::make_unique<Delete>(bus, objPath, *version);
227 return version;
Lei YU322f3f42019-02-21 16:10:41 +0800228}
229
230bool ItemUpdaterStatic::validateImage(const std::string& path)
231{
Lei YUb53425d2019-02-22 11:38:40 +0800232 // There is no need to validate static layout pnor
Lei YU322f3f42019-02-21 16:10:41 +0800233 return true;
234}
235
236void ItemUpdaterStatic::processPNORImage()
237{
Lei YUdec8cf92019-02-21 17:47:05 +0800238 auto fullVersion = utils::getPNORVersion();
239
240 const auto& [version, extendedVersion] = Version::getVersions(fullVersion);
241 auto id = Version::getId(version);
242
Lei YU91add6d2019-03-01 14:23:40 +0800243 if (id.empty())
244 {
245 // Possibly a corrupted PNOR
246 return;
247 }
248
Lei YUdec8cf92019-02-21 17:47:05 +0800249 auto activationState = server::Activation::Activations::Active;
250 if (version.empty())
251 {
252 log<level::ERR>("Failed to read version",
253 entry("VERSION=%s", fullVersion.c_str()));
254 activationState = server::Activation::Activations::Invalid;
255 }
256
257 if (extendedVersion.empty())
258 {
259 log<level::ERR>("Failed to read extendedVersion",
260 entry("VERSION=%s", fullVersion.c_str()));
261 activationState = server::Activation::Activations::Invalid;
262 }
263
264 auto purpose = server::Version::VersionPurpose::Host;
265 auto path = fs::path(SOFTWARE_OBJPATH) / id;
266 AssociationList associations = {};
267
268 if (activationState == server::Activation::Activations::Active)
269 {
270 // Create an association to the host inventory item
271 associations.emplace_back(std::make_tuple(ACTIVATION_FWD_ASSOCIATION,
272 ACTIVATION_REV_ASSOCIATION,
273 HOST_INVENTORY_PATH));
274
275 // Create an active association since this image is active
276 createActiveAssociation(path);
277 }
278
Adriana Kobylak3c810372020-07-15 16:47:03 -0500279 // All updateable firmware components must expose the updateable
280 // association.
281 createUpdateableAssociation(path);
282
Lei YUb53425d2019-02-22 11:38:40 +0800283 // Create Activation instance for this version.
284 activations.insert(std::make_pair(
285 id, std::make_unique<ActivationStatic>(bus, path, *this, id,
286 extendedVersion, activationState,
287 associations)));
288
289 // If Active, create RedundancyPriority instance for this version.
290 if (activationState == server::Activation::Activations::Active)
291 {
292 // For now only one PNOR is supported with static layout
293 activations.find(id)->second->redundancyPriority =
294 std::make_unique<RedundancyPriority>(
295 bus, path, *(activations.find(id)->second), 0);
296 }
297
Lei YUdec8cf92019-02-21 17:47:05 +0800298 // Create Version instance for this version.
299 auto versionPtr = std::make_unique<Version>(
300 bus, path, *this, id, version, purpose, "",
301 std::bind(&ItemUpdaterStatic::erase, this, std::placeholders::_1));
302 versionPtr->deleteObject = std::make_unique<Delete>(bus, path, *versionPtr);
303 versions.insert(std::make_pair(id, std::move(versionPtr)));
304
305 if (!id.empty())
306 {
307 updateFunctionalAssociation(id);
308 }
Lei YU322f3f42019-02-21 16:10:41 +0800309}
310
311void ItemUpdaterStatic::reset()
312{
Lei YU6cecc9b2019-02-26 17:27:23 +0800313 auto partitions = utils::getPartsToClear();
Lei YUa7b4ade2019-02-25 17:49:29 +0800314
Lei YUe4994462019-03-14 14:41:53 +0800315 utils::hiomapdSuspend(bus);
Lei YUa7b4ade2019-02-25 17:49:29 +0800316
Lei YUa7b4ade2019-02-25 17:49:29 +0800317 for (auto p : partitions)
318 {
319 utils::pnorClear(p.first, p.second);
320 }
321
Lei YUe4994462019-03-14 14:41:53 +0800322 utils::hiomapdResume(bus);
Lei YU322f3f42019-02-21 16:10:41 +0800323}
324
325bool ItemUpdaterStatic::isVersionFunctional(const std::string& versionId)
326{
Lei YUa2e67162019-02-22 17:35:24 +0800327 return versionId == functionalVersionId;
Lei YU322f3f42019-02-21 16:10:41 +0800328}
329
330void ItemUpdaterStatic::freePriority(uint8_t value,
331 const std::string& versionId)
Brad Bishop8facccf2020-11-04 09:44:58 -0500332{}
Lei YU322f3f42019-02-21 16:10:41 +0800333
334void ItemUpdaterStatic::deleteAll()
335{
Lei YUa2e67162019-02-22 17:35:24 +0800336 // Static layout has only one active and function pnor
337 // There is no implementation for this interface
Lei YU322f3f42019-02-21 16:10:41 +0800338}
339
Lei YU6da3dae2019-02-28 14:26:37 +0800340bool ItemUpdaterStatic::freeSpace()
Lei YU322f3f42019-02-21 16:10:41 +0800341{
Lei YUa2e67162019-02-22 17:35:24 +0800342 // For now assume static layout only has 1 active PNOR,
343 // so erase the active PNOR
344 for (const auto& iter : activations)
345 {
346 if (iter.second.get()->activation() ==
347 server::Activation::Activations::Active)
348 {
Lei YU6da3dae2019-02-28 14:26:37 +0800349 return erase(iter.second->versionId);
Lei YUa2e67162019-02-22 17:35:24 +0800350 }
351 }
Lei YU91add6d2019-03-01 14:23:40 +0800352 // No active PNOR means PNOR is empty or corrupted
353 return true;
Lei YUa2e67162019-02-22 17:35:24 +0800354}
355
356void ItemUpdaterStatic::updateFunctionalAssociation(
357 const std::string& versionId)
358{
359 functionalVersionId = versionId;
360 ItemUpdater::updateFunctionalAssociation(versionId);
Lei YU322f3f42019-02-21 16:10:41 +0800361}
362
Lei YU716de5b2019-03-01 16:03:53 +0800363void GardResetStatic::reset()
Lei YU322f3f42019-02-21 16:10:41 +0800364{
Lei YU5efca582019-02-25 17:05:29 +0800365 // Clear gard partition
Lei YUe4994462019-03-14 14:41:53 +0800366 utils::hiomapdSuspend(bus);
Lei YU5efca582019-02-25 17:05:29 +0800367
Lei YUa7b4ade2019-02-25 17:49:29 +0800368 utils::pnorClear("GUARD");
Lei YU5efca582019-02-25 17:05:29 +0800369
Lei YUe4994462019-03-14 14:41:53 +0800370 utils::hiomapdResume(bus);
Lei YU322f3f42019-02-21 16:10:41 +0800371}
372
373} // namespace updater
374} // namespace software
375} // namespace openpower