1 #include "config.h" 2 3 #include "item_updater_static.hpp" 4 5 #include "activation.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 {}; 119 } 120 121 std::unique_ptr<Version> ItemUpdaterStatic::createVersionObject( 122 const std::string& objPath, const std::string& versionId, 123 const std::string& versionString, 124 sdbusplus::xyz::openbmc_project::Software::server::Version::VersionPurpose 125 versionPurpose, 126 const std::string& filePath) 127 { 128 return {}; 129 } 130 131 bool ItemUpdaterStatic::validateImage(const std::string& path) 132 { 133 return true; 134 } 135 136 void ItemUpdaterStatic::processPNORImage() 137 { 138 auto fullVersion = utils::getPNORVersion(); 139 140 const auto& [version, extendedVersion] = Version::getVersions(fullVersion); 141 auto id = Version::getId(version); 142 143 auto activationState = server::Activation::Activations::Active; 144 if (version.empty()) 145 { 146 log<level::ERR>("Failed to read version", 147 entry("VERSION=%s", fullVersion.c_str())); 148 activationState = server::Activation::Activations::Invalid; 149 } 150 151 if (extendedVersion.empty()) 152 { 153 log<level::ERR>("Failed to read extendedVersion", 154 entry("VERSION=%s", fullVersion.c_str())); 155 activationState = server::Activation::Activations::Invalid; 156 } 157 158 auto purpose = server::Version::VersionPurpose::Host; 159 auto path = fs::path(SOFTWARE_OBJPATH) / id; 160 AssociationList associations = {}; 161 162 if (activationState == server::Activation::Activations::Active) 163 { 164 // Create an association to the host inventory item 165 associations.emplace_back(std::make_tuple(ACTIVATION_FWD_ASSOCIATION, 166 ACTIVATION_REV_ASSOCIATION, 167 HOST_INVENTORY_PATH)); 168 169 // Create an active association since this image is active 170 createActiveAssociation(path); 171 } 172 173 // Create Version instance for this version. 174 auto versionPtr = std::make_unique<Version>( 175 bus, path, *this, id, version, purpose, "", 176 std::bind(&ItemUpdaterStatic::erase, this, std::placeholders::_1)); 177 versionPtr->deleteObject = std::make_unique<Delete>(bus, path, *versionPtr); 178 versions.insert(std::make_pair(id, std::move(versionPtr))); 179 180 if (!id.empty()) 181 { 182 updateFunctionalAssociation(id); 183 } 184 } 185 186 void ItemUpdaterStatic::reset() 187 { 188 } 189 190 bool ItemUpdaterStatic::isVersionFunctional(const std::string& versionId) 191 { 192 return true; 193 } 194 195 void ItemUpdaterStatic::freePriority(uint8_t value, 196 const std::string& versionId) 197 { 198 } 199 200 void ItemUpdaterStatic::deleteAll() 201 { 202 } 203 204 void ItemUpdaterStatic::freeSpace() 205 { 206 } 207 208 void GardReset::reset() 209 { 210 } 211 212 } // namespace updater 213 } // namespace software 214 } // namespace openpower 215