diff mbox series

[RFC,4/8] pef: Ultravisor initialisation

Message ID 20190905132919.8765-5-grimm@linux.ibm.com
State Superseded
Headers show
Series PEF support in Skiboot | expand

Checks

Context Check Description
snowpatch_ozlabs/apply_patch warning Failed to apply on branch master (7b12d5489fcfd73ef7ec0cb27eff7f8a5f13b238)
snowpatch_ozlabs/apply_patch fail Failed to apply to any branch

Commit Message

Ryan Grimm Sept. 5, 2019, 1:29 p.m. UTC
From: Madhavan Srinivasan <maddy@linux.vnet.ibm.com>

The ultravisor image after is start on each CPU after being loaded from
the flash/fsp.  It is copied to secure memory and run.

Signed-off-by: Madhavan Srinivasan <maddy@linux.vnet.ibm.com>
Signed-off-by: Santosh Sivaraj <santosh@fossix.org>
[ grimm: Add init_uv comments, logging, and logic cleanups ]
[ grimm: Increase UV image max size to 2MB ]
[ grimm: Redfine the OPAL UV shared data structure ]
[ grimm: Remove Hostboot regions from secure range 0 ]
Signed-off-by: Ryan Grimm <grimm@linux.ibm.com>
[ andmike: Split init and start of ultravisor ]
Signed-off-by: Michael Anderson <andmike@linux.ibm.com>
---
 core/init.c              |  11 +-
 hw/ultravisor.c          | 256 +++++++++++++++++++++++++++++++++++++--
 include/ultravisor-api.h |  31 +++++
 include/ultravisor.h     |  13 +-
 4 files changed, 294 insertions(+), 17 deletions(-)
 create mode 100644 include/ultravisor-api.h

Comments

Stewart Smith Sept. 5, 2019, 4:45 p.m. UTC | #1
On Thu, Sep 5, 2019, at 6:29 AM, Ryan Grimm wrote:
> From: Madhavan Srinivasan <maddy@linux.vnet.ibm.com>
> 
> The ultravisor image after is start on each CPU after being loaded from
> the flash/fsp.  It is copied to secure memory and run.
> 
> Signed-off-by: Madhavan Srinivasan <maddy@linux.vnet.ibm.com>
> Signed-off-by: Santosh Sivaraj <santosh@fossix.org>
> [ grimm: Add init_uv comments, logging, and logic cleanups ]
> [ grimm: Increase UV image max size to 2MB ]
> [ grimm: Redfine the OPAL UV shared data structure ]
> [ grimm: Remove Hostboot regions from secure range 0 ]
> Signed-off-by: Ryan Grimm <grimm@linux.ibm.com>
> [ andmike: Split init and start of ultravisor ]
> Signed-off-by: Michael Anderson <andmike@linux.ibm.com>
> ---
>  core/init.c              |  11 +-
>  hw/ultravisor.c          | 256 +++++++++++++++++++++++++++++++++++++--
>  include/ultravisor-api.h |  31 +++++
>  include/ultravisor.h     |  13 +-
>  4 files changed, 294 insertions(+), 17 deletions(-)
>  create mode 100644 include/ultravisor-api.h
> 
> diff --git a/core/init.c b/core/init.c
> index 08989b2d..756ee211 100644
> --- a/core/init.c
> +++ b/core/init.c
> @@ -544,6 +544,10 @@ void __noreturn load_and_boot_kernel(bool is_reboot)
>  
>  	trustedboot_exit_boot_services();
>  
> +	if (!is_reboot) {
> +		start_ultravisor();
> +	}
> +

I don't think this should live in a function that is claled load_and_boot_kernel(), have it be a separate step in the main init flow.

It represents a fairly significant step in boot, and completely changes how we have to talk to things.

>  	ipmi_set_fw_progress_sensor(IPMI_FW_OS_BOOT);
>  
>  
> @@ -1265,12 +1269,7 @@ void __noreturn __nomcount main_cpu_entry(const 
> void *fdt)
>  	add_opal_interrupts();
>  
>  	/* Init uiltravisor software */
> -	fdt = create_dtb(dt_root, false);
> -	if (!fdt) {
> -		op_display(OP_FATAL, OP_MOD_INIT, 2);
> -		abort();
> -	}
> -	init_uv(fdt);
> +	init_uv();
>  
>  	/* Now release parts of memory nodes we haven't used ourselves... */
>  	mem_region_release_unused();
> diff --git a/hw/ultravisor.c b/hw/ultravisor.c
> index 4f049254..e3d7d42a 100644
> --- a/hw/ultravisor.c
> +++ b/hw/ultravisor.c
> @@ -23,10 +23,14 @@
>  #include <string.h>
>  #include <inttypes.h>
>  #include <ultravisor.h>
> +#include <mem_region.h>
> +#include <ultravisor-api.h>
> +#include <libfdt/libfdt.h>
>  
>  static char *uv_image = NULL;
>  static size_t uv_image_size;
>  struct xz_decompress *uv_xz = NULL;
> +static struct uv_opal *uv_opal;
>  
>  static struct dt_node *add_uv_dt_node(void)
>  {
> @@ -63,32 +67,266 @@ static struct dt_node *find_uv_node(void)
>  	return uv_node;
>  }
>  
> -void init_uv(const void *fdt)
> +static bool find_secure_mem_to_copy(uint64_t *target, uint64_t *sz)
> +{
> +	struct dt_node *uv_node = find_uv_node();
> +	const struct dt_property *ranges;
> +	uint64_t uv_pef_reg;
> +	uint64_t *range, sm_size, img_size = UV_LOAD_MAX_SIZE;
> +
> +	/*
> +	 * "uv-secure-memory" property could have multiple
> +	 * secure memory blocks. Pick first to load
> +	 * ultravisor in it.
> +	 */
> +	ranges = dt_find_property(uv_node, "secure-memory-ranges");
> +	if (!ranges)
> +		return false;
> +
> +	range = (void *)ranges->prop;
> +	do {
> +		uv_pef_reg = dt_get_number(range, 2);
> +		if (!uv_pef_reg)
> +			return false;
> +
> +		sm_size = dt_get_number(range + 1, 2);
> +		if (sm_size > img_size)
> +			break;
> +		range += 2;
> +	} while (range);
> +
> +	*target = uv_pef_reg;
> +	*sz = sm_size;
> +	return true;
> +}
> +
> +static uint64_t find_uv_fw_base_addr(struct dt_node *uv_node)
> +{
> +	uint64_t base_addr = 0;
> +
> +	if (dt_has_node_property(uv_node, "uv-base-address", NULL))
> +		base_addr = dt_prop_get_u64(uv_node, "uv-base-address");
> +
> +	return base_addr;
> +}
> +
> +static void reserve_secure_memory_region(void)
> +{
> +	struct dt_node *uv_node = find_uv_node();
> +	const struct dt_property *ranges;
> +	uint64_t *range, *rangesp, sm_size, addr;
> +	char buf[128];
> +	int i=0;
> +
> +	ranges = dt_find_property(uv_node, "secure-memory-ranges");
> +	if (!ranges)
> +		return;
> +
> +	for (rangesp = (uint64_t *)(ranges->prop + ranges->len),
> +			range = (uint64_t *)ranges->prop;
> +			range < rangesp;
> +			range += 2) {
> +		addr = dt_get_number(range, 2);
> +		if (!addr)
> +			break;
> +
> +		sm_size = dt_get_number(range + 1, 2);
> +		if (!sm_size)
> +			break;
> +
> +		/* Remove Hostboot regions from secure memory 0 so we don't abort
> +		 * on overlapping regions */
> +		if (i == 0) {
> +			prlog(PR_INFO, "Secure region 0, removing HB region\n");
> +			/* TODO: Check with Hostboot for memory map */
> +			sm_size = sm_size - UV_HB_RESERVE_SIZE;
> +		}
> +
> +		snprintf(buf, 128, "ibm,secure-region-%d",i++);
> +		mem_reserve_fw(strdup(buf), addr, sm_size);
> +	}
> +
> +	return;
> +}
> +
> +static void reserve_uv_memory(struct uv_opal *uv_opal)
> +{
> +	if (uv_opal->uv_base_addr == UV_LOAD_BASE) {
> +		mem_reserve_fw("ibm,uv-code", UV_LOAD_BASE, UV_LOAD_MAX_SIZE);
> +	} else {
> +		reserve_secure_memory_region();
> +	}
> +}
> +
> +static void cpu_start_ultravisor(void *data)
> +{
> +	struct uv_opal *ptr = (struct uv_opal *)data;
> +	start_uv(ptr->uv_base_addr, ptr);
> +}
> +
> +int start_ultravisor(void)
> +{
> +	struct cpu_thread *cpu;
> +	struct cpu_job **jobs;
> +	int i=0;
> +
> +	prlog(PR_NOTICE, "UV: Starting Ultravisor at 0x%llx sys_fdt 0x%llx 
> uv_fdt 0x%0llx\n",
> +				uv_opal->uv_base_addr, uv_opal->sys_fdt, uv_opal->uv_fdt);
> +
> +	/* Alloc memory for Jobs */
> +	jobs = zalloc(sizeof(struct cpu_job*) * cpu_max_pir);
> +
> +	for_each_available_cpu(cpu) {
> +		if (cpu == this_cpu())
> +			continue;
> +		jobs[i++] = cpu_queue_job(cpu, "start_ultravisor",
> +					cpu_start_ultravisor, (void *)uv_opal);
> +	}
> +
> +	cpu_start_ultravisor((void *)uv_opal);
> +
> +	/* wait for everyone to sync back */
> +	while (i > 0) {
> +		cpu_wait_job(jobs[--i], true);
> +	}
> +
> +	/* free used stuff */
> +	free(jobs);
> +
> +	/* Check everything is fine */
> +	if (uv_opal->uv_ret_code) {
> +		return OPAL_HARDWARE;
> +	}
> +
> +	return OPAL_SUCCESS;
> +}
> +
> +static int create_dtb_uv(void *uv_fdt)
> +{
> +	if (fdt_create(uv_fdt, UV_FDT_MAX_SIZE)) {
> +		prerror("UV: Failed to create uv_fdt\n");
> +		return 1;
> +	}
> +
> +	fdt_finish_reservemap(uv_fdt);
> +	fdt_begin_node(uv_fdt, "");
> +	fdt_property_string(uv_fdt, "description", "UV dt");
> +	fdt_begin_node(uv_fdt, "ibm,uv-firmware");
> +	fdt_property_string(uv_fdt, "compatible", "ibm,uv-v1");
> +	fdt_end_node(uv_fdt);
> +	fdt_end_node(uv_fdt);
> +	fdt_finish(uv_fdt);
> +
> +	return OPAL_SUCCESS;
> +}

why are you manually manipulating the FDT rather than just using the normal functions and flattening it?

> +/* We could be running on Mambo, Cronus, or Hostboot
> + *
> + * Detect Mambo via chip quirk.  Mambo writes the uncompressed UV 
> images
> + * directly to secure memory and passes secure memory location via 
> device tree.
> + *
> + * Detect Cronus when HB decompress fails.  Cronus writes the 
> uncompressed UV
> + * image to insecure memory and init_uv will copy from insecure to 
> secure.
> + *
> + * Assume HB by waiting for decompress.  UV should have been loaded 
> from FSP
> + * and decompressed earlier via uv_preload_image and 
> uv_decompress_image.  The
> + * secure location of the UV provided by those functions in xz struct. 
> */
> +void init_uv()
>  {
>  	struct dt_node *node;
>  	const struct dt_property *base;
> +	uint64_t uv_src_addr, uv_pef_reg, uv_pef_size;
> +	void *uv_fdt;
>  
> -	assert(fdt);
> +	prlog(PR_NOTICE, "UV: Init starting\n");
>  
> -	if (!is_msr_bit_set(MSR_S))
> +	uv_opal = zalloc(sizeof(struct uv_opal));
> +	if (!uv_opal) {
> +		prerror("UV: Failed to allocate uv_opal\n");
>  		return;
> +	}
> +
> +	if (!is_msr_bit_set(MSR_S)) {
> +		prlog(PR_DEBUG, "UV: S bit not set\n");
> +		return;
> +	}
>  
> -	if (!uv_xz)
> +	node = find_uv_node();
> +	if (!node) {
> +		prlog(PR_DEBUG, "UV: Device tree node not found\n");
>  		return;
> +	}
> +
> +	if (proc_chip_quirks & QUIRK_MAMBO_CALLOUTS) {
> +		prlog(PR_INFO, "UV: Mambo simulator detected\n");
> +
> +		if (!find_secure_mem_to_copy(&uv_pef_reg, &uv_pef_size)) {
> +			prlog(PR_DEBUG, "UV: No secure memory configured, exiting\n");
> +			goto load_error;
> +		}
> +
> +		goto start;
> +	}
> +
> +	/* This would be null in case we are on Mambo or Cronus */
> +	if (!uv_xz) {
> +
> +		prlog(PR_INFO, "UV: Platform load failed, detecting UV image via 
> device tree\n");
> +
> +		if (!find_secure_mem_to_copy(&uv_pef_reg, &uv_pef_size)) {
> +			prlog(PR_DEBUG, "UV: No secure memory configured, exiting\n");
> +			goto load_error;
> +		}
> +
> +		uv_src_addr = find_uv_fw_base_addr(node);
> +		if (!uv_src_addr) {
> +			prlog(PR_DEBUG, "UV: Couldn't find UV base address in device 
> tree\n");
> +			goto load_error;
> +		}
>  
> +		prlog(PR_INFO, "UV: Copying Ultravisor to protected memory 0x%llx 
> from 0x%llx\n", uv_pef_reg, uv_src_addr);
> +
> +		memcpy((void *)uv_pef_reg, (void *)uv_src_addr, UV_LOAD_MAX_SIZE);
> +
> +		goto start;
> +	}
> +
> +	/* Hostboot path */
>  	wait_xz_decompress(uv_xz);
> -	if (uv_xz->status)
> +	if (uv_xz->status) {
> +		prlog(PR_INFO, "UV: Compressed Ultravisor image failed to 
> decompress");
>  		goto load_error;
> +	}

IMNSHO in all of these situations the secure and trusted boot dance should be done.

> -	/* the uncompressed location will be the base address of ultravisor */
> -	node = find_uv_node();
> +	/* the uncompressed location will be the base address of ultravisor
> +	 * so fix up if it's already there */
>  	base = dt_find_property(node, "uv-base-address");
>  	if (base)
>  		dt_del_property(node, (struct dt_property *)base);
>  
>  	dt_add_property_u64(node, "uv-base-address", (uint64_t)uv_xz->dst);
>  
> -	/* TODO start ultravisor */
> +	uv_pef_reg = (uint64_t)uv_xz->dst;
> +	uv_pef_size = (uint64_t)uv_xz->dst_size;
> +
> +start:
> +	uv_opal->uv_base_addr = uv_pef_reg;
> +
> +	uv_opal->sys_fdt = (__be64)create_dtb(dt_root, false);
> +	if (!uv_opal->sys_fdt) {
> +		prerror("UV: Failed to create system fdt\n");
> +		goto load_error;
> +	}
> +
> +	uv_fdt = (void *)(uv_pef_reg + UV_LOAD_MAX_SIZE);
> +	if (create_dtb_uv(uv_fdt)) {
> +		prerror("UV: Failed to create uv fdt\n");
> +		goto load_error;
> +	}
> +	uv_opal->uv_fdt = (__be64)uv_fdt;
> +
> +	reserve_uv_memory(uv_opal);
> +
>  load_error:
>  	free(uv_image);
>  	free(uv_xz);
> @@ -190,7 +428,7 @@ void uv_preload_image(void)
>  	uv_image_size = MAX_COMPRESSED_UV_IMAGE_SIZE;
>  	uv_image = malloc(MAX_COMPRESSED_UV_IMAGE_SIZE);
>  	if (!uv_image) {
> -		prerror("Memory allocation for ultravisor failed\n");
> +		prerror("UV: Memory allocation for Ultravisor failed\n");
>  		return;
>  	}
>  
> diff --git a/include/ultravisor-api.h b/include/ultravisor-api.h
> new file mode 100644
> index 00000000..52d86024
> --- /dev/null
> +++ b/include/ultravisor-api.h
> @@ -0,0 +1,31 @@
> +/* Copyright 2013-2018 IBM Corp.

nothing in 2019?

Also, should be SPDX headers.

> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at
> + *
> + * 	http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> + * implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#ifndef __ULTRAVISOR_API_H
> +#define __ULTRAVISOR_API_H
> +
> +struct uv_opal {
> +	__be32 magic;		/**< 'OPUV' 0x4F505556 OPUV_MAGIC */
> +	__be32 version;		/**< uv_opal struct version */
> +	__be32 uv_ret_code;	/**< 0 - Success, <0> : error. */
> +	__be32 uv_api_ver;	/**< Current uv api version. */
> +	__be64 uv_base_addr;	/**< Base address of UV in secure memory. */
> +	__be64 sys_fdt;		/**< System FDT. */
> +	__be64 uv_fdt;		/**< UV FDT in secure memory. */
> +	__be64 uv_mem;		/**< struct memcons */
> +};
> +
> +#endif /* __ULTRAVISOR_API_H */
> diff --git a/include/ultravisor.h b/include/ultravisor.h
> index 08f10da4..976b6323 100644
> --- a/include/ultravisor.h
> +++ b/include/ultravisor.h
> @@ -17,15 +17,24 @@
>  #ifndef __ULTRAVISOR_H
>  #define __ULTRAVISOR_H

needs spdx header.
diff mbox series

Patch

diff --git a/core/init.c b/core/init.c
index 08989b2d..756ee211 100644
--- a/core/init.c
+++ b/core/init.c
@@ -544,6 +544,10 @@  void __noreturn load_and_boot_kernel(bool is_reboot)
 
 	trustedboot_exit_boot_services();
 
+	if (!is_reboot) {
+		start_ultravisor();
+	}
+
 	ipmi_set_fw_progress_sensor(IPMI_FW_OS_BOOT);
 
 
@@ -1265,12 +1269,7 @@  void __noreturn __nomcount main_cpu_entry(const void *fdt)
 	add_opal_interrupts();
 
 	/* Init uiltravisor software */
-	fdt = create_dtb(dt_root, false);
-	if (!fdt) {
-		op_display(OP_FATAL, OP_MOD_INIT, 2);
-		abort();
-	}
-	init_uv(fdt);
+	init_uv();
 
 	/* Now release parts of memory nodes we haven't used ourselves... */
 	mem_region_release_unused();
diff --git a/hw/ultravisor.c b/hw/ultravisor.c
index 4f049254..e3d7d42a 100644
--- a/hw/ultravisor.c
+++ b/hw/ultravisor.c
@@ -23,10 +23,14 @@ 
 #include <string.h>
 #include <inttypes.h>
 #include <ultravisor.h>
+#include <mem_region.h>
+#include <ultravisor-api.h>
+#include <libfdt/libfdt.h>
 
 static char *uv_image = NULL;
 static size_t uv_image_size;
 struct xz_decompress *uv_xz = NULL;
+static struct uv_opal *uv_opal;
 
 static struct dt_node *add_uv_dt_node(void)
 {
@@ -63,32 +67,266 @@  static struct dt_node *find_uv_node(void)
 	return uv_node;
 }
 
-void init_uv(const void *fdt)
+static bool find_secure_mem_to_copy(uint64_t *target, uint64_t *sz)
+{
+	struct dt_node *uv_node = find_uv_node();
+	const struct dt_property *ranges;
+	uint64_t uv_pef_reg;
+	uint64_t *range, sm_size, img_size = UV_LOAD_MAX_SIZE;
+
+	/*
+	 * "uv-secure-memory" property could have multiple
+	 * secure memory blocks. Pick first to load
+	 * ultravisor in it.
+	 */
+	ranges = dt_find_property(uv_node, "secure-memory-ranges");
+	if (!ranges)
+		return false;
+
+	range = (void *)ranges->prop;
+	do {
+		uv_pef_reg = dt_get_number(range, 2);
+		if (!uv_pef_reg)
+			return false;
+
+		sm_size = dt_get_number(range + 1, 2);
+		if (sm_size > img_size)
+			break;
+		range += 2;
+	} while (range);
+
+	*target = uv_pef_reg;
+	*sz = sm_size;
+	return true;
+}
+
+static uint64_t find_uv_fw_base_addr(struct dt_node *uv_node)
+{
+	uint64_t base_addr = 0;
+
+	if (dt_has_node_property(uv_node, "uv-base-address", NULL))
+		base_addr = dt_prop_get_u64(uv_node, "uv-base-address");
+
+	return base_addr;
+}
+
+static void reserve_secure_memory_region(void)
+{
+	struct dt_node *uv_node = find_uv_node();
+	const struct dt_property *ranges;
+	uint64_t *range, *rangesp, sm_size, addr;
+	char buf[128];
+	int i=0;
+
+	ranges = dt_find_property(uv_node, "secure-memory-ranges");
+	if (!ranges)
+		return;
+
+	for (rangesp = (uint64_t *)(ranges->prop + ranges->len),
+			range = (uint64_t *)ranges->prop;
+			range < rangesp;
+			range += 2) {
+		addr = dt_get_number(range, 2);
+		if (!addr)
+			break;
+
+		sm_size = dt_get_number(range + 1, 2);
+		if (!sm_size)
+			break;
+
+		/* Remove Hostboot regions from secure memory 0 so we don't abort
+		 * on overlapping regions */
+		if (i == 0) {
+			prlog(PR_INFO, "Secure region 0, removing HB region\n");
+			/* TODO: Check with Hostboot for memory map */
+			sm_size = sm_size - UV_HB_RESERVE_SIZE;
+		}
+
+		snprintf(buf, 128, "ibm,secure-region-%d",i++);
+		mem_reserve_fw(strdup(buf), addr, sm_size);
+	}
+
+	return;
+}
+
+static void reserve_uv_memory(struct uv_opal *uv_opal)
+{
+	if (uv_opal->uv_base_addr == UV_LOAD_BASE) {
+		mem_reserve_fw("ibm,uv-code", UV_LOAD_BASE, UV_LOAD_MAX_SIZE);
+	} else {
+		reserve_secure_memory_region();
+	}
+}
+
+static void cpu_start_ultravisor(void *data)
+{
+	struct uv_opal *ptr = (struct uv_opal *)data;
+	start_uv(ptr->uv_base_addr, ptr);
+}
+
+int start_ultravisor(void)
+{
+	struct cpu_thread *cpu;
+	struct cpu_job **jobs;
+	int i=0;
+
+	prlog(PR_NOTICE, "UV: Starting Ultravisor at 0x%llx sys_fdt 0x%llx uv_fdt 0x%0llx\n",
+				uv_opal->uv_base_addr, uv_opal->sys_fdt, uv_opal->uv_fdt);
+
+	/* Alloc memory for Jobs */
+	jobs = zalloc(sizeof(struct cpu_job*) * cpu_max_pir);
+
+	for_each_available_cpu(cpu) {
+		if (cpu == this_cpu())
+			continue;
+		jobs[i++] = cpu_queue_job(cpu, "start_ultravisor",
+					cpu_start_ultravisor, (void *)uv_opal);
+	}
+
+	cpu_start_ultravisor((void *)uv_opal);
+
+	/* wait for everyone to sync back */
+	while (i > 0) {
+		cpu_wait_job(jobs[--i], true);
+	}
+
+	/* free used stuff */
+	free(jobs);
+
+	/* Check everything is fine */
+	if (uv_opal->uv_ret_code) {
+		return OPAL_HARDWARE;
+	}
+
+	return OPAL_SUCCESS;
+}
+
+static int create_dtb_uv(void *uv_fdt)
+{
+	if (fdt_create(uv_fdt, UV_FDT_MAX_SIZE)) {
+		prerror("UV: Failed to create uv_fdt\n");
+		return 1;
+	}
+
+	fdt_finish_reservemap(uv_fdt);
+	fdt_begin_node(uv_fdt, "");
+	fdt_property_string(uv_fdt, "description", "UV dt");
+	fdt_begin_node(uv_fdt, "ibm,uv-firmware");
+	fdt_property_string(uv_fdt, "compatible", "ibm,uv-v1");
+	fdt_end_node(uv_fdt);
+	fdt_end_node(uv_fdt);
+	fdt_finish(uv_fdt);
+
+	return OPAL_SUCCESS;
+}
+
+/* We could be running on Mambo, Cronus, or Hostboot
+ *
+ * Detect Mambo via chip quirk.  Mambo writes the uncompressed UV images
+ * directly to secure memory and passes secure memory location via device tree.
+ *
+ * Detect Cronus when HB decompress fails.  Cronus writes the uncompressed UV
+ * image to insecure memory and init_uv will copy from insecure to secure.
+ *
+ * Assume HB by waiting for decompress.  UV should have been loaded from FSP
+ * and decompressed earlier via uv_preload_image and uv_decompress_image.  The
+ * secure location of the UV provided by those functions in xz struct. */
+void init_uv()
 {
 	struct dt_node *node;
 	const struct dt_property *base;
+	uint64_t uv_src_addr, uv_pef_reg, uv_pef_size;
+	void *uv_fdt;
 
-	assert(fdt);
+	prlog(PR_NOTICE, "UV: Init starting\n");
 
-	if (!is_msr_bit_set(MSR_S))
+	uv_opal = zalloc(sizeof(struct uv_opal));
+	if (!uv_opal) {
+		prerror("UV: Failed to allocate uv_opal\n");
 		return;
+	}
+
+	if (!is_msr_bit_set(MSR_S)) {
+		prlog(PR_DEBUG, "UV: S bit not set\n");
+		return;
+	}
 
-	if (!uv_xz)
+	node = find_uv_node();
+	if (!node) {
+		prlog(PR_DEBUG, "UV: Device tree node not found\n");
 		return;
+	}
+
+	if (proc_chip_quirks & QUIRK_MAMBO_CALLOUTS) {
+		prlog(PR_INFO, "UV: Mambo simulator detected\n");
+
+		if (!find_secure_mem_to_copy(&uv_pef_reg, &uv_pef_size)) {
+			prlog(PR_DEBUG, "UV: No secure memory configured, exiting\n");
+			goto load_error;
+		}
+
+		goto start;
+	}
+
+	/* This would be null in case we are on Mambo or Cronus */
+	if (!uv_xz) {
+
+		prlog(PR_INFO, "UV: Platform load failed, detecting UV image via device tree\n");
+
+		if (!find_secure_mem_to_copy(&uv_pef_reg, &uv_pef_size)) {
+			prlog(PR_DEBUG, "UV: No secure memory configured, exiting\n");
+			goto load_error;
+		}
+
+		uv_src_addr = find_uv_fw_base_addr(node);
+		if (!uv_src_addr) {
+			prlog(PR_DEBUG, "UV: Couldn't find UV base address in device tree\n");
+			goto load_error;
+		}
 
+		prlog(PR_INFO, "UV: Copying Ultravisor to protected memory 0x%llx from 0x%llx\n", uv_pef_reg, uv_src_addr);
+
+		memcpy((void *)uv_pef_reg, (void *)uv_src_addr, UV_LOAD_MAX_SIZE);
+
+		goto start;
+	}
+
+	/* Hostboot path */
 	wait_xz_decompress(uv_xz);
-	if (uv_xz->status)
+	if (uv_xz->status) {
+		prlog(PR_INFO, "UV: Compressed Ultravisor image failed to decompress");
 		goto load_error;
+	}
 
-	/* the uncompressed location will be the base address of ultravisor */
-	node = find_uv_node();
+	/* the uncompressed location will be the base address of ultravisor
+	 * so fix up if it's already there */
 	base = dt_find_property(node, "uv-base-address");
 	if (base)
 		dt_del_property(node, (struct dt_property *)base);
 
 	dt_add_property_u64(node, "uv-base-address", (uint64_t)uv_xz->dst);
 
-	/* TODO start ultravisor */
+	uv_pef_reg = (uint64_t)uv_xz->dst;
+	uv_pef_size = (uint64_t)uv_xz->dst_size;
+
+start:
+	uv_opal->uv_base_addr = uv_pef_reg;
+
+	uv_opal->sys_fdt = (__be64)create_dtb(dt_root, false);
+	if (!uv_opal->sys_fdt) {
+		prerror("UV: Failed to create system fdt\n");
+		goto load_error;
+	}
+
+	uv_fdt = (void *)(uv_pef_reg + UV_LOAD_MAX_SIZE);
+	if (create_dtb_uv(uv_fdt)) {
+		prerror("UV: Failed to create uv fdt\n");
+		goto load_error;
+	}
+	uv_opal->uv_fdt = (__be64)uv_fdt;
+
+	reserve_uv_memory(uv_opal);
+
 load_error:
 	free(uv_image);
 	free(uv_xz);
@@ -190,7 +428,7 @@  void uv_preload_image(void)
 	uv_image_size = MAX_COMPRESSED_UV_IMAGE_SIZE;
 	uv_image = malloc(MAX_COMPRESSED_UV_IMAGE_SIZE);
 	if (!uv_image) {
-		prerror("Memory allocation for ultravisor failed\n");
+		prerror("UV: Memory allocation for Ultravisor failed\n");
 		return;
 	}
 
diff --git a/include/ultravisor-api.h b/include/ultravisor-api.h
new file mode 100644
index 00000000..52d86024
--- /dev/null
+++ b/include/ultravisor-api.h
@@ -0,0 +1,31 @@ 
+/* Copyright 2013-2018 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * 	http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ULTRAVISOR_API_H
+#define __ULTRAVISOR_API_H
+
+struct uv_opal {
+	__be32 magic;		/**< 'OPUV' 0x4F505556 OPUV_MAGIC */
+	__be32 version;		/**< uv_opal struct version */
+	__be32 uv_ret_code;	/**< 0 - Success, <0> : error. */
+	__be32 uv_api_ver;	/**< Current uv api version. */
+	__be64 uv_base_addr;	/**< Base address of UV in secure memory. */
+	__be64 sys_fdt;		/**< System FDT. */
+	__be64 uv_fdt;		/**< UV FDT in secure memory. */
+	__be64 uv_mem;		/**< struct memcons */
+};
+
+#endif /* __ULTRAVISOR_API_H */
diff --git a/include/ultravisor.h b/include/ultravisor.h
index 08f10da4..976b6323 100644
--- a/include/ultravisor.h
+++ b/include/ultravisor.h
@@ -17,15 +17,24 @@ 
 #ifndef __ULTRAVISOR_H
 #define __ULTRAVISOR_H
 
+#include <ultravisor-api.h>
+
 /* Bit 15 of an address should be set for it to be used as a secure memory area
  * for the secure virtual machines */
 #define UV_SECURE_MEM_BIT              (PPC_BIT(15))
 #define MAX_COMPRESSED_UV_IMAGE_SIZE 0x40000 /* 256 Kilobytes */
+#define UV_ACCESS_BIT		0x1ULL << 48
+/* Address at which the Ultravisor is loaded for BML and Mambo */
+#define UV_LOAD_BASE		0xC0000000
+#define UV_LOAD_MAX_SIZE	0x200000
+#define UV_FDT_MAX_SIZE		0x100000
+#define UV_HB_RESERVE_SIZE	0x4000000;
 
-extern int start_uv(uint64_t entry, void *ptr);
+extern int start_uv(uint64_t entry, struct uv_opal *uv_opal);
 extern bool uv_add_mem_range(__be64 start, __be64 end);
 extern void uv_preload_image(void);
 extern void uv_decompress_image(void);
-extern void init_uv(const void *fdt);
+extern void init_uv(void);
+extern int start_ultravisor(void);
 
 #endif /* __ULTRAVISOR_H */