diff mbox series

[v4,1/2] Platform: Add a platform file which is suitable for arm64 Platform.

Message ID 20180526035359.24024-2-ge.song@hxt-semitech.com
State Changes Requested
Headers show
Series Add support for arm64 efi-based platform | expand

Commit Message

Ge Song May 26, 2018, 3:53 a.m. UTC
On 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 on 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 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 +-
 discover/platform-arm64.c | 746 ++++++++++++++++++++
 discover/platform.c       |   1 -
 3 files changed, 748 insertions(+), 2 deletions(-)

Comments

Geoff Levand May 29, 2018, 4:34 p.m. UTC | #1
Hi Ge,

On 05/25/2018 08:53 PM, Ge Song wrote:
> diff --git a/discover/platform-arm64.c b/discover/platform-arm64.c
> new file mode 100644
> index 000000000000..24bf505fe2e3
> --- /dev/null
> +++ b/discover/platform-arm64.c

> +#include "efi/efivar.h"

This patch depends on the efi patch, so the efi patch should be the
first patch [1/2] in your series, and this one the second [2/2].

-Geoff
samjonas May 30, 2018, 12:33 a.m. UTC | #2
On Sat, 2018-05-26 at 11:53 +0800, Ge Song wrote:
> On 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 on 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 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 +-
>  discover/platform-arm64.c | 746 ++++++++++++++++++++
>  discover/platform.c       |   1 -
>  3 files changed, 748 insertions(+), 2 deletions(-)
> 
> 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/discover/platform-arm64.c b/discover/platform-arm64.c
> new file mode 100644
> index 000000000000..24bf505fe2e3
> --- /dev/null
> +++ b/discover/platform-arm64.c
> @@ -0,0 +1,746 @@
> +/*
> + *  Defines an arm64 platform, based on the powerpc 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 <file/file.h>
> +#include <limits.h>
> +#include <list/list.h>
> +#include <log/log.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/fcntl.h>
> +#include <sys/statfs.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +#include <types/types.h>
> +
> +#include "efi/efivar.h"
> +#include "ipmi.h"
> +#include "platform.h"
> +#include "talloc/talloc.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 param {
> +	char *name;
> +	char *value;
> +	bool modified;
> +	struct list_item list;
> +};
> +
> +struct platform_arm64 {
> +	struct list params;
> +	struct ipmi *ipmi;
> +};
> +
> +static const char *known_params[] = {
> +	"auto-boot?",
> +	"network",
> +	"timeout",
> +	"bootdevs",
> +	"language",
> +	"debug?",
> +	"write?",
> +	"snapshots?",
> +	"console",
> +	"http_proxy",
> +	"https_proxy",
> +	NULL,
> +};

Along with what Geoff said, we may as well keep these parameter names the
same as found in platform-powerpc.c. Eg, "petitboot,snapshots?" rather
than just "snapshots?".

> +
> +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 param *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 param);
> +			param->modified = false;
> +			param->name = talloc_strdup(platform, known_param);
> +			param->value = talloc_strdup(platform, (char *)data);
> +			list_add(&platform->params, &param->list);
> +		}
> +	}
> +}
> +
> +static void write_nvram(struct platform_arm64 *platform)
> +{
> +	struct param *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);
> +		}
> +	}
> +}
> +
> +static const char *get_param(struct platform_arm64 *platform,
> +		const char *name)
> +{
> +	struct param *param;
> +
> +	list_for_each_entry(&platform->params, param, list)
> +		if (!strcmp(param->name, name))
> +			return param->value;
> +	return NULL;
> +}
> +
> +static void set_param(struct platform_arm64 *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, *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);
> +
> +		tok = strtok_r(NULL, ",", &saveptr);
> +		if (tok) {
> +			ifconf->static_config.gateway =
> +				talloc_strdup(ifconf, tok);
> +		}
> +
> +		tok = strtok_r(NULL, ",", &saveptr);
> +		if (tok) {
> +			ifconf->static_config.url =
> +				talloc_strdup(ifconf, tok);
> +		}
> +
> +	} 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_arm64 *platform,
> +		struct config *config)
> +{
> +	char *val, *saveptr = NULL;
> +	const char *cval;
> +	int i;
> +
> +	cval = get_param(platform, "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_arm64 *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, "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_arm64 *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, "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, "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, "debug?");
> +		config->debug = val && !strcmp(val, "true");
> +	}
> +
> +	val = get_param(platform, "write?");
> +	if (val)
> +		config->allow_writes = !strcmp(val, "true");
> +
> +	val = get_param(platform, "snapshots?");
> +	if (val)
> +		config->disable_snapshots = !strcmp(val, "false");
> +
> +	val = get_param(platform, "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, "http_proxy");
> +	if (val)
> +		config->http_proxy = talloc_strdup(config, val);
> +	val = get_param(platform, "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_arm64 *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_arm64 *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, "network", val);
> +
> +	talloc_free(val);
> +}
> +
> +static void update_bootdev_config(struct platform_arm64 *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, "bootdevs", val);
> +	talloc_free(tmp);
> +	if (boot_str)
> +		talloc_free(boot_str);
> +}
> +
> +static void update_config(struct platform_arm64 *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);
> +
> +	update_string_config(platform, "timeout", val);
> +	if (tmp)
> +		talloc_free(tmp);
> +
> +	val = config->lang ?: "";
> +	update_string_config(platform, "language", val);
> +
> +	if (config->allow_writes == defaults->allow_writes)
> +		val = "";
> +	else
> +		val = config->allow_writes ? "true" : "false";
> +	update_string_config(platform, "write?", val);
> +
> +	if (!config->manual_console) {
> +		val = config->boot_console ?: "";
> +		update_string_config(platform, "console", val);
> +	}
> +
> +	val = config->http_proxy ?: "";
> +	update_string_config(platform, "http_proxy", val);
> +	val = config->https_proxy ?: "";
> +	update_string_config(platform, "https_proxy", val);
> +
> +	update_network_config(platform, config);
> +
> +	update_bootdev_config(platform, config);
> +
> +	write_nvram(platform);
> +}
> +
> +static void get_ipmi_bmc_mac(struct platform *p, uint8_t *buf)
> +{
> +	struct platform_arm64 *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");
> +	}
> +
> +}
> +
> +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, config);
> +
> +	get_active_consoles(config);
> +
> +	return 0;
> +}
> +
> +static int save_config(struct platform *p, struct config *config)
> +{
> +	struct platform_arm64 *platform = to_platform_arm64(p);
> +	struct config *defaults;
> +
> +	defaults = talloc_zero(platform, struct config);
> +	config_set_defaults(defaults);
> +
> +	update_config(platform, config, defaults);
> +
> +	talloc_free(defaults);
> +
> +	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(p, sysinfo->bmc_mac);
> +
> +	return 0;
> +}
> +
> +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;
> +
> +		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.c b/discover/platform.c
> index cc6306f035b9..bcf6b1141c7a 100644
> --- a/discover/platform.c
> +++ b/discover/platform.c
> @@ -121,7 +121,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;
Ge Song June 5, 2018, 2:25 a.m. UTC | #3
Hi Geoff,

Sorry for late reply, since lots of chip bringup work need to do last 
few days,
New patches correct the issue.

On 05/30/2018 12:34 AM, Geoff Levand wrote:
> Hi Ge,
>
> On 05/25/2018 08:53 PM, Ge Song wrote:
>> diff --git a/discover/platform-arm64.c b/discover/platform-arm64.c
>> new file mode 100644
>> index 000000000000..24bf505fe2e3
>> --- /dev/null
>> +++ b/discover/platform-arm64.c
>> +#include "efi/efivar.h"
> This patch depends on the efi patch, so the efi patch should be the
> first patch [1/2] in your series, and this one the second [2/2].
>
> -Geoff
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  </head>
  <body text="#000000" bgcolor="#FFFFFF">
    <p><font face="Helvetica, Arial, sans-serif">Hi Geoff,</font></p>
    <font face="Helvetica, Arial, sans-serif">Sorry for late reply,
      since lots of chip bringup work need to do last few days,<br>
      New patches correct the issue. </font><br>
    <br>
    <div class="moz-cite-prefix">On 05/30/2018 12:34 AM, Geoff Levand
      wrote:<br>
    </div>
    <blockquote type="cite"
      cite="mid:6e7aa53c-e580-2c83-ad98-f06e91bcb5c6@infradead.org">
      <pre wrap="">Hi Ge,

On 05/25/2018 08:53 PM, Ge Song wrote:
</pre>
      <blockquote type="cite">
        <pre wrap="">diff --git a/discover/platform-arm64.c b/discover/platform-arm64.c
new file mode 100644
index 000000000000..24bf505fe2e3
--- /dev/null
+++ b/discover/platform-arm64.c
</pre>
      </blockquote>
      <pre wrap="">
</pre>
      <blockquote type="cite">
        <pre wrap="">+#include "efi/efivar.h"
</pre>
      </blockquote>
      <pre wrap="">
This patch depends on the efi patch, so the efi patch should be the
first patch [1/2] in your series, and this one the second [2/2].

-Geoff
</pre>
    </blockquote>
    <br>
  </body>
</html>
Ge Song June 5, 2018, 2:31 a.m. UTC | #4
Hi Sam,

Sorry for late reply, since lots of chip bringup work need to do last 
few days.
Next patch has used the same configuration names, besides some 
centralization
work for platform related parts is implemented, hopes it can fulfill the 
demand:)

On 05/30/2018 08:33 AM, Samuel Mendoza-Jonas wrote:
> On Sat, 2018-05-26 at 11:53 +0800, Ge Song wrote:
>> On 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 on 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 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 +-
>>   discover/platform-arm64.c | 746 ++++++++++++++++++++
>>   discover/platform.c       |   1 -
>>   3 files changed, 748 insertions(+), 2 deletions(-)
>>
>> 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/discover/platform-arm64.c b/discover/platform-arm64.c
>> new file mode 100644
>> index 000000000000..24bf505fe2e3
>> --- /dev/null
>> +++ b/discover/platform-arm64.c
>> @@ -0,0 +1,746 @@
>> +/*
>> + *  Defines an arm64 platform, based on the powerpc 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 <file/file.h>
>> +#include <limits.h>
>> +#include <list/list.h>
>> +#include <log/log.h>
>> +#include <stdlib.h>
>> +#include <string.h>
>> +#include <sys/fcntl.h>
>> +#include <sys/statfs.h>
>> +#include <sys/stat.h>
>> +#include <sys/types.h>
>> +#include <types/types.h>
>> +
>> +#include "efi/efivar.h"
>> +#include "ipmi.h"
>> +#include "platform.h"
>> +#include "talloc/talloc.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 param {
>> +	char *name;
>> +	char *value;
>> +	bool modified;
>> +	struct list_item list;
>> +};
>> +
>> +struct platform_arm64 {
>> +	struct list params;
>> +	struct ipmi *ipmi;
>> +};
>> +
>> +static const char *known_params[] = {
>> +	"auto-boot?",
>> +	"network",
>> +	"timeout",
>> +	"bootdevs",
>> +	"language",
>> +	"debug?",
>> +	"write?",
>> +	"snapshots?",
>> +	"console",
>> +	"http_proxy",
>> +	"https_proxy",
>> +	NULL,
>> +};
> Along with what Geoff said, we may as well keep these parameter names the
> same as found in platform-powerpc.c. Eg, "petitboot,snapshots?" rather
> than just "snapshots?".
>
>> +
>> +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 param *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 param);
>> +			param->modified = false;
>> +			param->name = talloc_strdup(platform, known_param);
>> +			param->value = talloc_strdup(platform, (char *)data);
>> +			list_add(&platform->params, &param->list);
>> +		}
>> +	}
>> +}
>> +
>> +static void write_nvram(struct platform_arm64 *platform)
>> +{
>> +	struct param *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);
>> +		}
>> +	}
>> +}
>> +
>> +static const char *get_param(struct platform_arm64 *platform,
>> +		const char *name)
>> +{
>> +	struct param *param;
>> +
>> +	list_for_each_entry(&platform->params, param, list)
>> +		if (!strcmp(param->name, name))
>> +			return param->value;
>> +	return NULL;
>> +}
>> +
>> +static void set_param(struct platform_arm64 *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, *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);
>> +
>> +		tok = strtok_r(NULL, ",", &saveptr);
>> +		if (tok) {
>> +			ifconf->static_config.gateway =
>> +				talloc_strdup(ifconf, tok);
>> +		}
>> +
>> +		tok = strtok_r(NULL, ",", &saveptr);
>> +		if (tok) {
>> +			ifconf->static_config.url =
>> +				talloc_strdup(ifconf, tok);
>> +		}
>> +
>> +	} 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_arm64 *platform,
>> +		struct config *config)
>> +{
>> +	char *val, *saveptr = NULL;
>> +	const char *cval;
>> +	int i;
>> +
>> +	cval = get_param(platform, "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_arm64 *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, "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_arm64 *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, "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, "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, "debug?");
>> +		config->debug = val && !strcmp(val, "true");
>> +	}
>> +
>> +	val = get_param(platform, "write?");
>> +	if (val)
>> +		config->allow_writes = !strcmp(val, "true");
>> +
>> +	val = get_param(platform, "snapshots?");
>> +	if (val)
>> +		config->disable_snapshots = !strcmp(val, "false");
>> +
>> +	val = get_param(platform, "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, "http_proxy");
>> +	if (val)
>> +		config->http_proxy = talloc_strdup(config, val);
>> +	val = get_param(platform, "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_arm64 *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_arm64 *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, "network", val);
>> +
>> +	talloc_free(val);
>> +}
>> +
>> +static void update_bootdev_config(struct platform_arm64 *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, "bootdevs", val);
>> +	talloc_free(tmp);
>> +	if (boot_str)
>> +		talloc_free(boot_str);
>> +}
>> +
>> +static void update_config(struct platform_arm64 *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);
>> +
>> +	update_string_config(platform, "timeout", val);
>> +	if (tmp)
>> +		talloc_free(tmp);
>> +
>> +	val = config->lang ?: "";
>> +	update_string_config(platform, "language", val);
>> +
>> +	if (config->allow_writes == defaults->allow_writes)
>> +		val = "";
>> +	else
>> +		val = config->allow_writes ? "true" : "false";
>> +	update_string_config(platform, "write?", val);
>> +
>> +	if (!config->manual_console) {
>> +		val = config->boot_console ?: "";
>> +		update_string_config(platform, "console", val);
>> +	}
>> +
>> +	val = config->http_proxy ?: "";
>> +	update_string_config(platform, "http_proxy", val);
>> +	val = config->https_proxy ?: "";
>> +	update_string_config(platform, "https_proxy", val);
>> +
>> +	update_network_config(platform, config);
>> +
>> +	update_bootdev_config(platform, config);
>> +
>> +	write_nvram(platform);
>> +}
>> +
>> +static void get_ipmi_bmc_mac(struct platform *p, uint8_t *buf)
>> +{
>> +	struct platform_arm64 *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");
>> +	}
>> +
>> +}
>> +
>> +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, config);
>> +
>> +	get_active_consoles(config);
>> +
>> +	return 0;
>> +}
>> +
>> +static int save_config(struct platform *p, struct config *config)
>> +{
>> +	struct platform_arm64 *platform = to_platform_arm64(p);
>> +	struct config *defaults;
>> +
>> +	defaults = talloc_zero(platform, struct config);
>> +	config_set_defaults(defaults);
>> +
>> +	update_config(platform, config, defaults);
>> +
>> +	talloc_free(defaults);
>> +
>> +	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(p, sysinfo->bmc_mac);
>> +
>> +	return 0;
>> +}
>> +
>> +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;
>> +
>> +		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.c b/discover/platform.c
>> index cc6306f035b9..bcf6b1141c7a 100644
>> --- a/discover/platform.c
>> +++ b/discover/platform.c
>> @@ -121,7 +121,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;
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  </head>
  <body text="#000000" bgcolor="#FFFFFF">
    <p><font face="Helvetica, Arial, sans-serif">Hi Sam,</font></p>
    <font face="Helvetica, Arial, sans-serif">Sorry for late reply,
      since lots of chip bringup work need to do last few days.<br>
      Next patch has used the same configuration names, besides some
      centralization<br>
      work for platform related parts is implemented, hopes it can
      fulfill the demand:)<br>
       </font><br>
    <div class="moz-cite-prefix">On 05/30/2018 08:33 AM, Samuel
      Mendoza-Jonas wrote:<br>
    </div>
    <blockquote type="cite"
cite="mid:31a457718a7ddd4c4d3b8a3843e3eed5e3f2748e.camel@mendozajonas.com">
      <pre wrap="">On Sat, 2018-05-26 at 11:53 +0800, Ge Song wrote:
</pre>
      <blockquote type="cite">
        <pre wrap="">On arm64 servers, plenty of devices(especially for those bootable devices)
cannot be supported from efi firmware. In this situation, linux&amp;petitboot
is the wonderful way to address the issue.

Since efi is the primary firmware on 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 <a class="moz-txt-link-rfc2396E" href="mailto:ge.song@hxt-semitech.com">&lt;ge.song@hxt-semitech.com&gt;</a>
---
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 +-
 discover/platform-arm64.c | 746 ++++++++++++++++++++
 discover/platform.c       |   1 -
 3 files changed, 748 insertions(+), 2 deletions(-)

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/discover/platform-arm64.c b/discover/platform-arm64.c
new file mode 100644
index 000000000000..24bf505fe2e3
--- /dev/null
+++ b/discover/platform-arm64.c
@@ -0,0 +1,746 @@
+/*
+ *  Defines an arm64 platform, based on the powerpc 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 <a class="moz-txt-link-rfc2396E" href="mailto:ge.song@hxt-semitech.com">&lt;ge.song@hxt-semitech.com&gt;</a>
+ */
+#include &lt;errno.h&gt;
+#include &lt;file/file.h&gt;
+#include &lt;limits.h&gt;
+#include &lt;list/list.h&gt;
+#include &lt;log/log.h&gt;
+#include &lt;stdlib.h&gt;
+#include &lt;string.h&gt;
+#include &lt;sys/fcntl.h&gt;
+#include &lt;sys/statfs.h&gt;
+#include &lt;sys/stat.h&gt;
+#include &lt;sys/types.h&gt;
+#include &lt;types/types.h&gt;
+
+#include "efi/efivar.h"
+#include "ipmi.h"
+#include "platform.h"
+#include "talloc/talloc.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 param {
+	char *name;
+	char *value;
+	bool modified;
+	struct list_item list;
+};
+
+struct platform_arm64 {
+	struct list params;
+	struct ipmi *ipmi;
+};
+
+static const char *known_params[] = {
+	"auto-boot?",
+	"network",
+	"timeout",
+	"bootdevs",
+	"language",
+	"debug?",
+	"write?",
+	"snapshots?",
+	"console",
+	"http_proxy",
+	"https_proxy",
+	NULL,
+};
</pre>
      </blockquote>
      <pre wrap="">
Along with what Geoff said, we may as well keep these parameter names the
same as found in platform-powerpc.c. Eg, "petitboot,snapshots?" rather
than just "snapshots?".

</pre>
      <blockquote type="cite">
        <pre wrap="">+
+static inline struct platform_arm64 *to_platform_arm64(struct platform *p)
+{
+	return (struct platform_arm64 *)(p-&gt;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 param *param;
+
+	for (i = 0; known_params[i]; i++) {
+		known_param = known_params[i];
+		rc = efi_get_variable(platform, pb_guid, known_param,
+				&amp;data, &amp;size, &amp;attr);
+		if (!rc) {
+			param = talloc(platform, struct param);
+			param-&gt;modified = false;
+			param-&gt;name = talloc_strdup(platform, known_param);
+			param-&gt;value = talloc_strdup(platform, (char *)data);
+			list_add(&amp;platform-&gt;params, &amp;param-&gt;list);
+		}
+	}
+}
+
+static void write_nvram(struct platform_arm64 *platform)
+{
+	struct param *param;
+	const char *pb_guid = pb_vars_guid;
+	uint32_t attr = DEF_ATTR;
+	size_t data_size;
+
+	list_for_each_entry(&amp;platform-&gt;params, param, list) {
+		if (param-&gt;modified) {
+			data_size = strlen(param-&gt;value) + 1;
+			efi_set_variable(platform, pb_guid, param-&gt;name,
+				(uint8_t *)param-&gt;value, data_size, attr);
+		}
+	}
+}
+
+static const char *get_param(struct platform_arm64 *platform,
+		const char *name)
+{
+	struct param *param;
+
+	list_for_each_entry(&amp;platform-&gt;params, param, list)
+		if (!strcmp(param-&gt;name, name))
+			return param-&gt;value;
+	return NULL;
+}
+
+static void set_param(struct platform_arm64 *platform, const char *name,
+		const char *value)
+{
+	struct param *param;
+
+	list_for_each_entry(&amp;platform-&gt;params, param, list) {
+		if (strcmp(param-&gt;name, name))
+			continue;
+
+		if (!strcmp(param-&gt;value, value))
+			return;
+
+		talloc_free(param-&gt;value);
+		param-&gt;value = talloc_strdup(param, value);
+		param-&gt;modified = true;
+		return;
+	}
+
+	param = talloc(platform, struct param);
+	param-&gt;modified = true;
+	param-&gt;name = talloc_strdup(platform, name);
+	param-&gt;value = talloc_strdup(platform, value);
+	list_add(&amp;platform-&gt;params, &amp;param-&gt;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 &lt; 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, &amp;endp, 16);
+		if (endp != byte + 2)
+			return -1;
+
+		ifconf-&gt;hwaddr[i] = x &amp; 0xff;
+	}
+
+	return 0;
+}
+
+static int parse_one_interface_config(struct config *config,
+		char *confstr)
+{
+	struct interface_config *ifconf;
+	char *tok, *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, ",", &amp;saveptr);
+	if (!tok)
+		goto out_err;
+
+	if (parse_hwaddr(ifconf, tok))
+		goto out_err;
+
+	/* second token is the method */
+	tok = strtok_r(NULL, ",", &amp;saveptr);
+	if (!tok || !strlen(tok) || !strcmp(tok, "ignore")) {
+		ifconf-&gt;ignore = true;
+
+	} else if (!strcmp(tok, "dhcp")) {
+		ifconf-&gt;method = CONFIG_METHOD_DHCP;
+
+	} else if (!strcmp(tok, "static")) {
+		ifconf-&gt;method = CONFIG_METHOD_STATIC;
+
+		/* ip/mask, [optional] gateway, [optional] url */
+		tok = strtok_r(NULL, ",", &amp;saveptr);
+		if (!tok)
+			goto out_err;
+		ifconf-&gt;static_config.address =
+			talloc_strdup(ifconf, tok);
+
+		tok = strtok_r(NULL, ",", &amp;saveptr);
+		if (tok) {
+			ifconf-&gt;static_config.gateway =
+				talloc_strdup(ifconf, tok);
+		}
+
+		tok = strtok_r(NULL, ",", &amp;saveptr);
+		if (tok) {
+			ifconf-&gt;static_config.url =
+				talloc_strdup(ifconf, tok);
+		}
+
+	} else {
+		pb_log("Unknown network configuration method %s\n", tok);
+		goto out_err;
+	}
+
+	config-&gt;network.interfaces = talloc_realloc(config,
+			config-&gt;network.interfaces,
+			struct interface_config *,
+			++config-&gt;network.n_interfaces);
+
+	config-&gt;network.interfaces[config-&gt;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, ",", &amp;saveptr); tok;
+			tok = strtok_r(NULL, ",", &amp;saveptr)) {
+
+		char *server = talloc_strdup(config, tok);
+
+		config-&gt;network.dns_servers = talloc_realloc(config,
+				config-&gt;network.dns_servers, const char *,
+				++config-&gt;network.n_dns_servers);
+
+		config-&gt;network.dns_servers[config-&gt;network.n_dns_servers - 1]
+				= server;
+	}
+
+	return 0;
+}
+
+static void populate_network_config(struct platform_arm64 *platform,
+		struct config *config)
+{
+	char *val, *saveptr = NULL;
+	const char *cval;
+	int i;
+
+	cval = get_param(platform, "network");
+	if (!cval || !strlen(cval))
+		return;
+
+	val = talloc_strdup(config, cval);
+
+	for (i = 0; ; i++) {
+		char *tok;
+
+		tok = strtok_r(i == 0 ? val : NULL, " ", &amp;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-&gt;boot_type = BOOT_DEVICE_UUID;
+	} else if (!strncmp(*pos, "mac:", strlen("mac:"))) {
+		prefix = strlen("mac:");
+		opt-&gt;boot_type = BOOT_DEVICE_UUID;
+	} else {
+		type = find_device_type(*pos);
+		if (type != DEVICE_TYPE_UNKNOWN) {
+			opt-&gt;type = type;
+			opt-&gt;boot_type = BOOT_DEVICE_TYPE;
+			rc = 0;
+		}
+	}
+
+	if (opt-&gt;boot_type == BOOT_DEVICE_UUID) {
+		if (delim)
+			len = (int)(delim - *pos) - prefix;
+		else
+			len = strlen(*pos) - prefix;
+
+		if (len) {
+			opt-&gt;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_arm64 *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, "bootdevs");
+	if (!val || !strlen(val)) {
+		pos = end = NULL;
+	} else {
+		pos = talloc_strdup(config, val);
+		end = strchr(pos, '\0');
+	}
+
+	while (pos &amp;&amp; pos &lt; end) {
+		opt = talloc(config, struct autoboot_option);
+
+		if (read_bootdev(config, &amp;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-&gt;autoboot_enabled) {
+			talloc_free(config-&gt;autoboot_opts);
+			config-&gt;n_autoboot_opts = 0;
+		}
+		return;
+	}
+
+	talloc_free(config-&gt;autoboot_opts);
+	config-&gt;autoboot_opts = new;
+	config-&gt;n_autoboot_opts = n_new;
+}
+
+static void populate_config(struct platform_arm64 *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-&gt;autoboot_enabled = !val || strcmp(val, "false");
+
+	val = get_param(platform, "timeout");
+	if (val) {
+		timeout = strtoul(val, &amp;end, 10);
+		if (end != val) {
+			if (timeout &gt;= INT_MAX)
+				timeout = INT_MAX;
+			config-&gt;autoboot_timeout_sec = (int)timeout;
+		}
+	}
+
+	val = get_param(platform, "language");
+	config-&gt;lang = val ? talloc_strdup(config, val) : NULL;
+
+	populate_network_config(platform, config);
+
+	populate_bootdev_config(platform, config);
+
+	if (!config-&gt;debug) {
+		val = get_param(platform, "debug?");
+		config-&gt;debug = val &amp;&amp; !strcmp(val, "true");
+	}
+
+	val = get_param(platform, "write?");
+	if (val)
+		config-&gt;allow_writes = !strcmp(val, "true");
+
+	val = get_param(platform, "snapshots?");
+	if (val)
+		config-&gt;disable_snapshots = !strcmp(val, "false");
+
+	val = get_param(platform, "console");
+	if (val)
+		config-&gt;boot_console = talloc_strdup(config, val);
+	/* If a full path is already set we don't want to override it */
+	config-&gt;manual_console = config-&gt;boot_console &amp;&amp;
+					!strchr(config-&gt;boot_console, '[');
+
+	val = get_param(platform, "http_proxy");
+	if (val)
+		config-&gt;http_proxy = talloc_strdup(config, val);
+	val = get_param(platform, "https_proxy");
+	if (val)
+		config-&gt;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-&gt;hwaddr[0], config-&gt;hwaddr[1],
+			config-&gt;hwaddr[2], config-&gt;hwaddr[3],
+			config-&gt;hwaddr[4], config-&gt;hwaddr[5]);
+
+	if (config-&gt;ignore) {
+		str = talloc_asprintf_append(str, "ignore");
+
+	} else if (config-&gt;method == CONFIG_METHOD_DHCP) {
+		str = talloc_asprintf_append(str, "dhcp");
+
+	} else if (config-&gt;method == CONFIG_METHOD_STATIC) {
+		str = talloc_asprintf_append(str, "static,%s%s%s%s%s",
+				config-&gt;static_config.address,
+				config-&gt;static_config.gateway ? "," : "",
+				config-&gt;static_config.gateway ?: "",
+				config-&gt;static_config.url ? "," : "",
+				config-&gt;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 &lt; n; i++) {
+		str = talloc_asprintf_append(str, "%s%s",
+				i == 0 ? "" : ",",
+				dns_servers[i]);
+	}
+
+	return str;
+}
+
+static void update_string_config(struct platform_arm64 *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 &amp;&amp; !strlen(value))
+		return;
+
+	set_param(platform, name, value);
+}
+
+static void update_network_config(struct platform_arm64 *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-&gt;network.n_interfaces &amp;&amp;
+		config-&gt;network.interfaces[0]-&gt;override)
+		return;
+
+	val = talloc_strdup(platform, "");
+
+	for (i = 0; i &lt; config-&gt;network.n_interfaces; i++) {
+		char *iface_str = iface_config_str(platform,
+					config-&gt;network.interfaces[i]);
+		val = talloc_asprintf_append(val, "%s%s",
+				*val == '\0' ? "" : " ", iface_str);
+		talloc_free(iface_str);
+	}
+
+	if (config-&gt;network.n_dns_servers) {
+		char *dns_str = dns_config_str(platform,
+						config-&gt;network.dns_servers,
+						config-&gt;network.n_dns_servers);
+		val = talloc_asprintf_append(val, "%s%s",
+				*val == '\0' ? "" : " ", dns_str);
+		talloc_free(dns_str);
+	}
+
+	update_string_config(platform, "network", val);
+
+	talloc_free(val);
+}
+
+static void update_bootdev_config(struct platform_arm64 *platform,
+		struct config *config)
+{
+	char *val = NULL, *boot_str = NULL, *tmp = NULL;
+	struct autoboot_option *opt;
+	const char delim = ' ';
+	unsigned int i;
+
+	if (!config-&gt;n_autoboot_opts)
+		val = "";
+
+	for (i = 0; i &lt; config-&gt;n_autoboot_opts; i++) {
+		opt = &amp;config-&gt;autoboot_opts[i];
+		switch (opt-&gt;boot_type) {
+			case BOOT_DEVICE_TYPE:
+				boot_str = talloc_asprintf(config, "%s%c",
+						device_type_name(opt-&gt;type),
+						delim);
+				break;
+			case BOOT_DEVICE_UUID:
+				boot_str = talloc_asprintf(config, "uuid:%s%c",
+						opt-&gt;uuid, delim);
+				break;
+			}
+			tmp = val = talloc_asprintf_append(val, "%s", boot_str);
+	}
+
+	update_string_config(platform, "bootdevs", val);
+	talloc_free(tmp);
+	if (boot_str)
+		talloc_free(boot_str);
+}
+
+static void update_config(struct platform_arm64 *platform,
+		struct config *config, struct config *defaults)
+{
+	char *tmp = NULL;
+	const char *val;
+
+	if (config-&gt;autoboot_enabled == defaults-&gt;autoboot_enabled)
+		val = "";
+	else
+		val = config-&gt;autoboot_enabled ? "true" : "false";
+	update_string_config(platform, "auto-boot?", val);
+
+	if (config-&gt;autoboot_timeout_sec == defaults-&gt;autoboot_timeout_sec)
+		val = "";
+	else
+		val = tmp = talloc_asprintf(platform, "%d",
+				config-&gt;autoboot_timeout_sec);
+
+	update_string_config(platform, "timeout", val);
+	if (tmp)
+		talloc_free(tmp);
+
+	val = config-&gt;lang ?: "";
+	update_string_config(platform, "language", val);
+
+	if (config-&gt;allow_writes == defaults-&gt;allow_writes)
+		val = "";
+	else
+		val = config-&gt;allow_writes ? "true" : "false";
+	update_string_config(platform, "write?", val);
+
+	if (!config-&gt;manual_console) {
+		val = config-&gt;boot_console ?: "";
+		update_string_config(platform, "console", val);
+	}
+
+	val = config-&gt;http_proxy ?: "";
+	update_string_config(platform, "http_proxy", val);
+	val = config-&gt;https_proxy ?: "";
+	update_string_config(platform, "https_proxy", val);
+
+	update_network_config(platform, config);
+
+	update_bootdev_config(platform, config);
+
+	write_nvram(platform);
+}
+
+static void get_ipmi_bmc_mac(struct platform *p, uint8_t *buf)
+{
+	struct platform_arm64 *platform = p-&gt;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-&gt;ipmi, IPMI_NETFN_TRANSPORT,
+			IPMI_CMD_TRANSPORT_GET_LAN_PARAMS,
+			req, sizeof(req),
+			resp, &amp;resp_len,
+			ipmi_timeout);
+
+	pb_debug("BMC MAC resp [%d][%d]:\n", rc, resp_len);
+
+	if (rc == 0 &amp;&amp; resp_len &gt; 0) {
+		for (i = 2; i &lt; resp_len; i++) {
+			pb_debug(" %x", resp[i]);
+			buf[i - 2] = resp[i];
+		}
+		pb_debug("\n");
+	}
+
+}
+
+static void get_active_consoles(struct config *config)
+{
+	config-&gt;n_consoles = 3;
+	config-&gt;consoles = talloc_array(config, char *, config-&gt;n_consoles);
+	if (!config-&gt;consoles)
+		goto err;
+
+	config-&gt;consoles[0] = talloc_asprintf(config-&gt;consoles,
+					"/dev/hvc0 [IPMI / Serial]");
+	config-&gt;consoles[1] = talloc_asprintf(config-&gt;consoles,
+					"/dev/ttyAMA0 [Serial]");
+	config-&gt;consoles[2] = talloc_asprintf(config-&gt;consoles,
+					"/dev/tty1 [VGA]");
+
+	return;
+err:
+	config-&gt;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, config);
+
+	get_active_consoles(config);
+
+	return 0;
+}
+
+static int save_config(struct platform *p, struct config *config)
+{
+	struct platform_arm64 *platform = to_platform_arm64(p);
+	struct config *defaults;
+
+	defaults = talloc_zero(platform, struct config);
+	config_set_defaults(defaults);
+
+	update_config(platform, config, defaults);
+
+	talloc_free(defaults);
+
+	return 0;
+}
+
+static int get_sysinfo(struct platform *p, struct system_info *sysinfo)
+{
+	struct platform_arm64 *platform = p-&gt;platform_data;
+
+	sysinfo-&gt;bmc_mac = talloc_zero_size(sysinfo, HWADDR_SIZE);
+
+	if (platform-&gt;ipmi)
+		get_ipmi_bmc_mac(p, sysinfo-&gt;bmc_mac);
+
+	return 0;
+}
+
+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(&amp;buf, '\0', sizeof(buf));
+	rc = statfs(path, &amp;buf);
+	if (rc)
+		return false;
+
+	typeof(buf.f_type) magic = EFIVARFS_MAGIC;
+	if (!memcmp(&amp;buf.f_type, &amp;magic, sizeof (magic))) {
+		platform = talloc_zero(ctx, struct platform_arm64);
+		list_init(&amp;platform-&gt;params);
+		p-&gt;platform_data = platform;
+
+		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.c b/discover/platform.c
index cc6306f035b9..bcf6b1141c7a 100644
--- a/discover/platform.c
+++ b/discover/platform.c
@@ -121,7 +121,6 @@ void config_set_defaults(struct config *config)
 
 	config-&gt;autoboot_enabled = true;
 	config-&gt;autoboot_timeout_sec = 10;
-	config-&gt;autoboot_enabled = true;
 	config-&gt;network.interfaces = NULL;
 	config-&gt;network.n_interfaces = 0;
 	config-&gt;network.dns_servers = NULL;
</pre>
      </blockquote>
      <pre wrap="">
</pre>
    </blockquote>
    <br>
  </body>
</html>
diff mbox series

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/discover/platform-arm64.c b/discover/platform-arm64.c
new file mode 100644
index 000000000000..24bf505fe2e3
--- /dev/null
+++ b/discover/platform-arm64.c
@@ -0,0 +1,746 @@ 
+/*
+ *  Defines an arm64 platform, based on the powerpc 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 <file/file.h>
+#include <limits.h>
+#include <list/list.h>
+#include <log/log.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/fcntl.h>
+#include <sys/statfs.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <types/types.h>
+
+#include "efi/efivar.h"
+#include "ipmi.h"
+#include "platform.h"
+#include "talloc/talloc.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 param {
+	char *name;
+	char *value;
+	bool modified;
+	struct list_item list;
+};
+
+struct platform_arm64 {
+	struct list params;
+	struct ipmi *ipmi;
+};
+
+static const char *known_params[] = {
+	"auto-boot?",
+	"network",
+	"timeout",
+	"bootdevs",
+	"language",
+	"debug?",
+	"write?",
+	"snapshots?",
+	"console",
+	"http_proxy",
+	"https_proxy",
+	NULL,
+};
+
+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 param *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 param);
+			param->modified = false;
+			param->name = talloc_strdup(platform, known_param);
+			param->value = talloc_strdup(platform, (char *)data);
+			list_add(&platform->params, &param->list);
+		}
+	}
+}
+
+static void write_nvram(struct platform_arm64 *platform)
+{
+	struct param *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);
+		}
+	}
+}
+
+static const char *get_param(struct platform_arm64 *platform,
+		const char *name)
+{
+	struct param *param;
+
+	list_for_each_entry(&platform->params, param, list)
+		if (!strcmp(param->name, name))
+			return param->value;
+	return NULL;
+}
+
+static void set_param(struct platform_arm64 *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, *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);
+
+		tok = strtok_r(NULL, ",", &saveptr);
+		if (tok) {
+			ifconf->static_config.gateway =
+				talloc_strdup(ifconf, tok);
+		}
+
+		tok = strtok_r(NULL, ",", &saveptr);
+		if (tok) {
+			ifconf->static_config.url =
+				talloc_strdup(ifconf, tok);
+		}
+
+	} 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_arm64 *platform,
+		struct config *config)
+{
+	char *val, *saveptr = NULL;
+	const char *cval;
+	int i;
+
+	cval = get_param(platform, "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_arm64 *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, "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_arm64 *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, "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, "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, "debug?");
+		config->debug = val && !strcmp(val, "true");
+	}
+
+	val = get_param(platform, "write?");
+	if (val)
+		config->allow_writes = !strcmp(val, "true");
+
+	val = get_param(platform, "snapshots?");
+	if (val)
+		config->disable_snapshots = !strcmp(val, "false");
+
+	val = get_param(platform, "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, "http_proxy");
+	if (val)
+		config->http_proxy = talloc_strdup(config, val);
+	val = get_param(platform, "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_arm64 *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_arm64 *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, "network", val);
+
+	talloc_free(val);
+}
+
+static void update_bootdev_config(struct platform_arm64 *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, "bootdevs", val);
+	talloc_free(tmp);
+	if (boot_str)
+		talloc_free(boot_str);
+}
+
+static void update_config(struct platform_arm64 *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);
+
+	update_string_config(platform, "timeout", val);
+	if (tmp)
+		talloc_free(tmp);
+
+	val = config->lang ?: "";
+	update_string_config(platform, "language", val);
+
+	if (config->allow_writes == defaults->allow_writes)
+		val = "";
+	else
+		val = config->allow_writes ? "true" : "false";
+	update_string_config(platform, "write?", val);
+
+	if (!config->manual_console) {
+		val = config->boot_console ?: "";
+		update_string_config(platform, "console", val);
+	}
+
+	val = config->http_proxy ?: "";
+	update_string_config(platform, "http_proxy", val);
+	val = config->https_proxy ?: "";
+	update_string_config(platform, "https_proxy", val);
+
+	update_network_config(platform, config);
+
+	update_bootdev_config(platform, config);
+
+	write_nvram(platform);
+}
+
+static void get_ipmi_bmc_mac(struct platform *p, uint8_t *buf)
+{
+	struct platform_arm64 *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");
+	}
+
+}
+
+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, config);
+
+	get_active_consoles(config);
+
+	return 0;
+}
+
+static int save_config(struct platform *p, struct config *config)
+{
+	struct platform_arm64 *platform = to_platform_arm64(p);
+	struct config *defaults;
+
+	defaults = talloc_zero(platform, struct config);
+	config_set_defaults(defaults);
+
+	update_config(platform, config, defaults);
+
+	talloc_free(defaults);
+
+	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(p, sysinfo->bmc_mac);
+
+	return 0;
+}
+
+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;
+
+		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.c b/discover/platform.c
index cc6306f035b9..bcf6b1141c7a 100644
--- a/discover/platform.c
+++ b/discover/platform.c
@@ -121,7 +121,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;