Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 15d7230e authored by David Anderson's avatar David Anderson Committed by Gerrit Code Review
Browse files

Merge "fastboot: Avoid reboots to userspace when using flashall/update."

parents 4c80eb13 667b1efa
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -286,6 +286,7 @@ cc_library_host_static {
        "fastboot.cpp",
        "fs.cpp",
        "socket.cpp",
        "super_flash_helper.cpp",
        "tcp.cpp",
        "udp.cpp",
        "util.cpp",
+116 −16
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@
#include <build/version.h>
#include <libavb/libavb.h>
#include <liblp/liblp.h>
#include <liblp/super_layout_builder.h>
#include <platform_tools_version.h>
#include <sparse/sparse.h>
#include <ziparchive/zip_archive.h>
@@ -72,6 +73,7 @@
#include "diagnose_usb.h"
#include "fastboot_driver.h"
#include "fs.h"
#include "super_flash_helper.h"
#include "tcp.h"
#include "transport.h"
#include "udp.h"
@@ -1405,20 +1407,30 @@ class FlashAllTool {

  private:
    void CheckRequirements();
    void DetermineSecondarySlot();
    void DetermineSlot();
    void CollectImages();
    void FlashImages(const std::vector<std::pair<const Image*, std::string>>& images);
    void FlashImage(const Image& image, const std::string& slot, fastboot_buffer* buf);
    void UpdateSuperPartition();
    bool OptimizedFlashSuper();

    // If the image uses the default slot, or the user specified "all", then
    // the paired string will be empty. If the image requests a specific slot
    // (for example, system_other) it is specified instead.
    using ImageEntry = std::pair<const Image*, std::string>;

    std::string GetPartitionName(const ImageEntry& entry);

    const ImageSource& source_;
    std::string slot_override_;
    bool skip_secondary_;
    bool wipe_;
    bool force_flash_;
    std::string current_slot_;
    std::string secondary_slot_;
    std::vector<std::pair<const Image*, std::string>> boot_images_;
    std::vector<std::pair<const Image*, std::string>> os_images_;

    std::vector<ImageEntry> boot_images_;
    std::vector<ImageEntry> os_images_;
};

FlashAllTool::FlashAllTool(const ImageSource& source, const std::string& slot_override,
@@ -1441,7 +1453,7 @@ void FlashAllTool::Flash() {
        set_active(slot_override_);
    }

    DetermineSecondarySlot();
    DetermineSlot();
    CollectImages();

    CancelSnapshotIfNeeded();
@@ -1450,6 +1462,7 @@ void FlashAllTool::Flash() {
    // or in bootloader fastboot.
    FlashImages(boot_images_);

    if (!OptimizedFlashSuper()) {
        // Sync the super partition. This will reboot to userspace fastboot if needed.
        UpdateSuperPartition();

@@ -1463,11 +1476,78 @@ void FlashAllTool::Flash() {
            };
            do_for_partitions(image->part_name, slot, resize_partition, false);
        }
    }

    // Flash OS images, resizing logical partitions as needed.
    FlashImages(os_images_);
}

bool FlashAllTool::OptimizedFlashSuper() {
    if (!supports_AB()) {
        LOG(VERBOSE) << "Cannot optimize flashing super on non-AB device";
        return false;
    }
    if (slot_override_ == "all") {
        LOG(VERBOSE) << "Cannot optimize flashing super for all slots";
        return false;
    }

    // Does this device use dynamic partitions at all?
    unique_fd fd = source_.OpenFile("super_empty.img");
    if (fd < 0) {
        LOG(VERBOSE) << "could not open super_empty.img";
        return false;
    }

    // Try to find whether there is a super partition.
    std::string super_name;
    if (fb->GetVar("super-partition-name", &super_name) != fastboot::SUCCESS) {
        super_name = "super";
    }
    std::string partition_size_str;
    if (fb->GetVar("partition-size:" + super_name, &partition_size_str) != fastboot::SUCCESS) {
        LOG(VERBOSE) << "Cannot optimize super flashing: could not determine super partition";
        return false;
    }

    SuperFlashHelper helper(source_);
    if (!helper.Open(fd)) {
        return false;
    }

    for (const auto& entry : os_images_) {
        auto partition = GetPartitionName(entry);
        auto image = entry.first;

        if (!helper.AddPartition(partition, image->img_name, image->optional_if_no_image)) {
            return false;
        }
    }

    auto s = helper.GetSparseLayout();
    if (!s) {
        return false;
    }

    std::vector<SparsePtr> files;
    if (int limit = get_sparse_limit(sparse_file_len(s.get(), false, false))) {
        files = resparse_file(s.get(), limit);
    } else {
        files.emplace_back(std::move(s));
    }

    // Send the data to the device.
    flash_partition_files(super_name, files);

    // Remove images that we already flashed, just in case we have non-dynamic OS images.
    auto remove_if_callback = [&, this](const ImageEntry& entry) -> bool {
        return helper.WillFlash(GetPartitionName(entry));
    };
    os_images_.erase(std::remove_if(os_images_.begin(), os_images_.end(), remove_if_callback),
                     os_images_.end());
    return true;
}

void FlashAllTool::CheckRequirements() {
    std::vector<char> contents;
    if (!source_.ReadFile("android-info.txt", &contents)) {
@@ -1476,7 +1556,13 @@ void FlashAllTool::CheckRequirements() {
    ::CheckRequirements({contents.data(), contents.size()}, force_flash_);
}

void FlashAllTool::DetermineSecondarySlot() {
void FlashAllTool::DetermineSlot() {
    if (slot_override_.empty()) {
        current_slot_ = get_current_slot();
    } else {
        current_slot_ = slot_override_;
    }

    if (skip_secondary_) {
        return;
    }
@@ -1575,6 +1661,20 @@ void FlashAllTool::UpdateSuperPartition() {
    }
}

std::string FlashAllTool::GetPartitionName(const ImageEntry& entry) {
    auto slot = entry.second;
    if (slot.empty()) {
        slot = current_slot_;
    }
    if (slot.empty()) {
        return entry.first->part_name;
    }
    if (slot == "all") {
        LOG(FATAL) << "Cannot retrieve a singular name when using all slots";
    }
    return entry.first->part_name + "_" + slot;
}

class ZipImageSource final : public ImageSource {
  public:
    explicit ZipImageSource(ZipArchiveHandle zip) : zip_(zip) {}
+125 −0
Original line number Diff line number Diff line
//
// Copyright (C) 2023 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#include "super_flash_helper.h"

#include <android-base/logging.h>

#include "util.h"

using android::base::borrowed_fd;
using android::base::unique_fd;
using android::fs_mgr::SuperImageExtent;

SuperFlashHelper::SuperFlashHelper(const ImageSource& source) : source_(source) {}

bool SuperFlashHelper::Open(borrowed_fd fd) {
    if (!builder_.Open(fd)) {
        LOG(VERBOSE) << "device does not support optimized super flashing";
        return false;
    }

    base_metadata_ = builder_.Export();
    return !!base_metadata_;
}

bool SuperFlashHelper::IncludeInSuper(const std::string& partition) {
    return should_flash_in_userspace(*base_metadata_.get(), partition);
}

bool SuperFlashHelper::AddPartition(const std::string& partition, const std::string& image_name,
                                    bool optional) {
    if (!IncludeInSuper(partition)) {
        return true;
    }
    auto iter = image_fds_.find(image_name);
    if (iter == image_fds_.end()) {
        unique_fd fd = source_.OpenFile(image_name);
        if (fd < 0) {
            if (!optional) {
                LOG(VERBOSE) << "could not find partition image: " << image_name;
                return false;
            }
            return true;
        }
        if (is_sparse_file(fd)) {
            LOG(VERBOSE) << "cannot optimize dynamic partitions with sparse images";
            return false;
        }
        iter = image_fds_.emplace(image_name, std::move(fd)).first;
    }

    if (!builder_.AddPartition(partition, image_name, get_file_size(iter->second))) {
        return false;
    }

    will_flash_.emplace(partition);
    return true;
}

SparsePtr SuperFlashHelper::GetSparseLayout() {
    // Cache extents since the sparse ptr depends on data pointers.
    if (extents_.empty()) {
        extents_ = builder_.GetImageLayout();
        if (extents_.empty()) {
            LOG(VERBOSE) << "device does not support optimized super flashing";
            return {nullptr, nullptr};
        }
    }

    unsigned int block_size = base_metadata_->geometry.logical_block_size;
    int64_t flashed_size = extents_.back().offset + extents_.back().size;
    SparsePtr s(sparse_file_new(block_size, flashed_size), sparse_file_destroy);

    for (const auto& extent : extents_) {
        if (extent.offset / block_size > UINT_MAX) {
            // Super image is too big to send via sparse files (>8TB).
            LOG(VERBOSE) << "super image is too big to flash";
            return {nullptr, nullptr};
        }
        unsigned int block = extent.offset / block_size;

        int rv = 0;
        switch (extent.type) {
            case SuperImageExtent::Type::DONTCARE:
                break;
            case SuperImageExtent::Type::ZERO:
                rv = sparse_file_add_fill(s.get(), 0, extent.size, block);
                break;
            case SuperImageExtent::Type::DATA:
                rv = sparse_file_add_data(s.get(), extent.blob->data(), extent.size, block);
                break;
            case SuperImageExtent::Type::PARTITION: {
                auto iter = image_fds_.find(extent.image_name);
                if (iter == image_fds_.end()) {
                    LOG(FATAL) << "image added but not found: " << extent.image_name;
                    return {nullptr, nullptr};
                }
                rv = sparse_file_add_fd(s.get(), iter->second.get(), extent.image_offset,
                                        extent.size, block);
                break;
            }
            default:
                LOG(VERBOSE) << "unrecognized extent type in super image layout";
                return {nullptr, nullptr};
        }
        if (rv) {
            LOG(VERBOSE) << "sparse failure building super image layout";
            return {nullptr, nullptr};
        }
    }
    return s;
}
+56 −0
Original line number Diff line number Diff line
//
// Copyright (C) 2023 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#pragma once

#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>

#include <android-base/unique_fd.h>
#include <liblp/liblp.h>
#include <liblp/super_layout_builder.h>

#include "util.h"

class SuperFlashHelper final {
  public:
    explicit SuperFlashHelper(const ImageSource& source);

    bool Open(android::base::borrowed_fd fd);
    bool IncludeInSuper(const std::string& partition);
    bool AddPartition(const std::string& partition, const std::string& image_name, bool optional);

    // Note: the SparsePtr if non-null should not outlive SuperFlashHelper, since
    // it depends on open fds and data pointers.
    SparsePtr GetSparseLayout();

    bool WillFlash(const std::string& partition) const {
        return will_flash_.find(partition) != will_flash_.end();
    }

  private:
    const ImageSource& source_;
    android::fs_mgr::SuperLayoutBuilder builder_;
    std::unique_ptr<android::fs_mgr::LpMetadata> base_metadata_;
    std::vector<android::fs_mgr::SuperImageExtent> extents_;

    // Cache open image fds. This keeps them alive while we flash the sparse
    // file.
    std::unordered_map<std::string, android::base::unique_fd> image_fds_;
    std::unordered_set<std::string> will_flash_;
};