diff mbox series

[RFC,v5,12/16] Load the ultravisor from flash and decompress

Message ID 20200227204023.22125-13-grimm@linux.ibm.com
State Superseded
Headers show
Series Ultravisor support in skiboot | expand

Checks

Context Check Description
snowpatch_ozlabs/apply_patch success Successfully applied on branch master (82aed17a5468aff6b600ee1694a10a60f942c018)
snowpatch_ozlabs/snowpatch_job_snowpatch-skiboot success Test snowpatch/job/snowpatch-skiboot on branch master
snowpatch_ozlabs/snowpatch_job_snowpatch-skiboot-dco success Signed-off-by present

Commit Message

Ryan Grimm Feb. 27, 2020, 8:40 p.m. UTC
The ultravisor, labeled UVISOR is preloaded from the PNOR in
main_cpu_entry after the kernel is preloaded.  This also works on
FSP-based systems with an ultra.lid on the FSP.

Skiboot decompresses it laster in init_uv.

Signed-off-by: Santosh Sivaraj <santosh@linux.ibm.com>
Signed-off-by: Ryan Grimm <grimm@linux.ibm.com>
---
 core/flash.c         |   1 +
 core/init.c          |   1 +
 hw/fsp/fsp.c         |   2 +
 hw/ultravisor.c      | 102 ++++++++++++++++++++++++++++++++++++++-----
 include/platform.h   |   1 +
 include/ultravisor.h |   4 +-
 6 files changed, 100 insertions(+), 11 deletions(-)

Comments

Alexey Kardashevskiy March 12, 2020, 1:08 a.m. UTC | #1
On 28/02/2020 07:40, Ryan Grimm wrote:
> The ultravisor, labeled UVISOR is preloaded from the PNOR in
> main_cpu_entry after the kernel is preloaded.  This also works on
> FSP-based systems with an ultra.lid on the FSP.
> 
> Skiboot decompresses it laster in init_uv.


s/laster/later/ ?

> 
> Signed-off-by: Santosh Sivaraj <santosh@linux.ibm.com>
> Signed-off-by: Ryan Grimm <grimm@linux.ibm.com>
> ---
>  core/flash.c         |   1 +
>  core/init.c          |   1 +
>  hw/fsp/fsp.c         |   2 +
>  hw/ultravisor.c      | 102 ++++++++++++++++++++++++++++++++++++++-----
>  include/platform.h   |   1 +
>  include/ultravisor.h |   4 +-
>  6 files changed, 100 insertions(+), 11 deletions(-)
> 
> diff --git a/core/flash.c b/core/flash.c
> index de748641..bc44a4e5 100644
> --- a/core/flash.c
> +++ b/core/flash.c
> @@ -45,6 +45,7 @@ static struct {
>  	{ RESOURCE_ID_INITRAMFS,RESOURCE_SUBID_NONE,		"ROOTFS" },
>  	{ RESOURCE_ID_CAPP,	RESOURCE_SUBID_SUPPORTED,	"CAPP" },
>  	{ RESOURCE_ID_IMA_CATALOG,  RESOURCE_SUBID_SUPPORTED,	"IMA_CATALOG" },
> +	{ RESOURCE_ID_UV_IMAGE, RESOURCE_SUBID_NONE,            "UVISOR" },
>  	{ RESOURCE_ID_VERSION,	RESOURCE_SUBID_NONE,		"VERSION" },
>  	{ RESOURCE_ID_KERNEL_FW,	RESOURCE_SUBID_NONE,		"BOOTKERNFW" },
>  };
> diff --git a/core/init.c b/core/init.c
> index f124f893..1300ab03 100644
> --- a/core/init.c
> +++ b/core/init.c
> @@ -1300,6 +1300,7 @@ void __noreturn __nomcount main_cpu_entry(const void *fdt)
>  
>  	preload_capp_ucode();
>  	start_preload_kernel();
> +	uv_preload_image();


Can this be a part of init_uv()?


>  
>  	/* Catalog decompression routine */
>  	imc_decompress_catalog();
> diff --git a/hw/fsp/fsp.c b/hw/fsp/fsp.c
> index 7592ee07..0411f035 100644
> --- a/hw/fsp/fsp.c
> +++ b/hw/fsp/fsp.c
> @@ -114,6 +114,7 @@ static u64 fsp_hir_timeout;
>  #define KERNEL_LID_PHYP			0x80a00701
>  #define KERNEL_LID_OPAL			0x80f00101
>  #define INITRAMFS_LID_OPAL		0x80f00102
> +#define ULTRA_LID_OPAL			0x80f00105
>  
>  /*
>   * We keep track on last logged values for some things to print only on
> @@ -2372,6 +2373,7 @@ static struct {
>  } fsp_lid_map[] = {
>  	{ RESOURCE_ID_KERNEL,	RESOURCE_SUBID_NONE,	KERNEL_LID_OPAL },
>  	{ RESOURCE_ID_INITRAMFS,RESOURCE_SUBID_NONE,	INITRAMFS_LID_OPAL },
> +	{ RESOURCE_ID_UV_IMAGE, RESOURCE_SUBID_NONE,	ULTRA_LID_OPAL },
>  	{ RESOURCE_ID_IMA_CATALOG,IMA_CATALOG_NIMBUS,	0x80f00103 },
>  	{ RESOURCE_ID_CAPP,	CAPP_IDX_MURANO_DD20,	0x80a02002 },
>  	{ RESOURCE_ID_CAPP,	CAPP_IDX_MURANO_DD21,	0x80a02001 },
> diff --git a/hw/ultravisor.c b/hw/ultravisor.c
> index ea14d8bd..277faeeb 100644
> --- a/hw/ultravisor.c
> +++ b/hw/ultravisor.c
> @@ -10,11 +10,16 @@
>  #include <cpu.h>
>  #include <debug_descriptor.h>
>  #include <console.h>
> +#include <chip.h>
> +#include <libstb/container.h>
>  
>  static struct dt_node *uv_fw_node;
>  static uint64_t uv_base_addr;
>  bool uv_present = false;
>  
> +static char *uv_image = NULL;
> +static size_t uv_image_size;
> +
>  struct memcons uv_memcons __section(".data.memcons") = {
>  	.magic		= MEMCONS_MAGIC,
>  	.obuf_phys	= INMEM_UV_CON_START,
> @@ -69,10 +74,52 @@ int start_ultravisor(void *fdt)
>  	return OPAL_SUCCESS;
>  }
>  
> +static int uv_decompress_image(void)
> +{
> +	struct xz_decompress *uv_xz;


xz_decompress is really small, allocate it on stack. Thanks,


> +	uint64_t uv_fw_size;
> +
> +	if (!uv_image) {
> +		prerror("UV: Preload hasn't started yet! Aborting.\n");
> +		return OPAL_INTERNAL_ERROR;
> +	}
> +
> +	if (wait_for_resource_loaded(RESOURCE_ID_UV_IMAGE,
> +				     RESOURCE_SUBID_NONE) != OPAL_SUCCESS) {
> +		prerror("UV: Ultravisor image load failed\n");
> +		return OPAL_INTERNAL_ERROR;
> +	}
> +
> +	uv_xz = malloc(sizeof(struct xz_decompress));
> +	if (!uv_xz) {
> +		prerror("UV: Cannot allocate memory for decompression of UV\n");
> +		return OPAL_NO_MEM;
> +	}
> +
> +	uv_xz->dst = (void *)dt_get_address(uv_fw_node, 0, &uv_fw_size);
> +	uv_xz->dst_size = uv_fw_size;
> +	uv_xz->src_size = uv_image_size;
> +	uv_xz->src = uv_image;
> +
> +	if (stb_is_container((void*)uv_xz->src, uv_xz->src_size))
> +		uv_xz->src = uv_xz->src + SECURE_BOOT_HEADERS_SIZE;
> +
> +	xz_start_decompress(uv_xz);
> +	if ((uv_xz->status != OPAL_PARTIAL) && (uv_xz->status != OPAL_SUCCESS)) {
> +		prerror("UV: XZ decompression failed status 0x%x\n", uv_xz->status);
> +		free(uv_xz);
> +		return OPAL_INTERNAL_ERROR;
> +	}
> +
> +	free(uv_xz);
> +	return OPAL_SUCCESS;
> +}
> +
>  void init_uv()
>  {
>  	uint64_t uv_dt_src, uv_fw_sz;
>  	struct dt_node *reserved_mem;
> +	int ret;
>  
>  	if (!is_msr_bit_set(MSR_S)) {
>  		prlog(PR_DEBUG, "UV: S bit not set\n");
> @@ -82,23 +129,58 @@ void init_uv()
>  	uv_fw_node = dt_find_compatible_node(dt_root, NULL, "ibm,uv-firmware");
>  	if (!uv_fw_node) {
>  		prerror("UV: No ibm,uv-firmware node found\n");
> -		return;
> +		goto err;
>  	}
>  
> -	reserved_mem = dt_find_by_path(dt_root, "/reserved-memory/ibm,uv-firmware");
> -	if (!reserved_mem) {
> -		prerror("UV: No reserved memory for ibm,uv-firmware found\n");
> -		return;
> -	}
> +	ret = uv_decompress_image();
> +	if (ret) {
> +		reserved_mem = dt_find_by_path(dt_root, "/reserved-memory/ibm,uv-firmware");
> +		if (!reserved_mem) {
> +			prerror("UV: No reserved memory for ibm,uv-firmware found\n");
> +			return;
> +		}
>  
> -	uv_dt_src = dt_get_address(reserved_mem, 0, &uv_fw_sz);
> -	uv_base_addr = dt_get_address(uv_fw_node, 0, NULL);
> +		uv_dt_src = dt_get_address(reserved_mem, 0, &uv_fw_sz);
> +		uv_base_addr = dt_get_address(uv_fw_node, 0, NULL);
>  
> -	prlog(PR_INFO, "UV: Copying 0x%llx bytes to protected memory 0x%llx from 0x%llx\n",
> +		prlog(PR_INFO, "UV: Copying 0x%llx bytes to protected memory 0x%llx from 0x%llx\n",
>  					uv_fw_sz, uv_base_addr, uv_dt_src);
>  
> -	memcpy((void *)uv_base_addr, (void *)uv_dt_src, uv_fw_sz);
> +		memcpy((void *)uv_base_addr, (void *)uv_dt_src, uv_fw_sz);
> +	}
>  
>  	dt_add_property_u64(uv_fw_node, "memcons", (u64)&uv_memcons);
>  	debug_descriptor.uv_memcons_phys = (u64)&uv_memcons;
> +err:
> +	local_free(uv_image);
> +}
> +
> +/*
> + * Preload the UV image from PNOR partition
> + *
> + * uv_image is allocated locally to the chip and freed here if preload fails
> + * or free in init_uv
> + */
> +void uv_preload_image(void)
> +{
> +	struct proc_chip *chip = next_chip(NULL);
> +	int ret;
> +
> +	prlog(PR_DEBUG, "UV: Preload starting\n");
> +
> +	uv_image_size = MAX_COMPRESSED_UV_IMAGE_SIZE;
> +	uv_image = local_alloc(chip->id, uv_image_size, uv_image_size);
> +	if (!uv_image) {
> +		prerror("UV: Memory allocation failed\n");
> +		return;
> +	}
> +	memset(uv_image, 0, uv_image_size);
> +
> +	ret = start_preload_resource(RESOURCE_ID_UV_IMAGE, RESOURCE_SUBID_NONE,
> +				     uv_image, &uv_image_size);
> +
> +	if (ret != OPAL_SUCCESS) {
> +		local_free(uv_image);
> +		prerror("UV: platform load failed: %d\n", ret);
> +	}
>  }
> diff --git a/include/platform.h b/include/platform.h
> index 6ecdbe47..04491d6a 100644
> --- a/include/platform.h
> +++ b/include/platform.h
> @@ -17,6 +17,7 @@ enum resource_id {
>  	RESOURCE_ID_INITRAMFS,
>  	RESOURCE_ID_CAPP,
>  	RESOURCE_ID_IMA_CATALOG,
> +	RESOURCE_ID_UV_IMAGE,
>  	RESOURCE_ID_VERSION,
>  	RESOURCE_ID_KERNEL_FW,
>  };
> diff --git a/include/ultravisor.h b/include/ultravisor.h
> index 0d4d4939..26a986cd 100644
> --- a/include/ultravisor.h
> +++ b/include/ultravisor.h
> @@ -8,7 +8,8 @@
>  #include <types.h>
>  #include <processor.h>
>  
> -#define UV_LOAD_MAX_SIZE        0x200000
> +#define MAX_COMPRESSED_UV_IMAGE_SIZE	0x40000 /* 256 Kilobytes */
> +#define UV_LOAD_MAX_SIZE		0x200000
>  
>  #define UCALL_BUFSIZE 4
>  #define UV_READ_SCOM  0xF114
> @@ -19,6 +20,7 @@ extern int start_uv(uint64_t entry, void *fdt);
>  extern bool uv_present;
>  
>  int start_ultravisor(void *fdt);
> +void uv_preload_image(void);
>  void init_uv(void);
>  
>  static inline int uv_xscom_read(u64 partid, u64 pcb_addr, u64 *val)
>
Ryan Grimm March 24, 2020, 3:22 p.m. UTC | #2
On Thu, 2020-03-12 at 12:08 +1100, Alexey Kardashevskiy wrote:
> 
> On 28/02/2020 07:40, Ryan Grimm wrote:
> > The ultravisor, labeled UVISOR is preloaded from the PNOR in
> > main_cpu_entry after the kernel is preloaded.  This also works on
> > FSP-based systems with an ultra.lid on the FSP.
> > 
> > Skiboot decompresses it laster in init_uv.
> 
> 
> s/laster/later/ ?
> 

k.

> > diff --git a/core/init.c b/core/init.c
> > index f124f893..1300ab03 100644
> > --- a/core/init.c
> > +++ b/core/init.c
> > @@ -1300,6 +1300,7 @@ void __noreturn __nomcount
> > main_cpu_entry(const void *fdt)
> >  
> >  	preload_capp_ucode();
> >  	start_preload_kernel();
> > +	uv_preload_image();
> 
> 
> Can this be a part of init_uv()?
> 

It could but isn't the point to preload it separately early in the boot
cause loading from flash or FSP is slow?

Then in init_uv the code decompresses it.

Does this sound OK?

> 
> >  
> > +static int uv_decompress_image(void)
> > +{
> > +	struct xz_decompress *uv_xz;
> 
> 
> xz_decompress is really small, allocate it on stack. Thanks,
> 

k, makes sense.

Thanks,
Ryan
Alexey Kardashevskiy March 25, 2020, 12:51 a.m. UTC | #3
On 25/03/2020 02:22, Ryan Grimm wrote:
> On Thu, 2020-03-12 at 12:08 +1100, Alexey Kardashevskiy wrote:
>>
>> On 28/02/2020 07:40, Ryan Grimm wrote:
>>> The ultravisor, labeled UVISOR is preloaded from the PNOR in
>>> main_cpu_entry after the kernel is preloaded.  This also works on
>>> FSP-based systems with an ultra.lid on the FSP.
>>>
>>> Skiboot decompresses it laster in init_uv.
>>
>>
>> s/laster/later/ ?
>>
> 
> k.
> 
>>> diff --git a/core/init.c b/core/init.c
>>> index f124f893..1300ab03 100644
>>> --- a/core/init.c
>>> +++ b/core/init.c
>>> @@ -1300,6 +1300,7 @@ void __noreturn __nomcount
>>> main_cpu_entry(const void *fdt)
>>>  
>>>  	preload_capp_ucode();
>>>  	start_preload_kernel();
>>> +	uv_preload_image();
>>
>>
>> Can this be a part of init_uv()?
>>
> 
> It could but isn't the point to preload it separately early in the boot
> cause loading from flash or FSP is slow?


No idea about how slow it is (Oliver may want to comment) but how moving
it earlier makes it faster? It is not running in parallel with something
else here. Thanks,


> 
> Then in init_uv the code decompresses it.
> 
> Does this sound OK?
> 
>>
>>>  
>>> +static int uv_decompress_image(void)
>>> +{
>>> +	struct xz_decompress *uv_xz;
>>
>>
>> xz_decompress is really small, allocate it on stack. Thanks,
>>
> 
> k, makes sense.
> 
> Thanks,
> Ryan
>
Oliver O'Halloran March 25, 2020, 1:45 a.m. UTC | #4
On Wed, Mar 25, 2020 at 11:51 AM Alexey Kardashevskiy <aik@ozlabs.ru> wrote:
>
>
>
> On 25/03/2020 02:22, Ryan Grimm wrote:
> > On Thu, 2020-03-12 at 12:08 +1100, Alexey Kardashevskiy wrote:
> >>
> >>> diff --git a/core/init.c b/core/init.c
> >>> index f124f893..1300ab03 100644
> >>> --- a/core/init.c
> >>> +++ b/core/init.c
> >>> @@ -1300,6 +1300,7 @@ void __noreturn __nomcount
> >>> main_cpu_entry(const void *fdt)
> >>>
> >>>     preload_capp_ucode();
> >>>     start_preload_kernel();
> >>> +   uv_preload_image();
> >>
> >>
> >> Can this be a part of init_uv()?
> >>
> >
> > It could but isn't the point to preload it separately early in the boot
> > cause loading from flash or FSP is slow?
>
>
> No idea about how slow it is (Oliver may want to comment) but how moving
> it earlier makes it faster? It is not running in parallel with something
> else here. Thanks,

Didn't look at the code, but the preload_* functions create a job
thread which handles the load in the background while the main thread
is doing other things.
Alexey Kardashevskiy March 25, 2020, 4:42 a.m. UTC | #5
On 25/03/2020 12:45, Oliver O'Halloran wrote:
> On Wed, Mar 25, 2020 at 11:51 AM Alexey Kardashevskiy <aik@ozlabs.ru> wrote:
>>
>>
>>
>> On 25/03/2020 02:22, Ryan Grimm wrote:
>>> On Thu, 2020-03-12 at 12:08 +1100, Alexey Kardashevskiy wrote:
>>>>
>>>>> diff --git a/core/init.c b/core/init.c
>>>>> index f124f893..1300ab03 100644
>>>>> --- a/core/init.c
>>>>> +++ b/core/init.c
>>>>> @@ -1300,6 +1300,7 @@ void __noreturn __nomcount
>>>>> main_cpu_entry(const void *fdt)
>>>>>
>>>>>     preload_capp_ucode();
>>>>>     start_preload_kernel();
>>>>> +   uv_preload_image();
>>>>
>>>>
>>>> Can this be a part of init_uv()?
>>>>
>>>
>>> It could but isn't the point to preload it separately early in the boot
>>> cause loading from flash or FSP is slow?
>>
>>
>> No idea about how slow it is (Oliver may want to comment) but how moving
>> it earlier makes it faster? It is not running in parallel with something
>> else here. Thanks,
> 
> Didn't look at the code, but the preload_* functions create a job
> thread which handles the load in the background while the main thread
> is doing other things.


aaah right, the code calls start_preload_resource() +
wait_for_resource_loaded(), makes sense now. Thanks,
diff mbox series

Patch

diff --git a/core/flash.c b/core/flash.c
index de748641..bc44a4e5 100644
--- a/core/flash.c
+++ b/core/flash.c
@@ -45,6 +45,7 @@  static struct {
 	{ RESOURCE_ID_INITRAMFS,RESOURCE_SUBID_NONE,		"ROOTFS" },
 	{ RESOURCE_ID_CAPP,	RESOURCE_SUBID_SUPPORTED,	"CAPP" },
 	{ RESOURCE_ID_IMA_CATALOG,  RESOURCE_SUBID_SUPPORTED,	"IMA_CATALOG" },
+	{ RESOURCE_ID_UV_IMAGE, RESOURCE_SUBID_NONE,            "UVISOR" },
 	{ RESOURCE_ID_VERSION,	RESOURCE_SUBID_NONE,		"VERSION" },
 	{ RESOURCE_ID_KERNEL_FW,	RESOURCE_SUBID_NONE,		"BOOTKERNFW" },
 };
diff --git a/core/init.c b/core/init.c
index f124f893..1300ab03 100644
--- a/core/init.c
+++ b/core/init.c
@@ -1300,6 +1300,7 @@  void __noreturn __nomcount main_cpu_entry(const void *fdt)
 
 	preload_capp_ucode();
 	start_preload_kernel();
+	uv_preload_image();
 
 	/* Catalog decompression routine */
 	imc_decompress_catalog();
diff --git a/hw/fsp/fsp.c b/hw/fsp/fsp.c
index 7592ee07..0411f035 100644
--- a/hw/fsp/fsp.c
+++ b/hw/fsp/fsp.c
@@ -114,6 +114,7 @@  static u64 fsp_hir_timeout;
 #define KERNEL_LID_PHYP			0x80a00701
 #define KERNEL_LID_OPAL			0x80f00101
 #define INITRAMFS_LID_OPAL		0x80f00102
+#define ULTRA_LID_OPAL			0x80f00105
 
 /*
  * We keep track on last logged values for some things to print only on
@@ -2372,6 +2373,7 @@  static struct {
 } fsp_lid_map[] = {
 	{ RESOURCE_ID_KERNEL,	RESOURCE_SUBID_NONE,	KERNEL_LID_OPAL },
 	{ RESOURCE_ID_INITRAMFS,RESOURCE_SUBID_NONE,	INITRAMFS_LID_OPAL },
+	{ RESOURCE_ID_UV_IMAGE, RESOURCE_SUBID_NONE,	ULTRA_LID_OPAL },
 	{ RESOURCE_ID_IMA_CATALOG,IMA_CATALOG_NIMBUS,	0x80f00103 },
 	{ RESOURCE_ID_CAPP,	CAPP_IDX_MURANO_DD20,	0x80a02002 },
 	{ RESOURCE_ID_CAPP,	CAPP_IDX_MURANO_DD21,	0x80a02001 },
diff --git a/hw/ultravisor.c b/hw/ultravisor.c
index ea14d8bd..277faeeb 100644
--- a/hw/ultravisor.c
+++ b/hw/ultravisor.c
@@ -10,11 +10,16 @@ 
 #include <cpu.h>
 #include <debug_descriptor.h>
 #include <console.h>
+#include <chip.h>
+#include <libstb/container.h>
 
 static struct dt_node *uv_fw_node;
 static uint64_t uv_base_addr;
 bool uv_present = false;
 
+static char *uv_image = NULL;
+static size_t uv_image_size;
+
 struct memcons uv_memcons __section(".data.memcons") = {
 	.magic		= MEMCONS_MAGIC,
 	.obuf_phys	= INMEM_UV_CON_START,
@@ -69,10 +74,52 @@  int start_ultravisor(void *fdt)
 	return OPAL_SUCCESS;
 }
 
+static int uv_decompress_image(void)
+{
+	struct xz_decompress *uv_xz;
+	uint64_t uv_fw_size;
+
+	if (!uv_image) {
+		prerror("UV: Preload hasn't started yet! Aborting.\n");
+		return OPAL_INTERNAL_ERROR;
+	}
+
+	if (wait_for_resource_loaded(RESOURCE_ID_UV_IMAGE,
+				     RESOURCE_SUBID_NONE) != OPAL_SUCCESS) {
+		prerror("UV: Ultravisor image load failed\n");
+		return OPAL_INTERNAL_ERROR;
+	}
+
+	uv_xz = malloc(sizeof(struct xz_decompress));
+	if (!uv_xz) {
+		prerror("UV: Cannot allocate memory for decompression of UV\n");
+		return OPAL_NO_MEM;
+	}
+
+	uv_xz->dst = (void *)dt_get_address(uv_fw_node, 0, &uv_fw_size);
+	uv_xz->dst_size = uv_fw_size;
+	uv_xz->src_size = uv_image_size;
+	uv_xz->src = uv_image;
+
+	if (stb_is_container((void*)uv_xz->src, uv_xz->src_size))
+		uv_xz->src = uv_xz->src + SECURE_BOOT_HEADERS_SIZE;
+
+	xz_start_decompress(uv_xz);
+	if ((uv_xz->status != OPAL_PARTIAL) && (uv_xz->status != OPAL_SUCCESS)) {
+		prerror("UV: XZ decompression failed status 0x%x\n", uv_xz->status);
+		free(uv_xz);
+		return OPAL_INTERNAL_ERROR;
+	}
+
+	free(uv_xz);
+	return OPAL_SUCCESS;
+}
+
 void init_uv()
 {
 	uint64_t uv_dt_src, uv_fw_sz;
 	struct dt_node *reserved_mem;
+	int ret;
 
 	if (!is_msr_bit_set(MSR_S)) {
 		prlog(PR_DEBUG, "UV: S bit not set\n");
@@ -82,23 +129,58 @@  void init_uv()
 	uv_fw_node = dt_find_compatible_node(dt_root, NULL, "ibm,uv-firmware");
 	if (!uv_fw_node) {
 		prerror("UV: No ibm,uv-firmware node found\n");
-		return;
+		goto err;
 	}
 
-	reserved_mem = dt_find_by_path(dt_root, "/reserved-memory/ibm,uv-firmware");
-	if (!reserved_mem) {
-		prerror("UV: No reserved memory for ibm,uv-firmware found\n");
-		return;
-	}
+	ret = uv_decompress_image();
+	if (ret) {
+		reserved_mem = dt_find_by_path(dt_root, "/reserved-memory/ibm,uv-firmware");
+		if (!reserved_mem) {
+			prerror("UV: No reserved memory for ibm,uv-firmware found\n");
+			return;
+		}
 
-	uv_dt_src = dt_get_address(reserved_mem, 0, &uv_fw_sz);
-	uv_base_addr = dt_get_address(uv_fw_node, 0, NULL);
+		uv_dt_src = dt_get_address(reserved_mem, 0, &uv_fw_sz);
+		uv_base_addr = dt_get_address(uv_fw_node, 0, NULL);
 
-	prlog(PR_INFO, "UV: Copying 0x%llx bytes to protected memory 0x%llx from 0x%llx\n",
+		prlog(PR_INFO, "UV: Copying 0x%llx bytes to protected memory 0x%llx from 0x%llx\n",
 					uv_fw_sz, uv_base_addr, uv_dt_src);
 
-	memcpy((void *)uv_base_addr, (void *)uv_dt_src, uv_fw_sz);
+		memcpy((void *)uv_base_addr, (void *)uv_dt_src, uv_fw_sz);
+	}
 
 	dt_add_property_u64(uv_fw_node, "memcons", (u64)&uv_memcons);
 	debug_descriptor.uv_memcons_phys = (u64)&uv_memcons;
+err:
+	local_free(uv_image);
+}
+
+/*
+ * Preload the UV image from PNOR partition
+ *
+ * uv_image is allocated locally to the chip and freed here if preload fails
+ * or free in init_uv
+ */
+void uv_preload_image(void)
+{
+	struct proc_chip *chip = next_chip(NULL);
+	int ret;
+
+	prlog(PR_DEBUG, "UV: Preload starting\n");
+
+	uv_image_size = MAX_COMPRESSED_UV_IMAGE_SIZE;
+	uv_image = local_alloc(chip->id, uv_image_size, uv_image_size);
+	if (!uv_image) {
+		prerror("UV: Memory allocation failed\n");
+		return;
+	}
+	memset(uv_image, 0, uv_image_size);
+
+	ret = start_preload_resource(RESOURCE_ID_UV_IMAGE, RESOURCE_SUBID_NONE,
+				     uv_image, &uv_image_size);
+
+	if (ret != OPAL_SUCCESS) {
+		local_free(uv_image);
+		prerror("UV: platform load failed: %d\n", ret);
+	}
 }
diff --git a/include/platform.h b/include/platform.h
index 6ecdbe47..04491d6a 100644
--- a/include/platform.h
+++ b/include/platform.h
@@ -17,6 +17,7 @@  enum resource_id {
 	RESOURCE_ID_INITRAMFS,
 	RESOURCE_ID_CAPP,
 	RESOURCE_ID_IMA_CATALOG,
+	RESOURCE_ID_UV_IMAGE,
 	RESOURCE_ID_VERSION,
 	RESOURCE_ID_KERNEL_FW,
 };
diff --git a/include/ultravisor.h b/include/ultravisor.h
index 0d4d4939..26a986cd 100644
--- a/include/ultravisor.h
+++ b/include/ultravisor.h
@@ -8,7 +8,8 @@ 
 #include <types.h>
 #include <processor.h>
 
-#define UV_LOAD_MAX_SIZE        0x200000
+#define MAX_COMPRESSED_UV_IMAGE_SIZE	0x40000 /* 256 Kilobytes */
+#define UV_LOAD_MAX_SIZE		0x200000
 
 #define UCALL_BUFSIZE 4
 #define UV_READ_SCOM  0xF114
@@ -19,6 +20,7 @@  extern int start_uv(uint64_t entry, void *fdt);
 extern bool uv_present;
 
 int start_ultravisor(void *fdt);
+void uv_preload_image(void);
 void init_uv(void);
 
 static inline int uv_xscom_read(u64 partid, u64 pcb_addr, u64 *val)