135e83f3eSSaqib Khan #include <fstream> 2ec1b41c4SGunnar Mills #include <string> 32ce7da29SGunnar Mills #include <phosphor-logging/log.hpp> 4ec1b41c4SGunnar Mills #include "config.h" 52ce7da29SGunnar Mills #include "item_updater.hpp" 62ce7da29SGunnar Mills #include "xyz/openbmc_project/Software/Version/server.hpp" 735e83f3eSSaqib Khan #include <experimental/filesystem> 8705f1bfcSSaqib Khan #include "version.hpp" 95d532675SSaqib Khan #include "serialize.hpp" 10ec1b41c4SGunnar Mills 11ec1b41c4SGunnar Mills namespace phosphor 12ec1b41c4SGunnar Mills { 13ec1b41c4SGunnar Mills namespace software 14ec1b41c4SGunnar Mills { 15ec1b41c4SGunnar Mills namespace updater 16ec1b41c4SGunnar Mills { 17ec1b41c4SGunnar Mills 182ce7da29SGunnar Mills // When you see server:: you know we're referencing our base class 192ce7da29SGunnar Mills namespace server = sdbusplus::xyz::openbmc_project::Software::server; 200129d926SMichael Tritz namespace control = sdbusplus::xyz::openbmc_project::Control::server; 212ce7da29SGunnar Mills 222ce7da29SGunnar Mills using namespace phosphor::logging; 2335e83f3eSSaqib Khan namespace fs = std::experimental::filesystem; 2435e83f3eSSaqib Khan 25b1cfdf99SMichael Tritz const std::vector<std::string> bmcImages = { "image-kernel", 26b1cfdf99SMichael Tritz "image-rofs", 27b1cfdf99SMichael Tritz "image-rwfs", 28b1cfdf99SMichael Tritz "image-u-boot" }; 292ce7da29SGunnar Mills 30e75d10f5SPatrick Williams void ItemUpdater::createActivation(sdbusplus::message::message& msg) 31ec1b41c4SGunnar Mills { 3284a0e693SSaqib Khan 3384a0e693SSaqib Khan using SVersion = server::Version; 3484a0e693SSaqib Khan using VersionPurpose = SVersion::VersionPurpose; 35*9a782243SGunnar Mills using VersionClass = phosphor::software::manager::Version; 3684a0e693SSaqib Khan namespace mesg = sdbusplus::message; 3784a0e693SSaqib Khan namespace variant_ns = mesg::variant_ns; 3884a0e693SSaqib Khan 3984a0e693SSaqib Khan mesg::object_path objPath; 4084a0e693SSaqib Khan auto purpose = VersionPurpose::Unknown; 41705f1bfcSSaqib Khan std::string version; 422ce7da29SGunnar Mills std::map<std::string, 432ce7da29SGunnar Mills std::map<std::string, 4484a0e693SSaqib Khan mesg::variant<std::string>>> interfaces; 45e75d10f5SPatrick Williams msg.read(objPath, interfaces); 462ce7da29SGunnar Mills std::string path(std::move(objPath)); 4719177d3eSSaqib Khan std::string filePath; 482ce7da29SGunnar Mills 492ce7da29SGunnar Mills for (const auto& intf : interfaces) 502ce7da29SGunnar Mills { 51705f1bfcSSaqib Khan if (intf.first == VERSION_IFACE) 522ce7da29SGunnar Mills { 532ce7da29SGunnar Mills for (const auto& property : intf.second) 542ce7da29SGunnar Mills { 55705f1bfcSSaqib Khan if (property.first == "Purpose") 562ce7da29SGunnar Mills { 5784a0e693SSaqib Khan auto value = SVersion::convertVersionPurposeFromString( 5884a0e693SSaqib Khan variant_ns::get<std::string>(property.second)); 5984a0e693SSaqib Khan if (value == VersionPurpose::BMC || 6084a0e693SSaqib Khan value == VersionPurpose::System) 6184a0e693SSaqib Khan { 6284a0e693SSaqib Khan purpose = value; 6384a0e693SSaqib Khan } 64705f1bfcSSaqib Khan } 65705f1bfcSSaqib Khan else if (property.first == "Version") 66705f1bfcSSaqib Khan { 6784a0e693SSaqib Khan version = variant_ns::get<std::string>(property.second); 68705f1bfcSSaqib Khan } 69705f1bfcSSaqib Khan } 70705f1bfcSSaqib Khan } 7119177d3eSSaqib Khan else if (intf.first == FILEPATH_IFACE) 7219177d3eSSaqib Khan { 7319177d3eSSaqib Khan for (const auto& property : intf.second) 7419177d3eSSaqib Khan { 7519177d3eSSaqib Khan if (property.first == "Path") 7619177d3eSSaqib Khan { 7784a0e693SSaqib Khan filePath = variant_ns::get<std::string>(property.second); 7819177d3eSSaqib Khan } 7919177d3eSSaqib Khan } 8019177d3eSSaqib Khan } 81705f1bfcSSaqib Khan } 82705f1bfcSSaqib Khan if (version.empty() || 8319177d3eSSaqib Khan filePath.empty() || 8484a0e693SSaqib Khan purpose == VersionPurpose::Unknown) 852ce7da29SGunnar Mills { 86e75d10f5SPatrick Williams return; 872ce7da29SGunnar Mills } 882ce7da29SGunnar Mills 892ce7da29SGunnar Mills // Version id is the last item in the path 902ce7da29SGunnar Mills auto pos = path.rfind("/"); 912ce7da29SGunnar Mills if (pos == std::string::npos) 922ce7da29SGunnar Mills { 932ce7da29SGunnar Mills log<level::ERR>("No version id found in object path", 942ce7da29SGunnar Mills entry("OBJPATH=%s", path)); 95e75d10f5SPatrick Williams return; 962ce7da29SGunnar Mills } 972ce7da29SGunnar Mills 982ce7da29SGunnar Mills auto versionId = path.substr(pos + 1); 992ce7da29SGunnar Mills 100e75d10f5SPatrick Williams if (activations.find(versionId) == activations.end()) 1012ce7da29SGunnar Mills { 10235e83f3eSSaqib Khan // Determine the Activation state by processing the given image dir. 10335e83f3eSSaqib Khan auto activationState = server::Activation::Activations::Invalid; 104*9a782243SGunnar Mills ItemUpdater::ActivationStatus result = 105*9a782243SGunnar Mills ItemUpdater::validateSquashFSImage(filePath); 10635e83f3eSSaqib Khan if (result == ItemUpdater::ActivationStatus::ready) 10735e83f3eSSaqib Khan { 10835e83f3eSSaqib Khan activationState = server::Activation::Activations::Ready; 10935e83f3eSSaqib Khan } 11035e83f3eSSaqib Khan activations.insert(std::make_pair( 1112ce7da29SGunnar Mills versionId, 112ec1b41c4SGunnar Mills std::make_unique<Activation>( 11335e83f3eSSaqib Khan bus, 11435e83f3eSSaqib Khan path, 1154c1aec09SSaqib Khan *this, 11635e83f3eSSaqib Khan versionId, 11735e83f3eSSaqib Khan activationState))); 118705f1bfcSSaqib Khan versions.insert(std::make_pair( 119705f1bfcSSaqib Khan versionId, 120*9a782243SGunnar Mills std::make_unique<VersionClass>( 121705f1bfcSSaqib Khan bus, 122705f1bfcSSaqib Khan path, 123705f1bfcSSaqib Khan version, 124705f1bfcSSaqib Khan purpose, 1253526ef73SLeonel Gonzalez filePath, 1263526ef73SLeonel Gonzalez std::bind(&ItemUpdater::erase, 1273526ef73SLeonel Gonzalez this, 1283526ef73SLeonel Gonzalez std::placeholders::_1)))); 1292ce7da29SGunnar Mills } 1307b5010f2SSaqib Khan else 1317b5010f2SSaqib Khan { 1327b5010f2SSaqib Khan log<level::INFO>("Software Object with the same version already exists", 1337b5010f2SSaqib Khan entry("VERSION_ID=%s", versionId)); 1347b5010f2SSaqib Khan } 135e75d10f5SPatrick Williams return; 136ec1b41c4SGunnar Mills } 137ec1b41c4SGunnar Mills 138ba239881SSaqib Khan void ItemUpdater::processBMCImage() 139ba239881SSaqib Khan { 140*9a782243SGunnar Mills using VersionClass = phosphor::software::manager::Version; 141ba239881SSaqib Khan auto purpose = server::Version::VersionPurpose::BMC; 142ba239881SSaqib Khan auto version = phosphor::software::manager::Version::getBMCVersion(); 143ba239881SSaqib Khan auto id = phosphor::software::manager::Version::getId(version); 144ba239881SSaqib Khan auto path = std::string{SOFTWARE_OBJPATH} + '/' + id; 145ba239881SSaqib Khan activations.insert(std::make_pair( 146ba239881SSaqib Khan id, 147ba239881SSaqib Khan std::make_unique<Activation>( 148ba239881SSaqib Khan bus, 149ba239881SSaqib Khan path, 1504c1aec09SSaqib Khan *this, 151ba239881SSaqib Khan id, 152ba239881SSaqib Khan server::Activation::Activations::Active))); 153ba239881SSaqib Khan versions.insert(std::make_pair( 154ba239881SSaqib Khan id, 155*9a782243SGunnar Mills std::make_unique<VersionClass>( 156ba239881SSaqib Khan bus, 157ba239881SSaqib Khan path, 158ba239881SSaqib Khan version, 159ba239881SSaqib Khan purpose, 1603526ef73SLeonel Gonzalez "", 1613526ef73SLeonel Gonzalez std::bind(&ItemUpdater::erase, 1623526ef73SLeonel Gonzalez this, 1633526ef73SLeonel Gonzalez std::placeholders::_1)))); 1643526ef73SLeonel Gonzalez 165ba239881SSaqib Khan return; 166ba239881SSaqib Khan } 167ba239881SSaqib Khan 1683526ef73SLeonel Gonzalez void ItemUpdater::erase(std::string entryId) 1693526ef73SLeonel Gonzalez { 1700c2eb262SSaqib Khan // Delete ReadOnly partitions 1713526ef73SLeonel Gonzalez removeReadOnlyPartition(entryId); 1723526ef73SLeonel Gonzalez 1733526ef73SLeonel Gonzalez // Removing entry in versions map 1743526ef73SLeonel Gonzalez auto it = versions.find(entryId); 1753526ef73SLeonel Gonzalez if (it == versions.end()) 1763526ef73SLeonel Gonzalez { 1773526ef73SLeonel Gonzalez log<level::ERR>(("Error: Failed to find version " + entryId + \ 1783526ef73SLeonel Gonzalez " in item updater versions map." \ 1793526ef73SLeonel Gonzalez " Unable to remove.").c_str()); 1803526ef73SLeonel Gonzalez return; 1813526ef73SLeonel Gonzalez } 1823526ef73SLeonel Gonzalez this->versions.erase(entryId); 1833526ef73SLeonel Gonzalez 1843526ef73SLeonel Gonzalez // Removing entry in activations map 1853526ef73SLeonel Gonzalez auto ita = activations.find(entryId); 1863526ef73SLeonel Gonzalez if (ita == activations.end()) 1873526ef73SLeonel Gonzalez { 1883526ef73SLeonel Gonzalez log<level::ERR>(("Error: Failed to find version " + entryId + \ 1893526ef73SLeonel Gonzalez " in item updater activations map." \ 1903526ef73SLeonel Gonzalez " Unable to remove.").c_str()); 1913526ef73SLeonel Gonzalez return; 1923526ef73SLeonel Gonzalez } 1933526ef73SLeonel Gonzalez // TODO: openbmc/openbmc#1986 1943526ef73SLeonel Gonzalez // Test if this is the currently running image 1953526ef73SLeonel Gonzalez // If not, don't continue. 1963526ef73SLeonel Gonzalez 1973526ef73SLeonel Gonzalez this->activations.erase(entryId); 1985d532675SSaqib Khan removeFile(entryId); 1993526ef73SLeonel Gonzalez } 2003526ef73SLeonel Gonzalez 20135e83f3eSSaqib Khan ItemUpdater::ActivationStatus ItemUpdater::validateSquashFSImage( 20219177d3eSSaqib Khan const std::string& filePath) 20335e83f3eSSaqib Khan { 204b1cfdf99SMichael Tritz bool invalid = false; 20535e83f3eSSaqib Khan 206b1cfdf99SMichael Tritz for (auto& bmcImage : bmcImages) 207b1cfdf99SMichael Tritz { 20819177d3eSSaqib Khan fs::path file(filePath); 20935e83f3eSSaqib Khan file /= bmcImage; 21035e83f3eSSaqib Khan std::ifstream efile(file.c_str()); 211b1cfdf99SMichael Tritz if (efile.good() != 1) 21235e83f3eSSaqib Khan { 213b1cfdf99SMichael Tritz log<level::ERR>("Failed to find the BMC image.", 214b1cfdf99SMichael Tritz entry("IMAGE=%s", bmcImage.c_str())); 215b1cfdf99SMichael Tritz invalid = true; 21635e83f3eSSaqib Khan } 217b1cfdf99SMichael Tritz } 218b1cfdf99SMichael Tritz 219b1cfdf99SMichael Tritz if (invalid) 22035e83f3eSSaqib Khan { 22135e83f3eSSaqib Khan return ItemUpdater::ActivationStatus::invalid; 22235e83f3eSSaqib Khan } 223b1cfdf99SMichael Tritz 224b1cfdf99SMichael Tritz return ItemUpdater::ActivationStatus::ready; 22535e83f3eSSaqib Khan } 22635e83f3eSSaqib Khan 2274c1aec09SSaqib Khan void ItemUpdater::freePriority(uint8_t value) 2284c1aec09SSaqib Khan { 2294c1aec09SSaqib Khan //TODO openbmc/openbmc#1896 Improve the performance of this function 2304c1aec09SSaqib Khan for (const auto& intf : activations) 2314c1aec09SSaqib Khan { 2324c1aec09SSaqib Khan if (intf.second->redundancyPriority) 2334c1aec09SSaqib Khan { 2344c1aec09SSaqib Khan if (intf.second->redundancyPriority.get()->priority() == value) 2354c1aec09SSaqib Khan { 2364c1aec09SSaqib Khan intf.second->redundancyPriority.get()->priority(value + 1); 2374c1aec09SSaqib Khan } 2384c1aec09SSaqib Khan } 2394c1aec09SSaqib Khan } 2404c1aec09SSaqib Khan } 2414c1aec09SSaqib Khan 24237a59043SMichael Tritz void ItemUpdater::reset() 24337a59043SMichael Tritz { 24437a59043SMichael Tritz // Mark the read-write partition for recreation upon reboot. 24537a59043SMichael Tritz auto method = bus.new_method_call( 24637a59043SMichael Tritz SYSTEMD_BUSNAME, 24737a59043SMichael Tritz SYSTEMD_PATH, 24837a59043SMichael Tritz SYSTEMD_INTERFACE, 24937a59043SMichael Tritz "StartUnit"); 2500129d926SMichael Tritz method.append("obmc-flash-bmc-setenv@rwreset\\x3dtrue.service", "replace"); 25137a59043SMichael Tritz bus.call_noreply(method); 25237a59043SMichael Tritz 25337a59043SMichael Tritz log<level::INFO>("BMC factory reset will take effect upon reboot."); 25437a59043SMichael Tritz 25537a59043SMichael Tritz return; 25637a59043SMichael Tritz } 25737a59043SMichael Tritz 2583526ef73SLeonel Gonzalez void ItemUpdater::removeReadOnlyPartition(std::string versionId) 2593526ef73SLeonel Gonzalez { 2603526ef73SLeonel Gonzalez auto serviceFile = "obmc-flash-bmc-ubiro-remove@" + versionId + 2613526ef73SLeonel Gonzalez ".service"; 2623526ef73SLeonel Gonzalez 2633526ef73SLeonel Gonzalez // Remove the read-only partitions. 2643526ef73SLeonel Gonzalez auto method = bus.new_method_call( 2653526ef73SLeonel Gonzalez SYSTEMD_BUSNAME, 2663526ef73SLeonel Gonzalez SYSTEMD_PATH, 2673526ef73SLeonel Gonzalez SYSTEMD_INTERFACE, 2683526ef73SLeonel Gonzalez "StartUnit"); 2693526ef73SLeonel Gonzalez method.append(serviceFile, "replace"); 2703526ef73SLeonel Gonzalez bus.call_noreply(method); 2713526ef73SLeonel Gonzalez } 2723526ef73SLeonel Gonzalez 2730129d926SMichael Tritz bool ItemUpdater::fieldModeEnabled(bool value) 2740129d926SMichael Tritz { 2750129d926SMichael Tritz // enabling field mode is intended to be one way: false -> true 2760129d926SMichael Tritz if (value && !control::FieldMode::fieldModeEnabled()) 2770129d926SMichael Tritz { 2780129d926SMichael Tritz control::FieldMode::fieldModeEnabled(value); 2790129d926SMichael Tritz 2800129d926SMichael Tritz auto method = bus.new_method_call( 2810129d926SMichael Tritz SYSTEMD_BUSNAME, 2820129d926SMichael Tritz SYSTEMD_PATH, 2830129d926SMichael Tritz SYSTEMD_INTERFACE, 2840129d926SMichael Tritz "StartUnit"); 2850129d926SMichael Tritz method.append("obmc-flash-bmc-setenv@fieldmode\\x3dtrue.service", 2860129d926SMichael Tritz "replace"); 2870129d926SMichael Tritz bus.call_noreply(method); 2880129d926SMichael Tritz 2890129d926SMichael Tritz method = bus.new_method_call( 2900129d926SMichael Tritz SYSTEMD_BUSNAME, 2910129d926SMichael Tritz SYSTEMD_PATH, 2920129d926SMichael Tritz SYSTEMD_INTERFACE, 2930129d926SMichael Tritz "StopUnit"); 2940129d926SMichael Tritz method.append("usr-local.mount", "replace"); 2950129d926SMichael Tritz bus.call_noreply(method); 2960129d926SMichael Tritz 2970129d926SMichael Tritz std::vector<std::string> usrLocal = {"usr-local.mount"}; 2980129d926SMichael Tritz 2990129d926SMichael Tritz method = bus.new_method_call( 3000129d926SMichael Tritz SYSTEMD_BUSNAME, 3010129d926SMichael Tritz SYSTEMD_PATH, 3020129d926SMichael Tritz SYSTEMD_INTERFACE, 3030129d926SMichael Tritz "MaskUnitFiles"); 3040129d926SMichael Tritz method.append(usrLocal, false, true); 3050129d926SMichael Tritz bus.call_noreply(method); 3060129d926SMichael Tritz } 3070129d926SMichael Tritz 3080129d926SMichael Tritz return control::FieldMode::fieldModeEnabled(); 3090129d926SMichael Tritz } 3100129d926SMichael Tritz 3110129d926SMichael Tritz void ItemUpdater::restoreFieldModeStatus() 3120129d926SMichael Tritz { 3130129d926SMichael Tritz std::ifstream input("/run/fw_env"); 3140129d926SMichael Tritz std::string envVar; 3150129d926SMichael Tritz std::getline(input, envVar); 3160129d926SMichael Tritz 3170129d926SMichael Tritz if (envVar.find("fieldmode=true") != std::string::npos) 3180129d926SMichael Tritz { 3190129d926SMichael Tritz ItemUpdater::fieldModeEnabled(true); 3200129d926SMichael Tritz } 3210129d926SMichael Tritz } 3220129d926SMichael Tritz 323ec1b41c4SGunnar Mills } // namespace updater 324ec1b41c4SGunnar Mills } // namespace software 325ec1b41c4SGunnar Mills } // namespace phosphor 326