blob: 3fc64ae1c3bdc038b8ccdf17b1720f84be653390 [file] [log] [blame]
Lei YUf3ce4332019-02-21 14:09:49 +08001#include "config.h"
2
3#include "item_updater_ubi.hpp"
4
Lei YU9b21efc2019-02-21 15:52:53 +08005#include "activation_ubi.hpp"
Lei YUf3ce4332019-02-21 14:09:49 +08006#include "serialize.hpp"
Lei YUe4994462019-03-14 14:41:53 +08007#include "utils.hpp"
Lei YUf3ce4332019-02-21 14:09:49 +08008#include "version.hpp"
9#include "xyz/openbmc_project/Common/error.hpp"
10
Lei YUf3ce4332019-02-21 14:09:49 +080011#include <phosphor-logging/elog-errors.hpp>
12#include <phosphor-logging/log.hpp>
Brad Bishop8facccf2020-11-04 09:44:58 -050013#include <xyz/openbmc_project/Software/Version/server.hpp>
14
Brad Bishop9f44c992020-11-06 14:48:46 -050015#include <filesystem>
Brad Bishop8facccf2020-11-04 09:44:58 -050016#include <fstream>
Lei YUf3ce4332019-02-21 14:09:49 +080017#include <queue>
18#include <string>
Lei YUf3ce4332019-02-21 14:09:49 +080019
20namespace openpower
21{
22namespace software
23{
24namespace updater
25{
26
27// When you see server:: you know we're referencing our base class
28namespace server = sdbusplus::xyz::openbmc_project::Software::server;
Lei YUf3ce4332019-02-21 14:09:49 +080029
30using namespace sdbusplus::xyz::openbmc_project::Common::Error;
31using namespace phosphor::logging;
32
Lei YUa9ac9272019-02-22 16:38:35 +080033std::unique_ptr<Activation> ItemUpdaterUbi::createActivationObject(
34 const std::string& path, const std::string& versionId,
35 const std::string& extVersion,
36 sdbusplus::xyz::openbmc_project::Software::server::Activation::Activations
37 activationStatus,
38 AssociationList& assocs)
Lei YUf3ce4332019-02-21 14:09:49 +080039{
Lei YUa9ac9272019-02-22 16:38:35 +080040 return std::make_unique<ActivationUbi>(
41 bus, path, *this, versionId, extVersion, activationStatus, assocs);
42}
Lei YUf3ce4332019-02-21 14:09:49 +080043
Lei YUa9ac9272019-02-22 16:38:35 +080044std::unique_ptr<Version> ItemUpdaterUbi::createVersionObject(
45 const std::string& objPath, const std::string& versionId,
46 const std::string& versionString,
47 sdbusplus::xyz::openbmc_project::Software::server::Version::VersionPurpose
48 versionPurpose,
49 const std::string& filePath)
50{
51 auto version = std::make_unique<Version>(
52 bus, objPath, *this, versionId, versionString, versionPurpose, filePath,
53 std::bind(&ItemUpdaterUbi::erase, this, std::placeholders::_1));
54 version->deleteObject = std::make_unique<Delete>(bus, objPath, *version);
55 return version;
56}
Lei YUf3ce4332019-02-21 14:09:49 +080057
Lei YUa9ac9272019-02-22 16:38:35 +080058bool ItemUpdaterUbi::validateImage(const std::string& path)
59{
60 return validateSquashFSImage(path) == 0;
Lei YUf3ce4332019-02-21 14:09:49 +080061}
62
63void ItemUpdaterUbi::processPNORImage()
64{
65 // Read pnor.toc from folders under /media/
66 // to get Active Software Versions.
Brad Bishop9f44c992020-11-06 14:48:46 -050067 for (const auto& iter : std::filesystem::directory_iterator(MEDIA_DIR))
Lei YUf3ce4332019-02-21 14:09:49 +080068 {
69 auto activationState = server::Activation::Activations::Active;
70
71 static const auto PNOR_RO_PREFIX_LEN = strlen(PNOR_RO_PREFIX);
72 static const auto PNOR_RW_PREFIX_LEN = strlen(PNOR_RW_PREFIX);
73
74 // Check if the PNOR_RO_PREFIX is the prefix of the iter.path
75 if (0 ==
76 iter.path().native().compare(0, PNOR_RO_PREFIX_LEN, PNOR_RO_PREFIX))
77 {
78 // The versionId is extracted from the path
79 // for example /media/pnor-ro-2a1022fe.
80 auto id = iter.path().native().substr(PNOR_RO_PREFIX_LEN);
81 auto pnorTOC = iter.path() / PNOR_TOC_FILE;
Brad Bishop9f44c992020-11-06 14:48:46 -050082 if (!std::filesystem::is_regular_file(pnorTOC))
Lei YUf3ce4332019-02-21 14:09:49 +080083 {
84 log<level::ERR>("Failed to read pnorTOC.",
85 entry("FILENAME=%s", pnorTOC.c_str()));
86 ItemUpdaterUbi::erase(id);
87 continue;
88 }
89 auto keyValues = Version::getValue(
90 pnorTOC, {{"version", ""}, {"extended_version", ""}});
91 auto& version = keyValues.at("version");
92 if (version.empty())
93 {
94 log<level::ERR>("Failed to read version from pnorTOC",
95 entry("FILENAME=%s", pnorTOC.c_str()));
96 activationState = server::Activation::Activations::Invalid;
97 }
98
99 auto& extendedVersion = keyValues.at("extended_version");
100 if (extendedVersion.empty())
101 {
102 log<level::ERR>("Failed to read extendedVersion from pnorTOC",
103 entry("FILENAME=%s", pnorTOC.c_str()));
104 activationState = server::Activation::Activations::Invalid;
105 }
106
107 auto purpose = server::Version::VersionPurpose::Host;
Brad Bishop9f44c992020-11-06 14:48:46 -0500108 auto path = std::filesystem::path(SOFTWARE_OBJPATH) / id;
Lei YUf3ce4332019-02-21 14:09:49 +0800109 AssociationList associations = {};
110
111 if (activationState == server::Activation::Activations::Active)
112 {
113 // Create an association to the host inventory item
114 associations.emplace_back(std::make_tuple(
115 ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION,
116 HOST_INVENTORY_PATH));
117
118 // Create an active association since this image is active
119 createActiveAssociation(path);
120 }
121
Adriana Kobylak3c810372020-07-15 16:47:03 -0500122 // All updateable firmware components must expose the updateable
123 // association.
124 createUpdateableAssociation(path);
125
Lei YUf3ce4332019-02-21 14:09:49 +0800126 // Create Activation instance for this version.
127 activations.insert(
Lei YU9b21efc2019-02-21 15:52:53 +0800128 std::make_pair(id, std::make_unique<ActivationUbi>(
Lei YUf3ce4332019-02-21 14:09:49 +0800129 bus, path, *this, id, extendedVersion,
130 activationState, associations)));
131
132 // If Active, create RedundancyPriority instance for this version.
133 if (activationState == server::Activation::Activations::Active)
134 {
135 uint8_t priority = std::numeric_limits<uint8_t>::max();
136 if (!restoreFromFile(id, priority))
137 {
138 log<level::ERR>("Unable to restore priority from file.",
139 entry("VERSIONID=%s", id.c_str()));
140 }
141 activations.find(id)->second->redundancyPriority =
Lei YU9b21efc2019-02-21 15:52:53 +0800142 std::make_unique<RedundancyPriorityUbi>(
Lei YUf3ce4332019-02-21 14:09:49 +0800143 bus, path, *(activations.find(id)->second), priority);
144 }
145
146 // Create Version instance for this version.
147 auto versionPtr = std::make_unique<Version>(
148 bus, path, *this, id, version, purpose, "",
149 std::bind(&ItemUpdaterUbi::erase, this, std::placeholders::_1));
Patrick Williams7fb6c342023-05-10 07:50:18 -0500150 versionPtr->deleteObject = std::make_unique<Delete>(bus, path,
151 *versionPtr);
Lei YUf3ce4332019-02-21 14:09:49 +0800152 versions.insert(std::make_pair(id, std::move(versionPtr)));
153 }
154 else if (0 == iter.path().native().compare(0, PNOR_RW_PREFIX_LEN,
155 PNOR_RW_PREFIX))
156 {
157 auto id = iter.path().native().substr(PNOR_RW_PREFIX_LEN);
158 auto roDir = PNOR_RO_PREFIX + id;
Brad Bishop9f44c992020-11-06 14:48:46 -0500159 if (!std::filesystem::is_directory(roDir))
Lei YUf3ce4332019-02-21 14:09:49 +0800160 {
161 log<level::ERR>("No corresponding read-only volume found.",
162 entry("DIRNAME=%s", roDir.c_str()));
163 ItemUpdaterUbi::erase(id);
164 }
165 }
166 }
167
168 // Look at the RO symlink to determine if there is a functional image
169 auto id = determineId(PNOR_RO_ACTIVE_PATH);
170 if (!id.empty())
171 {
172 updateFunctionalAssociation(id);
173 }
174 return;
175}
176
177int ItemUpdaterUbi::validateSquashFSImage(const std::string& filePath)
178{
Brad Bishop9f44c992020-11-06 14:48:46 -0500179 auto file = std::filesystem::path(filePath) / squashFSImage;
180 if (std::filesystem::is_regular_file(file))
Lei YUf3ce4332019-02-21 14:09:49 +0800181 {
182 return 0;
183 }
184 else
185 {
186 log<level::ERR>("Failed to find the SquashFS image.");
187 return -1;
188 }
189}
190
Lei YU1db9adf2019-03-05 16:02:31 +0800191void ItemUpdaterUbi::removeReadOnlyPartition(const std::string& versionId)
Lei YUf3ce4332019-02-21 14:09:49 +0800192{
193 auto serviceFile = "obmc-flash-bios-ubiumount-ro@" + versionId + ".service";
194
195 // Remove the read-only partitions.
196 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
197 SYSTEMD_INTERFACE, "StartUnit");
198 method.append(serviceFile, "replace");
199 bus.call_noreply(method);
200}
201
Lei YU1db9adf2019-03-05 16:02:31 +0800202void ItemUpdaterUbi::removeReadWritePartition(const std::string& versionId)
Lei YUf3ce4332019-02-21 14:09:49 +0800203{
204 auto serviceFile = "obmc-flash-bios-ubiumount-rw@" + versionId + ".service";
205
206 // Remove the read-write partitions.
207 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
208 SYSTEMD_INTERFACE, "StartUnit");
209 method.append(serviceFile, "replace");
210 bus.call_noreply(method);
211}
212
213void ItemUpdaterUbi::reset()
214{
Lei YUe4994462019-03-14 14:41:53 +0800215 utils::hiomapdSuspend(bus);
Lei YUf3ce4332019-02-21 14:09:49 +0800216
217 constexpr static auto patchDir = "/usr/local/share/pnor";
Brad Bishop9f44c992020-11-06 14:48:46 -0500218 if (std::filesystem::is_directory(patchDir))
Lei YUf3ce4332019-02-21 14:09:49 +0800219 {
Brad Bishop9f44c992020-11-06 14:48:46 -0500220 for (const auto& iter : std::filesystem::directory_iterator(patchDir))
Lei YUf3ce4332019-02-21 14:09:49 +0800221 {
Brad Bishop9f44c992020-11-06 14:48:46 -0500222 std::filesystem::remove_all(iter);
Lei YUf3ce4332019-02-21 14:09:49 +0800223 }
224 }
225
226 // Clear the read-write partitions.
227 for (const auto& it : activations)
228 {
229 auto rwDir = PNOR_RW_PREFIX + it.first;
Brad Bishop9f44c992020-11-06 14:48:46 -0500230 if (std::filesystem::is_directory(rwDir))
Lei YUf3ce4332019-02-21 14:09:49 +0800231 {
Brad Bishop9f44c992020-11-06 14:48:46 -0500232 for (const auto& iter : std::filesystem::directory_iterator(rwDir))
Lei YUf3ce4332019-02-21 14:09:49 +0800233 {
Brad Bishop9f44c992020-11-06 14:48:46 -0500234 std::filesystem::remove_all(iter);
Lei YUf3ce4332019-02-21 14:09:49 +0800235 }
236 }
237 }
238
Adriana Kobylaked44b452021-09-27 19:24:43 +0000239 // Clear the preserved partition, except for SECBOOT that contains keys
240 // provisioned for the system.
Brad Bishop9f44c992020-11-06 14:48:46 -0500241 if (std::filesystem::is_directory(PNOR_PRSV))
Lei YUf3ce4332019-02-21 14:09:49 +0800242 {
Brad Bishop9f44c992020-11-06 14:48:46 -0500243 for (const auto& iter : std::filesystem::directory_iterator(PNOR_PRSV))
Lei YUf3ce4332019-02-21 14:09:49 +0800244 {
Adriana Kobylaked44b452021-09-27 19:24:43 +0000245 auto secbootPartition = "SECBOOT";
246 if (iter.path().stem() == secbootPartition)
247 {
248 continue;
249 }
Brad Bishop9f44c992020-11-06 14:48:46 -0500250 std::filesystem::remove_all(iter);
Lei YUf3ce4332019-02-21 14:09:49 +0800251 }
252 }
253
Lei YUe4994462019-03-14 14:41:53 +0800254 utils::hiomapdResume(bus);
Lei YUf3ce4332019-02-21 14:09:49 +0800255}
256
257bool ItemUpdaterUbi::isVersionFunctional(const std::string& versionId)
258{
Brad Bishop9f44c992020-11-06 14:48:46 -0500259 if (!std::filesystem::exists(PNOR_RO_ACTIVE_PATH))
Lei YUf3ce4332019-02-21 14:09:49 +0800260 {
261 return false;
262 }
263
Brad Bishop9f44c992020-11-06 14:48:46 -0500264 std::filesystem::path activeRO =
265 std::filesystem::read_symlink(PNOR_RO_ACTIVE_PATH);
Lei YUf3ce4332019-02-21 14:09:49 +0800266
Brad Bishop9f44c992020-11-06 14:48:46 -0500267 if (!std::filesystem::is_directory(activeRO))
Lei YUf3ce4332019-02-21 14:09:49 +0800268 {
269 return false;
270 }
271
272 if (activeRO.string().find(versionId) == std::string::npos)
273 {
274 return false;
275 }
276
277 // active PNOR is the version we're checking
278 return true;
279}
280
281void ItemUpdaterUbi::freePriority(uint8_t value, const std::string& versionId)
282{
Adriana Kobylakabe862a2019-07-17 16:09:00 -0500283 // Versions with the lowest priority in front
284 std::priority_queue<std::pair<int, std::string>,
285 std::vector<std::pair<int, std::string>>,
286 std::greater<std::pair<int, std::string>>>
287 versionsPQ;
288
Lei YUf3ce4332019-02-21 14:09:49 +0800289 for (const auto& intf : activations)
290 {
291 if (intf.second->redundancyPriority)
292 {
Adriana Kobylakabe862a2019-07-17 16:09:00 -0500293 versionsPQ.push(std::make_pair(
294 intf.second->redundancyPriority.get()->priority(),
295 intf.second->versionId));
Lei YUf3ce4332019-02-21 14:09:49 +0800296 }
297 }
Adriana Kobylakabe862a2019-07-17 16:09:00 -0500298
299 while (!versionsPQ.empty())
300 {
301 if (versionsPQ.top().first == value &&
302 versionsPQ.top().second != versionId)
303 {
304 // Increase priority by 1 and update its value
305 ++value;
306 storeToFile(versionsPQ.top().second, value);
307 auto it = activations.find(versionsPQ.top().second);
308 it->second->redundancyPriority.get()->sdbusplus::xyz::
309 openbmc_project::Software::server::RedundancyPriority::priority(
310 value);
311 }
312 versionsPQ.pop();
313 }
Lei YUf3ce4332019-02-21 14:09:49 +0800314}
315
Lei YUf3ce4332019-02-21 14:09:49 +0800316bool ItemUpdaterUbi::erase(std::string entryId)
317{
318 if (!ItemUpdater::erase(entryId))
319 {
320 return false;
321 }
322
323 // Remove priority persistence file
324 removeFile(entryId);
325
326 // Removing read-only and read-write partitions
327 removeReadWritePartition(entryId);
328 removeReadOnlyPartition(entryId);
329
330 return true;
331}
332
333void ItemUpdaterUbi::deleteAll()
334{
335 auto chassisOn = isChassisOn();
336
337 for (const auto& activationIt : activations)
338 {
339 if (isVersionFunctional(activationIt.first) && chassisOn)
340 {
341 continue;
342 }
343 else
344 {
345 ItemUpdaterUbi::erase(activationIt.first);
346 }
347 }
348
349 // Remove any remaining pnor-ro- or pnor-rw- volumes that do not match
350 // the current version.
351 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
352 SYSTEMD_INTERFACE, "StartUnit");
353 method.append("obmc-flash-bios-cleanup.service", "replace");
354 bus.call_noreply(method);
355}
356
357// TODO: openbmc/openbmc#1402 Monitor flash usage
Lei YU6da3dae2019-02-28 14:26:37 +0800358bool ItemUpdaterUbi::freeSpace()
Lei YUf3ce4332019-02-21 14:09:49 +0800359{
Lei YU6da3dae2019-02-28 14:26:37 +0800360 bool isSpaceFreed = false;
Lei YUf3ce4332019-02-21 14:09:49 +0800361 // Versions with the highest priority in front
362 std::priority_queue<std::pair<int, std::string>,
363 std::vector<std::pair<int, std::string>>,
364 std::less<std::pair<int, std::string>>>
365 versionsPQ;
366
367 std::size_t count = 0;
368 for (const auto& iter : activations)
369 {
370 if (iter.second.get()->activation() ==
371 server::Activation::Activations::Active)
372 {
373 count++;
374 // Don't put the functional version on the queue since we can't
375 // remove the "running" PNOR version if it allows multiple PNORs
376 // But removing functional version if there is only one PNOR.
377 if (ACTIVE_PNOR_MAX_ALLOWED > 1 &&
378 isVersionFunctional(iter.second->versionId))
379 {
380 continue;
381 }
382 versionsPQ.push(std::make_pair(
383 iter.second->redundancyPriority.get()->priority(),
384 iter.second->versionId));
385 }
386 }
387
388 // If the number of PNOR versions is over ACTIVE_PNOR_MAX_ALLOWED -1,
389 // remove the highest priority one(s).
390 while ((count >= ACTIVE_PNOR_MAX_ALLOWED) && (!versionsPQ.empty()))
391 {
392 erase(versionsPQ.top().second);
393 versionsPQ.pop();
394 count--;
Lei YU6da3dae2019-02-28 14:26:37 +0800395 isSpaceFreed = true;
Lei YUf3ce4332019-02-21 14:09:49 +0800396 }
Lei YU6da3dae2019-02-28 14:26:37 +0800397 return isSpaceFreed;
Lei YUf3ce4332019-02-21 14:09:49 +0800398}
399
Lei YUbee51402019-02-26 11:36:34 +0800400std::string ItemUpdaterUbi::determineId(const std::string& symlinkPath)
Lei YUf3ce4332019-02-21 14:09:49 +0800401{
Brad Bishop9f44c992020-11-06 14:48:46 -0500402 if (!std::filesystem::exists(symlinkPath))
Lei YUf3ce4332019-02-21 14:09:49 +0800403 {
404 return {};
405 }
406
Brad Bishop9f44c992020-11-06 14:48:46 -0500407 auto target = std::filesystem::canonical(symlinkPath).string();
Lei YUf3ce4332019-02-21 14:09:49 +0800408
409 // check to make sure the target really exists
Brad Bishop9f44c992020-11-06 14:48:46 -0500410 if (!std::filesystem::is_regular_file(target + "/" + PNOR_TOC_FILE))
Lei YUf3ce4332019-02-21 14:09:49 +0800411 {
412 return {};
413 }
414 // Get the image <id> from the symlink target
415 // for example /media/ro-2a1022fe
416 static const auto PNOR_RO_PREFIX_LEN = strlen(PNOR_RO_PREFIX);
417 return target.substr(PNOR_RO_PREFIX_LEN);
418}
419
Lei YU716de5b2019-03-01 16:03:53 +0800420void GardResetUbi::reset()
Lei YUf3ce4332019-02-21 14:09:49 +0800421{
422 // The GARD partition is currently misspelled "GUARD." This file path will
423 // need to be updated in the future.
Brad Bishop9f44c992020-11-06 14:48:46 -0500424 auto path = std::filesystem::path(PNOR_PRSV_ACTIVE_PATH);
Lei YUf3ce4332019-02-21 14:09:49 +0800425 path /= "GUARD";
Lei YUf3ce4332019-02-21 14:09:49 +0800426
Lei YUe4994462019-03-14 14:41:53 +0800427 utils::hiomapdSuspend(bus);
Lei YUf3ce4332019-02-21 14:09:49 +0800428
Brad Bishop9f44c992020-11-06 14:48:46 -0500429 if (std::filesystem::is_regular_file(path))
Lei YUf3ce4332019-02-21 14:09:49 +0800430 {
Brad Bishop9f44c992020-11-06 14:48:46 -0500431 std::filesystem::remove(path);
Lei YUf3ce4332019-02-21 14:09:49 +0800432 }
433
Lei YUe4994462019-03-14 14:41:53 +0800434 utils::hiomapdResume(bus);
Lei YUf3ce4332019-02-21 14:09:49 +0800435}
436
437} // namespace updater
438} // namespace software
439} // namespace openpower