blob: f650cb51f28f458c6f8c6dae98e334c7f9587912 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <common.h>
#include <command.h>
#include <environment.h>
#include <malloc.h>
#include <asm/byteorder.h>
#include <config.h>
#include <asm/arch/io.h>
#include <emmc_partitions.h>
#include <partition_table.h>
#include <version.h>
#include <amlogic/storage.h>
#ifdef CONFIG_BOOTLOADER_CONTROL_BLOCK
/* Magic signature for LpMetadataGeometry. */
#define LP_METADATA_GEOMETRY_MAGIC 0x616c4467
/* Space reserved for geometry information. */
#define LP_METADATA_GEOMETRY_SIZE 4096
#define LP_METADATA_HEADER_SIZE 124
#define SUPERBUF_SIZE 16384
/* Magic signature for LpMetadataHeader. */
#define LP_METADATA_HEADER_MAGIC 0x414C5030
/* Current metadata version. */
#define LP_METADATA_MAJOR_VERSION 10
#define LP_METADATA_MINOR_VERSION_MIN 0
#define LP_METADATA_MINOR_VERSION_MAX 2
/* Attributes for the LpMetadataPartition::attributes field.
*
* READONLY - The partition should not be considered writable. When used with
* device mapper, the block device will be created as read-only.
*/
#define LP_PARTITION_ATTR_NONE 0x0
#define LP_PARTITION_ATTR_READONLY (1 << 0)
/* This flag is only intended to be used with super_empty.img and super.img on
* retrofit devices. On these devices there are A and B super partitions, and
* we don't know ahead of time which slot the image will be applied to.
*
* If set, the partition name needs a slot suffix applied. The slot suffix is
* determined by the metadata slot number (0 = _a, 1 = _b).
*/
#define LP_PARTITION_ATTR_SLOT_SUFFIXED (1 << 1)
/* Mask that defines all valid attributes. */
#define LP_PARTITION_ATTRIBUTE_MASK (LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_SLOT_SUFFIXED)
/* Default name of the physical partition that holds logical partition entries.
* The layout of this partition will look like:
*
* +--------------------+
* | Disk Geometry |
* +--------------------+
* | Geometry Backup |
* +--------------------+
* | Metadata |
* +--------------------+
* | Backup Metadata |
* +--------------------+
* | Logical Partitions |
* +--------------------+
*/
#define LP_METADATA_DEFAULT_PARTITION_NAME "super"
/* Size of a sector is always 512 bytes for compatibility with the Linux kernel. */
#define LP_SECTOR_SIZE 512
/* Amount of space reserved at the start of every super partition to avoid
* creating an accidental boot sector.
*/
#define LP_PARTITION_RESERVED_BYTES 4096
/* This structure is stored at block 0 in the first 4096 bytes of the
* partition, and again in the following block. It is never modified and
* describes how logical partition information can be located.
*/
typedef struct LpMetadataGeometry {
/* 0: Magic signature (LP_METADATA_GEOMETRY_MAGIC). */
uint32_t magic;
/* 4: Size of the LpMetadataGeometry struct. */
uint32_t struct_size;
/* 8: SHA256 checksum of this struct, with this field set to 0. */
uint8_t checksum[32];
/* 40: Maximum amount of space a single copy of the metadata can use. This
* must be a multiple of LP_SECTOR_SIZE.
*/
uint32_t metadata_max_size;
/* 44: Number of copies of the metadata to keep. For A/B devices, this
* will be 2. For an A/B/C device, it would be 3, et cetera. For Non-A/B
* it will be 1. A backup copy of each slot is kept, so if this is "2",
* there will be four copies total.
*/
uint32_t metadata_slot_count;
/* 48: Logical block size. This is the minimal alignment for partition and
* extent sizes, and it must be a multiple of LP_SECTOR_SIZE. Note that
* this must be equal across all LUNs that comprise the super partition,
* and thus this field is stored in the geometry, not per-device.
*/
uint32_t logical_block_size;
} __attribute__((packed)) LpMetadataGeometry;
/* The logical partition metadata has a number of tables; they are described
* in the header via the following structure.
*
* The size of the table can be computed by multiplying entry_size by
* num_entries, and the result must not overflow a 32-bit signed integer.
*/
typedef struct LpMetadataTableDescriptor {
/* 0: Location of the table, relative to end of the metadata header. */
uint32_t offset;
/* 4: Number of entries in the table. */
uint32_t num_entries;
/* 8: Size of each entry in the table, in bytes. */
uint32_t entry_size;
} __attribute__((packed)) LpMetadataTableDescriptor;
/* Binary format for the header of the logical partition metadata format.
*
* The format has three sections. The header must occur first, and the
* proceeding tables may be placed in any order after.
*
* +-----------------------------------------+
* | Header data - fixed size |
* +-----------------------------------------+
* | Partition table - variable size |
* +-----------------------------------------+
* | Partition table extents - variable size |
* +-----------------------------------------+
*
* The "Header" portion is described by LpMetadataHeader. It will always
* precede the other three blocks.
*
* All fields are stored in little-endian byte order when serialized.
*
* This struct is versioned; see the |major_version| and |minor_version|
* fields.
*/
typedef struct LpMetadataHeader {
/* 0: Four bytes equal to LP_METADATA_HEADER_MAGIC. */
uint32_t magic;
/* 4: Version number required to read this metadata. If the version is not
* equal to the library version, the metadata should be considered
* incompatible.
*/
uint16_t major_version;
/* 6: Minor version. A library supporting newer features should be able to
* read metadata with an older minor version. However, an older library
* should not support reading metadata if its minor version is higher.
*/
uint16_t minor_version;
/* 8: The size of this header struct. */
uint32_t header_size;
/* 12: SHA256 checksum of the header, up to |header_size| bytes, computed as
* if this field were set to 0.
*/
uint8_t header_checksum[32];
/* 44: The total size of all tables. This size is contiguous; tables may not
* have gaps in between, and they immediately follow the header.
*/
uint32_t tables_size;
/* 48: SHA256 checksum of all table contents. */
uint8_t tables_checksum[32];
/* 80: Partition table descriptor. */
LpMetadataTableDescriptor partitions;
/* 92: Extent table descriptor. */
LpMetadataTableDescriptor extents;
/* 104: Updateable group descriptor. */
LpMetadataTableDescriptor groups;
/* 116: Block device table. */
LpMetadataTableDescriptor block_devices;
} __attribute__((packed)) LpMetadataHeader;
/* This struct defines a logical partition entry, similar to what would be
* present in a GUID Partition Table.
*/
typedef struct LpMetadataPartition {
/* 0: Name of this partition in ASCII characters. Any unused characters in
* the buffer must be set to 0. Characters may only be alphanumeric or _.
* The name must include at least one ASCII character, and it must be unique
* across all partition names. The length (36) is the same as the maximum
* length of a GPT partition name.
*/
char name[36];
/* 36: Attributes for the partition (see LP_PARTITION_ATTR_* flags above). */
uint32_t attributes;
/* 40: Index of the first extent owned by this partition. The extent will
* start at logical sector 0. Gaps between extents are not allowed.
*/
uint32_t first_extent_index;
/* 44: Number of extents in the partition. Every partition must have at
* least one extent.
*/
uint32_t num_extents;
/* 48: Group this partition belongs to. */
uint32_t group_index;
} __attribute__((packed)) LpMetadataPartition;
/* This extent is a dm-linear target, and the index is an index into the
* LinearExtent table.
*/
#define LP_TARGET_TYPE_LINEAR 0
/* This extent is a dm-zero target. The index is ignored and must be 0. */
#define LP_TARGET_TYPE_ZERO 1
/* This struct defines an extent entry in the extent table block. */
typedef struct LpMetadataExtent {
/* 0: Length of this extent, in 512-byte sectors. */
uint64_t num_sectors;
/* 8: Target type for device-mapper (see LP_TARGET_TYPE_* values). */
uint32_t target_type;
/* 12: Contents depends on target_type.
*
* LINEAR: The sector on the physical partition that this extent maps onto.
* ZERO: This field must be 0.
*/
uint64_t target_data;
/* 20: Contents depends on target_type.
*
* LINEAR: Must be an index into the block devices table.
* ZERO: This field must be 0.
*/
uint32_t target_source;
} __attribute__((packed)) LpMetadataExtent;
/* This struct defines an entry in the groups table. Each group has a maximum
* size, and partitions in a group must not exceed that size. There is always
* a "default" group of unlimited size, which is used when not using update
* groups or when using overlayfs or fastbootd.
*/
typedef struct LpMetadataPartitionGroup {
/* 0: Name of this group. Any unused characters must be 0. */
char name[36];
/* 36: Flags (see LP_GROUP_*). */
uint32_t flags;
/* 40: Maximum size in bytes. If 0, the group has no maximum size. */
uint64_t maximum_size;
} __attribute__((packed)) LpMetadataPartitionGroup;
/* This flag is only intended to be used with super_empty.img and super.img on
* retrofit devices. If set, the group needs a slot suffix to be interpreted
* correctly. The suffix is automatically applied by ReadMetadata().
*/
#define LP_GROUP_SLOT_SUFFIXED (1 << 0)
/* This struct defines an entry in the block_devices table. There must be at
* least one device, and the first device must represent the partition holding
* the super metadata.
*/
typedef struct LpMetadataBlockDevice {
/* 0: First usable sector for allocating logical partitions. this will be
* the first sector after the initial geometry blocks, followed by the
* space consumed by metadata_max_size*metadata_slot_count*2.
*/
uint64_t first_logical_sector;
/* 8: Alignment for defining partitions or partition extents. For example,
* an alignment of 1MiB will require that all partitions have a size evenly
* divisible by 1MiB, and that the smallest unit the partition can grow by
* is 1MiB.
*
* Alignment is normally determined at runtime when growing or adding
* partitions. If for some reason the alignment cannot be determined, then
* this predefined alignment in the geometry is used instead. By default
* it is set to 1MiB.
*/
uint32_t alignment;
/* 12: Alignment offset for "stacked" devices. For example, if the "super"
* partition itself is not aligned within the parent block device's
* partition table, then we adjust for this in deciding where to place
* |first_logical_sector|.
*
* Similar to |alignment|, this will be derived from the operating system.
* If it cannot be determined, it is assumed to be 0.
*/
uint32_t alignment_offset;
/* 16: Block device size, as specified when the metadata was created. This
* can be used to verify the geometry against a target device.
*/
uint64_t size;
/* 24: Partition name in the GPT. Any unused characters must be 0. */
char partition_name[36];
/* 60: Flags (see LP_BLOCK_DEVICE_* flags below). */
uint32_t flags;
} __attribute__((packed)) LpMetadataBlockDevice;
/* This flag is only intended to be used with super_empty.img and super.img on
* retrofit devices. On these devices there are A and B super partitions, and
* we don't know ahead of time which slot the image will be applied to.
*
* If set, the block device needs a slot suffix applied before being used with
* IPartitionOpener. The slot suffix is determined by the metadata slot number
* (0 = _a, 1 = _b).
*/
#define LP_BLOCK_DEVICE_SLOT_SUFFIXED (1 << 0)
typedef struct PartitionList
{
char name[128];
struct PartitionList* next;
}__attribute__((packed)) PartitionList;
PartitionList* part_list = NULL;
void printlist(void)
{
PartitionList* node = part_list;
while (NULL != node)
{
printf("name: %s\n",node->name);
node = node->next;
}
}
int GetPrimaryGeometryOffset(void) {
int offset = LP_PARTITION_RESERVED_BYTES;
return offset;
}
int GetBackupGeometryOffset(void) {
int offset = GetPrimaryGeometryOffset() + LP_METADATA_GEOMETRY_SIZE;
return offset;
}
int GetPrimaryMetadataOffset(LpMetadataGeometry* geometry, int slot_number) {
int offset = LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2) +
geometry->metadata_max_size * slot_number;
//printf("GetPrimaryMetadataOffset : %d\n", offset);
return offset;
}
int GetBackupMetadataOffset(LpMetadataGeometry* geometry, int slot_number) {
int start = LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2) +
geometry->metadata_max_size * geometry->metadata_slot_count;
//printf("GetBackupMetadataOffset : %d\n", start + geometry->metadata_max_size * slot_number);
return start + geometry->metadata_max_size * slot_number;
}
int GetTotalMetadataSize(int metadata_max_size, int max_slots) {
return LP_PARTITION_RESERVED_BYTES +
(LP_METADATA_GEOMETRY_SIZE + metadata_max_size * max_slots) * 2;
}
int ParseGeometry(const void* buffer, LpMetadataGeometry* geometry) {
memcpy(geometry, buffer, sizeof(LpMetadataGeometry));
// Check the magic signature.
if (geometry->magic != LP_METADATA_GEOMETRY_MAGIC) {
printf("Logical partition metadata has invalid geometry magic signature\n");
return -1;
}
// Reject if the struct size is larger than what we compiled. This is so we
// can compute a checksum with the |struct_size| field rather than using
// sizeof.
if (geometry->struct_size > sizeof(LpMetadataGeometry)) {
printf("Logical partition metadata has unrecognized fields.\n");
return -1;
}
// Check that the struct size is equal (this will have to change if we ever
// change the struct size in a release).
if (geometry->struct_size != sizeof(LpMetadataGeometry)) {
printf("Logical partition metadata has invalid struct size.\n");
return -1;
}
if (geometry->metadata_slot_count == 0) {
printf("Logical partition metadata has invalid slot count.\n");
return -1;
}
if (geometry->metadata_max_size % LP_SECTOR_SIZE != 0) {
printf("Metadata max size is not sector-aligned.\n");
return -1;
}
return 0;
}
int ReadPrimaryGeometry(char *superbuf, LpMetadataGeometry* geometry) {
char buffer[LP_METADATA_GEOMETRY_SIZE];
memcpy(buffer, superbuf+LP_PARTITION_RESERVED_BYTES, LP_METADATA_GEOMETRY_SIZE);
return ParseGeometry(buffer, geometry);
}
int ReadBackupGeometry(char *superbuf, LpMetadataGeometry* geometry) {
char buffer[LP_METADATA_GEOMETRY_SIZE];
memcpy(buffer, superbuf+LP_PARTITION_RESERVED_BYTES+LP_METADATA_GEOMETRY_SIZE, LP_METADATA_GEOMETRY_SIZE);
return ParseGeometry(buffer, geometry);
}
// Read and validate geometry information from a block device that holds
// logical partitions. If the information is corrupted, this will attempt
// to read it from a secondary backup location.
int ReadLogicalPartitionGeometry(char *superbuf, LpMetadataGeometry* geometry) {
if (ReadPrimaryGeometry(superbuf, geometry) == 0) {
return 0;
}
return ReadBackupGeometry(superbuf, geometry);
}
static int ValidateMetadataHeader(LpMetadataHeader* header) {
// Do basic validation of key metadata bits.
if (header->magic != LP_METADATA_HEADER_MAGIC) {
printf("Logical partition metadata has invalid magic value.\n");
return -1;
}
// Check that the version is compatible.
if (header->major_version != LP_METADATA_MAJOR_VERSION ||
header->minor_version > LP_METADATA_MINOR_VERSION_MAX) {
printf("Logical partition metadata has incompatible version.\n");
return -1;
}
/*if (!ValidateTableBounds(header, &header->partitions) ||
!ValidateTableBounds(header, &header->extents) ||
!ValidateTableBounds(header, &header->groups) ||
!ValidateTableBounds(header, &header->block_devices)) {
printf("Logical partition metadata has invalid table bounds.\n");
return -1;
}*/
// Check that table entry sizes can accomodate their respective structs. If
// table sizes change, these checks will have to be adjusted.
if (header->partitions.entry_size != sizeof(LpMetadataPartition)) {
printf("Logical partition metadata has invalid partition table entry size.\n");
return -1;
}
if (header->extents.entry_size != sizeof(LpMetadataExtent)) {
printf("Logical partition metadata has invalid extent table entry size.\n");
return -1;
}
if (header->groups.entry_size != sizeof(LpMetadataPartitionGroup)) {
printf("Logical partition metadata has invalid group table entry size.\n");
return -1;
}
return 0;
}
int ReadMetadataHeader(char *superbuf, LpMetadataHeader* header,
LpMetadataGeometry* geometry, int slot_number) {
char* buffer = NULL;
int cursor = 0;
PartitionList* tail = NULL ;
PartitionList* node = NULL ;
int ishead = 0;
int i;
int index = 0;
char* flag;
//printf("metaoffset: %d\n", GetPrimaryMetadataOffset(geometry, slot_number));
memcpy(header, superbuf + GetPrimaryMetadataOffset(geometry, slot_number), sizeof(LpMetadataHeader));
if (ValidateMetadataHeader(header) != 0) {
return -1;
}
//printf("header table size = %d\n", header->tables_size);
buffer = (char*)malloc(header->tables_size);
if (buffer == NULL) {
printf("Out of memory reading logical partition tables.\n");
return -1;
}
index = GetPrimaryMetadataOffset(geometry, slot_number) + sizeof(LpMetadataHeader);
#ifdef CONFIG_CMD_BOOTCTOL_VAB
flag = CONFIG_CMD_BOOTCTOL_VAB;
strcpy(flag, CONFIG_CMD_BOOTCTOL_VAB);
//printf("CONFIG_CMD_BOOTCTOL_VAB: %s \n", CONFIG_CMD_BOOTCTOL_VAB);
if ((strcmp(flag, "1") == 0) && (has_boot_slot == 1)) {
index = index + 128;
}
#endif
memcpy(buffer, superbuf + index, header->tables_size);
cursor = index + header->partitions.offset;
//printf("index: %d\n", index);
//printf("cursor: %d\n", cursor);
// ValidateTableSize ensured that |cursor| is valid for the number of
// entries in the table.
for (i = 0; i < header->partitions.num_entries; i++) {
LpMetadataPartition partition;
memcpy(&partition, superbuf + cursor, sizeof(partition));
cursor += header->partitions.entry_size;
//printf("partition name : %s\n", partition.name);
if (partition.attributes & ~LP_PARTITION_ATTRIBUTE_MASK) {
printf("Logical partition has invalid attribute set.\n");
if (buffer)
free (buffer);
return -1;
}
if (partition.first_extent_index + partition.num_extents < partition.first_extent_index) {
printf("Logical partition first_extent_index + num_extents overflowed.\n");
if (buffer)
free (buffer);
return -1;
}
if (partition.first_extent_index + partition.num_extents > header->extents.num_entries) {
printf("Logical partition has invalid extent list.\n");
if (buffer)
free (buffer);
return -1;
}
if (partition.group_index >= header->groups.num_entries) {
printf("Logical partition has invalid group index.\n");
if (buffer)
free (buffer);
return -1;
}
node = malloc(sizeof(PartitionList));
strcpy(node->name, partition.name);
if (ishead == 0)
{
part_list = node ;
part_list->next = NULL ;
tail = node;
ishead = -1;
}
else
{
tail->next = node;
tail = node;
}
//metadata->partitions.push_back(partition);
}
if (NULL != tail)
tail->next = NULL;
if (buffer)
free (buffer);
return 0;
}
/*void dump_mem(char * buffer, int count)
{
int i;
printf("***********************************************\n");
for (i=0; i<count ; i++)
{
if (i % 16 == 0)
printf("\n");
printf("%02x ", buffer[i]);
}
printf("\n");
printf("***********************************************\n");
}*/
int do_ReadMetadata(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
char *partition = "super";
char* superbuf;
LpMetadataGeometry geometry;
LpMetadataHeader metadata_header;
char *slot;
int slot_number = 0;
superbuf = (char*)malloc(SUPERBUF_SIZE);
if (superbuf == NULL) {
printf("Out of memory reading logical partition tables.\n");
goto ERR;
}
if (dynamic_partition) {
if (store_read((const char *)partition,
0, SUPERBUF_SIZE, (unsigned char *)superbuf) < 0) {
printf("failed to store read %s.\n", partition);
goto ERR;
}
//dump_mem(superbuf, SUPERBUF_SIZE);
if (ReadLogicalPartitionGeometry(superbuf, &geometry) != 0) {
goto ERR;
}
if (has_boot_slot == 1) {
slot = env_get("slot-suffixes");
//printf("slot-suffixes: %s\n", slot);
if (strcmp(slot, "0") == 0) {
slot_number = 0;
} else if (strcmp(slot, "1") == 0) {
slot_number = 1;
}
}
ReadMetadataHeader(superbuf, &metadata_header, &geometry, slot_number);
//printlist();
}
if (superbuf)
free (superbuf);
return 0;
ERR:
if (superbuf)
free (superbuf);
return -1;
}
int is_partition_logical(char* parition_name) {
run_command("readMetadata", 0);
PartitionList* node = part_list;
while (NULL != node)
{
//printf("name: %s\n",node->name);
if (strcmp(node->name, parition_name) == 0)
return 0;
node = node->next;
}
return -1;
}
#else
static int do_ReadMetadata(
cmd_tbl_t * cmdtp,
int flag,
int argc,
char * const argv[]) {
// Do-Nothing!
return 0;
}
#endif /* CONFIG_BOOTLOADER_CONTROL_BLOCK */
U_BOOT_CMD(
readMetadata, 1, 0, do_ReadMetadata,
"readMetadata",
"\nThis command will read metadata in super \n"
"So you can execute command: readMetadata"
);