[Skiboot] [PATCH] core/flash: fix part_actual size

Claudio Carvalho cclaudio at linux.vnet.ibm.com
Mon Oct 24 14:12:26 AEDT 2016


The FFS API is not properly setting the part_actual (partition used
space) for the PNOR partitions at build time. However, skiboot needs
that to download and consequently measure and verify only the partition
used space. Currently, the FFS API is setting part_actual=part_size.

This changes the flash_load_resource() to manually calculate the
part_actual for the requested partition in order to verify and measure
only the partition used space.

Signed-off-by: Claudio Carvalho <cclaudio at linux.vnet.ibm.com>
---
 core/flash.c       | 283 ++++++++++++++++++++++++++++++++++-------------------
 include/skiboot.h  |   6 +-
 libstb/container.c |  10 ++
 libstb/container.h |   1 +
 libstb/stb.c       |  16 ++-
 libstb/stb.h       |   6 ++
 6 files changed, 214 insertions(+), 108 deletions(-)

diff --git a/core/flash.c b/core/flash.c
index 92de421..8bb4a9a 100644
--- a/core/flash.c
+++ b/core/flash.c
@@ -25,6 +25,8 @@
 #include <libflash/blocklevel.h>
 #include <libflash/ecc.h>
 #include <libstb/stb.h>
+#include <libstb/container.h>
+#include <elf.h>
 
 struct flash {
 	struct list_node	list;
@@ -436,52 +438,6 @@ struct flash_hostboot_header {
 	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)
-{
-	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, 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);
-	}
-
-end:
-	free(header);
-	return rc;
-}
-
 /*
  * load a resource from FLASH
  * buf and len shouldn't account for ECC even if partition is ECCed.
@@ -489,14 +445,21 @@ end:
 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, rc, hdr_size, size;
+	int part_num, part_size, part_actual, part_start;
 	struct ffs_handle *ffs;
 	struct flash *flash;
 	const char *name;
-	bool status, ecc;
+	bool status, ecc, part_signed;
+	uint8_t *bufp;
+	uint32_t offset;
 
 	rc = OPAL_RESOURCE;
 	status = false;
+	part_signed = false;
+	part_actual = 0;
+	offset = 0;
+	bufp = buf;
 
 	lock(&flash_lock);
 
@@ -557,50 +520,142 @@ static int flash_load_resource(enum resource_id id, uint32_t subid,
 	      name, ecc  ? "has" : "doesn't have");
 
 	/*
+	 * FIXME: Once the part_actual is correctly set in the PNOR build
+	 * process, the code added here to calculate part_actual can be removed.
+	 * In the meantime, we calculate the part_actual by parsing either the
+	 * secure boot headers or the header of the image.
+	 */
+	hdr_size = SECURE_BOOT_HEADERS_SIZE;
+	if ((ecc ? ecc_buffer_size_minus_ecc(part_size) : part_size) < hdr_size) {
+		prerror("FLASH: secboot headers bigger than "
+			"partition size 0x%x\n", part_size);
+		goto out_free_ffs;
+	}
+
+	rc = flash_read_corrected(flash->bl, part_start, bufp,
+			hdr_size, ecc);
+	if (rc) {
+		prerror("FLASH: failed to read the first 0x%x from "
+			"%s partition, rc %d\n", hdr_size, name, rc);
+		goto out_free_ffs;
+	}
+
+	part_signed = stb_is_container(bufp, hdr_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 (part_signed) {
+		part_actual = SECURE_BOOT_HEADERS_SIZE +
+			      stb_sw_payload_size(bufp, hdr_size);
+		size = part_actual - hdr_size;
+		part_start += hdr_size;
+		if (ecc)
+			part_start += ecc_size(hdr_size);
+
+	} else if (subid != RESOURCE_SUBID_NONE) {
+		/* Sanity check */
+		if (FLASH_SUBPART_HEADER_SIZE > SECURE_BOOT_HEADERS_SIZE) {
+			prerror("FLASH: flash header is bigger than secure boot headers\n");
+			goto out_free_ffs;
+		}
+		rc = flash_subpart_info(bufp, SECURE_BOOT_HEADERS_SIZE,
+					part_size, &part_actual, subid,
+					&offset, &size);
 		if (rc)
 			goto out_free_ffs;
+		/*
+		 * Read the partition as whole if we need to verify/measure
+		 * it, otherwise read just the subpartition
+		 */
+		if (sb_secure_mode() || tb_trusted_mode()) {
+			size = part_actual - hdr_size;
+			part_start += hdr_size;
+			if (ecc)
+				part_start += ecc_size(hdr_size);
+		} else {
+			part_start += offset;
+			if (ecc)
+				part_start += ecc_size(offset);
+		}
+	} else {
+		struct elf_hdr *elf = (struct elf_hdr*) bufp;
+
+		if (elf->ei_ident == ELF_IDENT) {
+			if (elf->ei_class == ELF_CLASS_64) {
+				struct elf64_hdr *elf64 = (struct elf64_hdr*) bufp;
+				part_actual = 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*) bufp;
+				part_actual = le32_to_cpu(elf32->e_shoff) +
+				       (le16_to_cpu(elf32->e_shentsize) *
+					le16_to_cpu(elf32->e_shnum));
+			}
+		}
+		size = part_actual - hdr_size;
+		part_start += hdr_size;
+		if (ecc)
+			part_start += ecc_size(hdr_size);
 	}
 
-	/* Work out what the final size of buffer will be without ECC */
-	size = part_size;
+	bufp += hdr_size;
+	prlog(PR_DEBUG, "FLASH: %s partition actual size 0x%x\n", name, part_actual);
+
+	/* Work out what the buffer size will be without ECC */
 	if (ecc) {
-		if (ecc_buffer_size_check(part_size)) {
-			prerror("FLASH: %s image invalid size for ECC %d\n",
-				name, part_size);
+		size = ecc_buffer_size(size);
+		if (ecc_buffer_size_check(size)) {
+			prerror("FLASH: %s image invalid size for ECC 0x%x\n",
+				name, size);
 			goto out_free_ffs;
 		}
-		size = ecc_buffer_size_minus_ecc(part_size);
+		size = ecc_buffer_size_minus_ecc(size);
 	}
 
-	if (size > *len) {
-		prerror("FLASH: %s image too large (%d > %zd)\n", name,
-			part_size, *len);
+	if (part_actual > *len) {
+		prerror("FLASH: %s image too large (0x%x > 0x%zx)\n", name,
+			part_actual, *len);
 		goto out_free_ffs;
 	}
 
-	rc = blocklevel_read(flash->bl, part_start, buf, size);
+	rc = blocklevel_read(flash->bl, part_start, bufp, size);
 	if (rc) {
 		prerror("FLASH: failed to read %s partition, rc %d\n", name, rc);
 		goto out_free_ffs;
 	}
 
-	*len = size;
-	status = true;
-
 	/*
 	 * Verify and measure the retrieved PNOR partition as part of the
 	 * secure boot and trusted boot requirements
 	 */
-	sb_verify(id, subid, buf, *len);
-	tb_measure(id, subid, buf, *len);
+	sb_verify(id, subid, buf, part_actual);
+	tb_measure(id, subid, buf, part_actual);
+
+	/* Find subpartition */
+	if (subid != RESOURCE_SUBID_NONE) {
+		if (part_signed) {
+			rc = flash_subpart_info(bufp, FLASH_SUBPART_HEADER_SIZE,
+						part_size, NULL, subid, &offset,
+						&size);
+			if (rc)
+				goto out_free_ffs;
+			memmove(buf, (void*)(buf + SECURE_BOOT_HEADERS_SIZE + offset),
+				size);
+		} else if (tb_trusted_mode()) {
+			memmove(buf, (void*)(buf + offset), size);
+		}
+		*len = size;
+	} else {
+		*len = hdr_size + size;
+	}
+
+	status = true;
 
 out_free_ffs:
 	ffs_close(ffs);
@@ -724,16 +779,24 @@ int flash_start_preload_resource(enum resource_id id, uint32_t subid,
 	return OPAL_SUCCESS;
 }
 
-int flash_subpart_info(void *part_header, uint32_t part_size, uint32_t subid,
-		       uint32_t *offset, uint32_t *size)
+int flash_subpart_info(void *part_header, uint32_t header_len,
+		       uint32_t part_size, uint32_t *part_actual,
+		       uint32_t subid, uint32_t *offset, uint32_t *size)
 {
 	struct flash_hostboot_header *header;
 	char eyecatcher[5];
-	uint32_t i, ec;
+	uint32_t i, j, ec, o, s, subparts, toc;
+	bool subpart_found;
 
-	if (!part_header || !offset || !size) {
-		prlog(PR_ERR, "FLASH: invalid parameters: "
-		      "ph %p of %p sz %p\n", part_header, offset, size);
+	if (!part_header || ( !offset && !size && !part_actual)) {
+		prlog(PR_ERR, "FLASH: invalid parameters: ph %p of %p sz %p "
+		      "tsz %p\n", part_header, offset, size, part_actual);
+		return OPAL_PARAMETER;
+	}
+
+	if (header_len < FLASH_SUBPART_HEADER_SIZE) {
+		prlog(PR_ERR, "FLASH: subpartition header too small 0x%x\n",
+		      header_len);
 		return OPAL_PARAMETER;
 	}
 
@@ -743,53 +806,69 @@ int flash_subpart_info(void *part_header, uint32_t part_size, uint32_t subid,
 	i = be32_to_cpu(header->version);
 	if (i != 1) {
 		prerror("FLASH: flash subpartition TOC version unknown %i\n", i);
-		goto end;
+		return OPAL_RESOURCE;
 	}
 
 	/* NULL terminate eyecatcher */
 	strncpy(eyecatcher, header->eyecatcher, 4);
 	eyecatcher[4] = '\0';
 	prlog(PR_DEBUG, "FLASH: flash subpartition eyecatcher %s\n",
-			eyecatcher);
+	      eyecatcher);
 
+	subpart_found = false;
+	subparts = 0;
+	toc = sizeof(header->eyecatcher) + sizeof(header->version);
 	for (i = 0; i < FLASH_HOSTBOOT_TOC_MAX_ENTRIES; i++) {
 
 		ec = be32_to_cpu(header->toc[i].ec);
-		*offset = be32_to_cpu(header->toc[i].offset);
-		*size = be32_to_cpu(header->toc[i].size);
+		o = be32_to_cpu(header->toc[i].offset);
+		s = be32_to_cpu(header->toc[i].size);
 
 		/* Check for null terminating entry */
-		if (!ec && !*offset && !*size) {
-			prerror("FLASH: flash subpartition not found.\n");
-			goto end;
+		if (!ec && !o && !s) {
+			if (part_actual)
+				*part_actual = ALIGN_UP(toc, FLASH_SUBPART_HEADER_SIZE) +
+					       ALIGN_UP(subparts, FLASH_SUBPART_HEADER_SIZE);
+			break;
 		}
-
-		if (ec != subid)
-			continue;
-
 		/* Sanity check the offset and size. */
-		if (*offset + *size > part_size) {
+		if (o + s > part_size) {
 			prerror("FLASH: flash subpartition too big: %i\n", i);
-			goto end;
+			return OPAL_RESOURCE;
 		}
-		if (!*size) {
+		if (!s) {
 			prerror("FLASH: flash subpartition zero size: %i\n", i);
-			goto end;
+			return OPAL_RESOURCE;
 		}
-		if (*offset < FLASH_SUBPART_HEADER_SIZE) {
-			prerror("FLASH: flash subpartition "
-					"offset too small: %i\n", i);
-			goto end;
+		if (o < FLASH_SUBPART_HEADER_SIZE) {
+			prerror("FLASH: flash subpartition offset too small: "
+			        "%i\n", i);
+			return OPAL_RESOURCE;
 		}
-
-		prlog(PR_DEBUG, "FLASH: flash found subpartition: "
-				"%i size: %i offset %i\n",
-				i, *size, *offset);
-
-		return OPAL_SUCCESS;
+		/*
+		 * Subpartitions content are different, but multiple toc entries
+		 * may point to the same subpartition.
+		 */
+		for (j=0; j < i; j++)
+			if (header->toc[j].offset == o)
+				break;
+		if (j+1 == i)
+			subparts += s;
+
+		if (ec == subid) {
+			if (offset)
+				*offset += o;
+			if (size)
+				*size = s;
+			if (!part_actual)
+				return OPAL_SUCCESS;
+			subpart_found = true;
+		}
+		toc += sizeof(struct flash_hostboot_toc);
 	}
-end:
-	*size = 0;
-	*offset = 0;
-	return OPAL_RESOURCE;
+	if (!subpart_found && (offset || size)) {
+		prerror("FLASH: flash subpartition not found.\n");
+		return OPAL_RESOURCE;
+	}
+	return OPAL_SUCCESS;
 }
diff --git a/include/skiboot.h b/include/skiboot.h
index 2ef7677..b50868d 100644
--- a/include/skiboot.h
+++ b/include/skiboot.h
@@ -225,9 +225,9 @@ extern int flash_start_preload_resource(enum resource_id id, uint32_t subid,
 extern int flash_resource_loaded(enum resource_id id, uint32_t idx);
 extern bool flash_reserve(void);
 extern void flash_release(void);
-extern int flash_subpart_info(void *part_header, uint32_t part_size,
-			      uint32_t subid, uint32_t *offset,
-			      uint32_t *size);
+extern int flash_subpart_info(void *part_header, uint32_t header_len,
+			      uint32_t part_size, uint32_t *actual_size,
+			      uint32_t subid, uint32_t *offset, uint32_t *size);
 
 /* NVRAM support */
 extern void nvram_init(void);
diff --git a/libstb/container.c b/libstb/container.c
index 19df556..a720fbb 100644
--- a/libstb/container.c
+++ b/libstb/container.c
@@ -38,6 +38,16 @@ uint32_t stb_payload_magic(const void *buf, size_t size)
 	return be32_to_cpu(*(uint32_t*)(p+SECURE_BOOT_HEADERS_SIZE));
 }
 
+uint64_t stb_sw_payload_size(const void *buf, size_t size)
+{
+	struct parsed_stb_container c;
+	if (!stb_is_container(buf, size))
+		return 0;
+	if (parse_stb_container(buf, size, &c) != 0)
+		return 0;
+	return be64_to_cpu(c.sh->payload_size);
+}
+
 int parse_stb_container(const void* data, size_t len, struct parsed_stb_container *c)
 {
 	const size_t prefix_data_min_size = 3 * (EC_COORDBYTES * 2);
diff --git a/libstb/container.h b/libstb/container.h
index f65615a..f8965d4 100644
--- a/libstb/container.h
+++ b/libstb/container.h
@@ -143,6 +143,7 @@ bool stb_is_container(const void* buf, size_t size);
 
 /* Get the pointer for the sw-payload-hash field of the container header */
 const uint8_t* stb_sw_payload_hash(const void* buf, size_t size);
+uint64_t       stb_sw_payload_size(const void *buf, size_t size);
 
 int parse_stb_container(const void* data, size_t len, struct parsed_stb_container *c);
 
diff --git a/libstb/stb.c b/libstb/stb.c
index 4db9ebf..442050b 100644
--- a/libstb/stb.c
+++ b/libstb/stb.c
@@ -185,6 +185,11 @@ int stb_final(void)
 	return rc;
 }
 
+bool tb_trusted_mode(void)
+{
+	return trusted_mode;
+}
+
 int tb_measure(enum resource_id id, uint32_t subid, void *buf, size_t len)
 {
 	int rc, r;
@@ -273,11 +278,16 @@ int tb_measure(enum resource_id id, uint32_t subid, void *buf, size_t len)
 			 EV_ACTION, resource_map[r].name);
 	if (rc)
 		return rc;
-	prlog(PR_NOTICE, "STB: %s%d measured to pcr%d\n", resource_map[r].name,
-	      subid, resource_map[r].pcr);
+	prlog(PR_NOTICE, "STB: %s measured to pcr%d\n", resource_map[r].name,
+	      resource_map[r].pcr);
 	return 0;
 }
 
+bool sb_secure_mode(void)
+{
+	return secure_mode;
+}
+
 int sb_verify(enum resource_id id, uint32_t subid, void *buf, size_t len)
 {
 	int r;
@@ -316,6 +326,6 @@ int sb_verify(enum resource_id id, uint32_t subid, void *buf, size_t len)
 		      *((uint64_t*)buf));
 		sb_enforce();
 	}
-	prlog(PR_NOTICE, "STB: %s%d verified\n", name, subid);
+	prlog(PR_NOTICE, "STB: %s verified\n", name);
 	return 0;
 }
diff --git a/libstb/stb.h b/libstb/stb.h
index 2141cf1..11c4bf5 100644
--- a/libstb/stb.h
+++ b/libstb/stb.h
@@ -72,4 +72,10 @@ extern int sb_verify(enum resource_id id, uint32_t subid, void *buf, size_t len)
 extern int tb_measure(enum resource_id id, uint32_t subid, void *buf,
 		      size_t len);
 
+/* Returns true if the system is booting in trusted mode */
+extern bool tb_trusted_mode(void);
+
+/* Returns true if the system is bootin in secure mode */
+extern bool sb_secure_mode(void);
+
 #endif /* __STB_H */
-- 
1.9.1



More information about the Skiboot mailing list