blob: ee8796584f787d114dc29c34cd0703a907ebdd3f [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
15#include <experimental/filesystem>
16#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;
29namespace fs = std::experimental::filesystem;
30
31using namespace sdbusplus::xyz::openbmc_project::Common::Error;
32using namespace phosphor::logging;
33
Lei YUa9ac9272019-02-22 16:38:35 +080034std::unique_ptr<Activation> ItemUpdaterUbi::createActivationObject(
35 const std::string& path, const std::string& versionId,
36 const std::string& extVersion,
37 sdbusplus::xyz::openbmc_project::Software::server::Activation::Activations
38 activationStatus,
39 AssociationList& assocs)
Lei YUf3ce4332019-02-21 14:09:49 +080040{
Lei YUa9ac9272019-02-22 16:38:35 +080041 return std::make_unique<ActivationUbi>(
42 bus, path, *this, versionId, extVersion, activationStatus, assocs);
43}
Lei YUf3ce4332019-02-21 14:09:49 +080044
Lei YUa9ac9272019-02-22 16:38:35 +080045std::unique_ptr<Version> ItemUpdaterUbi::createVersionObject(
46 const std::string& objPath, const std::string& versionId,
47 const std::string& versionString,
48 sdbusplus::xyz::openbmc_project::Software::server::Version::VersionPurpose
49 versionPurpose,
50 const std::string& filePath)
51{
52 auto version = std::make_unique<Version>(
53 bus, objPath, *this, versionId, versionString, versionPurpose, filePath,
54 std::bind(&ItemUpdaterUbi::erase, this, std::placeholders::_1));
55 version->deleteObject = std::make_unique<Delete>(bus, objPath, *version);
56 return version;
57}
Lei YUf3ce4332019-02-21 14:09:49 +080058
Lei YUa9ac9272019-02-22 16:38:35 +080059bool ItemUpdaterUbi::validateImage(const std::string& path)
60{
61 return validateSquashFSImage(path) == 0;
Lei YUf3ce4332019-02-21 14:09:49 +080062}
63
64void ItemUpdaterUbi::processPNORImage()
65{
66 // Read pnor.toc from folders under /media/
67 // to get Active Software Versions.
68 for (const auto& iter : fs::directory_iterator(MEDIA_DIR))
69 {
70 auto activationState = server::Activation::Activations::Active;
71
72 static const auto PNOR_RO_PREFIX_LEN = strlen(PNOR_RO_PREFIX);
73 static const auto PNOR_RW_PREFIX_LEN = strlen(PNOR_RW_PREFIX);
74
75 // Check if the PNOR_RO_PREFIX is the prefix of the iter.path
76 if (0 ==
77 iter.path().native().compare(0, PNOR_RO_PREFIX_LEN, PNOR_RO_PREFIX))
78 {
79 // The versionId is extracted from the path
80 // for example /media/pnor-ro-2a1022fe.
81 auto id = iter.path().native().substr(PNOR_RO_PREFIX_LEN);
82 auto pnorTOC = iter.path() / PNOR_TOC_FILE;
83 if (!fs::is_regular_file(pnorTOC))
84 {
85 log<level::ERR>("Failed to read pnorTOC.",
86 entry("FILENAME=%s", pnorTOC.c_str()));
87 ItemUpdaterUbi::erase(id);
88 continue;
89 }
90 auto keyValues = Version::getValue(
91 pnorTOC, {{"version", ""}, {"extended_version", ""}});
92 auto& version = keyValues.at("version");
93 if (version.empty())
94 {
95 log<level::ERR>("Failed to read version from pnorTOC",
96 entry("FILENAME=%s", pnorTOC.c_str()));
97 activationState = server::Activation::Activations::Invalid;
98 }
99
100 auto& extendedVersion = keyValues.at("extended_version");
101 if (extendedVersion.empty())
102 {
103 log<level::ERR>("Failed to read extendedVersion from pnorTOC",
104 entry("FILENAME=%s", pnorTOC.c_str()));
105 activationState = server::Activation::Activations::Invalid;
106 }
107
108 auto purpose = server::Version::VersionPurpose::Host;
109 auto path = fs::path(SOFTWARE_OBJPATH) / id;
110 AssociationList associations = {};
111
112 if (activationState == server::Activation::Activations::Active)
113 {
114 // Create an association to the host inventory item
115 associations.emplace_back(std::make_tuple(
116 ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION,
117 HOST_INVENTORY_PATH));
118
119 // Create an active association since this image is active
120 createActiveAssociation(path);
121 }
122
Adriana Kobylak3c810372020-07-15 16:47:03 -0500123 // All updateable firmware components must expose the updateable
124 // association.
125 createUpdateableAssociation(path);
126
Lei YUf3ce4332019-02-21 14:09:49 +0800127 // Create Activation instance for this version.
128 activations.insert(
Lei YU9b21efc2019-02-21 15:52:53 +0800129 std::make_pair(id, std::make_unique<ActivationUbi>(
Lei YUf3ce4332019-02-21 14:09:49 +0800130 bus, path, *this, id, extendedVersion,
131 activationState, associations)));
132
133 // If Active, create RedundancyPriority instance for this version.
134 if (activationState == server::Activation::Activations::Active)
135 {
136 uint8_t priority = std::numeric_limits<uint8_t>::max();
137 if (!restoreFromFile(id, priority))
138 {
139 log<level::ERR>("Unable to restore priority from file.",
140 entry("VERSIONID=%s", id.c_str()));
141 }
142 activations.find(id)->second->redundancyPriority =
Lei YU9b21efc2019-02-21 15:52:53 +0800143 std::make_unique<RedundancyPriorityUbi>(
Lei YUf3ce4332019-02-21 14:09:49 +0800144 bus, path, *(activations.find(id)->second), priority);
145 }
146
147 // Create Version instance for this version.
148 auto versionPtr = std::make_unique<Version>(
149 bus, path, *this, id, version, purpose, "",
150 std::bind(&ItemUpdaterUbi::erase, this, std::placeholders::_1));
151 versionPtr->deleteObject =
152 std::make_unique<Delete>(bus, path, *versionPtr);
153 versions.insert(std::make_pair(id, std::move(versionPtr)));
154 }
155 else if (0 == iter.path().native().compare(0, PNOR_RW_PREFIX_LEN,
156 PNOR_RW_PREFIX))
157 {
158 auto id = iter.path().native().substr(PNOR_RW_PREFIX_LEN);
159 auto roDir = PNOR_RO_PREFIX + id;
160 if (!fs::is_directory(roDir))
161 {
162 log<level::ERR>("No corresponding read-only volume found.",
163 entry("DIRNAME=%s", roDir.c_str()));
164 ItemUpdaterUbi::erase(id);
165 }
166 }
167 }
168
169 // Look at the RO symlink to determine if there is a functional image
170 auto id = determineId(PNOR_RO_ACTIVE_PATH);
171 if (!id.empty())
172 {
173 updateFunctionalAssociation(id);
174 }
175 return;
176}
177
178int ItemUpdaterUbi::validateSquashFSImage(const std::string& filePath)
179{
180 auto file = fs::path(filePath) / squashFSImage;
181 if (fs::is_regular_file(file))
182 {
183 return 0;
184 }
185 else
186 {
187 log<level::ERR>("Failed to find the SquashFS image.");
188 return -1;
189 }
190}
191
Lei YU1db9adf2019-03-05 16:02:31 +0800192void ItemUpdaterUbi::removeReadOnlyPartition(const std::string& versionId)
Lei YUf3ce4332019-02-21 14:09:49 +0800193{
194 auto serviceFile = "obmc-flash-bios-ubiumount-ro@" + versionId + ".service";
195
196 // Remove the read-only partitions.
197 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
198 SYSTEMD_INTERFACE, "StartUnit");
199 method.append(serviceFile, "replace");
200 bus.call_noreply(method);
201}
202
Lei YU1db9adf2019-03-05 16:02:31 +0800203void ItemUpdaterUbi::removeReadWritePartition(const std::string& versionId)
Lei YUf3ce4332019-02-21 14:09:49 +0800204{
205 auto serviceFile = "obmc-flash-bios-ubiumount-rw@" + versionId + ".service";
206
207 // Remove the read-write partitions.
208 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
209 SYSTEMD_INTERFACE, "StartUnit");
210 method.append(serviceFile, "replace");
211 bus.call_noreply(method);
212}
213
214void ItemUpdaterUbi::reset()
215{
Lei YUe4994462019-03-14 14:41:53 +0800216 utils::hiomapdSuspend(bus);
Lei YUf3ce4332019-02-21 14:09:49 +0800217
218 constexpr static auto patchDir = "/usr/local/share/pnor";
219 if (fs::is_directory(patchDir))
220 {
221 for (const auto& iter : fs::directory_iterator(patchDir))
222 {
223 fs::remove_all(iter);
224 }
225 }
226
227 // Clear the read-write partitions.
228 for (const auto& it : activations)
229 {
230 auto rwDir = PNOR_RW_PREFIX + it.first;
231 if (fs::is_directory(rwDir))
232 {
233 for (const auto& iter : fs::directory_iterator(rwDir))
234 {
235 fs::remove_all(iter);
236 }
237 }
238 }
239
240 // Clear the preserved partition.
241 if (fs::is_directory(PNOR_PRSV))
242 {
243 for (const auto& iter : fs::directory_iterator(PNOR_PRSV))
244 {
245 fs::remove_all(iter);
246 }
247 }
248
Lei YUe4994462019-03-14 14:41:53 +0800249 utils::hiomapdResume(bus);
Lei YUf3ce4332019-02-21 14:09:49 +0800250}
251
252bool ItemUpdaterUbi::isVersionFunctional(const std::string& versionId)
253{
254 if (!fs::exists(PNOR_RO_ACTIVE_PATH))
255 {
256 return false;
257 }
258
259 fs::path activeRO = fs::read_symlink(PNOR_RO_ACTIVE_PATH);
260
261 if (!fs::is_directory(activeRO))
262 {
263 return false;
264 }
265
266 if (activeRO.string().find(versionId) == std::string::npos)
267 {
268 return false;
269 }
270
271 // active PNOR is the version we're checking
272 return true;
273}
274
275void ItemUpdaterUbi::freePriority(uint8_t value, const std::string& versionId)
276{
Adriana Kobylakabe862a2019-07-17 16:09:00 -0500277 // Versions with the lowest priority in front
278 std::priority_queue<std::pair<int, std::string>,
279 std::vector<std::pair<int, std::string>>,
280 std::greater<std::pair<int, std::string>>>
281 versionsPQ;
282
Lei YUf3ce4332019-02-21 14:09:49 +0800283 for (const auto& intf : activations)
284 {
285 if (intf.second->redundancyPriority)
286 {
Adriana Kobylakabe862a2019-07-17 16:09:00 -0500287 versionsPQ.push(std::make_pair(
288 intf.second->redundancyPriority.get()->priority(),
289 intf.second->versionId));
Lei YUf3ce4332019-02-21 14:09:49 +0800290 }
291 }
Adriana Kobylakabe862a2019-07-17 16:09:00 -0500292
293 while (!versionsPQ.empty())
294 {
295 if (versionsPQ.top().first == value &&
296 versionsPQ.top().second != versionId)
297 {
298 // Increase priority by 1 and update its value
299 ++value;
300 storeToFile(versionsPQ.top().second, value);
301 auto it = activations.find(versionsPQ.top().second);
302 it->second->redundancyPriority.get()->sdbusplus::xyz::
303 openbmc_project::Software::server::RedundancyPriority::priority(
304 value);
305 }
306 versionsPQ.pop();
307 }
Lei YUf3ce4332019-02-21 14:09:49 +0800308}
309
Lei YUf3ce4332019-02-21 14:09:49 +0800310bool ItemUpdaterUbi::erase(std::string entryId)
311{
312 if (!ItemUpdater::erase(entryId))
313 {
314 return false;
315 }
316
317 // Remove priority persistence file
318 removeFile(entryId);
319
320 // Removing read-only and read-write partitions
321 removeReadWritePartition(entryId);
322 removeReadOnlyPartition(entryId);
323
324 return true;
325}
326
327void ItemUpdaterUbi::deleteAll()
328{
329 auto chassisOn = isChassisOn();
330
331 for (const auto& activationIt : activations)
332 {
333 if (isVersionFunctional(activationIt.first) && chassisOn)
334 {
335 continue;
336 }
337 else
338 {
339 ItemUpdaterUbi::erase(activationIt.first);
340 }
341 }
342
343 // Remove any remaining pnor-ro- or pnor-rw- volumes that do not match
344 // the current version.
345 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
346 SYSTEMD_INTERFACE, "StartUnit");
347 method.append("obmc-flash-bios-cleanup.service", "replace");
348 bus.call_noreply(method);
349}
350
351// TODO: openbmc/openbmc#1402 Monitor flash usage
Lei YU6da3dae2019-02-28 14:26:37 +0800352bool ItemUpdaterUbi::freeSpace()
Lei YUf3ce4332019-02-21 14:09:49 +0800353{
Lei YU6da3dae2019-02-28 14:26:37 +0800354 bool isSpaceFreed = false;
Lei YUf3ce4332019-02-21 14:09:49 +0800355 // Versions with the highest priority in front
356 std::priority_queue<std::pair<int, std::string>,
357 std::vector<std::pair<int, std::string>>,
358 std::less<std::pair<int, std::string>>>
359 versionsPQ;
360
361 std::size_t count = 0;
362 for (const auto& iter : activations)
363 {
364 if (iter.second.get()->activation() ==
365 server::Activation::Activations::Active)
366 {
367 count++;
368 // Don't put the functional version on the queue since we can't
369 // remove the "running" PNOR version if it allows multiple PNORs
370 // But removing functional version if there is only one PNOR.
371 if (ACTIVE_PNOR_MAX_ALLOWED > 1 &&
372 isVersionFunctional(iter.second->versionId))
373 {
374 continue;
375 }
376 versionsPQ.push(std::make_pair(
377 iter.second->redundancyPriority.get()->priority(),
378 iter.second->versionId));
379 }
380 }
381
382 // If the number of PNOR versions is over ACTIVE_PNOR_MAX_ALLOWED -1,
383 // remove the highest priority one(s).
384 while ((count >= ACTIVE_PNOR_MAX_ALLOWED) && (!versionsPQ.empty()))
385 {
386 erase(versionsPQ.top().second);
387 versionsPQ.pop();
388 count--;
Lei YU6da3dae2019-02-28 14:26:37 +0800389 isSpaceFreed = true;
Lei YUf3ce4332019-02-21 14:09:49 +0800390 }
Lei YU6da3dae2019-02-28 14:26:37 +0800391 return isSpaceFreed;
Lei YUf3ce4332019-02-21 14:09:49 +0800392}
393
Lei YUbee51402019-02-26 11:36:34 +0800394std::string ItemUpdaterUbi::determineId(const std::string& symlinkPath)
Lei YUf3ce4332019-02-21 14:09:49 +0800395{
396 if (!fs::exists(symlinkPath))
397 {
398 return {};
399 }
400
401 auto target = fs::canonical(symlinkPath).string();
402
403 // check to make sure the target really exists
404 if (!fs::is_regular_file(target + "/" + PNOR_TOC_FILE))
405 {
406 return {};
407 }
408 // Get the image <id> from the symlink target
409 // for example /media/ro-2a1022fe
410 static const auto PNOR_RO_PREFIX_LEN = strlen(PNOR_RO_PREFIX);
411 return target.substr(PNOR_RO_PREFIX_LEN);
412}
413
Lei YU716de5b2019-03-01 16:03:53 +0800414void GardResetUbi::reset()
Lei YUf3ce4332019-02-21 14:09:49 +0800415{
416 // The GARD partition is currently misspelled "GUARD." This file path will
417 // need to be updated in the future.
418 auto path = fs::path(PNOR_PRSV_ACTIVE_PATH);
419 path /= "GUARD";
Lei YUf3ce4332019-02-21 14:09:49 +0800420
Lei YUe4994462019-03-14 14:41:53 +0800421 utils::hiomapdSuspend(bus);
Lei YUf3ce4332019-02-21 14:09:49 +0800422
423 if (fs::is_regular_file(path))
424 {
425 fs::remove(path);
426 }
427
Lei YUe4994462019-03-14 14:41:53 +0800428 utils::hiomapdResume(bus);
Lei YUf3ce4332019-02-21 14:09:49 +0800429}
430
431} // namespace updater
432} // namespace software
433} // namespace openpower