blob: 7b7995ea2de95422ec613c651d6b5ff8187a9278 [file] [log] [blame]
Saqib Khan7254f0e2017-04-10 21:45:37 -05001#include <string>
Adriana Kobylak5ba6b102017-05-19 09:41:27 -05002#include <experimental/filesystem>
Saqib Khan7254f0e2017-04-10 21:45:37 -05003#include <fstream>
Gunnar Millsfedbf3d2018-01-17 11:17:31 -06004#include <queue>
Eddie James13fc66a2017-08-31 15:36:44 -05005#include <phosphor-logging/elog-errors.hpp>
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -05006#include <phosphor-logging/log.hpp>
Eddie James13fc66a2017-08-31 15:36:44 -05007#include "xyz/openbmc_project/Common/error.hpp"
Adriana Kobylakd6a549e2017-05-10 16:23:01 -05008#include <xyz/openbmc_project/Software/Version/server.hpp>
Saqib Khan167601b2017-06-18 23:33:46 -05009#include "version.hpp"
Adriana Kobylak2d8fa222017-03-15 12:34:32 -050010#include "config.h"
11#include "item_updater.hpp"
Saqib Khana8ade7e2017-04-12 10:27:56 -050012#include "activation.hpp"
Michael Tritz60bc20f2017-07-29 23:32:21 -050013#include "serialize.hpp"
Adriana Kobylak2d8fa222017-03-15 12:34:32 -050014
15namespace openpower
16{
17namespace software
18{
Adriana Kobylakbefe5ce2017-04-05 15:57:44 -050019namespace updater
Adriana Kobylak2d8fa222017-03-15 12:34:32 -050020{
21
Saqib Khana8ade7e2017-04-12 10:27:56 -050022// When you see server:: you know we're referencing our base class
23namespace server = sdbusplus::xyz::openbmc_project::Software::server;
Adriana Kobylak5ba6b102017-05-19 09:41:27 -050024namespace fs = std::experimental::filesystem;
Saqib Khana8ade7e2017-04-12 10:27:56 -050025
Eddie James13fc66a2017-08-31 15:36:44 -050026using namespace sdbusplus::xyz::openbmc_project::Common::Error;
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -050027using namespace phosphor::logging;
28
Saqib Khana8ade7e2017-04-12 10:27:56 -050029constexpr auto squashFSImage = "pnor.xz.squashfs";
30
Michael Tritzb541f1b2017-10-15 15:10:21 -050031// TODO: Change paths once openbmc/openbmc#1663 is completed.
32constexpr auto MBOXD_INTERFACE = "org.openbmc.mboxd";
33constexpr auto MBOXD_PATH = "/org/openbmc/mboxd";
34
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050035void ItemUpdater::createActivation(sdbusplus::message::message& m)
Adriana Kobylak2d8fa222017-03-15 12:34:32 -050036{
Patrick Williamse4290942017-06-16 05:43:08 -050037 using SVersion = server::Version;
38 using VersionPurpose = SVersion::VersionPurpose;
39 namespace msg = sdbusplus::message;
40 namespace variant_ns = msg::variant_ns;
41
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050042 sdbusplus::message::object_path objPath;
43 std::map<std::string,
Patrick Williamse4290942017-06-16 05:43:08 -050044 std::map<std::string, msg::variant<std::string>>> interfaces;
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050045 m.read(objPath, interfaces);
Patrick Williamse4290942017-06-16 05:43:08 -050046
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050047 std::string path(std::move(objPath));
Adriana Kobylak5ba6b102017-05-19 09:41:27 -050048 std::string filePath;
Patrick Williamse4290942017-06-16 05:43:08 -050049 auto purpose = VersionPurpose::Unknown;
Saqib Khance148702017-06-11 12:01:58 -050050 std::string version;
Adriana Kobylak5ba6b102017-05-19 09:41:27 -050051
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050052 for (const auto& intf : interfaces)
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -050053 {
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050054 if (intf.first == VERSION_IFACE)
55 {
56 for (const auto& property : intf.second)
57 {
58 if (property.first == "Purpose")
59 {
60 // Only process the Host and System images
Patrick Williamse4290942017-06-16 05:43:08 -050061 auto value = SVersion::convertVersionPurposeFromString(
62 variant_ns::get<std::string>(property.second));
63
64 if (value == VersionPurpose::Host ||
65 value == VersionPurpose::System)
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050066 {
Saqib Khance148702017-06-11 12:01:58 -050067 purpose = value;
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050068 }
69 }
Saqib Khance148702017-06-11 12:01:58 -050070 else if (property.first == "Version")
71 {
Patrick Williamse4290942017-06-16 05:43:08 -050072 version = variant_ns::get<std::string>(property.second);
Saqib Khance148702017-06-11 12:01:58 -050073 }
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050074 }
75 }
Adriana Kobylak5ba6b102017-05-19 09:41:27 -050076 else if (intf.first == FILEPATH_IFACE)
77 {
78 for (const auto& property : intf.second)
79 {
80 if (property.first == "Path")
81 {
Patrick Williamse4290942017-06-16 05:43:08 -050082 filePath = variant_ns::get<std::string>(property.second);
Adriana Kobylak5ba6b102017-05-19 09:41:27 -050083 }
84 }
85 }
86 }
Patrick Williamse4290942017-06-16 05:43:08 -050087 if ((filePath.empty()) || (purpose == VersionPurpose::Unknown))
Adriana Kobylak5ba6b102017-05-19 09:41:27 -050088 {
89 return;
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -050090 }
91
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050092 // Version id is the last item in the path
93 auto pos = path.rfind("/");
94 if (pos == std::string::npos)
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -050095 {
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050096 log<level::ERR>("No version id found in object path",
97 entry("OBJPATH=%s", path));
98 return;
99 }
100
101 auto versionId = path.substr(pos + 1);
102
103 if (activations.find(versionId) == activations.end())
104 {
105 // Determine the Activation state by processing the given image dir.
106 auto activationState = server::Activation::Activations::Invalid;
Gunnar Mills3588acc2017-09-07 13:13:22 -0500107 AssociationList associations = {};
Adriana Kobylak5ba6b102017-05-19 09:41:27 -0500108 if (ItemUpdater::validateSquashFSImage(filePath) == 0)
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -0500109 {
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500110 activationState = server::Activation::Activations::Ready;
Gunnar Mills3588acc2017-09-07 13:13:22 -0500111 // Create an association to the host inventory item
112 associations.emplace_back(std::make_tuple(
113 ACTIVATION_FWD_ASSOCIATION,
114 ACTIVATION_REV_ASSOCIATION,
115 HOST_INVENTORY_PATH));
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -0500116 }
117
Adriana Kobylak5ba6b102017-05-19 09:41:27 -0500118 fs::path manifestPath(filePath);
119 manifestPath /= MANIFEST_FILE;
Saqib Khan167601b2017-06-18 23:33:46 -0500120 std::string extendedVersion = (Version::getValue(manifestPath.string(),
121 std::map<std::string, std::string>
122 {{"extended_version", ""}})).begin()->second;
Gunnar Mills3edb84b2017-08-18 15:13:15 -0500123
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500124 activations.insert(std::make_pair(
125 versionId,
126 std::make_unique<Activation>(
127 bus,
128 path,
Saqib Khan81bac882017-06-08 12:17:01 -0500129 *this,
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500130 versionId,
131 extendedVersion,
Gunnar Mills3edb84b2017-08-18 15:13:15 -0500132 activationState,
133 associations)));
Michael Tritz5b756512017-10-06 16:52:01 -0500134
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500135 auto versionPtr = std::make_unique<Version>(
136 bus,
137 path,
138 *this,
139 versionId,
140 version,
141 purpose,
142 filePath,
143 std::bind(&ItemUpdater::erase,
144 this,
145 std::placeholders::_1));
146 versionPtr->deleteObject =
147 std::make_unique<Delete>(bus, path, *versionPtr);
148 versions.insert(std::make_pair(versionId, std::move(versionPtr)));
Saqib Khan00044f42017-07-10 17:24:43 -0500149 }
Patrick Williams3accb322017-05-30 16:29:52 -0500150 return;
Adriana Kobylak2d8fa222017-03-15 12:34:32 -0500151}
152
Saqib Khan167601b2017-06-18 23:33:46 -0500153void ItemUpdater::processPNORImage()
Saqib Khan7254f0e2017-04-10 21:45:37 -0500154{
Saqib Khan4c5d7442017-07-18 00:43:52 -0500155 // Read pnor.toc from folders under /media/
156 // to get Active Software Versions.
Gunnar Mills3fa70282017-08-18 15:30:42 -0500157 for (const auto& iter : fs::directory_iterator(MEDIA_DIR))
Saqib Khan4c5d7442017-07-18 00:43:52 -0500158 {
159 auto activationState = server::Activation::Activations::Active;
160
161 static const auto PNOR_RO_PREFIX_LEN = strlen(PNOR_RO_PREFIX);
Saqib Khan2be9ba92017-09-26 22:44:10 -0500162 static const auto PNOR_RW_PREFIX_LEN = strlen(PNOR_RW_PREFIX);
Saqib Khan4c5d7442017-07-18 00:43:52 -0500163
164 // Check if the PNOR_RO_PREFIX is the prefix of the iter.path
165 if (0 == iter.path().native().compare(0, PNOR_RO_PREFIX_LEN,
166 PNOR_RO_PREFIX))
167 {
Saqib Khan6a522262017-09-26 12:02:50 -0500168 // The versionId is extracted from the path
169 // for example /media/pnor-ro-2a1022fe.
170 auto id = iter.path().native().substr(PNOR_RO_PREFIX_LEN);
Saqib Khan4c5d7442017-07-18 00:43:52 -0500171 auto pnorTOC = iter.path() / PNOR_TOC_FILE;
172 if (!fs::is_regular_file(pnorTOC))
173 {
Gunnar Mills850d5f62017-10-19 17:04:38 -0500174 log<level::ERR>("Failed to read pnorTOC.",
175 entry("FILENAME=%s", pnorTOC.string()));
Saqib Khan6a522262017-09-26 12:02:50 -0500176 ItemUpdater::erase(id);
177 continue;
Saqib Khan4c5d7442017-07-18 00:43:52 -0500178 }
179 auto keyValues =
180 Version::getValue(pnorTOC,
181 {{ "version", "" },
182 { "extended_version", "" } });
183 auto& version = keyValues.at("version");
184 if (version.empty())
185 {
186 log<level::ERR>("Failed to read version from pnorTOC",
187 entry("FILENAME=%s", pnorTOC.string()));
188 activationState = server::Activation::Activations::Invalid;
189 }
190
191 auto& extendedVersion = keyValues.at("extended_version");
192 if (extendedVersion.empty())
193 {
194 log<level::ERR>("Failed to read extendedVersion from pnorTOC",
195 entry("FILENAME=%s", pnorTOC.string()));
196 activationState = server::Activation::Activations::Invalid;
197 }
198
Saqib Khan4c5d7442017-07-18 00:43:52 -0500199 auto purpose = server::Version::VersionPurpose::Host;
200 auto path = fs::path(SOFTWARE_OBJPATH) / id;
Gunnar Mills3588acc2017-09-07 13:13:22 -0500201 AssociationList associations = {};
Saqib Khan4c5d7442017-07-18 00:43:52 -0500202
Gunnar Mills3588acc2017-09-07 13:13:22 -0500203 if (activationState == server::Activation::Activations::Active)
204 {
205 // Create an association to the host inventory item
206 associations.emplace_back(std::make_tuple(
207 ACTIVATION_FWD_ASSOCIATION,
208 ACTIVATION_REV_ASSOCIATION,
209 HOST_INVENTORY_PATH));
Gunnar Mills3edb84b2017-08-18 15:13:15 -0500210
Gunnar Mills3588acc2017-09-07 13:13:22 -0500211 // Create an active association since this image is active
212 createActiveAssociation(path);
213 }
Gunnar Mills3edb84b2017-08-18 15:13:15 -0500214
Saqib Khan4c5d7442017-07-18 00:43:52 -0500215 // Create Activation instance for this version.
216 activations.insert(std::make_pair(
217 id,
218 std::make_unique<Activation>(
219 bus,
220 path,
221 *this,
222 id,
223 extendedVersion,
Gunnar Mills3edb84b2017-08-18 15:13:15 -0500224 activationState,
225 associations)));
Saqib Khan4c5d7442017-07-18 00:43:52 -0500226
227 // If Active, create RedundancyPriority instance for this version.
228 if (activationState == server::Activation::Activations::Active)
229 {
Michael Tritz36417922017-08-04 14:00:29 -0500230 uint8_t priority = std::numeric_limits<uint8_t>::max();
231 if (!restoreFromFile(id, priority))
Saqib Khan4c5d7442017-07-18 00:43:52 -0500232 {
Michael Tritz36417922017-08-04 14:00:29 -0500233 log<level::ERR>("Unable to restore priority from file.",
Gunnar Mills3fa70282017-08-18 15:30:42 -0500234 entry("VERSIONID=%s", id));
Saqib Khan4c5d7442017-07-18 00:43:52 -0500235 }
Michael Tritz36417922017-08-04 14:00:29 -0500236 activations.find(id)->second->redundancyPriority =
237 std::make_unique<RedundancyPriority>(
238 bus,
239 path,
240 *(activations.find(id)->second),
241 priority);
Saqib Khan4c5d7442017-07-18 00:43:52 -0500242 }
243
244 // Create Version instance for this version.
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500245 auto versionPtr = std::make_unique<Version>(
246 bus,
247 path,
248 *this,
249 id,
250 version,
251 purpose,
252 "",
253 std::bind(&ItemUpdater::erase,
254 this,
255 std::placeholders::_1));
256 versionPtr->deleteObject =
257 std::make_unique<Delete>(bus, path, *versionPtr);
Saqib Khan4c5d7442017-07-18 00:43:52 -0500258 versions.insert(std::make_pair(
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500259 id,
260 std::move(versionPtr)));
Saqib Khan4c5d7442017-07-18 00:43:52 -0500261 }
Saqib Khan2be9ba92017-09-26 22:44:10 -0500262 else if (0 == iter.path().native().compare(0, PNOR_RW_PREFIX_LEN,
263 PNOR_RW_PREFIX))
264 {
265 auto id = iter.path().native().substr(PNOR_RW_PREFIX_LEN);
266 auto roDir = PNOR_RO_PREFIX + id;
267 if (!fs::is_directory(roDir))
268 {
269 log<level::ERR>("No corresponding read-only volume found.",
270 entry("DIRNAME=%s", roDir));
271 ItemUpdater::erase(id);
272 }
273 }
Saqib Khan4c5d7442017-07-18 00:43:52 -0500274 }
Gunnar Mills2badd7a2017-09-20 12:51:28 -0500275
276 // Look at the RO symlink to determine if there is a functional image
277 auto id = determineId(PNOR_RO_ACTIVE_PATH);
278 if (!id.empty())
279 {
280 updateFunctionalAssociation(std::string{SOFTWARE_OBJPATH} + '/' + id);
281 }
Saqib Khan167601b2017-06-18 23:33:46 -0500282 return;
Saqib Khan7254f0e2017-04-10 21:45:37 -0500283}
284
Adriana Kobylak5ba6b102017-05-19 09:41:27 -0500285int ItemUpdater::validateSquashFSImage(const std::string& filePath)
Saqib Khana8ade7e2017-04-12 10:27:56 -0500286{
Saqib Khan4c5d7442017-07-18 00:43:52 -0500287 auto file = fs::path(filePath) / squashFSImage;
288 if (fs::is_regular_file(file))
Saqib Khana8ade7e2017-04-12 10:27:56 -0500289 {
290 return 0;
291 }
292 else
293 {
294 log<level::ERR>("Failed to find the SquashFS image.");
295 return -1;
296 }
297}
298
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500299void ItemUpdater::removeReadOnlyPartition(std::string versionId)
Michael Tritzdd961b62017-05-17 14:07:03 -0500300{
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500301 auto serviceFile = "obmc-flash-bios-ubiumount-ro@" + versionId +
302 ".service";
303
304 // Remove the read-only partitions.
305 auto method = bus.new_method_call(
306 SYSTEMD_BUSNAME,
307 SYSTEMD_PATH,
308 SYSTEMD_INTERFACE,
309 "StartUnit");
310 method.append(serviceFile, "replace");
311 bus.call_noreply(method);
312}
313
314void ItemUpdater::removeReadWritePartition(std::string versionId)
315{
316 auto serviceFile = "obmc-flash-bios-ubiumount-rw@" + versionId +
Michael Tritzdd961b62017-05-17 14:07:03 -0500317 ".service";
318
319 // Remove the read-write partitions.
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500320 auto method = bus.new_method_call(
Michael Tritzdd961b62017-05-17 14:07:03 -0500321 SYSTEMD_BUSNAME,
322 SYSTEMD_PATH,
323 SYSTEMD_INTERFACE,
324 "StartUnit");
325 method.append(serviceFile, "replace");
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500326 bus.call_noreply(method);
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500327}
Michael Tritzdd961b62017-05-17 14:07:03 -0500328
Michael Tritzfa7aa122017-09-22 15:16:11 -0500329void ItemUpdater::reset()
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500330{
Michael Tritzbb31f022017-11-22 11:51:29 -0600331 constexpr static auto patchDir = "/usr/local/share/pnor";
332 if (fs::is_directory(patchDir))
333 {
334 for (const auto& iter : fs::directory_iterator(patchDir))
335 {
336 fs::remove_all(iter);
337 }
338 }
339
Michael Tritzfa7aa122017-09-22 15:16:11 -0500340 for (const auto& it : activations)
341 {
342 auto serviceFile = "obmc-flash-bios-ubiclear@pnor-rw-" + it.first +
343 ".service";
344
345 // Clear the read-write partitions.
346 auto method = bus.new_method_call(
347 SYSTEMD_BUSNAME,
348 SYSTEMD_PATH,
349 SYSTEMD_INTERFACE,
350 "StartUnit");
351 method.append(serviceFile, "replace");
352 auto reply = bus.call(method);
353
354 if (reply.is_method_error())
355 {
Marri Devender Rao795d9842017-11-07 22:52:07 -0600356 log<level::ERR>("Failed to clear read-write partitions",
357 entry("SERVICE_FILE=%s", serviceFile));
Michael Tritzfa7aa122017-09-22 15:16:11 -0500358 elog<InternalFailure>();
359 }
Michael Tritzfa7aa122017-09-22 15:16:11 -0500360 }
Marri Devender Rao795d9842017-11-07 22:52:07 -0600361 static constexpr auto serviceFile =
362 "obmc-flash-bios-ubiclear@pnor-prsv.service";
Michael Tritzfa7aa122017-09-22 15:16:11 -0500363 // Clear the preserved partition.
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500364 auto method = bus.new_method_call(
Michael Tritzdd961b62017-05-17 14:07:03 -0500365 SYSTEMD_BUSNAME,
366 SYSTEMD_PATH,
367 SYSTEMD_INTERFACE,
368 "StartUnit");
Marri Devender Rao795d9842017-11-07 22:52:07 -0600369 method.append(serviceFile, "replace");
Michael Tritzfa7aa122017-09-22 15:16:11 -0500370 auto reply = bus.call(method);
Michael Tritzdd961b62017-05-17 14:07:03 -0500371
Michael Tritzfa7aa122017-09-22 15:16:11 -0500372 if (reply.is_method_error())
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500373 {
Marri Devender Rao795d9842017-11-07 22:52:07 -0600374 log<level::ERR>("Failed to clear preserved partition",
375 entry("SERVICE_FILE=%s", serviceFile));
Michael Tritzfa7aa122017-09-22 15:16:11 -0500376 elog<InternalFailure>();
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500377 }
Michael Tritzfa7aa122017-09-22 15:16:11 -0500378
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500379 return;
380}
381
Michael Tritz5b756512017-10-06 16:52:01 -0500382bool ItemUpdater::isVersionFunctional(const std::string& versionId)
Eddie James13fc66a2017-08-31 15:36:44 -0500383{
384 if (!fs::exists(PNOR_RO_ACTIVE_PATH))
385 {
386 return false;
387 }
388
389 fs::path activeRO = fs::read_symlink(PNOR_RO_ACTIVE_PATH);
390
391 if (!fs::is_directory(activeRO))
392 {
393 return false;
394 }
395
396 if (activeRO.string().find(versionId) == std::string::npos)
397 {
398 return false;
399 }
400
401 // active PNOR is the version we're checking
402 return true;
403}
404
405bool ItemUpdater::isChassisOn()
406{
407 auto mapperCall = bus.new_method_call(
408 MAPPER_BUSNAME,
409 MAPPER_PATH,
410 MAPPER_INTERFACE,
411 "GetObject");
412
413 mapperCall.append(CHASSIS_STATE_PATH,
414 std::vector<std::string>({CHASSIS_STATE_OBJ}));
415 auto mapperResponseMsg = bus.call(mapperCall);
416 if (mapperResponseMsg.is_method_error())
417 {
418 log<level::ERR>("Error in Mapper call");
419 elog<InternalFailure>();
420 }
421 using MapperResponseType = std::map<std::string, std::vector<std::string>>;
422 MapperResponseType mapperResponse;
423 mapperResponseMsg.read(mapperResponse);
424 if (mapperResponse.empty())
425 {
426 log<level::ERR>("Invalid Response from mapper");
427 elog<InternalFailure>();
428 }
429
430 auto method = bus.new_method_call((mapperResponse.begin()->first).c_str(),
431 CHASSIS_STATE_PATH,
432 SYSTEMD_PROPERTY_INTERFACE,
433 "Get");
434 method.append(CHASSIS_STATE_OBJ, "CurrentPowerState");
435 auto response = bus.call(method);
436 if (response.is_method_error())
437 {
438 log<level::ERR>("Error in fetching current Chassis State",
Gunnar Mills850d5f62017-10-19 17:04:38 -0500439 entry("MAPPERRESPONSE=%s",
Eddie James13fc66a2017-08-31 15:36:44 -0500440 (mapperResponse.begin()->first).c_str()));
441 elog<InternalFailure>();
442 }
443 sdbusplus::message::variant<std::string> currentChassisState;
444 response.read(currentChassisState);
445 auto strParam =
446 sdbusplus::message::variant_ns::get<std::string>(currentChassisState);
447 return (strParam != CHASSIS_STATE_OFF);
448}
449
Saqib Khanb8e7f312017-08-12 10:24:10 -0500450void ItemUpdater::freePriority(uint8_t value, const std::string& versionId)
Saqib Khan81bac882017-06-08 12:17:01 -0500451{
452 //TODO openbmc/openbmc#1896 Improve the performance of this function
453 for (const auto& intf : activations)
454 {
Gunnar Mills3fa70282017-08-18 15:30:42 -0500455 if (intf.second->redundancyPriority)
Saqib Khan81bac882017-06-08 12:17:01 -0500456 {
Saqib Khanb8e7f312017-08-12 10:24:10 -0500457 if (intf.second->redundancyPriority.get()->priority() == value &&
458 intf.second->versionId != versionId)
Saqib Khan81bac882017-06-08 12:17:01 -0500459 {
Gunnar Mills3fa70282017-08-18 15:30:42 -0500460 intf.second->redundancyPriority.get()->priority(value + 1);
Saqib Khan81bac882017-06-08 12:17:01 -0500461 }
462 }
463 }
464}
465
Saqib Khan2af5c492017-07-17 16:15:13 -0500466bool ItemUpdater::isLowestPriority(uint8_t value)
467{
468 for (const auto& intf : activations)
469 {
Gunnar Mills3fa70282017-08-18 15:30:42 -0500470 if (intf.second->redundancyPriority)
Saqib Khan2af5c492017-07-17 16:15:13 -0500471 {
472 if (intf.second->redundancyPriority.get()->priority() < value)
473 {
474 return false;
475 }
476 }
477 }
478 return true;
479}
480
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500481void ItemUpdater::erase(std::string entryId)
482{
Eddie James13fc66a2017-08-31 15:36:44 -0500483 if (isVersionFunctional(entryId) && isChassisOn()) {
484 log<level::ERR>(("Error: Version " + entryId + \
485 " is currently active and running on the host." \
486 " Unable to remove.").c_str());
487 return;
488 }
Saqib Khanef8cd9f2017-08-16 14:20:30 -0500489 // Remove priority persistence file
490 removeFile(entryId);
491
Saqib Khan1e0aa5c2017-08-31 11:04:17 -0500492 // Removing read-only and read-write partitions
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500493 removeReadWritePartition(entryId);
494 removeReadOnlyPartition(entryId);
495
496 // Removing entry in versions map
497 auto it = versions.find(entryId);
498 if (it == versions.end())
499 {
500 log<level::ERR>(("Error: Failed to find version " + entryId + \
Gunnar Mills3fa70282017-08-18 15:30:42 -0500501 " in item updater versions map." \
502 " Unable to remove.").c_str());
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500503 }
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500504 else
505 {
506 versions.erase(entryId);
507 }
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500508
509 // Removing entry in activations map
510 auto ita = activations.find(entryId);
511 if (ita == activations.end())
512 {
513 log<level::ERR>(("Error: Failed to find version " + entryId + \
Gunnar Mills3fa70282017-08-18 15:30:42 -0500514 " in item updater activations map." \
515 " Unable to remove.").c_str());
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500516 }
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500517 else
518 {
519 activations.erase(entryId);
520 }
521 return;
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500522}
523
Michael Tritz234a07e2017-09-21 00:53:06 -0500524void ItemUpdater::deleteAll()
525{
Michael Tritz234a07e2017-09-21 00:53:06 -0500526 for (const auto& activationIt : activations)
527 {
528 if (!isVersionFunctional(activationIt.first))
529 {
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500530 ItemUpdater::erase(activationIt.first);
Michael Tritz234a07e2017-09-21 00:53:06 -0500531 }
532 }
533
Michael Tritz234a07e2017-09-21 00:53:06 -0500534 // Remove any remaining pnor-ro- or pnor-rw- volumes that do not match
535 // the current version.
536 auto method = bus.new_method_call(
537 SYSTEMD_BUSNAME,
538 SYSTEMD_PATH,
539 SYSTEMD_INTERFACE,
540 "StartUnit");
541 method.append("obmc-flash-bios-cleanup.service", "replace");
542 bus.call_noreply(method);
543}
544
Saqib Khan2cbfa032017-08-17 14:52:37 -0500545// TODO: openbmc/openbmc#1402 Monitor flash usage
546void ItemUpdater::freeSpace()
547{
Gunnar Millsfedbf3d2018-01-17 11:17:31 -0600548 // Versions with the highest priority in front
549 std::priority_queue<std::pair<int, std::string>,
550 std::vector<std::pair<int, std::string>>,
551 std::less<std::pair<int, std::string>>> versionsPQ;
552
Saqib Khan2cbfa032017-08-17 14:52:37 -0500553 std::size_t count = 0;
Saqib Khan2cbfa032017-08-17 14:52:37 -0500554 for (const auto& iter : activations)
555 {
556 if (iter.second.get()->activation() == server::Activation::Activations::Active)
557 {
558 count++;
Gunnar Millsfedbf3d2018-01-17 11:17:31 -0600559 // Don't put the functional version on the queue since we can't
560 // remove the "running" PNOR version.
Adriana Kobylakee201a52017-11-09 15:05:04 -0600561 if (isVersionFunctional(iter.second->versionId))
562 {
563 continue;
564 }
Gunnar Millsfedbf3d2018-01-17 11:17:31 -0600565 versionsPQ.push(std::make_pair(
566 iter.second->redundancyPriority.get()->priority(),
567 iter.second->versionId));
Saqib Khan2cbfa032017-08-17 14:52:37 -0500568 }
569 }
Gunnar Millsfedbf3d2018-01-17 11:17:31 -0600570
571 // If the number of PNOR versions is over ACTIVE_PNOR_MAX_ALLOWED -1,
572 // remove the highest priority one(s).
573 while ((count >= ACTIVE_PNOR_MAX_ALLOWED) && (!versionsPQ.empty()))
Saqib Khan2cbfa032017-08-17 14:52:37 -0500574 {
Gunnar Millsfedbf3d2018-01-17 11:17:31 -0600575 erase(versionsPQ.top().second);
576 versionsPQ.pop();
577 count--;
Saqib Khan2cbfa032017-08-17 14:52:37 -0500578 }
579}
580
Gunnar Mills61010b22017-09-20 15:25:26 -0500581void ItemUpdater::createActiveAssociation(const std::string& path)
Gunnar Mills9741cd12017-08-28 15:09:00 -0500582{
583 assocs.emplace_back(std::make_tuple(ACTIVE_FWD_ASSOCIATION,
584 ACTIVE_REV_ASSOCIATION,
585 path));
586 associations(assocs);
587}
588
Gunnar Mills833e4f32017-09-14 12:30:27 -0500589void ItemUpdater::updateFunctionalAssociation(const std::string& path)
590{
591 // remove all functional associations
592 for (auto iter = assocs.begin(); iter != assocs.end();)
593 {
594 if ((std::get<0>(*iter)).compare(FUNCTIONAL_FWD_ASSOCIATION) == 0)
595 {
596 iter = assocs.erase(iter);
597 }
598 else
599 {
600 ++iter;
601 }
602 }
603 assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION,
604 FUNCTIONAL_REV_ASSOCIATION,
605 path));
606 associations(assocs);
607}
608
Gunnar Mills61010b22017-09-20 15:25:26 -0500609void ItemUpdater::removeActiveAssociation(const std::string& path)
Gunnar Mills9741cd12017-08-28 15:09:00 -0500610{
611 for (auto iter = assocs.begin(); iter != assocs.end();)
612 {
Gunnar Mills833e4f32017-09-14 12:30:27 -0500613 if ((std::get<0>(*iter)).compare(ACTIVE_FWD_ASSOCIATION) == 0 &&
614 (std::get<2>(*iter)).compare(path) == 0)
Gunnar Mills9741cd12017-08-28 15:09:00 -0500615 {
616 iter = assocs.erase(iter);
617 associations(assocs);
618 }
619 else
620 {
621 ++iter;
622 }
623 }
624}
625
Gunnar Mills2badd7a2017-09-20 12:51:28 -0500626std::string ItemUpdater::determineId(const std::string& symlinkPath)
627{
628 if (!fs::exists(symlinkPath))
629 {
630 return {};
631 }
632
633 auto target = fs::canonical(symlinkPath).string();
634
635 // check to make sure the target really exists
636 if (!fs::is_regular_file(target + "/" + PNOR_TOC_FILE))
637 {
638 return {};
639 }
640 // Get the image <id> from the symlink target
641 // for example /media/ro-2a1022fe
642 static const auto PNOR_RO_PREFIX_LEN = strlen(PNOR_RO_PREFIX);
643 return target.substr(PNOR_RO_PREFIX_LEN);
644}
645
Michael Tritzb541f1b2017-10-15 15:10:21 -0500646void GardReset::reset()
647{
648 // The GARD partition is currently misspelled "GUARD." This file path will
649 // need to be updated in the future.
650 auto path = fs::path(PNOR_PRSV_ACTIVE_PATH);
651 path /= "GUARD";
652 std::vector<uint8_t> mboxdArgs;
653
654 auto dbusCall = bus.new_method_call(
655 MBOXD_INTERFACE,
656 MBOXD_PATH,
657 MBOXD_INTERFACE,
658 "cmd");
659
660 // Suspend mboxd - no args required.
661 dbusCall.append(static_cast<uint8_t>(3), mboxdArgs);
662
663 auto responseMsg = bus.call(dbusCall);
664 if (responseMsg.is_method_error())
665 {
666 log<level::ERR>("Error in mboxd suspend call");
667 elog<InternalFailure>();
668 }
669
670 if (fs::is_regular_file(path))
671 {
672 fs::remove(path);
673 }
674
675 dbusCall = bus.new_method_call(
676 MBOXD_INTERFACE,
677 MBOXD_PATH,
678 MBOXD_INTERFACE,
679 "cmd");
680
681 // Resume mboxd with arg 1, indicating that the flash is modified.
682 mboxdArgs.push_back(1);
683 dbusCall.append(static_cast<uint8_t>(4), mboxdArgs);
684
685 responseMsg = bus.call(dbusCall);
686 if (responseMsg.is_method_error())
687 {
688 log<level::ERR>("Error in mboxd resume call");
689 elog<InternalFailure>();
690 }
691}
692
Adriana Kobylakbefe5ce2017-04-05 15:57:44 -0500693} // namespace updater
Adriana Kobylak2d8fa222017-03-15 12:34:32 -0500694} // namespace software
695} // namespace openpower