blob: 01d3725e93976d1cc7deda2a2f533e44ba58105c [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 -050016
17namespace pldm
18{
Sampa Misraaea5dde2020-08-31 08:33:47 -050019namespace responder
20{
21using namespace oem_ibm_platform;
22
Adriana Kobylakfa810d72020-10-16 16:27:28 -050023/** @brief Directory where the lid files without a header are stored */
24auto lidDirPath = fs::path(LID_STAGING_DIR) / "lid";
25
Adriana Kobylaka1f158c2020-11-09 12:47:29 -060026/** @brief Directory where the image files are stored as they are built */
27auto imageDirPath = fs::path(LID_STAGING_DIR) / "image";
28
Adriana Kobylak837fb472020-10-16 16:53:42 -050029/** @brief Directory where the code update tarball files are stored */
30auto updateDirPath = fs::path(LID_STAGING_DIR) / "update";
31
Adriana Kobylaka1f158c2020-11-09 12:47:29 -060032/** @brief The file name of the code update tarball */
33constexpr auto tarImageName = "image.tar";
34
Adriana Kobylak837fb472020-10-16 16:53:42 -050035/** @brief The file name of the hostfw image */
36constexpr auto hostfwImageName = "image-host-fw";
37
Adriana Kobylaka1f158c2020-11-09 12:47:29 -060038/** @brief The path to the code update tarball file */
39auto tarImagePath = fs::path(imageDirPath) / tarImageName;
40
Adriana Kobylak837fb472020-10-16 16:53:42 -050041/** @brief The path to the hostfw image */
42auto hostfwImagePath = fs::path(imageDirPath) / hostfwImageName;
43
44/** @brief The path to the tarball file expected by the phosphor software
45 * manager */
46auto updateImagePath = fs::path("/tmp/images") / tarImageName;
47
Sampa Misraaea5dde2020-08-31 08:33:47 -050048std::string CodeUpdate::fetchCurrentBootSide()
49{
50 return currBootSide;
51}
52
53std::string CodeUpdate::fetchNextBootSide()
54{
55 return nextBootSide;
56}
57
58int CodeUpdate::setCurrentBootSide(const std::string& currSide)
59{
60 currBootSide = currSide;
61 return PLDM_SUCCESS;
62}
63
64int CodeUpdate::setNextBootSide(const std::string& nextSide)
65{
66 nextBootSide = nextSide;
67 std::string objPath{};
68 if (nextBootSide == currBootSide)
69 {
70 objPath = runningVersion;
71 }
72 else
73 {
74 objPath = nonRunningVersion;
75 }
76 if (objPath.empty())
77 {
78 std::cerr << "no nonRunningVersion present \n";
79 return PLDM_PLATFORM_INVALID_STATE_VALUE;
80 }
81
82 pldm::utils::DBusMapping dbusMapping{objPath, redundancyIntf, "Priority",
83 "uint8_t"};
84 uint8_t val = 0;
85 pldm::utils::PropertyValue value = static_cast<uint8_t>(val);
86 try
87 {
88 dBusIntf->setDbusProperty(dbusMapping, value);
89 }
90 catch (const std::exception& e)
91 {
92 std::cerr << "failed to set the next boot side to " << objPath.c_str()
93 << " ERROR=" << e.what() << "\n";
94 return PLDM_ERROR;
95 }
96 return PLDM_SUCCESS;
97}
98
99void CodeUpdate::setVersions()
100{
101 static constexpr auto mapperService = "xyz.openbmc_project.ObjectMapper";
102 static constexpr auto functionalObjPath =
103 "/xyz/openbmc_project/software/functional";
104 static constexpr auto activeObjPath =
105 "/xyz/openbmc_project/software/active";
106 static constexpr auto propIntf = "org.freedesktop.DBus.Properties";
107
108 auto& bus = dBusIntf->getBus();
Sampa Misraaea5dde2020-08-31 08:33:47 -0500109 try
110 {
111 auto method = bus.new_method_call(mapperService, functionalObjPath,
112 propIntf, "Get");
113 method.append("xyz.openbmc_project.Association", "endpoints");
114 std::variant<std::vector<std::string>> paths;
115
116 auto reply = bus.call(method);
117 reply.read(paths);
118
119 runningVersion = std::get<std::vector<std::string>>(paths)[0];
120
121 auto method1 =
122 bus.new_method_call(mapperService, activeObjPath, propIntf, "Get");
123 method1.append("xyz.openbmc_project.Association", "endpoints");
124
125 auto reply1 = bus.call(method1);
126 reply1.read(paths);
127 for (const auto& path : std::get<std::vector<std::string>>(paths))
128 {
129 if (path != runningVersion)
130 {
131 nonRunningVersion = path;
132 break;
133 }
134 }
135 }
136 catch (const std::exception& e)
137 {
138 std::cerr << "failed to make a d-bus call to Object Mapper "
139 "Association, ERROR="
140 << e.what() << "\n";
141 return;
142 }
143
144 using namespace sdbusplus::bus::match::rules;
145 captureNextBootSideChange.push_back(
146 std::make_unique<sdbusplus::bus::match::match>(
147 pldm::utils::DBusHandler::getBus(),
148 propertiesChanged(runningVersion, redundancyIntf),
149 [this](sdbusplus::message::message& msg) {
150 DbusChangedProps props;
151 std::string iface;
152 msg.read(iface, props);
153 processPriorityChangeNotification(props);
154 }));
155 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>(
156 pldm::utils::DBusHandler::getBus(),
157 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
158 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
159 [this](sdbusplus::message::message& msg) {
160 DBusInterfaceAdded interfaces;
161 sdbusplus::message::object_path path;
162 msg.read(path, interfaces);
163 for (auto& interface : interfaces)
164 {
165 if (interface.first ==
166 "xyz.openbmc_project.Software.Activation")
167 {
168 newImageId = path.str;
169 break;
170 }
171 }
172 });
173}
174
175void CodeUpdate::processPriorityChangeNotification(
176 const DbusChangedProps& chProperties)
177{
178 static constexpr auto propName = "Priority";
179 const auto it = chProperties.find(propName);
180 if (it == chProperties.end())
181 {
182 return;
183 }
184 uint8_t newVal = std::get<uint8_t>(it->second);
185 nextBootSide = (newVal == 0) ? currBootSide
186 : ((currBootSide == Tside) ? Pside : Tside);
187}
188
189void CodeUpdate::setOemPlatformHandler(
190 pldm::responder::oem_platform::Handler* handler)
191{
192 oemPlatformHandler = handler;
193}
194
195uint8_t fetchBootSide(uint16_t entityInstance, CodeUpdate* codeUpdate)
196{
197 uint8_t sensorOpState = tSideNum;
Sampa Misraaea5dde2020-08-31 08:33:47 -0500198 if (entityInstance == 0)
199 {
200 auto currSide = codeUpdate->fetchCurrentBootSide();
201 if (currSide == Pside)
202 {
203 sensorOpState = pSideNum;
204 }
205 }
206 else if (entityInstance == 1)
207 {
208 auto nextSide = codeUpdate->fetchNextBootSide();
209 if (nextSide == Pside)
210 {
211 sensorOpState = pSideNum;
212 }
213 }
214 else
215 {
216 sensorOpState = PLDM_SENSOR_UNKNOWN;
217 }
218
219 return sensorOpState;
220}
221
222int setBootSide(uint16_t entityInstance, uint8_t currState,
223 const std::vector<set_effecter_state_field>& stateField,
224 CodeUpdate* codeUpdate)
225{
226 int rc = PLDM_SUCCESS;
227 auto side = (stateField[currState].effecter_state == pSideNum) ? "P" : "T";
228
229 if (entityInstance == 0)
230 {
231 rc = codeUpdate->setCurrentBootSide(side);
232 }
233 else if (entityInstance == 1)
234 {
235 rc = codeUpdate->setNextBootSide(side);
236 }
237 else
238 {
239 rc = PLDM_PLATFORM_INVALID_STATE_VALUE;
240 }
241 return rc;
242}
243
Adriana Kobylak837fb472020-10-16 16:53:42 -0500244template <typename... T>
245int executeCmd(T const&... t)
246{
247 std::stringstream cmd;
248 ((cmd << t << " "), ...) << std::endl;
249 FILE* pipe = popen(cmd.str().c_str(), "r");
250 if (!pipe)
251 {
252 throw std::runtime_error("popen() failed!");
253 }
254 int rc = pclose(pipe);
255 if (WEXITSTATUS(rc))
256 {
257 std::cerr << "Error executing: ";
258 ((std::cerr << " " << t), ...);
259 std::cerr << "\n";
260 return -1;
261 }
262
263 return 0;
264}
265
Adriana Kobylak727f7382020-09-01 14:38:25 -0500266int processCodeUpdateLid(const std::string& filePath)
267{
268 struct LidHeader
269 {
270 uint16_t magicNumber;
Adriana Kobylak86d14182020-10-16 16:11:08 -0500271 uint16_t headerVersion;
272 uint32_t lidNumber;
273 uint32_t lidDate;
274 uint16_t lidTime;
275 uint16_t lidClass;
276 uint32_t lidCrc;
277 uint32_t lidSize;
278 uint32_t headerSize;
Adriana Kobylak727f7382020-09-01 14:38:25 -0500279 };
280 LidHeader header;
281
282 std::ifstream ifs(filePath, std::ios::in | std::ios::binary);
283 if (!ifs)
284 {
285 std::cerr << "ifstream open error: " << filePath << "\n";
286 return PLDM_ERROR;
287 }
288 ifs.seekg(0);
289 ifs.read(reinterpret_cast<char*>(&header), sizeof(header));
Adriana Kobylak727f7382020-09-01 14:38:25 -0500290
Adriana Kobylak86d14182020-10-16 16:11:08 -0500291 // File size should be the value of lid size minus the header size
292 auto fileSize = fs::file_size(filePath);
293 fileSize -= htonl(header.headerSize);
294 if (fileSize < htonl(header.lidSize))
295 {
296 // File is not completely written yet
Adriana Kobylakfa810d72020-10-16 16:27:28 -0500297 ifs.close();
Adriana Kobylak86d14182020-10-16 16:11:08 -0500298 return PLDM_SUCCESS;
299 }
300
Adriana Kobylak727f7382020-09-01 14:38:25 -0500301 constexpr auto magicNumber = 0x0222;
302 if (htons(header.magicNumber) != magicNumber)
303 {
304 std::cerr << "Invalid magic number: " << filePath << "\n";
Adriana Kobylakfa810d72020-10-16 16:27:28 -0500305 ifs.close();
Adriana Kobylak727f7382020-09-01 14:38:25 -0500306 return PLDM_ERROR;
307 }
308
Adriana Kobylaka1f158c2020-11-09 12:47:29 -0600309 fs::create_directories(imageDirPath);
Adriana Kobylakfa810d72020-10-16 16:27:28 -0500310 fs::create_directories(lidDirPath);
311
Adriana Kobylaka1f158c2020-11-09 12:47:29 -0600312 constexpr auto bmcClass = 0x2000;
313 if (htons(header.lidClass) == bmcClass)
314 {
315 // Skip the header and concatenate the BMC LIDs into a tar file
316 std::ofstream ofs(tarImagePath,
317 std::ios::out | std::ios::binary | std::ios::app);
318 ifs.seekg(htonl(header.headerSize));
319 ofs << ifs.rdbuf();
320 ofs.flush();
321 ofs.close();
322 }
323 else
324 {
325 std::stringstream lidFileName;
326 lidFileName << std::hex << htonl(header.lidNumber) << ".lid";
327 auto lidNoHeaderPath = fs::path(lidDirPath) / lidFileName.str();
328 std::ofstream ofs(lidNoHeaderPath,
329 std::ios::out | std::ios::binary | std::ios::trunc);
330 ifs.seekg(htonl(header.headerSize));
331 ofs << ifs.rdbuf();
332 ofs.flush();
333 ofs.close();
334 }
Adriana Kobylakfa810d72020-10-16 16:27:28 -0500335
336 ifs.close();
337 fs::remove(filePath);
Adriana Kobylak727f7382020-09-01 14:38:25 -0500338 return PLDM_SUCCESS;
339}
340
Adriana Kobylak837fb472020-10-16 16:53:42 -0500341int assembleCodeUpdateImage()
342{
343 // Create the hostfw squashfs image from the LID files without header
344 auto rc = executeCmd("/usr/sbin/mksquashfs", lidDirPath.c_str(),
345 hostfwImagePath.c_str(), "-all-root", "-no-recovery");
346 if (rc < 0)
347 {
348 return PLDM_ERROR;
349 }
350
351 fs::create_directories(updateDirPath);
352
353 // Extract the BMC tarball content
354 rc = executeCmd("/bin/tar", "-xf", tarImagePath.c_str(), "-C",
355 updateDirPath);
356 if (rc < 0)
357 {
358 return PLDM_ERROR;
359 }
360
361 // Add the hostfw image to the directory where the contents were extracted
362 fs::copy_file(hostfwImagePath, fs::path(updateDirPath) / hostfwImageName,
363 fs::copy_options::overwrite_existing);
364
365 // Remove the tarball file, then re-generate it with so that the hostfw
366 // image becomes part of the tarball
367 fs::remove(tarImagePath);
368 rc = executeCmd("/bin/tar", "-cf", tarImagePath, ".", "-C", updateDirPath);
369 if (rc < 0)
370 {
371 return PLDM_ERROR;
372 }
373
374 // Copy the tarball to the update directory to trigger the phosphor software
375 // manager to create a version interface
376 fs::copy_file(tarImagePath, updateImagePath,
377 fs::copy_options::overwrite_existing);
378
379 // Cleanup
380 fs::remove_all(updateDirPath);
381 fs::remove_all(lidDirPath);
382 fs::remove_all(imageDirPath);
383
384 return PLDM_SUCCESS;
385}
386
Sampa Misraaea5dde2020-08-31 08:33:47 -0500387} // namespace responder
388} // namespace pldm