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