xref: /openbmc/phosphor-bmc-code-mgmt/common/src/device.cpp (revision 7e446a407a579ec72f48009008ad2f83c01c2262)
1 #include "device.hpp"
2 
3 #include "common/pldm/pldm_package_util.hpp"
4 #include "software.hpp"
5 #include "software_manager.hpp"
6 
7 #include <phosphor-logging/lg2.hpp>
8 #include <sdbusplus/asio/object_server.hpp>
9 #include <sdbusplus/async/context.hpp>
10 #include <sdbusplus/bus.hpp>
11 #include <xyz/openbmc_project/Association/Definitions/server.hpp>
12 #include <xyz/openbmc_project/State/Host/client.hpp>
13 
14 #include <utility>
15 
16 PHOSPHOR_LOG2_USING;
17 
18 using namespace phosphor::software::device;
19 
20 const auto applyTimeImmediate = sdbusplus::common::xyz::openbmc_project::
21     software::ApplyTime::RequestedApplyTimes::Immediate;
22 
23 const auto ActivationInvalid = ActivationInterface::Activations::Invalid;
24 const auto ActivationFailed = ActivationInterface::Activations::Failed;
25 
26 Device::Device(sdbusplus::async::context& ctx, const SoftwareConfig& config,
27                manager::SoftwareManager* parent,
28                std::set<RequestedApplyTimes> allowedApplyTimes =
29                    {RequestedApplyTimes::Immediate,
30                     RequestedApplyTimes::OnReset}) :
31     allowedApplyTimes(std::move(allowedApplyTimes)), config(config),
32     parent(parent), ctx(ctx)
33 {}
34 
35 // NOLINTBEGIN(readability-static-accessed-through-instance)
36 sdbusplus::async::task<bool> Device::getImageInfo(
37     std::unique_ptr<void, std::function<void(void*)>>& pldmPackage,
38     size_t pldmPackageSize, uint8_t** matchingComponentImage,
39     size_t* componentImageSize, std::string& componentVersion)
40 
41 // NOLINTEND(readability-static-accessed-through-instance)
42 {
43     std::shared_ptr<PackageParser> packageParser =
44         pldm_package_util::parsePLDMPackage(
45             static_cast<uint8_t*>(pldmPackage.get()), pldmPackageSize);
46 
47     if (packageParser == nullptr)
48     {
49         error("could not parse PLDM package");
50         co_return false;
51     }
52 
53     uint32_t componentOffset = 0;
54     const int status = pldm_package_util::extractMatchingComponentImage(
55         packageParser, config.compatibleHardware, config.vendorIANA,
56         &componentOffset, componentImageSize, componentVersion);
57 
58     if (status != 0)
59     {
60         error("could not extract matching component image");
61         co_return false;
62     }
63 
64     *matchingComponentImage =
65         static_cast<uint8_t*>(pldmPackage.get()) + componentOffset;
66 
67     co_return true;
68 }
69 
70 // NOLINTBEGIN(readability-static-accessed-through-instance)
71 sdbusplus::async::task<bool> Device::startUpdateAsync(
72     sdbusplus::message::unix_fd image, RequestedApplyTimes applyTime,
73     std::unique_ptr<Software> softwarePendingIn)
74 // NOLINTEND(readability-static-accessed-through-instance)
75 {
76     debug("starting the async update with memfd {FD}", "FD", image.fd);
77 
78     size_t pldm_pkg_size = 0;
79     auto pldm_pkg = pldm_package_util::mmapImagePackage(image, &pldm_pkg_size);
80 
81     if (pldm_pkg == nullptr)
82     {
83         softwarePendingIn->setActivation(ActivationInvalid);
84         co_return false;
85     }
86 
87     uint8_t* componentImage;
88     size_t componentImageSize = 0;
89     std::string componentVersion;
90 
91     if (!co_await getImageInfo(pldm_pkg, pldm_pkg_size, &componentImage,
92                                &componentImageSize, componentVersion))
93     {
94         error("could not extract matching component image");
95         softwarePendingIn->setActivation(ActivationInvalid);
96         co_return false;
97     }
98 
99     std::unique_ptr<Software> softwarePendingOld = std::move(softwarePending);
100 
101     softwarePending = std::move(softwarePendingIn);
102     softwarePendingIn = nullptr;
103 
104     const bool success = co_await continueUpdateWithMappedPackage(
105         componentImage, componentImageSize, componentVersion, applyTime);
106 
107     if (!success)
108     {
109         softwarePending->setActivation(ActivationFailed);
110         error("Failed to update the software for {SWID}", "SWID",
111               softwareCurrent->swid);
112 
113         softwarePending = std::move(softwarePendingOld);
114 
115         co_return false;
116     }
117 
118     if (applyTime == RequestedApplyTimes::Immediate)
119     {
120         softwareCurrent = std::move(softwarePending);
121 
122         // In case an immediate update is triggered after an update for
123         // onReset.
124         softwarePending = nullptr;
125 
126         debug("Successfully updated to software version {SWID}", "SWID",
127               softwareCurrent->swid);
128     }
129 
130     co_return true;
131 }
132 
133 std::string Device::getEMConfigType() const
134 {
135     return config.configType;
136 }
137 
138 // NOLINTBEGIN(readability-static-accessed-through-instance)
139 sdbusplus::async::task<bool> Device::resetDevice()
140 // NOLINTEND(readability-static-accessed-through-instance)
141 {
142     debug("Default implementation for device reset");
143 
144     co_return true;
145 }
146 
147 bool Device::setUpdateProgress(uint8_t progress) const
148 {
149     if (!softwarePending || !softwarePending->softwareActivationProgress)
150     {
151         return false;
152     }
153 
154     softwarePending->softwareActivationProgress->setProgress(progress);
155 
156     return true;
157 }
158 
159 // NOLINTBEGIN(readability-static-accessed-through-instance)
160 sdbusplus::async::task<bool> Device::continueUpdateWithMappedPackage(
161     const uint8_t* matchingComponentImage, size_t componentImageSize,
162     const std::string& componentVersion, RequestedApplyTimes applyTime)
163 // NOLINTEND(readability-static-accessed-through-instance)
164 {
165     softwarePending->setActivation(ActivationInterface::Activations::Ready);
166 
167     softwarePending->setVersion(componentVersion,
168                                 softwareCurrent->getPurpose());
169 
170     std::string objPath = softwarePending->objectPath;
171 
172     softwarePending->softwareActivationProgress =
173         std::make_unique<SoftwareActivationProgress>(ctx, objPath.c_str());
174 
175     softwarePending->setActivationBlocksTransition(true);
176 
177     softwarePending->setActivation(
178         ActivationInterface::Activations::Activating);
179 
180     bool success =
181         co_await updateDevice(matchingComponentImage, componentImageSize);
182 
183     if (success)
184     {
185         softwarePending->setActivation(
186             ActivationInterface::Activations::Active);
187     }
188 
189     softwarePending->setActivationBlocksTransition(false);
190 
191     softwarePending->softwareActivationProgress = nullptr;
192 
193     if (!success)
194     {
195         // do not apply the update, it has failed.
196         // We can delete the new software version.
197 
198         co_return false;
199     }
200 
201     if (applyTime == applyTimeImmediate)
202     {
203         co_await resetDevice();
204 
205         co_await softwarePending->createInventoryAssociations(true);
206 
207         softwarePending->enableUpdate(allowedApplyTimes);
208     }
209     else
210     {
211         co_await softwarePending->createInventoryAssociations(false);
212     }
213 
214     co_return true;
215 }
216