msm_shared: scm: add support for armv8 register width mode switch

The appropriate register width for the kernel to be booted
may not match the current armv8 execution mode. Utilize
exception level change through secure monitor to request
the register width setting during an exception return, instead
of using a regular branch.

Change-Id: I004c12a1889ce53013da4860d9bea13f7597c941
diff --git a/platform/msm_shared/include/scm.h b/platform/msm_shared/include/scm.h
index 6b9efb5..ba6ed88 100644
--- a/platform/msm_shared/include/scm.h
+++ b/platform/msm_shared/include/scm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
 
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -103,6 +103,19 @@
   uint32 status;
 } ssd_protect_keystore_rsp;
 
+typedef struct {
+	uint64_t el1_x0;
+	uint64_t el1_x1;
+	uint64_t el1_x2;
+	uint64_t el1_x3;
+	uint64_t el1_x4;
+	uint64_t el1_x5;
+	uint64_t el1_x6;
+	uint64_t el1_x7;
+	uint64_t el1_x8;
+	uint64_t el1_elr;
+} el1_system_param;
+
 /* Service IDs */
 #define TZBSP_SVC_INFO              0x06
 #define SCM_SVC_SSD                 0x07
@@ -177,6 +190,9 @@
 #define SCM_SVC_PWR                     0x9
 #define SCM_IO_DISABLE_PMIC_ARBITER     0x1
 
+#define SCM_SVC_MILESTONE_32_64_ID      0x1
+#define SCM_SVC_MILESTONE_CMD_ID        0xf
+
 enum ap_ce_channel_type {
 AP_CE_REGISTER_USE = 0,
 AP_CE_ADM_USE = 1
@@ -192,6 +208,8 @@
 
 int scm_halt_pmic_arbiter();
 
+void scm_elexec_call(paddr_t kernel_entry, paddr_t dtb_offset);
+
 /**
  * struct scm_command - one SCM command buffer
  * @len: total available memory for command and response
@@ -235,7 +253,4 @@
 	uint32_t buf_offset;
 	uint32_t is_complete;
 };
-
-
-
 #endif
diff --git a/platform/msm_shared/rules.mk b/platform/msm_shared/rules.mk
index 68ad843..7c3457e 100755
--- a/platform/msm_shared/rules.mk
+++ b/platform/msm_shared/rules.mk
@@ -149,6 +149,7 @@
 			$(LOCAL_DIR)/board.o \
 			$(LOCAL_DIR)/spmi.o \
 			$(LOCAL_DIR)/bam.o \
+			$(LOCAL_DIR)/scm.o \
 			$(LOCAL_DIR)/qpic_nand.o \
 			$(LOCAL_DIR)/gpio.o \
 			$(LOCAL_DIR)/dev_tree.o
@@ -165,6 +166,7 @@
 		$(LOCAL_DIR)/board.o \
 		$(LOCAL_DIR)/spmi.o \
 		$(LOCAL_DIR)/bam.o \
+		$(LOCAL_DIR)/scm.o \
 		$(LOCAL_DIR)/qpic_nand.o \
 		$(LOCAL_DIR)/gpio.o \
 		$(LOCAL_DIR)/dev_tree.o
@@ -283,6 +285,7 @@
 			$(LOCAL_DIR)/nand.o \
 			$(LOCAL_DIR)/uart_dm.o \
 			$(LOCAL_DIR)/interrupts.o \
+			$(LOCAL_DIR)/scm.o \
 			$(LOCAL_DIR)/timer.o
 endif
 
@@ -296,6 +299,7 @@
 			$(LOCAL_DIR)/spmi.o \
 			$(LOCAL_DIR)/qpic_nand.o \
 			$(LOCAL_DIR)/bam.o \
+			$(LOCAL_DIR)/scm.o \
 			$(LOCAL_DIR)/dev_tree.o \
 			$(LOCAL_DIR)/clock.o \
 			$(LOCAL_DIR)/clock_pll.o \
@@ -312,6 +316,7 @@
 			$(LOCAL_DIR)/spmi.o \
 			$(LOCAL_DIR)/qpic_nand.o \
 			$(LOCAL_DIR)/bam.o \
+			$(LOCAL_DIR)/scm.o \
 			$(LOCAL_DIR)/dev_tree.o \
 			$(LOCAL_DIR)/clock.o \
 			$(LOCAL_DIR)/clock_pll.o \
diff --git a/platform/msm_shared/scm.c b/platform/msm_shared/scm.c
index db09374..4294168 100644
--- a/platform/msm_shared/scm.c
+++ b/platform/msm_shared/scm.c
@@ -606,3 +606,40 @@
 
 	return ret;
 }
+
+/* Execption Level exec secure-os call
+ * Jumps to kernel via secure-os and does not return
+ * on successful jump. System parameters are setup &
+ * passed on to secure-os and are utilized to boot the
+ * kernel.
+ *
+ @ kernel_entry	: kernel entry point passed in as link register.
+ @ dtb_offset	: dt blob address passed in as w0.
+ @ svc_id	: indicates direction of switch 32->64 or 64->32
+ *
+ * Assumes all sanity checks have been performed on arguments.
+ */
+
+void scm_elexec_call(paddr_t kernel_entry, paddr_t dtb_offset)
+{
+	uint32_t svc_id = SCM_SVC_MILESTONE_32_64_ID;
+	uint32_t cmd_id = SCM_SVC_MILESTONE_CMD_ID;
+	void *cmd_buf;
+	size_t cmd_len;
+	static el1_system_param param;
+
+	param.el1_x0 = dtb_offset;
+	param.el1_elr = kernel_entry;
+
+	/* Command Buffer */
+	cmd_buf = (void *)&param;
+	cmd_len = sizeof(el1_system_param);
+
+	/* Response Buffer = Null as no response expected */
+	dprintf(INFO, "Jumping to kernel via monitor\n");
+	scm_call(svc_id, cmd_id, cmd_buf, cmd_len, NULL, 0);
+
+	/* Assert if execution ever reaches here */
+	dprintf(CRITICAL, "Failed to jump to kernel\n");
+	ASSERT(0);
+}