[PATCH 8/9] discover: Expose and configure Attribute Overrides
Samuel Mendoza-Jonas
sam.mj at au1.ibm.com
Tue Dec 15 14:15:29 AEDT 2015
Parse the attribute XML file and ATTR_PERM partition on startup when
running on a platform with a BMC available. If the descriptive XML file
is not available the ATTR_PERM partition is ignored.
This also adds server support for the PB_PROTOCOL_ACTION_FIRMWARE
message to send attribute details to clients, and for clients to inform
the server of updated values to those attributes.
It is defined that on a successful attribute override update the machine
must reboot immediately for the overrides to take effect.
Signed-off-by: Samuel Mendoza-Jonas <sam.mj at au1.ibm.com>
---
discover/Makefile.am | 3 +
discover/device-handler.c | 37 +++++
discover/device-handler.h | 2 +
discover/discover-server.c | 36 +++++
discover/hostboot.c | 337 ++++++++++++++++++++++++++++++++++++++++++++
discover/hostboot.h | 17 +++
discover/platform-powerpc.c | 32 +++++
discover/platform.c | 23 +++
discover/platform.h | 6 +
9 files changed, 493 insertions(+)
diff --git a/discover/Makefile.am b/discover/Makefile.am
index f55f1cd..7e057d9 100644
--- a/discover/Makefile.am
+++ b/discover/Makefile.am
@@ -90,9 +90,12 @@ discover_platform_ro_SOURCES += \
discover/hostboot.c
discover_platform_ro_LDFLAGS = \
+ `xml2-config --libs` \
$(core_lib) \
$(UDEV_LIBS)
+discover_platform_ro_CPPFLAGS += \
+ `xml2-config --cflags`
endif
discover_platform_ro_LINK = \
diff --git a/discover/device-handler.c b/discover/device-handler.c
index bfe5fff..56e0f1b 100644
--- a/discover/device-handler.c
+++ b/discover/device-handler.c
@@ -1057,6 +1057,43 @@ void device_handler_update_config(struct device_handler *handler,
device_handler_reinit(handler);
}
+void device_handler_update_platform_options(struct platform_options *options)
+{
+ struct process *p;
+ int rc;
+ const char *argv[] = {
+ pb_system_apps.shutdown,
+ "--reboot",
+ NULL
+ };
+
+ rc = platform_options_update(options);
+ if (rc < 0) {
+ /* There was an error updating platform attributes, avoid
+ * rebooting so it can be investigated */
+ pb_log("WARNING: Platform attribute update failed\n");
+ return;
+ }
+ if (rc == 0) {
+ pb_log("No platform updates performed - continuing\n");
+ return;
+ }
+
+ pb_log("%d platform updates performed - REBOOTING\n", rc);
+ p = process_create(options);
+
+ p->path = pb_system_apps.shutdown;
+ p->argv = argv;
+ p->keep_stdout = true;
+
+ rc = process_run_sync(p);
+
+ if (p->exit_status != 0)
+ pb_log("shutdown returns error: %d\n", p->exit_status);
+
+ process_release(p);
+}
+
static char *device_from_addr(void *ctx, struct pb_url *url)
{
char *ipaddr, *buf, *tok, *dev = NULL;
diff --git a/discover/device-handler.h b/discover/device-handler.h
index d18910a..ce35b26 100644
--- a/discover/device-handler.h
+++ b/discover/device-handler.h
@@ -14,6 +14,7 @@ struct event;
struct device;
struct waitset;
struct config;
+struct platform_options;
struct discover_device {
struct device *device;
@@ -132,6 +133,7 @@ void device_handler_boot(struct device_handler *handler,
void device_handler_cancel_default(struct device_handler *handler);
void device_handler_update_config(struct device_handler *handler,
struct config *config);
+void device_handler_update_platform_options(struct platform_options *options);
void device_handler_process_url(struct device_handler *handler,
const char *url);
void device_handler_reinit(struct device_handler *handler);
diff --git a/discover/discover-server.c b/discover/discover-server.c
index e4f3b67..a5fb972 100644
--- a/discover/discover-server.c
+++ b/discover/discover-server.c
@@ -210,10 +210,29 @@ static int write_config_message(struct discover_server *server,
return client_write_message(server, client, message);
}
+static int write_platform_message(struct discover_server *server,
+ struct client *client, const struct platform_options *options)
+{
+ struct pb_protocol_message *message;
+ int len;
+
+ len = pb_protocol_platform_options_len(options);
+
+ message = pb_protocol_create_message(client,
+ PB_PROTOCOL_ACTION_FIRMWARE, len);
+ if (!message)
+ return -1;
+
+ pb_protocol_serialise_platform_options(options, message->payload, len);
+
+ return client_write_message(server, client, message);
+}
+
static int discover_server_process_message(void *arg)
{
struct pb_protocol_message *message;
struct boot_command *boot_command;
+ struct platform_options *options;
struct client *client = arg;
struct config *config;
char *url;
@@ -269,6 +288,18 @@ static int discover_server_process_message(void *arg)
device_handler_process_url(client->server->device_handler, url);
break;
+ case PB_PROTOCOL_ACTION_FIRMWARE:
+ options = talloc_zero(client, struct platform_options);
+
+ rc = pb_protocol_deserialise_platform_options(options, message);
+ if (rc) {
+ pb_log("%s: no options?", __func__);
+ return 0;
+ }
+
+ device_handler_update_platform_options(options);
+ break;
+
default:
pb_log("%s: invalid action %d\n", __func__, message->action);
return 0;
@@ -313,6 +344,11 @@ static int discover_server_process_connection(void *arg)
if (rc)
return 0;
+ /* send platform options to client */
+ rc = write_platform_message(server, client, platform_options_get());
+ if (rc)
+ return 0;
+
/* send existing devices to client */
n_devices = device_handler_get_device_count(server->device_handler);
for (i = 0; i < n_devices; i++) {
diff --git a/discover/hostboot.c b/discover/hostboot.c
index 8ea5ef3..dadb052 100644
--- a/discover/hostboot.c
+++ b/discover/hostboot.c
@@ -4,10 +4,347 @@
#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 enum '%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;
+}
+
+int hostboot_load_attributes(struct platform_options *options,
+ const char *xmlpath)
+{
+ struct attribute *current_attrs;;
+ struct firmware_option *opts;
+ int n_opts, n_attrs;
+ xmlNodePtr xmlroot;
+ xmlDocPtr xmldoc;
+
+ /* If there is no XML file available existing overrides can't be
+ * interpreted, so exit early */
+ xmldoc = readXMLFile(xmlpath);
+ if (!xmldoc)
+ return -1;
+
+ xmlroot = findXMLRoot(xmldoc, "firmware-overrides");
+ if (!xmlroot) {
+ pb_log("Failed to find XML root\n");
+ finaliseXML(xmldoc);
+ return -1;
+ }
+
+ /* Gather existing attributes so current values can be used */
+ n_attrs = flash_read_attrs(options, ¤t_attrs);
+ if (n_attrs < 0) {
+ pb_log("Error retrieving current attribute overrides\n");
+ n_attrs = 0;
+ }
+
+ /* Parse all attributes from the XML file */
+ n_opts = build_config(options, &opts, xmldoc, xmlroot,
+ current_attrs, n_attrs);
+ options->n_options = n_opts;
+ options->options = opts;
+
+ finaliseXML(xmldoc);
+
+ pb_log("%d platform option%s found, with %d existing override%s\n",
+ n_opts, n_opts == 1 ? "" : "s",
+ n_attrs, n_attrs == 1 ? "" : "s");
+
+ return 0;
+}
+
+int hostboot_save_attribute(void *ctx, struct firmware_option option)
+{
+ int rc = 0, updated = 0;
+ unsigned int i;
+
+ if (option.type == FIRMWARE_GROUP) {
+ for (i = 0; i < option.n_opts; i++) {
+ rc = hostboot_save_attribute(ctx, option.opts[i]);
+ 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, option.attr, 1);
+ if (!rc)
+ updated = 1;
+ }
+
+out:
+ return rc < 0 ? rc : updated;
+}
+
int hostboot_load_versions(struct system_info *info)
{
return flash_read_version(info, &info->platform_versions);
diff --git a/discover/hostboot.h b/discover/hostboot.h
index 9ef5f7a..91c2819 100644
--- a/discover/hostboot.h
+++ b/discover/hostboot.h
@@ -5,8 +5,25 @@
#include <types/types.h>
#if MTD_SUPPORT
+int hostboot_load_attributes(struct platform_options *options,
+ const char *xmlpath);
+int hostboot_save_attribute(void *ctx, struct firmware_option option);
int hostboot_load_versions(struct system_info *info);
#else
+static inline int hostboot_load_attributes(struct platform_options *options,
+ const char *xmlpath)
+{
+ (void)options;
+ (void)xmlpath;
+ return NULL;
+}
+static inline int hostboot_save_attribute(void *ctx,
+ struct firmware_option option)
+{
+ (void)ctx;
+ (void)option;
+ return -1;
+}
static inline int hostboot_load_versions(struct system_info *info)
{
(void)info;
diff --git a/discover/platform-powerpc.c b/discover/platform-powerpc.c
index a36f451..de242a5 100644
--- a/discover/platform-powerpc.c
+++ b/discover/platform-powerpc.c
@@ -24,6 +24,7 @@
static const char *partition = "common";
static const char *sysparams_dir = "/sys/firmware/opal/sysparams/";
static const char *devtree_dir = "/proc/device-tree/";
+static const char *xml_path = "/usr/share/bios_metadata.xml";
static const int ipmi_timeout = 5000; /* milliseconds. */
struct param {
@@ -992,6 +993,35 @@ static int set_ipmi_os_boot_sensor(struct platform_powerpc *platform)
return 0;
}
+static int load_platform_options(struct platform_options *options)
+{
+ struct stat statbuf;
+ int rc;
+
+ /* Attribute overrides only supported on BMC-machines */
+ rc = stat("/proc/device-tree/bmc", &statbuf);
+ if (rc)
+ return 0;
+
+ rc = hostboot_load_attributes(options, xml_path);
+ return 0;
+}
+
+static int save_platform_options(struct platform_options *options)
+{
+ int rc, updated = 0;
+ unsigned int i;
+
+ for (i = 0; i < options->n_options; i++) {
+ rc = hostboot_save_attribute(options, options->options[i]);
+ if (rc < 0)
+ break;
+ updated += rc;
+ }
+
+ return rc < 0 ? rc : updated;
+}
+
static int load_config(struct platform *p, struct config *config)
{
struct platform_powerpc *platform = to_platform_powerpc(p);
@@ -1117,6 +1147,8 @@ static struct platform platform_powerpc = {
.save_config = save_config,
.pre_boot = pre_boot,
.get_sysinfo = get_sysinfo,
+ .load_options = load_platform_options,
+ .save_options = save_platform_options,
};
register_platform(platform_powerpc);
diff --git a/discover/platform.c b/discover/platform.c
index 5f448f1..4e024d0 100644
--- a/discover/platform.c
+++ b/discover/platform.c
@@ -15,6 +15,7 @@
void *platform_ctx;
static struct platform *platform;
static struct config *config;
+static struct platform_options *options;
static const char *kernel_cmdline_debug = "petitboot.debug";
@@ -155,10 +156,14 @@ int platform_init(void *ctx)
config = talloc(platform_ctx, struct config);
config_set_defaults(config);
+ options = talloc_zero(platform_ctx, struct platform_options);
+
if (platform) {
pb_log("Detected platform type: %s\n", platform->name);
if (platform->load_config)
platform->load_config(platform, config);
+ if (platform->load_options)
+ platform->load_options(options);
} else {
pb_log("No platform type detected, some platform-specific "
"functionality will be disabled\n");
@@ -212,6 +217,19 @@ int config_set(struct config *newconfig)
return rc;
}
+int platform_options_update(struct platform_options *options)
+{
+ int rc = 0, updated = 0;
+
+ if (platform && platform->save_options) {
+ rc = updated = platform->save_options(options);
+ goto out;
+ }
+
+out:
+ return rc < 0 ? rc : updated;
+}
+
/* A non-exported function to allow the test infrastructure to initialise
* (and change) the configuration variables */
struct parser_test;
@@ -229,6 +247,11 @@ const struct config *config_get(void)
return config;
}
+const struct platform_options *platform_options_get(void)
+{
+ return options;
+}
+
void config_set_autoboot(bool autoboot_enabled)
{
config->autoboot_enabled = autoboot_enabled;
diff --git a/discover/platform.h b/discover/platform.h
index 5aa8e3f..bbe21aa 100644
--- a/discover/platform.h
+++ b/discover/platform.h
@@ -11,6 +11,8 @@ struct platform {
void (*pre_boot)(struct platform *,
const struct config *);
int (*get_sysinfo)(struct platform *, struct system_info *);
+ int (*load_options)(struct platform_options *);
+ int (*save_options)(struct platform_options *);
uint16_t dhcp_arch_id;
void *platform_data;
};
@@ -29,6 +31,10 @@ void config_set_autoboot(bool autoboot_enabled);
/* for use by the platform-specific storage code */
void config_set_defaults(struct config *config);
+/* platform-specific options */
+const struct platform_options *platform_options_get(void);
+int platform_options_update(struct platform_options *options);
+
#define __platform_ptrname(_n) __platform_ ## _n
#define _platform_ptrname(_n) __platform_ptrname(_n)
--
2.6.3
More information about the Petitboot
mailing list