From patchwork Fri Mar 17 05:13:06 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cyril Bur X-Patchwork-Id: 740168 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3vkv5h2zm8z9ryZ for ; Fri, 17 Mar 2017 16:28:56 +1100 (AEDT) Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 3vkv5h25XHzDrCC for ; Fri, 17 Mar 2017 16:28:56 +1100 (AEDT) X-Original-To: skiboot@lists.ozlabs.org Delivered-To: skiboot@lists.ozlabs.org Received: from mx0a-001b2d01.pphosted.com (mx0a-001b2d01.pphosted.com [148.163.156.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 3vktqY3J3ZzDqlM for ; Fri, 17 Mar 2017 16:16:41 +1100 (AEDT) Received: from pps.filterd (m0098410.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.20/8.16.0.20) with SMTP id v2H5Dqie124096 for ; Fri, 17 Mar 2017 01:16:35 -0400 Received: from e23smtp04.au.ibm.com (e23smtp04.au.ibm.com [202.81.31.146]) by mx0a-001b2d01.pphosted.com with ESMTP id 297xku8nxs-1 (version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT) for ; Fri, 17 Mar 2017 01:16:35 -0400 Received: from localhost by e23smtp04.au.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Fri, 17 Mar 2017 15:16:32 +1000 Received: from d23relay07.au.ibm.com (202.81.31.226) by e23smtp04.au.ibm.com (202.81.31.210) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Fri, 17 Mar 2017 15:16:30 +1000 Received: from d23av03.au.ibm.com (d23av03.au.ibm.com [9.190.234.97]) by d23relay07.au.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id v2H5GLX018546800 for ; Fri, 17 Mar 2017 16:16:29 +1100 Received: from d23av03.au.ibm.com (localhost [127.0.0.1]) by d23av03.au.ibm.com (8.14.4/8.14.4/NCO v10.0 AVout) with ESMTP id v2H5FteU010743 for ; Fri, 17 Mar 2017 16:15:55 +1100 Received: from ozlabs.au.ibm.com (ozlabs.au.ibm.com [9.192.253.14]) by d23av03.au.ibm.com (8.14.4/8.14.4/NCO v10.0 AVin) with ESMTP id v2H5FtQg010167; Fri, 17 Mar 2017 16:15:55 +1100 Received: from camb691.ozlabs.ibm.com (haven.au.ibm.com [9.192.254.114]) (using TLSv1.2 with cipher DHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ozlabs.au.ibm.com (Postfix) with ESMTPSA id 0058AA0202; Fri, 17 Mar 2017 16:15:33 +1100 (AEDT) From: Cyril Bur To: skiboot@lists.ozlabs.org Date: Fri, 17 Mar 2017 16:13:06 +1100 X-Mailer: git-send-email 2.12.0 In-Reply-To: <20170317051309.16899-1-cyril.bur@au1.ibm.com> References: <20170317051309.16899-1-cyril.bur@au1.ibm.com> X-TM-AS-MML: disable x-cbid: 17031705-0012-0000-0000-0000021F80F3 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 17031705-0013-0000-0000-000007327D19 Message-Id: <20170317051309.16899-3-cyril.bur@au1.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-03-17_04:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 suspectscore=3 malwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1702020001 definitions=main-1703170044 Subject: [Skiboot] [PATCH 2/5] libffs: Understand how to create FFS partition TOCs and entries. X-BeenThere: skiboot@lists.ozlabs.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Mailing list for skiboot development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: skiboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Skiboot" Signed-off-by: Cyril Bur --- libflash/ffs.h | 26 ++++ libflash/libffs.c | 363 +++++++++++++++++++++++++++++++++++++++++++++++++++--- libflash/libffs.h | 18 +++ 3 files changed, 393 insertions(+), 14 deletions(-) diff --git a/libflash/ffs.h b/libflash/ffs.h index 64e5e6a1..6ba77136 100644 --- a/libflash/ffs.h +++ b/libflash/ffs.h @@ -54,6 +54,29 @@ enum ffs_type { FFS_TYPE_PARTITION = 3, }; +/* + * Flag bit definitions + */ +#define FFS_FLAGS_PROTECTED 0x0001 +#define FFS_FLAGS_U_BOOT_ENV 0x0002 + +/* Data integrity flags */ +#define FFS_ENRY_INTEG_ECC 0x8000 + +/* + * User verCheck definitions + */ +#define FFS_VERCHECK_SHA512V 0x80 +#define FFS_VERCHECK_SHA512EC 0x40 + +/* + * User miscFlags + */ +#define FFS_MISCFLAGS_PRESERVED 0x80 +#define FFS_MISCFLAGS_READONLY 0x40 +#define FFS_MISCFLAGS_BACKUP 0x20 +#define FFS_MISCFLAGS_REPROVISION 0x10 + /** * struct __ffs_entry_user - On flash user data entries * @@ -186,9 +209,12 @@ struct __ffs_hdr { */ struct ffs_hdr { uint32_t version; + uint32_t base; uint32_t size; uint32_t block_size; uint32_t block_count; + struct ffs_entry *backup; + struct ffs_hdr *side; struct list_head entries; }; diff --git a/libflash/libffs.c b/libflash/libffs.c index d2d77638..9ce72248 100644 --- a/libflash/libffs.c +++ b/libflash/libffs.c @@ -66,7 +66,12 @@ static uint32_t ffs_entry_checksum(struct __ffs_entry *ent) return ffs_checksum(ent, sizeof(struct __ffs_entry)); } -__unused static int ffs_num_entries(struct ffs_hdr *hdr) +static size_t ffs_hdr_raw_size(int num_entries) +{ + return sizeof(struct __ffs_hdr) + num_entries * sizeof(struct __ffs_entry); +} + +static int ffs_num_entries(struct ffs_hdr *hdr) { struct ffs_entry *ent; int num_entries = 0; @@ -77,11 +82,6 @@ __unused static int ffs_num_entries(struct ffs_hdr *hdr) return num_entries; } -__unused static size_t ffs_hdr_raw_size(int num_entries) -{ - return sizeof(struct __ffs_hdr) + num_entries * sizeof(struct __ffs_entry); -} - static int ffs_check_convert_header(struct ffs_hdr *dst, struct __ffs_hdr *src) { if (be32_to_cpu(src->magic) != FFS_MAGIC) @@ -104,8 +104,8 @@ static int ffs_check_convert_header(struct ffs_hdr *dst, struct __ffs_hdr *src) return 0; } -static int ffs_entry_user_to_flash(struct __ffs_entry_user *dst, - struct ffs_entry_user *src) +static int ffs_entry_user_to_flash(struct ffs_hdr *hdr __unused, + struct __ffs_entry_user *dst, struct ffs_entry_user *src) { memset(dst, 0, sizeof(struct __ffs_entry_user)); dst->datainteg = cpu_to_be16(src->datainteg); @@ -115,8 +115,8 @@ static int ffs_entry_user_to_flash(struct __ffs_entry_user *dst, return 0; } -static int ffs_entry_user_to_cpu(struct ffs_entry_user *dst, - struct __ffs_entry_user *src) +static int ffs_entry_user_to_cpu(struct ffs_hdr *hdr __unused, + struct ffs_entry_user *dst, struct __ffs_entry_user *src) { memset(dst, 0, sizeof(struct ffs_entry_user)); dst->datainteg = be16_to_cpu(src->datainteg); @@ -153,7 +153,7 @@ static int ffs_entry_to_flash(struct ffs_hdr *hdr, dst->type = cpu_to_be32(src->type); /* TODO: Check that it is valid? */ dst->flags = cpu_to_be32(src->flags); dst->actual = cpu_to_be32(src->actual); - rc = ffs_entry_user_to_flash(&dst->user, &src->user); + rc = ffs_entry_user_to_flash(hdr, &dst->user, &src->user); dst->checksum = ffs_entry_checksum(dst); return rc; @@ -175,7 +175,7 @@ static int ffs_entry_to_cpu(struct ffs_hdr *hdr, dst->pid = be32_to_cpu(src->pid); dst->type = be32_to_cpu(src->type); /* TODO: Check that it is valid? */ dst->flags = be32_to_cpu(src->flags); - rc = ffs_entry_user_to_cpu(&dst->user, &src->user); + rc = ffs_entry_user_to_cpu(hdr, &dst->user, &src->user); return rc; } @@ -351,7 +351,7 @@ int ffs_lookup_part(struct ffs_handle *ffs, const char *name, struct ffs_entry *ent = NULL; list_for_each(&ffs->hdr.entries, ent, list) { - if (!strncmp(name, ent->name, sizeof(ent->name))) + if (strncmp(name, ent->name, sizeof(ent->name)) == 0) break; i++; } @@ -431,6 +431,342 @@ int ffs_next_side(struct ffs_handle *ffs, struct ffs_handle **new_ffs, return ffs_init(offset, max_size, ffs->bl, new_ffs, mark_ecc); } +static int __ffs_entry_add(struct ffs_hdr *hdr, struct ffs_entry *entry) +{ + struct ffs_entry *ent; + uint32_t smallest_base; + const char *smallest_name; + int count = 0; + assert(!list_empty(&hdr->entries)); + + if (entry->base + entry->size > hdr->block_size * hdr->block_count) + return FFS_ERR_BAD_PART_SIZE; + + smallest_base = entry->base; + smallest_name = entry->name; + /* Input validate first to a) fail early b) do it all together */ + list_for_each(&hdr->entries, ent, list) { + /* Don't allow same names to differ only by case */ + if (strncasecmp(entry->name, ent->name, FFS_PART_NAME_MAX) == 0) + return FFS_ERR_BAD_PART_NAME; + + if (entry->base >= ent->base && entry->base < ent->base + ent->size) + return FFS_ERR_BAD_PART_BASE; + + if (entry->base + entry->size > ent->base && + entry->base + entry->size < ent->base + ent->size) + return FFS_ERR_BAD_PART_SIZE; + + if (entry->actual > entry->size) + return FFS_ERR_BAD_PART_SIZE; + + if (entry->pid != FFS_PID_TOPLEVEL) + return FFS_ERR_BAD_PART_PID; + + /* Skip the first partition as it IS the partition table */ + if (ent->base < smallest_base && count > 0) { + smallest_base = ent->base; + smallest_name = ent->name; + } + count++; + } + + if (count * sizeof(struct __ffs_entry) + + sizeof(struct __ffs_hdr) > smallest_base) { + fprintf(stderr, "Adding partition '%s' would cause partition '%s' at " + "0x%08x to overlap with the header\n", entry->name, smallest_name, + smallest_base); + return FFS_ERR_BAD_PART_BASE; + } + + /* + * A header can't have zero entries. Was asserted. + */ + list_for_each(&hdr->entries, ent, list) + if (entry->base < ent->base) + break; + + if (ent == list_top(&hdr->entries, struct ffs_entry, list)) { + /* + * This should never happen because the partition entry + * should ALWAYS be here + */ + fprintf(stderr, "Warning: replacing first entry in FFS header\n"); + list_add(&hdr->entries, &entry->list); + } else if (!ent) { + list_add_tail(&hdr->entries, &entry->list); + } else { + list_add_before(&hdr->entries, &entry->list, &ent->list); + } + + return 0; +} + +int ffs_entry_add(struct ffs_hdr *hdr, struct ffs_entry *entry, unsigned int side) +{ + int rc; + + /* + * Refuse to add anything after BACKUP_PART has been added, not + * sure why this is needed anymore + */ + if (hdr->backup) + return FLASH_ERR_PARM_ERROR; + + if (side == 0) { /* Sideless... */ + rc = __ffs_entry_add(hdr, entry); + if (!rc && hdr->side) { + struct ffs_entry *other_ent; + + /* + * A rather sneaky copy is hidden here. + * It doesn't make sense for a consumer to be aware that structures + * must be duplicated. The entries list in the header could have + * been an array of pointers and no copy would have been required. + */ + other_ent = calloc(1, sizeof (struct ffs_entry)); + if (!other_ent) + /* TODO Remove the added entry from side 1 */ + return FLASH_ERR_PARM_ERROR; + memcpy(other_ent, entry, sizeof(struct ffs_entry)); + rc = __ffs_entry_add(hdr->side, other_ent); + if (rc) + /* TODO Remove the added entry from side 1 */ + free(other_ent); + } + } else if (side == 1) { + rc = __ffs_entry_add(hdr, entry); + } else if (side == 2 && hdr->side) { + rc = __ffs_entry_add(hdr->side, entry); + } else { + rc = FLASH_ERR_PARM_ERROR; + } + + return rc; +} + +/* This should be done last! */ +int ffs_hdr_create_backup(struct ffs_hdr *hdr) +{ + struct ffs_entry *ent; + struct ffs_entry *backup; + int rc = 0; + ent = list_tail(&hdr->entries, struct ffs_entry, list); + if (!ent) { + return FLASH_ERR_PARM_ERROR; + } + + rc = ffs_entry_new("BACKUP_PART", + hdr->base + (hdr->block_size * (hdr->block_count - 1 )) - hdr->size, + hdr->size, &backup); + if (rc) + return rc; + + rc = __ffs_entry_add(hdr, backup); + if (rc) { + free(backup); + return rc; + } + + hdr->backup = backup; + + /* Do we try to roll back completely if that fails or leave what we've added? */ + if (hdr->side && hdr->base == 0) + rc = ffs_hdr_create_backup(hdr->side); + + return rc; +} + +int ffs_hdr_add_side(struct ffs_hdr *hdr) +{ + int rc; + + /* Only a second side for now */ + if (hdr->side) + return FLASH_ERR_PARM_ERROR; + + rc = ffs_hdr_new(hdr->size, hdr->block_size, hdr->block_count, &hdr->side); + if (rc) + return rc; + + hdr->side->base = hdr->block_size * hdr->block_count; + /* Sigh */ + hdr->side->side = hdr; + + return rc; +} + +int ffs_hdr_finalise(struct blocklevel_device *bl, struct ffs_hdr *hdr) +{ + int num_entries, i, rc = 0; + struct ffs_entry *ent; + struct __ffs_hdr *real_hdr; + + num_entries = ffs_num_entries(hdr); + + /* A TOC shouldn't have zero partitions */ + if (num_entries == 0) + return FFS_ERR_BAD_SIZE; + + if (hdr->side) { + struct ffs_entry *other_side; + /* TODO: Change the hard coded 0x8000 */ + rc = ffs_entry_new("OTHER_SIDE", hdr->side->base, 0x8000, &other_side); + if (rc) + return rc; + list_add_tail(&hdr->entries, &other_side->list); + num_entries++; + } + + real_hdr = malloc(ffs_hdr_raw_size(num_entries)); + if (!real_hdr) + return FLASH_ERR_MALLOC_FAILED; + + real_hdr->magic = cpu_to_be32(FFS_MAGIC); + real_hdr->version = cpu_to_be32(hdr->version); + real_hdr->size = cpu_to_be32(hdr->size / hdr->block_size); + real_hdr->entry_size = cpu_to_be32(sizeof(struct __ffs_entry)); + real_hdr->entry_count = cpu_to_be32(num_entries); + real_hdr->block_size = cpu_to_be32(hdr->block_size); + real_hdr->block_count = cpu_to_be32(hdr->block_count); + real_hdr->checksum = ffs_hdr_checksum(real_hdr); + + i = 0; + list_for_each(&hdr->entries, ent, list) { + rc = ffs_entry_to_flash(hdr, real_hdr->entries + i, ent); + if (rc) { + fprintf(stderr, "Couldn't format all entries for new TOC\n"); + goto out; + } + i++; + } + + /* Don't really care if this fails */ + blocklevel_erase(bl, hdr->base, hdr->size); + rc = blocklevel_write(bl, hdr->base, real_hdr, + ffs_hdr_raw_size(num_entries)); + if (rc) + goto out; + + if (hdr->backup) { + fprintf(stderr, "Actually writing backup part @ 0x%08x\n", hdr->backup->base); + blocklevel_erase(bl, hdr->backup->base, hdr->size); + rc = blocklevel_write(bl, hdr->backup->base, real_hdr, + ffs_hdr_raw_size(num_entries)); + } + if (rc) + goto out; + + if (hdr->side && hdr->base == 0) + rc = ffs_hdr_finalise(bl, hdr->side); +out: + free(real_hdr); + return rc; +} + +int ffs_entry_user_set(struct ffs_entry *ent, struct ffs_entry_user *user) +{ + if (!ent || !user) + return -1; + + /* + * Don't allow the user to specify anything we dont't know about. + * Rationale: This is the library providing access to the FFS structures. + * If the consumer of the library knows more about FFS structures then + * questions need to be asked. + * The other possibility is that they've unknowningly supplied invalid + * flags, we should tell them. + */ + if (user->chip) + return -1; + if (user->compresstype) + return -1; + if (user->datainteg & ~(FFS_ENRY_INTEG_ECC)) + return -1; + if (user->vercheck & ~(FFS_VERCHECK_SHA512V | FFS_VERCHECK_SHA512EC)) + return -1; + if (user->miscflags & ~(FFS_MISCFLAGS_PRESERVED | FFS_MISCFLAGS_BACKUP | + FFS_MISCFLAGS_READONLY | FFS_MISCFLAGS_REPROVISION)) + return -1; + + memcpy(&ent->user, user, sizeof(*user)); + return 0; +} + +int ffs_entry_new(const char *name, uint32_t base, uint32_t size, struct ffs_entry **r) +{ + struct ffs_entry *ret; + + ret = calloc(1, sizeof(*ret)); + if (!ret) + return FLASH_ERR_MALLOC_FAILED; + + strncpy(ret->name, name, FFS_PART_NAME_MAX); + ret->name[FFS_PART_NAME_MAX] = '\0'; + ret->base = base; + ret->size = size; + ret->actual = size; + ret->pid = FFS_PID_TOPLEVEL; + ret->type = FFS_TYPE_DATA; + + *r = ret; + return 0; +} + +int ffs_hdr_new(uint32_t size, uint32_t block_size, uint32_t block_count, struct ffs_hdr **r) +{ + struct ffs_hdr *ret; + struct ffs_entry *part_table; + int rc; + + if (size % block_size || size > block_size * block_count) + return FFS_ERR_BAD_SIZE; + + ret = calloc(1, sizeof(*ret)); + if (!ret) + return FLASH_ERR_MALLOC_FAILED; + + ret->version = FFS_VERSION_1; + ret->size = size; + ret->block_size = block_size; + ret->block_count = block_count; + list_head_init(&ret->entries); + + rc = ffs_entry_new("part", 0, size, &part_table); + if (rc) { + free(ret); + return rc; + } + + part_table->pid = FFS_PID_TOPLEVEL; + part_table->type = FFS_TYPE_PARTITION; + part_table->flags = FFS_FLAGS_PROTECTED; + + list_add(&ret->entries, &part_table->list); + + *r = ret; + + return 0; +} + +int ffs_hdr_free(struct ffs_hdr *hdr) +{ + struct ffs_entry *ent, *next; + + printf("Freeing hdr\n"); + list_for_each_safe(&hdr->entries, ent, next, list) { + list_del(&ent->list); + free(ent); + } + if (hdr->side) { + hdr->side->side = NULL; + ffs_hdr_free(hdr->side); + } + free(hdr); + + return 0; +} + int ffs_update_act_size(struct ffs_handle *ffs, uint32_t part_idx, uint32_t act_size) { @@ -461,4 +797,3 @@ int ffs_update_act_size(struct ffs_handle *ffs, uint32_t part_idx, return blocklevel_smart_write(ffs->bl, offset, &raw_ent, sizeof(struct __ffs_entry)); } - diff --git a/libflash/libffs.h b/libflash/libffs.h index 09769618..2c1fbd83 100644 --- a/libflash/libffs.h +++ b/libflash/libffs.h @@ -21,7 +21,9 @@ /* FFS handle, opaque */ struct ffs_handle; +struct ffs_hdr; struct ffs_entry; +struct ffs_entry_user; /** * struct ffs_entry_user - User data entries @@ -127,4 +129,20 @@ int ffs_part_info(struct ffs_handle *ffs, uint32_t part_idx, int ffs_update_act_size(struct ffs_handle *ffs, uint32_t part_idx, uint32_t act_size); +int ffs_hdr_new(uint32_t size, uint32_t block_size, uint32_t block_count, + struct ffs_hdr **r); + +int ffs_hdr_add_side(struct ffs_hdr *hdr); + +int ffs_entry_new(const char *name, uint32_t base, uint32_t size, struct ffs_entry **r); + +int ffs_entry_user_set(struct ffs_entry *ent, struct ffs_entry_user *user); + +int ffs_entry_add(struct ffs_hdr *hdr, struct ffs_entry *entry, unsigned int side); + +int ffs_hdr_create_backup(struct ffs_hdr *hdr); + +int ffs_hdr_finalise(struct blocklevel_device *bl, struct ffs_hdr *hdr); + +int ffs_hdr_free(struct ffs_hdr *hdr); #endif /* __LIBFFS_H */