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