[v5,2/2] Platform: Restructured the platform related part and add an arm64 profile

Message ID 20180605021448.9064-3-ge.song@hxt-semitech.com
State New
Headers show
Series
  • Add support for arm64 efi-based platform
Related show

Commit Message

Ge Song June 5, 2018, 2:14 a.m.
For arm64 servers, plenty of devices(especially for those bootable devices)
cannot be supported from efi firmware. In this situation, linux&petitboot
is the wonderful way to address the issue.

Since efi is the primary firmware for arm64 servers, most of the management
related works can be completed under that enviroment. Therefore some similar
functions implemented in petitboot have been removed.

Signed-off-by: Ge Song <ge.song@hxt-semitech.com>
---
 Since V5:
 * Restructured the platform related part, centralized the common parts to
   platform.c. This has been veryfied on arm64 platform, but modification
   platform-powerpc.c need to be veryfied due to the lack of power platform.

 Since V4:
 * Platform-arm64.c: Revise code to remove the warning complained by compiler.
                     Adjust to v4 lib efi to set efivarfs path in probe();
 * Platform.c: Remove one redundant line in config_set_defaults()(exist
               in master branch)

 discover/Makefile.am        |   3 +-
 test/parser/Makefile.am     |   1 +
 discover/platform.h         |  18 +-
 discover/platform-arm64.c   | 195 +++++++
 discover/platform-powerpc.c | 612 +-------------------
 discover/platform.c         | 589 ++++++++++++++++++-
 6 files changed, 823 insertions(+), 595 deletions(-)

Patch

diff --git a/discover/Makefile.am b/discover/Makefile.am
index ef4c6027d128..3681caac586b 100644
--- a/discover/Makefile.am
+++ b/discover/Makefile.am
@@ -82,7 +82,8 @@  discover_platform_ro_SOURCES = \
 	discover/dt.c \
 	discover/dt.h \
 	discover/hostboot.h \
-	discover/platform-powerpc.c
+	discover/platform-powerpc.c \
+	discover/platform-arm64.c
 
 discover_platform_ro_CPPFLAGS = \
 	$(AM_CPPFLAGS)
diff --git a/test/parser/Makefile.am b/test/parser/Makefile.am
index 6ff3972d8316..68eb18f7c046 100644
--- a/test/parser/Makefile.am
+++ b/test/parser/Makefile.am
@@ -123,6 +123,7 @@  test_parser_libtest_ro_SOURCES = \
 	discover/pxe-parser.c \
 	discover/syslinux-parser.c \
 	discover/platform.c \
+	discover/ipmi.c \
 	discover/resource.c \
 	discover/paths.c \
 	discover/device-handler.c \
diff --git a/discover/platform.h b/discover/platform.h
index 5aa8e3f9dcc8..919894fb0157 100644
--- a/discover/platform.h
+++ b/discover/platform.h
@@ -2,22 +2,35 @@ 
 #define PLATFORM_H
 
 #include <types/types.h>
+#include "ipmi.h"
 
 struct platform {
 	const char	*name;
 	bool		(*probe)(struct platform *, void *);
 	int		(*load_config)(struct platform *, struct config *);
-	int		(*save_config)(struct platform *, struct config *);
+	int		(*save_config)(struct platform *);
+	void		(*update_config)(struct platform *, struct config*);
 	void		(*pre_boot)(struct platform *,
 				const struct config *);
 	int		(*get_sysinfo)(struct platform *, struct system_info *);
+	struct list     *(*get_params_list)(struct platform *);
 	uint16_t	dhcp_arch_id;
 	void		*platform_data;
 };
 
+struct params {
+	char *name;
+	char *value;
+	bool modified;
+	struct list_item list;
+};
+
+extern const char *known_params[];
+
 int platform_init(void *ctx);
 int platform_fini(void);
 const struct platform *platform_get(void);
+void get_ipmi_bmc_mac(struct ipmi *ipmi, uint8_t *buf, int timeout);
 int platform_get_sysinfo(struct system_info *info);
 void platform_pre_boot(void);
 
@@ -25,6 +38,9 @@  void platform_pre_boot(void);
 const struct config *config_get(void);
 int config_set(struct config *config);
 void config_set_autoboot(bool autoboot_enabled);
+void populate_config(struct list *platform_params, struct config *config);
+int update_config(struct platform *p, struct config *config, struct config *defaults);
+void update_network_config(struct list *platform_params, struct config *config);
 
 /* for use by the platform-specific storage code */
 void config_set_defaults(struct config *config);
diff --git a/discover/platform-arm64.c b/discover/platform-arm64.c
new file mode 100644
index 000000000000..a427eaa8278f
--- /dev/null
+++ b/discover/platform-arm64.c
@@ -0,0 +1,195 @@ 
+/*
+ *  Defines an arm64 platform.
+ *
+ *  Actually, this is apply to platforms that adopt efi as its underlying
+ *  firmware(x86/x64/arm64).
+ *
+ *  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
+ *
+ *  Copyright (C) 2018 Huaxintong Semiconductor Technology Co.,Ltd. All rights
+ *  reserved.
+ *  Author: Ge Song <ge.song@hxt-semitech.com>
+ */
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/statfs.h>
+#include <sys/types.h>
+
+#include "efi/efivar.h"
+#include "file/file.h"
+#include "list/list.h"
+#include "log/log.h"
+#include "talloc/talloc.h"
+#include "types/types.h"
+
+#include "ipmi.h"
+#include "platform.h"
+
+#define DEF_ATTR	(EFI_VARIABLE_NON_VOLATILE | \
+	EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)
+static const char *pb_vars_guid = "fb78ab4b-bd43-41a0-99a2-4e74bef9169b";
+static const int ipmi_timeout = 10000; /* milliseconds. */
+
+struct platform_arm64 {
+	struct list params;
+	struct ipmi *ipmi;
+};
+
+static inline struct platform_arm64 *to_platform_arm64(struct platform *p)
+{
+	return (struct platform_arm64 *)(p->platform_data);
+}
+
+static void parse_nvram(struct platform_arm64 *platform)
+{
+	int i, rc;
+	size_t size;
+	uint32_t attr;
+	uint8_t *data;
+	const char *pb_guid = pb_vars_guid;
+	const char *known_param;
+	struct params *param;
+
+	for (i = 0; known_params[i]; i++) {
+		known_param = known_params[i];
+		rc = efi_get_variable(platform, pb_guid, known_param,
+				&data, &size, &attr);
+		if (!rc) {
+			param = talloc(platform, struct params);
+			param->modified = false;
+			param->name = talloc_strdup(platform, known_param);
+			param->value = talloc_strdup(platform, (char *)data);
+			list_add(&platform->params, &param->list);
+		}
+	}
+}
+
+static int write_nvram(struct platform_arm64 *platform)
+{
+	struct params *param;
+	const char *pb_guid = pb_vars_guid;
+	uint32_t attr = DEF_ATTR;
+	size_t data_size;
+
+	list_for_each_entry(&platform->params, param, list) {
+		if (param->modified) {
+			data_size = strlen(param->value) + 1;
+			efi_set_variable(platform, pb_guid, param->name,
+				(uint8_t *)param->value, data_size, attr);
+		}
+	}
+
+	return 0;
+}
+
+static struct list* get_params_list(struct platform* p)
+{
+	struct platform_arm64 *platform = to_platform_arm64(p);
+
+	return &platform->params;
+}
+
+static void get_active_consoles(struct config *config)
+{
+	config->n_consoles = 3;
+	config->consoles = talloc_array(config, char *, config->n_consoles);
+	if (!config->consoles)
+		goto err;
+
+	config->consoles[0] = talloc_asprintf(config->consoles,
+					"/dev/hvc0 [IPMI / Serial]");
+	config->consoles[1] = talloc_asprintf(config->consoles,
+					"/dev/ttyAMA0 [Serial]");
+	config->consoles[2] = talloc_asprintf(config->consoles,
+					"/dev/tty1 [VGA]");
+
+	return;
+err:
+	config->n_consoles = 0;
+	pb_log("Failed to allocate memory for consoles\n");
+}
+
+static int load_config(struct platform *p, struct config *config)
+{
+	struct platform_arm64 *platform = to_platform_arm64(p);
+
+	parse_nvram(platform);
+
+	populate_config(&platform->params, config);
+
+	get_active_consoles(config);
+
+	return 0;
+}
+
+static int get_sysinfo(struct platform *p, struct system_info *sysinfo)
+{
+	struct platform_arm64 *platform = p->platform_data;
+
+	sysinfo->bmc_mac = talloc_zero_size(sysinfo, HWADDR_SIZE);
+
+	if (platform->ipmi)
+		get_ipmi_bmc_mac(platform->ipmi, sysinfo->bmc_mac, ipmi_timeout);
+
+	return 0;
+}
+
+static int save_config(struct platform *p)
+{
+	struct platform_arm64 *platform = p->platform_data;
+
+	return write_nvram(platform);
+}
+
+static bool probe(struct platform *p, void *ctx)
+{
+	struct platform_arm64 *platform;
+	const char *path = "/sys/firmware/efi/efivars/";
+	int rc = 0;
+	struct statfs buf;
+
+	if (access(path, F_OK))
+		return false;
+
+	memset(&buf, '\0', sizeof(buf));
+	rc = statfs(path, &buf);
+	if (rc)
+		return false;
+
+	typeof(buf.f_type) magic = EFIVARFS_MAGIC;
+	if (!memcmp(&buf.f_type, &magic, sizeof (magic))) {
+		platform = talloc_zero(ctx, struct platform_arm64);
+		list_init(&platform->params);
+		p->platform_data = platform;
+		p->get_params_list = get_params_list;
+
+		set_efivarfs_path(path);
+		return true;
+	}
+
+	return false;
+}
+
+static struct platform platform_arm64 = {
+	.name			= "arm64",
+	.dhcp_arch_id		= 0x000d,
+	.probe			= probe,
+	.load_config		= load_config,
+	.save_config		= save_config,
+	.get_sysinfo		= get_sysinfo,
+};
+
+register_platform(platform_arm64);
diff --git a/discover/platform-powerpc.c b/discover/platform-powerpc.c
index df8c7d611d26..973cb23d5b8f 100644
--- a/discover/platform-powerpc.c
+++ b/discover/platform-powerpc.c
@@ -1,40 +1,33 @@ 
 
+#include <asm/byteorder.h>
 #include <assert.h>
-#include <string.h>
-#include <stdlib.h>
-#include <limits.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/wait.h>
-#include <sys/stat.h>
-#include <asm/byteorder.h>
 
 #include <file/file.h>
-#include <talloc/talloc.h>
 #include <list/list.h>
 #include <log/log.h>
 #include <process/process.h>
+#include <talloc/talloc.h>
 #include <types/types.h>
 #include <url/url.h>
 
+#include "dt.h"
 #include "hostboot.h"
-#include "platform.h"
 #include "ipmi.h"
-#include "dt.h"
+#include "platform.h"
 
 static const char *partition = "common";
 static const char *sysparams_dir = "/sys/firmware/opal/sysparams/";
 static const char *devtree_dir = "/proc/device-tree/";
 static const int ipmi_timeout = 10000; /* milliseconds. */
 
-struct param {
-	char			*name;
-	char			*value;
-	bool			modified;
-	struct list_item	list;
-};
-
 struct platform_powerpc {
 	struct list	params;
 	struct ipmi	*ipmi;
@@ -45,26 +38,11 @@  struct platform_powerpc {
 	int		(*clear_ipmi_bootdev)(
 				struct platform_powerpc *platform,
 				bool persistent);
-	int 		(*set_os_boot_sensor)(
+	int		(*set_os_boot_sensor)(
 				struct platform_powerpc *platform);
 	void		(*get_platform_versions)(struct system_info *info);
 };
 
-static const char *known_params[] = {
-	"auto-boot?",
-	"petitboot,network",
-	"petitboot,timeout",
-	"petitboot,bootdevs",
-	"petitboot,language",
-	"petitboot,debug?",
-	"petitboot,write?",
-	"petitboot,snapshots?",
-	"petitboot,console",
-	"petitboot,http_proxy",
-	"petitboot,https_proxy",
-	NULL,
-};
-
 #define to_platform_powerpc(p) \
 	(struct platform_powerpc *)(p->platform_data)
 
@@ -111,7 +89,7 @@  static int parse_nvram_params(struct platform_powerpc *platform,
 
 	for (pos = buf + i; pos < buf + len; pos += paramlen + 1) {
 		unsigned int namelen;
-		struct param *param;
+		struct params *param;
 		char *newline;
 
 		newline = strchr(pos, '\n');
@@ -136,7 +114,7 @@  static int parse_nvram_params(struct platform_powerpc *platform,
 
 		value++;
 
-		param = talloc(platform, struct param);
+		param = talloc(platform, struct params);
 		param->modified = false;
 		param->name = talloc_strndup(platform, name, namelen);
 		param->value = talloc_strdup(platform, value);
@@ -181,7 +159,7 @@  static int parse_nvram(struct platform_powerpc *platform)
 static int write_nvram(struct platform_powerpc *platform)
 {
 	struct process *process;
-	struct param *param;
+	struct params *param;
 	const char *argv[6];
 	int rc = 0;
 
@@ -222,525 +200,11 @@  static int write_nvram(struct platform_powerpc *platform)
 	return rc;
 }
 
-static const char *get_param(struct platform_powerpc *platform,
-		const char *name)
+static struct list* get_params_list(struct platform* p)
 {
-	struct param *param;
+	struct platform_powerpc *platform = to_platform_powerpc(p);
 
-	list_for_each_entry(&platform->params, param, list)
-		if (!strcmp(param->name, name))
-			return param->value;
-	return NULL;
-}
-
-static void set_param(struct platform_powerpc *platform, const char *name,
-		const char *value)
-{
-	struct param *param;
-
-	list_for_each_entry(&platform->params, param, list) {
-		if (strcmp(param->name, name))
-			continue;
-
-		if (!strcmp(param->value, value))
-			return;
-
-		talloc_free(param->value);
-		param->value = talloc_strdup(param, value);
-		param->modified = true;
-		return;
-	}
-
-
-	param = talloc(platform, struct param);
-	param->modified = true;
-	param->name = talloc_strdup(platform, name);
-	param->value = talloc_strdup(platform, value);
-	list_add(&platform->params, &param->list);
-}
-
-static int parse_hwaddr(struct interface_config *ifconf, char *str)
-{
-	int i;
-
-	if (strlen(str) != strlen("00:00:00:00:00:00"))
-		return -1;
-
-	for (i = 0; i < HWADDR_SIZE; i++) {
-		char byte[3], *endp;
-		unsigned long x;
-
-		byte[0] = str[i * 3 + 0];
-		byte[1] = str[i * 3 + 1];
-		byte[2] = '\0';
-
-		x = strtoul(byte, &endp, 16);
-		if (endp != byte + 2)
-			return -1;
-
-		ifconf->hwaddr[i] = x & 0xff;
-	}
-
-	return 0;
-}
-
-static int parse_one_interface_config(struct config *config,
-		char *confstr)
-{
-	struct interface_config *ifconf;
-	char *tok, *tok_gw, *tok_url, *saveptr;
-
-	ifconf = talloc_zero(config, struct interface_config);
-
-	if (!confstr || !strlen(confstr))
-		goto out_err;
-
-	/* first token should be the mac address */
-	tok = strtok_r(confstr, ",", &saveptr);
-	if (!tok)
-		goto out_err;
-
-	if (parse_hwaddr(ifconf, tok))
-		goto out_err;
-
-	/* second token is the method */
-	tok = strtok_r(NULL, ",", &saveptr);
-	if (!tok || !strlen(tok) || !strcmp(tok, "ignore")) {
-		ifconf->ignore = true;
-
-	} else if (!strcmp(tok, "dhcp")) {
-		ifconf->method = CONFIG_METHOD_DHCP;
-
-	} else if (!strcmp(tok, "static")) {
-		ifconf->method = CONFIG_METHOD_STATIC;
-
-		/* ip/mask, [optional] gateway, [optional] url */
-		tok = strtok_r(NULL, ",", &saveptr);
-		if (!tok)
-			goto out_err;
-		ifconf->static_config.address =
-			talloc_strdup(ifconf, tok);
-
-		/*
-		 * If a url is set but not a gateway, we can accidentally
-		 * interpret the url as the gateway. To avoid changing the
-		 * parameter format check if the "gateway" is actually a
-		 * pb-url if it's the last token.
-		 */
-		tok_gw = strtok_r(NULL, ",", &saveptr);
-		tok_url = strtok_r(NULL, ",", &saveptr);
-
-		if (tok_gw) {
-			if (tok_url || !is_url(tok_gw))
-				ifconf->static_config.gateway =
-					talloc_strdup(ifconf, tok_gw);
-			else
-					tok_url = tok_gw;
-		}
-
-		if (tok_url)
-			ifconf->static_config.url =
-				talloc_strdup(ifconf, tok_url);
-	} else {
-		pb_log("Unknown network configuration method %s\n", tok);
-		goto out_err;
-	}
-
-	config->network.interfaces = talloc_realloc(config,
-			config->network.interfaces,
-			struct interface_config *,
-			++config->network.n_interfaces);
-
-	config->network.interfaces[config->network.n_interfaces - 1] = ifconf;
-
-	return 0;
-out_err:
-	talloc_free(ifconf);
-	return -1;
-}
-
-static int parse_one_dns_config(struct config *config,
-		char *confstr)
-{
-	char *tok, *saveptr = NULL;
-
-	for (tok = strtok_r(confstr, ",", &saveptr); tok;
-			tok = strtok_r(NULL, ",", &saveptr)) {
-
-		char *server = talloc_strdup(config, tok);
-
-		config->network.dns_servers = talloc_realloc(config,
-				config->network.dns_servers, const char *,
-				++config->network.n_dns_servers);
-
-		config->network.dns_servers[config->network.n_dns_servers - 1]
-				= server;
-	}
-
-	return 0;
-}
-
-static void populate_network_config(struct platform_powerpc *platform,
-		struct config *config)
-{
-	char *val, *saveptr = NULL;
-	const char *cval;
-	int i;
-
-	cval = get_param(platform, "petitboot,network");
-	if (!cval || !strlen(cval))
-		return;
-
-	val = talloc_strdup(config, cval);
-
-	for (i = 0; ; i++) {
-		char *tok;
-
-		tok = strtok_r(i == 0 ? val : NULL, " ", &saveptr);
-		if (!tok)
-			break;
-
-		if (!strncasecmp(tok, "dns,", strlen("dns,")))
-			parse_one_dns_config(config, tok + strlen("dns,"));
-		else
-			parse_one_interface_config(config, tok);
-
-	}
-
-	talloc_free(val);
-}
-
-static int read_bootdev(void *ctx, char **pos, struct autoboot_option *opt)
-{
-	char *delim = strchr(*pos, ' ');
-	int len, prefix = 0, rc = -1;
-	enum device_type type;
-
-	if (!strncmp(*pos, "uuid:", strlen("uuid:"))) {
-		prefix = strlen("uuid:");
-		opt->boot_type = BOOT_DEVICE_UUID;
-	} else if (!strncmp(*pos, "mac:", strlen("mac:"))) {
-		prefix = strlen("mac:");
-		opt->boot_type = BOOT_DEVICE_UUID;
-	} else {
-		type = find_device_type(*pos);
-		if (type != DEVICE_TYPE_UNKNOWN) {
-			opt->type = type;
-			opt->boot_type = BOOT_DEVICE_TYPE;
-			rc = 0;
-		}
-	}
-
-	if (opt->boot_type == BOOT_DEVICE_UUID) {
-		if (delim)
-			len = (int)(delim - *pos) - prefix;
-		else
-			len = strlen(*pos) - prefix;
-
-		if (len) {
-			opt->uuid = talloc_strndup(ctx, *pos + prefix, len);
-			rc = 0;
-		}
-	}
-
-	/* Always advance pointer to next option or end */
-	if (delim)
-		*pos = delim + 1;
-	else
-		*pos += strlen(*pos);
-
-	return rc;
-}
-
-static void populate_bootdev_config(struct platform_powerpc *platform,
-		struct config *config)
-{
-	struct autoboot_option *opt, *new = NULL;
-	char *pos, *end;
-	unsigned int n_new = 0;
-	const char *val;
-
-	/* Check for ordered bootdevs */
-	val = get_param(platform, "petitboot,bootdevs");
-	if (!val || !strlen(val)) {
-		pos = end = NULL;
-	} else {
-		pos = talloc_strdup(config, val);
-		end = strchr(pos, '\0');
-	}
-
-	while (pos && pos < end) {
-		opt = talloc(config, struct autoboot_option);
-
-		if (read_bootdev(config, &pos, opt)) {
-			pb_log("bootdev config is in an unknown format "
-			       "(expected uuid:... or mac:...)\n");
-			talloc_free(opt);
-			continue;
-		}
-
-		new = talloc_realloc(config, new, struct autoboot_option,
-				     n_new + 1);
-		new[n_new] = *opt;
-		n_new++;
-		talloc_free(opt);
-
-	}
-
-	if (!n_new) {
-		/* If autoboot has been disabled, clear the default options */
-		if (!config->autoboot_enabled) {
-			talloc_free(config->autoboot_opts);
-			config->n_autoboot_opts = 0;
-		}
-		return;
-	}
-
-	talloc_free(config->autoboot_opts);
-	config->autoboot_opts = new;
-	config->n_autoboot_opts = n_new;
-}
-
-static void populate_config(struct platform_powerpc *platform,
-		struct config *config)
-{
-	const char *val;
-	char *end;
-	unsigned long timeout;
-
-	/* if the "auto-boot?' property is present and "false", disable auto
-	 * boot */
-	val = get_param(platform, "auto-boot?");
-	config->autoboot_enabled = !val || strcmp(val, "false");
-
-	val = get_param(platform, "petitboot,timeout");
-	if (val) {
-		timeout = strtoul(val, &end, 10);
-		if (end != val) {
-			if (timeout >= INT_MAX)
-				timeout = INT_MAX;
-			config->autoboot_timeout_sec = (int)timeout;
-		}
-	}
-
-	val = get_param(platform, "petitboot,language");
-	config->lang = val ? talloc_strdup(config, val) : NULL;
-
-	populate_network_config(platform, config);
-
-	populate_bootdev_config(platform, config);
-
-	if (!config->debug) {
-		val = get_param(platform, "petitboot,debug?");
-		config->debug = val && !strcmp(val, "true");
-	}
-
-	val = get_param(platform, "petitboot,write?");
-	if (val)
-		config->allow_writes = !strcmp(val, "true");
-
-	val = get_param(platform, "petitboot,snapshots?");
-	if (val)
-		config->disable_snapshots = !strcmp(val, "false");
-
-	val = get_param(platform, "petitboot,console");
-	if (val)
-		config->boot_console = talloc_strdup(config, val);
-	/* If a full path is already set we don't want to override it */
-	config->manual_console = config->boot_console &&
-					!strchr(config->boot_console, '[');
-
-	val = get_param(platform, "petitboot,http_proxy");
-	if (val)
-		config->http_proxy = talloc_strdup(config, val);
-	val = get_param(platform, "petitboot,https_proxy");
-	if (val)
-		config->https_proxy = talloc_strdup(config, val);
-}
-
-static char *iface_config_str(void *ctx, struct interface_config *config)
-{
-	char *str;
-
-	/* todo: HWADDR size is hardcoded as 6, but we may need to handle
-	 * different hardware address formats */
-	str = talloc_asprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x,",
-			config->hwaddr[0], config->hwaddr[1],
-			config->hwaddr[2], config->hwaddr[3],
-			config->hwaddr[4], config->hwaddr[5]);
-
-	if (config->ignore) {
-		str = talloc_asprintf_append(str, "ignore");
-
-	} else if (config->method == CONFIG_METHOD_DHCP) {
-		str = talloc_asprintf_append(str, "dhcp");
-
-	} else if (config->method == CONFIG_METHOD_STATIC) {
-		str = talloc_asprintf_append(str, "static,%s%s%s%s%s",
-				config->static_config.address,
-				config->static_config.gateway ? "," : "",
-				config->static_config.gateway ?: "",
-				config->static_config.url ? "," : "",
-				config->static_config.url ?: "");
-	}
-	return str;
-}
-
-static char *dns_config_str(void *ctx, const char **dns_servers, int n)
-{
-	char *str;
-	int i;
-
-	str = talloc_strdup(ctx, "dns,");
-	for (i = 0; i < n; i++) {
-		str = talloc_asprintf_append(str, "%s%s",
-				i == 0 ? "" : ",",
-				dns_servers[i]);
-	}
-
-	return str;
-}
-
-static void update_string_config(struct platform_powerpc *platform,
-		const char *name, const char *value)
-{
-	const char *cur;
-
-	cur = get_param(platform, name);
-
-	/* don't set an empty parameter if it doesn't already exist */
-	if (!cur && !strlen(value))
-		return;
-
-	set_param(platform, name, value);
-}
-
-static void update_network_config(struct platform_powerpc *platform,
-	struct config *config)
-{
-	unsigned int i;
-	char *val;
-
-	/*
-	 * Don't store IPMI overrides to NVRAM. If this was a persistent
-	 * override it was already stored in NVRAM by
-	 * get_ipmi_network_override()
-	 */
-	if (config->network.n_interfaces &&
-		config->network.interfaces[0]->override)
-		return;
-
-	val = talloc_strdup(platform, "");
-
-	for (i = 0; i < config->network.n_interfaces; i++) {
-		char *iface_str = iface_config_str(platform,
-					config->network.interfaces[i]);
-		val = talloc_asprintf_append(val, "%s%s",
-				*val == '\0' ? "" : " ", iface_str);
-		talloc_free(iface_str);
-	}
-
-	if (config->network.n_dns_servers) {
-		char *dns_str = dns_config_str(platform,
-						config->network.dns_servers,
-						config->network.n_dns_servers);
-		val = talloc_asprintf_append(val, "%s%s",
-				*val == '\0' ? "" : " ", dns_str);
-		talloc_free(dns_str);
-	}
-
-	update_string_config(platform, "petitboot,network", val);
-
-	talloc_free(val);
-}
-
-static void update_bootdev_config(struct platform_powerpc *platform,
-		struct config *config)
-{
-	char *val = NULL, *boot_str = NULL, *tmp = NULL;
-	struct autoboot_option *opt;
-	const char delim = ' ';
-	unsigned int i;
-
-	if (!config->n_autoboot_opts)
-		val = "";
-
-	for (i = 0; i < config->n_autoboot_opts; i++) {
-		opt = &config->autoboot_opts[i];
-		switch (opt->boot_type) {
-			case BOOT_DEVICE_TYPE:
-				boot_str = talloc_asprintf(config, "%s%c",
-						device_type_name(opt->type),
-						delim);
-				break;
-			case BOOT_DEVICE_UUID:
-				boot_str = talloc_asprintf(config, "uuid:%s%c",
-						opt->uuid, delim);
-				break;
-			}
-			tmp = val = talloc_asprintf_append(val, "%s", boot_str);
-	}
-
-	update_string_config(platform, "petitboot,bootdevs", val);
-	talloc_free(tmp);
-	if (boot_str)
-		talloc_free(boot_str);
-}
-
-static int update_config(struct platform_powerpc *platform,
-		struct config *config, struct config *defaults)
-{
-	char *tmp = NULL;
-	const char *val;
-
-	if (config->autoboot_enabled == defaults->autoboot_enabled)
-		val = "";
-	else
-		val = config->autoboot_enabled ? "true" : "false";
-	update_string_config(platform, "auto-boot?", val);
-
-	if (config->autoboot_timeout_sec == defaults->autoboot_timeout_sec)
-		val = "";
-	else
-		val = tmp = talloc_asprintf(platform, "%d",
-				config->autoboot_timeout_sec);
-
-	if (config->ipmi_bootdev == IPMI_BOOTDEV_INVALID &&
-	    platform->clear_ipmi_bootdev) {
-		platform->clear_ipmi_bootdev(platform,
-				config->ipmi_bootdev_persistent);
-		config->ipmi_bootdev = IPMI_BOOTDEV_NONE;
-		config->ipmi_bootdev_persistent = false;
-	}
-
-	update_string_config(platform, "petitboot,timeout", val);
-	if (tmp)
-		talloc_free(tmp);
-
-	val = config->lang ?: "";
-	update_string_config(platform, "petitboot,language", val);
-
-	if (config->allow_writes == defaults->allow_writes)
-		val = "";
-	else
-		val = config->allow_writes ? "true" : "false";
-	update_string_config(platform, "petitboot,write?", val);
-
-	if (!config->manual_console) {
-		val = config->boot_console ?: "";
-		update_string_config(platform, "petitboot,console", val);
-	}
-
-	val = config->http_proxy ?: "";
-	update_string_config(platform, "petitboot,http_proxy", val);
-	val = config->https_proxy ?: "";
-	update_string_config(platform, "petitboot,https_proxy", val);
-
-	update_network_config(platform, config);
-
-	update_bootdev_config(platform, config);
-
-	return write_nvram(platform);
+	return &platform->params;
 }
 
 static void set_ipmi_bootdev(struct config *config, enum ipmi_bootdev bootdev,
@@ -992,32 +456,6 @@  static int set_ipmi_os_boot_sensor(struct platform_powerpc *platform)
 	return 0;
 }
 
-static void get_ipmi_bmc_mac(struct platform *p, uint8_t *buf)
-{
-	struct platform_powerpc *platform = p->platform_data;
-	uint16_t resp_len = 8;
-	uint8_t resp[8];
-	uint8_t req[] = { 0x1, 0x5, 0x0, 0x0 };
-	int i, rc;
-
-	rc = ipmi_transaction(platform->ipmi, IPMI_NETFN_TRANSPORT,
-			IPMI_CMD_TRANSPORT_GET_LAN_PARAMS,
-			req, sizeof(req),
-			resp, &resp_len,
-			ipmi_timeout);
-
-	pb_debug("BMC MAC resp [%d][%d]:\n", rc, resp_len);
-
-	if (rc == 0 && resp_len > 0) {
-		for (i = 2; i < resp_len; i++) {
-		        pb_debug(" %x", resp[i]);
-			buf[i - 2] = resp[i];
-		}
-		pb_debug("\n");
-	}
-
-}
-
 /*
  * Retrieve info from the "Get Device ID" IPMI commands.
  * See Chapter 20.1 in the IPMIv2 specification.
@@ -1212,7 +650,7 @@  static void get_ipmi_network_override(struct platform_powerpc *platform,
 
 	if (!rc && persistent) {
 		/* Write this new config to NVRAM */
-		update_network_config(platform, config);
+		update_network_config(&platform->params, config);
 		rc = write_nvram(platform);
 		if (rc)
 			pb_log("platform: Failed to save persistent interface override\n");
@@ -1261,7 +699,7 @@  static int load_config(struct platform *p, struct config *config)
 	if (rc)
 		pb_log("%s: Failed to parse nvram\n", __func__);
 
-	populate_config(platform, config);
+	populate_config(&platform->params, config);
 
 	if (platform->get_ipmi_bootdev) {
 		bool bootdev_persistent;
@@ -1281,19 +719,11 @@  static int load_config(struct platform *p, struct config *config)
 	return 0;
 }
 
-static int save_config(struct platform *p, struct config *config)
+static int save_config(struct platform *p)
 {
 	struct platform_powerpc *platform = to_platform_powerpc(p);
-	struct config *defaults;
-	int rc;
 
-	defaults = talloc_zero(platform, struct config);
-	config_set_defaults(defaults);
-
-	rc = update_config(platform, config, defaults);
-
-	talloc_free(defaults);
-	return rc;
+	return write_nvram(platform);
 }
 
 static void pre_boot(struct platform *p, const struct config *config)
@@ -1327,7 +757,7 @@  static int get_sysinfo(struct platform *p, struct system_info *sysinfo)
 
 	sysinfo->bmc_mac = talloc_zero_size(sysinfo, HWADDR_SIZE);
 	if (platform->ipmi)
-		get_ipmi_bmc_mac(p, sysinfo->bmc_mac);
+		get_ipmi_bmc_mac(platform->ipmi, sysinfo->bmc_mac, ipmi_timeout);
 
 	if (platform->ipmi)
 		get_ipmi_bmc_versions(p, sysinfo);
@@ -1357,6 +787,7 @@  static bool probe(struct platform *p, void *ctx)
 	list_init(&platform->params);
 
 	p->platform_data = platform;
+	p->get_params_list = get_params_list;
 
 	bmc_present = stat("/proc/device-tree/bmc", &statbuf) == 0;
 
@@ -1381,7 +812,6 @@  static bool probe(struct platform *p, void *ctx)
 	return true;
 }
 
-
 static struct platform platform_powerpc = {
 	.name			= "powerpc",
 	.dhcp_arch_id		= 0x000e,
diff --git a/discover/platform.c b/discover/platform.c
index cc6306f035b9..f8e4646fd3f7 100644
--- a/discover/platform.c
+++ b/discover/platform.c
@@ -3,7 +3,9 @@ 
 
 #include <fcntl.h>
 #include <string.h>
+#include <stdlib.h>
 #include <locale.h>
+#include <limits.h>
 
 #include <log/log.h>
 #include <file/file.h>
@@ -11,11 +13,27 @@ 
 #include <talloc/talloc.h>
 
 #include "platform.h"
+#include <url/url.h>
 
 void			*platform_ctx;
 static struct platform	*platform;
 static struct config	*config;
 
+const char *known_params[] = {
+	"auto-boot?",
+	"petitboot,network",
+	"petitboot,timeout",
+	"petitboot,bootdevs",
+	"petitboot,language",
+	"petitboot,debug?",
+	"petitboot,write?",
+	"petitboot,snapshots?",
+	"petitboot,console",
+	"petitboot,http_proxy",
+	"petitboot,https_proxy",
+	NULL,
+};
+
 static const char *kernel_cmdline_debug = "petitboot.debug";
 
 static void dump_config(struct config *config)
@@ -121,7 +139,6 @@  void config_set_defaults(struct config *config)
 
 	config->autoboot_enabled = true;
 	config->autoboot_timeout_sec = 10;
-	config->autoboot_enabled = true;
 	config->network.interfaces = NULL;
 	config->network.n_interfaces = 0;
 	config->network.dns_servers = NULL;
@@ -158,6 +175,555 @@  void config_set_defaults(struct config *config)
 
 }
 
+static struct list *get_platform_params(struct platform *p)
+{
+	if (p->get_params_list)
+		return p->get_params_list(p);
+
+	return NULL;
+}
+
+static const char *get_param(struct list *platform_params, const char *name)
+{
+	struct params*param;
+
+	list_for_each_entry(platform_params, param, list)
+		if (!strcmp(param->name, name))
+			return param->value;
+	return NULL;
+}
+
+static void set_param(void *ctx, struct list *platform_params,
+		const char *name, const char *value)
+{
+	struct params*param;
+
+	list_for_each_entry(platform_params, param, list) {
+		if (strcmp(param->name, name))
+			continue;
+
+		if (!strcmp(param->value, value))
+			return;
+
+		talloc_free(param->value);
+		param->value = talloc_strdup(param, value);
+		param->modified = true;
+		return;
+	}
+
+	param = talloc(ctx, struct params);
+	param->modified = true;
+	param->name = talloc_strdup(ctx, name);
+	param->value = talloc_strdup(ctx, value);
+	list_add(platform_params, &param->list);
+}
+
+static int parse_hwaddr(struct interface_config *ifconf, char *str)
+{
+	int i;
+
+	if (strlen(str) != strlen("00:00:00:00:00:00"))
+		return -1;
+
+	for (i = 0; i < HWADDR_SIZE; i++) {
+		char byte[3], *endp;
+		unsigned long x;
+
+		byte[0] = str[i * 3 + 0];
+		byte[1] = str[i * 3 + 1];
+		byte[2] = '\0';
+
+		x = strtoul(byte, &endp, 16);
+		if (endp != byte + 2)
+			return -1;
+
+		ifconf->hwaddr[i] = x & 0xff;
+	}
+
+	return 0;
+}
+
+static int parse_one_interface_config(struct config *config, char *confstr)
+{
+	struct interface_config *ifconf;
+	char *tok, *tok_gw, *tok_url, *saveptr;
+
+	ifconf = talloc_zero(config, struct interface_config);
+
+	if (!confstr || !strlen(confstr))
+		goto out_err;
+
+	/* first token should be the mac address */
+	tok = strtok_r(confstr, ",", &saveptr);
+	if (!tok)
+		goto out_err;
+
+	if (parse_hwaddr(ifconf, tok))
+		goto out_err;
+
+	/* second token is the method */
+	tok = strtok_r(NULL, ",", &saveptr);
+	if (!tok || !strlen(tok) || !strcmp(tok, "ignore")) {
+		ifconf->ignore = true;
+
+	} else if (!strcmp(tok, "dhcp")) {
+		ifconf->method = CONFIG_METHOD_DHCP;
+
+	} else if (!strcmp(tok, "static")) {
+		ifconf->method = CONFIG_METHOD_STATIC;
+
+		/* ip/mask, [optional] gateway, [optional] url */
+		tok = strtok_r(NULL, ",", &saveptr);
+		if (!tok)
+			goto out_err;
+		ifconf->static_config.address =
+			talloc_strdup(ifconf, tok);
+
+		/*
+		 * If a url is set but not a gateway, we can accidentally
+		 * interpret the url as the gateway. To avoid changing the
+		 * parameter format check if the "gateway" is actually a
+		 * pb-url if it's the last token.
+		 */
+		tok_gw = strtok_r(NULL, ",", &saveptr);
+		tok_url = strtok_r(NULL, ",", &saveptr);
+
+		if (tok_gw) {
+			if (tok_url || !is_url(tok_gw))
+				ifconf->static_config.gateway =
+					talloc_strdup(ifconf, tok_gw);
+			else
+					tok_url = tok_gw;
+		}
+
+		if (tok_url)
+			ifconf->static_config.url =
+				talloc_strdup(ifconf, tok_url);
+	} else {
+		pb_log("Unknown network configuration method %s\n", tok);
+		goto out_err;
+	}
+
+	config->network.interfaces = talloc_realloc(config,
+			config->network.interfaces,
+			struct interface_config *,
+			++config->network.n_interfaces);
+
+	config->network.interfaces[config->network.n_interfaces - 1] = ifconf;
+
+	return 0;
+out_err:
+	talloc_free(ifconf);
+	return -1;
+}
+
+static int parse_one_dns_config(struct config *config, char *confstr)
+{
+	char *tok, *saveptr = NULL;
+
+	for (tok = strtok_r(confstr, ",", &saveptr); tok;
+			tok = strtok_r(NULL, ",", &saveptr)) {
+
+		char *server = talloc_strdup(config, tok);
+
+		config->network.dns_servers = talloc_realloc(config,
+				config->network.dns_servers, const char *,
+				++config->network.n_dns_servers);
+
+		config->network.dns_servers[config->network.n_dns_servers - 1]
+				= server;
+	}
+
+	return 0;
+}
+
+static void populate_network_config(struct list *platform_params,
+		struct config *config)
+{
+	char *val, *saveptr = NULL;
+	const char *cval;
+	int i;
+
+	cval = get_param(platform_params, "petitboot,network");
+	if (!cval || !strlen(cval))
+		return;
+
+	val = talloc_strdup(config, cval);
+
+	for (i = 0; ; i++) {
+		char *tok;
+
+		tok = strtok_r(i == 0 ? val : NULL, " ", &saveptr);
+		if (!tok)
+			break;
+
+		if (!strncasecmp(tok, "dns,", strlen("dns,")))
+			parse_one_dns_config(config, tok + strlen("dns,"));
+		else
+			parse_one_interface_config(config, tok);
+
+	}
+
+	talloc_free(val);
+}
+
+static int read_bootdev(void *ctx, char **pos, struct autoboot_option *opt)
+{
+	char *delim = strchr(*pos, ' ');
+	int len, prefix = 0, rc = -1;
+	enum device_type type;
+
+	if (!strncmp(*pos, "uuid:", strlen("uuid:"))) {
+		prefix = strlen("uuid:");
+		opt->boot_type = BOOT_DEVICE_UUID;
+	} else if (!strncmp(*pos, "mac:", strlen("mac:"))) {
+		prefix = strlen("mac:");
+		opt->boot_type = BOOT_DEVICE_UUID;
+	} else {
+		type = find_device_type(*pos);
+		if (type != DEVICE_TYPE_UNKNOWN) {
+			opt->type = type;
+			opt->boot_type = BOOT_DEVICE_TYPE;
+			rc = 0;
+		}
+	}
+
+	if (opt->boot_type == BOOT_DEVICE_UUID) {
+		if (delim)
+			len = (int)(delim - *pos) - prefix;
+		else
+			len = strlen(*pos) - prefix;
+
+		if (len) {
+			opt->uuid = talloc_strndup(ctx, *pos + prefix, len);
+			rc = 0;
+		}
+	}
+
+	/* Always advance pointer to next option or end */
+	if (delim)
+		*pos = delim + 1;
+	else
+		*pos += strlen(*pos);
+
+	return rc;
+}
+
+static void populate_bootdev_config(struct list *platform_params,
+		struct config *config)
+{
+	struct autoboot_option *opt, *new = NULL;
+	char *pos, *end;
+	unsigned int n_new = 0;
+	const char *val;
+
+	/* Check for ordered bootdevs */
+	val = get_param(platform_params, "petitboot,bootdevs");
+	if (!val || !strlen(val)) {
+		pos = end = NULL;
+	} else {
+		pos = talloc_strdup(config, val);
+		end = strchr(pos, '\0');
+	}
+
+	while (pos && pos < end) {
+		opt = talloc(config, struct autoboot_option);
+
+		if (read_bootdev(config, &pos, opt)) {
+			pb_log("bootdev config is in an unknown format "
+			       "(expected uuid:... or mac:...)\n");
+			talloc_free(opt);
+			continue;
+		}
+
+		new = talloc_realloc(config, new, struct autoboot_option,
+				     n_new + 1);
+		new[n_new] = *opt;
+		n_new++;
+		talloc_free(opt);
+
+	}
+
+	if (!n_new) {
+		/* If autoboot has been disabled, clear the default options */
+		if (!config->autoboot_enabled) {
+			talloc_free(config->autoboot_opts);
+			config->n_autoboot_opts = 0;
+		}
+		return;
+	}
+
+	talloc_free(config->autoboot_opts);
+	config->autoboot_opts = new;
+	config->n_autoboot_opts = n_new;
+}
+
+void populate_config(struct list *platform_params, struct config *config)
+{
+	const char *val;
+	char *end;
+	unsigned long timeout;
+
+	/* if the "auto-boot?' property is present and "false", disable auto
+	 * boot */
+	val = get_param(platform_params, "auto-boot?");
+	config->autoboot_enabled = !val || strcmp(val, "false");
+
+	val = get_param(platform_params, "petitboot,timeout");
+	if (val) {
+		timeout = strtoul(val, &end, 10);
+		if (end != val) {
+			if (timeout >= INT_MAX)
+				timeout = INT_MAX;
+			config->autoboot_timeout_sec = (int)timeout;
+		}
+	}
+
+	val = get_param(platform_params, "petitboot,language");
+	config->lang = val ? talloc_strdup(config, val) : NULL;
+
+	populate_network_config(platform_params, config);
+
+	populate_bootdev_config(platform_params, config);
+
+	if (!config->debug) {
+		val = get_param(platform_params, "petitboot,debug?");
+		config->debug = val && !strcmp(val, "true");
+	}
+
+	val = get_param(platform_params, "petitboot,write?");
+	if (val)
+		config->allow_writes = !strcmp(val, "true");
+
+	val = get_param(platform_params, "petitboot,snapshots?");
+	if (val)
+		config->disable_snapshots = !strcmp(val, "false");
+
+	val = get_param(platform_params, "petitboot,console");
+	if (val)
+		config->boot_console = talloc_strdup(config, val);
+	/* If a full path is already set we don't want to override it */
+	config->manual_console = config->boot_console &&
+					!strchr(config->boot_console, '[');
+
+	val = get_param(platform_params, "petitboot,http_proxy");
+	if (val)
+		config->http_proxy = talloc_strdup(config, val);
+	val = get_param(platform_params, "petitboot,https_proxy");
+	if (val)
+		config->https_proxy = talloc_strdup(config, val);
+}
+
+static char *iface_config_str(void *ctx, struct interface_config *config)
+{
+	char *str;
+
+	/* todo: HWADDR size is hardcoded as 6, but we may need to handle
+	 * different hardware address formats */
+	str = talloc_asprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x,",
+			config->hwaddr[0], config->hwaddr[1],
+			config->hwaddr[2], config->hwaddr[3],
+			config->hwaddr[4], config->hwaddr[5]);
+
+	if (config->ignore) {
+		str = talloc_asprintf_append(str, "ignore");
+
+	} else if (config->method == CONFIG_METHOD_DHCP) {
+		str = talloc_asprintf_append(str, "dhcp");
+
+	} else if (config->method == CONFIG_METHOD_STATIC) {
+		str = talloc_asprintf_append(str, "static,%s%s%s%s%s",
+				config->static_config.address,
+				config->static_config.gateway ? "," : "",
+				config->static_config.gateway ?: "",
+				config->static_config.url ? "," : "",
+				config->static_config.url ?: "");
+	}
+	return str;
+}
+
+static char *dns_config_str(void *ctx, const char **dns_servers, int n)
+{
+	char *str;
+	int i;
+
+	str = talloc_strdup(ctx, "dns,");
+	for (i = 0; i < n; i++) {
+		str = talloc_asprintf_append(str, "%s%s",
+				i == 0 ? "" : ",",
+				dns_servers[i]);
+	}
+
+	return str;
+}
+
+static void update_string_config(void *ctx, struct list *platform_params,
+		const char *name, const char *value)
+{
+	const char *cur;
+
+	cur = get_param(platform_params, name);
+
+	/* don't set an empty parameter if it doesn't already exist */
+	if (!cur && !strlen(value))
+		return;
+
+	set_param(ctx, platform_params, name, value);
+}
+
+void update_network_config(struct list *platform_params, struct config *config)
+{
+	unsigned int i;
+	char *val;
+
+	/*
+	 * Don't store IPMI overrides to NVRAM. If this was a persistent
+	 * override it was already stored in NVRAM by
+	 * get_ipmi_network_override()
+	 */
+	if (config->network.n_interfaces &&
+		config->network.interfaces[0]->override)
+		return;
+
+	val = talloc_strdup(config, "");
+
+	for (i = 0; i < config->network.n_interfaces; i++) {
+		char *iface_str = iface_config_str(config,
+					config->network.interfaces[i]);
+		val = talloc_asprintf_append(val, "%s%s",
+				*val == '\0' ? "" : " ", iface_str);
+		talloc_free(iface_str);
+	}
+
+	if (config->network.n_dns_servers) {
+		char *dns_str = dns_config_str(config,
+						config->network.dns_servers,
+						config->network.n_dns_servers);
+		val = talloc_asprintf_append(val, "%s%s",
+				*val == '\0' ? "" : " ", dns_str);
+		talloc_free(dns_str);
+	}
+
+	update_string_config(config, platform_params, "petitboot,network", val);
+
+	talloc_free(val);
+}
+
+static void update_bootdev_config(struct list *platform_params,
+		struct config *config)
+{
+	char *val = NULL, *boot_str = NULL, *tmp = NULL;
+	struct autoboot_option *opt;
+	const char delim = ' ';
+	unsigned int i;
+
+	if (!config->n_autoboot_opts)
+		val = "";
+
+	for (i = 0; i < config->n_autoboot_opts; i++) {
+		opt = &config->autoboot_opts[i];
+		switch (opt->boot_type) {
+			case BOOT_DEVICE_TYPE:
+				boot_str = talloc_asprintf(config, "%s%c",
+						device_type_name(opt->type),
+						delim);
+				break;
+			case BOOT_DEVICE_UUID:
+				boot_str = talloc_asprintf(config, "uuid:%s%c",
+						opt->uuid, delim);
+				break;
+			}
+			tmp = val = talloc_asprintf_append(val, "%s", boot_str);
+	}
+
+	update_string_config(config, platform_params, "petitboot,bootdevs", val);
+	talloc_free(tmp);
+	if (boot_str)
+		talloc_free(boot_str);
+}
+
+int update_config(struct platform *p, struct config *config,
+		  struct config *defaults)
+{
+	char *tmp = NULL;
+	const char *val;
+	struct list *platform_params;
+
+	platform_params = get_platform_params(p);
+	if (!platform_params)
+		return -1;
+
+	if (config->autoboot_enabled == defaults->autoboot_enabled)
+		val = "";
+	else
+		val = config->autoboot_enabled ? "true" : "false";
+	update_string_config(config, platform_params, "auto-boot?", val);
+
+	if (config->autoboot_timeout_sec == defaults->autoboot_timeout_sec)
+		val = "";
+	else
+		val = tmp = talloc_asprintf(config, "%d",
+				config->autoboot_timeout_sec);
+
+
+	update_string_config(config, platform_params, "petitboot,timeout", val);
+	if (tmp)
+		talloc_free(tmp);
+
+	val = config->lang ?: "";
+	update_string_config(config, platform_params, "petitboot,language", val);
+
+	if (config->allow_writes == defaults->allow_writes)
+		val = "";
+	else
+		val = config->allow_writes ? "true" : "false";
+	update_string_config(config, platform_params, "petitboot,write?", val);
+
+	if (!config->manual_console) {
+		val = config->boot_console ?: "";
+		update_string_config(config, platform_params, "petitboot,console", val);
+	}
+
+	val = config->http_proxy ?: "";
+	update_string_config(config, platform_params, "petitboot,http_proxy", val);
+	val = config->https_proxy ?: "";
+	update_string_config(config, platform_params, "petitboot,https_proxy", val);
+
+	update_network_config(platform_params, config);
+
+	update_bootdev_config(platform_params, config);
+
+	if(platform->update_config)
+		platform->update_config(p, config);
+
+	return 0;
+}
+
+void get_ipmi_bmc_mac(struct ipmi *ipmi, uint8_t *buf, int ipmi_timeout)
+{
+	uint16_t resp_len = 8;
+	uint8_t resp[8];
+	uint8_t req[] = { 0x1, 0x5, 0x0, 0x0 };
+	int i, rc;
+
+	rc = ipmi_transaction(ipmi, IPMI_NETFN_TRANSPORT,
+			IPMI_CMD_TRANSPORT_GET_LAN_PARAMS,
+			req, sizeof(req),
+			resp, &resp_len,
+			ipmi_timeout);
+
+	pb_debug("BMC MAC resp [%d][%d]:\n", rc, resp_len);
+
+	if (rc == 0 && resp_len > 0) {
+		for (i = 2; i < resp_len; i++) {
+			pb_debug(" %x", resp[i]);
+			buf[i - 2] = resp[i];
+		}
+		pb_debug("\n");
+	}
+
+}
+
 int platform_init(void *ctx)
 {
 	extern struct platform *__start_platforms,  *__stop_platforms;
@@ -209,6 +775,25 @@  int platform_get_sysinfo(struct system_info *info)
 	return -1;
 }
 
+static int save_config(struct platform *platform, struct config *config)
+{
+	struct config *defaults;
+	int rc;
+
+	defaults = talloc_zero(platform_ctx, struct config);
+	config_set_defaults(defaults);
+
+	rc = update_config(platform, config, defaults);
+	if (rc)
+		goto finish;
+
+	rc = platform->save_config(platform);
+
+finish:
+	talloc_free(defaults);
+	return rc;
+}
+
 int config_set(struct config *newconfig)
 {
 	int rc;
@@ -222,7 +807,7 @@  int config_set(struct config *newconfig)
 	pb_log("new configuration data received\n");
 	dump_config(newconfig);
 
-	rc = platform->save_config(platform, newconfig);
+	rc = save_config(platform, newconfig);
 
 	if (!rc)
 		config = talloc_steal(platform_ctx, newconfig);