blob: 7055c46b0717c23a194b58223b730f694b972374 [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
Varsha Kaverappa3ca29df2020-09-27 12:39:22 -0500195void CodeUpdate::clearDirPath(const std::string& dirPath)
196{
197 for (auto& path : fs::directory_iterator(dirPath.c_str()))
198 {
199 fs::remove_all(path);
200 }
201 return;
202}
203
Sampa Misraaea5dde2020-08-31 08:33:47 -0500204uint8_t fetchBootSide(uint16_t entityInstance, CodeUpdate* codeUpdate)
205{
206 uint8_t sensorOpState = tSideNum;
Sampa Misraaea5dde2020-08-31 08:33:47 -0500207 if (entityInstance == 0)
208 {
209 auto currSide = codeUpdate->fetchCurrentBootSide();
210 if (currSide == Pside)
211 {
212 sensorOpState = pSideNum;
213 }
214 }
215 else if (entityInstance == 1)
216 {
217 auto nextSide = codeUpdate->fetchNextBootSide();
218 if (nextSide == Pside)
219 {
220 sensorOpState = pSideNum;
221 }
222 }
223 else
224 {
225 sensorOpState = PLDM_SENSOR_UNKNOWN;
226 }
227
228 return sensorOpState;
229}
230
231int setBootSide(uint16_t entityInstance, uint8_t currState,
232 const std::vector<set_effecter_state_field>& stateField,
233 CodeUpdate* codeUpdate)
234{
235 int rc = PLDM_SUCCESS;
236 auto side = (stateField[currState].effecter_state == pSideNum) ? "P" : "T";
237
238 if (entityInstance == 0)
239 {
240 rc = codeUpdate->setCurrentBootSide(side);
241 }
242 else if (entityInstance == 1)
243 {
244 rc = codeUpdate->setNextBootSide(side);
245 }
246 else
247 {
248 rc = PLDM_PLATFORM_INVALID_STATE_VALUE;
249 }
250 return rc;
251}
252
Adriana Kobylak837fb472020-10-16 16:53:42 -0500253template <typename... T>
254int executeCmd(T const&... t)
255{
256 std::stringstream cmd;
257 ((cmd << t << " "), ...) << std::endl;
258 FILE* pipe = popen(cmd.str().c_str(), "r");
259 if (!pipe)
260 {
261 throw std::runtime_error("popen() failed!");
262 }
263 int rc = pclose(pipe);
264 if (WEXITSTATUS(rc))
265 {
266 std::cerr << "Error executing: ";
267 ((std::cerr << " " << t), ...);
268 std::cerr << "\n";
269 return -1;
270 }
271
272 return 0;
273}
274
Adriana Kobylak727f7382020-09-01 14:38:25 -0500275int processCodeUpdateLid(const std::string& filePath)
276{
277 struct LidHeader
278 {
279 uint16_t magicNumber;
Adriana Kobylak86d14182020-10-16 16:11:08 -0500280 uint16_t headerVersion;
281 uint32_t lidNumber;
282 uint32_t lidDate;
283 uint16_t lidTime;
284 uint16_t lidClass;
285 uint32_t lidCrc;
286 uint32_t lidSize;
287 uint32_t headerSize;
Adriana Kobylak727f7382020-09-01 14:38:25 -0500288 };
289 LidHeader header;
290
291 std::ifstream ifs(filePath, std::ios::in | std::ios::binary);
292 if (!ifs)
293 {
294 std::cerr << "ifstream open error: " << filePath << "\n";
295 return PLDM_ERROR;
296 }
297 ifs.seekg(0);
298 ifs.read(reinterpret_cast<char*>(&header), sizeof(header));
Adriana Kobylak727f7382020-09-01 14:38:25 -0500299
Adriana Kobylak86d14182020-10-16 16:11:08 -0500300 // File size should be the value of lid size minus the header size
301 auto fileSize = fs::file_size(filePath);
302 fileSize -= htonl(header.headerSize);
303 if (fileSize < htonl(header.lidSize))
304 {
305 // File is not completely written yet
Adriana Kobylakfa810d72020-10-16 16:27:28 -0500306 ifs.close();
Adriana Kobylak86d14182020-10-16 16:11:08 -0500307 return PLDM_SUCCESS;
308 }
309
Adriana Kobylak727f7382020-09-01 14:38:25 -0500310 constexpr auto magicNumber = 0x0222;
311 if (htons(header.magicNumber) != magicNumber)
312 {
313 std::cerr << "Invalid magic number: " << filePath << "\n";
Adriana Kobylakfa810d72020-10-16 16:27:28 -0500314 ifs.close();
Adriana Kobylak727f7382020-09-01 14:38:25 -0500315 return PLDM_ERROR;
316 }
317
Adriana Kobylaka1f158c2020-11-09 12:47:29 -0600318 fs::create_directories(imageDirPath);
Adriana Kobylakfa810d72020-10-16 16:27:28 -0500319 fs::create_directories(lidDirPath);
320
Adriana Kobylaka1f158c2020-11-09 12:47:29 -0600321 constexpr auto bmcClass = 0x2000;
322 if (htons(header.lidClass) == bmcClass)
323 {
324 // Skip the header and concatenate the BMC LIDs into a tar file
325 std::ofstream ofs(tarImagePath,
326 std::ios::out | std::ios::binary | std::ios::app);
327 ifs.seekg(htonl(header.headerSize));
328 ofs << ifs.rdbuf();
329 ofs.flush();
330 ofs.close();
331 }
332 else
333 {
334 std::stringstream lidFileName;
335 lidFileName << std::hex << htonl(header.lidNumber) << ".lid";
336 auto lidNoHeaderPath = fs::path(lidDirPath) / lidFileName.str();
337 std::ofstream ofs(lidNoHeaderPath,
338 std::ios::out | std::ios::binary | std::ios::trunc);
339 ifs.seekg(htonl(header.headerSize));
340 ofs << ifs.rdbuf();
341 ofs.flush();
342 ofs.close();
343 }
Adriana Kobylakfa810d72020-10-16 16:27:28 -0500344
345 ifs.close();
346 fs::remove(filePath);
Adriana Kobylak727f7382020-09-01 14:38:25 -0500347 return PLDM_SUCCESS;
348}
349
Adriana Kobylak837fb472020-10-16 16:53:42 -0500350int assembleCodeUpdateImage()
351{
352 // Create the hostfw squashfs image from the LID files without header
353 auto rc = executeCmd("/usr/sbin/mksquashfs", lidDirPath.c_str(),
354 hostfwImagePath.c_str(), "-all-root", "-no-recovery");
355 if (rc < 0)
356 {
357 return PLDM_ERROR;
358 }
359
360 fs::create_directories(updateDirPath);
361
362 // Extract the BMC tarball content
363 rc = executeCmd("/bin/tar", "-xf", tarImagePath.c_str(), "-C",
364 updateDirPath);
365 if (rc < 0)
366 {
367 return PLDM_ERROR;
368 }
369
370 // Add the hostfw image to the directory where the contents were extracted
371 fs::copy_file(hostfwImagePath, fs::path(updateDirPath) / hostfwImageName,
372 fs::copy_options::overwrite_existing);
373
374 // Remove the tarball file, then re-generate it with so that the hostfw
375 // image becomes part of the tarball
376 fs::remove(tarImagePath);
377 rc = executeCmd("/bin/tar", "-cf", tarImagePath, ".", "-C", updateDirPath);
378 if (rc < 0)
379 {
380 return PLDM_ERROR;
381 }
382
383 // Copy the tarball to the update directory to trigger the phosphor software
384 // manager to create a version interface
385 fs::copy_file(tarImagePath, updateImagePath,
386 fs::copy_options::overwrite_existing);
387
388 // Cleanup
389 fs::remove_all(updateDirPath);
390 fs::remove_all(lidDirPath);
391 fs::remove_all(imageDirPath);
392
393 return PLDM_SUCCESS;
394}
395
Sampa Misraaea5dde2020-08-31 08:33:47 -0500396} // namespace responder
397} // namespace pldm