blob: 188b0bffdc217f7062ad6e89091cd85c3b49586a [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
11#include <experimental/filesystem>
12#include <fstream>
13#include <phosphor-logging/elog-errors.hpp>
14#include <phosphor-logging/log.hpp>
15#include <queue>
16#include <string>
17#include <xyz/openbmc_project/Software/Version/server.hpp>
18
19namespace openpower
20{
21namespace software
22{
23namespace updater
24{
25
26// When you see server:: you know we're referencing our base class
27namespace server = sdbusplus::xyz::openbmc_project::Software::server;
28namespace fs = std::experimental::filesystem;
29
30using namespace sdbusplus::xyz::openbmc_project::Common::Error;
31using namespace phosphor::logging;
32
33constexpr auto squashFSImage = "pnor.xz.squashfs";
34
Lei YUa9ac9272019-02-22 16:38:35 +080035std::unique_ptr<Activation> ItemUpdaterUbi::createActivationObject(
36 const std::string& path, const std::string& versionId,
37 const std::string& extVersion,
38 sdbusplus::xyz::openbmc_project::Software::server::Activation::Activations
39 activationStatus,
40 AssociationList& assocs)
Lei YUf3ce4332019-02-21 14:09:49 +080041{
Lei YUa9ac9272019-02-22 16:38:35 +080042 return std::make_unique<ActivationUbi>(
43 bus, path, *this, versionId, extVersion, activationStatus, assocs);
44}
Lei YUf3ce4332019-02-21 14:09:49 +080045
Lei YUa9ac9272019-02-22 16:38:35 +080046std::unique_ptr<Version> ItemUpdaterUbi::createVersionObject(
47 const std::string& objPath, const std::string& versionId,
48 const std::string& versionString,
49 sdbusplus::xyz::openbmc_project::Software::server::Version::VersionPurpose
50 versionPurpose,
51 const std::string& filePath)
52{
53 auto version = std::make_unique<Version>(
54 bus, objPath, *this, versionId, versionString, versionPurpose, filePath,
55 std::bind(&ItemUpdaterUbi::erase, this, std::placeholders::_1));
56 version->deleteObject = std::make_unique<Delete>(bus, objPath, *version);
57 return version;
58}
Lei YUf3ce4332019-02-21 14:09:49 +080059
Lei YUa9ac9272019-02-22 16:38:35 +080060bool ItemUpdaterUbi::validateImage(const std::string& path)
61{
62 return validateSquashFSImage(path) == 0;
Lei YUf3ce4332019-02-21 14:09:49 +080063}
64
65void ItemUpdaterUbi::processPNORImage()
66{
67 // Read pnor.toc from folders under /media/
68 // to get Active Software Versions.
69 for (const auto& iter : fs::directory_iterator(MEDIA_DIR))
70 {
71 auto activationState = server::Activation::Activations::Active;
72
73 static const auto PNOR_RO_PREFIX_LEN = strlen(PNOR_RO_PREFIX);
74 static const auto PNOR_RW_PREFIX_LEN = strlen(PNOR_RW_PREFIX);
75
76 // Check if the PNOR_RO_PREFIX is the prefix of the iter.path
77 if (0 ==
78 iter.path().native().compare(0, PNOR_RO_PREFIX_LEN, PNOR_RO_PREFIX))
79 {
80 // The versionId is extracted from the path
81 // for example /media/pnor-ro-2a1022fe.
82 auto id = iter.path().native().substr(PNOR_RO_PREFIX_LEN);
83 auto pnorTOC = iter.path() / PNOR_TOC_FILE;
84 if (!fs::is_regular_file(pnorTOC))
85 {
86 log<level::ERR>("Failed to read pnorTOC.",
87 entry("FILENAME=%s", pnorTOC.c_str()));
88 ItemUpdaterUbi::erase(id);
89 continue;
90 }
91 auto keyValues = Version::getValue(
92 pnorTOC, {{"version", ""}, {"extended_version", ""}});
93 auto& version = keyValues.at("version");
94 if (version.empty())
95 {
96 log<level::ERR>("Failed to read version from pnorTOC",
97 entry("FILENAME=%s", pnorTOC.c_str()));
98 activationState = server::Activation::Activations::Invalid;
99 }
100
101 auto& extendedVersion = keyValues.at("extended_version");
102 if (extendedVersion.empty())
103 {
104 log<level::ERR>("Failed to read extendedVersion from pnorTOC",
105 entry("FILENAME=%s", pnorTOC.c_str()));
106 activationState = server::Activation::Activations::Invalid;
107 }
108
109 auto purpose = server::Version::VersionPurpose::Host;
110 auto path = fs::path(SOFTWARE_OBJPATH) / id;
111 AssociationList associations = {};
112
113 if (activationState == server::Activation::Activations::Active)
114 {
115 // Create an association to the host inventory item
116 associations.emplace_back(std::make_tuple(
117 ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION,
118 HOST_INVENTORY_PATH));
119
120 // Create an active association since this image is active
121 createActiveAssociation(path);
122 }
123
124 // Create Activation instance for this version.
125 activations.insert(
Lei YU9b21efc2019-02-21 15:52:53 +0800126 std::make_pair(id, std::make_unique<ActivationUbi>(
Lei YUf3ce4332019-02-21 14:09:49 +0800127 bus, path, *this, id, extendedVersion,
128 activationState, associations)));
129
130 // If Active, create RedundancyPriority instance for this version.
131 if (activationState == server::Activation::Activations::Active)
132 {
133 uint8_t priority = std::numeric_limits<uint8_t>::max();
134 if (!restoreFromFile(id, priority))
135 {
136 log<level::ERR>("Unable to restore priority from file.",
137 entry("VERSIONID=%s", id.c_str()));
138 }
139 activations.find(id)->second->redundancyPriority =
Lei YU9b21efc2019-02-21 15:52:53 +0800140 std::make_unique<RedundancyPriorityUbi>(
Lei YUf3ce4332019-02-21 14:09:49 +0800141 bus, path, *(activations.find(id)->second), priority);
142 }
143
144 // Create Version instance for this version.
145 auto versionPtr = std::make_unique<Version>(
146 bus, path, *this, id, version, purpose, "",
147 std::bind(&ItemUpdaterUbi::erase, this, std::placeholders::_1));
148 versionPtr->deleteObject =
149 std::make_unique<Delete>(bus, path, *versionPtr);
150 versions.insert(std::make_pair(id, std::move(versionPtr)));
151 }
152 else if (0 == iter.path().native().compare(0, PNOR_RW_PREFIX_LEN,
153 PNOR_RW_PREFIX))
154 {
155 auto id = iter.path().native().substr(PNOR_RW_PREFIX_LEN);
156 auto roDir = PNOR_RO_PREFIX + id;
157 if (!fs::is_directory(roDir))
158 {
159 log<level::ERR>("No corresponding read-only volume found.",
160 entry("DIRNAME=%s", roDir.c_str()));
161 ItemUpdaterUbi::erase(id);
162 }
163 }
164 }
165
166 // Look at the RO symlink to determine if there is a functional image
167 auto id = determineId(PNOR_RO_ACTIVE_PATH);
168 if (!id.empty())
169 {
170 updateFunctionalAssociation(id);
171 }
172 return;
173}
174
175int ItemUpdaterUbi::validateSquashFSImage(const std::string& filePath)
176{
177 auto file = fs::path(filePath) / squashFSImage;
178 if (fs::is_regular_file(file))
179 {
180 return 0;
181 }
182 else
183 {
184 log<level::ERR>("Failed to find the SquashFS image.");
185 return -1;
186 }
187}
188
Lei YU1db9adf2019-03-05 16:02:31 +0800189void ItemUpdaterUbi::removeReadOnlyPartition(const std::string& versionId)
Lei YUf3ce4332019-02-21 14:09:49 +0800190{
191 auto serviceFile = "obmc-flash-bios-ubiumount-ro@" + versionId + ".service";
192
193 // Remove the read-only partitions.
194 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
195 SYSTEMD_INTERFACE, "StartUnit");
196 method.append(serviceFile, "replace");
197 bus.call_noreply(method);
198}
199
Lei YU1db9adf2019-03-05 16:02:31 +0800200void ItemUpdaterUbi::removeReadWritePartition(const std::string& versionId)
Lei YUf3ce4332019-02-21 14:09:49 +0800201{
202 auto serviceFile = "obmc-flash-bios-ubiumount-rw@" + versionId + ".service";
203
204 // Remove the read-write partitions.
205 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
206 SYSTEMD_INTERFACE, "StartUnit");
207 method.append(serviceFile, "replace");
208 bus.call_noreply(method);
209}
210
211void ItemUpdaterUbi::reset()
212{
Lei YUe4994462019-03-14 14:41:53 +0800213 utils::hiomapdSuspend(bus);
Lei YUf3ce4332019-02-21 14:09:49 +0800214
215 constexpr static auto patchDir = "/usr/local/share/pnor";
216 if (fs::is_directory(patchDir))
217 {
218 for (const auto& iter : fs::directory_iterator(patchDir))
219 {
220 fs::remove_all(iter);
221 }
222 }
223
224 // Clear the read-write partitions.
225 for (const auto& it : activations)
226 {
227 auto rwDir = PNOR_RW_PREFIX + it.first;
228 if (fs::is_directory(rwDir))
229 {
230 for (const auto& iter : fs::directory_iterator(rwDir))
231 {
232 fs::remove_all(iter);
233 }
234 }
235 }
236
237 // Clear the preserved partition.
238 if (fs::is_directory(PNOR_PRSV))
239 {
240 for (const auto& iter : fs::directory_iterator(PNOR_PRSV))
241 {
242 fs::remove_all(iter);
243 }
244 }
245
Lei YUe4994462019-03-14 14:41:53 +0800246 utils::hiomapdResume(bus);
Lei YUf3ce4332019-02-21 14:09:49 +0800247}
248
249bool ItemUpdaterUbi::isVersionFunctional(const std::string& versionId)
250{
251 if (!fs::exists(PNOR_RO_ACTIVE_PATH))
252 {
253 return false;
254 }
255
256 fs::path activeRO = fs::read_symlink(PNOR_RO_ACTIVE_PATH);
257
258 if (!fs::is_directory(activeRO))
259 {
260 return false;
261 }
262
263 if (activeRO.string().find(versionId) == std::string::npos)
264 {
265 return false;
266 }
267
268 // active PNOR is the version we're checking
269 return true;
270}
271
272void ItemUpdaterUbi::freePriority(uint8_t value, const std::string& versionId)
273{
274 // TODO openbmc/openbmc#1896 Improve the performance of this function
275 for (const auto& intf : activations)
276 {
277 if (intf.second->redundancyPriority)
278 {
279 if (intf.second->redundancyPriority.get()->priority() == value &&
280 intf.second->versionId != versionId)
281 {
282 intf.second->redundancyPriority.get()->priority(value + 1);
283 }
284 }
285 }
286}
287
Lei YUf3ce4332019-02-21 14:09:49 +0800288bool ItemUpdaterUbi::erase(std::string entryId)
289{
290 if (!ItemUpdater::erase(entryId))
291 {
292 return false;
293 }
294
295 // Remove priority persistence file
296 removeFile(entryId);
297
298 // Removing read-only and read-write partitions
299 removeReadWritePartition(entryId);
300 removeReadOnlyPartition(entryId);
301
302 return true;
303}
304
305void ItemUpdaterUbi::deleteAll()
306{
307 auto chassisOn = isChassisOn();
308
309 for (const auto& activationIt : activations)
310 {
311 if (isVersionFunctional(activationIt.first) && chassisOn)
312 {
313 continue;
314 }
315 else
316 {
317 ItemUpdaterUbi::erase(activationIt.first);
318 }
319 }
320
321 // Remove any remaining pnor-ro- or pnor-rw- volumes that do not match
322 // the current version.
323 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
324 SYSTEMD_INTERFACE, "StartUnit");
325 method.append("obmc-flash-bios-cleanup.service", "replace");
326 bus.call_noreply(method);
327}
328
329// TODO: openbmc/openbmc#1402 Monitor flash usage
Lei YU6da3dae2019-02-28 14:26:37 +0800330bool ItemUpdaterUbi::freeSpace()
Lei YUf3ce4332019-02-21 14:09:49 +0800331{
Lei YU6da3dae2019-02-28 14:26:37 +0800332 bool isSpaceFreed = false;
Lei YUf3ce4332019-02-21 14:09:49 +0800333 // Versions with the highest priority in front
334 std::priority_queue<std::pair<int, std::string>,
335 std::vector<std::pair<int, std::string>>,
336 std::less<std::pair<int, std::string>>>
337 versionsPQ;
338
339 std::size_t count = 0;
340 for (const auto& iter : activations)
341 {
342 if (iter.second.get()->activation() ==
343 server::Activation::Activations::Active)
344 {
345 count++;
346 // Don't put the functional version on the queue since we can't
347 // remove the "running" PNOR version if it allows multiple PNORs
348 // But removing functional version if there is only one PNOR.
349 if (ACTIVE_PNOR_MAX_ALLOWED > 1 &&
350 isVersionFunctional(iter.second->versionId))
351 {
352 continue;
353 }
354 versionsPQ.push(std::make_pair(
355 iter.second->redundancyPriority.get()->priority(),
356 iter.second->versionId));
357 }
358 }
359
360 // If the number of PNOR versions is over ACTIVE_PNOR_MAX_ALLOWED -1,
361 // remove the highest priority one(s).
362 while ((count >= ACTIVE_PNOR_MAX_ALLOWED) && (!versionsPQ.empty()))
363 {
364 erase(versionsPQ.top().second);
365 versionsPQ.pop();
366 count--;
Lei YU6da3dae2019-02-28 14:26:37 +0800367 isSpaceFreed = true;
Lei YUf3ce4332019-02-21 14:09:49 +0800368 }
Lei YU6da3dae2019-02-28 14:26:37 +0800369 return isSpaceFreed;
Lei YUf3ce4332019-02-21 14:09:49 +0800370}
371
Lei YUbee51402019-02-26 11:36:34 +0800372std::string ItemUpdaterUbi::determineId(const std::string& symlinkPath)
Lei YUf3ce4332019-02-21 14:09:49 +0800373{
374 if (!fs::exists(symlinkPath))
375 {
376 return {};
377 }
378
379 auto target = fs::canonical(symlinkPath).string();
380
381 // check to make sure the target really exists
382 if (!fs::is_regular_file(target + "/" + PNOR_TOC_FILE))
383 {
384 return {};
385 }
386 // Get the image <id> from the symlink target
387 // for example /media/ro-2a1022fe
388 static const auto PNOR_RO_PREFIX_LEN = strlen(PNOR_RO_PREFIX);
389 return target.substr(PNOR_RO_PREFIX_LEN);
390}
391
Lei YU716de5b2019-03-01 16:03:53 +0800392void GardResetUbi::reset()
Lei YUf3ce4332019-02-21 14:09:49 +0800393{
394 // The GARD partition is currently misspelled "GUARD." This file path will
395 // need to be updated in the future.
396 auto path = fs::path(PNOR_PRSV_ACTIVE_PATH);
397 path /= "GUARD";
Lei YUf3ce4332019-02-21 14:09:49 +0800398
Lei YUe4994462019-03-14 14:41:53 +0800399 utils::hiomapdSuspend(bus);
Lei YUf3ce4332019-02-21 14:09:49 +0800400
401 if (fs::is_regular_file(path))
402 {
403 fs::remove(path);
404 }
405
Lei YUe4994462019-03-14 14:41:53 +0800406 utils::hiomapdResume(bus);
Lei YUf3ce4332019-02-21 14:09:49 +0800407}
408
409} // namespace updater
410} // namespace software
411} // namespace openpower