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

Ge Song songgebird at gmail.com
Sat May 26 13:53:58 AEST 2018


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 at 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 at 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;
-- 
2.11.0



More information about the Petitboot mailing list