blob: bb0688231c7ee7826f3315a583ff6288ad8bea5a [file] [log] [blame]
Sampa Misraaea5dde2020-08-31 08:33:47 -05001#include "inband_code_update.hpp"
2
Sagar Srinivas78a225a2020-08-27 00:52:20 -05003#include "libpldm/entity.h"
4
5#include "libpldmresponder/pdr.hpp"
Sampa Misraaea5dde2020-08-31 08:33:47 -05006#include "oem_ibm_handler.hpp"
7#include "xyz/openbmc_project/Common/error.hpp"
8
Adriana Kobylak727f7382020-09-01 14:38:25 -05009#include <arpa/inet.h>
10
Sampa Misraaea5dde2020-08-31 08:33:47 -050011#include <sdbusplus/server.hpp>
12#include <xyz/openbmc_project/Dump/NewDump/server.hpp>
13
14#include <exception>
Adriana Kobylak727f7382020-09-01 14:38:25 -050015#include <fstream>
Sampa Misraaea5dde2020-08-31 08:33:47 -050016namespace pldm
17{
Sampa Misraaea5dde2020-08-31 08:33:47 -050018namespace responder
19{
20using namespace oem_ibm_platform;
21
Adriana Kobylakfa810d72020-10-16 16:27:28 -050022/** @brief Directory where the lid files without a header are stored */
23auto lidDirPath = fs::path(LID_STAGING_DIR) / "lid";
24
Adriana Kobylaka1f158c2020-11-09 12:47:29 -060025/** @brief Directory where the image files are stored as they are built */
26auto imageDirPath = fs::path(LID_STAGING_DIR) / "image";
27
Adriana Kobylak837fb472020-10-16 16:53:42 -050028/** @brief Directory where the code update tarball files are stored */
29auto updateDirPath = fs::path(LID_STAGING_DIR) / "update";
30
Adriana Kobylaka1f158c2020-11-09 12:47:29 -060031/** @brief The file name of the code update tarball */
32constexpr auto tarImageName = "image.tar";
33
Adriana Kobylak837fb472020-10-16 16:53:42 -050034/** @brief The file name of the hostfw image */
35constexpr auto hostfwImageName = "image-host-fw";
36
Adriana Kobylaka1f158c2020-11-09 12:47:29 -060037/** @brief The path to the code update tarball file */
38auto tarImagePath = fs::path(imageDirPath) / tarImageName;
39
Adriana Kobylak837fb472020-10-16 16:53:42 -050040/** @brief The path to the hostfw image */
41auto hostfwImagePath = fs::path(imageDirPath) / hostfwImageName;
42
43/** @brief The path to the tarball file expected by the phosphor software
44 * manager */
45auto updateImagePath = fs::path("/tmp/images") / tarImageName;
46
Sampa Misraaea5dde2020-08-31 08:33:47 -050047std::string CodeUpdate::fetchCurrentBootSide()
48{
49 return currBootSide;
50}
51
52std::string CodeUpdate::fetchNextBootSide()
53{
54 return nextBootSide;
55}
56
57int CodeUpdate::setCurrentBootSide(const std::string& currSide)
58{
59 currBootSide = currSide;
60 return PLDM_SUCCESS;
61}
62
63int CodeUpdate::setNextBootSide(const std::string& nextSide)
64{
65 nextBootSide = nextSide;
66 std::string objPath{};
67 if (nextBootSide == currBootSide)
68 {
69 objPath = runningVersion;
70 }
71 else
72 {
73 objPath = nonRunningVersion;
74 }
75 if (objPath.empty())
76 {
77 std::cerr << "no nonRunningVersion present \n";
78 return PLDM_PLATFORM_INVALID_STATE_VALUE;
79 }
80
81 pldm::utils::DBusMapping dbusMapping{objPath, redundancyIntf, "Priority",
82 "uint8_t"};
83 uint8_t val = 0;
84 pldm::utils::PropertyValue value = static_cast<uint8_t>(val);
85 try
86 {
87 dBusIntf->setDbusProperty(dbusMapping, value);
88 }
89 catch (const std::exception& e)
90 {
91 std::cerr << "failed to set the next boot side to " << objPath.c_str()
92 << " ERROR=" << e.what() << "\n";
93 return PLDM_ERROR;
94 }
95 return PLDM_SUCCESS;
96}
97
Sagar Srinivascfdbca72020-09-22 10:03:35 -050098int CodeUpdate::setRequestedApplyTime()
99{
100 int rc = PLDM_SUCCESS;
101 pldm::utils::PropertyValue value =
102 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
103 DBusMapping dbusMapping;
104 dbusMapping.objectPath = "/xyz/openbmc_project/software/apply_time";
105 dbusMapping.interface = "xyz.openbmc_project.Software.ApplyTime";
106 dbusMapping.propertyName = "RequestedApplyTime";
107 dbusMapping.propertyType = "string";
108 try
109 {
110 pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
111 }
112 catch (const std::exception& e)
113 {
114 std::cerr << "Failed To set RequestedApplyTime property "
115 << "ERROR=" << e.what() << std::endl;
116 rc = PLDM_ERROR;
117 }
118 return rc;
119}
120
121int CodeUpdate::setRequestedActivation()
122{
123 int rc = PLDM_SUCCESS;
124 pldm::utils::PropertyValue value =
125 "xyz.openbmc_project.Software.Activation.RequestedActivations.Active";
126 DBusMapping dbusMapping;
127 dbusMapping.objectPath = newImageId;
128 dbusMapping.interface = "xyz.openbmc_project.Software.Activation";
129 dbusMapping.propertyName = "RequestedActivation";
130 dbusMapping.propertyType = "string";
131 try
132 {
133 pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
134 }
135 catch (const std::exception& e)
136 {
137 std::cerr << "Failed To set RequestedActivation property"
138 << "ERROR=" << e.what() << std::endl;
139 rc = PLDM_ERROR;
140 }
141 newImageId.clear();
142 return rc;
143}
144
Sampa Misraaea5dde2020-08-31 08:33:47 -0500145void CodeUpdate::setVersions()
146{
147 static constexpr auto mapperService = "xyz.openbmc_project.ObjectMapper";
148 static constexpr auto functionalObjPath =
149 "/xyz/openbmc_project/software/functional";
150 static constexpr auto activeObjPath =
151 "/xyz/openbmc_project/software/active";
152 static constexpr auto propIntf = "org.freedesktop.DBus.Properties";
153
154 auto& bus = dBusIntf->getBus();
Sampa Misraaea5dde2020-08-31 08:33:47 -0500155 try
156 {
157 auto method = bus.new_method_call(mapperService, functionalObjPath,
158 propIntf, "Get");
159 method.append("xyz.openbmc_project.Association", "endpoints");
160 std::variant<std::vector<std::string>> paths;
161
162 auto reply = bus.call(method);
163 reply.read(paths);
164
165 runningVersion = std::get<std::vector<std::string>>(paths)[0];
166
167 auto method1 =
168 bus.new_method_call(mapperService, activeObjPath, propIntf, "Get");
169 method1.append("xyz.openbmc_project.Association", "endpoints");
170
171 auto reply1 = bus.call(method1);
172 reply1.read(paths);
173 for (const auto& path : std::get<std::vector<std::string>>(paths))
174 {
175 if (path != runningVersion)
176 {
177 nonRunningVersion = path;
178 break;
179 }
180 }
181 }
182 catch (const std::exception& e)
183 {
184 std::cerr << "failed to make a d-bus call to Object Mapper "
185 "Association, ERROR="
186 << e.what() << "\n";
187 return;
188 }
189
190 using namespace sdbusplus::bus::match::rules;
191 captureNextBootSideChange.push_back(
192 std::make_unique<sdbusplus::bus::match::match>(
193 pldm::utils::DBusHandler::getBus(),
194 propertiesChanged(runningVersion, redundancyIntf),
195 [this](sdbusplus::message::message& msg) {
196 DbusChangedProps props;
197 std::string iface;
198 msg.read(iface, props);
199 processPriorityChangeNotification(props);
200 }));
201 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>(
202 pldm::utils::DBusHandler::getBus(),
203 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
204 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
205 [this](sdbusplus::message::message& msg) {
206 DBusInterfaceAdded interfaces;
207 sdbusplus::message::object_path path;
208 msg.read(path, interfaces);
209 for (auto& interface : interfaces)
210 {
211 if (interface.first ==
212 "xyz.openbmc_project.Software.Activation")
213 {
214 newImageId = path.str;
215 break;
216 }
217 }
218 });
219}
220
221void CodeUpdate::processPriorityChangeNotification(
222 const DbusChangedProps& chProperties)
223{
224 static constexpr auto propName = "Priority";
225 const auto it = chProperties.find(propName);
226 if (it == chProperties.end())
227 {
228 return;
229 }
230 uint8_t newVal = std::get<uint8_t>(it->second);
231 nextBootSide = (newVal == 0) ? currBootSide
232 : ((currBootSide == Tside) ? Pside : Tside);
233}
234
235void CodeUpdate::setOemPlatformHandler(
236 pldm::responder::oem_platform::Handler* handler)
237{
238 oemPlatformHandler = handler;
239}
240
Varsha Kaverappa3ca29df2020-09-27 12:39:22 -0500241void CodeUpdate::clearDirPath(const std::string& dirPath)
242{
243 for (auto& path : fs::directory_iterator(dirPath.c_str()))
244 {
245 fs::remove_all(path);
246 }
247 return;
248}
249
Sampa Misraaea5dde2020-08-31 08:33:47 -0500250uint8_t fetchBootSide(uint16_t entityInstance, CodeUpdate* codeUpdate)
251{
252 uint8_t sensorOpState = tSideNum;
Sampa Misraaea5dde2020-08-31 08:33:47 -0500253 if (entityInstance == 0)
254 {
255 auto currSide = codeUpdate->fetchCurrentBootSide();
256 if (currSide == Pside)
257 {
258 sensorOpState = pSideNum;
259 }
260 }
261 else if (entityInstance == 1)
262 {
263 auto nextSide = codeUpdate->fetchNextBootSide();
264 if (nextSide == Pside)
265 {
266 sensorOpState = pSideNum;
267 }
268 }
269 else
270 {
271 sensorOpState = PLDM_SENSOR_UNKNOWN;
272 }
273
274 return sensorOpState;
275}
276
277int setBootSide(uint16_t entityInstance, uint8_t currState,
278 const std::vector<set_effecter_state_field>& stateField,
279 CodeUpdate* codeUpdate)
280{
281 int rc = PLDM_SUCCESS;
282 auto side = (stateField[currState].effecter_state == pSideNum) ? "P" : "T";
283
284 if (entityInstance == 0)
285 {
286 rc = codeUpdate->setCurrentBootSide(side);
287 }
288 else if (entityInstance == 1)
289 {
290 rc = codeUpdate->setNextBootSide(side);
291 }
292 else
293 {
294 rc = PLDM_PLATFORM_INVALID_STATE_VALUE;
295 }
296 return rc;
297}
298
Adriana Kobylak837fb472020-10-16 16:53:42 -0500299template <typename... T>
300int executeCmd(T const&... t)
301{
302 std::stringstream cmd;
303 ((cmd << t << " "), ...) << std::endl;
304 FILE* pipe = popen(cmd.str().c_str(), "r");
305 if (!pipe)
306 {
307 throw std::runtime_error("popen() failed!");
308 }
309 int rc = pclose(pipe);
310 if (WEXITSTATUS(rc))
311 {
312 std::cerr << "Error executing: ";
313 ((std::cerr << " " << t), ...);
314 std::cerr << "\n";
315 return -1;
316 }
317
318 return 0;
319}
320
Adriana Kobylak727f7382020-09-01 14:38:25 -0500321int processCodeUpdateLid(const std::string& filePath)
322{
323 struct LidHeader
324 {
325 uint16_t magicNumber;
Adriana Kobylak86d14182020-10-16 16:11:08 -0500326 uint16_t headerVersion;
327 uint32_t lidNumber;
328 uint32_t lidDate;
329 uint16_t lidTime;
330 uint16_t lidClass;
331 uint32_t lidCrc;
332 uint32_t lidSize;
333 uint32_t headerSize;
Adriana Kobylak727f7382020-09-01 14:38:25 -0500334 };
335 LidHeader header;
336
337 std::ifstream ifs(filePath, std::ios::in | std::ios::binary);
338 if (!ifs)
339 {
340 std::cerr << "ifstream open error: " << filePath << "\n";
341 return PLDM_ERROR;
342 }
343 ifs.seekg(0);
344 ifs.read(reinterpret_cast<char*>(&header), sizeof(header));
Adriana Kobylak727f7382020-09-01 14:38:25 -0500345
Adriana Kobylak86d14182020-10-16 16:11:08 -0500346 // File size should be the value of lid size minus the header size
347 auto fileSize = fs::file_size(filePath);
348 fileSize -= htonl(header.headerSize);
349 if (fileSize < htonl(header.lidSize))
350 {
351 // File is not completely written yet
Adriana Kobylakfa810d72020-10-16 16:27:28 -0500352 ifs.close();
Adriana Kobylak86d14182020-10-16 16:11:08 -0500353 return PLDM_SUCCESS;
354 }
355
Adriana Kobylak727f7382020-09-01 14:38:25 -0500356 constexpr auto magicNumber = 0x0222;
357 if (htons(header.magicNumber) != magicNumber)
358 {
359 std::cerr << "Invalid magic number: " << filePath << "\n";
Adriana Kobylakfa810d72020-10-16 16:27:28 -0500360 ifs.close();
Adriana Kobylak727f7382020-09-01 14:38:25 -0500361 return PLDM_ERROR;
362 }
363
Adriana Kobylaka1f158c2020-11-09 12:47:29 -0600364 fs::create_directories(imageDirPath);
Adriana Kobylakfa810d72020-10-16 16:27:28 -0500365 fs::create_directories(lidDirPath);
366
Adriana Kobylaka1f158c2020-11-09 12:47:29 -0600367 constexpr auto bmcClass = 0x2000;
368 if (htons(header.lidClass) == bmcClass)
369 {
370 // Skip the header and concatenate the BMC LIDs into a tar file
371 std::ofstream ofs(tarImagePath,
372 std::ios::out | std::ios::binary | std::ios::app);
373 ifs.seekg(htonl(header.headerSize));
374 ofs << ifs.rdbuf();
375 ofs.flush();
376 ofs.close();
377 }
378 else
379 {
380 std::stringstream lidFileName;
381 lidFileName << std::hex << htonl(header.lidNumber) << ".lid";
382 auto lidNoHeaderPath = fs::path(lidDirPath) / lidFileName.str();
383 std::ofstream ofs(lidNoHeaderPath,
384 std::ios::out | std::ios::binary | std::ios::trunc);
385 ifs.seekg(htonl(header.headerSize));
386 ofs << ifs.rdbuf();
387 ofs.flush();
388 ofs.close();
389 }
Adriana Kobylakfa810d72020-10-16 16:27:28 -0500390
391 ifs.close();
392 fs::remove(filePath);
Adriana Kobylak727f7382020-09-01 14:38:25 -0500393 return PLDM_SUCCESS;
394}
395
Adriana Kobylak837fb472020-10-16 16:53:42 -0500396int assembleCodeUpdateImage()
397{
398 // Create the hostfw squashfs image from the LID files without header
399 auto rc = executeCmd("/usr/sbin/mksquashfs", lidDirPath.c_str(),
400 hostfwImagePath.c_str(), "-all-root", "-no-recovery");
401 if (rc < 0)
402 {
403 return PLDM_ERROR;
404 }
405
406 fs::create_directories(updateDirPath);
407
408 // Extract the BMC tarball content
409 rc = executeCmd("/bin/tar", "-xf", tarImagePath.c_str(), "-C",
410 updateDirPath);
411 if (rc < 0)
412 {
413 return PLDM_ERROR;
414 }
415
416 // Add the hostfw image to the directory where the contents were extracted
417 fs::copy_file(hostfwImagePath, fs::path(updateDirPath) / hostfwImageName,
418 fs::copy_options::overwrite_existing);
419
420 // Remove the tarball file, then re-generate it with so that the hostfw
421 // image becomes part of the tarball
422 fs::remove(tarImagePath);
423 rc = executeCmd("/bin/tar", "-cf", tarImagePath, ".", "-C", updateDirPath);
424 if (rc < 0)
425 {
426 return PLDM_ERROR;
427 }
428
429 // Copy the tarball to the update directory to trigger the phosphor software
430 // manager to create a version interface
431 fs::copy_file(tarImagePath, updateImagePath,
432 fs::copy_options::overwrite_existing);
433
434 // Cleanup
435 fs::remove_all(updateDirPath);
436 fs::remove_all(lidDirPath);
437 fs::remove_all(imageDirPath);
438
439 return PLDM_SUCCESS;
440}
441
Sampa Misraaea5dde2020-08-31 08:33:47 -0500442} // namespace responder
443} // namespace pldm