blob: 03ff20b923d797d4289cb6abce680c8563abe569 [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 Tritzfa7aa122017-09-22 15:16:11 -0500330 for (const auto& it : activations)
331 {
332 auto serviceFile = "obmc-flash-bios-ubiclear@pnor-rw-" + it.first +
333 ".service";
334
335 // Clear the read-write partitions.
336 auto method = bus.new_method_call(
337 SYSTEMD_BUSNAME,
338 SYSTEMD_PATH,
339 SYSTEMD_INTERFACE,
340 "StartUnit");
341 method.append(serviceFile, "replace");
342 auto reply = bus.call(method);
343
344 if (reply.is_method_error())
345 {
Marri Devender Rao795d9842017-11-07 22:52:07 -0600346 log<level::ERR>("Failed to clear read-write partitions",
347 entry("SERVICE_FILE=%s", serviceFile));
Michael Tritzfa7aa122017-09-22 15:16:11 -0500348 elog<InternalFailure>();
349 }
350
351 removeFile(it.first);
352 }
Marri Devender Rao795d9842017-11-07 22:52:07 -0600353 static constexpr auto serviceFile =
354 "obmc-flash-bios-ubiclear@pnor-prsv.service";
Michael Tritzfa7aa122017-09-22 15:16:11 -0500355 // Clear the preserved partition.
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500356 auto method = bus.new_method_call(
Michael Tritzdd961b62017-05-17 14:07:03 -0500357 SYSTEMD_BUSNAME,
358 SYSTEMD_PATH,
359 SYSTEMD_INTERFACE,
360 "StartUnit");
Marri Devender Rao795d9842017-11-07 22:52:07 -0600361 method.append(serviceFile, "replace");
Michael Tritzfa7aa122017-09-22 15:16:11 -0500362 auto reply = bus.call(method);
Michael Tritzdd961b62017-05-17 14:07:03 -0500363
Michael Tritzfa7aa122017-09-22 15:16:11 -0500364 if (reply.is_method_error())
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500365 {
Marri Devender Rao795d9842017-11-07 22:52:07 -0600366 log<level::ERR>("Failed to clear preserved partition",
367 entry("SERVICE_FILE=%s", serviceFile));
Michael Tritzfa7aa122017-09-22 15:16:11 -0500368 elog<InternalFailure>();
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500369 }
Michael Tritzfa7aa122017-09-22 15:16:11 -0500370
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500371 return;
372}
373
Michael Tritz5b756512017-10-06 16:52:01 -0500374bool ItemUpdater::isVersionFunctional(const std::string& versionId)
Eddie James13fc66a2017-08-31 15:36:44 -0500375{
376 if (!fs::exists(PNOR_RO_ACTIVE_PATH))
377 {
378 return false;
379 }
380
381 fs::path activeRO = fs::read_symlink(PNOR_RO_ACTIVE_PATH);
382
383 if (!fs::is_directory(activeRO))
384 {
385 return false;
386 }
387
388 if (activeRO.string().find(versionId) == std::string::npos)
389 {
390 return false;
391 }
392
393 // active PNOR is the version we're checking
394 return true;
395}
396
397bool ItemUpdater::isChassisOn()
398{
399 auto mapperCall = bus.new_method_call(
400 MAPPER_BUSNAME,
401 MAPPER_PATH,
402 MAPPER_INTERFACE,
403 "GetObject");
404
405 mapperCall.append(CHASSIS_STATE_PATH,
406 std::vector<std::string>({CHASSIS_STATE_OBJ}));
407 auto mapperResponseMsg = bus.call(mapperCall);
408 if (mapperResponseMsg.is_method_error())
409 {
410 log<level::ERR>("Error in Mapper call");
411 elog<InternalFailure>();
412 }
413 using MapperResponseType = std::map<std::string, std::vector<std::string>>;
414 MapperResponseType mapperResponse;
415 mapperResponseMsg.read(mapperResponse);
416 if (mapperResponse.empty())
417 {
418 log<level::ERR>("Invalid Response from mapper");
419 elog<InternalFailure>();
420 }
421
422 auto method = bus.new_method_call((mapperResponse.begin()->first).c_str(),
423 CHASSIS_STATE_PATH,
424 SYSTEMD_PROPERTY_INTERFACE,
425 "Get");
426 method.append(CHASSIS_STATE_OBJ, "CurrentPowerState");
427 auto response = bus.call(method);
428 if (response.is_method_error())
429 {
430 log<level::ERR>("Error in fetching current Chassis State",
Gunnar Mills850d5f62017-10-19 17:04:38 -0500431 entry("MAPPERRESPONSE=%s",
Eddie James13fc66a2017-08-31 15:36:44 -0500432 (mapperResponse.begin()->first).c_str()));
433 elog<InternalFailure>();
434 }
435 sdbusplus::message::variant<std::string> currentChassisState;
436 response.read(currentChassisState);
437 auto strParam =
438 sdbusplus::message::variant_ns::get<std::string>(currentChassisState);
439 return (strParam != CHASSIS_STATE_OFF);
440}
441
Saqib Khanb8e7f312017-08-12 10:24:10 -0500442void ItemUpdater::freePriority(uint8_t value, const std::string& versionId)
Saqib Khan81bac882017-06-08 12:17:01 -0500443{
444 //TODO openbmc/openbmc#1896 Improve the performance of this function
445 for (const auto& intf : activations)
446 {
Gunnar Mills3fa70282017-08-18 15:30:42 -0500447 if (intf.second->redundancyPriority)
Saqib Khan81bac882017-06-08 12:17:01 -0500448 {
Saqib Khanb8e7f312017-08-12 10:24:10 -0500449 if (intf.second->redundancyPriority.get()->priority() == value &&
450 intf.second->versionId != versionId)
Saqib Khan81bac882017-06-08 12:17:01 -0500451 {
Gunnar Mills3fa70282017-08-18 15:30:42 -0500452 intf.second->redundancyPriority.get()->priority(value + 1);
Saqib Khan81bac882017-06-08 12:17:01 -0500453 }
454 }
455 }
456}
457
Saqib Khan2af5c492017-07-17 16:15:13 -0500458bool ItemUpdater::isLowestPriority(uint8_t value)
459{
460 for (const auto& intf : activations)
461 {
Gunnar Mills3fa70282017-08-18 15:30:42 -0500462 if (intf.second->redundancyPriority)
Saqib Khan2af5c492017-07-17 16:15:13 -0500463 {
464 if (intf.second->redundancyPriority.get()->priority() < value)
465 {
466 return false;
467 }
468 }
469 }
470 return true;
471}
472
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500473void ItemUpdater::erase(std::string entryId)
474{
Eddie James13fc66a2017-08-31 15:36:44 -0500475 if (isVersionFunctional(entryId) && isChassisOn()) {
476 log<level::ERR>(("Error: Version " + entryId + \
477 " is currently active and running on the host." \
478 " Unable to remove.").c_str());
479 return;
480 }
Saqib Khanef8cd9f2017-08-16 14:20:30 -0500481 // Remove priority persistence file
482 removeFile(entryId);
483
Saqib Khan1e0aa5c2017-08-31 11:04:17 -0500484 // Removing read-only and read-write partitions
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500485 removeReadWritePartition(entryId);
486 removeReadOnlyPartition(entryId);
487
488 // Removing entry in versions map
489 auto it = versions.find(entryId);
490 if (it == versions.end())
491 {
492 log<level::ERR>(("Error: Failed to find version " + entryId + \
Gunnar Mills3fa70282017-08-18 15:30:42 -0500493 " in item updater versions map." \
494 " Unable to remove.").c_str());
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500495 }
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500496 else
497 {
498 versions.erase(entryId);
499 }
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500500
501 // Removing entry in activations map
502 auto ita = activations.find(entryId);
503 if (ita == activations.end())
504 {
505 log<level::ERR>(("Error: Failed to find version " + entryId + \
Gunnar Mills3fa70282017-08-18 15:30:42 -0500506 " in item updater activations map." \
507 " Unable to remove.").c_str());
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500508 }
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500509 else
510 {
511 activations.erase(entryId);
512 }
513 return;
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500514}
515
Michael Tritz234a07e2017-09-21 00:53:06 -0500516void ItemUpdater::deleteAll()
517{
Michael Tritz234a07e2017-09-21 00:53:06 -0500518 for (const auto& activationIt : activations)
519 {
520 if (!isVersionFunctional(activationIt.first))
521 {
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500522 ItemUpdater::erase(activationIt.first);
Michael Tritz234a07e2017-09-21 00:53:06 -0500523 }
524 }
525
Michael Tritz234a07e2017-09-21 00:53:06 -0500526 // Remove any remaining pnor-ro- or pnor-rw- volumes that do not match
527 // the current version.
528 auto method = bus.new_method_call(
529 SYSTEMD_BUSNAME,
530 SYSTEMD_PATH,
531 SYSTEMD_INTERFACE,
532 "StartUnit");
533 method.append("obmc-flash-bios-cleanup.service", "replace");
534 bus.call_noreply(method);
535}
536
Saqib Khan2cbfa032017-08-17 14:52:37 -0500537// TODO: openbmc/openbmc#1402 Monitor flash usage
538void ItemUpdater::freeSpace()
539{
540 std::size_t count = 0;
541 decltype(activations.begin()->second->redundancyPriority.get()->priority())
542 highestPriority = 0;
543 decltype(activations.begin()->second->versionId) highestPriorityVersion;
544 for (const auto& iter : activations)
545 {
546 if (iter.second.get()->activation() == server::Activation::Activations::Active)
547 {
548 count++;
Adriana Kobylakee201a52017-11-09 15:05:04 -0600549 if (isVersionFunctional(iter.second->versionId))
550 {
551 continue;
552 }
553 if (iter.second->redundancyPriority.get()->priority() >= highestPriority)
Saqib Khan2cbfa032017-08-17 14:52:37 -0500554 {
555 highestPriority = iter.second->redundancyPriority.get()->priority();
556 highestPriorityVersion = iter.second->versionId;
557 }
558 }
559 }
560 // Remove the pnor version with highest priority since the PNOR
561 // can't hold more than 2 versions.
562 if (count >= ACTIVE_PNOR_MAX_ALLOWED)
563 {
564 erase(highestPriorityVersion);
565 }
566}
567
Gunnar Mills61010b22017-09-20 15:25:26 -0500568void ItemUpdater::createActiveAssociation(const std::string& path)
Gunnar Mills9741cd12017-08-28 15:09:00 -0500569{
570 assocs.emplace_back(std::make_tuple(ACTIVE_FWD_ASSOCIATION,
571 ACTIVE_REV_ASSOCIATION,
572 path));
573 associations(assocs);
574}
575
Gunnar Mills833e4f32017-09-14 12:30:27 -0500576void ItemUpdater::updateFunctionalAssociation(const std::string& path)
577{
578 // remove all functional associations
579 for (auto iter = assocs.begin(); iter != assocs.end();)
580 {
581 if ((std::get<0>(*iter)).compare(FUNCTIONAL_FWD_ASSOCIATION) == 0)
582 {
583 iter = assocs.erase(iter);
584 }
585 else
586 {
587 ++iter;
588 }
589 }
590 assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION,
591 FUNCTIONAL_REV_ASSOCIATION,
592 path));
593 associations(assocs);
594}
595
Gunnar Mills61010b22017-09-20 15:25:26 -0500596void ItemUpdater::removeActiveAssociation(const std::string& path)
Gunnar Mills9741cd12017-08-28 15:09:00 -0500597{
598 for (auto iter = assocs.begin(); iter != assocs.end();)
599 {
Gunnar Mills833e4f32017-09-14 12:30:27 -0500600 if ((std::get<0>(*iter)).compare(ACTIVE_FWD_ASSOCIATION) == 0 &&
601 (std::get<2>(*iter)).compare(path) == 0)
Gunnar Mills9741cd12017-08-28 15:09:00 -0500602 {
603 iter = assocs.erase(iter);
604 associations(assocs);
605 }
606 else
607 {
608 ++iter;
609 }
610 }
611}
612
Gunnar Mills2badd7a2017-09-20 12:51:28 -0500613std::string ItemUpdater::determineId(const std::string& symlinkPath)
614{
615 if (!fs::exists(symlinkPath))
616 {
617 return {};
618 }
619
620 auto target = fs::canonical(symlinkPath).string();
621
622 // check to make sure the target really exists
623 if (!fs::is_regular_file(target + "/" + PNOR_TOC_FILE))
624 {
625 return {};
626 }
627 // Get the image <id> from the symlink target
628 // for example /media/ro-2a1022fe
629 static const auto PNOR_RO_PREFIX_LEN = strlen(PNOR_RO_PREFIX);
630 return target.substr(PNOR_RO_PREFIX_LEN);
631}
632
Michael Tritzb541f1b2017-10-15 15:10:21 -0500633void GardReset::reset()
634{
635 // The GARD partition is currently misspelled "GUARD." This file path will
636 // need to be updated in the future.
637 auto path = fs::path(PNOR_PRSV_ACTIVE_PATH);
638 path /= "GUARD";
639 std::vector<uint8_t> mboxdArgs;
640
641 auto dbusCall = bus.new_method_call(
642 MBOXD_INTERFACE,
643 MBOXD_PATH,
644 MBOXD_INTERFACE,
645 "cmd");
646
647 // Suspend mboxd - no args required.
648 dbusCall.append(static_cast<uint8_t>(3), mboxdArgs);
649
650 auto responseMsg = bus.call(dbusCall);
651 if (responseMsg.is_method_error())
652 {
653 log<level::ERR>("Error in mboxd suspend call");
654 elog<InternalFailure>();
655 }
656
657 if (fs::is_regular_file(path))
658 {
659 fs::remove(path);
660 }
661
662 dbusCall = bus.new_method_call(
663 MBOXD_INTERFACE,
664 MBOXD_PATH,
665 MBOXD_INTERFACE,
666 "cmd");
667
668 // Resume mboxd with arg 1, indicating that the flash is modified.
669 mboxdArgs.push_back(1);
670 dbusCall.append(static_cast<uint8_t>(4), mboxdArgs);
671
672 responseMsg = bus.call(dbusCall);
673 if (responseMsg.is_method_error())
674 {
675 log<level::ERR>("Error in mboxd resume call");
676 elog<InternalFailure>();
677 }
678}
679
Adriana Kobylakbefe5ce2017-04-05 15:57:44 -0500680} // namespace updater
Adriana Kobylak2d8fa222017-03-15 12:34:32 -0500681} // namespace software
682} // namespace openpower