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