msm: pil-8960: Support secure PIL

Add support for secure peripheral loading. In addition, add a
module parameter 'secure_pil' for use on the kernel command line.
This allows CONFIG_MSM_SECURE_PIL to be overriden at runtime if
needed.

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
diff --git a/arch/arm/mach-msm/peripheral-reset-8960.c b/arch/arm/mach-msm/peripheral-reset-8960.c
index 007d22c..626b207 100644
--- a/arch/arm/mach-msm/peripheral-reset-8960.c
+++ b/arch/arm/mach-msm/peripheral-reset-8960.c
@@ -18,9 +18,12 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/regulator/consumer.h>
-#include <mach/msm_iomap.h>
+
 #include <asm/mach-types.h>
 
+#include <mach/msm_iomap.h>
+#include <mach/scm.h>
+
 #include "peripheral-loader.h"
 
 #define MSM_FW_QDSP6SS_PHYS	0x08800000
@@ -116,6 +119,41 @@
 #define PPSS_PROC_CLK_CTL		(MSM_CLK_CTL_BASE + 0x2588)
 #define PPSS_HCLK_CTL			(MSM_CLK_CTL_BASE + 0x2580)
 
+#define PAS_Q6		1
+#define PAS_DSPS	2
+#define PAS_MODEM_SW	4
+#define PAS_MODEM_FW	5
+
+#define PAS_INIT_IMAGE_CMD	1
+#define PAS_MEM_CMD		2
+#define PAS_AUTH_AND_RESET_CMD	5
+#define PAS_SHUTDOWN_CMD	6
+
+struct pas_init_image_req {
+	u32	proc;
+	u32	image_addr;
+};
+
+struct pas_init_image_resp {
+	u32	image_valid;
+};
+
+struct pas_auth_image_req {
+	u32	proc;
+};
+
+struct pas_auth_image_resp {
+	u32	reset_initiated;
+};
+
+struct pas_shutdown_req {
+	u32	proc;
+};
+
+struct pas_shutdown_resp {
+	u32	success;
+};
+
 struct q6_data {
 	const unsigned strap_tcm_base;
 	const unsigned strap_ahb_upper;
@@ -163,6 +201,45 @@
 static void __iomem *msm_riva_base;
 static unsigned long riva_start;
 
+static int init_image_trusted(int id, const u8 *metadata, size_t size)
+{
+	int ret;
+	struct pas_init_image_req request;
+	struct pas_init_image_resp resp = {0};
+	void *mdata_buf;
+
+	/* Make memory physically contiguous */
+	mdata_buf = kmemdup(metadata, size, GFP_KERNEL);
+	if (!mdata_buf)
+		return -ENOMEM;
+
+	request.proc = id;
+	request.image_addr = virt_to_phys(mdata_buf);
+
+	ret = scm_call(SCM_SVC_PIL, PAS_INIT_IMAGE_CMD, &request,
+			sizeof(request), &resp, sizeof(resp));
+	kfree(mdata_buf);
+
+	if (ret)
+		return ret;
+	return resp.image_valid;
+}
+
+static int init_image_lpass_q6_trusted(const u8 *metadata, size_t size)
+{
+	return init_image_trusted(PAS_Q6, metadata, size);
+}
+
+static int init_image_modem_fw_q6_trusted(const u8 *metadata, size_t size)
+{
+	return init_image_trusted(PAS_MODEM_FW, metadata, size);
+}
+
+static int init_image_modem_sw_q6_trusted(const u8 *metadata, size_t size)
+{
+	return init_image_trusted(PAS_MODEM_SW, metadata, size);
+}
+
 static int init_image_lpass_q6_untrusted(const u8 *metadata, size_t size)
 {
 	const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
@@ -189,6 +266,56 @@
 	return 0;
 }
 
+static int auth_and_reset_trusted(int id)
+{
+	int ret;
+	struct pas_auth_image_req request;
+	struct pas_auth_image_resp resp = {0};
+
+	request.proc = id;
+	ret = scm_call(SCM_SVC_PIL, PAS_AUTH_AND_RESET_CMD, &request,
+			sizeof(request), &resp, sizeof(resp));
+	if (ret)
+		return ret;
+
+	return resp.reset_initiated;
+}
+
+static int reset_q6_trusted(int id, struct q6_data *q6)
+{
+	int err;
+
+	err = regulator_set_voltage(q6->vreg, 1050000, 1050000);
+	if (err) {
+		pr_err("Failed to set %s regulator's voltage.\n", q6->name);
+		return err;
+	}
+	err = regulator_enable(q6->vreg);
+	if (err) {
+		pr_err("Failed to enable %s's regulator.\n", q6->name);
+		return err;
+	}
+	q6->vreg_enabled = true;
+
+	return auth_and_reset_trusted(id);
+}
+
+
+static int reset_lpass_q6_trusted(void)
+{
+	return reset_q6_trusted(PAS_Q6, &q6_lpass);
+}
+
+static int reset_modem_fw_q6_trusted(void)
+{
+	return reset_q6_trusted(PAS_MODEM_FW, &q6_modem_fw);
+}
+
+static int reset_modem_sw_q6_trusted(void)
+{
+	return reset_q6_trusted(PAS_MODEM_SW, &q6_modem_sw);
+}
+
 static int reset_q6_untrusted(struct q6_data *q6)
 {
 	u32 reg, err = 0;
@@ -318,6 +445,49 @@
 	return reset_q6_untrusted(&q6_modem_sw);
 }
 
+static int shutdown_trusted(int id)
+{
+	int ret;
+	struct pas_shutdown_req request;
+	struct pas_shutdown_resp resp = {0};
+
+	request.proc = id;
+	ret = scm_call(SCM_SVC_PIL, PAS_SHUTDOWN_CMD, &request, sizeof(request),
+			&resp, sizeof(resp));
+	if (ret)
+		return ret;
+
+	return resp.success;
+}
+
+static int shutdown_q6_trusted(int id, struct q6_data *q6)
+{
+	int ret;
+
+	ret = shutdown_trusted(id);
+	if (q6->vreg_enabled) {
+		regulator_disable(q6->vreg);
+		q6->vreg_enabled = false;
+	}
+
+	return ret;
+}
+
+static int shutdown_lpass_q6_trusted(void)
+{
+	return shutdown_q6_trusted(PAS_Q6, &q6_lpass);
+}
+
+static int shutdown_modem_fw_q6_trusted(void)
+{
+	return shutdown_q6_trusted(PAS_MODEM_FW, &q6_modem_fw);
+}
+
+static int shutdown_modem_sw_q6_trusted(void)
+{
+	return shutdown_q6_trusted(PAS_MODEM_SW, &q6_modem_sw);
+}
+
 static int shutdown_q6_untrusted(struct q6_data *q6)
 {
 	u32 reg;
@@ -492,6 +662,21 @@
 	return 0;
 }
 
+static int init_image_dsps_trusted(const u8 *metadata, size_t size)
+{
+	return init_image_trusted(PAS_DSPS, metadata, size);
+}
+
+static int reset_dsps_trusted(void)
+{
+	return auth_and_reset_trusted(PAS_DSPS);
+}
+
+static int shutdown_dsps_trusted(void)
+{
+	return shutdown_trusted(PAS_DSPS);
+}
+
 static struct pil_reset_ops pil_modem_fw_q6_ops = {
 	.init_image = init_image_modem_fw_q6_untrusted,
 	.verify_blob = verify_blob,
@@ -598,6 +783,12 @@
 	return err;
 }
 
+#ifdef CONFIG_MSM_SECURE_PIL
+static bool secure_pil = true;
+#else
+static bool secure_pil;
+#endif
+
 static int __init msm_peripheral_reset_init(void)
 {
 	int err;
@@ -609,6 +800,24 @@
 	if (machine_is_msm8960_sim() || machine_is_msm8960_rumi3())
 		return 0;
 
+	if (secure_pil) {
+		pil_lpass_q6_ops.init_image = init_image_lpass_q6_trusted;
+		pil_lpass_q6_ops.auth_and_reset = reset_lpass_q6_trusted;
+		pil_lpass_q6_ops.shutdown = shutdown_lpass_q6_trusted;
+
+		pil_modem_fw_q6_ops.init_image = init_image_modem_fw_q6_trusted;
+		pil_modem_fw_q6_ops.auth_and_reset = reset_modem_fw_q6_trusted;
+		pil_modem_fw_q6_ops.shutdown = shutdown_modem_fw_q6_trusted;
+
+		pil_modem_sw_q6_ops.init_image = init_image_modem_sw_q6_trusted;
+		pil_modem_sw_q6_ops.auth_and_reset = reset_modem_sw_q6_trusted;
+		pil_modem_sw_q6_ops.shutdown = shutdown_modem_sw_q6_trusted;
+
+		pil_dsps_ops.init_image = init_image_dsps_trusted;
+		pil_dsps_ops.auth_and_reset = reset_dsps_trusted;
+		pil_dsps_ops.shutdown = shutdown_dsps_trusted;
+	}
+
 	err = q6_reset_init(&q6_lpass);
 	if (err)
 		return err;
@@ -640,3 +849,5 @@
 	return 0;
 }
 arch_initcall(msm_peripheral_reset_init);
+module_param(secure_pil, bool, S_IRUGO);
+MODULE_PARM_DESC(secure_pil, "Use Secure PIL");