xref: /openbmc/openpower-pnor-code-mgmt/static/item_updater_static.cpp (revision cbf707b6fd0b11b6879df8c7ef623c80417b5c08)
1322f3f47SLei YU #include "config.h"
2322f3f47SLei YU 
3322f3f47SLei YU #include "item_updater_static.hpp"
4322f3f47SLei YU 
5b53425d4SLei YU #include "activation_static.hpp"
6e4994464SLei YU #include "utils.hpp"
7dec8cf9cSLei YU #include "version.hpp"
8dec8cf9cSLei YU 
98facccfaSBrad Bishop #include <phosphor-logging/elog-errors.hpp>
108facccfaSBrad Bishop #include <phosphor-logging/log.hpp>
118facccfaSBrad Bishop #include <xyz/openbmc_project/Common/error.hpp>
128facccfaSBrad Bishop 
13a7b4adeeSLei YU #include <array>
14dec8cf9cSLei YU #include <cstring>
15dec8cf9cSLei YU #include <filesystem>
16dec8cf9cSLei YU #include <fstream>
17a2e67163SLei YU #include <sstream>
18dec8cf9cSLei YU #include <string>
195efca586SLei YU #include <tuple>
20dec8cf9cSLei YU 
21dec8cf9cSLei YU using namespace sdbusplus::xyz::openbmc_project::Common::Error;
22dec8cf9cSLei YU using namespace phosphor::logging;
23dec8cf9cSLei YU 
24dec8cf9cSLei YU // When you see server:: you know we're referencing our base class
25dec8cf9cSLei YU namespace server = sdbusplus::xyz::openbmc_project::Software::server;
26dec8cf9cSLei YU namespace fs = std::filesystem;
27dec8cf9cSLei YU 
28dec8cf9cSLei YU namespace utils
29dec8cf9cSLei YU {
30dec8cf9cSLei YU 
31dec8cf9cSLei YU template <typename... Ts>
concat_string(const Ts &...ts)327fb6c346SPatrick Williams std::string concat_string(const Ts&... ts)
33dec8cf9cSLei YU {
34dec8cf9cSLei YU     std::stringstream s;
35dec8cf9cSLei YU     ((s << ts << " "), ...) << std::endl;
36dec8cf9cSLei YU     return s.str();
37dec8cf9cSLei YU }
38dec8cf9cSLei YU 
39dec8cf9cSLei YU // Helper function to run pflash command
405efca586SLei YU // Returns return code and the stdout
41dec8cf9cSLei YU template <typename... Ts>
pflash(const Ts &...ts)427fb6c346SPatrick Williams std::pair<int, std::string> pflash(const Ts&... ts)
43dec8cf9cSLei YU {
44dec8cf9cSLei YU     std::array<char, 512> buffer;
45dec8cf9cSLei YU     std::string cmd = concat_string("pflash", ts...);
46dec8cf9cSLei YU     std::stringstream result;
475efca586SLei YU     int rc;
485efca586SLei YU     FILE* pipe = popen(cmd.c_str(), "r");
49dec8cf9cSLei YU     if (!pipe)
50dec8cf9cSLei YU     {
51dec8cf9cSLei YU         throw std::runtime_error("popen() failed!");
52dec8cf9cSLei YU     }
535efca586SLei YU     while (fgets(buffer.data(), buffer.size(), pipe) != nullptr)
54dec8cf9cSLei YU     {
55dec8cf9cSLei YU         result << buffer.data();
56dec8cf9cSLei YU     }
575efca586SLei YU     rc = pclose(pipe);
585efca586SLei YU     return {rc, result.str()};
59dec8cf9cSLei YU }
60dec8cf9cSLei YU 
getPNORVersion()61dec8cf9cSLei YU std::string getPNORVersion()
62dec8cf9cSLei YU {
63dec8cf9cSLei YU     // A signed version partition will have an extra 4K header starting with
64dec8cf9cSLei YU     // the magic number 17082011 in big endian:
65dec8cf9cSLei YU     // https://github.com/open-power/skiboot/blob/master/libstb/container.h#L47
66dec8cf9cSLei YU 
67dec8cf9cSLei YU     constexpr uint8_t MAGIC[] = {0x17, 0x08, 0x20, 0x11};
68dec8cf9cSLei YU     constexpr auto MAGIC_SIZE = sizeof(MAGIC);
69dec8cf9cSLei YU     static_assert(MAGIC_SIZE == 4);
70dec8cf9cSLei YU 
71dec8cf9cSLei YU     auto tmp = fs::temp_directory_path();
72dec8cf9cSLei YU     std::string tmpDir(tmp / "versionXXXXXX");
73dec8cf9cSLei YU     if (!mkdtemp(tmpDir.data()))
74dec8cf9cSLei YU     {
75dec8cf9cSLei YU         log<level::ERR>("Failed to create temp dir");
76dec8cf9cSLei YU         return {};
77dec8cf9cSLei YU     }
78dec8cf9cSLei YU 
79dec8cf9cSLei YU     fs::path versionFile = tmpDir;
80dec8cf9cSLei YU     versionFile /= "version";
81dec8cf9cSLei YU 
82f8e02429SPatrick Williams     auto [rc, r] =
83f8e02429SPatrick Williams         pflash("-P VERSION -r", versionFile.string(), "2>&1 > /dev/null");
845efca586SLei YU     if (rc != 0)
855efca586SLei YU     {
865efca586SLei YU         log<level::ERR>("Failed to read VERSION", entry("RETURNCODE=%d", rc));
875efca586SLei YU         return {};
885efca586SLei YU     }
895efca586SLei YU 
90dec8cf9cSLei YU     std::ifstream f(versionFile.c_str(), std::ios::in | std::ios::binary);
91dec8cf9cSLei YU     uint8_t magic[MAGIC_SIZE];
92dec8cf9cSLei YU     std::string version;
93dec8cf9cSLei YU 
94dec8cf9cSLei YU     f.read(reinterpret_cast<char*>(magic), MAGIC_SIZE);
95dec8cf9cSLei YU     f.seekg(0, std::ios::beg);
96dec8cf9cSLei YU     if (std::memcmp(magic, MAGIC, MAGIC_SIZE) == 0)
97dec8cf9cSLei YU     {
98dec8cf9cSLei YU         // Skip the first 4K header
99dec8cf9cSLei YU         f.ignore(4096);
100dec8cf9cSLei YU     }
101dec8cf9cSLei YU 
102dec8cf9cSLei YU     getline(f, version, '\0');
103dec8cf9cSLei YU     f.close();
104dec8cf9cSLei YU 
105dec8cf9cSLei YU     // Clear the temp dir
106dec8cf9cSLei YU     std::error_code ec;
107dec8cf9cSLei YU     fs::remove_all(tmpDir, ec);
108dec8cf9cSLei YU     if (ec)
109dec8cf9cSLei YU     {
110dec8cf9cSLei YU         log<level::ERR>("Failed to remove temp dir",
111dec8cf9cSLei YU                         entry("DIR=%s", tmpDir.c_str()),
112dec8cf9cSLei YU                         entry("ERR=%s", ec.message().c_str()));
113dec8cf9cSLei YU     }
114dec8cf9cSLei YU 
115dec8cf9cSLei YU     return version;
116dec8cf9cSLei YU }
117dec8cf9cSLei YU 
pnorClear(const std::string & part,bool shouldEcc=true)1186cecc9b4SLei YU void pnorClear(const std::string& part, bool shouldEcc = true)
119a7b4adeeSLei YU {
120a7b4adeeSLei YU     int rc;
121a7b4adeeSLei YU     std::tie(rc, std::ignore) =
1226cecc9b4SLei YU         utils::pflash("-P", part, shouldEcc ? "-c" : "-e", "-f >/dev/null");
123a7b4adeeSLei YU     if (rc != 0)
124a7b4adeeSLei YU     {
125a7b4adeeSLei YU         log<level::ERR>("Failed to clear partition",
126a7b4adeeSLei YU                         entry("PART=%s", part.c_str()),
127a7b4adeeSLei YU                         entry("RETURNCODE=%d", rc));
128a7b4adeeSLei YU     }
129a7b4adeeSLei YU     else
130a7b4adeeSLei YU     {
131a7b4adeeSLei YU         log<level::INFO>("Clear partition successfully",
132a7b4adeeSLei YU                          entry("PART=%s", part.c_str()));
133a7b4adeeSLei YU     }
134a7b4adeeSLei YU }
135a7b4adeeSLei YU 
1366cecc9b4SLei YU // The pair contains the partition name and if it should use ECC clear
1376cecc9b4SLei YU using PartClear = std::pair<std::string, bool>;
1386cecc9b4SLei YU 
getPartsToClear(const std::string & info)1396cecc9b4SLei YU std::vector<PartClear> getPartsToClear(const std::string& info)
1406cecc9b4SLei YU {
1416cecc9b4SLei YU     std::vector<PartClear> ret;
1426cecc9b4SLei YU     std::istringstream iss(info);
1436cecc9b4SLei YU     std::string line;
1446cecc9b4SLei YU 
1456cecc9b4SLei YU     while (std::getline(iss, line))
1466cecc9b4SLei YU     {
1476cecc9b4SLei YU         // Each line looks like
1486cecc9b4SLei YU         // ID=06 MVPD 0x0012d000..0x001bd000 (actual=0x00090000) [E--P--F-C-]
1496cecc9b4SLei YU         // Flag 'F' means REPROVISION
150fa9a6becSAlexander Filippov         // Flag 'E' means ECC required
15107830764SLei YU         auto pos = line.find('[');
15207830764SLei YU         if (pos == std::string::npos)
15307830764SLei YU         {
15407830764SLei YU             continue;
15507830764SLei YU         }
15607830764SLei YU         auto flags = line.substr(pos);
1576cecc9b4SLei YU         if (flags.find('F') != std::string::npos)
1586cecc9b4SLei YU         {
1596cecc9b4SLei YU             // This is a partition to be cleared
16007830764SLei YU             pos = line.find_first_of(' '); // After "ID=xx"
16107830764SLei YU             if (pos == std::string::npos)
16207830764SLei YU             {
16307830764SLei YU                 continue;
16407830764SLei YU             }
16596442c88SManojkiran Eda             line = line.substr(pos);           // Skipping "ID=xx"
16607830764SLei YU 
16707830764SLei YU             pos = line.find_first_not_of(' '); // After spaces
16807830764SLei YU             if (pos == std::string::npos)
16907830764SLei YU             {
17007830764SLei YU                 continue;
17107830764SLei YU             }
17207830764SLei YU             line = line.substr(pos);       // Skipping spaces
17307830764SLei YU 
17407830764SLei YU             pos = line.find_first_of(' '); // The end of part name
17507830764SLei YU             if (pos == std::string::npos)
17607830764SLei YU             {
17707830764SLei YU                 continue;
17807830764SLei YU             }
17907830764SLei YU             line = line.substr(0, pos); // The part name
18007830764SLei YU 
181fa9a6becSAlexander Filippov             bool ecc = flags.find('E') != std::string::npos;
1826cecc9b4SLei YU             ret.emplace_back(line, ecc);
1836cecc9b4SLei YU         }
1846cecc9b4SLei YU     }
1856cecc9b4SLei YU     return ret;
1866cecc9b4SLei YU }
1876cecc9b4SLei YU 
1886cecc9b4SLei YU // Get partitions that should be cleared
getPartsToClear()1896cecc9b4SLei YU std::vector<PartClear> getPartsToClear()
1906cecc9b4SLei YU {
1916cecc9b4SLei YU     const auto& [rc, pflashInfo] = pflash("-i | grep ^ID | grep 'F'");
1926cecc9b4SLei YU     return getPartsToClear(pflashInfo);
1936cecc9b4SLei YU }
1946cecc9b4SLei YU 
195dec8cf9cSLei YU } // namespace utils
196dec8cf9cSLei YU 
197322f3f47SLei YU namespace openpower
198322f3f47SLei YU {
199322f3f47SLei YU namespace software
200322f3f47SLei YU {
201322f3f47SLei YU namespace updater
202322f3f47SLei YU {
2035efca586SLei YU 
createActivationObject(const std::string & path,const std::string & versionId,const std::string & extVersion,sdbusplus::xyz::openbmc_project::Software::server::Activation::Activations activationStatus,AssociationList & assocs)204322f3f47SLei YU std::unique_ptr<Activation> ItemUpdaterStatic::createActivationObject(
205322f3f47SLei YU     const std::string& path, const std::string& versionId,
206322f3f47SLei YU     const std::string& extVersion,
207322f3f47SLei YU     sdbusplus::xyz::openbmc_project::Software::server::Activation::Activations
208322f3f47SLei YU         activationStatus,
209322f3f47SLei YU     AssociationList& assocs)
210322f3f47SLei YU {
211b53425d4SLei YU     return std::make_unique<ActivationStatic>(
212b53425d4SLei YU         bus, path, *this, versionId, extVersion, activationStatus, assocs);
213322f3f47SLei YU }
214322f3f47SLei YU 
createVersionObject(const std::string & objPath,const std::string & versionId,const std::string & versionString,sdbusplus::xyz::openbmc_project::Software::server::Version::VersionPurpose versionPurpose,const std::string & filePath)215322f3f47SLei YU std::unique_ptr<Version> ItemUpdaterStatic::createVersionObject(
216322f3f47SLei YU     const std::string& objPath, const std::string& versionId,
217322f3f47SLei YU     const std::string& versionString,
218322f3f47SLei YU     sdbusplus::xyz::openbmc_project::Software::server::Version::VersionPurpose
219322f3f47SLei YU         versionPurpose,
220322f3f47SLei YU     const std::string& filePath)
221322f3f47SLei YU {
222b53425d4SLei YU     auto version = std::make_unique<Version>(
223b53425d4SLei YU         bus, objPath, *this, versionId, versionString, versionPurpose, filePath,
224b53425d4SLei YU         std::bind(&ItemUpdaterStatic::erase, this, std::placeholders::_1));
225b53425d4SLei YU     version->deleteObject = std::make_unique<Delete>(bus, objPath, *version);
226b53425d4SLei YU     return version;
227322f3f47SLei YU }
228322f3f47SLei YU 
validateImage(const std::string &)229c8f22502SBrad Bishop bool ItemUpdaterStatic::validateImage(const std::string&)
230322f3f47SLei YU {
231b53425d4SLei YU     // There is no need to validate static layout pnor
232322f3f47SLei YU     return true;
233322f3f47SLei YU }
234322f3f47SLei YU 
processPNORImage()235322f3f47SLei YU void ItemUpdaterStatic::processPNORImage()
236322f3f47SLei YU {
237dec8cf9cSLei YU     auto fullVersion = utils::getPNORVersion();
238dec8cf9cSLei YU 
239dec8cf9cSLei YU     const auto& [version, extendedVersion] = Version::getVersions(fullVersion);
240dec8cf9cSLei YU     auto id = Version::getId(version);
241dec8cf9cSLei YU 
24291add6dfSLei YU     if (id.empty())
24391add6dfSLei YU     {
24491add6dfSLei YU         // Possibly a corrupted PNOR
24591add6dfSLei YU         return;
24691add6dfSLei YU     }
24791add6dfSLei YU 
248dec8cf9cSLei YU     auto activationState = server::Activation::Activations::Active;
249dec8cf9cSLei YU     if (version.empty())
250dec8cf9cSLei YU     {
251dec8cf9cSLei YU         log<level::ERR>("Failed to read version",
252dec8cf9cSLei YU                         entry("VERSION=%s", fullVersion.c_str()));
253dec8cf9cSLei YU         activationState = server::Activation::Activations::Invalid;
254dec8cf9cSLei YU     }
255dec8cf9cSLei YU 
256dec8cf9cSLei YU     if (extendedVersion.empty())
257dec8cf9cSLei YU     {
258dec8cf9cSLei YU         log<level::ERR>("Failed to read extendedVersion",
259dec8cf9cSLei YU                         entry("VERSION=%s", fullVersion.c_str()));
260dec8cf9cSLei YU         activationState = server::Activation::Activations::Invalid;
261dec8cf9cSLei YU     }
262dec8cf9cSLei YU 
263dec8cf9cSLei YU     auto purpose = server::Version::VersionPurpose::Host;
264dec8cf9cSLei YU     auto path = fs::path(SOFTWARE_OBJPATH) / id;
265dec8cf9cSLei YU     AssociationList associations = {};
266dec8cf9cSLei YU 
267dec8cf9cSLei YU     if (activationState == server::Activation::Activations::Active)
268dec8cf9cSLei YU     {
269dec8cf9cSLei YU         // Create an association to the host inventory item
270f8e02429SPatrick Williams         associations.emplace_back(
271f8e02429SPatrick Williams             std::make_tuple(ACTIVATION_FWD_ASSOCIATION,
272f8e02429SPatrick Williams                             ACTIVATION_REV_ASSOCIATION, HOST_INVENTORY_PATH));
273dec8cf9cSLei YU 
274dec8cf9cSLei YU         // Create an active association since this image is active
275dec8cf9cSLei YU         createActiveAssociation(path);
276dec8cf9cSLei YU     }
277dec8cf9cSLei YU 
2783c81037eSAdriana Kobylak     // All updateable firmware components must expose the updateable
2793c81037eSAdriana Kobylak     // association.
2803c81037eSAdriana Kobylak     createUpdateableAssociation(path);
2813c81037eSAdriana Kobylak 
282b53425d4SLei YU     // Create Activation instance for this version.
283f8e02429SPatrick Williams     activations.insert(
284f8e02429SPatrick Williams         std::make_pair(id, std::make_unique<ActivationStatic>(
285f8e02429SPatrick Williams                                bus, path, *this, id, extendedVersion,
286f8e02429SPatrick Williams                                activationState, associations)));
287b53425d4SLei YU 
288b53425d4SLei YU     // If Active, create RedundancyPriority instance for this version.
289b53425d4SLei YU     if (activationState == server::Activation::Activations::Active)
290b53425d4SLei YU     {
291b53425d4SLei YU         // For now only one PNOR is supported with static layout
292b53425d4SLei YU         activations.find(id)->second->redundancyPriority =
293b53425d4SLei YU             std::make_unique<RedundancyPriority>(
294b53425d4SLei YU                 bus, path, *(activations.find(id)->second), 0);
295b53425d4SLei YU     }
296b53425d4SLei YU 
297dec8cf9cSLei YU     // Create Version instance for this version.
298dec8cf9cSLei YU     auto versionPtr = std::make_unique<Version>(
299dec8cf9cSLei YU         bus, path, *this, id, version, purpose, "",
300dec8cf9cSLei YU         std::bind(&ItemUpdaterStatic::erase, this, std::placeholders::_1));
301dec8cf9cSLei YU     versionPtr->deleteObject = std::make_unique<Delete>(bus, path, *versionPtr);
302dec8cf9cSLei YU     versions.insert(std::make_pair(id, std::move(versionPtr)));
303dec8cf9cSLei YU 
304dec8cf9cSLei YU     if (!id.empty())
305dec8cf9cSLei YU     {
306dec8cf9cSLei YU         updateFunctionalAssociation(id);
307dec8cf9cSLei YU     }
308322f3f47SLei YU }
309322f3f47SLei YU 
reset()310322f3f47SLei YU void ItemUpdaterStatic::reset()
311322f3f47SLei YU {
3126cecc9b4SLei YU     auto partitions = utils::getPartsToClear();
313a7b4adeeSLei YU 
314e4994464SLei YU     utils::hiomapdSuspend(bus);
315a7b4adeeSLei YU 
316a7b4adeeSLei YU     for (auto p : partitions)
317a7b4adeeSLei YU     {
318a7b4adeeSLei YU         utils::pnorClear(p.first, p.second);
319a7b4adeeSLei YU     }
320a7b4adeeSLei YU 
321e4994464SLei YU     utils::hiomapdResume(bus);
322322f3f47SLei YU }
323322f3f47SLei YU 
isVersionFunctional(const std::string & versionId)324322f3f47SLei YU bool ItemUpdaterStatic::isVersionFunctional(const std::string& versionId)
325322f3f47SLei YU {
326a2e67163SLei YU     return versionId == functionalVersionId;
327322f3f47SLei YU }
328322f3f47SLei YU 
freePriority(uint8_t,const std::string &)3297fb6c346SPatrick Williams void ItemUpdaterStatic::freePriority(uint8_t, const std::string&) {}
330322f3f47SLei YU 
deleteAll()331322f3f47SLei YU void ItemUpdaterStatic::deleteAll()
332322f3f47SLei YU {
333a2e67163SLei YU     // Static layout has only one active and function pnor
334a2e67163SLei YU     // There is no implementation for this interface
335322f3f47SLei YU }
336322f3f47SLei YU 
freeSpace()3376da3dae3SLei YU bool ItemUpdaterStatic::freeSpace()
338322f3f47SLei YU {
339a2e67163SLei YU     // For now assume static layout only has 1 active PNOR,
340a2e67163SLei YU     // so erase the active PNOR
341a2e67163SLei YU     for (const auto& iter : activations)
342a2e67163SLei YU     {
343a2e67163SLei YU         if (iter.second.get()->activation() ==
344a2e67163SLei YU             server::Activation::Activations::Active)
345a2e67163SLei YU         {
3466da3dae3SLei YU             return erase(iter.second->versionId);
347a2e67163SLei YU         }
348a2e67163SLei YU     }
34991add6dfSLei YU     // No active PNOR means PNOR is empty or corrupted
35091add6dfSLei YU     return true;
351a2e67163SLei YU }
352a2e67163SLei YU 
updateFunctionalAssociation(const std::string & versionId)353*cbf707b6SPatrick Williams void ItemUpdaterStatic::updateFunctionalAssociation(
354*cbf707b6SPatrick Williams     const std::string& versionId)
355a2e67163SLei YU {
356a2e67163SLei YU     functionalVersionId = versionId;
357a2e67163SLei YU     ItemUpdater::updateFunctionalAssociation(versionId);
358322f3f47SLei YU }
359322f3f47SLei YU 
reset()360716de5b8SLei YU void GardResetStatic::reset()
361322f3f47SLei YU {
36296442c88SManojkiran Eda     // Clear guard partition
363e4994464SLei YU     utils::hiomapdSuspend(bus);
3645efca586SLei YU 
365a7b4adeeSLei YU     utils::pnorClear("GUARD");
3665efca586SLei YU 
367e4994464SLei YU     utils::hiomapdResume(bus);
368322f3f47SLei YU }
369322f3f47SLei YU 
370322f3f47SLei YU } // namespace updater
371322f3f47SLei YU } // namespace software
372322f3f47SLei YU } // namespace openpower
373