blob: 52427f0f6cf4852f9a7c7b56f829433b121e8ac0 [file] [log] [blame]
Adriana Kobylak692b5552017-04-17 14:02:58 -05001#include "config.h"
Gunnar Millsf6ed5892018-09-07 17:08:02 -05002
3#include "activation.hpp"
4
Saqib Khan81bac882017-06-08 12:17:01 -05005#include "item_updater.hpp"
Michael Tritz60bc20f2017-07-29 23:32:21 -05006#include "serialize.hpp"
Gunnar Millsf6ed5892018-09-07 17:08:02 -05007
8#include <experimental/filesystem>
Saqib Khan7f80e0b2017-10-22 11:29:07 -05009#include <phosphor-logging/log.hpp>
Gunnar Mills74b657e2018-07-13 09:27:31 -050010#include <sdbusplus/exception.hpp>
Adriana Kobylakbefe5ce2017-04-05 15:57:44 -050011
Jayanth Othayoth4016e522018-03-20 09:39:06 -050012#ifdef WANT_SIGNATURE_VERIFY
Jayanth Othayoth4016e522018-03-20 09:39:06 -050013#include "image_verify.hpp"
Gunnar Millsf6ed5892018-09-07 17:08:02 -050014
15#include <phosphor-logging/elog-errors.hpp>
16#include <phosphor-logging/elog.hpp>
17#include <sdbusplus/server.hpp>
18#include <xyz/openbmc_project/Common/error.hpp>
Jayanth Othayoth4016e522018-03-20 09:39:06 -050019#endif
20
Adriana Kobylakbefe5ce2017-04-05 15:57:44 -050021namespace openpower
22{
23namespace software
24{
25namespace updater
26{
27
Adriana Kobylak55f9e832017-05-14 16:13:00 -050028namespace fs = std::experimental::filesystem;
Adriana Kobylak99c8c0e2017-04-17 13:39:11 -050029namespace softwareServer = sdbusplus::xyz::openbmc_project::Software::server;
30
Saqib Khan7f80e0b2017-10-22 11:29:07 -050031using namespace phosphor::logging;
Gunnar Mills74b657e2018-07-13 09:27:31 -050032using sdbusplus::exception::SdBusError;
Saqib Khan7f80e0b2017-10-22 11:29:07 -050033
Jayanth Othayoth4016e522018-03-20 09:39:06 -050034#ifdef WANT_SIGNATURE_VERIFY
35using InternalFailure =
36 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Jayanth Othayoth11271fb2018-03-29 10:25:50 -050037
38// Field mode path and interface.
39constexpr auto FIELDMODE_PATH("/xyz/openbmc_project/software");
40constexpr auto FIELDMODE_INTERFACE("xyz.openbmc_project.Control.FieldMode");
Jayanth Othayoth4016e522018-03-20 09:39:06 -050041#endif
42
Adriana Kobylak70dcb632018-02-27 15:46:52 -060043constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
44constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
Michael Tritz9d25b602017-06-14 14:41:43 -050045
46void Activation::subscribeToSystemdSignals()
47{
Adriana Kobylak70dcb632018-02-27 15:46:52 -060048 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
49 SYSTEMD_INTERFACE, "Subscribe");
Gunnar Mills74b657e2018-07-13 09:27:31 -050050 try
51 {
52 this->bus.call_noreply(method);
53 }
54 catch (const SdBusError& e)
55 {
56 if (e.name() != nullptr &&
57 strcmp("org.freedesktop.systemd1.AlreadySubscribed", e.name()) == 0)
58 {
59 // If an Activation attempt fails, the Unsubscribe method is not
60 // called. This may lead to an AlreadySubscribed error if the
61 // Activation is re-attempted.
62 }
63 else
64 {
65 log<level::ERR>("Error subscribing to systemd",
66 entry("ERROR=%s", e.what()));
67 }
68 }
Michael Tritz9d25b602017-06-14 14:41:43 -050069 return;
70}
71
Michael Tritz1cb127f2017-07-26 15:40:38 -050072void Activation::unsubscribeFromSystemdSignals()
73{
Adriana Kobylak70dcb632018-02-27 15:46:52 -060074 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
75 SYSTEMD_INTERFACE, "Unsubscribe");
Michael Tritz1cb127f2017-07-26 15:40:38 -050076 this->bus.call_noreply(method);
77
78 return;
79}
80
Michael Tritz1cb127f2017-07-26 15:40:38 -050081void Activation::startActivation()
82{
83 // Since the squashfs image has not yet been loaded to pnor and the
84 // RW volumes have not yet been created, we need to start the
85 // service files for each of those actions.
86
87 if (!activationProgress)
88 {
Adriana Kobylak70dcb632018-02-27 15:46:52 -060089 activationProgress = std::make_unique<ActivationProgress>(bus, path);
Michael Tritz1cb127f2017-07-26 15:40:38 -050090 }
91
92 if (!activationBlocksTransition)
93 {
94 activationBlocksTransition =
Adriana Kobylak70dcb632018-02-27 15:46:52 -060095 std::make_unique<ActivationBlocksTransition>(bus, path);
Michael Tritz1cb127f2017-07-26 15:40:38 -050096 }
97
Michael Tritz1cb127f2017-07-26 15:40:38 -050098 constexpr auto ubimountService = "obmc-flash-bios-ubimount@";
Adriana Kobylak70dcb632018-02-27 15:46:52 -060099 auto ubimountServiceFile =
100 std::string(ubimountService) + versionId + ".service";
101 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
102 SYSTEMD_INTERFACE, "StartUnit");
Michael Tritz1cb127f2017-07-26 15:40:38 -0500103 method.append(ubimountServiceFile, "replace");
104 bus.call_noreply(method);
105
106 activationProgress->progress(10);
107}
108
109void Activation::finishActivation()
110{
111 activationProgress->progress(90);
Michael Tritz1cb127f2017-07-26 15:40:38 -0500112
113 // Set Redundancy Priority before setting to Active
114 if (!redundancyPriority)
115 {
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600116 redundancyPriority =
117 std::make_unique<RedundancyPriority>(bus, path, *this, 0);
Michael Tritz1cb127f2017-07-26 15:40:38 -0500118 }
119
120 activationProgress->progress(100);
121
122 activationBlocksTransition.reset(nullptr);
123 activationProgress.reset(nullptr);
124
Saqib Khan1e0aa5c2017-08-31 11:04:17 -0500125 ubiVolumesCreated = false;
Michael Tritz1cb127f2017-07-26 15:40:38 -0500126 Activation::unsubscribeFromSystemdSignals();
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500127 // Remove version object from image manager
128 Activation::deleteImageManagerObject();
Gunnar Mills9741cd12017-08-28 15:09:00 -0500129 // Create active association
130 parent.createActiveAssociation(path);
Michael Tritz1cb127f2017-07-26 15:40:38 -0500131}
132
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600133auto Activation::activation(Activations value) -> Activations
Adriana Kobylakbefe5ce2017-04-05 15:57:44 -0500134{
Saqib Khan942df8a2017-06-01 14:09:27 -0500135
136 if (value != softwareServer::Activation::Activations::Active)
137 {
138 redundancyPriority.reset(nullptr);
139 }
140
Adriana Kobylak99c8c0e2017-04-17 13:39:11 -0500141 if (value == softwareServer::Activation::Activations::Activating)
142 {
Saqib Khan2cbfa032017-08-17 14:52:37 -0500143 parent.freeSpace();
Adriana Kobylak2fdb9312017-05-14 19:08:26 -0500144 softwareServer::Activation::activation(value);
145
Saqib Khan1e0aa5c2017-08-31 11:04:17 -0500146 if (ubiVolumesCreated == false)
Adriana Kobylak99c8c0e2017-04-17 13:39:11 -0500147 {
Gunnar Mills74b657e2018-07-13 09:27:31 -0500148 // Enable systemd signals
149 Activation::subscribeToSystemdSignals();
Jayanth Othayoth4016e522018-03-20 09:39:06 -0500150
151#ifdef WANT_SIGNATURE_VERIFY
Jayanth Othayoth4016e522018-03-20 09:39:06 -0500152 // Validate the signed image.
Jayanth Othayoth11271fb2018-03-29 10:25:50 -0500153 if (!validateSignature())
Jayanth Othayoth4016e522018-03-20 09:39:06 -0500154 {
Jayanth Othayoth11271fb2018-03-29 10:25:50 -0500155 // Cleanup
156 activationBlocksTransition.reset(nullptr);
157 activationProgress.reset(nullptr);
Jayanth Othayoth4016e522018-03-20 09:39:06 -0500158
159 return softwareServer::Activation::activation(
160 softwareServer::Activation::Activations::Failed);
161 }
162#endif
Michael Tritz1cb127f2017-07-26 15:40:38 -0500163 Activation::startActivation();
Michael Tritz9d25b602017-06-14 14:41:43 -0500164 return softwareServer::Activation::activation(value);
165 }
Saqib Khan1e0aa5c2017-08-31 11:04:17 -0500166 else if (ubiVolumesCreated == true)
Michael Tritz9d25b602017-06-14 14:41:43 -0500167 {
168 // Only when the squashfs image is finished loading AND the RW
Michael Tritz1cb127f2017-07-26 15:40:38 -0500169 // volumes have been created do we proceed with activation. To
170 // verify that this happened, we check for the mount dirs PNOR_PRSV
171 // and PNOR_RW_PREFIX_<versionid>, as well as the image dir R0.
Michael Tritz9d25b602017-06-14 14:41:43 -0500172
Michael Tritz9d25b602017-06-14 14:41:43 -0500173 if ((fs::is_directory(PNOR_PRSV)) &&
174 (fs::is_directory(PNOR_RW_PREFIX + versionId)) &&
175 (fs::is_directory(PNOR_RO_PREFIX + versionId)))
176 {
Michael Tritz1cb127f2017-07-26 15:40:38 -0500177 Activation::finishActivation();
Michael Tritz9d25b602017-06-14 14:41:43 -0500178 return softwareServer::Activation::activation(
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600179 softwareServer::Activation::Activations::Active);
Michael Tritz9d25b602017-06-14 14:41:43 -0500180 }
181 else
182 {
Saqib Khancb9df4e2017-06-26 11:06:07 -0500183 activationBlocksTransition.reset(nullptr);
Michael Tritz1793b642017-06-28 18:35:58 -0500184 activationProgress.reset(nullptr);
Michael Tritz9d25b602017-06-14 14:41:43 -0500185 return softwareServer::Activation::activation(
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600186 softwareServer::Activation::Activations::Failed);
Michael Tritz9d25b602017-06-14 14:41:43 -0500187 }
Adriana Kobylak55f9e832017-05-14 16:13:00 -0500188 }
Adriana Kobylak692b5552017-04-17 14:02:58 -0500189 }
Adriana Kobylak2fdb9312017-05-14 19:08:26 -0500190 else
191 {
192 activationBlocksTransition.reset(nullptr);
Michael Tritz1793b642017-06-28 18:35:58 -0500193 activationProgress.reset(nullptr);
Adriana Kobylak2fdb9312017-05-14 19:08:26 -0500194 }
Michael Tritz1cb127f2017-07-26 15:40:38 -0500195
196 return softwareServer::Activation::activation(value);
Adriana Kobylak2fdb9312017-05-14 19:08:26 -0500197}
198
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600199auto Activation::requestedActivation(RequestedActivations value)
200 -> RequestedActivations
Adriana Kobylak2fdb9312017-05-14 19:08:26 -0500201{
Saqib Khan1e0aa5c2017-08-31 11:04:17 -0500202 ubiVolumesCreated = false;
Michael Tritz9d25b602017-06-14 14:41:43 -0500203
Adriana Kobylak2fdb9312017-05-14 19:08:26 -0500204 if ((value == softwareServer::Activation::RequestedActivations::Active) &&
205 (softwareServer::Activation::requestedActivation() !=
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600206 softwareServer::Activation::RequestedActivations::Active))
Adriana Kobylak2fdb9312017-05-14 19:08:26 -0500207 {
208 if ((softwareServer::Activation::activation() ==
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600209 softwareServer::Activation::Activations::Ready) ||
Adriana Kobylak2fdb9312017-05-14 19:08:26 -0500210 (softwareServer::Activation::activation() ==
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600211 softwareServer::Activation::Activations::Failed))
Adriana Kobylak2fdb9312017-05-14 19:08:26 -0500212 {
213 Activation::activation(
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600214 softwareServer::Activation::Activations::Activating);
Adriana Kobylak2fdb9312017-05-14 19:08:26 -0500215 }
216 }
Adriana Kobylak99c8c0e2017-04-17 13:39:11 -0500217 return softwareServer::Activation::requestedActivation(value);
Adriana Kobylakbefe5ce2017-04-05 15:57:44 -0500218}
219
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500220void Activation::deleteImageManagerObject()
221{
222 // Get the Delete object for <versionID> inside image_manager
Lei YUc9caf862019-01-24 15:40:25 +0800223 constexpr auto versionServiceStr = "xyz.openbmc_project.Software.Version";
224 constexpr auto deleteInterface = "xyz.openbmc_project.Object.Delete";
225 std::string versionService;
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600226 auto method = this->bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
227 MAPPER_INTERFACE, "GetObject");
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500228
229 method.append(path);
Lei YUc9caf862019-01-24 15:40:25 +0800230 method.append(std::vector<std::string>({deleteInterface}));
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500231 auto mapperResponseMsg = bus.call(method);
232 if (mapperResponseMsg.is_method_error())
233 {
234 log<level::ERR>("Error in Get Delete Object",
Joseph Reynoldsafd0a452018-05-30 11:16:03 -0500235 entry("VERSIONPATH=%s", path.c_str()));
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500236 return;
237 }
238 std::map<std::string, std::vector<std::string>> mapperResponse;
239 mapperResponseMsg.read(mapperResponse);
240 if (mapperResponse.begin() == mapperResponse.end())
241 {
242 log<level::ERR>("ERROR in reading the mapper response",
Joseph Reynoldsafd0a452018-05-30 11:16:03 -0500243 entry("VERSIONPATH=%s", path.c_str()));
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500244 return;
245 }
246
Lei YUc9caf862019-01-24 15:40:25 +0800247 // We need to find the phosphor-software-manager's version service
248 // to invoke the delete interface
249 for (auto resp : mapperResponse)
250 {
251 if (resp.first.find(versionServiceStr) != std::string::npos)
252 {
253 versionService = resp.first;
254 }
255 }
256
257 if (versionService.empty())
258 {
259 log<level::ERR>("Error finding version service");
260 return;
261 }
262
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500263 // Call the Delete object for <versionID> inside image_manager
Lei YUc9caf862019-01-24 15:40:25 +0800264 method = this->bus.new_method_call(versionService.c_str(), path.c_str(),
265 deleteInterface, "Delete");
Adriana Kobylakab435df2018-07-16 11:37:19 -0500266 try
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500267 {
Adriana Kobylakab435df2018-07-16 11:37:19 -0500268 auto mapperResponseMsg = bus.call(method);
269
270 // Check that the bus call didn't result in an error
271 if (mapperResponseMsg.is_method_error())
272 {
273 log<level::ERR>("Error in Deleting image from image manager",
274 entry("VERSIONPATH=%s", path.c_str()));
275 return;
276 }
277 }
278 catch (const SdBusError& e)
279 {
280 if (e.name() != nullptr && strcmp("System.Error.ELOOP", e.name()) == 0)
281 {
282 // TODO: Error being tracked with openbmc/openbmc#3311
283 }
284 else
285 {
286 log<level::ERR>("Error performing call to Delete object path",
287 entry("ERROR=%s", e.what()),
288 entry("PATH=%s", path.c_str()));
289 }
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500290 return;
291 }
292}
293
Saqib Khan2021b4c2017-06-07 14:37:36 -0500294uint8_t RedundancyPriority::priority(uint8_t value)
295{
Saqib Khanb8e7f312017-08-12 10:24:10 -0500296 parent.parent.freePriority(value, parent.versionId);
Michael Tritz60bc20f2017-07-29 23:32:21 -0500297 storeToFile(parent.versionId, value);
Saqib Khan2021b4c2017-06-07 14:37:36 -0500298 return softwareServer::RedundancyPriority::priority(value);
299}
300
Michael Tritz9d25b602017-06-14 14:41:43 -0500301void Activation::unitStateChange(sdbusplus::message::message& msg)
302{
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600303 uint32_t newStateID{};
Michael Tritz9d25b602017-06-14 14:41:43 -0500304 sdbusplus::message::object_path newStateObjPath;
305 std::string newStateUnit{};
306 std::string newStateResult{};
307
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600308 // Read the msg and populate each variable
Michael Tritz9d25b602017-06-14 14:41:43 -0500309 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
310
Michael Tritz9d25b602017-06-14 14:41:43 -0500311 auto ubimountServiceFile =
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600312 "obmc-flash-bios-ubimount@" + versionId + ".service";
Michael Tritz9d25b602017-06-14 14:41:43 -0500313
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600314 if (newStateUnit == ubimountServiceFile && newStateResult == "done")
Michael Tritz9d25b602017-06-14 14:41:43 -0500315 {
Saqib Khan1e0aa5c2017-08-31 11:04:17 -0500316 ubiVolumesCreated = true;
Michael Tritz1793b642017-06-28 18:35:58 -0500317 activationProgress->progress(activationProgress->progress() + 50);
Michael Tritz9d25b602017-06-14 14:41:43 -0500318 }
319
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600320 if (ubiVolumesCreated)
Michael Tritz9d25b602017-06-14 14:41:43 -0500321 {
322 Activation::activation(
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600323 softwareServer::Activation::Activations::Activating);
Michael Tritz9d25b602017-06-14 14:41:43 -0500324 }
325
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600326 if ((newStateUnit == ubimountServiceFile) &&
Michael Tritz9d25b602017-06-14 14:41:43 -0500327 (newStateResult == "failed" || newStateResult == "dependency"))
328 {
329 Activation::activation(softwareServer::Activation::Activations::Failed);
330 }
331
332 return;
333}
334
Jayanth Othayoth11271fb2018-03-29 10:25:50 -0500335#ifdef WANT_SIGNATURE_VERIFY
336inline bool Activation::validateSignature()
337{
338 using Signature = openpower::software::image::Signature;
339 fs::path imageDir(IMG_DIR);
340
341 Signature signature(imageDir / versionId, PNOR_SIGNED_IMAGE_CONF_PATH);
342
343 // Validate the signed image.
344 if (signature.verify())
345 {
346 return true;
347 }
348 // Log error and continue activation process, if field mode disabled.
349 log<level::ERR>("Error occurred during image validation");
350 report<InternalFailure>();
351
352 try
353 {
354 if (!fieldModeEnabled())
355 {
356 return true;
357 }
358 }
359 catch (const InternalFailure& e)
360 {
361 report<InternalFailure>();
362 }
363 return false;
364}
365
366bool Activation::fieldModeEnabled()
367{
368 auto fieldModeSvc = getService(bus, FIELDMODE_PATH, FIELDMODE_INTERFACE);
369
370 auto method = bus.new_method_call(fieldModeSvc.c_str(), FIELDMODE_PATH,
371 "org.freedesktop.DBus.Properties", "Get");
372
373 method.append(FIELDMODE_INTERFACE, "FieldModeEnabled");
374 auto reply = bus.call(method);
375 if (reply.is_method_error())
376 {
377 log<level::ERR>("Error in fieldModeEnabled getValue");
378 elog<InternalFailure>();
379 }
380 sdbusplus::message::variant<bool> fieldMode;
381 reply.read(fieldMode);
382
William A. Kennington III17f55a82018-11-27 15:22:05 -0800383 return sdbusplus::message::variant_ns::get<bool>(fieldMode);
Jayanth Othayoth11271fb2018-03-29 10:25:50 -0500384}
385
386std::string Activation::getService(sdbusplus::bus::bus& bus,
387 const std::string& path,
388 const std::string& intf)
389{
390 auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
391 MAPPER_INTERFACE, "GetObject");
392
393 mapperCall.append(path);
394 mapperCall.append(std::vector<std::string>({intf}));
395
396 auto mapperResponseMsg = bus.call(mapperCall);
397
398 if (mapperResponseMsg.is_method_error())
399 {
400 log<level::ERR>("ERROR in getting service",
401 entry("PATH=%s", path.c_str()),
402 entry("INTERFACE=%s", intf.c_str()));
403
404 elog<InternalFailure>();
405 }
406
407 std::map<std::string, std::vector<std::string>> mapperResponse;
408 mapperResponseMsg.read(mapperResponse);
409
410 if (mapperResponse.begin() == mapperResponse.end())
411 {
412 log<level::ERR>("ERROR reading mapper response",
413 entry("PATH=%s", path.c_str()),
414 entry("INTERFACE=%s", intf.c_str()));
415
416 elog<InternalFailure>();
417 }
418 return mapperResponse.begin()->first;
419}
420#endif
421
Adriana Kobylakbefe5ce2017-04-05 15:57:44 -0500422} // namespace updater
423} // namespace software
424} // namespace openpower