[RFC PATCH 5/7] discover: Add hostboot XML handling and processing

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


Add hostboot-specific logic to parse an XML file for attribute override
definitions, and construct the corresponding tree of firmware options.
This will also call into lib/flash to check for existing attribute
overrides.

Signed-off-by: Samuel Mendoza-Jonas <sam.mj at au1.ibm.com>
---
 discover/hostboot.c | 344 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 discover/hostboot.h |  10 ++
 2 files changed, 354 insertions(+)
 create mode 100644 discover/hostboot.c
 create mode 100644 discover/hostboot.h

diff --git a/discover/hostboot.c b/discover/hostboot.c
new file mode 100644
index 0000000..3293d40
--- /dev/null
+++ b/discover/hostboot.c
@@ -0,0 +1,344 @@
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <log/log.h>
+#include <talloc/talloc.h>
+#include <xml/xml.h>
+#include <flash/flash.h>
+
+#include "hostboot.h"
+
+static void parseEnumeration(struct firmware_attr *attr, xmlDocPtr doc,
+			xmlNodePtr node)
+{
+	xmlNodePtr enumerator, child = node->children;
+	char *val;
+	unsigned int i;
+
+	attr->limit = LIMIT_ENUM;
+
+	while (child) {
+		if (xmlStrcmp(child->name, (xmlChar *)"enumerator")) {
+			child = child->next;
+			continue;
+		}
+
+		i = attr->n_range++;
+		attr->enums = talloc_realloc(attr, attr->enums,
+					struct enumeration, attr->n_range);
+		enumerator = child->children;
+		while (enumerator) {
+			val = nodeContent(attr, doc, enumerator);
+			errno = 0;
+
+			if (!xmlStrcmp(enumerator->name, (xmlChar *)"display-name"))
+				attr->enums[i].display_name
+					= talloc_strdup(attr, val);
+			if (!xmlStrcmp(enumerator->name, (xmlChar *)"description"))
+				attr->enums[i].description
+					= talloc_strdup(attr, val);
+			if (!xmlStrcmp(enumerator->name, (xmlChar *)"value"))
+				attr->enums[i].value = strtoul(val, NULL, 16);
+
+			if (errno)
+				pb_log("Error parsing firmware value '%s'"
+				       ", %s\n", val, strerror(errno));
+
+			enumerator = enumerator->next;
+		}
+		child = child->next;
+	}
+}
+
+static void parseRange(struct firmware_attr *attr, xmlDocPtr doc,
+			xmlNodePtr node)
+{
+	xmlNodePtr child = node->children;
+	char *val;
+
+	attr->limit = LIMIT_RANGE;
+	attr->n_range = 2;
+	attr->range = talloc_array(attr, int, attr->n_range);
+
+	while (child) {
+		errno = 0;
+		val = nodeContent(attr, doc, child);
+
+		if (!xmlStrcmp(child->name, (xmlChar *)"start"))
+			attr->range[0] = strtol(val, NULL, 0);
+		if (!xmlStrcmp(child->name, (xmlChar *)"end"))
+			attr->range[1] = strtol(val, NULL, 0);
+
+		if (errno)
+			pb_log("Error parsing firmware value '%s'"
+			       ", %s\n", val, strerror(errno));
+
+		child = child->next;
+	}
+}
+
+static void parseType(struct firmware_attr *attr, xmlDocPtr doc,
+			xmlNodePtr node)
+{
+	xmlNodePtr child = node->children;
+	char *val;
+
+	while (child) {
+		errno = 0;
+		val = nodeContent(attr, doc, child);
+
+		if (!xmlStrcmp(child->name, (xmlChar *)"size"))
+			attr->size = strtol(val, NULL, 0);
+
+		if (!xmlStrcmp(child->name, (xmlChar *)"encoding")) {
+			if (strncmp(val, "signed", strlen("signed")))
+				attr->is_signed = true;
+			else if (strncmp(val, "unsigned", strlen("unsigned")))
+				attr->is_signed = false;
+			else
+				pb_log("Unrecognised encoding: '%s'\n", val);
+		}
+
+		if (errno)
+			pb_log("Error parsing firmware value '%s'"
+			       ", %s\n", val, strerror(errno));
+
+		child = child->next;
+	}
+}
+
+static void parseTarget(struct firmware_attr *attr, xmlDocPtr doc,
+			xmlNodePtr node)
+{
+
+	xmlNodePtr child = node->children;
+	char *val;
+
+	while (child) {
+		errno = 0;
+		val = nodeContent(attr, doc, child);
+
+		if (!xmlStrcmp(child->name, (xmlChar *)"type"))
+			attr->type = strtoul(val, NULL, 16);
+
+		if (!xmlStrcmp(child->name, (xmlChar *)"node"))
+			attr->node = strtoul(val, NULL, 16);
+
+		if (!xmlStrcmp(child->name, (xmlChar *)"position"))
+			attr->pos = strtoul(val, NULL, 16);
+
+		if (!xmlStrcmp(child->name, (xmlChar *)"unit"))
+			attr->unitPos = strtoul(val, NULL, 16);
+
+		if (errno)
+			pb_log("Error parsing firmware value '%s'"
+			       ", %s\n", val, strerror(errno));
+
+		child = child->next;
+	}
+}
+
+static inline bool attribute_match(struct firmware_attr *attr,
+				   struct attribute_header *hdr)
+{
+	uint32_t id  = strtoul(attr->numeric_id, NULL, 0);
+
+	return id == hdr->iv_attrID && attr->type == hdr->iv_targetType &&
+		attr->pos == hdr->iv_pos && attr->unitPos == hdr->iv_unitPos &&
+		attr->node == hdr->iv_node;
+}
+
+static void createAttribute(void *ctx, struct firmware_attr **attr,
+				xmlDocPtr doc, xmlNodePtr root,
+				struct attribute *current_attrs, int n_attrs)
+{
+	xmlNodePtr *nodes;
+	struct firmware_attr *tmp;
+	int i, n_nodes;
+
+	n_nodes = nodeChildren(ctx, &nodes, root, XML_ELEMENT_NODE);
+	if (!n_nodes) {
+		pb_log("No attribute information found\n");
+		return;
+	}
+
+	tmp = talloc_zero(ctx, struct firmware_attr);
+
+	tmp->id = nodeAttribute(ctx, root, "id");
+
+	/* Iterate through possible elements */
+	for (i = 0; i < n_nodes; i++) {
+		if (!xmlStrcmp(nodes[i]->name, (xmlChar *)"numeric-id"))
+			tmp->numeric_id = nodeContent(ctx, doc, nodes[i]);
+
+		if (!xmlStrcmp(nodes[i]->name, (xmlChar *)"type"))
+			parseType(tmp, doc, nodes[i]);
+
+		if (!xmlStrcmp(nodes[i]->name, (xmlChar *)"default"))
+			tmp->default_value = nodeContent(ctx, doc, nodes[i]);
+
+		if (!xmlStrcmp(nodes[i]->name, (xmlChar *)"display-name"))
+			tmp->display_name = nodeContent(ctx, doc, nodes[i]);
+
+		if (!xmlStrcmp(nodes[i]->name, (xmlChar *)"description"))
+			tmp->description = nodeContent(ctx, doc, nodes[i]);
+
+		if (!xmlStrcmp(nodes[i]->name, (xmlChar *)"target"))
+			parseTarget(tmp, doc, nodes[i]);
+
+		if (!xmlStrcmp(nodes[i]->name, (xmlChar *)"range"))
+			parseRange(tmp, doc, nodes[i]);
+
+		if (!xmlStrcmp(nodes[i]->name, (xmlChar *)"enumeration"))
+			parseEnumeration(tmp, doc, nodes[i]);
+	}
+
+	for (i = 0; i < n_attrs; i++) {
+		if (attribute_match(tmp, current_attrs[i].hdr)) {
+			tmp->current_value = parse_attribute_value(ctx,
+							   current_attrs[i],
+							   tmp->is_signed);
+			pb_log("Existing override for %s: %s\n",
+			       tmp->id, tmp->current_value);
+			break;
+		}
+	}
+
+	if (!tmp->current_value)
+		tmp->current_value = talloc_strdup(ctx, tmp->default_value);
+	/* Init user-specified value to current value */
+	tmp->value = talloc_strdup(ctx, tmp->current_value);
+
+	talloc_free(nodes);
+	*attr = tmp;
+}
+
+static int build_config(void *ctx, struct firmware_option **opts,
+			xmlDocPtr doc, xmlNodePtr root,
+			struct attribute *current_attrs, int n_attrs)
+{
+	xmlNodePtr *nodes;
+	struct firmware_option *options;
+	int i, n_nodes, n_valid, cur;
+
+	n_valid = n_nodes = nodeChildren(ctx, &nodes, root, XML_ELEMENT_NODE);
+	if (!n_nodes) {
+		pb_log("No options found in hostboot document\n");
+		return 0;
+	}
+
+	options = talloc_zero_array(ctx, struct firmware_option, n_nodes);
+
+	for (cur = i = 0; i < n_nodes; i++) {
+		/* If unrecognised skip option but keep array consistent
+		 * and continue to parse options */
+		if (xmlStrcmp(nodes[i]->name, (xmlChar *)"attribute")
+		    && xmlStrcmp(nodes[i]->name, (xmlChar *)"group")) {
+			pb_debug("Unrecognized element: '%s'\n", nodes[i]->name);
+			n_valid--;
+			options = talloc_realloc(ctx, options,
+						struct firmware_option, n_valid);
+			continue;
+		}
+
+		if (!xmlStrcmp(nodes[i]->name, (xmlChar *)"attribute")) {
+			options[cur].type = FIRMWARE_ATTR;
+			createAttribute(ctx, &options[cur].attr, doc, nodes[i],
+					current_attrs, n_attrs);
+		}
+
+		if (!xmlStrcmp(nodes[i]->name, (xmlChar *)"group")) {
+			options[cur].type = FIRMWARE_GROUP;
+			xmlNodePtr nameptr = findChildNode(nodes[i],
+						XML_ELEMENT_NODE, "name");
+			if (nameptr)
+				options[cur].name = nodeContent(ctx,
+							doc, nameptr);
+			options[cur].n_opts = build_config(ctx,
+						&options[cur].opts,
+						doc, nodes[i],
+						current_attrs, n_attrs);
+		}
+		cur++;
+	}
+
+	*opts = options;
+	return n_valid;
+}
+
+struct firmware_option *load_hostboot(struct platform_options *options,
+				      const char *xmlpath, const char *mtdpath)
+{
+	struct attribute *current_attrs;;
+	struct firmware_option *opts;
+	int n_opts, n_attrs;
+	xmlNodePtr xmlroot;
+	xmlDocPtr xmldoc;
+
+	/* TODO Incorporate DTD when/if available */
+	xmldoc = readXMLFile(xmlpath);
+		if (!xmldoc) {
+		pb_log("No doc!\n");
+		return NULL;
+	}
+
+	xmlroot = findXMLRoot(xmldoc, "firmware-overrides");
+	if (!xmlroot) {
+		pb_log("No root!\n");
+		finaliseXML(xmldoc);
+		return NULL;
+	}
+
+	/* Gather existing attributes so current values can be used */
+	n_attrs = flash_read_attrs(options, mtdpath, true, true, &current_attrs);
+	if (n_attrs < 0) {
+		pb_log("Error retrieving current attribute overrides\n");
+		n_attrs = 0;
+	}
+
+	n_opts = build_config(options, &opts, xmldoc, xmlroot,
+			      current_attrs, n_attrs);
+	options->n_options = n_opts;
+	options->options = opts;
+
+	finaliseXML(xmldoc);
+
+	return NULL;
+}
+
+int update_platform_option(void *ctx, struct firmware_option option,
+			   const char *mtd_path)
+{
+	int rc = 0, updated = 0;
+	unsigned int i;
+
+	if (option.type == FIRMWARE_GROUP) {
+		for (i = 0; i < option.n_opts; i++) {
+			rc = update_platform_option(ctx, option.opts[i],
+						     mtd_path);
+			if (rc < 0)
+				goto out;
+			updated += rc;
+		}
+	}
+
+	if (option.type == FIRMWARE_ATTR) {
+		if (strlen(option.attr->value) == strlen(option.attr->current_value))
+			if (!strncmp(option.attr->value, option.attr->current_value,
+				     strlen(option.attr->current_value)))
+				return 0;
+
+		pb_log("Attribute %s: '%s' updated, writing to flash...\n",
+		       option.attr->numeric_id, option.attr->id);
+		pb_log("\tNew value: %s\n", option.attr->value);
+
+		/* Process and commit the update */
+		rc = flash_write_attribute(ctx, mtd_path, option.attr, 1, true, true);
+		if (!rc)
+			updated = 1;
+	}
+
+out:
+	return rc < 0 ? rc : updated;
+}
diff --git a/discover/hostboot.h b/discover/hostboot.h
new file mode 100644
index 0000000..ae4e3f6
--- /dev/null
+++ b/discover/hostboot.h
@@ -0,0 +1,10 @@
+#ifndef HOSTBOOT_H
+#define HOSTBOOT_H
+
+#include <types/types.h>
+
+struct firmware_option *load_hostboot(struct platform_options *optiins,
+				      const char *xmlpath, const char *mtdpath);
+int update_platform_option(void *ctx, struct firmware_option option,
+			   const char *mtd_path);
+#endif /* HOSTBOOT_H */
-- 
2.6.2



More information about the Petitboot mailing list