[PATCH v2 2/2] Platform: Add support for arm64 platform.
Ge Song
songgebird at gmail.com
Mon Apr 9 12:22:02 AEST 2018
Platform: Add a platform file which is suitable for arm64 platform.
Besides, this can applies to x86/x64 platforms which adopt efi as
their underlying firmware.
Signed-off-by: Ge Song <ge.song at hxt-semitech.com>
---
discover/Makefile.am | 3 +-
discover/platform-arm64.c | 744 ++++++++++++++++++++
2 files changed, 746 insertions(+), 1 deletion(-)
diff --git a/discover/Makefile.am b/discover/Makefile.am
index 4a6cbd094737..db26c14e0e46 100644
--- a/discover/Makefile.am
+++ b/discover/Makefile.am
@@ -81,7 +81,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..34588d8ffc1f
--- /dev/null
+++ b/discover/platform-arm64.c
@@ -0,0 +1,744 @@
+/*
+ * 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;
+ 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, ¶m->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, ¶m->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;
+ 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);
--
2.11.0
More information about the Petitboot
mailing list