[[RFC] v2 14/14] discover/platform-powerpc-update: Implement OpenBMC support

Message ID 20180118050517.2442-15-sam@mendozajonas.com
State RFC
Headers show
Series
  • [[RFC] v2 14/14] discover/platform-powerpc-update: Implement OpenBMC support
Related show

Commit Message

Samuel Mendoza-Jonas Jan. 18, 2018, 5:05 a.m.
Add support for firmware updates on POWER platforms whose firmware is
backed by BMCs using Virtual-PNOR (VPNOR). At the moment this is
exclusively POWER9 "Witherspoon" machines. This requires the Host to
have network connectivity to the BMC since there is no in-band method to
update the firmware on these machines.

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 <sam@mendozajonas.com>
---
 discover/platform-powerpc-update.c | 147 ++++++++++++++++++++++++++++++++++++-
 discover/platform-powerpc-update.h |   6 ++
 discover/platform-powerpc.c        |  34 ++++++++-
 3 files changed, 183 insertions(+), 4 deletions(-)

Patch

diff --git a/discover/platform-powerpc-update.c b/discover/platform-powerpc-update.c
index d257bdd..faa72c1 100644
--- a/discover/platform-powerpc-update.c
+++ b/discover/platform-powerpc-update.c
@@ -20,7 +20,10 @@  struct update_data {
 	void		(*status_fn)(void *arg, struct status *status,
 				enum update_status state);
 	void		*status_arg;
-	const char	*nvram_file;
+	union {
+		const char	*nvram_file;
+		const char	*bmc_ip;
+	};
 	enum {
 				PFLASH_UNKNOWN = 0,
 				PFLASH_ERASE,
@@ -88,6 +91,148 @@  static int pflash_progress_cb(void *arg)
 	return 0;
 }
 
+static void rest_update_update_cb(struct process *process)
+{
+	struct update_data *data = process->data;
+	struct status status;
+
+	status.backlog = false;
+	if (process->exit_status) {
+		status.type = STATUS_ERROR;
+		status.message = talloc_asprintf(data,
+				_("Failed to activate image"));
+		data->status_fn(data->status_arg, &status,
+				UPDATE_ERROR);
+		pb_log("Failed to activate BMC image\n");
+	} else {
+		status.type = STATUS_INFO;
+		status.message = talloc_asprintf(data,
+				_("Image activated - update complete!"));
+		data->status_fn(data->status_arg, &status,
+				UPDATE_COMPLETED);
+		pb_log("BMC image activated\n");
+	}
+
+	pb_log("%s\n", process->stdout_buf);
+	talloc_free(data);
+}
+
+static void rest_update_upload_cb(struct process *process)
+{
+	struct update_data *data = process->data;
+	struct process *p;
+	struct status status;
+	char *image_version;
+	int rc;
+
+	if (process->exit_status) {
+		pb_log("REST API Upload Failed!\n");
+		pb_log("%s\n", process->stdout_buf);
+
+		status.backlog = false;
+		status.type = STATUS_ERROR;
+		status.message = talloc_asprintf(process,
+				_("Failed to upload update image"));
+		data->status_fn(data->status_arg, &status,
+				UPDATE_ERROR);
+		goto out;
+	}
+
+	image_version = process->stdout_data;
+	pb_log("Uploaded image to BMC with version '%s'\n", image_version);
+	status.backlog = false;
+	status.type = STATUS_INFO;
+	status.message = talloc_asprintf(process, _("Image Uploaded"));
+	data->status_fn(data->status_arg, &status,
+			UPDATE_RUNNING);
+
+	/* Activate new image */
+	const char *argv[] = {
+		pb_system_apps.obmc_update,
+		"update",
+		image_version,
+		data->bmc_ip,
+		NULL
+	};
+
+	p = process_create(data);
+	if (!p) {
+		pb_log("update: Failed to create p\n");
+		goto out;
+	}
+
+	p->path = pb_system_apps.obmc_update;
+	p->argv = argv;
+	p->keep_stdout = true;
+	p->exit_cb = rest_update_update_cb;
+	p->data = data;
+	rc = process_run_async(p);
+
+	if (rc || p->exit_status) {
+		pb_log("Error updating firmware\n");
+		talloc_free(p);
+	}
+
+out:
+	talloc_free(status.message);
+	talloc_free(process);
+}
+
+int platform_update_rest(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;
+	int rc;
+
+	/* Only compressed squashfs accepted */
+	if (!pb_url_check_extension(url, ".squashfs.tar")) {
+		pb_log("update: Only .squashfs.tar 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;
+	data->bmc_ip = talloc_strdup(data, info->bmc_ip);
+
+	/* Upload new image */
+	const char *argv[] = {
+		pb_system_apps.obmc_update,
+		"upload",
+		file,
+		data->bmc_ip,
+		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.obmc_update;
+	process->argv = argv;
+	process->keep_stdout = true;
+	process->exit_cb = rest_update_upload_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;
+}
+
 /* Restore NVRAM contents after update for flash based update */
 static void nvram_restore_cb(struct process *process)
 {
diff --git a/discover/platform-powerpc-update.h b/discover/platform-powerpc-update.h
index e629cd5..4f25d08 100644
--- a/discover/platform-powerpc-update.h
+++ b/discover/platform-powerpc-update.h
@@ -4,6 +4,12 @@ 
 #include "config.h"
 #include <types/types.h>
 
+int platform_update_rest(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);
+
 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),
diff --git a/discover/platform-powerpc.c b/discover/platform-powerpc.c
index 4b7ee60..1b75512 100644
--- a/discover/platform-powerpc.c
+++ b/discover/platform-powerpc.c
@@ -1356,6 +1356,20 @@  static void pre_boot(struct platform *p, const struct config *config)
 		platform->set_os_boot_sensor(platform);
 }
 
+static void bmc_check_rest_cb(struct process *process)
+{
+	struct platform_powerpc *platform = process->data;
+
+	pb_debug("%s returns with status %d\n", __func__, process->exit_status);
+
+	if (process->exit_status)
+		platform->update = platform_update_flash;
+	else
+		platform->update = platform_update_rest;
+
+	process_release(process);
+}
+
 static int get_sysinfo(struct platform *p, struct system_info *sysinfo)
 {
 	struct platform_powerpc *platform = p->platform_data;
@@ -1400,10 +1414,24 @@  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
+	if (platform->type == POWERPC_PLATFORM_FSP) {
 		platform->update = NULL;
+	} else if (platform->type == POWERPC_PLATFORM_BMC && sysinfo->bmc_ip) {
+		process = process_create(platform);
+		process->path = pb_system_apps.obmc_update;
+		process->argv = argv;
+		process->exit_cb = bmc_check_rest_cb;
+		process->data = platform;
+
+		rc = process_run_async(process);
+		if (rc) {
+			process_release(process);
+			pb_log("Failed to call obmc_update tool\n");
+			/* Assume normal flash */
+			platform->update = platform_update_flash;
+		}
+	} else
+		pb_log("Can not check BMC update method - no IP address\n");
 
 	sysinfo->update_support = platform->type == POWERPC_PLATFORM_BMC;