From 030f918b90ea45104bccf68082c2d634c6694238 Mon Sep 17 00:00:00 2001 From: Vikram Bodireddy Date: Tue, 13 Aug 2019 22:43:12 +0530 Subject: [PATCH] PFR images support in phosphor-software-manager This commit adds support for handling the PFR images upload and processing. Testing: tested PFR image uploads and updates Signed-off-by: Vikram Bodireddy --- activation.cpp | 2 +- item_updater.cpp | 7 +- meson.build | 7 +- meson_options.txt | 3 + pfr_image_manager.cpp | 217 ++++++++++++++++++++++++++++++++++++++++++ pfr_image_manager.hpp | 75 +++++++++++++++ 6 files changed, 306 insertions(+), 5 deletions(-) create mode 100644 pfr_image_manager.cpp create mode 100644 pfr_image_manager.hpp diff --git a/activation.cpp b/activation.cpp index bad17b8..3363230 100644 --- a/activation.cpp +++ b/activation.cpp @@ -119,7 +119,7 @@ auto Activation::activation(Activations value) -> Activations } else if (activationProgress->progress() == 100) { - log("[Jennifer] progress == 100..."); + log("progress == 100..."); if (!redundancyPriority) { redundancyPriority = diff --git a/item_updater.cpp b/item_updater.cpp index df8595c..694975f 100644 --- a/item_updater.cpp +++ b/item_updater.cpp @@ -64,10 +64,10 @@ void ItemUpdater::createActivation(sdbusplus::message::message& msg) auto value = SVersion::convertVersionPurposeFromString( std::get(property.second)); if (value == VersionPurpose::BMC || -#ifdef HOST_BIOS_UPGRADE +#if defined(HOST_BIOS_UPGRADE) || defined(PFR_UPDATE) value == VersionPurpose::Host || #endif - value == VersionPurpose::System) + value == VersionPurpose::Other) { purpose = value; } @@ -399,6 +399,7 @@ void ItemUpdater::deleteAll() ItemUpdater::ActivationStatus ItemUpdater::validateSquashFSImage(const std::string& filePath) { +#ifndef PFR_UPDATE bool valid = true; // Record the images which are being updated @@ -416,7 +417,7 @@ ItemUpdater::ActivationStatus return ItemUpdater::ActivationStatus::invalid; } } - +#endif return ItemUpdater::ActivationStatus::ready; } @@ -690,8 +691,8 @@ void ItemUpdater::freeSpace(Activation& caller) // Failed activations don't have priority, assign them a large value // for sorting purposes. auto priority = 999; - if ((iter.second.get()->activation() == - server::Activation::Activations::Active)&& + if ((iter.second.get()->activation() == + server::Activation::Activations::Active) && iter.second->redundancyPriority.get()) { priority = iter.second->redundancyPriority.get()->priority(); diff --git a/meson.build b/meson.build index 08d6f71..c61d59f 100644 --- a/meson.build +++ b/meson.build @@ -55,6 +55,7 @@ conf.set('MMC_LAYOUT', get_option('bmc-layout').contains('mmc')) conf.set('HOST_BIOS_UPGRADE', get_option('host-bios-upgrade').enabled()) conf.set('WANT_SIGNATURE_VERIFY', get_option('verify-signature').enabled()) conf.set('FWUPD_SCRIPT', get_option('fwupd-script').enabled()) +conf.set('PFR_UPDATE', get_option('pfr-update').enabled()) # Configurable variables conf.set('ACTIVE_BMC_MAX_ALLOWED', get_option('active-bmc-max-allowed')) @@ -195,12 +196,16 @@ executable( install: true ) +image_manager_source = files('image_manager.cpp') +if get_option('pfr-update').enabled() + image_manager_source = files('pfr_image_manager.cpp') +endif executable( 'phosphor-version-software-manager', image_error_cpp, image_error_hpp, - 'image_manager.cpp', 'image_manager_main.cpp', + image_manager_source, 'version.cpp', 'watch.cpp', dependencies: [deps, ssl], diff --git a/meson_options.txt b/meson_options.txt index 4f7e62a..1593502 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -25,6 +25,9 @@ option('verify-signature', type: 'feature', option('fwupd-script', type: 'feature', description: 'Enable fwupd script support.') +option('pfr-update', type: 'feature', + description: 'Enable fwupd script support.') + # Variables option( 'active-bmc-max-allowed', type: 'integer', diff --git a/pfr_image_manager.cpp b/pfr_image_manager.cpp new file mode 100644 index 0000000..242a6ca --- /dev/null +++ b/pfr_image_manager.cpp @@ -0,0 +1,218 @@ +#include "config.h" + +#include "pfr_image_manager.hpp" + +#include "version.hpp" +#include "watch.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace phosphor +{ +namespace software +{ +namespace manager +{ + +using namespace sdbusplus::xyz::openbmc_project::Software::Image::Error; +namespace Software = phosphor::logging::xyz::openbmc_project::Software; + +static constexpr const uint32_t pfmPos = 2054; + +static int getPFRImgInfo(const std::filesystem::path imgPath, uint8_t& imgType, + std::string& version) +{ + struct pfrImgBlock0 block0Data; + uint8_t verData[2]; + + if (std::filesystem::exists(imgPath)) + { + try + { + std::ifstream imgFile(imgPath, std::ios::binary | std::ios::in); + + if (!imgFile.good()) + { + phosphor::logging::log( + "Image file read failed"); + return -1; + } + + imgFile.read(reinterpret_cast(&block0Data), + sizeof(block0Data)); + imgType = block0Data.pcType[0]; + imgFile.seekg(pfmPos, + std::ios::beg); // Version is at 0x806 in the PFM + imgFile.read(reinterpret_cast(&verData), sizeof(verData)); + imgFile.close(); + version = + std::to_string(verData[0]) + "." + std::to_string(verData[1]); + phosphor::logging::log( + "PFR image", + phosphor::logging::entry("PCType=%d", block0Data.pcType[0]), + phosphor::logging::entry("VERSION=%s", version.c_str())); + } + catch (std::exception& e) + { + phosphor::logging::log(e.what()); + return -1; + } + } + + return 0; +} + +int Manager::processImage(const std::string& imgFilePath) +{ + std::filesystem::path imgPath(imgFilePath); + + if (!std::filesystem::exists(imgPath)) + return -1; + + uint8_t imgType; + int retry = 3; + std::string ver; + std::string purposeString; + + if (0 != getPFRImgInfo(imgFilePath, imgType, ver)) + { + phosphor::logging::log( + "Error reading uploaded image type and version"); + return -1; + } + + if (ver.empty()) + { + phosphor::logging::log( + "Empty version from image file"); + return -1; + } + + if (imgType == pfrBMCUpdateCap) + { + purposeString = + "xyz.openbmc_project.Software.Version.VersionPurpose.BMC"; + } + else if (imgType == pfrPCHUpdateCap) + { + purposeString = + "xyz.openbmc_project.Software.Version.VersionPurpose.Host"; + } + else if (imgType == pfrCPLDUpdateCap) + { + purposeString = + "xyz.openbmc_project.Software.Version.VersionPurpose.Other"; + } + else + { + purposeString = + "xyz.openbmc_project.Software.Version.VersionPurpose.Unknown"; + + phosphor::logging::log( + "Unknown image type"); + return -1; + } + + sdbusplus::xyz::openbmc_project::Software::server::Version::VersionPurpose + purpose = Version::VersionPurpose::Unknown; + try + { + purpose = Version::convertVersionPurposeFromString(purposeString); + } + catch (const sdbusplus::exception::InvalidEnumString& e) + { + phosphor::logging::log( + "Error: Failed to convert purpose to enum." + " Setting to Unknown."); + } + + // Compute id + std::string id = Version::getId(ver); + + // Append a random number after the original version hash + // This will allow forcing image update onto the same version + // with 3 retries on random number generation. + do + { + srand(time(NULL)); + id = id + "_" + std::to_string(rand()); + } while ((versions.find(id) != versions.end()) && retry--); + + if (versions.find(id) != versions.end()) + { + phosphor::logging::log( + "Software Object with the same version already exists, exiting " + "the update", + phosphor::logging::entry("VERSION_ID=%s", id.c_str())); + + return -1; + } + + std::filesystem::path imageDirPath(IMG_UPLOAD_DIR); + imageDirPath /= id; + + std::filesystem::create_directory(imageDirPath); + + std::filesystem::path newFileName = imageDirPath / "image-runtime"; + std::filesystem::rename(imgFilePath, newFileName); + + // Create Version object + std::string objPath = std::string{SOFTWARE_OBJPATH} + '/' + id; + + auto versionPtr = std::make_unique( + bus, objPath, ver, purpose, imageDirPath.string(), + std::bind(&Manager::erase, this, std::placeholders::_1)); + versionPtr->deleteObject = + std::make_unique(bus, objPath, + *versionPtr); + versions.insert(std::make_pair(id, std::move(versionPtr))); + + return 0; +} + +void Manager::erase(std::string entryId) +{ + auto it = versions.find(entryId); + if (it == versions.end()) + { + return; + } + + if (it->second->isFunctional()) + { + phosphor::logging::log( + ("Error: Version " + entryId + + " is currently running on the BMC." + " Unable to remove.") + .c_str()); + return; + } + + // Delete image dir + std::filesystem::path imageDirPath = (*(it->second)).path(); + if (std::filesystem::exists(imageDirPath)) + { + std::filesystem::remove_all(imageDirPath); + } + this->versions.erase(entryId); +} + +} // namespace manager +} // namespace software +} // namespace phosphor diff --git a/pfr_image_manager.hpp b/pfr_image_manager.hpp new file mode 100644 index 0000000..c6ee6a4 --- /dev/null +++ b/pfr_image_manager.hpp @@ -0,0 +1,76 @@ +#pragma once +#include "version.hpp" + +#include + +namespace phosphor +{ +namespace software +{ +namespace manager +{ + +enum pfrImgPCType +{ + pfrCPLDUpdateCap = 0x00, + pfrPCHPFM = 0x01, + pfrPCHUpdateCap = 0x02, + pfrBMCPFM = 0x03, + pfrBMCUpdateCap = 0x04 +}; + +/* PFR image block 0 - As defined in HAS */ +struct pfrImgBlock0 +{ + uint8_t tag[4]; + uint8_t pcLength[4]; + uint8_t pcType[4]; + uint8_t reserved1[4]; + uint8_t hash256[32]; + uint8_t hash384[48]; + uint8_t reserved2[32]; +} __attribute__((packed)); + +/** @class Manager + * @brief Contains a map of Version dbus objects. + * @details The software image manager class that contains the Version dbus + * objects and their version ids. + */ +class Manager +{ + public: + /** @brief Constructs Manager Class + * + * @param[in] bus - The Dbus bus object + */ + Manager(sdbusplus::bus::bus& bus) : bus(bus){}; + + /** + * @brief Verify the image and provide the image to updater. + * Create and populate the version and file path interfaces. + * + * @param[in] uploaded image. + * @param[out] result - 0 if successful. + */ + int processImage(const std::string& imageFilePath); + + /** + * @brief Erase specified entry d-bus object + * and deletes the image file. + * + * @param[in] entryId - unique identifier of the entry + */ + void erase(std::string entryId); + + private: + /** @brief Persistent map of Version dbus objects and their + * version id */ + std::map> versions; + + /** @brief Persistent sdbusplus DBus bus connection. */ + sdbusplus::bus::bus& bus; +}; + +} // namespace manager +} // namespace software +} // namespace phosphor -- 2.17.1