blob: dd458b0dc161d066fe3178f4fd065cf3c6dab889 [file] [log] [blame]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This header exposes some parts of the partition migration for unit
// testing.
#ifndef INSTALLER_REVEN_PARTITION_MIGRATION_PRIVATE_H_
#define INSTALLER_REVEN_PARTITION_MIGRATION_PRIVATE_H_
#include <cstdint>
#include "installer/cgpt_manager.h"
#include "installer/inst_util.h"
#include "installer/metrics.h"
// These values are persisted to logs. Entries should not be renumbered
// and numeric values should never be reused.
enum class [[nodiscard]] PartitionMigrationResult {
kSuccess = 0,
kNoMigrationNeeded = 1,
kGptReadKernError = 2,
kGptReadRootError = 3,
kGptWriteKernError = 4,
kGptWriteRootError = 5,
kDiskOpenError = 6,
kDiskReadError = 7,
kDiskWriteError = 8,
kRootPartitionUnexpectedSize = 9,
kMax = kRootPartitionUnexpectedSize,
};
// Plan for migrating one kernel partition (either slot A or slot B).
//
// This separates gathering the info needed to perform the migration
// from the migration itself. This allows the slot migration to be
// planned before running it; if anything fails when initializing a slot
// plan then the whole migration (for both slots) is canceled. This
// allows us to minimize the number of errors that can occur in the
// migration itself.
class SlotPlan {
public:
static SlotPlan ForSlotA(CgptManagerInterface& cgpt_manager);
static SlotPlan ForSlotB(CgptManagerInterface& cgpt_manager);
// Initialize the plan.
//
// * Returns `kSuccess` if the initialization is successful and a
// migration is needed.
// * Returns `kNoMigrationNeeded` if the kernel partition is already
// big enough.
// * Returns an error if the kernel or root partition entries can't be
// read, or if the root partition is too small for the migration.
PartitionMigrationResult Initialize();
// Write out the new kernel partition's data to disk.
//
// The new kernel data starts with a copy of the kernel data from the
// original kernel partition.The rest of the data is initialized with
// zeroes.
//
// This is not a destructive action since the data being written is
// within the bounds of the current root partition, but outside the
// region within the root partition that's actually used.
//
// * Returns `kSuccess` if the data is successfully copied.
// * Returns an error if the disk couldn't be opened, or if reading
// the original data fails, or if writing out the data fails.
PartitionMigrationResult WriteNewKernelData() const;
// Shrink the root partition to make room for the new kernel
// partition.
//
// * Returns `kSuccess` if the partition entry is successfully updated.
// * Returns an error if the partition entry can't be updated.
PartitionMigrationResult ShrinkRootPartition() const;
// Move and expand the kernel partition.
//
// * Returns `kSuccess` if the partition entry is successfully updated.
// * Returns an error if the partition entry can't be updated.
PartitionMigrationResult UpdateKernelPartition() const;
// Perform the slot's migration using `WriteNewKernelData`,
// `ShrinkRootPartition`, and `UpdateKernelPartition`.
//
// * Returns `kSuccess` if all operations succeed.
// * Returns an error if any operation fails.
PartitionMigrationResult Run() const;
private:
SlotPlan(CgptManagerInterface& cgpt_manager,
PartitionNum kern_num,
PartitionNum root_num);
CgptManagerInterface& cgpt_manager_;
const PartitionNum kern_num_;
const PartitionNum root_num_;
// New (smaller) size of the root partition.
uint64_t root_new_num_sectors_ = 0;
// Sectors used by the original kernel partition.
SectorRange kern_orig_sectors_;
// New location and size of the kernel partition.
SectorRange kern_new_sectors_;
};
// Plan for migrating both slots.
//
// This separates gathering the info needed to perform the migration
// from the migration itself. This allows the full migration to be
// planned before running it; if anything fails when initializing a slot
// plan then the whole migration (for both slots) is canceled. This
// allows us to minimize the number of errors that can occur in the
// migration itself.
class FullPlan {
public:
explicit FullPlan(CgptManagerInterface& cgpt_manager);
// Initialize the plan.
//
// * Returns `kSuccess` if the initialization is successful and at
// * least one slot needs to be migrated.
// * Returns `kNoMigrationNeeded` if both slots have already been migrated.
// * Returns an error if either slot plan failed to initialize.
PartitionMigrationResult Initialize();
// Perform the slot migrations.
//
// If a slot has already been migrated, it will be skipped.
//
// * Returns `kSuccess` if all operations succeed.
// * Returns an error if any operation fails.
PartitionMigrationResult Run();
private:
CgptManagerInterface& cgpt_manager_;
// Result of initializing the slot plans.
PartitionMigrationResult slot_a_result_;
PartitionMigrationResult slot_b_result_;
SlotPlan slot_a_plan_;
SlotPlan slot_b_plan_;
};
// Convert from MiB to 512-byte disk sectors.
uint64_t MibToSectors(uint64_t mib);
// Convert from 512-byte disk sectors to bytes.
uint64_t SectorsToBytes(uint64_t sectors);
#endif // INSTALLER_REVEN_PARTITION_MIGRATION_PRIVATE_H_