blob: becff4671d67a02270466ad66882f57d5ed6a76c [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>
Eddie James13fc66a2017-08-31 15:36:44 -05004#include <phosphor-logging/elog-errors.hpp>
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -05005#include <phosphor-logging/log.hpp>
Eddie James13fc66a2017-08-31 15:36:44 -05006#include "xyz/openbmc_project/Common/error.hpp"
Adriana Kobylakd6a549e2017-05-10 16:23:01 -05007#include <xyz/openbmc_project/Software/Version/server.hpp>
Saqib Khan167601b2017-06-18 23:33:46 -05008#include "version.hpp"
Adriana Kobylak2d8fa222017-03-15 12:34:32 -05009#include "config.h"
10#include "item_updater.hpp"
Saqib Khana8ade7e2017-04-12 10:27:56 -050011#include "activation.hpp"
Michael Tritz60bc20f2017-07-29 23:32:21 -050012#include "serialize.hpp"
Adriana Kobylak2d8fa222017-03-15 12:34:32 -050013
14namespace openpower
15{
16namespace software
17{
Adriana Kobylakbefe5ce2017-04-05 15:57:44 -050018namespace updater
Adriana Kobylak2d8fa222017-03-15 12:34:32 -050019{
20
Saqib Khana8ade7e2017-04-12 10:27:56 -050021// When you see server:: you know we're referencing our base class
22namespace server = sdbusplus::xyz::openbmc_project::Software::server;
Adriana Kobylak5ba6b102017-05-19 09:41:27 -050023namespace fs = std::experimental::filesystem;
Saqib Khana8ade7e2017-04-12 10:27:56 -050024
Eddie James13fc66a2017-08-31 15:36:44 -050025using namespace sdbusplus::xyz::openbmc_project::Common::Error;
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -050026using namespace phosphor::logging;
27
Saqib Khana8ade7e2017-04-12 10:27:56 -050028constexpr auto squashFSImage = "pnor.xz.squashfs";
29
Michael Tritzb541f1b2017-10-15 15:10:21 -050030// TODO: Change paths once openbmc/openbmc#1663 is completed.
31constexpr auto MBOXD_INTERFACE = "org.openbmc.mboxd";
32constexpr auto MBOXD_PATH = "/org/openbmc/mboxd";
33
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050034void ItemUpdater::createActivation(sdbusplus::message::message& m)
Adriana Kobylak2d8fa222017-03-15 12:34:32 -050035{
Patrick Williamse4290942017-06-16 05:43:08 -050036 using SVersion = server::Version;
37 using VersionPurpose = SVersion::VersionPurpose;
38 namespace msg = sdbusplus::message;
39 namespace variant_ns = msg::variant_ns;
40
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050041 sdbusplus::message::object_path objPath;
42 std::map<std::string,
Patrick Williamse4290942017-06-16 05:43:08 -050043 std::map<std::string, msg::variant<std::string>>> interfaces;
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050044 m.read(objPath, interfaces);
Patrick Williamse4290942017-06-16 05:43:08 -050045
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050046 std::string path(std::move(objPath));
Adriana Kobylak5ba6b102017-05-19 09:41:27 -050047 std::string filePath;
Patrick Williamse4290942017-06-16 05:43:08 -050048 auto purpose = VersionPurpose::Unknown;
Saqib Khance148702017-06-11 12:01:58 -050049 std::string version;
Adriana Kobylak5ba6b102017-05-19 09:41:27 -050050
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050051 for (const auto& intf : interfaces)
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -050052 {
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050053 if (intf.first == VERSION_IFACE)
54 {
55 for (const auto& property : intf.second)
56 {
57 if (property.first == "Purpose")
58 {
59 // Only process the Host and System images
Patrick Williamse4290942017-06-16 05:43:08 -050060 auto value = SVersion::convertVersionPurposeFromString(
61 variant_ns::get<std::string>(property.second));
62
63 if (value == VersionPurpose::Host ||
64 value == VersionPurpose::System)
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050065 {
Saqib Khance148702017-06-11 12:01:58 -050066 purpose = value;
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050067 }
68 }
Saqib Khance148702017-06-11 12:01:58 -050069 else if (property.first == "Version")
70 {
Patrick Williamse4290942017-06-16 05:43:08 -050071 version = variant_ns::get<std::string>(property.second);
Saqib Khance148702017-06-11 12:01:58 -050072 }
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050073 }
74 }
Adriana Kobylak5ba6b102017-05-19 09:41:27 -050075 else if (intf.first == FILEPATH_IFACE)
76 {
77 for (const auto& property : intf.second)
78 {
79 if (property.first == "Path")
80 {
Patrick Williamse4290942017-06-16 05:43:08 -050081 filePath = variant_ns::get<std::string>(property.second);
Adriana Kobylak5ba6b102017-05-19 09:41:27 -050082 }
83 }
84 }
85 }
Patrick Williamse4290942017-06-16 05:43:08 -050086 if ((filePath.empty()) || (purpose == VersionPurpose::Unknown))
Adriana Kobylak5ba6b102017-05-19 09:41:27 -050087 {
88 return;
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -050089 }
90
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050091 // Version id is the last item in the path
92 auto pos = path.rfind("/");
93 if (pos == std::string::npos)
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -050094 {
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050095 log<level::ERR>("No version id found in object path",
96 entry("OBJPATH=%s", path));
97 return;
98 }
99
100 auto versionId = path.substr(pos + 1);
101
102 if (activations.find(versionId) == activations.end())
103 {
104 // Determine the Activation state by processing the given image dir.
105 auto activationState = server::Activation::Activations::Invalid;
Gunnar Mills3588acc2017-09-07 13:13:22 -0500106 AssociationList associations = {};
Adriana Kobylak5ba6b102017-05-19 09:41:27 -0500107 if (ItemUpdater::validateSquashFSImage(filePath) == 0)
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -0500108 {
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500109 activationState = server::Activation::Activations::Ready;
Gunnar Mills3588acc2017-09-07 13:13:22 -0500110 // Create an association to the host inventory item
111 associations.emplace_back(std::make_tuple(
112 ACTIVATION_FWD_ASSOCIATION,
113 ACTIVATION_REV_ASSOCIATION,
114 HOST_INVENTORY_PATH));
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -0500115 }
116
Adriana Kobylak5ba6b102017-05-19 09:41:27 -0500117 fs::path manifestPath(filePath);
118 manifestPath /= MANIFEST_FILE;
Saqib Khan167601b2017-06-18 23:33:46 -0500119 std::string extendedVersion = (Version::getValue(manifestPath.string(),
120 std::map<std::string, std::string>
121 {{"extended_version", ""}})).begin()->second;
Gunnar Mills3edb84b2017-08-18 15:13:15 -0500122
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500123 activations.insert(std::make_pair(
124 versionId,
125 std::make_unique<Activation>(
126 bus,
127 path,
Saqib Khan81bac882017-06-08 12:17:01 -0500128 *this,
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500129 versionId,
130 extendedVersion,
Gunnar Mills3edb84b2017-08-18 15:13:15 -0500131 activationState,
132 associations)));
Michael Tritz5b756512017-10-06 16:52:01 -0500133
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500134 auto versionPtr = std::make_unique<Version>(
135 bus,
136 path,
137 *this,
138 versionId,
139 version,
140 purpose,
141 filePath,
142 std::bind(&ItemUpdater::erase,
143 this,
144 std::placeholders::_1));
145 versionPtr->deleteObject =
146 std::make_unique<Delete>(bus, path, *versionPtr);
147 versions.insert(std::make_pair(versionId, std::move(versionPtr)));
Saqib Khan00044f42017-07-10 17:24:43 -0500148 }
Patrick Williams3accb322017-05-30 16:29:52 -0500149 return;
Adriana Kobylak2d8fa222017-03-15 12:34:32 -0500150}
151
Saqib Khan167601b2017-06-18 23:33:46 -0500152void ItemUpdater::processPNORImage()
Saqib Khan7254f0e2017-04-10 21:45:37 -0500153{
Saqib Khan4c5d7442017-07-18 00:43:52 -0500154 // Read pnor.toc from folders under /media/
155 // to get Active Software Versions.
Gunnar Mills3fa70282017-08-18 15:30:42 -0500156 for (const auto& iter : fs::directory_iterator(MEDIA_DIR))
Saqib Khan4c5d7442017-07-18 00:43:52 -0500157 {
158 auto activationState = server::Activation::Activations::Active;
159
160 static const auto PNOR_RO_PREFIX_LEN = strlen(PNOR_RO_PREFIX);
Saqib Khan2be9ba92017-09-26 22:44:10 -0500161 static const auto PNOR_RW_PREFIX_LEN = strlen(PNOR_RW_PREFIX);
Saqib Khan4c5d7442017-07-18 00:43:52 -0500162
163 // Check if the PNOR_RO_PREFIX is the prefix of the iter.path
164 if (0 == iter.path().native().compare(0, PNOR_RO_PREFIX_LEN,
165 PNOR_RO_PREFIX))
166 {
Saqib Khan6a522262017-09-26 12:02:50 -0500167 // The versionId is extracted from the path
168 // for example /media/pnor-ro-2a1022fe.
169 auto id = iter.path().native().substr(PNOR_RO_PREFIX_LEN);
Saqib Khan4c5d7442017-07-18 00:43:52 -0500170 auto pnorTOC = iter.path() / PNOR_TOC_FILE;
171 if (!fs::is_regular_file(pnorTOC))
172 {
Gunnar Mills850d5f62017-10-19 17:04:38 -0500173 log<level::ERR>("Failed to read pnorTOC.",
174 entry("FILENAME=%s", pnorTOC.string()));
Saqib Khan6a522262017-09-26 12:02:50 -0500175 ItemUpdater::erase(id);
176 continue;
Saqib Khan4c5d7442017-07-18 00:43:52 -0500177 }
178 auto keyValues =
179 Version::getValue(pnorTOC,
180 {{ "version", "" },
181 { "extended_version", "" } });
182 auto& version = keyValues.at("version");
183 if (version.empty())
184 {
185 log<level::ERR>("Failed to read version from pnorTOC",
186 entry("FILENAME=%s", pnorTOC.string()));
187 activationState = server::Activation::Activations::Invalid;
188 }
189
190 auto& extendedVersion = keyValues.at("extended_version");
191 if (extendedVersion.empty())
192 {
193 log<level::ERR>("Failed to read extendedVersion from pnorTOC",
194 entry("FILENAME=%s", pnorTOC.string()));
195 activationState = server::Activation::Activations::Invalid;
196 }
197
Saqib Khan4c5d7442017-07-18 00:43:52 -0500198 auto purpose = server::Version::VersionPurpose::Host;
199 auto path = fs::path(SOFTWARE_OBJPATH) / id;
Gunnar Mills3588acc2017-09-07 13:13:22 -0500200 AssociationList associations = {};
Saqib Khan4c5d7442017-07-18 00:43:52 -0500201
Gunnar Mills3588acc2017-09-07 13:13:22 -0500202 if (activationState == server::Activation::Activations::Active)
203 {
204 // Create an association to the host inventory item
205 associations.emplace_back(std::make_tuple(
206 ACTIVATION_FWD_ASSOCIATION,
207 ACTIVATION_REV_ASSOCIATION,
208 HOST_INVENTORY_PATH));
Gunnar Mills3edb84b2017-08-18 15:13:15 -0500209
Gunnar Mills3588acc2017-09-07 13:13:22 -0500210 // Create an active association since this image is active
211 createActiveAssociation(path);
212 }
Gunnar Mills3edb84b2017-08-18 15:13:15 -0500213
Saqib Khan4c5d7442017-07-18 00:43:52 -0500214 // Create Activation instance for this version.
215 activations.insert(std::make_pair(
216 id,
217 std::make_unique<Activation>(
218 bus,
219 path,
220 *this,
221 id,
222 extendedVersion,
Gunnar Mills3edb84b2017-08-18 15:13:15 -0500223 activationState,
224 associations)));
Saqib Khan4c5d7442017-07-18 00:43:52 -0500225
226 // If Active, create RedundancyPriority instance for this version.
227 if (activationState == server::Activation::Activations::Active)
228 {
Michael Tritz36417922017-08-04 14:00:29 -0500229 uint8_t priority = std::numeric_limits<uint8_t>::max();
230 if (!restoreFromFile(id, priority))
Saqib Khan4c5d7442017-07-18 00:43:52 -0500231 {
Michael Tritz36417922017-08-04 14:00:29 -0500232 log<level::ERR>("Unable to restore priority from file.",
Gunnar Mills3fa70282017-08-18 15:30:42 -0500233 entry("VERSIONID=%s", id));
Saqib Khan4c5d7442017-07-18 00:43:52 -0500234 }
Michael Tritz36417922017-08-04 14:00:29 -0500235 activations.find(id)->second->redundancyPriority =
236 std::make_unique<RedundancyPriority>(
237 bus,
238 path,
239 *(activations.find(id)->second),
240 priority);
Saqib Khan4c5d7442017-07-18 00:43:52 -0500241 }
242
243 // Create Version instance for this version.
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500244 auto versionPtr = std::make_unique<Version>(
245 bus,
246 path,
247 *this,
248 id,
249 version,
250 purpose,
251 "",
252 std::bind(&ItemUpdater::erase,
253 this,
254 std::placeholders::_1));
255 versionPtr->deleteObject =
256 std::make_unique<Delete>(bus, path, *versionPtr);
Saqib Khan4c5d7442017-07-18 00:43:52 -0500257 versions.insert(std::make_pair(
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500258 id,
259 std::move(versionPtr)));
Saqib Khan4c5d7442017-07-18 00:43:52 -0500260 }
Saqib Khan2be9ba92017-09-26 22:44:10 -0500261 else if (0 == iter.path().native().compare(0, PNOR_RW_PREFIX_LEN,
262 PNOR_RW_PREFIX))
263 {
264 auto id = iter.path().native().substr(PNOR_RW_PREFIX_LEN);
265 auto roDir = PNOR_RO_PREFIX + id;
266 if (!fs::is_directory(roDir))
267 {
268 log<level::ERR>("No corresponding read-only volume found.",
269 entry("DIRNAME=%s", roDir));
270 ItemUpdater::erase(id);
271 }
272 }
Saqib Khan4c5d7442017-07-18 00:43:52 -0500273 }
Gunnar Mills2badd7a2017-09-20 12:51:28 -0500274
275 // Look at the RO symlink to determine if there is a functional image
276 auto id = determineId(PNOR_RO_ACTIVE_PATH);
277 if (!id.empty())
278 {
279 updateFunctionalAssociation(std::string{SOFTWARE_OBJPATH} + '/' + id);
280 }
Saqib Khan167601b2017-06-18 23:33:46 -0500281 return;
Saqib Khan7254f0e2017-04-10 21:45:37 -0500282}
283
Adriana Kobylak5ba6b102017-05-19 09:41:27 -0500284int ItemUpdater::validateSquashFSImage(const std::string& filePath)
Saqib Khana8ade7e2017-04-12 10:27:56 -0500285{
Saqib Khan4c5d7442017-07-18 00:43:52 -0500286 auto file = fs::path(filePath) / squashFSImage;
287 if (fs::is_regular_file(file))
Saqib Khana8ade7e2017-04-12 10:27:56 -0500288 {
289 return 0;
290 }
291 else
292 {
293 log<level::ERR>("Failed to find the SquashFS image.");
294 return -1;
295 }
296}
297
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500298void ItemUpdater::removeReadOnlyPartition(std::string versionId)
Michael Tritzdd961b62017-05-17 14:07:03 -0500299{
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500300 auto serviceFile = "obmc-flash-bios-ubiumount-ro@" + versionId +
301 ".service";
302
303 // Remove the read-only partitions.
304 auto method = bus.new_method_call(
305 SYSTEMD_BUSNAME,
306 SYSTEMD_PATH,
307 SYSTEMD_INTERFACE,
308 "StartUnit");
309 method.append(serviceFile, "replace");
310 bus.call_noreply(method);
311}
312
313void ItemUpdater::removeReadWritePartition(std::string versionId)
314{
315 auto serviceFile = "obmc-flash-bios-ubiumount-rw@" + versionId +
Michael Tritzdd961b62017-05-17 14:07:03 -0500316 ".service";
317
318 // Remove the read-write partitions.
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500319 auto method = bus.new_method_call(
Michael Tritzdd961b62017-05-17 14:07:03 -0500320 SYSTEMD_BUSNAME,
321 SYSTEMD_PATH,
322 SYSTEMD_INTERFACE,
323 "StartUnit");
324 method.append(serviceFile, "replace");
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500325 bus.call_noreply(method);
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500326}
Michael Tritzdd961b62017-05-17 14:07:03 -0500327
Michael Tritzfa7aa122017-09-22 15:16:11 -0500328void ItemUpdater::reset()
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500329{
Michael Tritzbb31f022017-11-22 11:51:29 -0600330 constexpr static auto patchDir = "/usr/local/share/pnor";
331 if (fs::is_directory(patchDir))
332 {
333 for (const auto& iter : fs::directory_iterator(patchDir))
334 {
335 fs::remove_all(iter);
336 }
337 }
338
Michael Tritzfa7aa122017-09-22 15:16:11 -0500339 for (const auto& it : activations)
340 {
341 auto serviceFile = "obmc-flash-bios-ubiclear@pnor-rw-" + it.first +
342 ".service";
343
344 // Clear the read-write partitions.
345 auto method = bus.new_method_call(
346 SYSTEMD_BUSNAME,
347 SYSTEMD_PATH,
348 SYSTEMD_INTERFACE,
349 "StartUnit");
350 method.append(serviceFile, "replace");
351 auto reply = bus.call(method);
352
353 if (reply.is_method_error())
354 {
Marri Devender Rao795d9842017-11-07 22:52:07 -0600355 log<level::ERR>("Failed to clear read-write partitions",
356 entry("SERVICE_FILE=%s", serviceFile));
Michael Tritzfa7aa122017-09-22 15:16:11 -0500357 elog<InternalFailure>();
358 }
359
360 removeFile(it.first);
361 }
Marri Devender Rao795d9842017-11-07 22:52:07 -0600362 static constexpr auto serviceFile =
363 "obmc-flash-bios-ubiclear@pnor-prsv.service";
Michael Tritzfa7aa122017-09-22 15:16:11 -0500364 // Clear the preserved partition.
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500365 auto method = bus.new_method_call(
Michael Tritzdd961b62017-05-17 14:07:03 -0500366 SYSTEMD_BUSNAME,
367 SYSTEMD_PATH,
368 SYSTEMD_INTERFACE,
369 "StartUnit");
Marri Devender Rao795d9842017-11-07 22:52:07 -0600370 method.append(serviceFile, "replace");
Michael Tritzfa7aa122017-09-22 15:16:11 -0500371 auto reply = bus.call(method);
Michael Tritzdd961b62017-05-17 14:07:03 -0500372
Michael Tritzfa7aa122017-09-22 15:16:11 -0500373 if (reply.is_method_error())
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500374 {
Marri Devender Rao795d9842017-11-07 22:52:07 -0600375 log<level::ERR>("Failed to clear preserved partition",
376 entry("SERVICE_FILE=%s", serviceFile));
Michael Tritzfa7aa122017-09-22 15:16:11 -0500377 elog<InternalFailure>();
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500378 }
Michael Tritzfa7aa122017-09-22 15:16:11 -0500379
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500380 return;
381}
382
Michael Tritz5b756512017-10-06 16:52:01 -0500383bool ItemUpdater::isVersionFunctional(const std::string& versionId)
Eddie James13fc66a2017-08-31 15:36:44 -0500384{
385 if (!fs::exists(PNOR_RO_ACTIVE_PATH))
386 {
387 return false;
388 }
389
390 fs::path activeRO = fs::read_symlink(PNOR_RO_ACTIVE_PATH);
391
392 if (!fs::is_directory(activeRO))
393 {
394 return false;
395 }
396
397 if (activeRO.string().find(versionId) == std::string::npos)
398 {
399 return false;
400 }
401
402 // active PNOR is the version we're checking
403 return true;
404}
405
406bool ItemUpdater::isChassisOn()
407{
408 auto mapperCall = bus.new_method_call(
409 MAPPER_BUSNAME,
410 MAPPER_PATH,
411 MAPPER_INTERFACE,
412 "GetObject");
413
414 mapperCall.append(CHASSIS_STATE_PATH,
415 std::vector<std::string>({CHASSIS_STATE_OBJ}));
416 auto mapperResponseMsg = bus.call(mapperCall);
417 if (mapperResponseMsg.is_method_error())
418 {
419 log<level::ERR>("Error in Mapper call");
420 elog<InternalFailure>();
421 }
422 using MapperResponseType = std::map<std::string, std::vector<std::string>>;
423 MapperResponseType mapperResponse;
424 mapperResponseMsg.read(mapperResponse);
425 if (mapperResponse.empty())
426 {
427 log<level::ERR>("Invalid Response from mapper");
428 elog<InternalFailure>();
429 }
430
431 auto method = bus.new_method_call((mapperResponse.begin()->first).c_str(),
432 CHASSIS_STATE_PATH,
433 SYSTEMD_PROPERTY_INTERFACE,
434 "Get");
435 method.append(CHASSIS_STATE_OBJ, "CurrentPowerState");
436 auto response = bus.call(method);
437 if (response.is_method_error())
438 {
439 log<level::ERR>("Error in fetching current Chassis State",
Gunnar Mills850d5f62017-10-19 17:04:38 -0500440 entry("MAPPERRESPONSE=%s",
Eddie James13fc66a2017-08-31 15:36:44 -0500441 (mapperResponse.begin()->first).c_str()));
442 elog<InternalFailure>();
443 }
444 sdbusplus::message::variant<std::string> currentChassisState;
445 response.read(currentChassisState);
446 auto strParam =
447 sdbusplus::message::variant_ns::get<std::string>(currentChassisState);
448 return (strParam != CHASSIS_STATE_OFF);
449}
450
Saqib Khanb8e7f312017-08-12 10:24:10 -0500451void ItemUpdater::freePriority(uint8_t value, const std::string& versionId)
Saqib Khan81bac882017-06-08 12:17:01 -0500452{
453 //TODO openbmc/openbmc#1896 Improve the performance of this function
454 for (const auto& intf : activations)
455 {
Gunnar Mills3fa70282017-08-18 15:30:42 -0500456 if (intf.second->redundancyPriority)
Saqib Khan81bac882017-06-08 12:17:01 -0500457 {
Saqib Khanb8e7f312017-08-12 10:24:10 -0500458 if (intf.second->redundancyPriority.get()->priority() == value &&
459 intf.second->versionId != versionId)
Saqib Khan81bac882017-06-08 12:17:01 -0500460 {
Gunnar Mills3fa70282017-08-18 15:30:42 -0500461 intf.second->redundancyPriority.get()->priority(value + 1);
Saqib Khan81bac882017-06-08 12:17:01 -0500462 }
463 }
464 }
465}
466
Saqib Khan2af5c492017-07-17 16:15:13 -0500467bool ItemUpdater::isLowestPriority(uint8_t value)
468{
469 for (const auto& intf : activations)
470 {
Gunnar Mills3fa70282017-08-18 15:30:42 -0500471 if (intf.second->redundancyPriority)
Saqib Khan2af5c492017-07-17 16:15:13 -0500472 {
473 if (intf.second->redundancyPriority.get()->priority() < value)
474 {
475 return false;
476 }
477 }
478 }
479 return true;
480}
481
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500482void ItemUpdater::erase(std::string entryId)
483{
Eddie James13fc66a2017-08-31 15:36:44 -0500484 if (isVersionFunctional(entryId) && isChassisOn()) {
485 log<level::ERR>(("Error: Version " + entryId + \
486 " is currently active and running on the host." \
487 " Unable to remove.").c_str());
488 return;
489 }
Saqib Khanef8cd9f2017-08-16 14:20:30 -0500490 // Remove priority persistence file
491 removeFile(entryId);
492
Saqib Khan1e0aa5c2017-08-31 11:04:17 -0500493 // Removing read-only and read-write partitions
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500494 removeReadWritePartition(entryId);
495 removeReadOnlyPartition(entryId);
496
497 // Removing entry in versions map
498 auto it = versions.find(entryId);
499 if (it == versions.end())
500 {
501 log<level::ERR>(("Error: Failed to find version " + entryId + \
Gunnar Mills3fa70282017-08-18 15:30:42 -0500502 " in item updater versions map." \
503 " Unable to remove.").c_str());
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500504 }
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500505 else
506 {
507 versions.erase(entryId);
508 }
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500509
510 // Removing entry in activations map
511 auto ita = activations.find(entryId);
512 if (ita == activations.end())
513 {
514 log<level::ERR>(("Error: Failed to find version " + entryId + \
Gunnar Mills3fa70282017-08-18 15:30:42 -0500515 " in item updater activations map." \
516 " Unable to remove.").c_str());
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500517 }
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500518 else
519 {
520 activations.erase(entryId);
521 }
522 return;
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500523}
524
Michael Tritz234a07e2017-09-21 00:53:06 -0500525void ItemUpdater::deleteAll()
526{
Michael Tritz234a07e2017-09-21 00:53:06 -0500527 for (const auto& activationIt : activations)
528 {
529 if (!isVersionFunctional(activationIt.first))
530 {
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500531 ItemUpdater::erase(activationIt.first);
Michael Tritz234a07e2017-09-21 00:53:06 -0500532 }
533 }
534
Michael Tritz234a07e2017-09-21 00:53:06 -0500535 // Remove any remaining pnor-ro- or pnor-rw- volumes that do not match
536 // the current version.
537 auto method = bus.new_method_call(
538 SYSTEMD_BUSNAME,
539 SYSTEMD_PATH,
540 SYSTEMD_INTERFACE,
541 "StartUnit");
542 method.append("obmc-flash-bios-cleanup.service", "replace");
543 bus.call_noreply(method);
544}
545
Saqib Khan2cbfa032017-08-17 14:52:37 -0500546// TODO: openbmc/openbmc#1402 Monitor flash usage
547void ItemUpdater::freeSpace()
548{
549 std::size_t count = 0;
550 decltype(activations.begin()->second->redundancyPriority.get()->priority())
551 highestPriority = 0;
552 decltype(activations.begin()->second->versionId) highestPriorityVersion;
553 for (const auto& iter : activations)
554 {
555 if (iter.second.get()->activation() == server::Activation::Activations::Active)
556 {
557 count++;
Adriana Kobylakee201a52017-11-09 15:05:04 -0600558 if (isVersionFunctional(iter.second->versionId))
559 {
560 continue;
561 }
562 if (iter.second->redundancyPriority.get()->priority() >= highestPriority)
Saqib Khan2cbfa032017-08-17 14:52:37 -0500563 {
564 highestPriority = iter.second->redundancyPriority.get()->priority();
565 highestPriorityVersion = iter.second->versionId;
566 }
567 }
568 }
569 // Remove the pnor version with highest priority since the PNOR
570 // can't hold more than 2 versions.
571 if (count >= ACTIVE_PNOR_MAX_ALLOWED)
572 {
573 erase(highestPriorityVersion);
574 }
575}
576
Gunnar Mills61010b22017-09-20 15:25:26 -0500577void ItemUpdater::createActiveAssociation(const std::string& path)
Gunnar Mills9741cd12017-08-28 15:09:00 -0500578{
579 assocs.emplace_back(std::make_tuple(ACTIVE_FWD_ASSOCIATION,
580 ACTIVE_REV_ASSOCIATION,
581 path));
582 associations(assocs);
583}
584
Gunnar Mills833e4f32017-09-14 12:30:27 -0500585void ItemUpdater::updateFunctionalAssociation(const std::string& path)
586{
587 // remove all functional associations
588 for (auto iter = assocs.begin(); iter != assocs.end();)
589 {
590 if ((std::get<0>(*iter)).compare(FUNCTIONAL_FWD_ASSOCIATION) == 0)
591 {
592 iter = assocs.erase(iter);
593 }
594 else
595 {
596 ++iter;
597 }
598 }
599 assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION,
600 FUNCTIONAL_REV_ASSOCIATION,
601 path));
602 associations(assocs);
603}
604
Gunnar Mills61010b22017-09-20 15:25:26 -0500605void ItemUpdater::removeActiveAssociation(const std::string& path)
Gunnar Mills9741cd12017-08-28 15:09:00 -0500606{
607 for (auto iter = assocs.begin(); iter != assocs.end();)
608 {
Gunnar Mills833e4f32017-09-14 12:30:27 -0500609 if ((std::get<0>(*iter)).compare(ACTIVE_FWD_ASSOCIATION) == 0 &&
610 (std::get<2>(*iter)).compare(path) == 0)
Gunnar Mills9741cd12017-08-28 15:09:00 -0500611 {
612 iter = assocs.erase(iter);
613 associations(assocs);
614 }
615 else
616 {
617 ++iter;
618 }
619 }
620}
621
Gunnar Mills2badd7a2017-09-20 12:51:28 -0500622std::string ItemUpdater::determineId(const std::string& symlinkPath)
623{
624 if (!fs::exists(symlinkPath))
625 {
626 return {};
627 }
628
629 auto target = fs::canonical(symlinkPath).string();
630
631 // check to make sure the target really exists
632 if (!fs::is_regular_file(target + "/" + PNOR_TOC_FILE))
633 {
634 return {};
635 }
636 // Get the image <id> from the symlink target
637 // for example /media/ro-2a1022fe
638 static const auto PNOR_RO_PREFIX_LEN = strlen(PNOR_RO_PREFIX);
639 return target.substr(PNOR_RO_PREFIX_LEN);
640}
641
Michael Tritzb541f1b2017-10-15 15:10:21 -0500642void GardReset::reset()
643{
644 // The GARD partition is currently misspelled "GUARD." This file path will
645 // need to be updated in the future.
646 auto path = fs::path(PNOR_PRSV_ACTIVE_PATH);
647 path /= "GUARD";
648 std::vector<uint8_t> mboxdArgs;
649
650 auto dbusCall = bus.new_method_call(
651 MBOXD_INTERFACE,
652 MBOXD_PATH,
653 MBOXD_INTERFACE,
654 "cmd");
655
656 // Suspend mboxd - no args required.
657 dbusCall.append(static_cast<uint8_t>(3), mboxdArgs);
658
659 auto responseMsg = bus.call(dbusCall);
660 if (responseMsg.is_method_error())
661 {
662 log<level::ERR>("Error in mboxd suspend call");
663 elog<InternalFailure>();
664 }
665
666 if (fs::is_regular_file(path))
667 {
668 fs::remove(path);
669 }
670
671 dbusCall = bus.new_method_call(
672 MBOXD_INTERFACE,
673 MBOXD_PATH,
674 MBOXD_INTERFACE,
675 "cmd");
676
677 // Resume mboxd with arg 1, indicating that the flash is modified.
678 mboxdArgs.push_back(1);
679 dbusCall.append(static_cast<uint8_t>(4), mboxdArgs);
680
681 responseMsg = bus.call(dbusCall);
682 if (responseMsg.is_method_error())
683 {
684 log<level::ERR>("Error in mboxd resume call");
685 elog<InternalFailure>();
686 }
687}
688
Adriana Kobylakbefe5ce2017-04-05 15:57:44 -0500689} // namespace updater
Adriana Kobylak2d8fa222017-03-15 12:34:32 -0500690} // namespace software
691} // namespace openpower