blob: 4a54fbb7e2da9f3771d0f2698c4f7f7dcf14bee7 [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
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600223 auto method = this->bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
224 MAPPER_INTERFACE, "GetObject");
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500225
226 method.append(path);
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600227 method.append(
228 std::vector<std::string>({"xyz.openbmc_project.Object.Delete"}));
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500229 auto mapperResponseMsg = bus.call(method);
230 if (mapperResponseMsg.is_method_error())
231 {
232 log<level::ERR>("Error in Get Delete Object",
Joseph Reynoldsafd0a452018-05-30 11:16:03 -0500233 entry("VERSIONPATH=%s", path.c_str()));
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500234 return;
235 }
236 std::map<std::string, std::vector<std::string>> mapperResponse;
237 mapperResponseMsg.read(mapperResponse);
238 if (mapperResponse.begin() == mapperResponse.end())
239 {
240 log<level::ERR>("ERROR in reading the mapper response",
Joseph Reynoldsafd0a452018-05-30 11:16:03 -0500241 entry("VERSIONPATH=%s", path.c_str()));
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500242 return;
243 }
244
245 // Call the Delete object for <versionID> inside image_manager
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600246 method = this->bus.new_method_call(
247 (mapperResponse.begin()->first).c_str(), path.c_str(),
248 "xyz.openbmc_project.Object.Delete", "Delete");
Adriana Kobylakab435df2018-07-16 11:37:19 -0500249 try
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500250 {
Adriana Kobylakab435df2018-07-16 11:37:19 -0500251 auto mapperResponseMsg = bus.call(method);
252
253 // Check that the bus call didn't result in an error
254 if (mapperResponseMsg.is_method_error())
255 {
256 log<level::ERR>("Error in Deleting image from image manager",
257 entry("VERSIONPATH=%s", path.c_str()));
258 return;
259 }
260 }
261 catch (const SdBusError& e)
262 {
263 if (e.name() != nullptr && strcmp("System.Error.ELOOP", e.name()) == 0)
264 {
265 // TODO: Error being tracked with openbmc/openbmc#3311
266 }
267 else
268 {
269 log<level::ERR>("Error performing call to Delete object path",
270 entry("ERROR=%s", e.what()),
271 entry("PATH=%s", path.c_str()));
272 }
Saqib Khan7f80e0b2017-10-22 11:29:07 -0500273 return;
274 }
275}
276
Saqib Khan2021b4c2017-06-07 14:37:36 -0500277uint8_t RedundancyPriority::priority(uint8_t value)
278{
Saqib Khanb8e7f312017-08-12 10:24:10 -0500279 parent.parent.freePriority(value, parent.versionId);
Michael Tritz60bc20f2017-07-29 23:32:21 -0500280 storeToFile(parent.versionId, value);
Saqib Khan2021b4c2017-06-07 14:37:36 -0500281 return softwareServer::RedundancyPriority::priority(value);
282}
283
Michael Tritz9d25b602017-06-14 14:41:43 -0500284void Activation::unitStateChange(sdbusplus::message::message& msg)
285{
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600286 uint32_t newStateID{};
Michael Tritz9d25b602017-06-14 14:41:43 -0500287 sdbusplus::message::object_path newStateObjPath;
288 std::string newStateUnit{};
289 std::string newStateResult{};
290
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600291 // Read the msg and populate each variable
Michael Tritz9d25b602017-06-14 14:41:43 -0500292 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
293
Michael Tritz9d25b602017-06-14 14:41:43 -0500294 auto ubimountServiceFile =
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600295 "obmc-flash-bios-ubimount@" + versionId + ".service";
Michael Tritz9d25b602017-06-14 14:41:43 -0500296
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600297 if (newStateUnit == ubimountServiceFile && newStateResult == "done")
Michael Tritz9d25b602017-06-14 14:41:43 -0500298 {
Saqib Khan1e0aa5c2017-08-31 11:04:17 -0500299 ubiVolumesCreated = true;
Michael Tritz1793b642017-06-28 18:35:58 -0500300 activationProgress->progress(activationProgress->progress() + 50);
Michael Tritz9d25b602017-06-14 14:41:43 -0500301 }
302
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600303 if (ubiVolumesCreated)
Michael Tritz9d25b602017-06-14 14:41:43 -0500304 {
305 Activation::activation(
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600306 softwareServer::Activation::Activations::Activating);
Michael Tritz9d25b602017-06-14 14:41:43 -0500307 }
308
Adriana Kobylak70dcb632018-02-27 15:46:52 -0600309 if ((newStateUnit == ubimountServiceFile) &&
Michael Tritz9d25b602017-06-14 14:41:43 -0500310 (newStateResult == "failed" || newStateResult == "dependency"))
311 {
312 Activation::activation(softwareServer::Activation::Activations::Failed);
313 }
314
315 return;
316}
317
Jayanth Othayoth11271fb2018-03-29 10:25:50 -0500318#ifdef WANT_SIGNATURE_VERIFY
319inline bool Activation::validateSignature()
320{
321 using Signature = openpower::software::image::Signature;
322 fs::path imageDir(IMG_DIR);
323
324 Signature signature(imageDir / versionId, PNOR_SIGNED_IMAGE_CONF_PATH);
325
326 // Validate the signed image.
327 if (signature.verify())
328 {
329 return true;
330 }
331 // Log error and continue activation process, if field mode disabled.
332 log<level::ERR>("Error occurred during image validation");
333 report<InternalFailure>();
334
335 try
336 {
337 if (!fieldModeEnabled())
338 {
339 return true;
340 }
341 }
342 catch (const InternalFailure& e)
343 {
344 report<InternalFailure>();
345 }
346 return false;
347}
348
349bool Activation::fieldModeEnabled()
350{
351 auto fieldModeSvc = getService(bus, FIELDMODE_PATH, FIELDMODE_INTERFACE);
352
353 auto method = bus.new_method_call(fieldModeSvc.c_str(), FIELDMODE_PATH,
354 "org.freedesktop.DBus.Properties", "Get");
355
356 method.append(FIELDMODE_INTERFACE, "FieldModeEnabled");
357 auto reply = bus.call(method);
358 if (reply.is_method_error())
359 {
360 log<level::ERR>("Error in fieldModeEnabled getValue");
361 elog<InternalFailure>();
362 }
363 sdbusplus::message::variant<bool> fieldMode;
364 reply.read(fieldMode);
365
366 return (fieldMode.get<bool>());
367}
368
369std::string Activation::getService(sdbusplus::bus::bus& bus,
370 const std::string& path,
371 const std::string& intf)
372{
373 auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
374 MAPPER_INTERFACE, "GetObject");
375
376 mapperCall.append(path);
377 mapperCall.append(std::vector<std::string>({intf}));
378
379 auto mapperResponseMsg = bus.call(mapperCall);
380
381 if (mapperResponseMsg.is_method_error())
382 {
383 log<level::ERR>("ERROR in getting service",
384 entry("PATH=%s", path.c_str()),
385 entry("INTERFACE=%s", intf.c_str()));
386
387 elog<InternalFailure>();
388 }
389
390 std::map<std::string, std::vector<std::string>> mapperResponse;
391 mapperResponseMsg.read(mapperResponse);
392
393 if (mapperResponse.begin() == mapperResponse.end())
394 {
395 log<level::ERR>("ERROR reading mapper response",
396 entry("PATH=%s", path.c_str()),
397 entry("INTERFACE=%s", intf.c_str()));
398
399 elog<InternalFailure>();
400 }
401 return mapperResponse.begin()->first;
402}
403#endif
404
Adriana Kobylakbefe5ce2017-04-05 15:57:44 -0500405} // namespace updater
406} // namespace software
407} // namespace openpower