blob: fcec2b1d46e1531230ff4633643abc2b3955b827 [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>
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -05004#include <phosphor-logging/log.hpp>
Adriana Kobylakd6a549e2017-05-10 16:23:01 -05005#include <xyz/openbmc_project/Software/Version/server.hpp>
Saqib Khan167601b2017-06-18 23:33:46 -05006#include "version.hpp"
Adriana Kobylak2d8fa222017-03-15 12:34:32 -05007#include "config.h"
8#include "item_updater.hpp"
Saqib Khana8ade7e2017-04-12 10:27:56 -05009#include "activation.hpp"
Michael Tritz60bc20f2017-07-29 23:32:21 -050010#include "serialize.hpp"
Adriana Kobylak2d8fa222017-03-15 12:34:32 -050011
12namespace openpower
13{
14namespace software
15{
Adriana Kobylakbefe5ce2017-04-05 15:57:44 -050016namespace updater
Adriana Kobylak2d8fa222017-03-15 12:34:32 -050017{
18
Saqib Khana8ade7e2017-04-12 10:27:56 -050019// When you see server:: you know we're referencing our base class
20namespace server = sdbusplus::xyz::openbmc_project::Software::server;
Adriana Kobylak5ba6b102017-05-19 09:41:27 -050021namespace fs = std::experimental::filesystem;
Saqib Khana8ade7e2017-04-12 10:27:56 -050022
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -050023using namespace phosphor::logging;
24
Saqib Khana8ade7e2017-04-12 10:27:56 -050025constexpr auto squashFSImage = "pnor.xz.squashfs";
26
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050027void ItemUpdater::createActivation(sdbusplus::message::message& m)
Adriana Kobylak2d8fa222017-03-15 12:34:32 -050028{
Patrick Williamse4290942017-06-16 05:43:08 -050029 using SVersion = server::Version;
30 using VersionPurpose = SVersion::VersionPurpose;
31 namespace msg = sdbusplus::message;
32 namespace variant_ns = msg::variant_ns;
33
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050034 sdbusplus::message::object_path objPath;
35 std::map<std::string,
Patrick Williamse4290942017-06-16 05:43:08 -050036 std::map<std::string, msg::variant<std::string>>> interfaces;
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050037 m.read(objPath, interfaces);
Patrick Williamse4290942017-06-16 05:43:08 -050038
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050039 std::string path(std::move(objPath));
Adriana Kobylak5ba6b102017-05-19 09:41:27 -050040 std::string filePath;
Patrick Williamse4290942017-06-16 05:43:08 -050041 auto purpose = VersionPurpose::Unknown;
Saqib Khance148702017-06-11 12:01:58 -050042 std::string version;
Adriana Kobylak5ba6b102017-05-19 09:41:27 -050043
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050044 for (const auto& intf : interfaces)
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -050045 {
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050046 if (intf.first == VERSION_IFACE)
47 {
48 for (const auto& property : intf.second)
49 {
50 if (property.first == "Purpose")
51 {
52 // Only process the Host and System images
Patrick Williamse4290942017-06-16 05:43:08 -050053 auto value = SVersion::convertVersionPurposeFromString(
54 variant_ns::get<std::string>(property.second));
55
56 if (value == VersionPurpose::Host ||
57 value == VersionPurpose::System)
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050058 {
Saqib Khance148702017-06-11 12:01:58 -050059 purpose = value;
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050060 }
61 }
Saqib Khance148702017-06-11 12:01:58 -050062 else if (property.first == "Version")
63 {
Patrick Williamse4290942017-06-16 05:43:08 -050064 version = variant_ns::get<std::string>(property.second);
Saqib Khance148702017-06-11 12:01:58 -050065 }
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050066 }
67 }
Adriana Kobylak5ba6b102017-05-19 09:41:27 -050068 else if (intf.first == FILEPATH_IFACE)
69 {
70 for (const auto& property : intf.second)
71 {
72 if (property.first == "Path")
73 {
Patrick Williamse4290942017-06-16 05:43:08 -050074 filePath = variant_ns::get<std::string>(property.second);
Adriana Kobylak5ba6b102017-05-19 09:41:27 -050075 }
76 }
77 }
78 }
Patrick Williamse4290942017-06-16 05:43:08 -050079 if ((filePath.empty()) || (purpose == VersionPurpose::Unknown))
Adriana Kobylak5ba6b102017-05-19 09:41:27 -050080 {
81 return;
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -050082 }
83
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050084 // Version id is the last item in the path
85 auto pos = path.rfind("/");
86 if (pos == std::string::npos)
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -050087 {
Adriana Kobylakd6a549e2017-05-10 16:23:01 -050088 log<level::ERR>("No version id found in object path",
89 entry("OBJPATH=%s", path));
90 return;
91 }
92
93 auto versionId = path.substr(pos + 1);
94
95 if (activations.find(versionId) == activations.end())
96 {
97 // Determine the Activation state by processing the given image dir.
98 auto activationState = server::Activation::Activations::Invalid;
Gunnar Mills3588acc2017-09-07 13:13:22 -050099 AssociationList associations = {};
Adriana Kobylak5ba6b102017-05-19 09:41:27 -0500100 if (ItemUpdater::validateSquashFSImage(filePath) == 0)
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -0500101 {
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500102 activationState = server::Activation::Activations::Ready;
Gunnar Mills3588acc2017-09-07 13:13:22 -0500103 // Create an association to the host inventory item
104 associations.emplace_back(std::make_tuple(
105 ACTIVATION_FWD_ASSOCIATION,
106 ACTIVATION_REV_ASSOCIATION,
107 HOST_INVENTORY_PATH));
Adriana Kobylakb66ac3a2017-03-28 13:33:20 -0500108 }
109
Adriana Kobylak5ba6b102017-05-19 09:41:27 -0500110 fs::path manifestPath(filePath);
111 manifestPath /= MANIFEST_FILE;
Saqib Khan167601b2017-06-18 23:33:46 -0500112 std::string extendedVersion = (Version::getValue(manifestPath.string(),
113 std::map<std::string, std::string>
114 {{"extended_version", ""}})).begin()->second;
Gunnar Mills3edb84b2017-08-18 15:13:15 -0500115
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500116 activations.insert(std::make_pair(
117 versionId,
118 std::make_unique<Activation>(
119 bus,
120 path,
Saqib Khan81bac882017-06-08 12:17:01 -0500121 *this,
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500122 versionId,
123 extendedVersion,
Gunnar Mills3edb84b2017-08-18 15:13:15 -0500124 activationState,
125 associations)));
Saqib Khance148702017-06-11 12:01:58 -0500126 versions.insert(std::make_pair(
127 versionId,
128 std::make_unique<Version>(
129 bus,
130 path,
131 version,
132 purpose,
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500133 filePath,
134 *this)));
Saqib Khan00044f42017-07-10 17:24:43 -0500135 }
Saqib Khan3fb2d172017-08-07 12:14:03 -0500136 else
137 {
138 log<level::INFO>("Software Object with the same version already exists",
Gunnar Mills3fa70282017-08-18 15:30:42 -0500139 entry("VERSION_ID=%s", versionId));
Saqib Khan3fb2d172017-08-07 12:14:03 -0500140 }
Patrick Williams3accb322017-05-30 16:29:52 -0500141 return;
Adriana Kobylak2d8fa222017-03-15 12:34:32 -0500142}
143
Saqib Khan167601b2017-06-18 23:33:46 -0500144void ItemUpdater::processPNORImage()
Saqib Khan7254f0e2017-04-10 21:45:37 -0500145{
Saqib Khan4c5d7442017-07-18 00:43:52 -0500146 // Read pnor.toc from folders under /media/
147 // to get Active Software Versions.
Gunnar Mills3fa70282017-08-18 15:30:42 -0500148 for (const auto& iter : fs::directory_iterator(MEDIA_DIR))
Saqib Khan4c5d7442017-07-18 00:43:52 -0500149 {
150 auto activationState = server::Activation::Activations::Active;
151
152 static const auto PNOR_RO_PREFIX_LEN = strlen(PNOR_RO_PREFIX);
153
154 // Check if the PNOR_RO_PREFIX is the prefix of the iter.path
155 if (0 == iter.path().native().compare(0, PNOR_RO_PREFIX_LEN,
156 PNOR_RO_PREFIX))
157 {
158 auto pnorTOC = iter.path() / PNOR_TOC_FILE;
159 if (!fs::is_regular_file(pnorTOC))
160 {
161 log<level::ERR>("Failed to read pnorTOC\n",
162 entry("FileName=%s", pnorTOC.string()));
163 activationState = server::Activation::Activations::Invalid;
164 }
165 auto keyValues =
166 Version::getValue(pnorTOC,
167 {{ "version", "" },
168 { "extended_version", "" } });
169 auto& version = keyValues.at("version");
170 if (version.empty())
171 {
172 log<level::ERR>("Failed to read version from pnorTOC",
173 entry("FILENAME=%s", pnorTOC.string()));
174 activationState = server::Activation::Activations::Invalid;
175 }
176
177 auto& extendedVersion = keyValues.at("extended_version");
178 if (extendedVersion.empty())
179 {
180 log<level::ERR>("Failed to read extendedVersion from pnorTOC",
181 entry("FILENAME=%s", pnorTOC.string()));
182 activationState = server::Activation::Activations::Invalid;
183 }
184
185 // The versionId is extracted from the path
186 // for example /media/pnor-ro-2a1022fe
187 auto id = iter.path().native().substr(PNOR_RO_PREFIX_LEN);
188 auto purpose = server::Version::VersionPurpose::Host;
189 auto path = fs::path(SOFTWARE_OBJPATH) / id;
Gunnar Mills3588acc2017-09-07 13:13:22 -0500190 AssociationList associations = {};
Saqib Khan4c5d7442017-07-18 00:43:52 -0500191
Gunnar Mills3588acc2017-09-07 13:13:22 -0500192 if (activationState == server::Activation::Activations::Active)
193 {
194 // Create an association to the host inventory item
195 associations.emplace_back(std::make_tuple(
196 ACTIVATION_FWD_ASSOCIATION,
197 ACTIVATION_REV_ASSOCIATION,
198 HOST_INVENTORY_PATH));
Gunnar Mills3edb84b2017-08-18 15:13:15 -0500199
Gunnar Mills3588acc2017-09-07 13:13:22 -0500200 // Create an active association since this image is active
201 createActiveAssociation(path);
202 }
Gunnar Mills3edb84b2017-08-18 15:13:15 -0500203
Saqib Khan4c5d7442017-07-18 00:43:52 -0500204 // Create Activation instance for this version.
205 activations.insert(std::make_pair(
206 id,
207 std::make_unique<Activation>(
208 bus,
209 path,
210 *this,
211 id,
212 extendedVersion,
Gunnar Mills3edb84b2017-08-18 15:13:15 -0500213 activationState,
214 associations)));
Saqib Khan4c5d7442017-07-18 00:43:52 -0500215
216 // If Active, create RedundancyPriority instance for this version.
217 if (activationState == server::Activation::Activations::Active)
218 {
Michael Tritz36417922017-08-04 14:00:29 -0500219 uint8_t priority = std::numeric_limits<uint8_t>::max();
220 if (!restoreFromFile(id, priority))
Saqib Khan4c5d7442017-07-18 00:43:52 -0500221 {
Michael Tritz36417922017-08-04 14:00:29 -0500222 log<level::ERR>("Unable to restore priority from file.",
Gunnar Mills3fa70282017-08-18 15:30:42 -0500223 entry("VERSIONID=%s", id));
Saqib Khan4c5d7442017-07-18 00:43:52 -0500224 }
Michael Tritz36417922017-08-04 14:00:29 -0500225 activations.find(id)->second->redundancyPriority =
226 std::make_unique<RedundancyPriority>(
227 bus,
228 path,
229 *(activations.find(id)->second),
230 priority);
Saqib Khan4c5d7442017-07-18 00:43:52 -0500231 }
232
233 // Create Version instance for this version.
234 versions.insert(std::make_pair(
235 id,
236 std::make_unique<Version>(
237 bus,
238 path,
239 version,
240 purpose,
241 "",
242 *this)));
243 }
244 }
Saqib Khan167601b2017-06-18 23:33:46 -0500245 return;
Saqib Khan7254f0e2017-04-10 21:45:37 -0500246}
247
Adriana Kobylak5ba6b102017-05-19 09:41:27 -0500248int ItemUpdater::validateSquashFSImage(const std::string& filePath)
Saqib Khana8ade7e2017-04-12 10:27:56 -0500249{
Saqib Khan4c5d7442017-07-18 00:43:52 -0500250 auto file = fs::path(filePath) / squashFSImage;
251 if (fs::is_regular_file(file))
Saqib Khana8ade7e2017-04-12 10:27:56 -0500252 {
253 return 0;
254 }
255 else
256 {
257 log<level::ERR>("Failed to find the SquashFS image.");
258 return -1;
259 }
260}
261
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500262void ItemUpdater::removeReadOnlyPartition(std::string versionId)
Michael Tritzdd961b62017-05-17 14:07:03 -0500263{
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500264 auto serviceFile = "obmc-flash-bios-ubiumount-ro@" + versionId +
265 ".service";
266
267 // Remove the read-only partitions.
268 auto method = bus.new_method_call(
269 SYSTEMD_BUSNAME,
270 SYSTEMD_PATH,
271 SYSTEMD_INTERFACE,
272 "StartUnit");
273 method.append(serviceFile, "replace");
274 bus.call_noreply(method);
275}
276
277void ItemUpdater::removeReadWritePartition(std::string versionId)
278{
279 auto serviceFile = "obmc-flash-bios-ubiumount-rw@" + versionId +
Michael Tritzdd961b62017-05-17 14:07:03 -0500280 ".service";
281
282 // Remove the read-write partitions.
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500283 auto method = bus.new_method_call(
Michael Tritzdd961b62017-05-17 14:07:03 -0500284 SYSTEMD_BUSNAME,
285 SYSTEMD_PATH,
286 SYSTEMD_INTERFACE,
287 "StartUnit");
288 method.append(serviceFile, "replace");
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500289 bus.call_noreply(method);
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500290}
Michael Tritzdd961b62017-05-17 14:07:03 -0500291
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500292void ItemUpdater::removePreservedPartition()
293{
Michael Tritzdd961b62017-05-17 14:07:03 -0500294 // Remove the preserved partition.
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500295 auto method = bus.new_method_call(
Michael Tritzdd961b62017-05-17 14:07:03 -0500296 SYSTEMD_BUSNAME,
297 SYSTEMD_PATH,
298 SYSTEMD_INTERFACE,
299 "StartUnit");
300 method.append("obmc-flash-bios-ubiumount-prsv.service", "replace");
Adriana Kobylakd6a549e2017-05-10 16:23:01 -0500301 bus.call_noreply(method);
Michael Tritzdd961b62017-05-17 14:07:03 -0500302
303 return;
304}
305
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500306void ItemUpdater::reset()
307{
Gunnar Mills3fa70282017-08-18 15:30:42 -0500308 for (const auto& it : activations)
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500309 {
310 removeReadWritePartition(it.first);
Michael Tritz60bc20f2017-07-29 23:32:21 -0500311 removeFile(it.first);
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500312 }
313 removePreservedPartition();
314 return;
315}
316
Saqib Khanb8e7f312017-08-12 10:24:10 -0500317void ItemUpdater::freePriority(uint8_t value, const std::string& versionId)
Saqib Khan81bac882017-06-08 12:17:01 -0500318{
319 //TODO openbmc/openbmc#1896 Improve the performance of this function
320 for (const auto& intf : activations)
321 {
Gunnar Mills3fa70282017-08-18 15:30:42 -0500322 if (intf.second->redundancyPriority)
Saqib Khan81bac882017-06-08 12:17:01 -0500323 {
Saqib Khanb8e7f312017-08-12 10:24:10 -0500324 if (intf.second->redundancyPriority.get()->priority() == value &&
325 intf.second->versionId != versionId)
Saqib Khan81bac882017-06-08 12:17:01 -0500326 {
Gunnar Mills3fa70282017-08-18 15:30:42 -0500327 intf.second->redundancyPriority.get()->priority(value + 1);
Saqib Khan81bac882017-06-08 12:17:01 -0500328 }
329 }
330 }
331}
332
Saqib Khan2af5c492017-07-17 16:15:13 -0500333bool ItemUpdater::isLowestPriority(uint8_t value)
334{
335 for (const auto& intf : activations)
336 {
Gunnar Mills3fa70282017-08-18 15:30:42 -0500337 if (intf.second->redundancyPriority)
Saqib Khan2af5c492017-07-17 16:15:13 -0500338 {
339 if (intf.second->redundancyPriority.get()->priority() < value)
340 {
341 return false;
342 }
343 }
344 }
345 return true;
346}
347
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500348void ItemUpdater::erase(std::string entryId)
349{
Saqib Khanef8cd9f2017-08-16 14:20:30 -0500350 // Remove priority persistence file
351 removeFile(entryId);
352
Saqib Khan1e0aa5c2017-08-31 11:04:17 -0500353 // Removing read-only and read-write partitions
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500354 removeReadWritePartition(entryId);
355 removeReadOnlyPartition(entryId);
356
357 // Removing entry in versions map
358 auto it = versions.find(entryId);
359 if (it == versions.end())
360 {
361 log<level::ERR>(("Error: Failed to find version " + entryId + \
Gunnar Mills3fa70282017-08-18 15:30:42 -0500362 " in item updater versions map." \
363 " Unable to remove.").c_str());
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500364 return;
365 }
366 versions.erase(entryId);
367
368 // Removing entry in activations map
369 auto ita = activations.find(entryId);
370 if (ita == activations.end())
371 {
372 log<level::ERR>(("Error: Failed to find version " + entryId + \
Gunnar Mills3fa70282017-08-18 15:30:42 -0500373 " in item updater activations map." \
374 " Unable to remove.").c_str());
Leonel Gonzalez9c8adfa2017-07-12 11:08:40 -0500375 return;
376 }
377 activations.erase(entryId);
378}
379
Saqib Khan2cbfa032017-08-17 14:52:37 -0500380// TODO: openbmc/openbmc#1402 Monitor flash usage
381void ItemUpdater::freeSpace()
382{
383 std::size_t count = 0;
384 decltype(activations.begin()->second->redundancyPriority.get()->priority())
385 highestPriority = 0;
386 decltype(activations.begin()->second->versionId) highestPriorityVersion;
387 for (const auto& iter : activations)
388 {
389 if (iter.second.get()->activation() == server::Activation::Activations::Active)
390 {
391 count++;
392 if (iter.second->redundancyPriority.get()->priority() > highestPriority)
393 {
394 highestPriority = iter.second->redundancyPriority.get()->priority();
395 highestPriorityVersion = iter.second->versionId;
396 }
397 }
398 }
399 // Remove the pnor version with highest priority since the PNOR
400 // can't hold more than 2 versions.
401 if (count >= ACTIVE_PNOR_MAX_ALLOWED)
402 {
403 erase(highestPriorityVersion);
404 }
405}
406
Gunnar Mills9741cd12017-08-28 15:09:00 -0500407void ItemUpdater::createActiveAssociation(std::string path)
408{
409 assocs.emplace_back(std::make_tuple(ACTIVE_FWD_ASSOCIATION,
410 ACTIVE_REV_ASSOCIATION,
411 path));
412 associations(assocs);
413}
414
415void ItemUpdater::removeActiveAssociation(std::string path)
416{
417 for (auto iter = assocs.begin(); iter != assocs.end();)
418 {
419 if ((std::get<2>(*iter)).compare(path) == 0)
420 {
421 iter = assocs.erase(iter);
422 associations(assocs);
423 }
424 else
425 {
426 ++iter;
427 }
428 }
429}
430
Adriana Kobylakbefe5ce2017-04-05 15:57:44 -0500431} // namespace updater
Adriana Kobylak2d8fa222017-03-15 12:34:32 -0500432} // namespace software
433} // namespace openpower