diff mbox

[04/10] core: Add subid to load_resource()

Message ID 1423706279-12170-4-git-send-email-mikey@neuling.org
State Accepted
Headers show

Commit Message

Michael Neuling Feb. 12, 2015, 1:57 a.m. UTC
This adds a subid to load_resource() so that sub-partitions can be accessed
inside a PNOR partition.  These sub-partitions follow the format used by the
hostboot SBE image.

The subid will match on the EC field of the SBE table of contents.  If it's
found, only that sub-partition is returned to the caller.

Current partitions (kernel and ramfs) don't support sub-partitions.  If caller
tries to access a sub-partition within these, we fail the call.

Signed-off-by: Michael Neuling <mikey@neuling.org>
---
 core/init.c               |   5 +-
 core/platform.c           |   4 +-
 hw/fsp/fsp.c              |  39 ++++++++++-----
 include/fsp.h             |   3 +-
 include/platform.h        |   6 ++-
 platforms/astbmc/astbmc.h |   4 +-
 platforms/astbmc/pnor.c   | 124 +++++++++++++++++++++++++++++++++++++++++++---
 7 files changed, 158 insertions(+), 27 deletions(-)
diff mbox

Patch

diff --git a/core/init.c b/core/init.c
index c18b71c..f76d6c3 100644
--- a/core/init.c
+++ b/core/init.c
@@ -293,7 +293,8 @@  static bool load_kernel(void)
 
 	/* Try to load an external kernel payload through the platform hooks */
 	ksize = KERNEL_LOAD_SIZE;
-	if (!load_resource(RESOURCE_ID_KERNEL, KERNEL_LOAD_BASE,
+	if (!load_resource(RESOURCE_ID_KERNEL, RESOURCE_SUBID_NONE,
+			   KERNEL_LOAD_BASE,
 			   &ksize)) {
 		printf("INIT: platform kernel load failed\n");
 		ksize = 0;
@@ -334,7 +335,7 @@  static void load_initramfs(void)
 	bool loaded;
 
 	size = INITRAMFS_LOAD_SIZE;
-	loaded = load_resource(RESOURCE_ID_INITRAMFS,
+	loaded = load_resource(RESOURCE_ID_INITRAMFS, RESOURCE_SUBID_NONE,
 			       INITRAMFS_LOAD_BASE, &size);
 
 	if (!loaded || !size)
diff --git a/core/platform.c b/core/platform.c
index 4232294..877251e 100644
--- a/core/platform.c
+++ b/core/platform.c
@@ -77,12 +77,12 @@  void probe_platform(void)
 	printf("PLAT: Detected %s platform\n", platform.name);
 }
 
-bool load_resource(enum resource_id id,
+bool load_resource(enum resource_id id, uint32_t subid,
 		   void *buf, size_t *len)
 {
 	if (!platform.load_resource)
 		return false;
 
-	return platform.load_resource(id, buf, len);
+	return platform.load_resource(id, subid, buf, len);
 
 }
diff --git a/hw/fsp/fsp.c b/hw/fsp/fsp.c
index fdc4ffb..da68eef 100644
--- a/hw/fsp/fsp.c
+++ b/hw/fsp/fsp.c
@@ -2245,22 +2245,33 @@  int fsp_fetch_data_queue(uint8_t flags, uint16_t id, uint32_t sub_id,
 	return OPAL_SUCCESS;
 }
 
-bool fsp_load_resource(enum resource_id id, void *buf, size_t *size)
+static struct {
+	enum resource_id	id;
+	uint32_t		idx;
+	uint32_t		lid_no;
+} fsp_lid_map[] = {
+	{ RESOURCE_ID_KERNEL,	RESOURCE_SUBID_NONE,	KERNEL_LID_OPAL },
+	{ RESOURCE_ID_INITRAMFS,RESOURCE_SUBID_NONE,	INITRAMFS_LID_OPAL },
+};
+
+bool fsp_load_resource(enum resource_id id, uint32_t idx,
+		       void *buf, size_t *size)
 {
-	uint32_t lid_no, lid;
+	uint32_t lid_no = 0, lid;
 	size_t tmp_size;
-	int rc;
+	int rc, i;
 
-	switch (id) {
-	case RESOURCE_ID_KERNEL:
-		lid_no = KERNEL_LID_OPAL;
-		break;
-	case RESOURCE_ID_INITRAMFS:
-		lid_no = INITRAMFS_LID_OPAL;
-		break;
-	default:
-		return false;
+	for (i = 0; i < ARRAY_SIZE(fsp_lid_map); i++) {
+		if (id != fsp_lid_map[i].id)
+			continue;
+
+		if (fsp_lid_map[i].idx == idx) {
+			lid_no = fsp_lid_map[i].lid_no;
+			break;
+		}
 	}
+	if (lid_no == 0)
+		return false;
 
 retry:
 	tmp_size = *size;
@@ -2285,8 +2296,10 @@  retry:
 		prerror("Failed to load LID\n");
 		return false;
 	}
-
+	if (*size < tmp_size)
+		return false;
 	*size = tmp_size;
+
 	return true;
 }
 
diff --git a/include/fsp.h b/include/fsp.h
index 9bb7fd8..88db74a 100644
--- a/include/fsp.h
+++ b/include/fsp.h
@@ -709,7 +709,8 @@  extern int fsp_fetch_data(uint8_t flags, uint16_t id, uint32_t sub_id,
 extern int fsp_fetch_data_queue(uint8_t flags, uint16_t id, uint32_t sub_id,
 				uint32_t offset, void *buffer, size_t *length,
 				void (*comp)(struct fsp_msg *msg)) __warn_unused_result;
-extern bool fsp_load_resource(enum resource_id id, void *buf, size_t *size);
+extern bool fsp_load_resource(enum resource_id id, uint32_t subid,
+			      void *buf, size_t *size);
 
 /* FSP console stuff */
 extern void fsp_console_preinit(void);
diff --git a/include/platform.h b/include/platform.h
index ec5cbc6..690772e 100644
--- a/include/platform.h
+++ b/include/platform.h
@@ -26,6 +26,8 @@  enum resource_id {
 	RESOURCE_ID_KERNEL,
 	RESOURCE_ID_INITRAMFS,
 };
+#define RESOURCE_SUBID_NONE 0
+#define RESOURCE_SUBID_SUPPORTED 1
 
 /*
  * Each platform can provide a set of hooks
@@ -129,7 +131,7 @@  struct platform {
 	 * Load an external resource (eg, kernel payload) into a preallocated
 	 * buffer. Returns true on success.
 	 */
-	bool		(*load_resource)(enum resource_id id,
+	bool		(*load_resource)(enum resource_id id, uint32_t idx,
 					 void *buf, size_t *len);
 
 	/*
@@ -148,7 +150,7 @@  static const struct platform __used __section(".platforms") name ##_platform
 
 extern void probe_platform(void);
 
-extern bool load_resource(enum resource_id id,
+extern bool load_resource(enum resource_id id, uint32_t subid,
 			  void *buf, size_t *len);
 
 #endif /* __PLATFORM_H */
diff --git a/platforms/astbmc/astbmc.h b/platforms/astbmc/astbmc.h
index 7e33f61..cff4757 100644
--- a/platforms/astbmc/astbmc.h
+++ b/platforms/astbmc/astbmc.h
@@ -24,6 +24,8 @@  extern int64_t astbmc_ipmi_power_down(uint64_t request);
 extern void astbmc_init(void);
 extern void astbmc_ext_irq(unsigned int chip_id);
 extern int pnor_init(void);
-extern bool pnor_load_resource(enum resource_id id, void *buf, size_t *len);
+extern int pnor_load_part(const char *name, void *addr, size_t *len);
+extern bool pnor_load_resource(enum resource_id id, uint32_t subid,
+			       void *buf, size_t *len);
 
 #endif /* __ASTBMC_H */
diff --git a/platforms/astbmc/pnor.c b/platforms/astbmc/pnor.c
index 2cdb29b..c566877 100644
--- a/platforms/astbmc/pnor.c
+++ b/platforms/astbmc/pnor.c
@@ -84,15 +84,110 @@  int pnor_init(void)
 	return rc;
 }
 
-static const struct {
-	enum resource_id id;
-	char name[PART_NAME_MAX+1];
+static struct {
+	enum resource_id	id;
+	uint32_t		subid;
+	char			name[PART_NAME_MAX+1];
 } part_name_map[] = {
-	{ RESOURCE_ID_KERNEL, "KERNEL" },
-	{ RESOURCE_ID_INITRAMFS, "ROOTFS" },
+	{ RESOURCE_ID_KERNEL,	RESOURCE_SUBID_NONE,		"KERNEL" },
+	{ RESOURCE_ID_INITRAMFS,RESOURCE_SUBID_NONE,		"ROOTFS" },
 };
 
-bool pnor_load_resource(enum resource_id id, void *buf, size_t *len)
+/* This mimics the hostboot SBE format */
+#define PNOR_SUBPART_ALIGNMENT 0x1000
+#define PNOR_SUBPART_HEADER_SIZE PNOR_SUBPART_ALIGNMENT
+struct pnor_hostboot_toc {
+	be32 ec;
+	be32 offset; /* From start of header.  4K aligned */
+	be32 size;
+};
+#define PNOR_HOSTBOOT_TOC_MAX_ENTRIES ((PNOR_SUBPART_HEADER_SIZE - 8)/sizeof(struct pnor_hostboot_toc))
+struct pnor_hostboot_header {
+	char eyecatcher[4];
+	be32 version;
+	struct pnor_hostboot_toc toc[PNOR_HOSTBOOT_TOC_MAX_ENTRIES];
+};
+
+static int pnor_find_subpartition(struct flash_chip *chip,
+			      uint32_t subid,
+			      uint32_t *start,
+			      uint32_t *total_size)
+{
+	struct pnor_hostboot_header *header;
+	uint32_t i;
+	bool rc;
+	char eyecatcher[5];
+
+	header = malloc(PNOR_SUBPART_HEADER_SIZE);
+	if (!header)
+		return false;
+
+	/* Get the TOC */
+	rc = flash_read(chip, *start, header, PNOR_SUBPART_HEADER_SIZE);
+	if (rc) {
+		prerror("PLAT: pnor subpartition TOC read failed %i", rc);
+		goto end;
+	}
+
+	/* Perform sanity */
+	i = be32_to_cpu(header->version);
+	if (i != 1) {
+		prerror("PLAT: pnor subpartition TOC version unknown %i", i);
+		rc = OPAL_RESOURCE;
+		goto end;
+	}
+	/* NULL terminate eyecatcher */
+	strncpy(eyecatcher, header->eyecatcher, 4);
+	eyecatcher[4] = 0;
+	printf("PLAT: pnor subpartition eyecatcher %s\n", eyecatcher);
+
+	rc = OPAL_RESOURCE;
+	for (i = 0; i< PNOR_HOSTBOOT_TOC_MAX_ENTRIES; i++) {
+		uint32_t ec, offset, size;
+
+		ec = be32_to_cpu(header->toc[i].ec);
+		offset = be32_to_cpu(header->toc[i].offset);
+		size = be32_to_cpu(header->toc[i].size);
+		/* Check for null terminating entry */
+		if (!ec && !offset && !size) {
+			prerror("PLAT: pnor subpartition not found.");
+			goto end;
+		}
+
+		if (ec != subid)
+			continue;
+
+		/* Sanity check the offset and size */
+		if (offset + size > *total_size) {
+			prerror("PLAT: pnor subpartition too big: %i", i);
+			goto end;
+		}
+		if (!size) {
+			prerror("PLAT: pnor subpartition zero size: %i", i);
+			goto end;
+		}
+		if (offset < PNOR_SUBPART_HEADER_SIZE) {
+			prerror("PLAT: pnor subpartition offset too small: %i", i);
+			goto end;
+		}
+
+		/* All good, let's adjust the start and size */
+		printf("PLAT: pnor found subpartition: %i size: %i offset %i\n",
+		       i, size, offset);
+		*start += offset;
+		size = (size + (PNOR_SUBPART_ALIGNMENT - 1)) & ~(PNOR_SUBPART_ALIGNMENT - 1);
+		*total_size = size;
+		rc = 0;
+		goto end;
+	}
+
+end:
+	free(header);
+	return rc;
+}
+
+bool pnor_load_resource(enum resource_id id, uint32_t subid,
+			void *buf, size_t *len)
 {
 	int i, rc, part_num, part_size, part_start;
 	const char *name;
@@ -111,6 +206,16 @@  bool pnor_load_resource(enum resource_id id, void *buf, size_t *len)
 		return false;
 	}
 
+	/*
+	 * If partition doesn't have a subindex but the caller specifies one,
+	 * we fail.  eg. kernel partition doesn't have a subindex
+	 */
+	if ((part_name_map[i].subid == RESOURCE_SUBID_NONE) &&
+	    (subid != RESOURCE_SUBID_NONE)) {
+		prerror("PLAT: Partition %s doesn't have subindex\n", name);
+		return false;
+	}
+
 	rc = ffs_lookup_part(pnor_ffs, name, &part_num);
 	if (rc) {
 		prerror("PLAT: No %s partition in PNOR\n", name);
@@ -123,6 +228,13 @@  bool pnor_load_resource(enum resource_id id, void *buf, size_t *len)
 		return false;
 	}
 
+	/* Find the sub partition if required */
+	if (subid != RESOURCE_SUBID_NONE) {
+		rc = pnor_find_subpartition(pnor_chip, subid, &part_start,
+					    &part_size);
+		if (rc)
+			return false;
+	}
 	if (part_size > *len) {
 		prerror("PLAT: %s image too large (%d > %zd)\n", name,
 			part_size, *len);