blob: 3fe5160d686be9e7b8b116ef14af42719b7a0b26 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include <digest/digest.h>
#include <stdlib.h>
#include <string.h>
#define FVM_MAGIC (0x54524150204d5646ull) // 'FVM PART'
#define FVM_VERSION 0x00000001
#define FVM_SLICE_ENTRY_FREE 0
#define FVM_BLOCK_SIZE 8192lu
#define FVM_NAME_LEN 24
#ifdef __cplusplus
#include <fbl/algorithm.h>
#include <gpt/gpt.h>
#define FVM_GUID_LEN GPT_GUID_LEN
#define FVM_GUID_STRLEN GPT_GUID_STRLEN
namespace fvm {
typedef struct {
uint64_t magic;
uint64_t version;
uint64_t pslice_count; // Slices which can be used by vpartitions
uint64_t slice_size; // All sizes in bytes
uint64_t fvm_partition_size;
uint64_t vpartition_table_size;
uint64_t allocation_table_size;
uint64_t generation;
uint8_t hash[SHA256_DIGEST_LENGTH];
uint8_t reserved[0]; // Up to the rest of the block
} fvm_t;
static_assert(sizeof(fvm_t) <= FVM_BLOCK_SIZE, "FVM Superblock too large");
#define FVM_MAX_ENTRIES 1024
// Identifies that the partition is inactive, and should be destroyed on
// reboot (unless activated before rebinding the FVM).
constexpr uint32_t kVPartFlagInactive = 0x00000001;
constexpr uint32_t kVPartAllocateMask = 0x00000001; // All acceptable flags to pass to allocate.
typedef struct {
void init(const uint8_t* type_, const uint8_t* guid_, uint32_t slices_,
const char* name_, uint32_t flags_) {
slices = slices_;
memcpy(type, type_, FVM_GUID_LEN);
memcpy(guid, guid_, FVM_GUID_LEN);
memcpy(name, name_, FVM_NAME_LEN);
flags = flags_;
}
void clear() {
memset(this, 0, sizeof(*this));
}
uint8_t type[FVM_GUID_LEN]; // Mirroring GPT value
uint8_t guid[FVM_GUID_LEN]; // Mirroring GPT value
uint32_t slices; // '0' if unallocated
uint32_t flags;
uint8_t name[FVM_NAME_LEN];
} vpart_entry_t;
static_assert(sizeof(vpart_entry_t) == 64, "Unexpected VPart entry size");
static_assert(FVM_BLOCK_SIZE % sizeof(vpart_entry_t) == 0,
"VPart entries might cross block");
static_assert(sizeof(vpart_entry_t) * FVM_MAX_ENTRIES % FVM_BLOCK_SIZE == 0,
"VPart entries don't cleanly fit within block");
#define VPART_BITS 16
#define VPART_MAX ((1UL << VPART_BITS) - 1)
#define VPART_MASK VPART_MAX
#define VSLICE_BITS 32
#define VSLICE_MAX ((1UL << VSLICE_BITS) - 1)
#define VSLICE_MASK ((VSLICE_MAX) << VPART_BITS)
#define RESERVED_BITS 16
#define PSLICE_UNALLOCATED 0
// A Slice Entry represents the allocation of a slice.
//
// Slice Entries are laid out in an array on disk. The index into this array
// determines the "physical slice" being accessed, where physical slices consist
// of all disk space immediately following the FVM metadata on an FVM partition.
//
// The "Vpart" field describes which virtual partition allocated the slice.
// If this field is set to FVM_SLICE_ENTRY_FREE, the slice is not allocated.
//
// If the slice is allocated, the "Vslice" field describes which virtual slice
// within the virtual partition is using this slice.
typedef struct slice_entry {
uint64_t data;
// Vpart is set to 'FVM_SLICE_ENTRY_FREE' if unallocated.
uint64_t Vpart() const;
void SetVpart(uint64_t vpart);
// Vslice is only valid if Vpart is not set to 'FVM_SLICE_ENTRY_FREE'.
uint64_t Vslice() const;
void SetVslice(uint64_t vslice);
} slice_entry_t;
static_assert(FVM_MAX_ENTRIES <= VPART_MAX, "vpart address space too small");
static_assert(sizeof(slice_entry_t) == 8, "Unexpected FVM slice entry size");
static_assert(FVM_BLOCK_SIZE % sizeof(slice_entry_t) == 0,
"FVM slice entry might cross block");
constexpr size_t kVPartTableOffset = FVM_BLOCK_SIZE;
constexpr size_t kVPartTableLength = (sizeof(vpart_entry_t) * FVM_MAX_ENTRIES);
constexpr size_t kAllocTableOffset = kVPartTableOffset + kVPartTableLength;
constexpr size_t AllocTableLength(size_t total_size, size_t slice_size) {
return fbl::round_up(sizeof(slice_entry_t) * (total_size / slice_size),
FVM_BLOCK_SIZE);
}
constexpr size_t MetadataSize(size_t total_size, size_t slice_size) {
return kAllocTableOffset + AllocTableLength(total_size, slice_size);
}
constexpr size_t BackupStart(size_t total_size, size_t slice_size) {
return MetadataSize(total_size, slice_size);
}
constexpr size_t SlicesStart(size_t total_size, size_t slice_size) {
return 2 * MetadataSize(total_size, slice_size);
}
constexpr size_t UsableSlicesCount(size_t total_size, size_t slice_size) {
return (total_size - SlicesStart(total_size, slice_size)) / slice_size;
}
constexpr size_t SliceStart(size_t total_size, size_t slice_size, size_t pslice) {
return SlicesStart(total_size, slice_size) + (pslice - 1) * slice_size;
}
constexpr size_t BlocksToSlices(size_t slice_size, size_t block_size, size_t block_count) {
if (slice_size == 0 || slice_size < block_size) {
return 0;
}
const size_t kBlocksPerSlice = slice_size / block_size;
return (block_count + kBlocksPerSlice - 1) / kBlocksPerSlice;
}
constexpr size_t SlicesToBlocks(size_t slice_size, size_t block_size, size_t slice_count) {
return slice_count * slice_size / block_size;
}
} // namespace fvm
#endif // __cplusplus
__BEGIN_CDECLS
// Update's the metadata's hash field to accurately reflect
// the contents of metadata.
void fvm_update_hash(void* metadata, size_t metadata_size);
// Validate the FVM header information, and identify which
// copy of metadata (primary or backup) should be used for
// initial reading, if either.
//
// "out" is an optional output parameter which is equal to a
// valid copy of either metadata or backup on success.
zx_status_t fvm_validate_header(const void* metadata, const void* backup,
size_t metadata_size, const void** out);
__END_CDECLS