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