blob: bbfdef4aba0ff301113ead176a8a346cdb6db862 [file] [log] [blame]
Saqib Khan35e83f32017-05-22 11:37:32 -05001#include <fstream>
Adriana Kobylakb77551c2017-10-27 12:46:23 -05002#include <set>
Gunnar Millsec1b41c2017-05-02 12:20:36 -05003#include <string>
Gunnar Mills2ce7da22017-05-04 15:37:56 -05004#include <phosphor-logging/log.hpp>
Saqib Khandcbfa042017-09-18 13:08:39 -05005#include <phosphor-logging/elog.hpp>
6#include <elog-errors.hpp>
7#include <xyz/openbmc_project/Software/Version/error.hpp>
Gunnar Millsec1b41c2017-05-02 12:20:36 -05008#include "config.h"
Gunnar Mills2ce7da22017-05-04 15:37:56 -05009#include "item_updater.hpp"
10#include "xyz/openbmc_project/Software/Version/server.hpp"
Saqib Khan35e83f32017-05-22 11:37:32 -050011#include <experimental/filesystem>
Saqib Khan705f1bf2017-06-09 23:58:38 -050012#include "version.hpp"
Saqib Khan5d532672017-08-09 10:44:50 -050013#include "serialize.hpp"
Gunnar Millsec1b41c2017-05-02 12:20:36 -050014
15namespace phosphor
16{
17namespace software
18{
19namespace updater
20{
21
Gunnar Mills2ce7da22017-05-04 15:37:56 -050022// When you see server:: you know we're referencing our base class
23namespace server = sdbusplus::xyz::openbmc_project::Software::server;
Michael Tritz0129d922017-08-10 19:33:46 -050024namespace control = sdbusplus::xyz::openbmc_project::Control::server;
Gunnar Mills2ce7da22017-05-04 15:37:56 -050025
26using namespace phosphor::logging;
Saqib Khandcbfa042017-09-18 13:08:39 -050027using namespace sdbusplus::xyz::openbmc_project::Software::Version::Error;
Saqib Khan35e83f32017-05-22 11:37:32 -050028namespace fs = std::experimental::filesystem;
29
Gunnar Mills9a782242017-08-22 16:23:15 -050030const std::vector<std::string> bmcImages = { "image-kernel",
31 "image-rofs",
32 "image-rwfs",
33 "image-u-boot" };
Gunnar Mills2ce7da22017-05-04 15:37:56 -050034
Patrick Williamse75d10f2017-05-30 16:56:32 -050035void ItemUpdater::createActivation(sdbusplus::message::message& msg)
Gunnar Millsec1b41c2017-05-02 12:20:36 -050036{
Saqib Khan84a0e692017-06-28 17:27:01 -050037
38 using SVersion = server::Version;
39 using VersionPurpose = SVersion::VersionPurpose;
Gunnar Mills9a782242017-08-22 16:23:15 -050040 using VersionClass = phosphor::software::manager::Version;
Saqib Khan84a0e692017-06-28 17:27:01 -050041 namespace mesg = sdbusplus::message;
42 namespace variant_ns = mesg::variant_ns;
43
44 mesg::object_path objPath;
45 auto purpose = VersionPurpose::Unknown;
Saqib Khan705f1bf2017-06-09 23:58:38 -050046 std::string version;
Gunnar Mills2ce7da22017-05-04 15:37:56 -050047 std::map<std::string,
Patrick Williamse75d10f2017-05-30 16:56:32 -050048 std::map<std::string,
Saqib Khan84a0e692017-06-28 17:27:01 -050049 mesg::variant<std::string>>> interfaces;
Patrick Williamse75d10f2017-05-30 16:56:32 -050050 msg.read(objPath, interfaces);
Gunnar Mills2ce7da22017-05-04 15:37:56 -050051 std::string path(std::move(objPath));
Saqib Khan19177d32017-06-20 08:11:49 -050052 std::string filePath;
Gunnar Mills2ce7da22017-05-04 15:37:56 -050053
54 for (const auto& intf : interfaces)
55 {
Saqib Khan705f1bf2017-06-09 23:58:38 -050056 if (intf.first == VERSION_IFACE)
Gunnar Mills2ce7da22017-05-04 15:37:56 -050057 {
Saqib Khan705f1bf2017-06-09 23:58:38 -050058 for (const auto& property : intf.second)
Gunnar Mills2ce7da22017-05-04 15:37:56 -050059 {
Saqib Khan705f1bf2017-06-09 23:58:38 -050060 if (property.first == "Purpose")
Gunnar Mills2ce7da22017-05-04 15:37:56 -050061 {
Saqib Khan84a0e692017-06-28 17:27:01 -050062 auto value = SVersion::convertVersionPurposeFromString(
Gunnar Mills9a782242017-08-22 16:23:15 -050063 variant_ns::get<std::string>(property.second));
Saqib Khan84a0e692017-06-28 17:27:01 -050064 if (value == VersionPurpose::BMC ||
65 value == VersionPurpose::System)
66 {
67 purpose = value;
68 }
Saqib Khan705f1bf2017-06-09 23:58:38 -050069 }
70 else if (property.first == "Version")
71 {
Saqib Khan84a0e692017-06-28 17:27:01 -050072 version = variant_ns::get<std::string>(property.second);
Gunnar Mills2ce7da22017-05-04 15:37:56 -050073 }
74 }
75 }
Saqib Khan19177d32017-06-20 08:11:49 -050076 else if (intf.first == FILEPATH_IFACE)
77 {
78 for (const auto& property : intf.second)
79 {
80 if (property.first == "Path")
81 {
Saqib Khan84a0e692017-06-28 17:27:01 -050082 filePath = variant_ns::get<std::string>(property.second);
Saqib Khan19177d32017-06-20 08:11:49 -050083 }
84 }
85 }
Gunnar Mills2ce7da22017-05-04 15:37:56 -050086 }
Saqib Khan705f1bf2017-06-09 23:58:38 -050087 if (version.empty() ||
Saqib Khan19177d32017-06-20 08:11:49 -050088 filePath.empty() ||
Saqib Khan84a0e692017-06-28 17:27:01 -050089 purpose == VersionPurpose::Unknown)
Saqib Khan705f1bf2017-06-09 23:58:38 -050090 {
91 return;
92 }
Gunnar Mills2ce7da22017-05-04 15:37:56 -050093
94 // Version id is the last item in the path
95 auto pos = path.rfind("/");
96 if (pos == std::string::npos)
97 {
98 log<level::ERR>("No version id found in object path",
99 entry("OBJPATH=%s", path));
Patrick Williamse75d10f2017-05-30 16:56:32 -0500100 return;
Gunnar Mills2ce7da22017-05-04 15:37:56 -0500101 }
102
103 auto versionId = path.substr(pos + 1);
104
Patrick Williamse75d10f2017-05-30 16:56:32 -0500105 if (activations.find(versionId) == activations.end())
Gunnar Mills2ce7da22017-05-04 15:37:56 -0500106 {
Saqib Khan35e83f32017-05-22 11:37:32 -0500107 // Determine the Activation state by processing the given image dir.
108 auto activationState = server::Activation::Activations::Invalid;
Gunnar Mills9a782242017-08-22 16:23:15 -0500109 ItemUpdater::ActivationStatus result =
110 ItemUpdater::validateSquashFSImage(filePath);
Gunnar Mills43b25cd2017-09-07 13:19:34 -0500111 AssociationList associations = {};
112
Saqib Khan35e83f32017-05-22 11:37:32 -0500113 if (result == ItemUpdater::ActivationStatus::ready)
114 {
115 activationState = server::Activation::Activations::Ready;
Gunnar Mills43b25cd2017-09-07 13:19:34 -0500116 // Create an association to the BMC inventory item
117 associations.emplace_back(std::make_tuple(
118 ACTIVATION_FWD_ASSOCIATION,
119 ACTIVATION_REV_ASSOCIATION,
120 bmcInventoryPath));
Saqib Khan35e83f32017-05-22 11:37:32 -0500121 }
Gunnar Millsb60add12017-08-24 16:41:42 -0500122
Saqib Khanee13e832017-10-23 12:53:11 -0500123 activations.insert(std::make_pair(
124 versionId,
125 std::make_unique<Activation>(
126 bus,
127 path,
128 *this,
129 versionId,
130 activationState,
131 associations)));
Michael Tritz4254bec2017-10-03 17:18:22 -0500132
Saqib Khanee13e832017-10-23 12:53:11 -0500133 auto versionPtr = std::make_unique<VersionClass>(
134 bus,
135 path,
136 version,
137 purpose,
138 filePath,
139 std::bind(&ItemUpdater::erase,
140 this,
141 std::placeholders::_1));
142 versionPtr->deleteObject =
143 std::make_unique<phosphor::software::manager::Delete>(
144 bus, path, *versionPtr);
145 versions.insert(std::make_pair(versionId, std::move(versionPtr)));
Gunnar Mills2ce7da22017-05-04 15:37:56 -0500146 }
Patrick Williamse75d10f2017-05-30 16:56:32 -0500147 return;
Gunnar Millsec1b41c2017-05-02 12:20:36 -0500148}
149
Saqib Khanba239882017-05-26 08:41:54 -0500150void ItemUpdater::processBMCImage()
151{
Gunnar Mills88e8a322017-09-13 11:09:28 -0500152 using VersionClass = phosphor::software::manager::Version;
153 // Read os-release from /etc/ to get the functional BMC version
154 auto functionalVersion = VersionClass::getBMCVersion(OS_RELEASE_FILE);
155
Saqib Khan1eef62d2017-08-10 15:29:34 -0500156 // Read os-release from folders under /media/ to get
157 // BMC Software Versions.
Gunnar Millsd16bcbd2017-10-08 16:50:42 -0500158 for (const auto& iter : fs::directory_iterator(MEDIA_DIR))
Saqib Khan1eef62d2017-08-10 15:29:34 -0500159 {
160 auto activationState = server::Activation::Activations::Active;
Saqib Khan6fab70d2017-09-07 00:13:50 -0500161 static const auto BMC_RO_PREFIX_LEN = strlen(BMC_ROFS_PREFIX);
Saqib Khan1eef62d2017-08-10 15:29:34 -0500162
163 // Check if the BMC_RO_PREFIXis the prefix of the iter.path
164 if (0 == iter.path().native().compare(0, BMC_RO_PREFIX_LEN,
Saqib Khan6fab70d2017-09-07 00:13:50 -0500165 BMC_ROFS_PREFIX))
Saqib Khan1eef62d2017-08-10 15:29:34 -0500166 {
Saqib Khan021c3652017-09-26 12:11:02 -0500167 // The versionId is extracted from the path
168 // for example /media/ro-2a1022fe.
169 auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN);
Saqib Khan1eef62d2017-08-10 15:29:34 -0500170 auto osRelease = iter.path() / OS_RELEASE_FILE;
171 if (!fs::is_regular_file(osRelease))
172 {
Gunnar Mills2ad1b552017-10-19 15:58:52 -0500173 log<level::ERR>("Failed to read osRelease",
174 entry("FILENAME=%s", osRelease.string()));
Saqib Khan021c3652017-09-26 12:11:02 -0500175 ItemUpdater::erase(id);
176 continue;
Saqib Khan1eef62d2017-08-10 15:29:34 -0500177 }
Gunnar Mills88e8a322017-09-13 11:09:28 -0500178 auto version = VersionClass::getBMCVersion(osRelease);
Saqib Khan1eef62d2017-08-10 15:29:34 -0500179 if (version.empty())
180 {
181 log<level::ERR>("Failed to read version from osRelease",
182 entry("FILENAME=%s", osRelease.string()));
183 activationState = server::Activation::Activations::Invalid;
184 }
Saqib Khan021c3652017-09-26 12:11:02 -0500185
Saqib Khan1eef62d2017-08-10 15:29:34 -0500186 auto purpose = server::Version::VersionPurpose::BMC;
187 auto path = fs::path(SOFTWARE_OBJPATH) / id;
188
Gunnar Mills88e8a322017-09-13 11:09:28 -0500189 // Create functional association if this is the functional version
190 if (version.compare(functionalVersion) == 0)
191 {
192 createFunctionalAssociation(path);
193 }
194
Gunnar Mills43b25cd2017-09-07 13:19:34 -0500195 AssociationList associations = {};
196
197 if (activationState == server::Activation::Activations::Active)
198 {
199 // Create an association to the BMC inventory item
200 associations.emplace_back(std::make_tuple(
201 ACTIVATION_FWD_ASSOCIATION,
202 ACTIVATION_REV_ASSOCIATION,
203 bmcInventoryPath));
204
205 // Create an active association since this image is active
206 createActiveAssociation(path);
207 }
208
Adriana Kobylakee590c72017-09-26 15:16:06 -0500209 // Create Version instance for this version.
210 auto versionPtr = std::make_unique<VersionClass>(
Saqib Khanee13e832017-10-23 12:53:11 -0500211 bus,
212 path,
213 version,
214 purpose,
215 "",
216 std::bind(&ItemUpdater::erase,
217 this,
218 std::placeholders::_1));
Adriana Kobylakee590c72017-09-26 15:16:06 -0500219 auto isVersionFunctional = versionPtr->isFunctional();
Michael Tritz4254bec2017-10-03 17:18:22 -0500220 if (!isVersionFunctional)
221 {
Saqib Khanee13e832017-10-23 12:53:11 -0500222 versionPtr->deleteObject =
223 std::make_unique<phosphor::software::manager::Delete>(
224 bus, path, *versionPtr);
Michael Tritz4254bec2017-10-03 17:18:22 -0500225 }
Saqib Khanee13e832017-10-23 12:53:11 -0500226 versions.insert(std::make_pair(
227 id,
228 std::move(versionPtr)));
Michael Tritz4254bec2017-10-03 17:18:22 -0500229
Saqib Khanee13e832017-10-23 12:53:11 -0500230 // Create Activation instance for this version.
231 activations.insert(std::make_pair(
232 id,
233 std::make_unique<Activation>(
234 bus,
235 path,
236 *this,
237 id,
238 activationState,
239 associations)));
Saqib Khan1eef62d2017-08-10 15:29:34 -0500240
241 // If Active, create RedundancyPriority instance for this version.
242 if (activationState == server::Activation::Activations::Active)
243 {
244 uint8_t priority = std::numeric_limits<uint8_t>::max();
245 if (!restoreFromFile(id, priority))
246 {
Adriana Kobylakee590c72017-09-26 15:16:06 -0500247 if (isVersionFunctional)
248 {
249 priority = 0;
250 }
251 else
252 {
253 log<level::ERR>("Unable to restore priority from file.",
254 entry("VERSIONID=%s", id));
255 }
Saqib Khan1eef62d2017-08-10 15:29:34 -0500256 }
257 activations.find(id)->second->redundancyPriority =
258 std::make_unique<RedundancyPriority>(
Saqib Khanba239882017-05-26 08:41:54 -0500259 bus,
260 path,
Saqib Khan1eef62d2017-08-10 15:29:34 -0500261 *(activations.find(id)->second),
Adriana Kobylakb77551c2017-10-27 12:46:23 -0500262 priority,
263 false);
Saqib Khan1eef62d2017-08-10 15:29:34 -0500264 }
Saqib Khan1eef62d2017-08-10 15:29:34 -0500265 }
266 }
Saqib Khandcbfa042017-09-18 13:08:39 -0500267
268 // If there is no ubi volume for bmc version then read the /etc/os-release
269 // and create rofs-<versionId> under /media
270 if (activations.size() == 0)
271 {
Gunnar Millsd16bcbd2017-10-08 16:50:42 -0500272 auto version = VersionClass::getBMCVersion(OS_RELEASE_FILE);
Saqib Khandcbfa042017-09-18 13:08:39 -0500273 auto id = phosphor::software::manager::Version::getId(version);
274 auto versionFileDir = BMC_ROFS_PREFIX + id + "/etc/";
275 try
276 {
Gunnar Millsd16bcbd2017-10-08 16:50:42 -0500277 if (!fs::is_directory(versionFileDir))
Saqib Khandcbfa042017-09-18 13:08:39 -0500278 {
279 fs::create_directories(versionFileDir);
280 }
281 auto versionFilePath = BMC_ROFS_PREFIX + id + OS_RELEASE_FILE;
282 fs::create_directory_symlink(OS_RELEASE_FILE, versionFilePath);
283 ItemUpdater::processBMCImage();
284 }
285 catch (const std::exception& e)
286 {
287 log<level::ERR>(e.what());
288 }
289 }
Saqib Khanba239882017-05-26 08:41:54 -0500290 return;
291}
292
Leonel Gonzalez3526ef72017-07-07 14:38:25 -0500293void ItemUpdater::erase(std::string entryId)
294{
Eddie James6d873712017-09-01 11:29:07 -0500295 // Find entry in versions map
296 auto it = versions.find(entryId);
297 if (it != versions.end())
298 {
299 if (it->second->isFunctional())
300 {
301 log<level::ERR>(("Error: Version " + entryId + \
302 " is currently running on the BMC." \
303 " Unable to remove.").c_str());
Gunnar Millsd16bcbd2017-10-08 16:50:42 -0500304 return;
Eddie James6d873712017-09-01 11:29:07 -0500305 }
306
307 // Delete ReadOnly partitions if it's not active
308 removeReadOnlyPartition(entryId);
309 removeFile(entryId);
Saqib Khanee13e832017-10-23 12:53:11 -0500310
311 // Removing entry in versions map
312 this->versions.erase(entryId);
Eddie James6d873712017-09-01 11:29:07 -0500313 }
314 else
315 {
316 // Delete ReadOnly partitions even if we can't find the version
317 removeReadOnlyPartition(entryId);
318 removeFile(entryId);
319
320 log<level::ERR>(("Error: Failed to find version " + entryId + \
321 " in item updater versions map." \
322 " Unable to remove.").c_str());
Eddie James6d873712017-09-01 11:29:07 -0500323 }
Saqib Khan1eef62d2017-08-10 15:29:34 -0500324
325 // Remove the priority environment variable.
326 auto serviceFile = "obmc-flash-bmc-setenv@" + entryId + ".service";
327 auto method = bus.new_method_call(
328 SYSTEMD_BUSNAME,
329 SYSTEMD_PATH,
330 SYSTEMD_INTERFACE,
331 "StartUnit");
332 method.append(serviceFile, "replace");
333 bus.call_noreply(method);
Leonel Gonzalez3526ef72017-07-07 14:38:25 -0500334
Leonel Gonzalez3526ef72017-07-07 14:38:25 -0500335 // Removing entry in activations map
336 auto ita = activations.find(entryId);
337 if (ita == activations.end())
338 {
339 log<level::ERR>(("Error: Failed to find version " + entryId + \
Gunnar Mills9a782242017-08-22 16:23:15 -0500340 " in item updater activations map." \
341 " Unable to remove.").c_str());
Leonel Gonzalez3526ef72017-07-07 14:38:25 -0500342 }
Saqib Khanee13e832017-10-23 12:53:11 -0500343 else
344 {
345 this->activations.erase(entryId);
346 }
Saqib Khan49446ae2017-10-02 10:54:20 -0500347 ItemUpdater::resetUbootEnvVars();
Saqib Khanee13e832017-10-23 12:53:11 -0500348 return;
Leonel Gonzalez3526ef72017-07-07 14:38:25 -0500349}
350
Michael Tritzbc1bf3a2017-09-18 16:38:23 -0500351void ItemUpdater::deleteAll()
352{
Michael Tritzbc1bf3a2017-09-18 16:38:23 -0500353 for (const auto& versionIt : versions)
354 {
355 if (!versionIt.second->isFunctional())
356 {
Saqib Khanee13e832017-10-23 12:53:11 -0500357 ItemUpdater::erase(versionIt.first);
Michael Tritzbc1bf3a2017-09-18 16:38:23 -0500358 }
359 }
360
Michael Tritzbc1bf3a2017-09-18 16:38:23 -0500361 // Remove any volumes that do not match current versions.
362 auto method = bus.new_method_call(
363 SYSTEMD_BUSNAME,
364 SYSTEMD_PATH,
365 SYSTEMD_INTERFACE,
366 "StartUnit");
367 method.append("obmc-flash-bmc-cleanup.service", "replace");
368 bus.call_noreply(method);
369}
370
Saqib Khan35e83f32017-05-22 11:37:32 -0500371ItemUpdater::ActivationStatus ItemUpdater::validateSquashFSImage(
Gunnar Mills9a782242017-08-22 16:23:15 -0500372 const std::string& filePath)
Saqib Khan35e83f32017-05-22 11:37:32 -0500373{
Michael Tritzb1cfdf92017-08-14 14:33:30 -0500374 bool invalid = false;
Saqib Khan35e83f32017-05-22 11:37:32 -0500375
Michael Tritzb1cfdf92017-08-14 14:33:30 -0500376 for (auto& bmcImage : bmcImages)
Saqib Khan35e83f32017-05-22 11:37:32 -0500377 {
Michael Tritzb1cfdf92017-08-14 14:33:30 -0500378 fs::path file(filePath);
379 file /= bmcImage;
380 std::ifstream efile(file.c_str());
381 if (efile.good() != 1)
382 {
383 log<level::ERR>("Failed to find the BMC image.",
Gunnar Mills9a782242017-08-22 16:23:15 -0500384 entry("IMAGE=%s", bmcImage.c_str()));
Michael Tritzb1cfdf92017-08-14 14:33:30 -0500385 invalid = true;
386 }
Saqib Khan35e83f32017-05-22 11:37:32 -0500387 }
Michael Tritzb1cfdf92017-08-14 14:33:30 -0500388
389 if (invalid)
Saqib Khan35e83f32017-05-22 11:37:32 -0500390 {
Saqib Khan35e83f32017-05-22 11:37:32 -0500391 return ItemUpdater::ActivationStatus::invalid;
392 }
Michael Tritzb1cfdf92017-08-14 14:33:30 -0500393
394 return ItemUpdater::ActivationStatus::ready;
Saqib Khan35e83f32017-05-22 11:37:32 -0500395}
396
Saqib Khanb9da6632017-09-13 09:48:37 -0500397void ItemUpdater::freePriority(uint8_t value, const std::string& versionId)
Saqib Khan4c1aec02017-07-06 11:46:13 -0500398{
Adriana Kobylakb77551c2017-10-27 12:46:23 -0500399 std::map<std::string, uint8_t> priorityMap;
400
401 // Insert the requested version and priority, it may not exist yet.
402 priorityMap.insert(std::make_pair(versionId, value));
403
Saqib Khan4c1aec02017-07-06 11:46:13 -0500404 for (const auto& intf : activations)
405 {
Gunnar Mills9a782242017-08-22 16:23:15 -0500406 if (intf.second->redundancyPriority)
Saqib Khan4c1aec02017-07-06 11:46:13 -0500407 {
Adriana Kobylakb77551c2017-10-27 12:46:23 -0500408 priorityMap.insert(std::make_pair(
409 intf.first,
410 intf.second->redundancyPriority.get()->priority()));
Saqib Khan4c1aec02017-07-06 11:46:13 -0500411 }
412 }
Adriana Kobylakb77551c2017-10-27 12:46:23 -0500413
414 // Lambda function to compare 2 priority values, use <= to allow duplicates
415 typedef std::function<bool(
416 std::pair<std::string, uint8_t>,
417 std::pair<std::string, uint8_t>)> cmpPriority;
418 cmpPriority cmpPriorityFunc = [](
419 std::pair<std::string, uint8_t> priority1,
420 std::pair<std::string, uint8_t> priority2)
421 {
422 return priority1.second <= priority2.second;
423 };
424
425 // Sort versions by ascending priority
426 std::set<std::pair<std::string, uint8_t>, cmpPriority> prioritySet(
427 priorityMap.begin(), priorityMap.end(), cmpPriorityFunc);
428
429 auto freePriorityValue = value;
430 for (auto& element : prioritySet)
431 {
432 if (element.first == versionId)
433 {
434 continue;
435 }
436 if (element.second == freePriorityValue)
437 {
438 ++freePriorityValue;
439 auto it = activations.find(element.first);
440 it->second->redundancyPriority.get()->sdbusPriority(
441 freePriorityValue);
442 }
443 }
444
445 auto lowestVersion = prioritySet.begin()->first;
446 if (value == prioritySet.begin()->second)
447 {
448 lowestVersion = versionId;
449 }
450 updateUbootEnvVars(lowestVersion);
Saqib Khan4c1aec02017-07-06 11:46:13 -0500451}
452
Michael Tritz37a59042017-07-12 13:44:53 -0500453void ItemUpdater::reset()
454{
455 // Mark the read-write partition for recreation upon reboot.
456 auto method = bus.new_method_call(
457 SYSTEMD_BUSNAME,
458 SYSTEMD_PATH,
459 SYSTEMD_INTERFACE,
460 "StartUnit");
Michael Tritz0129d922017-08-10 19:33:46 -0500461 method.append("obmc-flash-bmc-setenv@rwreset\\x3dtrue.service", "replace");
Michael Tritz37a59042017-07-12 13:44:53 -0500462 bus.call_noreply(method);
463
464 log<level::INFO>("BMC factory reset will take effect upon reboot.");
465
466 return;
467}
468
Leonel Gonzalez3526ef72017-07-07 14:38:25 -0500469void ItemUpdater::removeReadOnlyPartition(std::string versionId)
470{
471 auto serviceFile = "obmc-flash-bmc-ubiro-remove@" + versionId +
472 ".service";
473
474 // Remove the read-only partitions.
475 auto method = bus.new_method_call(
476 SYSTEMD_BUSNAME,
477 SYSTEMD_PATH,
478 SYSTEMD_INTERFACE,
479 "StartUnit");
480 method.append(serviceFile, "replace");
481 bus.call_noreply(method);
482}
483
Michael Tritz0129d922017-08-10 19:33:46 -0500484bool ItemUpdater::fieldModeEnabled(bool value)
485{
486 // enabling field mode is intended to be one way: false -> true
487 if (value && !control::FieldMode::fieldModeEnabled())
488 {
489 control::FieldMode::fieldModeEnabled(value);
490
491 auto method = bus.new_method_call(
492 SYSTEMD_BUSNAME,
493 SYSTEMD_PATH,
494 SYSTEMD_INTERFACE,
495 "StartUnit");
496 method.append("obmc-flash-bmc-setenv@fieldmode\\x3dtrue.service",
Gunnar Mills9a782242017-08-22 16:23:15 -0500497 "replace");
Michael Tritz0129d922017-08-10 19:33:46 -0500498 bus.call_noreply(method);
499
500 method = bus.new_method_call(
501 SYSTEMD_BUSNAME,
502 SYSTEMD_PATH,
503 SYSTEMD_INTERFACE,
504 "StopUnit");
505 method.append("usr-local.mount", "replace");
506 bus.call_noreply(method);
507
508 std::vector<std::string> usrLocal = {"usr-local.mount"};
509
510 method = bus.new_method_call(
511 SYSTEMD_BUSNAME,
512 SYSTEMD_PATH,
513 SYSTEMD_INTERFACE,
514 "MaskUnitFiles");
515 method.append(usrLocal, false, true);
516 bus.call_noreply(method);
517 }
518
519 return control::FieldMode::fieldModeEnabled();
520}
521
522void ItemUpdater::restoreFieldModeStatus()
523{
Michael Tritzff0b4212017-10-24 17:38:09 -0500524 std::ifstream input("/dev/mtd/u-boot-env");
Michael Tritz0129d922017-08-10 19:33:46 -0500525 std::string envVar;
526 std::getline(input, envVar);
527
Gunnar Mills9a782242017-08-22 16:23:15 -0500528 if (envVar.find("fieldmode=true") != std::string::npos)
Michael Tritz0129d922017-08-10 19:33:46 -0500529 {
530 ItemUpdater::fieldModeEnabled(true);
531 }
532}
533
Gunnar Millsb60add12017-08-24 16:41:42 -0500534void ItemUpdater::setBMCInventoryPath()
535{
536 //TODO: openbmc/openbmc#1786 - Get the BMC path by looking for objects
537 // that implement the BMC inventory interface
538 auto depth = 0;
539 auto mapperCall = bus.new_method_call(MAPPER_BUSNAME,
540 MAPPER_PATH,
541 MAPPER_INTERFACE,
542 "GetSubTreePaths");
543
544 mapperCall.append(CHASSIS_INVENTORY_PATH);
545 mapperCall.append(depth);
546
547 // TODO: openbmc/openbmc#2226 - Add Inventory Item filter when
548 // mapper is fixed.
549 std::vector<std::string> filter = {};
550 mapperCall.append(filter);
551
552 auto response = bus.call(mapperCall);
553 if (response.is_method_error())
554 {
555 log<level::ERR>("Error in mapper GetSubTreePath");
556 return;
557 }
558
559 using ObjectPaths = std::vector<std::string>;
560 ObjectPaths result;
561 response.read(result);
562
563 if (result.empty())
564 {
565 log<level::ERR>("Invalid response from mapper");
566 return;
567 }
568
569 for (auto& iter : result)
570 {
571 const auto& path = iter;
572 if (path.substr(path.find_last_of('/') + 1).compare("bmc") == 0)
573 {
574 bmcInventoryPath = path;
575 return;
576 }
577 }
578}
579
Gunnar Millsf10b2322017-09-21 15:31:55 -0500580void ItemUpdater::createActiveAssociation(const std::string& path)
Gunnar Millsded875d2017-08-28 16:44:52 -0500581{
582 assocs.emplace_back(std::make_tuple(ACTIVE_FWD_ASSOCIATION,
583 ACTIVE_REV_ASSOCIATION,
584 path));
585 associations(assocs);
586}
587
Gunnar Mills88e8a322017-09-13 11:09:28 -0500588void ItemUpdater::createFunctionalAssociation(const std::string& path)
589{
590 assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION,
591 FUNCTIONAL_REV_ASSOCIATION,
592 path));
593 associations(assocs);
594}
595
Gunnar Millsf10b2322017-09-21 15:31:55 -0500596void ItemUpdater::removeActiveAssociation(const std::string& path)
Gunnar Millsded875d2017-08-28 16:44:52 -0500597{
598 for (auto iter = assocs.begin(); iter != assocs.end();)
599 {
Gunnar Mills88e8a322017-09-13 11:09:28 -0500600 // Since there could be multiple associations to the same path,
601 // only remove ones that have an active forward association.
602 if ((std::get<0>(*iter)).compare(ACTIVE_FWD_ASSOCIATION) == 0 &&
603 (std::get<2>(*iter)).compare(path) == 0)
Gunnar Millsded875d2017-08-28 16:44:52 -0500604 {
605 iter = assocs.erase(iter);
606 associations(assocs);
607 }
608 else
609 {
610 ++iter;
611 }
612 }
613}
614
Saqib Khanb9da6632017-09-13 09:48:37 -0500615bool ItemUpdater::isLowestPriority(uint8_t value)
616{
617 for (const auto& intf : activations)
618 {
Gunnar Millsd16bcbd2017-10-08 16:50:42 -0500619 if (intf.second->redundancyPriority)
Saqib Khanb9da6632017-09-13 09:48:37 -0500620 {
621 if (intf.second->redundancyPriority.get()->priority() < value)
622 {
623 return false;
624 }
625 }
626 }
627 return true;
628}
629
Adriana Kobylakb77551c2017-10-27 12:46:23 -0500630void ItemUpdater::updateUbootEnvVars(const std::string& versionId)
631{
632 auto method = bus.new_method_call(
633 SYSTEMD_BUSNAME,
634 SYSTEMD_PATH,
635 SYSTEMD_INTERFACE,
636 "StartUnit");
637 auto updateEnvVarsFile = "obmc-flash-bmc-updateubootvars@" + versionId +
638 ".service";
639 method.append(updateEnvVarsFile, "replace");
640 auto result = bus.call(method);
641
642 //Check that the bus call didn't result in an error
643 if (result.is_method_error())
644 {
645 log<level::ERR>("Failed to update u-boot env variables",
646 entry("VERSIONID=%s", versionId));
647 }
648}
649
Saqib Khan49446ae2017-10-02 10:54:20 -0500650void ItemUpdater::resetUbootEnvVars()
651{
652 decltype(activations.begin()->second->redundancyPriority.get()->priority())
653 lowestPriority = std::numeric_limits<uint8_t>::max();
654 decltype(activations.begin()->second->versionId) lowestPriorityVersion;
655 for (const auto& intf : activations)
656 {
657 if (!intf.second->redundancyPriority.get())
658 {
659 // Skip this version if the redundancyPriority is not initialized.
660 continue;
661 }
662
663 if (intf.second->redundancyPriority.get()->priority()
664 <= lowestPriority)
665 {
666 lowestPriority = intf.second->redundancyPriority.get()->priority();
667 lowestPriorityVersion = intf.second->versionId;
668 }
669 }
670
Saqib Khanf0382c32017-10-24 13:36:22 -0500671 // Update the U-boot environment variable to point to the lowest priority
Adriana Kobylakb77551c2017-10-27 12:46:23 -0500672 updateUbootEnvVars(lowestPriorityVersion);
Saqib Khan49446ae2017-10-02 10:54:20 -0500673}
674
Gunnar Millsec1b41c2017-05-02 12:20:36 -0500675} // namespace updater
676} // namespace software
677} // namespace phosphor