[RFC PATCH 4/7] lib: Add libflash support for Open Power machines

Samuel Mendoza-Jonas sam.mj at au1.ibm.com
Mon Nov 30 14:13:16 AEDT 2015


Attribute overrides are implemented by writing to the ATTR_PERM
partition on BMC flash, which is available via /dev/mtd.
libflash from the skiboot project supports reading and writing to MTD
flash, allowing us to avoid the details of handling partitions and ECC
memory.
This adds enough logic to read and write attribute override structures
to the ATTR_PERM partition.

TODO: Add build options to make libflash support optional.
Signed-off-by: Samuel Mendoza-Jonas <sam.mj at au1.ibm.com>
---
 lib/Makefile.am    |  12 +-
 lib/flash/config.h |  19 ++
 lib/flash/flash.c  | 648 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/flash/flash.h  |  50 +++++
 4 files changed, 726 insertions(+), 3 deletions(-)
 create mode 100644 lib/flash/config.h
 create mode 100644 lib/flash/flash.c
 create mode 100644 lib/flash/flash.h

diff --git a/lib/Makefile.am b/lib/Makefile.am
index 1d55914..ac1f483 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -19,10 +19,13 @@ noinst_LTLIBRARIES += $(core_lib)
 lib_libpbcore_la_CPPFLAGS = \
 	$(AM_CPPFLAGS) \
 	-DPREFIX='"$(prefix)"' \
-	`xml2-config --cflags`
+	`xml2-config --cflags` \
+	-I/path/to/libflash/headers/
 
 lib_libpbcore_la_LDFLAGS = \
-	`xml2-config --libs`
+	`xml2-config --libs` \
+	-L/path/to/libflash/library/ \
+	-l:libflash.so.1.0
 
 lib_libpbcore_la_SOURCES = \
 	lib/file/file.h \
@@ -55,4 +58,7 @@ lib_libpbcore_la_SOURCES = \
 	lib/util/util.c \
 	lib/util/util.h \
 	lib/xml/xml.c \
-	lib/xml/xml.h
+	lib/xml/xml.h \
+	lib/flash/config.h \
+	lib/flash/flash.c \
+	lib/flash/flash.h
diff --git a/lib/flash/config.h b/lib/flash/config.h
new file mode 100644
index 0000000..a132a01
--- /dev/null
+++ b/lib/flash/config.h
@@ -0,0 +1,19 @@
+/* For CCAN */
+
+#include <endian.h>
+#include <byteswap.h>
+
+#define HAVE_TYPEOF			1
+#define HAVE_BUILTIN_TYPES_COMPATIBLE_P	1
+
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define HAVE_BIG_ENDIAN         0
+#define HAVE_LITTLE_ENDIAN      1
+#else
+#define HAVE_BIG_ENDIAN         1
+#define HAVE_LITTLE_ENDIAN      0
+#endif
+
+#define HAVE_BYTESWAP_H 1
+#define HAVE_BSWAP_64	1
diff --git a/lib/flash/flash.c b/lib/flash/flash.c
new file mode 100644
index 0000000..c1ea293
--- /dev/null
+++ b/lib/flash/flash.c
@@ -0,0 +1,648 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <asm/byteorder.h>
+
+#include <log/log.h>
+#include <talloc/talloc.h>
+#include <flash/flash.h>
+#include <ccan/endian/endian.h>
+
+#include <libflash/arch_flash.h>
+#include <libflash/blocklevel.h>
+#include <libflash/libflash.h>
+#include <libflash/libffs.h>
+#include <libflash/file.h>
+#include <libflash/ecc.h>
+
+
+/* Header constants */
+#define		ATTR_POS_NA		0xffff
+#define		ATTR_UNIT_POS_NA	0xff
+#define		ATTR_NODE_NA		0xf
+
+/* Defines layout of attributes in the PNOR */
+struct attribute_section {
+	enum layer {
+		LAYER_NONE,
+		LAYER_FAPI,
+		LAYER_TARG,
+		LAYER_PERM,
+		LAYER_LAST = LAYER_PERM,
+		LAYER_TERM = 0xFFFFFFFF,
+	} layer;
+	size_t		iv_size;	/* Attribute header + data */
+	uint8_t		iv_chunk[0];
+};
+
+struct flash_info {
+	/* Device information */
+	struct blocklevel_device	*bl;
+	struct ffs_handle		*ffs;
+	uint32_t			size;
+	const char			*path;
+	bool				ecc;
+	uint32_t			erase_granule;
+
+	/* Partition information */
+	uint32_t			attr_part_idx;
+	uint32_t			attr_data_pos;
+	uint32_t			attr_data_len; /* Includes ECC bytes */
+
+	/* Buffer information */
+	char				*buffer;
+	uint32_t			len;
+	uint32_t			offset;
+	uint32_t			chunksize;
+};
+
+static void dump_attribute(struct attribute *attr)
+{
+	unsigned int i;
+
+	pb_log("Dumping Attribute:\n");
+	pb_log("-------------------------\n");
+	pb_log("\t.iv_attrID\t%x\n", attr->hdr->iv_attrID);
+	pb_log("\t.iv_targetType\t%x\n", attr->hdr->iv_targetType);
+	pb_log("\t.iv_pos\t\t%x\n", attr->hdr->iv_pos);
+	pb_log("\t.iv_unitPos\t%x\n", attr->hdr->iv_unitPos);
+	pb_log("\t.iv_node\t%x\n", attr->hdr->iv_node);
+	pb_log("\t.iv_flags\t%x\n", attr->hdr->iv_flags);
+	pb_log("\t.iv_valSize\t%x\n", attr->hdr->iv_valSize);
+	pb_log("Data Section:\n\t");
+	for (i = 0; i < attr->hdr->iv_valSize; i++)
+		pb_log("%x ", attr->data[i]);
+	pb_log("\n-------------------------\n");
+}
+
+/* Initialise a layer section if not present. Primarily for use with local
+ * files, but it may also be needed when dealing with an uninitialised
+ * flash partition */
+//TODO Check for other (non-PERM) layers and stamp header at an offset
+static int stamp_header(struct flash_info *info)
+{
+
+	uint8_t *pos;
+	struct attribute_section section = {
+		.layer		= LAYER_PERM,
+		.iv_size	= info->len -
+			sizeof(struct attribute_section),
+	};
+	section.iv_size -= sizeof(section.iv_chunk);
+
+	if (!info->buffer) {
+		pb_log("No buffer to stamp\n");
+		return -1;
+	}
+
+	if (__be32_to_cpu(*(uint32_t *)info->buffer == LAYER_PERM))  {
+		pb_log("Header appears intact\n");
+		return 0;
+	}
+
+	/* Attribute Section */
+	pos = (uint8_t *)info->buffer;
+	*(uint32_t *)pos = __cpu_to_be32(section.layer);
+	pos += 4;
+	/* Two bytes of padding */
+	*(uint32_t *)pos = 0x00000000;
+	pos += 4;
+	*(uint64_t *)pos = __cpu_to_be64(section.iv_size);
+
+	info->offset = sizeof(struct attribute_section) -
+		sizeof(section.iv_chunk);
+	/* We're not expecting other layers in within ATTR_PERM */
+	info->chunksize = section.iv_size - info->offset;
+
+	//TODO may need to initialise chunksize bytes to zero
+
+	return 0;
+}
+
+
+/* Find offset of TANK_LAYER_PERM marker in ATTR_PERM partition */
+static int layer_perm_offset(char *buffer, uint32_t bufsz,
+			     uint32_t *offset, uint32_t *chunksize)
+{
+
+	struct attribute_section section;
+	uint32_t i, pos;
+	bool found = false;
+
+	*offset = *chunksize = 0;
+
+	for (i = 0; i < bufsz; i++) {
+		pos = __be32_to_cpu(*(uint32_t *)(&buffer[i]));
+		switch (pos) {
+		case LAYER_NONE:
+			pb_log("LAYER_NONE encountered - continuing...\n");
+			break;
+		//TODO these should either be unsupported, or we should read
+		//their size and jump past them
+		case LAYER_FAPI:
+			pb_log("LAYER_FAPI encountered - continuing...\n");
+			break;
+		case LAYER_TARG:
+			pb_log("LAYER_TARG encountered - continuing...\n");
+			break;
+		case LAYER_PERM:
+			found = true;
+			break;
+		case LAYER_TERM:
+			return -1;
+		default:
+			pb_log("Unrecognised header %x - wrong offset?\n", pos);
+			break;
+		}
+
+		if (found)
+			break;
+	}
+
+	if (!found) {
+		pb_log("Reached end of buffer without LAYER_TERM\n");
+		return -1;
+	}
+
+	//+8 = iv_layer + padding
+	section.iv_size = __be64_to_cpu(*(uint64_t *)(&buffer[i+8]));
+	*chunksize = section.iv_size;
+	*offset = i + sizeof(struct attribute_section) -
+		sizeof(section.iv_chunk);
+	pb_debug("Found section header LAYER_PERM at pos %u, chunk %u\n",
+	       i, *chunksize);
+
+	return 0;
+}
+
+//TODO May need to consider flash sides
+static struct flash_info *flash_setup(void *ctx, const char *filename,
+				      bool debug, bool direct)
+{
+	struct flash_info *info;
+	int rc = 0;
+
+	info = talloc_zero(ctx, struct flash_info);
+
+	/* Takes a path which can be either a file or an mtd device and inits
+	 * the blocklevel_device struct */
+	rc = arch_flash_init(&info->bl, filename);
+	if (rc) {
+		pb_log("Failed to init %s\n",
+		       filename ? "%s" : "mtd device");
+		return NULL;
+	}
+
+	rc = blocklevel_get_info(info->bl, &info->path, &info->size,
+				 &info->erase_granule);
+	if (rc) {
+		pb_log("Failed to retrieve blocklevel info\n");
+		return NULL;
+	}
+
+	// At the moment we just look for the ATTR_PERM partition. If we need
+	// other partitions later make the partition name a parameter
+	if (!direct) {
+		rc = ffs_init(0, info->size, info->bl, &info->ffs, 1);
+		if (rc) {
+			pb_log("Failed to init ffs\n");
+			goto out;
+		}
+		rc = ffs_lookup_part(info->ffs, "ATTR_PERM", &info->attr_part_idx);
+		if (rc) {
+			pb_log("Failed to find ATTR_PERM\n");
+			goto out;
+		}
+		rc = ffs_part_info(info->ffs, info->attr_part_idx, NULL,
+				   &info->attr_data_pos, &info->attr_data_len,
+				   NULL, &info->ecc);
+		if (rc) {
+			pb_log("Failed to retrive partition info\n");
+			goto out;
+		}
+	} else {
+		rc = blocklevel_ecc_protect(info->bl, 0, info->size);
+		if (rc) {
+			pb_log("Error checking ecc: %d\n", rc);
+			goto out;
+		}
+		info->attr_data_pos = 0;
+		info->attr_data_len = info->size;
+		info->ecc = true; //why not
+	}
+
+	pb_debug("ATTR_PERM Partition Details\n");
+	pb_debug("\tName\t\t%s\n", info->path);
+	pb_debug("\tFlash Size\t%u\n", info->size);
+	pb_debug("\tGranule\t\t%u\n", info->erase_granule);
+	pb_debug("\tECC\t\t%s\n", info->ecc ? "Protected" : "Unprotected");
+	pb_debug("\tIndex\t\t%u\n", info->attr_part_idx);
+	pb_debug("\tOffset\t\t%u\n", info->attr_data_pos);
+	pb_debug("\tPart. Size\t%u\n", info->attr_data_len);
+
+	info->len = info->attr_data_len -  ecc_size(info->attr_data_len);
+	info->buffer = talloc_array(info, char, info->len);
+	if (!info->buffer) {
+		pb_log("%s: Failed to init buffer!\n", __func__);
+		goto out;
+	}
+	/* The returned info->size is not necessarily the length of all ecc-
+	 * protected data. Rather it is the length of the entire partition
+	 * regardless. The distinction comes into effect if, for example, the
+	 * bottom x bytes of the buffer are not ecc-corrected (which given the
+	 * size of the partition is unavoidable). */
+
+	rc = blocklevel_read(info->bl, info->attr_data_pos,
+			     info->buffer, info->len);
+	if (rc) {
+		pb_log("Failed to read ATTR_PERM partition\n");
+		goto out;
+	}
+	rc = layer_perm_offset(info->buffer, info->attr_data_len,
+			       &info->offset, &info->chunksize);
+
+	if (rc) {
+		pb_log("Failed to find LAYER_PERM\n");
+		if (debug)
+			stamp_header(info);
+		else
+			goto out;
+
+	}
+
+	/* Offset must be non-zero past the AttrOverrideSection header */
+	if (info->offset == 0) {
+		pb_log("Nonsensical offset, closing flash.\n");
+		goto out;
+	}
+
+	return info;
+out:
+	arch_flash_close(info->bl, filename);
+	talloc_free(info);
+	return NULL;
+}
+
+static inline void parse_attribute(void *ctx, char *buf, struct attribute *attr)
+{
+	uint8_t tmp;
+
+	/* Retrieve header section */
+	attr->hdr = talloc(ctx, struct attribute_header);
+	attr->hdr->iv_attrID = __be32_to_cpu(*(uint32_t *)buf);
+	buf += 4;
+	attr->hdr->iv_targetType = __be32_to_cpu(*(uint32_t *)buf);
+	buf += 4;
+	attr->hdr->iv_pos = __be16_to_cpu(*(uint16_t *)buf);
+	buf += 2;
+	attr->hdr->iv_unitPos = *(uint8_t *)buf;
+	buf += 1;
+
+	tmp = *(uint8_t *)buf;
+	buf += 1;
+	attr->hdr->iv_node = (tmp & 0xf0) >> 4;
+	attr->hdr->iv_flags = (tmp & 0x0f);
+
+	attr->hdr->iv_valSize = __be32_to_cpu(*(uint32_t *)buf);
+	buf += 4;
+
+	/* Retrieve data section */
+	attr->data = talloc_array(ctx, uint8_t, attr->hdr->iv_valSize);
+	switch (attr->hdr->iv_valSize) {
+	case 1:
+		*attr->data = *(uint8_t *)buf;
+		break;
+	case 2:
+		*(uint16_t *)attr->data = __be16_to_cpu(*(uint16_t *)buf);
+		break;
+	case 4:
+		*(uint32_t *)attr->data = __be32_to_cpu(*(uint32_t *)buf);
+		break;
+	case 8:
+		*(uint64_t *)attr->data = __be64_to_cpu(*(uint64_t *)buf);
+		break;
+	default:
+		pb_log("Attribute '%x' has unsupported value size: %u\n",
+		       attr->hdr->iv_attrID, attr->hdr->iv_valSize);
+		talloc_free(attr->data);
+		attr->data = NULL;
+		break;
+	}
+}
+
+/* Attempt to find an attribute ID in the partition.
+ * Returns true if the attribute is found, and offset is set to the start
+ * of the struct.
+ * Otherwise returns false and offset is set to the next writable area.
+ */
+static bool find_attribute(struct flash_info *info,
+			   struct attribute_header *hdr, uint32_t *offset)
+{
+	struct attribute tmp;
+	bool match = false;
+	uint32_t i;
+
+	*offset = 0;
+
+	for (i = info->offset; i < info->chunksize;) {
+		parse_attribute(info, &info->buffer[i], &tmp);
+		/* End of valid attributes */
+		if (tmp.hdr->iv_attrID == 0) {
+			pb_log("Zero-id attribute found, stopping\n");
+			break;
+		}
+		/* Start of unused memory */
+		if (tmp.hdr->iv_attrID == LAYER_TERM) {
+			pb_debug("ID %x not found - last position offset %u\n",
+			       hdr->iv_attrID, i);
+			*offset = i;
+			break;
+		}
+		/* Existing attribute */
+		if (tmp.hdr->iv_attrID == hdr->iv_attrID) {
+			pb_debug("Matching id for %X found..\n", hdr->iv_attrID);
+			if (hdr->iv_targetType == tmp.hdr->iv_targetType &&
+			    hdr->iv_pos == tmp.hdr->iv_pos &&
+			    hdr->iv_unitPos == tmp.hdr->iv_unitPos &&
+			    hdr->iv_node == tmp.hdr->iv_node) {
+				*offset = i;
+				match = true;
+				break;
+			}
+			pb_debug("Non-unique attribute ID!\n");
+		}
+		i += sizeof(struct attribute_header) + tmp.hdr->iv_valSize;
+	}
+
+	return match;
+}
+
+/* Convert a firmware_attribute struct formed from XML data to an
+ * attribute struct resembling the ATTR_PERM layout */
+static struct attribute *init_attribute(void *ctx, struct firmware_attr *attr)
+{
+	struct attribute *attribute;
+	long int value;
+	if (!attr) {
+		pb_log("%s: No source attribute!\n", __func__);
+		return NULL;
+	}
+
+	attribute = talloc(ctx, struct attribute);
+	if (!attribute) {
+		pb_log("%s: Unable to allocate attribute\n", __func__);
+		return NULL;
+	}
+	attribute->hdr = talloc(attribute, struct attribute_header);
+	if (!attribute->hdr) {
+		pb_log("%s: Unable to allocate header\n", __func__);
+		talloc_free(attribute);
+		return NULL;
+	}
+
+	attribute->hdr->iv_attrID = strtoul(attr->numeric_id, NULL, 0);
+	attribute->hdr->iv_targetType = attr->type;
+	attribute->hdr->iv_pos =  attr->pos;
+	attribute->hdr->iv_unitPos = attr->unitPos;
+	attribute->hdr->iv_node = attr->node;
+	attribute->hdr->iv_flags = attr->flags;
+	attribute->hdr->iv_valSize = attr->size;
+
+	errno = 0;
+	if (attr->is_signed)
+		value = strtoul(attr->value, NULL, 0);
+	else
+		value = strtol(attr->value, NULL, 0);
+	if (errno) {
+		pb_log("Error serialising value '%s': %m\n", attr->value);
+		talloc_free(attribute);
+		return NULL;
+	}
+
+	attribute->data = talloc_array(attribute, uint8_t,
+				       attribute->hdr->iv_valSize);
+	memcpy(attribute->data, attr->value, attribute->hdr->iv_valSize);
+	memcpy(attribute->data,
+	       (uint8_t *)&value, attribute->hdr->iv_valSize);
+
+	return attribute;
+}
+
+/* Add an attribute to the buffer. The ATTR_PERM buffer is defined big-endian */
+static int write_attribute(char *buf, struct attribute *attr, bool update)
+{
+	struct attribute_header *hdr;
+	int rc = 0;
+
+	if (!update) {
+		/* Write header section */
+		*(uint32_t *)buf = __cpu_to_be32(attr->hdr->iv_attrID);
+		buf += 4;
+		*(uint32_t *)buf = __cpu_to_be32(attr->hdr->iv_targetType);
+		buf += 4;
+		*(uint16_t *)buf = __cpu_to_be16(attr->hdr->iv_pos);
+		buf += 2;
+		*(uint8_t *)buf = attr->hdr->iv_unitPos;
+		buf += 1;
+
+		*(uint8_t *)buf = 0;
+		*(uint8_t *)buf |= attr->hdr->iv_node << 4;
+		*(uint8_t *)buf |= attr->hdr->iv_flags;
+		buf += 1;
+
+		*(uint32_t *)buf = __cpu_to_be32(attr->hdr->iv_valSize);
+		buf += 4;
+	} else {
+		pb_log("Attribute already exists, updating in place\n");
+		hdr = (struct attribute_header *)buf;
+		if (__be32_to_cpu(hdr->iv_valSize) != attr->hdr->iv_valSize) {
+			pb_log("%s: Size of existing attribute does not match!",
+			       __func__);
+			pb_log("%s: Trying to write %u bytes to %u byte field"
+			       " - aborting", __func__, attr->hdr->iv_valSize,
+			       __be32_to_cpu(hdr->iv_valSize));
+			rc = -1;
+			goto out;
+		}
+
+		buf += sizeof(struct attribute_header);
+	}
+
+	/* Write data section */
+	switch (attr->hdr->iv_valSize) {
+	case 1:
+		*(uint8_t *)buf = *attr->data;
+		break;
+	case 2:
+		*(uint16_t *)buf = __cpu_to_be16(*(uint16_t *)attr->data);
+		break;
+	case 4:
+		*(uint32_t *)buf = __cpu_to_be32(*(uint32_t *)attr->data);
+		break;
+	case 8:
+		*(uint64_t *)buf = __cpu_to_be64(*(uint64_t *)attr->data);
+		break;
+	default:
+		pb_log("Unsupported attribute value size: %u\n",
+		       attr->hdr->iv_valSize);
+		rc = -1;
+		break;
+	}
+
+out:
+	return rc;
+}
+
+int flash_write_attribute(void *ctx, const char *filename,
+			  struct firmware_attr *attributes, int n_attrs,
+			  bool debug, bool direct)
+{
+	struct firmware_attr *current;
+	uint32_t pos = 0, bufsz = 0;
+	struct flash_info *info;
+	struct attribute *attr;
+	int rc = 0, i;
+	bool found;
+
+	info = flash_setup(ctx, filename, debug, direct);
+	if (!info)
+		return -1;
+
+	if (!attributes) {
+		pb_log("No attributes to write!\n");
+		return -1;
+	}
+
+	/* flash_setup() gives us a buffer to work in, just convert each
+	 * firmware attribute to hostboot format and copy into the buffer.
+	 * The buffer is written out to flash as the final step.
+	 */
+	for (i = 0; i < n_attrs; i++) {
+		current = &attributes[i];
+		bufsz += sizeof(struct attribute_header) + current->size;
+		pb_log("Writing %zu bytes for attribute %s: %s, "
+		       "%u bytes for data section (total %u)\n",
+		       sizeof(struct attribute_header),
+		       current->id, current->numeric_id, current->size,
+		       bufsz);
+
+		attr = init_attribute(info, current);
+
+		found = find_attribute(info, attr->hdr, &pos);
+		if (!pos) {
+			pb_log("Unable to find suitable location\n");
+			talloc_free(attr);
+			rc = -1;
+			goto out;
+		}
+		bufsz = sizeof(struct attribute_header) + attr->hdr->iv_valSize;
+		/* So far we can't update the size of an attribute layer even
+		 * if space is availabe, so abort if we fill chunksize */
+		if (pos + bufsz > info->chunksize + info->offset) {
+			pb_log("Not enough space to write attribute - need "
+			       "%u bytes, %u available\n",
+			       bufsz, info->chunksize - pos - info->offset);
+			rc = -1;
+			talloc_free(attr);
+			goto out;
+		}
+
+		rc = write_attribute(&info->buffer[pos], attr, found);
+		if (rc) {
+			talloc_free(attr);
+			goto out;
+		}
+		pos += bufsz;
+	}
+
+	/* Commit back to flash */
+	rc = blocklevel_smart_write(info->bl, info->attr_data_pos,
+				    info->buffer, info->len);
+	if (rc) {
+		pb_log("Failed to write attribute data\n");
+		pb_log("WARNING: Touched flash at ATTR_PERM should be erased/reset\n");
+	}
+
+out:
+	arch_flash_close(info->bl, filename);
+	talloc_free(info);
+
+	return rc;
+}
+
+int flash_read_attrs(void *ctx, const char *filename, bool debug, bool direct,
+		     struct attribute **attrs)
+{
+	struct attribute *tmp = NULL;
+	struct flash_info *info;
+	uint32_t i, pos;
+	int j, n_attrs = 0;
+
+	info = flash_setup(ctx, filename, debug, direct);
+	if (!info)
+		return -1;
+
+	/* Loop through attributes contained in iv_chunk */
+	for (i = info->offset; i < info->chunksize;) {
+		//TODO We probably also need to check for attrID == 0x00000000
+		//in several places, depending on how ATTR_PERM is initialised
+		pos = __be32_to_cpu(*(uint32_t *)(&info->buffer[i]));
+		if (pos == LAYER_TERM)
+			break;
+
+		tmp = talloc_realloc(ctx, tmp, struct attribute,
+				       n_attrs + 1);
+		if (!tmp) {
+			pb_log("Error reallocating space for attributes\n");
+			break;
+		}
+		parse_attribute(ctx, &info->buffer[i], &tmp[n_attrs]);
+		i += sizeof(struct attribute_header) + tmp[n_attrs].hdr->iv_valSize;
+		n_attrs++;
+	}
+
+	pb_log("%s: %d existing attribute%s found\n", __func__, n_attrs,
+	       n_attrs == 1 ? "" : "s");
+	if (debug)
+		for (j = 0; j < n_attrs; j++)
+			dump_attribute(&tmp[j]);
+
+	arch_flash_close(info->bl, filename);
+	talloc_free(info);
+
+	*attrs = tmp;
+
+	return n_attrs;
+}
+
+char *parse_attribute_value(void *ctx, struct attribute attr, bool is_signed)
+{
+	uint32_t size;
+	char *fmt;
+
+	if (!attr.hdr || !attr.data) {
+		pb_log("%s: NULL attribute\n", __func__);
+		return NULL;
+	}
+
+	size = attr.hdr->iv_valSize;
+	switch (size) {
+	case 1:
+		fmt = talloc_asprintf(ctx, is_signed ? "%%hhd" : "%%hhu");
+	case 2:
+		fmt = talloc_asprintf(ctx, is_signed ? "%%hd" : "%%hu");
+	case 4:
+		fmt = talloc_asprintf(ctx, is_signed ? "%%d" : "%%u");
+	case 8:
+		fmt = talloc_asprintf(ctx, is_signed ? "%%ld" : "%%lu");
+		break;
+	default:
+		pb_log("Unsupported data type, size %u\n", size);
+		return NULL;
+	}
+
+	return talloc_asprintf(ctx, fmt, *(long long int *)attr.data);
+}
diff --git a/lib/flash/flash.h b/lib/flash/flash.h
new file mode 100644
index 0000000..6b1826a
--- /dev/null
+++ b/lib/flash/flash.h
@@ -0,0 +1,50 @@
+/*
+ *  Copyright (C) 2013 IBM Corporation
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef FLASH_H
+#define FLASH_H
+
+#include <flash/config.h>
+#include <types/types.h>
+
+#endif /* FLASH_H */
+
+/* Attribute header fields (attributeTank.H) */
+struct attribute_header {
+	uint32_t	iv_attrID;	/* xml: numeric-id */
+	uint32_t	iv_targetType;	/* xml: type */
+	uint16_t	iv_pos;		/* xml: position */
+	uint8_t		iv_unitPos;	/* xml: unit */
+	uint8_t		iv_node  : 4;	/* xml: node */
+	uint8_t		iv_flags : 4;	/* xml: flags */
+	uint32_t	iv_valSize;	/* xml: size */
+};//__attribute__((packed));
+
+/* Internal */
+struct attribute {
+	int flags; /* eg. PROC_PCIE_IOP_SWAP, not sure where this applies */
+	struct attribute_header	*hdr;
+	uint8_t		*data;
+};
+
+char *parse_attribute_value(void *ctx, struct attribute attr, bool is_signed);
+
+int flash_write_attribute(void *ctx, const char *filename,
+			  struct firmware_attr *attr, int n_attrs,
+			  bool debug, bool direct);
+int flash_read_attrs(void *ctx, const char *filename, bool debug, bool direct,
+		     struct attribute **attrs);
-- 
2.6.2



More information about the Petitboot mailing list