From patchwork Thu Jan 18 05:05:15 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sam Mendoza-Jonas X-Patchwork-Id: 862752 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 3zMXDM11rBz9t2l for ; Thu, 18 Jan 2018 16:13:39 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=mendozajonas.com header.i=@mendozajonas.com header.b="wIiX5Skh"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.b="mtTAQMZk"; 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 3zMXDL6DSjzF0Sg for ; Thu, 18 Jan 2018 16:13:38 +1100 (AEDT) Authentication-Results: lists.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=mendozajonas.com header.i=@mendozajonas.com header.b="wIiX5Skh"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.b="mtTAQMZk"; dkim-atps=neutral X-Original-To: petitboot@lists.ozlabs.org Delivered-To: petitboot@lists.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=mendozajonas.com (client-ip=66.111.4.25; helo=out1-smtp.messagingengine.com; envelope-from=sam@mendozajonas.com; receiver=) Authentication-Results: lists.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=mendozajonas.com header.i=@mendozajonas.com header.b="wIiX5Skh"; dkim=pass (2048-bit key; unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.b="mtTAQMZk"; dkim-atps=neutral Received: from out1-smtp.messagingengine.com (out1-smtp.messagingengine.com [66.111.4.25]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 3zMXCY6FJbzF0hF for ; Thu, 18 Jan 2018 16:12:57 +1100 (AEDT) Received: from compute2.internal (compute2.nyi.internal [10.202.2.42]) by mailout.nyi.internal (Postfix) with ESMTP id 26A2D20A28; Thu, 18 Jan 2018 00:05:46 -0500 (EST) Received: from frontend2 ([10.202.2.161]) by compute2.internal (MEProxy); Thu, 18 Jan 2018 00:05:46 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= mendozajonas.com; h=cc:date:from:in-reply-to:message-id :references:subject:to:x-me-sender:x-me-sender:x-sasl-enc; s= fm1; bh=grxNmavwST+vu/6saHR576GlJjrLBLDZagK9kjBYnFk=; b=wIiX5Skh SYkKnJvFhSTws+jvpFcGQh/3amG6wVx133CcvL1/yRGzvPb36bAkI77yi3VoH4kt KhnSf3CrJX2Q2kTYEevVLDeUWI7s7HDmMh80TdMsMVesMkVJwV9ScE/F8B1Hnn4Z waRS2znZiQzwS+3xCgFErBy5R1NcYZG3xx33Jj8bJgFl419KfBDiYrWWTFQPu8m9 Uu0BInjmTtIyE57VlIP6irqvFjg8c12pRYJO37mMINnYvsrGgdBXPrHX7C45LaRn +TYyhV4zIyki5tVg7PzTsiSYjEM5Bm8qwLBx2PVRScR0TR9mxD9KNFhUl5airs6o d4/7t0DZkKDKpA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:date:from:in-reply-to:message-id :references:subject:to:x-me-sender:x-me-sender:x-sasl-enc; s= fm1; bh=grxNmavwST+vu/6saHR576GlJjrLBLDZagK9kjBYnFk=; b=mtTAQMZk qmL3g0vh0Hd7qY2b1aORMdXd0KxBaZ/BWqUoKy6HjSaGdFE75J5b3wRLiyk7Wa/g 7EpF2Sy0w/+OpS0YaD6+83f+f4uDJ0W5r8lIaH1m8DxcA/QhD3deN9plYQM2U23c MCsSY9uQkJSIxC/bdE0bR7pi/o1X4g2HS+96vdFJgXEGs2vnMl2NCTdwng3j8mFj x0wB855fIqsvrIjzZWldCR2dL7KudIseBxMb7ltN+kv7DnP5XR2m40tJjLT/u7Va e6cGjONy+GBl8Fgv75Zb06cqcORO0KjdLNcwtFgQApjZtmaYLz5i4YIoADiZSqIK OZQfL1k5hV3FdA== X-ME-Sender: Received: from v4.ozlabs.ibm.com (unknown [122.99.82.10]) by mail.messagingengine.com (Postfix) with ESMTPA id 00124240F8; Thu, 18 Jan 2018 00:05:44 -0500 (EST) From: Samuel Mendoza-Jonas To: petitboot@lists.ozlabs.org Subject: [[RFC PATCH] v2 12/14] discover/platform-powerpc: Platform Firmware Updates Date: Thu, 18 Jan 2018 16:05:15 +1100 Message-Id: <20180118050517.2442-13-sam@mendozajonas.com> X-Mailer: git-send-email 2.15.1 In-Reply-To: <20180118050517.2442-1-sam@mendozajonas.com> References: <20180118050517.2442-1-sam@mendozajonas.com> X-BeenThere: petitboot@lists.ozlabs.org X-Mailman-Version: 2.1.24 Precedence: list List-Id: Petitboot bootloader development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Samuel Mendoza-Jonas MIME-Version: 1.0 Errors-To: petitboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Petitboot" Provide an interface to update the current firmware on POWER platforms. Currently this supports updating only on BMC-based platforms, ie. those where platform firmware is stored in a PNOR device accessible via MTD. The pflash utility is used to safely access MTD and provide progress updates to the user. Platform updates are currently only intended for development use, and explicitly not recommended in production environments. This is primarily because there is no authentication or verification of the update metadata or update source due to the lack of HTTPS and certificate management in existing Petitboot environments (eg. op-build). Platform updates are only enabled if the "petitboot,platform_update?" parameter is set to true, and are by default disabled. Signed-off-by: Samuel Mendoza-Jonas --- discover/Makefile.am | 2 + discover/platform-powerpc-update.c | 210 +++++++++++++++++++++++++++++++++++++ discover/platform-powerpc-update.h | 13 +++ discover/platform-powerpc.c | 60 +++++++++++ discover/platform.c | 19 ++++ discover/platform.h | 12 +++ 6 files changed, 316 insertions(+) create mode 100644 discover/platform-powerpc-update.c create mode 100644 discover/platform-powerpc-update.h diff --git a/discover/Makefile.am b/discover/Makefile.am index 4a6cbd0..a25622c 100644 --- a/discover/Makefile.am +++ b/discover/Makefile.am @@ -76,6 +76,8 @@ discover_pb_discover_CPPFLAGS = \ discover_platform_ro_SOURCES = \ discover/platform.c \ discover/platform.h \ + discover/platform-powerpc-update.c \ + discover/platform-powerpc-update.h \ discover/ipmi.c \ discover/ipmi.h \ discover/dt.c \ diff --git a/discover/platform-powerpc-update.c b/discover/platform-powerpc-update.c new file mode 100644 index 0000000..d257bdd --- /dev/null +++ b/discover/platform-powerpc-update.c @@ -0,0 +1,210 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "platform-powerpc-update.h" + +struct update_data { + void (*status_fn)(void *arg, struct status *status, + enum update_status state); + void *status_arg; + const char *nvram_file; + enum { + PFLASH_UNKNOWN = 0, + PFLASH_ERASE, + PFLASH_WRITE, + } flash_state; +}; + +static int pflash_progress_cb(void *arg) +{ + + const char *pflash_fmt = "\r[%*[= ]] %u%% ETA:%8s\n"; + struct process_info *procinfo = arg; + char *line = NULL; + unsigned int percentage; + struct update_data *data; + struct status status; + struct process *p; + int rc; + char eta[9]; + + if (!arg) + return -1; + + p = procinfo_get_process(procinfo); + data = p->stdout_data; + + rc = process_stdout_custom(procinfo, &line); + + if (rc || !line) + return rc; + + switch (data->flash_state) { + case PFLASH_UNKNOWN: + if (strstr(line, "Erasing")) + data->flash_state = PFLASH_ERASE; + else if (strstr(line, "Programming")) + data->flash_state = PFLASH_WRITE; + break; + case PFLASH_ERASE: + if (strstr(line, "Programming")) + data->flash_state = PFLASH_WRITE; + break; + case PFLASH_WRITE: + break; + } + + rc = sscanf(line, pflash_fmt, &percentage, eta); + if (rc != 2) { + pb_debug("pflash ??: %s\n", line); + talloc_free(line); + return 0; + } + + status.type = STATUS_INFO; + status.message = talloc_asprintf(line, + _("Update Progress: %s %u%%, ETA %s\n"), + data->flash_state == PFLASH_ERASE ? _("erasing") : + data->flash_state == PFLASH_WRITE ? _("writing") : "", + percentage, eta); + status.backlog = false; + data->status_fn(data->status_arg, &status, UPDATE_RUNNING); + pb_debug("pflash cb: %u%%, eta %s\n", percentage, eta); + + talloc_free(line); + return 0; +} + +/* Restore NVRAM contents after update for flash based update */ +static void nvram_restore_cb(struct process *process) +{ + struct update_data *data = process->data; + struct status status; + int rc; + + pb_log("Update complete!\n"); + + if (!process || !process->data) + return; + + status.backlog = false; + + if (process->exit_status) { + pb_log("pflash returned an error - leaving NVRAM alone\n"); + status.type = STATUS_ERROR; + status.message = talloc_asprintf(data, + _("Update failed - check logs for details")); + data->status_fn(data->status_arg, &status, + UPDATE_ERROR); + goto out; + } + + rc = process_run_simple(process, pb_system_apps.pflash, + "-e", "-f", "-p", data->nvram_file, "-P", "NVRAM", + NULL); + if (rc) { + pb_log("Failed to restore NVRAM!\n"); + status.type = STATUS_ERROR; + status.message = talloc_asprintf(data, + _("Failed to restore NVRAM!")); + data->status_fn(data->status_arg, &status, + UPDATE_ERROR); + } else { + status.type = STATUS_INFO; + status.message = talloc_asprintf(data, + _("Update complete! You may now reboot")); + data->status_fn(data->status_arg, &status, + UPDATE_COMPLETED); + } + +out: + talloc_free(process); + talloc_free(data); +} + +int platform_update_flash(void *ctx, struct pb_url *url, const char *file, + void (*status_fn)(void *arg, struct status *status, + enum update_status state), + void *status_arg, + const struct system_info *info) +{ + struct update_data *data; + struct process *process; + (void)info; + int rc; + + /* Only complete PNOR images accepted */ + if (!pb_url_check_extension(url, ".pnor")) { + pb_log("update: Only .pnor files supported\n"); + return -1; + } + + data = talloc_zero(ctx, struct update_data); + if (!data) + return -1; + + data->status_fn = status_fn; + data->status_arg = status_arg; + + /* Preserve NVRAM partition - will be restored in callback */ + data->nvram_file = talloc_asprintf(data, "/tmp/pb-nvram"); + if (!data->nvram_file) { + talloc_free(data); + return -1; + } + + rc = process_run_simple(ctx, pb_system_apps.pflash, + "-r", data->nvram_file, "-P", "NVRAM", NULL); + if (rc) { + pb_log("Failed to preserve NVRAM\n"); + talloc_free(data); + return rc; + } + + /* Perform Update */ + const char *argv[] = { + pb_system_apps.pflash, + "-e", + "-p", + file, + "-f", + NULL + }; + + process = process_create(ctx); + if (!process) { + pb_log("update: Failed to create process\n"); + talloc_free(data); + return -1; + } + + process->path = pb_system_apps.pflash; + process->argv = argv; + process->keep_stdout = true; + process->stdout_cb = pflash_progress_cb; + process->stdout_data = data; + process->exit_cb = nvram_restore_cb; + process->data = data; + rc = process_run_async(process); + + if (rc || process->exit_status) { + pb_log("Error updating firmware\n"); + talloc_free(process); + talloc_free(data); + } + + return rc; +} diff --git a/discover/platform-powerpc-update.h b/discover/platform-powerpc-update.h new file mode 100644 index 0000000..e629cd5 --- /dev/null +++ b/discover/platform-powerpc-update.h @@ -0,0 +1,13 @@ +#ifndef PLATFORM_UPDATE_H +#define PLATFORM_UPDATE_H + +#include "config.h" +#include + +int platform_update_flash(void *ctx, struct pb_url *url, const char *file, + void (*status_fn)(void *arg, struct status *status, + enum update_status state), + void *status_arg, + const struct system_info *info); + +#endif /* PLATFORM_UPDATE_H */ diff --git a/discover/platform-powerpc.c b/discover/platform-powerpc.c index 0e2f87d..4b7ee60 100644 --- a/discover/platform-powerpc.c +++ b/discover/platform-powerpc.c @@ -16,11 +16,17 @@ #include #include #include +#include +#include +#include +#include #include "hostboot.h" #include "platform.h" #include "ipmi.h" #include "dt.h" +#include "device-handler.h" +#include "platform-powerpc-update.h" static const char *partition = "common"; static const char *sysparams_dir = "/sys/firmware/opal/sysparams/"; @@ -47,6 +53,13 @@ struct platform_powerpc { int (*set_os_boot_sensor)( struct platform_powerpc *platform); int (*get_platform_versions)(struct system_info *info); + int (*update)( + void *ctx, struct pb_url *url, const char *file, + void (*status_fn)( + void *arg, struct status *status, + enum update_status state), + void *status_arg, + const struct system_info *info); enum { POWERPC_PLATFORM_FSP, @@ -67,6 +80,8 @@ static const char *known_params[] = { "petitboot,console", "petitboot,http_proxy", "petitboot,https_proxy", + "petitboot,metadata_source", + "petitboot,platform_update?", NULL, }; @@ -553,6 +568,14 @@ static void populate_config(struct platform_powerpc *platform, val = get_param(platform, "petitboot,https_proxy"); if (val) config->https_proxy = talloc_strdup(config, val); + + val = get_param(platform, "petitboot,metadata_source"); + if (val) + config->metadata_source = talloc_strdup(config, val); + + val = get_param(platform, "petitboot,platform_update?"); + if (val) + config->platform_update = !strcmp(val, "true"); } static char *iface_config_str(void *ctx, struct interface_config *config) @@ -733,6 +756,15 @@ static int update_config(struct platform_powerpc *platform, val = config->https_proxy ?: ""; update_string_config(platform, "petitboot,https_proxy", val); + val = config->metadata_source ?: ""; + update_string_config(platform, "petitboot,metadata_source", val); + + if (config->platform_update == defaults->platform_update) + val = ""; + else + val = config->platform_update ? "true" : "false"; + update_string_config(platform, "petitboot,platform_update?", val); + update_network_config(platform, config); update_bootdev_config(platform, config); @@ -1327,8 +1359,15 @@ static void pre_boot(struct platform *p, const struct config *config) static int get_sysinfo(struct platform *p, struct system_info *sysinfo) { struct platform_powerpc *platform = p->platform_data; + struct process *process; char *buf, *filename; int len, rc; + const char *argv[] = { + pb_system_apps.obmc_update, + "check", + sysinfo->bmc_ip, + NULL, + }; filename = talloc_asprintf(platform, "%smodel", devtree_dir); rc = read_file(platform, filename, &buf, &len); @@ -1361,9 +1400,29 @@ static int get_sysinfo(struct platform *p, struct system_info *sysinfo) if (platform->get_platform_versions(sysinfo)) pb_log("Failed to read platform versions\n"); + if (platform->type == POWERPC_PLATFORM_BMC) + platform->update = platform_update_flash; + else + platform->update = NULL; + + sysinfo->update_support = platform->type == POWERPC_PLATFORM_BMC; + return 0; } +// we could just pass the update_data in here directly +static int update_platform(struct platform *p, struct pb_url *url, + const char *file, + void (*status_fn)(void *arg, struct status *status, + enum update_status state), + void *status_arg, const struct system_info *info) +{ + struct platform_powerpc *platform = to_platform_powerpc(p); + + return platform->update(platform, url, file, status_fn, status_arg, + info); +} + static bool probe(struct platform *p, void *ctx) { struct platform_powerpc *platform; @@ -1424,6 +1483,7 @@ static struct platform platform_powerpc = { .save_config = save_config, .pre_boot = pre_boot, .get_sysinfo = get_sysinfo, + .update_platform = update_platform, }; register_platform(platform_powerpc); diff --git a/discover/platform.c b/discover/platform.c index cc6306f..accc62a 100644 --- a/discover/platform.c +++ b/discover/platform.c @@ -94,6 +94,11 @@ static void dump_config(struct config *config) pb_log(" language: %s\n", config->lang ?: ""); + + pb_log(" metadata source: %s\n", config->metadata_source ?: ""); + pb_log(" update source: %s\n", config->update_source ?: ""); + pb_log(" Platform updates allowed: %s\n", + config->platform_update ? "yes" : "no"); } static bool config_debug_on_cmdline(void) @@ -131,6 +136,9 @@ void config_set_defaults(struct config *config) config->safe_mode = false; config->allow_writes = true; config->disable_snapshots = false; + config->metadata_source = NULL; + config->update_source = NULL; + config->platform_update = false; config->n_consoles = 0; config->consoles = NULL; @@ -202,6 +210,17 @@ void platform_pre_boot(void) platform->pre_boot(platform, config); } +int platform_update(struct pb_url *url, const char *file, + void (*status_fn)(void *arg, struct status *status, + enum update_status state), + void *status_arg, const struct system_info *info) +{ + if (platform && platform->update_platform) + return platform->update_platform(platform, url, file, status_fn, + status_arg, info); + return -1; +} + int platform_get_sysinfo(struct system_info *info) { if (platform && platform->get_sysinfo) diff --git a/discover/platform.h b/discover/platform.h index 5aa8e3f..6a3e108 100644 --- a/discover/platform.h +++ b/discover/platform.h @@ -2,6 +2,7 @@ #define PLATFORM_H #include +#include "paths.h" struct platform { const char *name; @@ -11,6 +12,13 @@ struct platform { void (*pre_boot)(struct platform *, const struct config *); int (*get_sysinfo)(struct platform *, struct system_info *); + int (*update_platform)(struct platform *p, + struct pb_url *url, const char *file, + void (*status_fn)(void *arg, + struct status *status, + enum update_status state), + void *status_arg, + const struct system_info *info); uint16_t dhcp_arch_id; void *platform_data; }; @@ -20,6 +28,10 @@ int platform_fini(void); const struct platform *platform_get(void); int platform_get_sysinfo(struct system_info *info); void platform_pre_boot(void); +int platform_update(struct pb_url *url, const char *file, + void (*status_fn)(void *arg, struct status *status, + enum update_status state), + void *status_arg, const struct system_info *info); /* configuration interface */ const struct config *config_get(void);