[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, ¤t_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