From patchwork Tue Oct 25 07:15:02 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stewart Smith X-Patchwork-Id: 686353 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [103.22.144.68]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3t34Dq6yBcz9t2n for ; Tue, 25 Oct 2016 18:15:39 +1100 (AEDT) Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 3t34Dq68fxzDvlh for ; Tue, 25 Oct 2016 18:15:39 +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 3t34DP4GqZzDvhk for ; Tue, 25 Oct 2016 18:15:17 +1100 (AEDT) Received: from pps.filterd (m0098399.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.17/8.16.0.17) with SMTP id u9P7Etmn085310 for ; Tue, 25 Oct 2016 03:15:15 -0400 Received: from e38.co.us.ibm.com (e38.co.us.ibm.com [32.97.110.159]) by mx0a-001b2d01.pphosted.com with ESMTP id 269vkueqrc-1 (version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT) for ; Tue, 25 Oct 2016 03:15:15 -0400 Received: from localhost by e38.co.us.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Tue, 25 Oct 2016 01:15:14 -0600 Received: from d03dlp02.boulder.ibm.com (9.17.202.178) by e38.co.us.ibm.com (192.168.1.138) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Tue, 25 Oct 2016 01:15:11 -0600 Received: from b03cxnp07029.gho.boulder.ibm.com (b03cxnp07029.gho.boulder.ibm.com [9.17.130.16]) by d03dlp02.boulder.ibm.com (Postfix) with ESMTP id 6831C3E4004F for ; Tue, 25 Oct 2016 01:15:11 -0600 (MDT) Received: from b03ledav006.gho.boulder.ibm.com (b03ledav006.gho.boulder.ibm.com [9.17.130.237]) by b03cxnp07029.gho.boulder.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id u9P7FBvr16318950; Tue, 25 Oct 2016 00:15:11 -0700 Received: from b03ledav006.gho.boulder.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 285FEC603E; Tue, 25 Oct 2016 01:15:10 -0600 (MDT) Received: from birb.localdomain (unknown [9.83.7.67]) by b03ledav006.gho.boulder.ibm.com (Postfix) with SMTP id 33E8BC604C; Tue, 25 Oct 2016 01:15:08 -0600 (MDT) Received: from ka1.ozlabs.ibm.com (localhost.localdomain [127.0.0.1]) by birb.localdomain (Postfix) with ESMTP id 9CA1E229EA80; Tue, 25 Oct 2016 18:15:06 +1100 (AEDT) From: Stewart Smith To: skiboot@lists.ozlabs.org Date: Tue, 25 Oct 2016 18:15:02 +1100 X-Mailer: git-send-email 2.1.4 In-Reply-To: <1477379702-2376-1-git-send-email-stewart@linux.vnet.ibm.com> References: <1477379702-2376-1-git-send-email-stewart@linux.vnet.ibm.com> X-TM-AS-GCONF: 00 X-Content-Scanned: Fidelis XPS MAILER x-cbid: 16102507-0028-0000-0000-000005E14CE5 X-IBM-SpamModules-Scores: X-IBM-SpamModules-Versions: BY=3.00005974; HX=3.00000240; KW=3.00000007; PH=3.00000004; SC=3.00000188; SDB=6.00772421; UDB=6.00370753; IPR=6.00549241; BA=6.00004831; NDR=6.00000001; ZLA=6.00000005; ZF=6.00000009; ZB=6.00000000; ZP=6.00000000; ZH=6.00000000; ZU=6.00000002; MB=3.00013098; XFM=3.00000011; UTC=2016-10-25 07:15:13 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 16102507-0029-0000-0000-0000304BE1B2 Message-Id: <1477379702-2376-5-git-send-email-stewart@linux.vnet.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2016-10-25_02:, , 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-1609300000 definitions=main-1610250121 Subject: [Skiboot] [PATCH 4/4] flash: rework flash_load_resource to correctly read FFS/STB 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" This fixes the previous reverts of loading the CAPP partition with STB headers (which broke CAPP partitions without STB headers). The new logic fixes both CAPP partition loading with STB headers *and* addresses a long standing bug due to differing interpretations of FFS. The f_part utility that *constructs* PNOR files just sets actualSize=totalSize no matter on what the size of the partition is. Prior to this patch, skiboot would always load actualSize, leading to longer than needed IPL. The pflash utility updates actualSize, so no developer has really ever noticed this, apart from maybe an inkling that it's odd that a freshly baked PNOR from op-build takes ever so slightly longer to boot than one that has had individual partitions pflashed in. With this patch, we now compute actualSize. For partitions with a STB header, we take the payload size from the STB header. For partitions that don't have a STB header, we compute the size either by parsing the ELF header or by looking at the subpartition header and computing it. We now need to read the entire partition for partitions with subpartitions so that we pass consistent values to be measured as part of Trusted Boot. As of this patch, the actualSize field in FFS is *not* relied on for partition size, we determine it from the content of the partition. However, this patch *will* break loading of partitions that are not ELF and do not contain subpartitions. Luckily, nothing in-tree makes use of that. Fixes: f0a23e4fadcdc49f134e122fa134f183f2e172f7 Based-on-patch-by: Claudio Carvalho Signed-off-by: Stewart Smith --- core/flash.c | 241 +++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 152 insertions(+), 89 deletions(-) diff --git a/core/flash.c b/core/flash.c index 2299f45c814c..83cd6c840253 100644 --- a/core/flash.c +++ b/core/flash.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include struct flash { struct list_node list; @@ -419,81 +421,59 @@ static struct { }; -struct flash_hostboot_toc { - be32 ec; - be32 offset; /* From start of header. 4K aligned */ - be32 size; -}; -#define FLASH_HOSTBOOT_TOC_MAX_ENTRIES ((FLASH_SUBPART_HEADER_SIZE - 8) \ - /sizeof(struct flash_hostboot_toc)) - -struct flash_hostboot_header { - char eyecatcher[4]; - be32 version; - struct flash_hostboot_toc toc[FLASH_HOSTBOOT_TOC_MAX_ENTRIES]; -}; - -/* start and total size include ECC */ -static int flash_find_subpartition(struct blocklevel_device *bl, uint32_t subid, - uint32_t *start, uint32_t *total_size, - bool ecc) +static size_t sizeof_elf_from_hdr(void *buf) { - struct flash_hostboot_header *header; - uint32_t partsize, offset, size; - int rc; - - header = malloc(FLASH_SUBPART_HEADER_SIZE); - if (!header) - return false; - - /* Get raw partition size without ECC */ - partsize = *total_size; - if (ecc) - partsize = ecc_buffer_size_minus_ecc(*total_size); - - /* Get the TOC */ - rc = flash_read_corrected(bl, *start, header, - FLASH_SUBPART_HEADER_SIZE, ecc); - if (rc) { - prerror("FLASH: flash subpartition TOC read failed %i\n", rc); - goto end; - } - - rc = flash_subpart_info(header, partsize, partsize, NULL, subid, &offset, &size); - if (rc) - goto end; - - /* - * Adjust the start and size. The start location in the needs - * to account for ecc but the size doesn't. - */ - *start += offset; - *total_size = size; - if (ecc) { - *start += ecc_size(offset); - *total_size += ecc_size(size); + struct elf_hdr *elf = (struct elf_hdr*) buf; + size_t sz = 0; + + BUILD_ASSERT(SECURE_BOOT_HEADERS_SIZE > sizeof(struct elf_hdr)); + BUILD_ASSERT(SECURE_BOOT_HEADERS_SIZE > sizeof(struct elf64_hdr)); + BUILD_ASSERT(SECURE_BOOT_HEADERS_SIZE > sizeof(struct elf32_hdr)); + + if (elf->ei_ident == ELF_IDENT) { + if (elf->ei_class == ELF_CLASS_64) { + struct elf64_hdr *elf64 = (struct elf64_hdr*) buf; + sz = le64_to_cpu(elf64->e_shoff) + + (le16_to_cpu(elf64->e_shentsize) * + le16_to_cpu(elf64->e_shnum)); + } else if (elf->ei_class == ELF_CLASS_32) { + struct elf32_hdr *elf32 = (struct elf32_hdr*) buf; + sz = le32_to_cpu(elf32->e_shoff) + + (le16_to_cpu(elf32->e_shentsize) * + le16_to_cpu(elf32->e_shnum)); + } } -end: - free(header); - return rc; + return sz; } /* * load a resource from FLASH * buf and len shouldn't account for ECC even if partition is ECCed. + * + * The API here is a bit strange. + * If resource has a STB container, buf will contain it + * If loading subpartition with STB container, buff will *NOT* contain it + * For trusted boot, the whole partition containing the subpart is measured. + * + * Additionally, the logic to work out how much to read from flash is insane. */ static int flash_load_resource(enum resource_id id, uint32_t subid, void *buf, size_t *len) { - int i, rc, part_num, part_size, part_start, size; + int i; + int rc = OPAL_RESOURCE; struct ffs_handle *ffs; struct flash *flash; const char *name; - bool status, ecc; - - rc = OPAL_RESOURCE; - status = false; + bool status = false; + bool ecc; + bool part_signed = false; + void *bufp = buf; + size_t bufsz = *len; + int ffs_part_num, ffs_part_start, ffs_part_size; + int content_size = 0; + int offset = 0; lock(&flash_lock); @@ -539,13 +519,13 @@ static int flash_load_resource(enum resource_id id, uint32_t subid, goto out_unlock; } - rc = ffs_lookup_part(ffs, name, &part_num); + rc = ffs_lookup_part(ffs, name, &ffs_part_num); if (rc) { prerror("FLASH: No %s partition\n", name); goto out_free_ffs; } - rc = ffs_part_info(ffs, part_num, NULL, - &part_start, NULL, &part_size, &ecc); + rc = ffs_part_info(ffs, ffs_part_num, NULL, + &ffs_part_start, NULL, &ffs_part_size, &ecc); if (rc) { prerror("FLASH: Failed to get %s partition info\n", name); goto out_free_ffs; @@ -553,45 +533,120 @@ static int flash_load_resource(enum resource_id id, uint32_t subid, prlog(PR_DEBUG,"FLASH: %s partition %s ECC\n", name, ecc ? "has" : "doesn't have"); + if ((ecc ? ecc_buffer_size_minus_ecc(ffs_part_size) : ffs_part_size) < SECURE_BOOT_HEADERS_SIZE) { + prerror("FLASH: secboot headers bigger than " + "partition size 0x%x\n", ffs_part_size); + goto out_free_ffs; + } + + rc = flash_read_corrected(flash->bl, ffs_part_start, bufp, + SECURE_BOOT_HEADERS_SIZE, ecc); + if (rc) { + prerror("FLASH: failed to read the first 0x%x from " + "%s partition, rc %d\n", SECURE_BOOT_HEADERS_SIZE, name, rc); + goto out_free_ffs; + } + + part_signed = stb_is_container(bufp, SECURE_BOOT_HEADERS_SIZE); + + prlog(PR_DEBUG, "FLASH: %s partition %s signed\n", name, + part_signed ? "is" : "isn't"); + /* * part_start/size are raw pointers into the partition. * ie. they will account for ECC if included. */ - /* Find the sub partition if required */ - if (subid != RESOURCE_SUBID_NONE) { - rc = flash_find_subpartition(flash->bl, subid, &part_start, - &part_size, ecc); - if (rc) + if (part_signed) { + bufp += SECURE_BOOT_HEADERS_SIZE; + bufsz -= SECURE_BOOT_HEADERS_SIZE; + content_size = stb_sw_payload_size(buf, SECURE_BOOT_HEADERS_SIZE); + *len = content_size + SECURE_BOOT_HEADERS_SIZE; + + if (content_size > bufsz) { + prerror("FLASH: content size > buffer size\n"); goto out_free_ffs; - } + } + + ffs_part_start += SECURE_BOOT_HEADERS_SIZE; + if (ecc) + ffs_part_start += ecc_size(SECURE_BOOT_HEADERS_SIZE); - /* Work out what the final size of buffer will be without ECC */ - size = part_size; - if (ecc) { - if (ecc_buffer_size_check(part_size)) { - prerror("FLASH: %s image invalid size for ECC %d\n", - name, part_size); + rc = flash_read_corrected(flash->bl, ffs_part_start, bufp, + content_size, ecc); + if (rc) { + prerror("FLASH: failed to read content size %d" + " %s partition, rc %d\n", + content_size, name, rc); goto out_free_ffs; } - size = ecc_buffer_size_minus_ecc(part_size); - } - if (size > *len) { - prerror("FLASH: %s image too large (%d > %zd)\n", name, - part_size, *len); - goto out_free_ffs; - } + if (subid == RESOURCE_SUBID_NONE) + goto done_reading; - rc = blocklevel_read(flash->bl, part_start, buf, size); - if (rc) { - prerror("FLASH: failed to read %s partition, rc %d\n", name, rc); - goto out_free_ffs; - } + rc = flash_subpart_info(bufp, content_size, ffs_part_size, + NULL, subid, &offset, &content_size); + if (rc) { + prerror("FLASH: Failed to parse subpart info for %s\n", + name); + goto out_free_ffs; + } + bufp += offset; + goto done_reading; + } else /* stb_signed */ { + /* + * Back to the old way of doing things, no STB header. + */ + if (subid == RESOURCE_SUBID_NONE) { + /* + * Because actualSize is a lie, we compute the size + * of the BOOTKERNEL based on what the ELF headers + * say. Otherwise we end up reading more than we should + */ + content_size = sizeof_elf_from_hdr(buf); + if (!content_size) { + prerror("FLASH: Invalid ELF header part %s\n", + name); + goto out_free_ffs; + } + prlog(PR_DEBUG, "FLASH: computed %s size %u\n", + name, content_size); + rc = flash_read_corrected(flash->bl, ffs_part_start, + buf, content_size, ecc); + if (rc) { + prerror("FLASH: failed to read content size %d" + " %s partition, rc %d\n", + content_size, name, rc); + goto out_free_ffs; + } + *len = content_size; + goto done_reading; + } + BUILD_ASSERT(FLASH_SUBPART_HEADER_SIZE <= SECURE_BOOT_HEADERS_SIZE); + rc = flash_subpart_info(bufp, SECURE_BOOT_HEADERS_SIZE, + ffs_part_size, &ffs_part_size, subid, + &offset, &content_size); + if (rc) { + prerror("FLASH: FAILED reading subpart info. rc=%d\n", + rc); + goto out_free_ffs; + } - *len = size; - status = true; + *len = ffs_part_size; + prlog(PR_DEBUG, "FLASH: Computed %s partition size: %u (subpart %u size %u offset %u)\n", name, ffs_part_size, subid, content_size, offset); + /* + * For a sub partition, we read the whole (computed) + * partition, and then measure that. + * Afterwards, we memmove() things back into place for + * the caller. + */ + rc = flash_read_corrected(flash->bl, ffs_part_start, + buf, ffs_part_size, ecc); + bufp += offset; + } + +done_reading: /* * Verify and measure the retrieved PNOR partition as part of the * secure boot and trusted boot requirements @@ -599,6 +654,14 @@ static int flash_load_resource(enum resource_id id, uint32_t subid, sb_verify(id, subid, buf, *len); tb_measure(id, subid, buf, *len); + /* Find subpartition */ + if (subid != RESOURCE_SUBID_NONE) { + memmove(buf, bufp, content_size); + *len = content_size; + } + + status = true; + out_free_ffs: ffs_close(ffs); out_unlock: