[01/10] hw/fsp: Decode payload initrd on FSP machines

Message ID 20170609065546.28963-1-matthew.brown.dev@gmail.com
State New
Headers show

Commit Message

Matt Brown June 9, 2017, 6:55 a.m.
During bootup on FSP systems, the payload kernel and initrd files are
loaded seperately. While the initrd has been loaded, it is still in its
compressed form. The initrd is generally compressed using xz encoding.

As we have plenty of spare resources during the Skiboot initialisation we
can preemptively decode the initrd file before the Linux kernel needs it.
This saves time during the kernel boot as it doesn't need to wait for the
initrd to decompress before continuing.

This patch adds the load_done function to platforms, with a FSP
specific implementation, which loads and configures the boot resources.
This consists of asyncronously loading and then decoding the initramfs.

By using the asyncronous load_done function we reduce the boot time by 2.5
seconds.

Note: This requires the skiboot libxz patches

Signed-off-by: Matt Brown <matthew.brown.dev@gmail.com>
---
The next load of patches adds this asyncronous decoding for both FSP and
Openpower systems.

The FSP method works with the existing file formats.
To use this improvement in openpower we need to package the payload kernel
and initramfs as a Flattened Image Tree (FIT).

It will still work if you use a zImage but there won't be any significant
performance gain.
---
 core/init.c        | 92 +++++++++++++++++++++++++++++++++++++++++++-----------
 core/platform.c    | 11 +++++++
 hw/fsp/fsp.c       | 59 ++++++++++++++++++++++++++++++++++
 include/fsp.h      |  3 ++
 include/platform.h | 24 ++++++++++++++
 5 files changed, 170 insertions(+), 19 deletions(-)

Patch

diff --git a/core/init.c b/core/init.c
index 6b8137c..bf42f3e 100644
--- a/core/init.c
+++ b/core/init.c
@@ -47,6 +47,8 @@ 
 #include <nvram.h>
 #include <libstb/stb.h>
 #include <libstb/container.h>
+#include <timebase.h>
+#include "libxz/xz.h"
 
 enum proc_gen proc_gen;
 unsigned int pcie_max_link_speed;
@@ -302,34 +304,50 @@  extern char __builtin_kernel_start[];
 extern char __builtin_kernel_end[];
 extern uint64_t boot_offset;
 
-static size_t initramfs_size;
+static struct boot_resources boot_res = {
+	.kernel_base      = KERNEL_LOAD_BASE,
+	.kernel_size      = KERNEL_LOAD_SIZE,
+	.kernel_loaded    = false,
+	.initramfs_base   = INITRAMFS_LOAD_BASE,
+	.initramfs_size   = INITRAMFS_LOAD_SIZE,
+	.initramfs_loaded = false,
+	.success          = false,
+};
+static struct cpu_job *load_boot_resource_job = NULL;
 
 bool start_preload_kernel(void)
 {
 	int loaded;
-
+	
 	/* Try to load an external kernel payload through the platform hooks */
-	kernel_size = KERNEL_LOAD_SIZE;
 	loaded = start_preload_resource(RESOURCE_ID_KERNEL,
 					RESOURCE_SUBID_NONE,
 					KERNEL_LOAD_BASE,
-					&kernel_size);
+					&boot_res.kernel_size);
 	if (loaded != OPAL_SUCCESS) {
 		printf("INIT: platform start load kernel failed\n");
 		kernel_size = 0;
 		return false;
 	}
 
-	initramfs_size = INITRAMFS_LOAD_SIZE;
 	loaded = start_preload_resource(RESOURCE_ID_INITRAMFS,
 					RESOURCE_SUBID_NONE,
-					INITRAMFS_LOAD_BASE, &initramfs_size);
+					INITRAMFS_LOAD_BASE,
+					&boot_res.initramfs_size);
 	if (loaded != OPAL_SUCCESS) {
 		printf("INIT: platform start load initramfs failed\n");
-		initramfs_size = 0;
 		return false;
 	}
 
+
+	/*
+	 * Async job to load, configure and decode kernel and initramfs
+	 * This is platform specific. FSPs only have to decode the initramfs.
+	 * BMCs have to parse the FIT, configure the resource locations, then
+	 * decode the initramfs.
+	 */
+	load_boot_resource_job = start_async_load(&boot_res);
+
 	return true;
 }
 
@@ -342,12 +360,16 @@  static bool load_kernel(void)
 
 	prlog(PR_NOTICE, "INIT: Waiting for kernel...\n");
 
-	loaded = wait_for_resource_loaded(RESOURCE_ID_KERNEL,
-					  RESOURCE_SUBID_NONE);
+	/* If on FSP machine, initramfs is decoded but need to load the kernel*/
+	if (!boot_res.kernel_loaded) {
 
-	if (loaded != OPAL_SUCCESS) {
-		printf("INIT: platform wait for kernel load failed\n");
-		kernel_size = 0;
+		loaded = wait_for_resource_loaded(RESOURCE_ID_KERNEL,
+						  RESOURCE_SUBID_NONE);
+
+		if (loaded != OPAL_SUCCESS) {
+			printf("INIT: platform wait for kernel load failed\n");
+			kernel_size = 0;
+		}
 	}
 
 	/* Try embedded kernel payload */
@@ -395,6 +417,9 @@  static bool load_kernel(void)
 		}
 	}
 
+	if (boot_res.success)
+		kh = (struct elf_hdr *)boot_res.kernel_base;
+
 	printf("INIT: Kernel loaded, size: %zu bytes (0 = unknown preload)\n",
 	       kernel_size);
 
@@ -433,23 +458,27 @@  static bool load_kernel(void)
 
 static void load_initramfs(void)
 {
-	int loaded;
+	int loaded = 0;
 
-	loaded = wait_for_resource_loaded(RESOURCE_ID_INITRAMFS,
-					  RESOURCE_SUBID_NONE);
+	printf("INIT: called load_initramfs\n");
+	if (!boot_res.initramfs_loaded)
+		loaded = wait_for_resource_loaded(RESOURCE_ID_INITRAMFS,
+						  RESOURCE_SUBID_NONE);
 
-	if (loaded != OPAL_SUCCESS || !initramfs_size)
+	if (loaded != OPAL_SUCCESS || !boot_res.initramfs_size)
 		return;
 
 	dt_check_del_prop(dt_chosen, "linux,initrd-start");
 	dt_check_del_prop(dt_chosen, "linux,initrd-end");
 
-	printf("INIT: Initramfs loaded, size: %zu bytes\n", initramfs_size);
+	printf("INIT: Initramfs loaded, base: %p, size: %zu bytes\n",
+			boot_res.initramfs_base, boot_res.initramfs_size);
 
 	dt_add_property_u64(dt_chosen, "linux,initrd-start",
-			(uint64_t)INITRAMFS_LOAD_BASE);
+			    (uint64_t)boot_res.initramfs_base);
 	dt_add_property_u64(dt_chosen, "linux,initrd-end",
-			(uint64_t)INITRAMFS_LOAD_BASE + initramfs_size);
+			    (uint64_t)boot_res.initramfs_base +
+				  boot_res.initramfs_size);
 }
 
 int64_t mem_dump_free(void);
@@ -553,6 +582,23 @@  void __noreturn load_and_boot_kernel(bool is_reboot)
 	start_kernel(kernel_entry, fdt, mem_top);
 }
 
+static int wait_for_async_load(void)
+{
+	cpu_wait_job(load_boot_resource_job, true);
+	prlog(PR_PRINTF, "Completed async job. boot_res.success = %i, \
+			kernel_base %p, size %lu \n",
+			boot_res.success, boot_res.kernel_base,
+			boot_res.kernel_size);
+
+	/* Set new local values for kernel base and size */
+	if (boot_res.kernel_loaded) {
+		kernel_entry = (uint64_t)boot_res.kernel_base;
+		kernel_size  = boot_res.kernel_size;
+	}
+
+	return OPAL_SUCCESS;
+}
+
 static void dt_fixups(void)
 {
 	struct dt_node *n;
@@ -936,6 +982,10 @@  void __noreturn __nomcount main_cpu_entry(const void *fdt)
 
 	phb3_preload_vpd();
 	phb3_preload_capp_ucode();
+
+	/* Initialise crc32 tables before trying to decode */
+	xz_crc32_init();
+
 	start_preload_kernel();
 
 	/* NX init */
@@ -978,6 +1028,10 @@  void __noreturn __nomcount main_cpu_entry(const void *fdt)
 	/* Add the list of interrupts going to OPAL */
 	add_opal_interrupts();
 
+	/* Wait for async job to setup images*/
+	if (load_boot_resource_job)
+		wait_for_async_load();
+
 	/* Now release parts of memory nodes we haven't used ourselves... */
 	mem_region_release_unused();
 
diff --git a/core/platform.c b/core/platform.c
index 2a206c8..63b67c3 100644
--- a/core/platform.c
+++ b/core/platform.c
@@ -261,3 +261,14 @@  int wait_for_resource_loaded(enum resource_id id, uint32_t idx)
 	      id, idx, waited);
 	return r;
 }
+
+/* Start async job to load and setup the boot resources */
+struct cpu_job* start_async_load(struct boot_resources *r)
+{
+	if (!platform.load_done) {
+		printf("PLATFORM: start_async_load not supported on this arch");
+		return NULL;
+	}
+
+	return platform.load_done(r);
+}
diff --git a/hw/fsp/fsp.c b/hw/fsp/fsp.c
index a0c5a78..c564a3e 100644
--- a/hw/fsp/fsp.c
+++ b/hw/fsp/fsp.c
@@ -23,6 +23,7 @@ 
  * If we are going to support P8 PSI and FSP2, we probably want
  * to split the PSI support from the FSP support proper first.
  */
+#include <skiboot.h>
 #include <stdarg.h>
 #include <processor.h>
 #include <io.h>
@@ -38,6 +39,8 @@ 
 #include <opal.h>
 #include <opal-msg.h>
 #include <ccan/list/list.h>
+#include <libxz/xz.h>
+#include <string.h>
 
 DEFINE_LOG_ENTRY(OPAL_RC_FSP_POLL_TIMEOUT, OPAL_PLATFORM_ERR_EVT, OPAL_FSP,
 		 OPAL_PLATFORM_FIRMWARE, OPAL_ERROR_PANIC, OPAL_NA);
@@ -2635,3 +2638,59 @@  void fsp_used_by_console(void)
 	lock(&fsp_lock);
 	unlock(&fsp_lock);
 }
+
+struct boot_resources *boot_res;
+static struct cpu_job *fsp_load_res_job = NULL;
+
+static void fsp_setup_boot_resource(void *data __unused)
+{
+	int r, waited;
+	size_t len;
+	void *buf;
+
+	waited = 0;
+	/* Wait for initramfs to load */
+	do {
+		opal_run_pollers();
+		r = fsp_resource_loaded(RESOURCE_ID_INITRAMFS,
+					RESOURCE_SUBID_NONE);
+		if (r != OPAL_BUSY)
+			break;
+		time_wait_ms_nopoll(5);
+		waited += 5;
+	}
+	while (r == OPAL_BUSY);
+
+	boot_res->initramfs_loaded = true;
+	prlog(PR_PRINTF, "FSP: fsp_decode_resource loaded INITRAMFS %u ms\n",
+			waited);
+
+	/* Decode the initramfs*/
+	buf = boot_res->initramfs_base;
+	len = boot_res->initramfs_size;
+
+	r = decode_resource_xz(&buf, &len, NULL);
+	printf("FSP: deocde result r=%i\n", r);
+
+	if (r) {
+		boot_res->initramfs_base = buf;
+		boot_res->initramfs_size = len;
+	}
+
+	boot_res->success = true;
+	return;
+}
+
+struct cpu_job* fsp_load_boot_resource(struct boot_resources *r)
+{
+	boot_res = r;
+	if (fsp_load_res_job)
+		cpu_wait_job(fsp_load_res_job, true);
+
+	fsp_load_res_job = cpu_queue_job(NULL, "fsp_setup_boot_resource",
+					 fsp_setup_boot_resource, NULL);
+
+	cpu_process_local_jobs();
+
+	return fsp_load_res_job;
+}
diff --git a/include/fsp.h b/include/fsp.h
index f75b6ad..74582e6 100644
--- a/include/fsp.h
+++ b/include/fsp.h
@@ -756,6 +756,9 @@  extern int fsp_resource_loaded(enum resource_id id, uint32_t idx);
 extern int fsp_preload_lid(uint32_t lid_no, char *buf, size_t *size);
 extern int fsp_wait_lid_loaded(uint32_t lid_no);
 
+/* Setup boot resources */
+extern struct cpu_job* fsp_load_boot_resource(struct boot_resources *r);
+
 /* FSP console stuff */
 extern void fsp_console_preinit(void);
 extern void fsp_console_init(void);
diff --git a/include/platform.h b/include/platform.h
index 9133204..e8848ca 100644
--- a/include/platform.h
+++ b/include/platform.h
@@ -42,6 +42,18 @@  struct bmc_platform {
 	uint32_t ipmi_oem_pnor_access_status;
 };
 
+struct boot_resources {
+	void  *kernel_base;
+	size_t kernel_size;
+	bool   kernel_loaded;
+
+	void  *initramfs_base;
+	size_t initramfs_size;
+	bool   initramfs_loaded;
+	/* Success of loading boot resources */
+	bool    success;
+};
+
 /*
  * Each platform can provide a set of hooks
  * that can affect the generic code
@@ -168,6 +180,16 @@  struct platform {
 	int		(*resource_loaded)(enum resource_id id, uint32_t idx);
 
 	/*
+	 * Returns a pointer to the cpu_job struct.
+	 * load_done is platform specific. On FSP machines we wait for the
+	 * initramfs to load, then decode it.
+	 * On BMC machines we first have to parse and setup the FIT, then
+	 * decode the initramfs.
+	 * This is done asyncronously.
+	 */
+	struct cpu_job* (*load_done)(struct boot_resources *r);
+
+	/*
 	 * Executed just prior to handing control over to the payload.
 	 */
 	void		(*exit)(void);
@@ -210,4 +232,6 @@  extern int wait_for_resource_loaded(enum resource_id id, uint32_t idx);
 
 extern void set_bmc_platform(const struct bmc_platform *bmc);
 
+extern struct cpu_job* start_async_load(struct boot_resources *r);
+
 #endif /* __PLATFORM_H */