// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2019 Amlogic, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #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 /* Metadata version needed to use the UPDATED partition attribute. */ #define LP_METADATA_VERSION_FOR_UPDATED_ATTR 1 /* Metadata version needed for the new expanded header struct. */ #define LP_METADATA_VERSION_FOR_EXPANDED_HEADER 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) /* This flag is applied automatically when using MetadataBuilder::NewForUpdate. * It signals that the partition was created (or modified) for a snapshot-based * update. If this flag is not present, the partition was likely flashed via * fastboot. */ #define LP_PARTITION_ATTR_UPDATED (1 << 2) /* This flag marks a partition as disabled. It should not be used or mapped. */ #define LP_PARTITION_ATTR_DISABLED (1 << 3) /* Mask that defines all valid attributes. When changing this, make sure to * update ParseMetadata(). */ #define LP_PARTITION_ATTRIBUTE_MASK_V0 \ (LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_SLOT_SUFFIXED) #define LP_PARTITION_ATTRIBUTE_MASK_V1 (LP_PARTITION_ATTR_UPDATED | LP_PARTITION_ATTR_DISABLED) #define LP_PARTITION_ATTRIBUTE_MASK \ (LP_PARTITION_ATTRIBUTE_MASK_V0 | LP_PARTITION_ATTRIBUTE_MASK_V1) /* 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). */ u32 magic; /* 4: Size of the LpMetadataGeometry struct. */ u32 struct_size; /* 8: SHA256 checksum of this struct, with this field set to 0. */ u8 checksum[32]; /* 40: Maximum amount of space a single copy of the metadata can use. This * must be a multiple of LP_SECTOR_SIZE. */ u32 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. */ u32 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. */ u32 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. */ u32 offset; /* 4: Number of entries in the table. */ u32 num_entries; /* 8: Size of each entry in the table, in bytes. */ u32 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. */ u32 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. */ u16 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. */ u16 minor_version; /* 8: The size of this header struct. */ u32 header_size; /* 12: SHA256 checksum of the header, up to |header_size| bytes, computed as * if this field were set to 0. */ u8 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. */ u32 tables_size; /* 48: SHA256 checksum of all table contents. */ u8 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; /* Everything past here is header version 1.2+, and is only included if * needed. When liblp supporting >= 1.2 reads a < 1.2 header, it must * zero these additional fields. */ /* 128: See LP_HEADER_FLAG_ constants for possible values. Header flags are * independent of the version number and intended to be informational only. * New flags can be added without bumping the version. */ u32 flags; /* 132: Reserved (zero), pad to 256 bytes. */ u8 reserved[124]; } __attribute__((packed)) LpMetadataHeader; /* This device uses Virtual A/B. Note that on retrofit devices, the expanded * header may not be present. */ #define LP_HEADER_FLAG_VIRTUAL_AB_DEVICE 0x1 /* 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). */ u32 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. */ u32 first_extent_index; /* 44: Number of extents in the partition. Every partition must have at * least one extent. */ u32 num_extents; /* 48: Group this partition belongs to. */ u32 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. */ u64 num_sectors; /* 8: Target type for device-mapper (see LP_TARGET_TYPE_* values). */ u32 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. */ u64 target_data; /* 20: Contents depends on target_type. * * LINEAR: Must be an index into the block devices table. * ZERO: This field must be 0. */ u32 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_*). */ u32 flags; /* 40: Maximum size in bytes. If 0, the group has no maximum size. */ u64 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. */ u64 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. */ u32 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. */ u32 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. */ u64 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). */ u32 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) /* For ease of writing compatibility checks, the original metadata header is * preserved below, and typedefs are provided for the current version. */ typedef struct LpMetadataHeaderV1_0 { u32 magic; u16 major_version; u16 minor_version; u32 header_size; u8 header_checksum[32]; u32 tables_size; u8 tables_checksum[32]; LpMetadataTableDescriptor partitions; LpMetadataTableDescriptor extents; LpMetadataTableDescriptor groups; LpMetadataTableDescriptor block_devices; } __attribute__((packed)) LpMetadataHeaderV1_0; typedef LpMetadataHeader LpMetadataHeaderV1_2; typedef struct PartitionList { char name[128]; struct PartitionList *next; }__attribute__((packed)) PartitionList; PartitionList* part_list = NULL; void printlist(void) { PartitionList *node = part_list; while (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; 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; } // Check that table entry sizes can match 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; //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) { printf("Out of memory reading logical partition tables.\n"); return -1; } if (header->minor_version == 0) index = GetPrimaryMetadataOffset(geometry, slot_number) + sizeof(LpMetadataHeaderV1_0); else index = GetPrimaryMetadataOffset(geometry, slot_number) + sizeof(LpMetadataHeader); memcpy(buffer, superbuf + index, header->tables_size); cursor = index + header->partitions.offset; //printf("index: %d\n", index); //printf("cursor: %d\n", cursor); u32 valid_attributes = LP_PARTITION_ATTRIBUTE_MASK_V0; if (header->minor_version >= LP_METADATA_VERSION_FOR_UPDATED_ATTR) valid_attributes |= LP_PARTITION_ATTRIBUTE_MASK_V1; // 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 & ~valid_attributes) { 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)); strlcpy(node->name, partition.name, min(sizeof(PartitionList), sizeof(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 (tail) tail->next = NULL; if (buffer) free(buffer); return 0; } 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) { 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 *partition_name) { run_command("readMetadata", 0); PartitionList *node = part_list; while (node) { //printf("name: %s\n",node->name); if (strcmp(node->name, partition_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" );