1 #include "config.h" 2 3 #include "item_updater_static.hpp" 4 5 #include "activation_static.hpp" 6 #include "version.hpp" 7 8 #include <cstring> 9 #include <filesystem> 10 #include <fstream> 11 #include <phosphor-logging/elog-errors.hpp> 12 #include <phosphor-logging/log.hpp> 13 #include <string> 14 15 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 16 using namespace phosphor::logging; 17 18 // When you see server:: you know we're referencing our base class 19 namespace server = sdbusplus::xyz::openbmc_project::Software::server; 20 namespace fs = std::filesystem; 21 22 namespace utils 23 { 24 25 template <typename... Ts> 26 std::string concat_string(Ts const&... ts) 27 { 28 std::stringstream s; 29 ((s << ts << " "), ...) << std::endl; 30 return s.str(); 31 } 32 33 // Helper function to run pflash command 34 template <typename... Ts> 35 std::string pflash(Ts const&... ts) 36 { 37 std::array<char, 512> buffer; 38 std::string cmd = concat_string("pflash", ts...); 39 std::stringstream result; 40 std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), 41 pclose); 42 if (!pipe) 43 { 44 throw std::runtime_error("popen() failed!"); 45 } 46 while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) 47 { 48 result << buffer.data(); 49 } 50 return result.str(); 51 } 52 53 std::string getPNORVersion() 54 { 55 // A signed version partition will have an extra 4K header starting with 56 // the magic number 17082011 in big endian: 57 // https://github.com/open-power/skiboot/blob/master/libstb/container.h#L47 58 59 constexpr uint8_t MAGIC[] = {0x17, 0x08, 0x20, 0x11}; 60 constexpr auto MAGIC_SIZE = sizeof(MAGIC); 61 static_assert(MAGIC_SIZE == 4); 62 63 auto tmp = fs::temp_directory_path(); 64 std::string tmpDir(tmp / "versionXXXXXX"); 65 if (!mkdtemp(tmpDir.data())) 66 { 67 log<level::ERR>("Failed to create temp dir"); 68 return {}; 69 } 70 71 fs::path versionFile = tmpDir; 72 versionFile /= "version"; 73 74 pflash("-P VERSION -r", versionFile.string(), "2>&1 > /dev/null"); 75 std::ifstream f(versionFile.c_str(), std::ios::in | std::ios::binary); 76 uint8_t magic[MAGIC_SIZE]; 77 std::string version; 78 79 f.read(reinterpret_cast<char*>(magic), MAGIC_SIZE); 80 f.seekg(0, std::ios::beg); 81 if (std::memcmp(magic, MAGIC, MAGIC_SIZE) == 0) 82 { 83 // Skip the first 4K header 84 f.ignore(4096); 85 } 86 87 getline(f, version, '\0'); 88 f.close(); 89 90 // Clear the temp dir 91 std::error_code ec; 92 fs::remove_all(tmpDir, ec); 93 if (ec) 94 { 95 log<level::ERR>("Failed to remove temp dir", 96 entry("DIR=%s", tmpDir.c_str()), 97 entry("ERR=%s", ec.message().c_str())); 98 } 99 100 return version; 101 } 102 103 } // namespace utils 104 105 namespace openpower 106 { 107 namespace software 108 { 109 namespace updater 110 { 111 std::unique_ptr<Activation> ItemUpdaterStatic::createActivationObject( 112 const std::string& path, const std::string& versionId, 113 const std::string& extVersion, 114 sdbusplus::xyz::openbmc_project::Software::server::Activation::Activations 115 activationStatus, 116 AssociationList& assocs) 117 { 118 return std::make_unique<ActivationStatic>( 119 bus, path, *this, versionId, extVersion, activationStatus, assocs); 120 } 121 122 std::unique_ptr<Version> ItemUpdaterStatic::createVersionObject( 123 const std::string& objPath, const std::string& versionId, 124 const std::string& versionString, 125 sdbusplus::xyz::openbmc_project::Software::server::Version::VersionPurpose 126 versionPurpose, 127 const std::string& filePath) 128 { 129 auto version = std::make_unique<Version>( 130 bus, objPath, *this, versionId, versionString, versionPurpose, filePath, 131 std::bind(&ItemUpdaterStatic::erase, this, std::placeholders::_1)); 132 version->deleteObject = std::make_unique<Delete>(bus, objPath, *version); 133 return version; 134 } 135 136 bool ItemUpdaterStatic::validateImage(const std::string& path) 137 { 138 // There is no need to validate static layout pnor 139 return true; 140 } 141 142 void ItemUpdaterStatic::processPNORImage() 143 { 144 auto fullVersion = utils::getPNORVersion(); 145 146 const auto& [version, extendedVersion] = Version::getVersions(fullVersion); 147 auto id = Version::getId(version); 148 149 auto activationState = server::Activation::Activations::Active; 150 if (version.empty()) 151 { 152 log<level::ERR>("Failed to read version", 153 entry("VERSION=%s", fullVersion.c_str())); 154 activationState = server::Activation::Activations::Invalid; 155 } 156 157 if (extendedVersion.empty()) 158 { 159 log<level::ERR>("Failed to read extendedVersion", 160 entry("VERSION=%s", fullVersion.c_str())); 161 activationState = server::Activation::Activations::Invalid; 162 } 163 164 auto purpose = server::Version::VersionPurpose::Host; 165 auto path = fs::path(SOFTWARE_OBJPATH) / id; 166 AssociationList associations = {}; 167 168 if (activationState == server::Activation::Activations::Active) 169 { 170 // Create an association to the host inventory item 171 associations.emplace_back(std::make_tuple(ACTIVATION_FWD_ASSOCIATION, 172 ACTIVATION_REV_ASSOCIATION, 173 HOST_INVENTORY_PATH)); 174 175 // Create an active association since this image is active 176 createActiveAssociation(path); 177 } 178 179 // Create Activation instance for this version. 180 activations.insert(std::make_pair( 181 id, std::make_unique<ActivationStatic>(bus, path, *this, id, 182 extendedVersion, activationState, 183 associations))); 184 185 // If Active, create RedundancyPriority instance for this version. 186 if (activationState == server::Activation::Activations::Active) 187 { 188 // For now only one PNOR is supported with static layout 189 activations.find(id)->second->redundancyPriority = 190 std::make_unique<RedundancyPriority>( 191 bus, path, *(activations.find(id)->second), 0); 192 } 193 194 // Create Version instance for this version. 195 auto versionPtr = std::make_unique<Version>( 196 bus, path, *this, id, version, purpose, "", 197 std::bind(&ItemUpdaterStatic::erase, this, std::placeholders::_1)); 198 versionPtr->deleteObject = std::make_unique<Delete>(bus, path, *versionPtr); 199 versions.insert(std::make_pair(id, std::move(versionPtr))); 200 201 if (!id.empty()) 202 { 203 updateFunctionalAssociation(id); 204 } 205 } 206 207 void ItemUpdaterStatic::reset() 208 { 209 } 210 211 bool ItemUpdaterStatic::isVersionFunctional(const std::string& versionId) 212 { 213 return true; 214 } 215 216 void ItemUpdaterStatic::freePriority(uint8_t value, 217 const std::string& versionId) 218 { 219 } 220 221 void ItemUpdaterStatic::deleteAll() 222 { 223 } 224 225 void ItemUpdaterStatic::freeSpace() 226 { 227 } 228 229 void GardReset::reset() 230 { 231 } 232 233 } // namespace updater 234 } // namespace software 235 } // namespace openpower 236