From patchwork Wed Mar 28 11:29:42 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ge Song X-Patchwork-Id: 892105 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 40B67w5W3lz9s0b for ; Wed, 28 Mar 2018 23:07:24 +1100 (AEDT) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=hxt-semitech.com Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 40B67w4BTpzF2Hf for ; Wed, 28 Mar 2018 23:07:24 +1100 (AEDT) Authentication-Results: lists.ozlabs.org; dmarc=none (p=none dis=none) header.from=hxt-semitech.com X-Original-To: petitboot@lists.ozlabs.org Delivered-To: petitboot@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=hxt-semitech.com (client-ip=223.203.96.7; helo=barracuda.hxt-semitech.com; envelope-from=ge.song@hxt-semitech.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=none (p=none dis=none) header.from=hxt-semitech.com X-Greylist: delayed 1070 seconds by postgrey-1.36 at bilbo; Wed, 28 Mar 2018 23:07:09 AEDT Received: from barracuda.hxt-semitech.com (unknown [223.203.96.7]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 40B67d26NBzF2HJ for ; Wed, 28 Mar 2018 23:07:07 +1100 (AEDT) X-ASG-Debug-ID: 1522237652-093b7e7b3e00930002-2WxHk7 Received: from HXTBJIDCEMVIW02.hxtcorp.net ([10.128.0.15]) by barracuda.hxt-semitech.com with ESMTP id jwTq1jdDBoAU7fOl (version=TLSv1 cipher=ECDHE-RSA-AES256-SHA bits=256 verify=NO) for ; Wed, 28 Mar 2018 19:48:37 +0800 (CST) X-Barracuda-Envelope-From: ge.song@hxt-semitech.com Received: from vbox.hxtcorp.net (10.64.16.123) by HXTBJIDCEMVIW02.hxtcorp.net (10.128.0.15) with Microsoft SMTP Server (TLS) id 15.0.847.32; Wed, 28 Mar 2018 19:47:33 +0800 From: Ge Song To: Subject: [PATCH v1 1/1] Platform: Add support for Arm64 UEFI-based Platform Date: Wed, 28 Mar 2018 19:29:42 +0800 X-ASG-Orig-Subj: [PATCH v1 1/1] Platform: Add support for Arm64 UEFI-based Platform Message-ID: <20180328112942.18883-2-ge.song@hxt-semitech.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20180328112942.18883-1-ge.song@hxt-semitech.com> References: <20180328112942.18883-1-ge.song@hxt-semitech.com> MIME-Version: 1.0 X-Originating-IP: [10.64.16.123] X-ClientProxiedBy: HXTBJIDCEMVIW01.hxtcorp.net (10.128.0.14) To HXTBJIDCEMVIW02.hxtcorp.net (10.128.0.15) X-Barracuda-Connect: UNKNOWN[10.128.0.15] X-Barracuda-Start-Time: 1522237656 X-Barracuda-Encrypted: ECDHE-RSA-AES256-SHA X-Barracuda-URL: https://192.168.50.101:443/cgi-mod/mark.cgi X-Barracuda-BRTS-Status: 1 X-Virus-Scanned: by bsmtpd at hxt-semitech.com X-Barracuda-Bayes: SPAM GLOBAL 1.0000 1.0000 4.3428 X-Barracuda-Spam-Score: 4.34 X-Barracuda-Spam-Status: No, SCORE=4.34 using global scores of TAG_LEVEL=1000.0 QUARANTINE_LEVEL=1000.0 KILL_LEVEL=9.0 tests= X-Barracuda-Spam-Report: Code version 3.2, rules version 3.2.3.49378 Rule breakdown below pts rule name description ---- ---------------------- -------------------------------------------------- X-BeenThere: petitboot@lists.ozlabs.org X-Mailman-Version: 2.1.26 Precedence: list List-Id: Petitboot bootloader development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Ge Song Errors-To: petitboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Petitboot" Signed-off-by: Ge Song --- discover/Makefile.am | 3 +- lib/Makefile.am | 2 + lib/efi/efivar.h | 42 ++ discover/platform-arm64.c | 751 ++++++++++++++++++++ lib/efi/efivar.c | 183 +++++ 5 files changed, 980 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/lib/Makefile.am b/lib/Makefile.am index bb7dfe489132..50fb9d14df49 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -58,6 +58,8 @@ lib_libpbcore_la_SOURCES = \ lib/util/util.h \ lib/flash/config.h \ lib/flash/flash.h \ + lib/efi/efivar.h \ + lib/efi/efivar.c \ $(gpg_int_SOURCES) if ENABLE_MTD diff --git a/lib/efi/efivar.h b/lib/efi/efivar.h new file mode 100644 index 000000000000..b0efcf8aacaf --- /dev/null +++ b/lib/efi/efivar.h @@ -0,0 +1,42 @@ +/* + * Methods to manipulation of EFI variables on UEFI-based platforms + * + * 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. + * Copyright (C) 2018 Ge Song + */ +#ifndef EFIVAR_H +#define EFIVAR_H 1 + +#include + +#define EFI_VARIABLE_NON_VOLATILE 0x00000001 +#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x00000002 +#define EFI_VARIABLE_RUNTIME_ACCESS 0x00000004 +#define EFI_VARIABLE_HARDWARE_ERROR_RECORD 0x00000008 +#define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x00000010 +#define EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS 0x00000020 +#define EFI_VARIABLE_APPEND_WRITE 0x00000040 + +#define EFIVARFS_MAGIC 0xde5e81e4 + +extern char const efivarfs_path[]; +int vars_get_variable(void *ctx, char *guidstr, char *name, + uint8_t **data, size_t *data_size, uint32_t *attributes); +int vars_set_variable(void *ctx, char *guidstr, char *name, + uint8_t *data, size_t data_size, uint32_t attributes); + +#endif /* EFIVAR_H */ diff --git a/discover/platform-arm64.c b/discover/platform-arm64.c new file mode 100644 index 000000000000..4b7204228d95 --- /dev/null +++ b/discover/platform-arm64.c @@ -0,0 +1,751 @@ +/* + * Defines an arm64 platform, based on the powerpc platform. + * + * Actually, this is apply to platforms that adopt UEFI as its underlying + * firmware(x86/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. + * Copyright (C) 2018 Ge Song + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "platform.h" +#include "ipmi.h" +#include "efi/efivar.h" + +#define DEF_ATTR EFI_VARIABLE_NON_VOLATILE | \ + EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS +#define PETITBOOT_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, +}; + +#define to_platform_arm64(p) \ + (struct platform_arm64 *)(p->platform_data) + +static int parse_nvram(struct platform_arm64 *platform) +{ + int i, rc; + size_t size; + uint32_t attr; + uint8_t *data; + char *pb_guid = PETITBOOT_VARS_GUID; + char *known_param; + struct param *param; + + for (i = 0; known_params[i]; i++) { + known_param = known_params[i]; + rc = vars_get_variable(platform, pb_guid, known_param, + &data, &size, &attr); + if (!rc) { + param = talloc(platform, struct param); + param->modified = false; + param->name = talloc_strndup(platform, known_param, + sizeof(known_param)); + param->value = talloc_strdup(platform, (char *)data); + list_add(&platform->params, ¶m->list); + } + } + + return 0; +} + +static int write_nvram(struct platform_arm64 *platform) +{ + struct param *param; + char *pb_guid = PETITBOOT_VARS_GUID; + uint32_t attr = DEF_ATTR; + size_t data_size; + + list_for_each_entry(&platform->params, param, list) { + if (param->modified) { + data_size = sizeof(param->value) + 1; + vars_set_variable(platform, pb_guid, param->name, + (uint8_t *)param->value, data_size, attr); + } + } + + return 0; +} + +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 int 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); + + return 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); + int rc; + + rc = parse_nvram(platform); + if (rc) + pb_log("%s: Failed to parse nvram\n", __func__); + + 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; + int rc; + + defaults = talloc_zero(platform, struct config); + config_set_defaults(defaults); + + rc = update_config(platform, config, defaults); + + talloc_free(defaults); + return rc; +} + +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 = efivarfs_path; + 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); diff --git a/lib/efi/efivar.c b/lib/efi/efivar.c new file mode 100644 index 000000000000..59c2b9fe1cdf --- /dev/null +++ b/lib/efi/efivar.c @@ -0,0 +1,183 @@ +/* + * Methods to manipulation of EFI variables on UEFI-based platforms + * + * The library relies on pesudo file system named "efivarfs". This is the + * new interface to manipulate EFI varibles and was part of the linux + * kernel v3.10 release. + * + * There is also a legacy efivars interface exported via the path + * "firmware/efi/vars/" relative to the mount point of sysfs. Since it has + * some drawbacks and is deprecated, we don't support this interface. + * + * 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. + * Copyright (C) 2018 Ge Song + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "efivar.h" + +char const efivarfs_path[] = "/sys/firmware/efi/efivars/"; + +static int vars_del_variable(void *ctx, char *guidstr, char *name) +{ + int fd, flag, errno_value; + int rc = -1; + char *path; + + path = talloc_asprintf(ctx, "%s%s-%s", + efivarfs_path, name, guidstr); + if (!path) + return ENOMEM; + + fd = open(path, O_RDONLY|O_NONBLOCK); + if (fd == -1) + goto err; + + rc = ioctl (fd, FS_IOC_GETFLAGS, &flag); + if (rc == -1) + goto err; + + flag &= ~FS_IMMUTABLE_FL; + rc = ioctl (fd, FS_IOC_SETFLAGS, &flag); + if (rc == -1) + goto err; + + close(fd); + + rc = unlink(path); + if (rc == -1) + goto err; + +err: + errno_value = errno; + if (fd > 0) + close(fd); + + errno = errno_value; + return rc; +} + +int vars_get_variable(void *ctx, char *guidstr, char *name, + uint8_t **data, size_t *data_size, uint32_t *attributes) +{ + int fd, errno_value; + int rc = -1; + void *p, *buf; + size_t bufsize = 4096; + size_t filesize = 0; + ssize_t sz; + char *path; + + path = talloc_asprintf(ctx, "%s%s-%s", + efivarfs_path, name, guidstr); + if (!path) + return ENOMEM; + + fd = open(path, O_RDONLY); + if (fd < 0) + goto err; + + buf = talloc_size(ctx, bufsize); + if (!buf) + goto err; + + do { + p = buf + filesize; + sz = read(fd, p, bufsize); + if (sz < 0 && errno == EAGAIN) { + continue; + } else if (sz == 0) { + break; + } + filesize += sz; + } while (1); + + *attributes = *(uint32_t *)buf; + *data = (uint8_t *)(buf + sizeof(uint32_t)); + *data_size = strlen(buf + sizeof(uint32_t)); + rc = 0; + +err: + errno_value = errno; + if (fd > 0) + close(fd); + + errno = errno_value; + return rc; +} + +int vars_set_variable(void *ctx, char *guidstr, char *name, + uint8_t *data, size_t data_size, uint32_t attributes) +{ + int fd = -1, errno_value; + int rc = -1; + ssize_t len; + char *path; + void *buf; + size_t bufsize; + mode_t mask = umask(umask(0)); + + path = talloc_asprintf(ctx, "%s%s-%s", + efivarfs_path, name, guidstr); + if (!path) + return ENOMEM; + + if (!access(path, F_OK)) { + rc = vars_del_variable(ctx, guidstr, name); + if (rc < 0) { + goto err; + } + } + + fd = open(path, O_CREAT|O_WRONLY, mask); + if (fd < 0) + goto err; + + bufsize = sizeof(uint32_t) + data_size; + buf = talloc_size(ctx, bufsize); + if (!buf) + goto err; + + *(uint32_t *)buf = attributes; + memcpy(buf + sizeof(uint32_t), data, data_size); + + len = write(fd, buf, bufsize); + if ((size_t)len != bufsize) + goto err; + else + rc = 0; + +err: + errno_value = errno; + if (fd > 0) + close(fd); + + errno = errno_value; + return rc; +}