From patchwork Sat May 26 03:53:58 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ge Song X-Patchwork-Id: 920868 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 40t8Wn5wGPz9s0x for ; Sat, 26 May 2018 13:59:33 +1000 (AEST) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="feFPFusQ"; dkim-atps=neutral Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 40t8Wn45G7zDr10 for ; Sat, 26 May 2018 13:59:33 +1000 (AEST) Authentication-Results: lists.ozlabs.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: lists.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="feFPFusQ"; dkim-atps=neutral X-Original-To: petitboot@lists.ozlabs.org Delivered-To: petitboot@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gmail.com (client-ip=2607:f8b0:400e:c01::242; helo=mail-pl0-x242.google.com; envelope-from=songgebird@gmail.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: lists.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="feFPFusQ"; dkim-atps=neutral Received: from mail-pl0-x242.google.com (mail-pl0-x242.google.com [IPv6:2607:f8b0:400e:c01::242]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 40t8WZ745pzDqH3 for ; Sat, 26 May 2018 13:59:22 +1000 (AEST) Received: by mail-pl0-x242.google.com with SMTP id t12-v6so4198634plo.7 for ; Fri, 25 May 2018 20:59:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=LHn9aJQ0qSoIk91NIZLlLStfrMy19bVT7yywYsjR4L8=; b=feFPFusQ+WSFicxRQ9W/7aaqB7VRi0GcI8lt1CILJTUG4gYoz4EuqcpaGlhN0P0AJk Lf1D1Awz1nkBoH64mh98+EuA1/kC6uTyYxc/+GLAbzGFK41Z1HK3x6k0F6fKYqCr0s6I AMnHuYjWdzU22wqfsR7Dewcl7mJcYiVK3q4KsTfk98VbQmqHTGwWXrvxvpnH8astw+zU fJ0pxLuVQq9TXuxZVCmZEtNtQDP8NhfrwWghrKa0cAdpyYIAy5ZvElhD/ZfZU6B5Qut/ 4HUISCnDga0dePVsqEjSdyVMVF/swgRKwIBSxc9ijyc0/VXdgNGjQRZ8x37gptOmfcRN xC4Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=LHn9aJQ0qSoIk91NIZLlLStfrMy19bVT7yywYsjR4L8=; b=DOFt9RZhg7hoLxBOixtomS8NHx61WspnXmrjB6Aozd0m016gUMUWhpQnLCqjiiP0GW p8KsmMqtRiuO4v3O3/Jzotj4v5TrsQ0ApLOrFNmeUFA0miymN32aVy4xCdfS4NLof+sI 7atovb/CJ/YqpKcuKEbuE1WCnyGrjjrRIugvw6t8g5A4lHK7vKjcMTZyEMKTbqA6yKCz n639dJG60keuAOiViX9FDGRBcLs/Fr81HL2fFiTr/ftcZ3aM0DuroRlDHV5fzBiVJqkj TyV9nHc6re8dBuRW+l3W/HKBeWuKK6TRnUorblWm4Mu12l4QimBdZCQQZmPgk59OwFFb 2fnw== X-Gm-Message-State: ALKqPwdITqTnUo0h/QWZn7Gn0ynmaPpSc2xWIiCVgvlej4iRVr/yKAxO BeMTsABEmkYeRDR/MRao/0Rj9ouu X-Google-Smtp-Source: AB8JxZrVgEOL/Zim37ACOGomW/WhCVn1aFadPxPJwDxoL8fR6j5kP+x/idM9RuwDawdMY8mFwXjTHQ== X-Received: by 2002:a17:902:26a:: with SMTP id 97-v6mr5131252plc.367.1527307160190; Fri, 25 May 2018 20:59:20 -0700 (PDT) Received: from localhost.localdomain ([2002:9922:19f0:0:2c2:c6ff:fe3b:c671]) by smtp.gmail.com with ESMTPSA id v15-v6sm47761679pfa.107.2018.05.25.20.59.11 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 25 May 2018 20:59:19 -0700 (PDT) From: Ge Song X-Google-Original-From: Ge Song To: petitboot@lists.ozlabs.org, sam@mendozajonas.com Subject: [PATCH v4 1/2] Platform: Add a platform file which is suitable for arm64 Platform. Date: Sat, 26 May 2018 11:53:58 +0800 Message-Id: <20180526035359.24024-2-ge.song@hxt-semitech.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20180526035359.24024-1-ge.song@hxt-semitech.com> References: <20180526035359.24024-1-ge.song@hxt-semitech.com> 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 MIME-Version: 1.0 Errors-To: petitboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Petitboot" 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 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 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, ¶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; + + 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; From patchwork Sat May 26 03:53:59 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ge Song X-Patchwork-Id: 920869 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 40t8X65vKTz9s0w for ; Sat, 26 May 2018 13:59:50 +1000 (AEST) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="qrbchBYq"; dkim-atps=neutral Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 40t8X648VtzDr2J for ; Sat, 26 May 2018 13:59:50 +1000 (AEST) Authentication-Results: lists.ozlabs.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: lists.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="qrbchBYq"; dkim-atps=neutral X-Original-To: petitboot@lists.ozlabs.org Delivered-To: petitboot@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gmail.com (client-ip=2607:f8b0:400e:c01::231; helo=mail-pl0-x231.google.com; envelope-from=songgebird@gmail.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: lists.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="qrbchBYq"; dkim-atps=neutral Received: from mail-pl0-x231.google.com (mail-pl0-x231.google.com [IPv6:2607:f8b0:400e:c01::231]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 40t8X125RJzDqH3 for ; Sat, 26 May 2018 13:59:45 +1000 (AEST) Received: by mail-pl0-x231.google.com with SMTP id n10-v6so4202450plp.0 for ; Fri, 25 May 2018 20:59:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=L3FhigGw9+e/2I8vgwwoPbNJZcgPcCr2DmTFxZ9UUzw=; b=qrbchBYq6V0or1M2/0hKrpRgfY94TFgFptaEqveKhXd7L3NwlkqC4Q/Fvhbc1tdZ72 KF9nKi984PUFLx4sM8CLhcKz3U4cQ716jaHePgHd7lCXI5kBbK+sA9nVJLcgE/kwYLfn vKMuqsjIytQsgRLh4v8QlcYw3CFMMxmGdjs+kSztthm756hfnr9RXYcpNggYpnvMwQ1M 8Hc7cZKIz4OAS67RNDn8r/0RPTGwGglcYKYvYOlFdmEooDIVZk62Ll3D4kM1zpI8MPIA KjyfNJcUKly1Hwzld/HRDHBP+Q/Kpzr/Yu1tCu/jLDF2t5PCjsi8WC9lbwonSIYwtFDt g7xg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=L3FhigGw9+e/2I8vgwwoPbNJZcgPcCr2DmTFxZ9UUzw=; b=ACie2w/6hdQfVmFB53hZKaMeQNAjvR9ubcsfrQmqBT1R9+8eO0sIyBglpfjaHgN5dA +vzJB+WOu+YdVjImd+gaJhQ4cJjvD6BQzJAdgiOpjBmg4BY2wcMj52YiT2qTeE6ry3V4 OH7WYLgPOYENiG36aBGk6R5MjlDr2AKSXZtyK/oKX3dZzY+4oCLcWbBYC5cslx+eSxHd EA+jz/qxbeKrq1SsOXjIlZl1guVwVdsO+wR5tSGwHrWlQXTv8YJf90V1UwdzO1BSrOZp D6sxY+rv/pZovnT6sz262j7QJvLoZ5Jjnu7n39EElSTWpIuGxEbRZLHFwOHF1XYKuBPq xflw== X-Gm-Message-State: ALKqPwdDZ8osLMqxqRq4XAOWTGh+VD/EyluBVTwCFg1pY+q9vtQxl4Te AX9AmP1BI8ikxRbANdcxPVLKOOn7 X-Google-Smtp-Source: AB8JxZrIDJMOj5srr8iu9BIal59X82qwaAWNYVroEkVpR5/tcAc1vbG7dHUB/n+G8un3gGmfk3urzg== X-Received: by 2002:a17:902:74cc:: with SMTP id f12-v6mr5106147plt.7.1527307182727; Fri, 25 May 2018 20:59:42 -0700 (PDT) Received: from localhost.localdomain ([2002:9922:19f0:0:2c2:c6ff:fe3b:c671]) by smtp.gmail.com with ESMTPSA id v15-v6sm47761679pfa.107.2018.05.25.20.59.27 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 25 May 2018 20:59:42 -0700 (PDT) From: Ge Song X-Google-Original-From: Ge Song To: petitboot@lists.ozlabs.org, sam@mendozajonas.com Subject: [PATCH v4 2/2] lib: Support to deal with petitboot's configuration on efi-based platforms Date: Sat, 26 May 2018 11:53:59 +0800 Message-Id: <20180526035359.24024-3-ge.song@hxt-semitech.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20180526035359.24024-1-ge.song@hxt-semitech.com> References: <20180526035359.24024-1-ge.song@hxt-semitech.com> 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 MIME-Version: 1.0 Errors-To: petitboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Petitboot" Provide methods to load/store petitboot's configuration on efi-based platforms. A test case is also provided. Signed-off-by: Ge Song --- Since V4: * test-efivar.c: Use a test directory other than the system's efivarfs to complete the test for efivar lib. * lib/efi: Add API to support caller to pass a custom efivarfs path. lib/Makefile.am | 2 + test/lib/Makefile.am | 3 +- lib/efi/efivar.h | 46 +++++ lib/efi/efivar.c | 204 ++++++++++++++++++++ test/lib/test-efivar.c | 127 ++++++++++++ 5 files changed, 381 insertions(+), 1 deletion(-) diff --git a/lib/Makefile.am b/lib/Makefile.am index 8f682021d45a..cfaa0d2cad47 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -59,6 +59,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/test/lib/Makefile.am b/test/lib/Makefile.am index 9636b08d6a6b..22e3ac91499d 100644 --- a/test/lib/Makefile.am +++ b/test/lib/Makefile.am @@ -23,7 +23,8 @@ lib_TESTS = \ test/lib/test-process-parent-stdout \ test/lib/test-process-both \ test/lib/test-process-stdout-eintr \ - test/lib/test-fold + test/lib/test-fold \ + test/lib/test-efivar $(lib_TESTS): LIBS += $(core_lib) diff --git a/lib/efi/efivar.h b/lib/efi/efivar.h new file mode 100644 index 000000000000..058b196e5050 --- /dev/null +++ b/lib/efi/efivar.h @@ -0,0 +1,46 @@ +/* + * 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 + */ +#ifndef EFIVAR_H +#define EFIVAR_H + +#include +#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 + +#ifndef EFIVARFS_MAGIC +#define EFIVARFS_MAGIC 0xde5e81e4 +#endif + +extern const char *efivarfs_path; + +void set_efivarfs_path(const char *path); +const char *get_efivarfs_path(void); +int efi_get_variable(void *ctx, const char *guidstr, const char *name, + uint8_t **data, size_t *data_size, uint32_t *attributes); +int efi_set_variable(void *ctx, const char *guidstr, const char *name, + uint8_t *data, size_t data_size, uint32_t attributes); +int efi_del_variable(void *ctx, const char *guidstr, const char *name); +#endif /* EFIVAR_H */ diff --git a/lib/efi/efivar.c b/lib/efi/efivar.c new file mode 100644 index 000000000000..8e6fce820bfb --- /dev/null +++ b/lib/efi/efivar.c @@ -0,0 +1,204 @@ +/* + * 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. + * Author: Ge Song + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "efivar.h" +#include "talloc/talloc.h" + +const char *efivarfs_path = NULL; + +inline void set_efivarfs_path(const char *path) +{ + efivarfs_path = path; +} + +inline const char *get_efivarfs_path(void) +{ + + return efivarfs_path; +} + +int efi_del_variable(void *ctx, const char *guidstr, + const char *name) +{ + int fd, flag, errno_value; + int rc = -1; + const char *dir; + char *path; + + dir = get_efivarfs_path(); + if (!dir) + return EFAULT; + + path = talloc_asprintf(ctx, "%s%s-%s", dir, 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); + +err: + errno_value = errno; + if (fd > 0) + close(fd); + + errno = errno_value; + return rc; +} + +int efi_get_variable(void *ctx, const char *guidstr, const 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; + const char *dir; + char *path; + + dir = get_efivarfs_path(); + if (!dir) + return EFAULT; + + path = talloc_asprintf(ctx, "%s%s-%s", dir, name, guidstr); + if (!path) + return ENOMEM; + + fd = open(path, O_RDONLY|O_NONBLOCK); + 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 efi_set_variable(void *ctx, const char *guidstr, const char *name, + uint8_t *data, size_t data_size, uint32_t attributes) +{ + int rc = -1, errno_value; + int fd = -1; + ssize_t len; + const char *dir; + char *path; + void *buf; + size_t bufsize; + mode_t mask = 0644; + + dir = get_efivarfs_path(); + if (!dir) + return EFAULT; + + path = talloc_asprintf(ctx, "%s%s-%s", dir, name, guidstr); + if (!path) + return ENOMEM; + + if (!access(path, F_OK)) { + rc = efi_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; +} diff --git a/test/lib/test-efivar.c b/test/lib/test-efivar.c new file mode 100644 index 000000000000..8ceb8f50cae9 --- /dev/null +++ b/test/lib/test-efivar.c @@ -0,0 +1,127 @@ +/* + * 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 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "efi/efivar.h" +#include "talloc/talloc.h" + +#define DEF_ATTR (EFI_VARIABLE_NON_VOLATILE | \ + EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS) + +static const char *test_efivar_guid = "c9c07add-256e-4452-b911-f8d0d35a1ac7"; +static const char *test_varname = "efivartest"; +static const char *test_data = "petitboot"; + +static char* find_efitest_path(void) +{ + static char dir[PATH_MAX] = {0}; + static bool run = false; + char *rest_path = "/efivarfs_data/"; + char *pos = NULL; + + if (run) + return dir; + + readlink("/proc/self/exe", dir, PATH_MAX); + + pos = strrchr(dir, '/'); + *pos = '\0'; + + strcat(dir, rest_path); + run = true; + + return dir; +} + +static bool probe(void) +{ + char *path; + int rc; + + path = find_efitest_path(); + + rc = access(path, F_OK); + if (rc) { + if (errno == ENOENT) { + rc = mkdir(path, 0755); + if(rc) + return false; + } else { + return false; + } + } + + set_efivarfs_path(path); + + return true; +} + +int main(void) +{ + void *ctx = NULL; + int rc, errno_value; + size_t size; + uint8_t *data = NULL; + uint32_t attr = DEF_ATTR; + char *path = NULL; + + if(!probe()) + return ENOENT; + + talloc_new(ctx); + size = strlen(test_data) + 1; + rc = efi_set_variable(ctx, test_efivar_guid, test_varname, + (uint8_t *)test_data, size, attr); + + rc = efi_get_variable(ctx, test_efivar_guid, test_varname, + &data, &size, &attr); + + assert(data != NULL); + rc = strcmp((char *)data, test_data); + if (rc) { + talloc_free(ctx); + assert(0); + } + + rc = efi_del_variable(ctx, test_efivar_guid, test_varname); + + rc = efi_get_variable(ctx, test_efivar_guid, test_varname, + &data, &size, &attr); + + errno_value = errno; + talloc_free(ctx); + + assert(errno_value == ENOENT); + + path = find_efitest_path(); + rmdir(path); + + return EXIT_SUCCESS; +}