blob: ccc540a7ddd1986ce24afeb372f430803ec75490 [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
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.
67 for (const auto& iter : fs::directory_iterator(MEDIA_DIR))
68 {
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;
82 if (!fs::is_regular_file(pnorTOC))
83 {
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;
108 auto path = fs::path(SOFTWARE_OBJPATH) / id;
109 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
122 // Create Activation instance for this version.
123 activations.insert(
Lei YU9b21efc2019-02-21 15:52:53 +0800124 std::make_pair(id, std::make_unique<ActivationUbi>(
Lei YUf3ce4332019-02-21 14:09:49 +0800125 bus, path, *this, id, extendedVersion,
126 activationState, associations)));
127
128 // If Active, create RedundancyPriority instance for this version.
129 if (activationState == server::Activation::Activations::Active)
130 {
131 uint8_t priority = std::numeric_limits<uint8_t>::max();
132 if (!restoreFromFile(id, priority))
133 {
134 log<level::ERR>("Unable to restore priority from file.",
135 entry("VERSIONID=%s", id.c_str()));
136 }
137 activations.find(id)->second->redundancyPriority =
Lei YU9b21efc2019-02-21 15:52:53 +0800138 std::make_unique<RedundancyPriorityUbi>(
Lei YUf3ce4332019-02-21 14:09:49 +0800139 bus, path, *(activations.find(id)->second), priority);
140 }
141
142 // Create Version instance for this version.
143 auto versionPtr = std::make_unique<Version>(
144 bus, path, *this, id, version, purpose, "",
145 std::bind(&ItemUpdaterUbi::erase, this, std::placeholders::_1));
146 versionPtr->deleteObject =
147 std::make_unique<Delete>(bus, path, *versionPtr);
148 versions.insert(std::make_pair(id, std::move(versionPtr)));
149 }
150 else if (0 == iter.path().native().compare(0, PNOR_RW_PREFIX_LEN,
151 PNOR_RW_PREFIX))
152 {
153 auto id = iter.path().native().substr(PNOR_RW_PREFIX_LEN);
154 auto roDir = PNOR_RO_PREFIX + id;
155 if (!fs::is_directory(roDir))
156 {
157 log<level::ERR>("No corresponding read-only volume found.",
158 entry("DIRNAME=%s", roDir.c_str()));
159 ItemUpdaterUbi::erase(id);
160 }
161 }
162 }
163
164 // Look at the RO symlink to determine if there is a functional image
165 auto id = determineId(PNOR_RO_ACTIVE_PATH);
166 if (!id.empty())
167 {
168 updateFunctionalAssociation(id);
169 }
170 return;
171}
172
173int ItemUpdaterUbi::validateSquashFSImage(const std::string& filePath)
174{
175 auto file = fs::path(filePath) / squashFSImage;
176 if (fs::is_regular_file(file))
177 {
178 return 0;
179 }
180 else
181 {
182 log<level::ERR>("Failed to find the SquashFS image.");
183 return -1;
184 }
185}
186
Lei YU1db9adf2019-03-05 16:02:31 +0800187void ItemUpdaterUbi::removeReadOnlyPartition(const std::string& versionId)
Lei YUf3ce4332019-02-21 14:09:49 +0800188{
189 auto serviceFile = "obmc-flash-bios-ubiumount-ro@" + versionId + ".service";
190
191 // Remove the read-only partitions.
192 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
193 SYSTEMD_INTERFACE, "StartUnit");
194 method.append(serviceFile, "replace");
195 bus.call_noreply(method);
196}
197
Lei YU1db9adf2019-03-05 16:02:31 +0800198void ItemUpdaterUbi::removeReadWritePartition(const std::string& versionId)
Lei YUf3ce4332019-02-21 14:09:49 +0800199{
200 auto serviceFile = "obmc-flash-bios-ubiumount-rw@" + versionId + ".service";
201
202 // Remove the read-write partitions.
203 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
204 SYSTEMD_INTERFACE, "StartUnit");
205 method.append(serviceFile, "replace");
206 bus.call_noreply(method);
207}
208
209void ItemUpdaterUbi::reset()
210{
Lei YUe4994462019-03-14 14:41:53 +0800211 utils::hiomapdSuspend(bus);
Lei YUf3ce4332019-02-21 14:09:49 +0800212
213 constexpr static auto patchDir = "/usr/local/share/pnor";
214 if (fs::is_directory(patchDir))
215 {
216 for (const auto& iter : fs::directory_iterator(patchDir))
217 {
218 fs::remove_all(iter);
219 }
220 }
221
222 // Clear the read-write partitions.
223 for (const auto& it : activations)
224 {
225 auto rwDir = PNOR_RW_PREFIX + it.first;
226 if (fs::is_directory(rwDir))
227 {
228 for (const auto& iter : fs::directory_iterator(rwDir))
229 {
230 fs::remove_all(iter);
231 }
232 }
233 }
234
235 // Clear the preserved partition.
236 if (fs::is_directory(PNOR_PRSV))
237 {
238 for (const auto& iter : fs::directory_iterator(PNOR_PRSV))
239 {
240 fs::remove_all(iter);
241 }
242 }
243
Lei YUe4994462019-03-14 14:41:53 +0800244 utils::hiomapdResume(bus);
Lei YUf3ce4332019-02-21 14:09:49 +0800245}
246
247bool ItemUpdaterUbi::isVersionFunctional(const std::string& versionId)
248{
249 if (!fs::exists(PNOR_RO_ACTIVE_PATH))
250 {
251 return false;
252 }
253
254 fs::path activeRO = fs::read_symlink(PNOR_RO_ACTIVE_PATH);
255
256 if (!fs::is_directory(activeRO))
257 {
258 return false;
259 }
260
261 if (activeRO.string().find(versionId) == std::string::npos)
262 {
263 return false;
264 }
265
266 // active PNOR is the version we're checking
267 return true;
268}
269
270void ItemUpdaterUbi::freePriority(uint8_t value, const std::string& versionId)
271{
Adriana Kobylakabe862a2019-07-17 16:09:00 -0500272 // Versions with the lowest priority in front
273 std::priority_queue<std::pair<int, std::string>,
274 std::vector<std::pair<int, std::string>>,
275 std::greater<std::pair<int, std::string>>>
276 versionsPQ;
277
Lei YUf3ce4332019-02-21 14:09:49 +0800278 for (const auto& intf : activations)
279 {
280 if (intf.second->redundancyPriority)
281 {
Adriana Kobylakabe862a2019-07-17 16:09:00 -0500282 versionsPQ.push(std::make_pair(
283 intf.second->redundancyPriority.get()->priority(),
284 intf.second->versionId));
Lei YUf3ce4332019-02-21 14:09:49 +0800285 }
286 }
Adriana Kobylakabe862a2019-07-17 16:09:00 -0500287
288 while (!versionsPQ.empty())
289 {
290 if (versionsPQ.top().first == value &&
291 versionsPQ.top().second != versionId)
292 {
293 // Increase priority by 1 and update its value
294 ++value;
295 storeToFile(versionsPQ.top().second, value);
296 auto it = activations.find(versionsPQ.top().second);
297 it->second->redundancyPriority.get()->sdbusplus::xyz::
298 openbmc_project::Software::server::RedundancyPriority::priority(
299 value);
300 }
301 versionsPQ.pop();
302 }
Lei YUf3ce4332019-02-21 14:09:49 +0800303}
304
Lei YUf3ce4332019-02-21 14:09:49 +0800305bool ItemUpdaterUbi::erase(std::string entryId)
306{
307 if (!ItemUpdater::erase(entryId))
308 {
309 return false;
310 }
311
312 // Remove priority persistence file
313 removeFile(entryId);
314
315 // Removing read-only and read-write partitions
316 removeReadWritePartition(entryId);
317 removeReadOnlyPartition(entryId);
318
319 return true;
320}
321
322void ItemUpdaterUbi::deleteAll()
323{
324 auto chassisOn = isChassisOn();
325
326 for (const auto& activationIt : activations)
327 {
328 if (isVersionFunctional(activationIt.first) && chassisOn)
329 {
330 continue;
331 }
332 else
333 {
334 ItemUpdaterUbi::erase(activationIt.first);
335 }
336 }
337
338 // Remove any remaining pnor-ro- or pnor-rw- volumes that do not match
339 // the current version.
340 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
341 SYSTEMD_INTERFACE, "StartUnit");
342 method.append("obmc-flash-bios-cleanup.service", "replace");
343 bus.call_noreply(method);
344}
345
346// TODO: openbmc/openbmc#1402 Monitor flash usage
Lei YU6da3dae2019-02-28 14:26:37 +0800347bool ItemUpdaterUbi::freeSpace()
Lei YUf3ce4332019-02-21 14:09:49 +0800348{
Lei YU6da3dae2019-02-28 14:26:37 +0800349 bool isSpaceFreed = false;
Lei YUf3ce4332019-02-21 14:09:49 +0800350 // Versions with the highest priority in front
351 std::priority_queue<std::pair<int, std::string>,
352 std::vector<std::pair<int, std::string>>,
353 std::less<std::pair<int, std::string>>>
354 versionsPQ;
355
356 std::size_t count = 0;
357 for (const auto& iter : activations)
358 {
359 if (iter.second.get()->activation() ==
360 server::Activation::Activations::Active)
361 {
362 count++;
363 // Don't put the functional version on the queue since we can't
364 // remove the "running" PNOR version if it allows multiple PNORs
365 // But removing functional version if there is only one PNOR.
366 if (ACTIVE_PNOR_MAX_ALLOWED > 1 &&
367 isVersionFunctional(iter.second->versionId))
368 {
369 continue;
370 }
371 versionsPQ.push(std::make_pair(
372 iter.second->redundancyPriority.get()->priority(),
373 iter.second->versionId));
374 }
375 }
376
377 // If the number of PNOR versions is over ACTIVE_PNOR_MAX_ALLOWED -1,
378 // remove the highest priority one(s).
379 while ((count >= ACTIVE_PNOR_MAX_ALLOWED) && (!versionsPQ.empty()))
380 {
381 erase(versionsPQ.top().second);
382 versionsPQ.pop();
383 count--;
Lei YU6da3dae2019-02-28 14:26:37 +0800384 isSpaceFreed = true;
Lei YUf3ce4332019-02-21 14:09:49 +0800385 }
Lei YU6da3dae2019-02-28 14:26:37 +0800386 return isSpaceFreed;
Lei YUf3ce4332019-02-21 14:09:49 +0800387}
388
Lei YUbee51402019-02-26 11:36:34 +0800389std::string ItemUpdaterUbi::determineId(const std::string& symlinkPath)
Lei YUf3ce4332019-02-21 14:09:49 +0800390{
391 if (!fs::exists(symlinkPath))
392 {
393 return {};
394 }
395
396 auto target = fs::canonical(symlinkPath).string();
397
398 // check to make sure the target really exists
399 if (!fs::is_regular_file(target + "/" + PNOR_TOC_FILE))
400 {
401 return {};
402 }
403 // Get the image <id> from the symlink target
404 // for example /media/ro-2a1022fe
405 static const auto PNOR_RO_PREFIX_LEN = strlen(PNOR_RO_PREFIX);
406 return target.substr(PNOR_RO_PREFIX_LEN);
407}
408
Lei YU716de5b2019-03-01 16:03:53 +0800409void GardResetUbi::reset()
Lei YUf3ce4332019-02-21 14:09:49 +0800410{
411 // The GARD partition is currently misspelled "GUARD." This file path will
412 // need to be updated in the future.
413 auto path = fs::path(PNOR_PRSV_ACTIVE_PATH);
414 path /= "GUARD";
Lei YUf3ce4332019-02-21 14:09:49 +0800415
Lei YUe4994462019-03-14 14:41:53 +0800416 utils::hiomapdSuspend(bus);
Lei YUf3ce4332019-02-21 14:09:49 +0800417
418 if (fs::is_regular_file(path))
419 {
420 fs::remove(path);
421 }
422
Lei YUe4994462019-03-14 14:41:53 +0800423 utils::hiomapdResume(bus);
Lei YUf3ce4332019-02-21 14:09:49 +0800424}
425
426} // namespace updater
427} // namespace software
428} // namespace openpower