diff mbox series

[v2,4/4] discover: Check if the kernel image has Ultravisor support

Message ID 20190923223024.995-5-maxiwell@linux.ibm.com
State Under Review
Headers show
Series Check if the kernel image has Ultravisor support | expand

Commit Message

Maxiwell S. Garcia Sept. 23, 2019, 10:30 p.m. UTC
The PPC kernel image has an ELF Note 'namespace' called 'PowerPC'
to store capabilities and information which can be used by a
bootloader or userland. The capabilities can be accessed using
the 'type' PPC_ELFNOTE_CAPABILITIES which returns a bitmap
as 'descriptor' field.

Bit 0 in this bitmap indicates that the powerpc kernel binary
knows how to run in an ultravisor-enabled system. So, using this
bit, the petitboot can decide to abort the boot if the kernel is
incompatible, avoiding the crash later.

This validation only occours on PowerPC ultravisor-system and if
the config 'preboot check' in UI screen is enabled.

Signed-off-by: Maxiwell S. Garcia <maxiwell@linux.ibm.com>
---
 discover/boot.c             | 21 ++++++++++++++++++
 discover/platform-powerpc.c | 43 +++++++++++++++++++++++++++++++++++++
 discover/platform.c         | 11 ++++++++++
 discover/platform.h         |  5 +++++
 4 files changed, 80 insertions(+)
diff mbox series

Patch

diff --git a/discover/boot.c b/discover/boot.c
index 91fc46d..9963498 100644
--- a/discover/boot.c
+++ b/discover/boot.c
@@ -401,6 +401,24 @@  static void cleanup_cancellations(struct boot_task *task,
 		talloc_free(task);
 }
 
+static bool preboot_check(struct boot_task *task)
+{
+	const char *local_image = (task->local_image_override) ?
+		task->local_image_override : task->local_image;
+
+	char *preboot_check_err_msg = NULL;
+	bool preboot_check_ret = platform_preboot_check(local_image,
+						&preboot_check_err_msg);
+
+	if (preboot_check_err_msg) {
+		update_status(task->status_fn, task->status_arg,
+				STATUS_ERROR, "%s", preboot_check_err_msg);
+		talloc_free(preboot_check_err_msg);
+	}
+
+	return preboot_check_ret;
+}
+
 static void boot_process(struct load_url_result *result, void *data)
 {
 	struct boot_task *task = data;
@@ -424,6 +442,9 @@  static void boot_process(struct load_url_result *result, void *data)
 
 	run_boot_hooks(task);
 
+	if (!preboot_check(task))
+		return;
+
 	update_status(task->status_fn, task->status_arg, STATUS_INFO,
 			_("Performing kexec load"));
 
diff --git a/discover/platform-powerpc.c b/discover/platform-powerpc.c
index e4422d9..7efa531 100644
--- a/discover/platform-powerpc.c
+++ b/discover/platform-powerpc.c
@@ -21,6 +21,7 @@ 
 #include "platform.h"
 #include "ipmi.h"
 #include "dt.h"
+#include "elf.h"
 
 static const char *partition = "common";
 static const char *sysparams_dir = "/sys/firmware/opal/sysparams/";
@@ -930,6 +931,47 @@  static void pre_boot(struct platform *p, const struct config *config)
 		platform->set_os_boot_sensor(platform);
 }
 
+static bool preboot_check(struct platform *p,
+			const struct config *config,
+			const char *image,
+			char **err_msg)
+{
+	struct platform_powerpc *platform = p->platform_data;
+	unsigned int *ppc_cap_bitmap = NULL;
+	bool ultravisor_enabled;
+	struct stat statbuf;
+	bool ret = true;
+
+	/* check if ultravisor-system is enabled */
+	ultravisor_enabled = stat("/proc/device-tree/ibm,ultravisor",
+		       		&statbuf) == 0;
+
+	/* if ultravisor-system is disabled, continue the boot process */
+	if (!ultravisor_enabled)
+		return true;
+
+	ppc_cap_bitmap = elf_getnote_desc(elf_open_image(image),
+					POWERPC_ELFNOTE_NAMESPACE,
+					PPC_ELFNOTE_CAPABILITIES);
+
+	if ((ppc_cap_bitmap) && (*ppc_cap_bitmap & PPCCAP_ULTRAVISOR_BIT)) {
+		pb_debug("kernel capabilities: ultravisor mode found.\n");
+	} else {
+		ret = false;
+		pb_log_fn("kernel capabilities failed:"
+			" IBM Ultravisor mode is required.\n");
+		*err_msg = talloc_strdup(platform, "IBM Ultravisor capability"
+						" not found");
+	}
+	free(ppc_cap_bitmap);
+
+	/* if preboot_check is disabled, continue the boot process */
+	if (!config->preboot_check_enabled)
+		return true;
+	
+	return ret;
+}
+
 static int get_sysinfo(struct platform *p, struct system_info *sysinfo)
 {
 	struct platform_powerpc *platform = p->platform_data;
@@ -1035,6 +1077,7 @@  static struct platform platform_powerpc = {
 	.get_sysinfo		= get_sysinfo,
 	.restrict_clients	= restrict_clients,
 	.set_password		= set_password,
+	.preboot_check		= preboot_check,
 };
 
 register_platform(platform_powerpc);
diff --git a/discover/platform.c b/discover/platform.c
index ea45594..9d52f2a 100644
--- a/discover/platform.c
+++ b/discover/platform.c
@@ -207,6 +207,17 @@  void platform_pre_boot(void)
 		platform->pre_boot(platform, config);
 }
 
+bool platform_preboot_check(const char *image, char **err_msg)
+{
+	const struct config *config = config_get();
+
+	if (platform && config && platform->preboot_check)
+		return platform->preboot_check(platform, config,
+                                    image, err_msg);
+
+    return true;
+}
+
 int platform_get_sysinfo(struct system_info *info)
 {
 	if (platform && platform->get_sysinfo)
diff --git a/discover/platform.h b/discover/platform.h
index 5a5c990..fe3d390 100644
--- a/discover/platform.h
+++ b/discover/platform.h
@@ -14,6 +14,10 @@  struct platform {
 	int		(*get_sysinfo)(struct platform *, struct system_info *);
 	bool		(*restrict_clients)(struct platform *);
 	int		(*set_password)(struct platform *, const char *hash);
+	bool		(*preboot_check)(struct platform *,
+					const struct config *,
+					const char *image,
+					char **err_msg);
 	uint16_t	dhcp_arch_id;
 	void		*platform_data;
 };
@@ -25,6 +29,7 @@  int platform_get_sysinfo(struct system_info *info);
 bool platform_restrict_clients(void);
 int platform_set_password(const char *hash);
 void platform_pre_boot(void);
+bool platform_preboot_check(const char *image, char **err_msg);
 
 /* configuration interface */
 const struct config *config_get(void);